summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2010-03-01 14:19:05 -0800
committerTony Lindgren <tony@atomide.com>2010-03-01 14:19:05 -0800
commitd702d12167a2c05a346f49aac7a311d597762495 (patch)
treebaae42c299cce34d6df24b5d01f8b1d0b481bd9a /drivers
parent9418c65f9bd861d0f7e39aab9cfb3aa6f2275d11 (diff)
parentac0f6f927db539e03e1f3f61bcd4ed57d5cde7a9 (diff)
Merge with mainline to remove plat-omap/Kconfig conflict
Conflicts: arch/arm/plat-omap/Kconfig
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpica/acevents.h10
-rw-r--r--drivers/acpi/acpica/aclocal.h2
-rw-r--r--drivers/acpi/acpica/acobject.h2
-rw-r--r--drivers/acpi/acpica/evgpe.c161
-rw-r--r--drivers/acpi/acpica/evgpeblk.c87
-rw-r--r--drivers/acpi/acpica/evmisc.c12
-rw-r--r--drivers/acpi/acpica/evxface.c189
-rw-r--r--drivers/acpi/acpica/evxfevnt.c96
-rw-r--r--drivers/acpi/button.c15
-rw-r--r--drivers/acpi/dock.c1
-rw-r--r--drivers/acpi/ec.c28
-rw-r--r--drivers/acpi/internal.h2
-rw-r--r--drivers/acpi/pci_bind.c14
-rw-r--r--drivers/acpi/pci_root.c9
-rw-r--r--drivers/acpi/processor_idle.c36
-rw-r--r--drivers/acpi/processor_pdc.c14
-rw-r--r--drivers/acpi/processor_perflib.c6
-rw-r--r--drivers/acpi/scan.c65
-rw-r--r--drivers/acpi/sleep.c15
-rw-r--r--drivers/acpi/system.c4
-rw-r--r--drivers/acpi/tables.c4
-rw-r--r--drivers/acpi/wakeup.c84
-rw-r--r--drivers/ata/ahci.c12
-rw-r--r--drivers/ata/libata-scsi.c2
-rw-r--r--drivers/ata/sata_nv.c2
-rw-r--r--drivers/base/class.c2
-rw-r--r--drivers/base/power/main.c143
-rw-r--r--drivers/base/power/power.h6
-rw-r--r--drivers/base/power/runtime.c45
-rw-r--r--drivers/base/power/sysfs.c100
-rw-r--r--drivers/block/DAC960.c6
-rw-r--r--drivers/block/ataflop.c2
-rw-r--r--drivers/block/brd.c2
-rw-r--r--drivers/block/cciss.c218
-rw-r--r--drivers/block/cciss.h21
-rw-r--r--drivers/block/cciss_cmd.h164
-rw-r--r--drivers/block/cciss_scsi.c145
-rw-r--r--drivers/block/cciss_scsi.h18
-rw-r--r--drivers/block/cpqarray.c5
-rw-r--r--drivers/block/drbd/drbd_nl.c5
-rw-r--r--drivers/block/floppy.c2
-rw-r--r--drivers/block/hd.c2
-rw-r--r--drivers/block/mg_disk.c2
-rw-r--r--drivers/block/paride/pd.c2
-rw-r--r--drivers/block/paride/pf.c3
-rw-r--r--drivers/block/pktcdvd.c97
-rw-r--r--drivers/block/ps3disk.c5
-rw-r--r--drivers/block/ps3vram.c7
-rw-r--r--drivers/block/sunvdc.c5
-rw-r--r--drivers/block/swim.c4
-rw-r--r--drivers/block/sx8.c5
-rw-r--r--drivers/block/ub.c7
-rw-r--r--drivers/block/viodasd.c91
-rw-r--r--drivers/block/virtio_blk.c63
-rw-r--r--drivers/block/xd.c2
-rw-r--r--drivers/block/xen-blkfront.c7
-rw-r--r--drivers/block/xsysace.c2
-rw-r--r--drivers/cdrom/gdrom.c2
-rw-r--r--drivers/cdrom/viocd.c5
-rw-r--r--drivers/char/Kconfig8
-rw-r--r--drivers/char/agp/intel-agp.c15
-rw-r--r--drivers/char/hvc_beat.c4
-rw-r--r--drivers/char/hvc_console.c9
-rw-r--r--drivers/char/hvc_console.h9
-rw-r--r--drivers/char/hvc_iseries.c2
-rw-r--r--drivers/char/hvc_iucv.c2
-rw-r--r--drivers/char/hvc_rtas.c2
-rw-r--r--drivers/char/hvc_udbg.c2
-rw-r--r--drivers/char/hvc_vio.c2
-rw-r--r--drivers/char/hvc_xen.c2
-rw-r--r--drivers/char/hw_random/Kconfig12
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/nomadik-rng.c103
-rw-r--r--drivers/char/nvram.c3
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c2
-rw-r--r--drivers/char/virtio_console.c1578
-rw-r--r--drivers/char/vme_scc.c12
-rw-r--r--drivers/clocksource/cs5535-clockevt.c2
-rw-r--r--drivers/clocksource/sh_cmt.c67
-rw-r--r--drivers/clocksource/sh_mtu2.c6
-rw-r--r--drivers/clocksource/sh_tmu.c6
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.c2
-rw-r--r--drivers/crypto/geode-aes.c8
-rw-r--r--drivers/crypto/talitos.c2
-rw-r--r--drivers/dma/shdma.c411
-rw-r--r--drivers/dma/shdma.h7
-rw-r--r--drivers/firewire/net.c53
-rw-r--r--drivers/firewire/ohci.c13
-rw-r--r--drivers/firewire/sbp2.c2
-rw-r--r--drivers/firmware/iscsi_ibft.c8
-rw-r--r--drivers/gpu/drm/drm_edid.c47
-rw-r--r--drivers/gpu/drm/drm_mm.c3
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c30
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c113
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dac.c6
-rw-r--r--drivers/gpu/drm/nouveau/nv17_tv.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_instmem.c58
-rw-r--r--drivers/gpu/drm/radeon/atom.c7
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c2
-rw-r--r--drivers/gpu/drm/radeon/r600_blit_kms.c3
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon.h9
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c44
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c10
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h3
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c36
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c107
-rw-r--r--drivers/gpu/drm/radeon/rv770.c9
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c6
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c18
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c49
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c108
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c3
-rw-r--r--drivers/gpu/vga/Kconfig8
-rw-r--r--drivers/gpu/vga/vgaarb.c29
-rw-r--r--drivers/hid/Kconfig54
-rw-r--r--drivers/hid/Makefile9
-rw-r--r--drivers/hid/hid-3m-pct.c290
-rw-r--r--drivers/hid/hid-apple.c17
-rw-r--r--drivers/hid/hid-core.c23
-rw-r--r--drivers/hid/hid-debug.c6
-rw-r--r--drivers/hid/hid-ids.h37
-rw-r--r--drivers/hid/hid-input.c12
-rw-r--r--drivers/hid/hid-lg.c7
-rw-r--r--drivers/hid/hid-lg.h6
-rw-r--r--drivers/hid/hid-lg3ff.c176
-rw-r--r--drivers/hid/hid-lgff.c1
-rw-r--r--drivers/hid/hid-magicmouse.c449
-rw-r--r--drivers/hid/hid-mosart.c273
-rw-r--r--drivers/hid/hid-ntrig.c212
-rw-r--r--drivers/hid/hid-ortek.c56
-rw-r--r--drivers/hid/hid-quanta.c260
-rw-r--r--drivers/hid/hid-sony.c23
-rw-r--r--drivers/hid/hid-stantum.c283
-rw-r--r--drivers/hid/hid-wacom.c28
-rw-r--r--drivers/hid/hidraw.c2
-rw-r--r--drivers/hid/usbhid/hid-core.c42
-rw-r--r--drivers/hid/usbhid/hid-quirks.c3
-rw-r--r--drivers/hid/usbhid/usbhid.h2
-rw-r--r--drivers/hwmon/ams/ams-core.c11
-rw-r--r--drivers/hwmon/ams/ams-i2c.c2
-rw-r--r--drivers/hwmon/ams/ams-pmu.c2
-rw-r--r--drivers/hwmon/ams/ams.h1
-rw-r--r--drivers/i2c/busses/Kconfig10
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-octeon.c651
-rw-r--r--drivers/i2c/busses/i2c-pnx.c285
-rw-r--r--drivers/ide/au1xxx-ide.c21
-rw-r--r--drivers/ide/ide-disk.c2
-rw-r--r--drivers/ide/ide-floppy.c4
-rw-r--r--drivers/ide/ide-probe.c5
-rw-r--r--drivers/ieee1394/sbp2.c2
-rw-r--r--drivers/input/input-polldev.c6
-rw-r--r--drivers/input/serio/i8042.c8
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c8
-rw-r--r--drivers/isdn/hisax/Kconfig18
-rw-r--r--drivers/isdn/hisax/avm_pci.c6
-rw-r--r--drivers/isdn/hisax/bkm_a4t.c2
-rw-r--r--drivers/isdn/hisax/bkm_a8.c2
-rw-r--r--drivers/isdn/hisax/diva.c14
-rw-r--r--drivers/isdn/hisax/elsa.c8
-rw-r--r--drivers/isdn/hisax/enternow_pci.c2
-rw-r--r--drivers/isdn/hisax/gazel.c8
-rw-r--r--drivers/isdn/hisax/hfc_pci.c2
-rw-r--r--drivers/isdn/hisax/hisax.h23
-rw-r--r--drivers/isdn/hisax/niccy.c6
-rw-r--r--drivers/isdn/hisax/nj_s.c2
-rw-r--r--drivers/isdn/hisax/nj_u.c2
-rw-r--r--drivers/isdn/hisax/sedlbauer.c6
-rw-r--r--drivers/isdn/hisax/telespci.c2
-rw-r--r--drivers/isdn/hisax/w6692.c2
-rw-r--r--drivers/macintosh/adb.c10
-rw-r--r--drivers/macintosh/therm_adt746x.c36
-rw-r--r--drivers/macintosh/therm_pm72.c8
-rw-r--r--drivers/macintosh/therm_windtunnel.c2
-rw-r--r--drivers/macintosh/via-cuda.c74
-rw-r--r--drivers/macintosh/via-pmu-backlight.c8
-rw-r--r--drivers/macintosh/via-pmu.c8
-rw-r--r--drivers/macintosh/windfarm_core.c6
-rw-r--r--drivers/macintosh/windfarm_cpufreq_clamp.c6
-rw-r--r--drivers/macintosh/windfarm_lm75_sensor.c6
-rw-r--r--drivers/macintosh/windfarm_max6690_sensor.c6
-rw-r--r--drivers/macintosh/windfarm_pm112.c2
-rw-r--r--drivers/macintosh/windfarm_pm121.c2
-rw-r--r--drivers/macintosh/windfarm_pm81.c4
-rw-r--r--drivers/macintosh/windfarm_pm91.c2
-rw-r--r--drivers/macintosh/windfarm_smu_sensors.c6
-rw-r--r--drivers/md/dm-log-userspace-transfer.c10
-rw-r--r--drivers/md/dm-raid1.c2
-rw-r--r--drivers/md/dm-region-hash.c5
-rw-r--r--drivers/md/dm-snap-persistent.c2
-rw-r--r--drivers/md/dm-stripe.c2
-rw-r--r--drivers/md/dm-sysfs.c8
-rw-r--r--drivers/md/dm.c21
-rw-r--r--drivers/md/linear.c2
-rw-r--r--drivers/md/multipath.c4
-rw-r--r--drivers/md/raid0.c4
-rw-r--r--drivers/md/raid1.c4
-rw-r--r--drivers/md/raid10.c4
-rw-r--r--drivers/md/raid5.c2
-rw-r--r--drivers/media/IR/Makefile2
-rw-r--r--drivers/media/IR/ir-functions.c2
-rw-r--r--drivers/media/IR/ir-keymaps.c99
-rw-r--r--drivers/media/IR/ir-keytable.c61
-rw-r--r--drivers/media/IR/ir-sysfs.c211
-rw-r--r--drivers/media/common/saa7146_fops.c11
-rw-r--r--drivers/media/common/tuners/tuner-types.c21
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c79
-rw-r--r--drivers/media/dvb/Kconfig4
-rw-r--r--drivers/media/dvb/Makefile3
-rw-r--r--drivers/media/dvb/bt8xx/bt878.c21
-rw-r--r--drivers/media/dvb/bt8xx/dst.c12
-rw-r--r--drivers/media/dvb/dm1105/Kconfig1
-rw-r--r--drivers/media/dvb/dm1105/dm1105.c505
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c12
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c1
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.c1
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig12
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c351
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h1
-rw-r--r--drivers/media/dvb/dvb-usb/az6027.c1151
-rw-r--r--drivers/media/dvb/dvb-usb/az6027.h14
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c3
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700.h1
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c197
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c160
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h9
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-init.c2
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-remote.c1
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c95
-rw-r--r--drivers/media/dvb/dvb-usb/friio-fe.c2
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.c141
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.h2
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.c2
-rw-r--r--drivers/media/dvb/firewire/firedtv-1394.c19
-rw-r--r--drivers/media/dvb/firewire/firedtv-avc.c514
-rw-r--r--drivers/media/dvb/firewire/firedtv-dvb.c1
-rw-r--r--drivers/media/dvb/firewire/firedtv-fw.c2
-rw-r--r--drivers/media/dvb/firewire/firedtv.h6
-rw-r--r--drivers/media/dvb/frontends/af9013.h1
-rw-r--r--drivers/media/dvb/frontends/atbm8830.c16
-rw-r--r--drivers/media/dvb/frontends/dib0090.c2
-rw-r--r--drivers/media/dvb/frontends/dib8000.c2
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c2
-rw-r--r--drivers/media/dvb/frontends/l64781.c4
-rw-r--r--drivers/media/dvb/frontends/lnbp21.c3
-rw-r--r--drivers/media/dvb/frontends/si21xx.c38
-rw-r--r--drivers/media/dvb/frontends/stv0900.h2
-rw-r--r--drivers/media/dvb/frontends/stv0900_core.c109
-rw-r--r--drivers/media/dvb/frontends/stv0900_priv.h11
-rw-r--r--drivers/media/dvb/frontends/stv0900_reg.h6
-rw-r--r--drivers/media/dvb/frontends/stv0900_sw.c54
-rw-r--r--drivers/media/dvb/frontends/stv090x.c486
-rw-r--r--drivers/media/dvb/frontends/stv090x.h13
-rw-r--r--drivers/media/dvb/frontends/stv090x_priv.h17
-rw-r--r--drivers/media/dvb/frontends/stv6110x.c188
-rw-r--r--drivers/media/dvb/frontends/stv6110x.h1
-rw-r--r--drivers/media/dvb/frontends/stv6110x_priv.h1
-rw-r--r--drivers/media/dvb/frontends/tda665x.c2
-rw-r--r--drivers/media/dvb/frontends/tda8261.c2
-rw-r--r--drivers/media/dvb/frontends/zl10036.c2
-rw-r--r--drivers/media/dvb/frontends/zl10039.c1
-rw-r--r--drivers/media/dvb/mantis/mantis_hif.c2
-rw-r--r--drivers/media/dvb/mantis/mantis_input.c2
-rw-r--r--drivers/media/dvb/mantis/mantis_pci.c5
-rw-r--r--drivers/media/dvb/ngene/Kconfig9
-rw-r--r--drivers/media/dvb/ngene/Makefile11
-rw-r--r--drivers/media/dvb/ngene/ngene-core.c2024
-rw-r--r--drivers/media/dvb/ngene/ngene.h859
-rw-r--r--drivers/media/dvb/siano/sms-cards.c1
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c7
-rw-r--r--drivers/media/dvb/siano/smscoreapi.h77
-rw-r--r--drivers/media/dvb/siano/smsdvb.c318
-rw-r--r--drivers/media/dvb/siano/smsir.c6
-rw-r--r--drivers/media/dvb/ttpci/av7110_ir.c14
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c2
-rw-r--r--drivers/media/dvb/ttpci/budget.c4
-rw-r--r--drivers/media/radio/Kconfig23
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/radio-timb.c244
-rw-r--r--drivers/media/radio/saa7706h.c451
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c8
-rw-r--r--drivers/media/video/Kconfig36
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/bt819.c10
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c8
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c8
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c4
-rw-r--r--drivers/media/video/bt8xx/bttvp.h1
-rw-r--r--drivers/media/video/cafe_ccic.c6
-rw-r--r--drivers/media/video/cpia.c3
-rw-r--r--drivers/media/video/cx18/Kconfig11
-rw-r--r--drivers/media/video/cx18/Makefile2
-rw-r--r--drivers/media/video/cx18/cx18-alsa-main.c293
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.c175
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.h23
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.c354
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.h27
-rw-r--r--drivers/media/video/cx18/cx18-alsa.h75
-rw-r--r--drivers/media/video/cx18/cx18-cards.c2
-rw-r--r--drivers/media/video/cx18/cx18-driver.c70
-rw-r--r--drivers/media/video/cx18/cx18-driver.h50
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c22
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c205
-rw-r--r--drivers/media/video/cx18/cx18-fileops.h3
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c135
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c45
-rw-r--r--drivers/media/video/cx18/cx18-queue.c3
-rw-r--r--drivers/media/video/cx18/cx18-streams.c72
-rw-r--r--drivers/media/video/cx18/cx18-streams.h10
-rw-r--r--drivers/media/video/cx18/cx18-version.h2
-rw-r--r--drivers/media/video/cx18/cx23418.h3
-rw-r--r--drivers/media/video/cx231xx/cx231xx-dvb.c8
-rw-r--r--drivers/media/video/cx231xx/cx231xx-input.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c4
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c32
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c17
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c13
-rw-r--r--drivers/media/video/cx23885/cx23885.h1
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c54
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c10
-rw-r--r--drivers/media/video/cx88/cx88-cards.c23
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c214
-rw-r--r--drivers/media/video/cx88/cx88-input.c4
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c3
-rw-r--r--drivers/media/video/cx88/cx88.h1
-rw-r--r--drivers/media/video/dabusb.c2
-rw-r--r--drivers/media/video/davinci/Makefile1
-rw-r--r--drivers/media/video/davinci/dm355_ccdc.c410
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc.c361
-rw-r--r--drivers/media/video/davinci/isif.c1172
-rw-r--r--drivers/media/video/davinci/isif_regs.h269
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c131
-rw-r--r--drivers/media/video/davinci/vpss.c289
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c80
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c19
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c3
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c113
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h4
-rw-r--r--drivers/media/video/em28xx/em28xx-vbi.c17
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c58
-rw-r--r--drivers/media/video/em28xx/em28xx.h6
-rw-r--r--drivers/media/video/et61x251/Kconfig6
-rw-r--r--drivers/media/video/gspca/Kconfig46
-rw-r--r--drivers/media/video/gspca/Makefile8
-rw-r--r--drivers/media/video/gspca/benq.c322
-rw-r--r--drivers/media/video/gspca/coarse_expo_autogain.h116
-rw-r--r--drivers/media/video/gspca/conex.c4
-rw-r--r--drivers/media/video/gspca/cpia1.c2022
-rw-r--r--drivers/media/video/gspca/etoms.c4
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c10
-rw-r--r--drivers/media/video/gspca/gspca.c259
-rw-r--r--drivers/media/video/gspca/gspca.h30
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c4
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.c2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.h2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c2
-rw-r--r--drivers/media/video/gspca/mars.c2
-rw-r--r--drivers/media/video/gspca/mr97310a.c226
-rw-r--r--drivers/media/video/gspca/ov519.c151
-rw-r--r--drivers/media/video/gspca/ov534.c1253
-rw-r--r--drivers/media/video/gspca/ov534_9.c1477
-rw-r--r--drivers/media/video/gspca/pac207.c25
-rw-r--r--drivers/media/video/gspca/pac7302.c411
-rw-r--r--drivers/media/video/gspca/pac7311.c251
-rw-r--r--drivers/media/video/gspca/pac_common.h9
-rw-r--r--drivers/media/video/gspca/sn9c2028.c757
-rw-r--r--drivers/media/video/gspca/sn9c2028.h51
-rw-r--r--drivers/media/video/gspca/sn9c20x.c33
-rw-r--r--drivers/media/video/gspca/sonixb.c451
-rw-r--r--drivers/media/video/gspca/sonixj.c341
-rw-r--r--drivers/media/video/gspca/spca500.c4
-rw-r--r--drivers/media/video/gspca/spca501.c2
-rw-r--r--drivers/media/video/gspca/spca505.c2
-rw-r--r--drivers/media/video/gspca/spca506.c4
-rw-r--r--drivers/media/video/gspca/spca508.c2
-rw-r--r--drivers/media/video/gspca/spca561.c4
-rw-r--r--drivers/media/video/gspca/sq905c.c45
-rw-r--r--drivers/media/video/gspca/stk014.c2
-rw-r--r--drivers/media/video/gspca/stv0680.c16
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c32
-rw-r--r--drivers/media/video/gspca/sunplus.c186
-rw-r--r--drivers/media/video/gspca/t613.c51
-rw-r--r--drivers/media/video/gspca/tv8532.c2
-rw-r--r--drivers/media/video/gspca/vc032x.c694
-rw-r--r--drivers/media/video/gspca/zc3xx.c679
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c5
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c5
-rw-r--r--drivers/media/video/hdpvr/hdpvr.h1
-rw-r--r--drivers/media/video/hexium_gemini.c9
-rw-r--r--drivers/media/video/hexium_orion.c4
-rw-r--r--drivers/media/video/ir-kbd-i2c.c5
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c48
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h4
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c1
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c14
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.c9
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.h3
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c6
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c1
-rw-r--r--drivers/media/video/mt9t112.c2
-rw-r--r--drivers/media/video/mt9v022.c17
-rw-r--r--drivers/media/video/mxb.c10
-rw-r--r--drivers/media/video/ov772x.c22
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h12
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c53
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h1
-rw-r--r--drivers/media/video/pwc/pwc-ctrl.c2
-rw-r--r--drivers/media/video/pxa_camera.c10
-rw-r--r--drivers/media/video/rj54n1cb0c.c12
-rw-r--r--drivers/media/video/saa7115.c27
-rw-r--r--drivers/media/video/saa7127.c47
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c52
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c7
-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-api.c2
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c18
-rw-r--r--drivers/media/video/sn9c102/Kconfig5
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h6
-rw-r--r--drivers/media/video/soc_camera.c36
-rw-r--r--drivers/media/video/soc_mediabus.c48
-rw-r--r--drivers/media/video/tlg2300/Kconfig16
-rw-r--r--drivers/media/video/tlg2300/Makefile9
-rw-r--r--drivers/media/video/tlg2300/pd-alsa.c332
-rw-r--r--drivers/media/video/tlg2300/pd-common.h282
-rw-r--r--drivers/media/video/tlg2300/pd-dvb.c593
-rw-r--r--drivers/media/video/tlg2300/pd-main.c539
-rw-r--r--drivers/media/video/tlg2300/pd-radio.c420
-rw-r--r--drivers/media/video/tlg2300/pd-video.c1667
-rw-r--r--drivers/media/video/tlg2300/vendorcmds.h243
-rw-r--r--drivers/media/video/tuner-core.c1
-rw-r--r--drivers/media/video/tveeprom.c5
-rw-r--r--drivers/media/video/tvp7002.c1187
-rw-r--r--drivers/media/video/tvp7002_reg.h150
-rw-r--r--drivers/media/video/tw9910.c8
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c2
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c242
-rw-r--r--drivers/media/video/uvc/uvc_driver.c74
-rw-r--r--drivers/media/video/uvc/uvc_queue.c1
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c4
-rw-r--r--drivers/media/video/uvc/uvc_video.c10
-rw-r--r--drivers/media/video/uvc/uvcvideo.h19
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c5
-rw-r--r--drivers/media/video/videobuf-dma-sg.c2
-rw-r--r--drivers/media/video/videobuf-vmalloc.c2
-rw-r--r--drivers/media/video/vivi.c2
-rw-r--r--drivers/media/video/zc0301/Kconfig6
-rw-r--r--drivers/media/video/zoran/zoran_device.c6
-rw-r--r--drivers/media/video/zoran/zoran_driver.c4
-rw-r--r--drivers/media/video/zr364xx.c37
-rw-r--r--drivers/memstick/core/mspro_block.c5
-rw-r--r--drivers/message/fusion/mptbase.c5
-rw-r--r--drivers/message/fusion/mptbase.h4
-rw-r--r--drivers/message/fusion/mptctl.c4
-rw-r--r--drivers/message/fusion/mptfc.c17
-rw-r--r--drivers/message/fusion/mptsas.c211
-rw-r--r--drivers/message/fusion/mptscsih.c9
-rw-r--r--drivers/message/i2o/i2o_block.c5
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/timberdale.c727
-rw-r--r--drivers/mfd/timberdale.h130
-rw-r--r--drivers/mfd/twl-core.c18
-rw-r--r--drivers/mmc/card/queue.c10
-rw-r--r--drivers/mmc/host/au1xmmc.c12
-rw-r--r--drivers/mmc/host/mmci.c41
-rw-r--r--drivers/mtd/maps/Kconfig6
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/alchemy-flash.c166
-rw-r--r--drivers/mtd/nand/Kconfig4
-rw-r--r--drivers/mtd/nand/au1550nd.c4
-rw-r--r--drivers/mtd/nand/sh_flctl.c69
-rw-r--r--drivers/net/au1000_eth.c441
-rw-r--r--drivers/net/au1000_eth.h9
-rw-r--r--drivers/net/benet/be_cmds.c1
-rw-r--r--drivers/net/cpmac.c10
-rw-r--r--drivers/net/e1000/e1000_main.c19
-rw-r--r--drivers/net/irda/au1k_ir.c14
-rw-r--r--drivers/net/ixgbe/ixgbe_82598.c22
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c4
-rw-r--r--drivers/net/mace.c2
-rw-r--r--drivers/net/macmace.c33
-rw-r--r--drivers/net/macsonic.c33
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c6
-rw-r--r--drivers/net/sfc/efx.c1
-rw-r--r--drivers/net/sfc/falcon_boards.c45
-rw-r--r--drivers/net/sfc/mcdi.c2
-rw-r--r--drivers/net/sfc/qt202x_phy.c2
-rw-r--r--drivers/net/tc35815.c1
-rw-r--r--drivers/net/usb/cdc_ether.c5
-rw-r--r--drivers/net/via-velocity.c41
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c4
-rw-r--r--drivers/net/wireless/b43/b43.h1
-rw-r--r--drivers/net/wireless/b43/main.c13
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c22
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c2
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c1
-rw-r--r--drivers/of/Kconfig8
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/base.c318
-rw-r--r--drivers/of/fdt.c590
-rw-r--r--drivers/of/gpio.c13
-rw-r--r--drivers/of/of_i2c.c4
-rw-r--r--drivers/of/of_mdio.c8
-rw-r--r--drivers/of/of_spi.c6
-rw-r--r--drivers/pci/Kconfig11
-rw-r--r--drivers/pci/Makefile5
-rw-r--r--drivers/pci/bus.c56
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c2
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c6
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c1
-rw-r--r--drivers/pci/hotplug/cpqphp.h2
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c57
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c27
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c106
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c13
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c1
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c132
-rw-r--r--drivers/pci/hotplug/pciehp_core.c25
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c1
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c72
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c23
-rw-r--r--drivers/pci/hotplug/rpaphp_core.c24
-rw-r--r--drivers/pci/hotplug/shpchp.h2
-rw-r--r--drivers/pci/hotplug/shpchp_core.c35
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c14
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c149
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c9
-rw-r--r--drivers/pci/legacy.c34
-rw-r--r--drivers/pci/pci-acpi.c211
-rw-r--r--drivers/pci/pci-driver.c160
-rw-r--r--drivers/pci/pci.c183
-rw-r--r--drivers/pci/pci.h16
-rw-r--r--drivers/pci/pcie/Kconfig4
-rw-r--r--drivers/pci/pcie/Makefile2
-rw-r--r--drivers/pci/pcie/pme/Makefile8
-rw-r--r--drivers/pci/pcie/pme/pcie_pme.c505
-rw-r--r--drivers/pci/pcie/pme/pcie_pme.h28
-rw-r--r--drivers/pci/pcie/pme/pcie_pme_acpi.c54
-rw-r--r--drivers/pci/pcie/portdrv.h17
-rw-r--r--drivers/pci/pcie/portdrv_core.c13
-rw-r--r--drivers/pci/pcie/portdrv_pci.c27
-rw-r--r--drivers/pci/probe.c293
-rw-r--r--drivers/pci/quirks.c14
-rw-r--r--drivers/pci/setup-bus.c514
-rw-r--r--drivers/pci/slot.c55
-rw-r--r--drivers/pcmcia/Kconfig30
-rw-r--r--drivers/pcmcia/Makefile16
-rw-r--r--drivers/pcmcia/at91_cf.c2
-rw-r--r--drivers/pcmcia/au1000_db1x00.c305
-rw-r--r--drivers/pcmcia/au1000_generic.c10
-rw-r--r--drivers/pcmcia/au1000_generic.h18
-rw-r--r--drivers/pcmcia/au1000_pb1x00.c119
-rw-r--r--drivers/pcmcia/au1000_xxs1500.c188
-rw-r--r--drivers/pcmcia/bfin_cf_pcmcia.c2
-rw-r--r--drivers/pcmcia/cardbus.c175
-rw-r--r--drivers/pcmcia/cistpl.c606
-rw-r--r--drivers/pcmcia/cs.c312
-rw-r--r--drivers/pcmcia/cs_internal.h89
-rw-r--r--drivers/pcmcia/db1xxx_ss.c623
-rw-r--r--drivers/pcmcia/ds.c333
-rw-r--r--drivers/pcmcia/electra_cf.c2
-rw-r--r--drivers/pcmcia/i82365.h4
-rw-r--r--drivers/pcmcia/m32r_cfc.c2
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c4
-rw-r--r--drivers/pcmcia/o2micro.h45
-rw-r--r--drivers/pcmcia/omap_cf.c2
-rw-r--r--drivers/pcmcia/pcmcia_ioctl.c42
-rw-r--r--drivers/pcmcia/pcmcia_resource.c169
-rw-r--r--drivers/pcmcia/rsrc_mgr.c61
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c310
-rw-r--r--drivers/pcmcia/socket_sysfs.c196
-rw-r--r--drivers/pcmcia/xxs1500_ss.c350
-rw-r--r--drivers/pcmcia/yenta_socket.c10
-rw-r--r--drivers/platform/x86/Kconfig1
-rw-r--r--drivers/platform/x86/acer-wmi.c2
-rw-r--r--drivers/platform/x86/classmate-laptop.c31
-rw-r--r--drivers/platform/x86/compal-laptop.c247
-rw-r--r--drivers/platform/x86/dell-laptop.c256
-rw-r--r--drivers/platform/x86/hp-wmi.c1
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c2
-rw-r--r--drivers/platform/x86/toshiba_acpi.c206
-rw-r--r--drivers/ps3/ps3av.c2
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-mpc5121.c387
-rw-r--r--drivers/rtc/rtc-pl031.c365
-rw-r--r--drivers/s390/block/dasd.c53
-rw-r--r--drivers/s390/block/dasd_devmap.c13
-rw-r--r--drivers/s390/block/dasd_genhd.c1
-rw-r--r--drivers/s390/block/dasd_int.h1
-rw-r--r--drivers/s390/block/dasd_proc.c109
-rw-r--r--drivers/s390/char/tape_block.c5
-rw-r--r--drivers/s390/char/zcore.c163
-rw-r--r--drivers/s390/cio/ccwreq.c2
-rw-r--r--drivers/s390/cio/chsc.c2
-rw-r--r--drivers/s390/cio/chsc_sch.c4
-rw-r--r--drivers/s390/cio/cio.c14
-rw-r--r--drivers/s390/cio/crw.c29
-rw-r--r--drivers/s390/cio/css.c79
-rw-r--r--drivers/s390/cio/css.h5
-rw-r--r--drivers/s390/cio/device.c160
-rw-r--r--drivers/s390/cio/device.h3
-rw-r--r--drivers/s390/cio/device_fsm.c43
-rw-r--r--drivers/s390/cio/qdio.h92
-rw-r--r--drivers/s390/cio/qdio_debug.c23
-rw-r--r--drivers/s390/cio/qdio_main.c28
-rw-r--r--drivers/s390/cio/qdio_setup.c20
-rw-r--r--drivers/s390/cio/qdio_thinint.c4
-rw-r--r--drivers/s390/crypto/zcrypt_api.c158
-rw-r--r--drivers/s390/kvm/kvm_virtio.c4
-rw-r--r--drivers/s390/scsi/zfcp_aux.c90
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c11
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c20
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h34
-rw-r--r--drivers/s390/scsi/zfcp_def.h114
-rw-r--r--drivers/s390/scsi/zfcp_erp.c36
-rw-r--r--drivers/s390/scsi/zfcp_ext.h9
-rw-r--r--drivers/s390/scsi/zfcp_fc.c23
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c163
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c50
-rw-r--r--drivers/s390/scsi/zfcp_qdio.h109
-rw-r--r--drivers/s390/scsi/zfcp_reqlist.h183
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c38
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c37
-rw-r--r--drivers/sbus/char/openprom.c10
-rw-r--r--drivers/scsi/FlashPoint.c2
-rw-r--r--drivers/scsi/arm/fas216.c2
-rw-r--r--drivers/scsi/be2iscsi/be.h21
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c88
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h14
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c136
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h2
-rw-r--r--drivers/scsi/be2iscsi/be_main.c488
-rw-r--r--drivers/scsi/be2iscsi/be_main.h27
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c139
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h6
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c5
-rw-r--r--drivers/scsi/constants.c20
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c17
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.c7
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_pdu.c6
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c2
-rw-r--r--drivers/scsi/eata.c2
-rw-r--r--drivers/scsi/esp_scsi.c14
-rw-r--r--drivers/scsi/fcoe/fcoe.c18
-rw-r--r--drivers/scsi/fcoe/libfcoe.c2
-rw-r--r--drivers/scsi/fnic/fnic.h2
-rw-r--r--drivers/scsi/fnic/fnic_main.c4
-rw-r--r--drivers/scsi/fnic/vnic_devcmd.h2
-rw-r--r--drivers/scsi/gdth.c430
-rw-r--r--drivers/scsi/gdth.h952
-rw-r--r--drivers/scsi/gdth_ioctl.h366
-rw-r--r--drivers/scsi/gdth_proc.c42
-rw-r--r--drivers/scsi/gdth_proc.h4
-rw-r--r--drivers/scsi/hosts.c4
-rw-r--r--drivers/scsi/hpsa.c793
-rw-r--r--drivers/scsi/hpsa.h136
-rw-r--r--drivers/scsi/hpsa_cmd.h204
-rw-r--r--drivers/scsi/ibmmca.c2
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c4
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c2
-rw-r--r--drivers/scsi/ipr.c2
-rw-r--r--drivers/scsi/iscsi_tcp.c8
-rw-r--r--drivers/scsi/libfc/fc_exch.c2
-rw-r--r--drivers/scsi/libfc/fc_fcp.c3
-rw-r--r--drivers/scsi/libfc/fc_lport.c3
-rw-r--r--drivers/scsi/libfc/fc_rport.c2
-rw-r--r--drivers/scsi/libiscsi.c53
-rw-r--r--drivers/scsi/libiscsi_tcp.c8
-rw-r--r--drivers/scsi/libsrp.c8
-rw-r--r--drivers/scsi/lpfc/lpfc.h14
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c118
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c2473
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.h98
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h22
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c15
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c145
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c735
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h23
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h265
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c547
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c111
-rw-r--r--drivers/scsi/lpfc/lpfc_nl.h22
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c85
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c46
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c329
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h10
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h82
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c7
-rw-r--r--drivers/scsi/mac_esp.c152
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.c264
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h36
-rw-r--r--drivers/scsi/mpt2sas/Kconfig1
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2.h16
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h25
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_history.txt93
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_init.h24
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_ioc.h77
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_sas.h6
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c18
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h14
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_config.c51
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c13
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c266
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c196
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c2
-rw-r--r--drivers/scsi/pmcraid.c2
-rw-r--r--drivers/scsi/qla1280.c4
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c732
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h155
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h33
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h7
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c32
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c120
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c110
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c151
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c135
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h6
-rw-r--r--drivers/scsi/qla4xxx/ql4_init.c14
-rw-r--r--drivers/scsi/raid_class.c1
-rw-r--r--drivers/scsi/scsi.c42
-rw-r--r--drivers/scsi/scsi_lib.c16
-rw-r--r--drivers/scsi/scsi_sas_internal.h2
-rw-r--r--drivers/scsi/scsi_scan.c11
-rw-r--r--drivers/scsi/scsi_sysfs.c22
-rw-r--r--drivers/scsi/scsi_transport_fc.c26
-rw-r--r--drivers/scsi/scsi_transport_sas.c103
-rw-r--r--drivers/scsi/sd.c54
-rw-r--r--drivers/scsi/ses.c10
-rw-r--r--drivers/scsi/sg.c6
-rw-r--r--drivers/scsi/st.c3
-rw-r--r--drivers/scsi/u14-34f.c2
-rw-r--r--drivers/scsi/vmw_pvscsi.c3
-rw-r--r--drivers/serial/8250.c22
-rw-r--r--drivers/serial/Kconfig12
-rw-r--r--drivers/serial/amba-pl011.c19
-rw-r--r--drivers/serial/mpc52xx_uart.c251
-rw-r--r--drivers/serial/pmac_zilog.c246
-rw-r--r--drivers/serial/pmac_zilog.h34
-rw-r--r--drivers/serial/serial_cs.c7
-rw-r--r--drivers/serial/sh-sci.h220
-rw-r--r--drivers/sh/intc.c266
-rw-r--r--drivers/sh/pfc.c37
-rw-r--r--drivers/spi/Kconfig23
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/amba-pl022.c18
-rw-r--r--drivers/spi/au1550_spi.c6
-rw-r--r--drivers/spi/coldfire_qspi.c640
-rw-r--r--drivers/spi/davinci_spi.c1255
-rw-r--r--drivers/spi/dw_spi.c111
-rw-r--r--drivers/spi/dw_spi_mmio.c147
-rw-r--r--drivers/spi/dw_spi_pci.c2
-rw-r--r--drivers/spi/mpc52xx_psc_spi.c2
-rw-r--r--drivers/spi/mpc52xx_spi.c2
-rw-r--r--drivers/spi/spi_imx.c2
-rw-r--r--drivers/spi/spi_mpc8xxx.c8
-rw-r--r--drivers/spi/spi_ppc4xx.c2
-rw-r--r--drivers/spi/spi_s3c64xx.c89
-rw-r--r--drivers/spi/spi_sh_msiof.c2
-rw-r--r--drivers/spi/spi_stmp.c2
-rw-r--r--drivers/spi/xilinx_spi.c28
-rw-r--r--drivers/spi/xilinx_spi_of.c2
-rw-r--r--drivers/ssb/main.c3
-rw-r--r--drivers/staging/go7007/s2250-board.c2
-rw-r--r--drivers/staging/hv/blkvsc_drv.c5
-rw-r--r--drivers/staging/octeon/Makefile1
-rw-r--r--drivers/staging/octeon/ethernet-defines.h34
-rw-r--r--drivers/staging/octeon/ethernet-mdio.c6
-rw-r--r--drivers/staging/octeon/ethernet-mdio.h1
-rw-r--r--drivers/staging/octeon/ethernet-mem.c124
-rw-r--r--drivers/staging/octeon/ethernet-proc.c144
-rw-r--r--drivers/staging/octeon/ethernet-proc.h29
-rw-r--r--drivers/staging/octeon/ethernet-rgmii.c56
-rw-r--r--drivers/staging/octeon/ethernet-rx.c384
-rw-r--r--drivers/staging/octeon/ethernet-rx.h25
-rw-r--r--drivers/staging/octeon/ethernet-sgmii.c1
-rw-r--r--drivers/staging/octeon/ethernet-spi.c1
-rw-r--r--drivers/staging/octeon/ethernet-tx.c441
-rw-r--r--drivers/staging/octeon/ethernet-tx.h29
-rw-r--r--drivers/staging/octeon/ethernet-util.h13
-rw-r--r--drivers/staging/octeon/ethernet-xaui.c1
-rw-r--r--drivers/staging/octeon/ethernet.c254
-rw-r--r--drivers/staging/octeon/octeon-ethernet.h58
-rw-r--r--drivers/staging/sm7xx/smtc2d.c2
-rw-r--r--drivers/staging/sm7xx/smtc2d.h2
-rw-r--r--drivers/staging/sm7xx/smtcfb.c2
-rw-r--r--drivers/staging/sm7xx/smtcfb.h2
-rw-r--r--drivers/usb/core/devio.c48
-rw-r--r--drivers/usb/core/driver.c8
-rw-r--r--drivers/usb/core/endpoint.c1
-rw-r--r--drivers/usb/core/hcd-pci.c127
-rw-r--r--drivers/usb/core/hub.c1
-rw-r--r--drivers/usb/core/message.c1
-rw-r--r--drivers/usb/gadget/f_audio.c6
-rw-r--r--drivers/usb/gadget/f_eem.c3
-rw-r--r--drivers/usb/gadget/gmidi.c2
-rw-r--r--drivers/usb/gadget/multi.c2
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c1
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c1
-rw-r--r--drivers/usb/host/ehci-hub.c13
-rw-r--r--drivers/usb/host/fhci-tds.c6
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c1
-rw-r--r--drivers/usb/otg/Kconfig1
-rw-r--r--drivers/usb/serial/ftdi_sio.c25
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h18
-rw-r--r--drivers/usb/serial/sierra.c1
-rw-r--r--drivers/usb/storage/scsiglue.c6
-rw-r--r--drivers/usb/storage/unusual_devs.h2
-rw-r--r--drivers/usb/storage/usb.c2
-rw-r--r--drivers/video/aty/aty128fb.c14
-rw-r--r--drivers/video/aty/atyfb_base.c10
-rw-r--r--drivers/video/aty/radeon_backlight.c6
-rw-r--r--drivers/video/efifb.c11
-rw-r--r--drivers/video/fsl-diu-fb.c5
-rw-r--r--drivers/video/macfb.c736
-rw-r--r--drivers/video/macmodes.c80
-rw-r--r--drivers/video/pvr2fb.c2
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c88
-rw-r--r--drivers/video/valkyriefb.c6
-rw-r--r--drivers/video/valkyriefb.h31
-rw-r--r--drivers/virtio/virtio_balloon.c109
-rw-r--r--drivers/virtio/virtio_pci.c2
-rw-r--r--drivers/virtio/virtio_ring.c59
-rw-r--r--drivers/watchdog/ar7_wdt.c18
-rw-r--r--drivers/watchdog/bfin_wdt.c13
-rw-r--r--drivers/watchdog/pnx4008_wdt.c39
849 files changed, 55543 insertions, 15816 deletions
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 0bba148a2c6..4ced54f7a5d 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -76,12 +76,9 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
* evgpe - GPE handling and dispatch
*/
acpi_status
-acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
- u8 type);
+acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info);
-acpi_status
-acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info,
- u8 write_to_hardware);
+acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info);
@@ -122,9 +119,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info,
u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list);
acpi_status
-acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type);
-
-acpi_status
acpi_ev_check_for_wake_only_gpe(struct acpi_gpe_event_info *gpe_event_info);
acpi_status acpi_ev_gpe_initialize(void);
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 81e64f47867..13cb80caacd 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -426,6 +426,8 @@ struct acpi_gpe_event_info {
struct acpi_gpe_register_info *register_info; /* Backpointer to register info */
u8 flags; /* Misc info about this GPE */
u8 gpe_number; /* This GPE */
+ u8 runtime_count;
+ u8 wakeup_count;
};
/* Information about a GPE register pair, one per each status/enable pair in an array */
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 64062b1be3e..07f6e2ea2ee 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -287,8 +287,10 @@ struct acpi_object_buffer_field {
struct acpi_object_notify_handler {
ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */
+ u32 handler_type;
acpi_notify_handler handler;
void *context;
+ struct acpi_object_notify_handler *next;
};
struct acpi_object_addr_handler {
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index afacf4416c7..0b453467a5a 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -54,54 +54,9 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context);
/*******************************************************************************
*
- * FUNCTION: acpi_ev_set_gpe_type
- *
- * PARAMETERS: gpe_event_info - GPE to set
- * Type - New type
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the new type for the GPE (wake, run, or wake/run)
- *
- ******************************************************************************/
-
-acpi_status
-acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(ev_set_gpe_type);
-
- /* Validate type and update register enable masks */
-
- switch (type) {
- case ACPI_GPE_TYPE_WAKE:
- case ACPI_GPE_TYPE_RUNTIME:
- case ACPI_GPE_TYPE_WAKE_RUN:
- break;
-
- default:
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
-
- /* Disable the GPE if currently enabled */
-
- status = acpi_ev_disable_gpe(gpe_event_info);
-
- /* Clear the type bits and insert the new Type */
-
- gpe_event_info->flags &= ~ACPI_GPE_TYPE_MASK;
- gpe_event_info->flags |= type;
- return_ACPI_STATUS(status);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_ev_update_gpe_enable_masks
*
* PARAMETERS: gpe_event_info - GPE to update
- * Type - What to do: ACPI_GPE_DISABLE or
- * ACPI_GPE_ENABLE
*
* RETURN: Status
*
@@ -110,8 +65,7 @@ acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type)
******************************************************************************/
acpi_status
-acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
- u8 type)
+acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info)
{
struct acpi_gpe_register_info *gpe_register_info;
u8 register_bit;
@@ -127,37 +81,14 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
(1 <<
(gpe_event_info->gpe_number - gpe_register_info->base_gpe_number));
- /* 1) Disable case. Simply clear all enable bits */
-
- if (type == ACPI_GPE_DISABLE) {
- ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
- register_bit);
- ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
- return_ACPI_STATUS(AE_OK);
- }
-
- /* 2) Enable case. Set/Clear the appropriate enable bits */
+ ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, register_bit);
+ ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
- switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
- case ACPI_GPE_TYPE_WAKE:
- ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit);
- ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
- break;
-
- case ACPI_GPE_TYPE_RUNTIME:
- ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
- register_bit);
+ if (gpe_event_info->runtime_count)
ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit);
- break;
- case ACPI_GPE_TYPE_WAKE_RUN:
+ if (gpe_event_info->wakeup_count)
ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit);
- ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit);
- break;
-
- default:
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
return_ACPI_STATUS(AE_OK);
}
@@ -167,8 +98,6 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
* FUNCTION: acpi_ev_enable_gpe
*
* PARAMETERS: gpe_event_info - GPE to enable
- * write_to_hardware - Enable now, or just mark data structs
- * (WAKE GPEs should be deferred)
*
* RETURN: Status
*
@@ -176,9 +105,7 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
*
******************************************************************************/
-acpi_status
-acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info,
- u8 write_to_hardware)
+acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
{
acpi_status status;
@@ -186,47 +113,20 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info,
/* Make sure HW enable masks are updated */
- status =
- acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_ENABLE);
- if (ACPI_FAILURE(status)) {
+ status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
+ if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
- }
/* Mark wake-enabled or HW enable, or both */
- switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
- case ACPI_GPE_TYPE_WAKE:
-
- ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
- break;
-
- case ACPI_GPE_TYPE_WAKE_RUN:
-
- ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
-
- /*lint -fallthrough */
-
- case ACPI_GPE_TYPE_RUNTIME:
-
- ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);
-
- if (write_to_hardware) {
-
- /* Clear the GPE (of stale events), then enable it */
-
- status = acpi_hw_clear_gpe(gpe_event_info);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Enable the requested runtime GPE */
-
- status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
- }
- break;
+ if (gpe_event_info->runtime_count) {
+ /* Clear the GPE (of stale events), then enable it */
+ status = acpi_hw_clear_gpe(gpe_event_info);
+ if (ACPI_FAILURE(status))
+ return_ACPI_STATUS(status);
- default:
- return_ACPI_STATUS(AE_BAD_PARAMETER);
+ /* Enable the requested runtime GPE */
+ status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
}
return_ACPI_STATUS(AE_OK);
@@ -252,34 +152,9 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
/* Make sure HW enable masks are updated */
- status =
- acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_DISABLE);
- if (ACPI_FAILURE(status)) {
+ status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
+ if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
- }
-
- /* Clear the appropriate enabled flags for this GPE */
-
- switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
- case ACPI_GPE_TYPE_WAKE:
- ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
- break;
-
- case ACPI_GPE_TYPE_WAKE_RUN:
- ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
-
- /* fallthrough */
-
- case ACPI_GPE_TYPE_RUNTIME:
-
- /* Disable the requested runtime GPE */
-
- ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);
- break;
-
- default:
- break;
- }
/*
* Even if we don't know the GPE type, make sure that we always
@@ -521,7 +396,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
/* Set the GPE flags for return to enabled state */
- (void)acpi_ev_enable_gpe(gpe_event_info, FALSE);
+ (void)acpi_ev_update_gpe_enable_masks(gpe_event_info);
/*
* Take a snapshot of the GPE info for this level - we copy the info to
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index 24792090018..3d4c4aca11c 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -258,7 +258,6 @@ acpi_ev_save_method_info(acpi_handle obj_handle,
u32 gpe_number;
char name[ACPI_NAME_SIZE + 1];
u8 type;
- acpi_status status;
ACPI_FUNCTION_TRACE(ev_save_method_info);
@@ -325,26 +324,20 @@ acpi_ev_save_method_info(acpi_handle obj_handle,
/*
* Now we can add this information to the gpe_event_info block for use
- * during dispatch of this GPE. Default type is RUNTIME, although this may
- * change when the _PRW methods are executed later.
+ * during dispatch of this GPE.
*/
gpe_event_info =
&gpe_block->event_info[gpe_number - gpe_block->block_base_number];
- gpe_event_info->flags = (u8)
- (type | ACPI_GPE_DISPATCH_METHOD | ACPI_GPE_TYPE_RUNTIME);
+ gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD);
gpe_event_info->dispatch.method_node =
(struct acpi_namespace_node *)obj_handle;
- /* Update enable mask, but don't enable the HW GPE as of yet */
-
- status = acpi_ev_enable_gpe(gpe_event_info, FALSE);
-
ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
"Registered GPE method %s as GPE number 0x%.2X\n",
name, gpe_number));
- return_ACPI_STATUS(status);
+ return_ACPI_STATUS(AE_OK);
}
/*******************************************************************************
@@ -454,20 +447,7 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle,
gpe_block->
block_base_number];
- /* Mark GPE for WAKE-ONLY but WAKE_DISABLED */
-
- gpe_event_info->flags &=
- ~(ACPI_GPE_WAKE_ENABLED | ACPI_GPE_RUN_ENABLED);
-
- status =
- acpi_ev_set_gpe_type(gpe_event_info, ACPI_GPE_TYPE_WAKE);
- if (ACPI_FAILURE(status)) {
- goto cleanup;
- }
-
- status =
- acpi_ev_update_gpe_enable_masks(gpe_event_info,
- ACPI_GPE_DISABLE);
+ gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
}
cleanup:
@@ -989,7 +969,6 @@ acpi_status
acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
struct acpi_gpe_block_info *gpe_block)
{
- acpi_status status;
struct acpi_gpe_event_info *gpe_event_info;
struct acpi_gpe_walk_info gpe_info;
u32 wake_gpe_count;
@@ -1019,42 +998,50 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
gpe_info.gpe_block = gpe_block;
gpe_info.gpe_device = gpe_device;
- status =
- acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
acpi_ev_match_prw_and_gpe, NULL,
&gpe_info, NULL);
}
/*
- * Enable all GPEs in this block that have these attributes:
- * 1) are "runtime" or "run/wake" GPEs, and
- * 2) have a corresponding _Lxx or _Exx method
- *
- * Any other GPEs within this block must be enabled via the
- * acpi_enable_gpe() external interface.
+ * Enable all GPEs that have a corresponding method and aren't
+ * capable of generating wakeups. Any other GPEs within this block
+ * must be enabled via the acpi_enable_gpe() interface.
*/
wake_gpe_count = 0;
gpe_enabled_count = 0;
+ if (gpe_device == acpi_gbl_fadt_gpe_device)
+ gpe_device = NULL;
for (i = 0; i < gpe_block->register_count; i++) {
- for (j = 0; j < 8; j++) {
+ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
+ acpi_status status;
+ acpi_size gpe_index;
+ int gpe_number;
/* Get the info block for this particular GPE */
+ gpe_index = (acpi_size)i * ACPI_GPE_REGISTER_WIDTH + j;
+ gpe_event_info = &gpe_block->event_info[gpe_index];
- gpe_event_info = &gpe_block->event_info[((acpi_size) i *
- ACPI_GPE_REGISTER_WIDTH)
- + j];
-
- if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_METHOD) &&
- (gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) {
- gpe_enabled_count++;
- }
-
- if (gpe_event_info->flags & ACPI_GPE_TYPE_WAKE) {
+ if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) {
wake_gpe_count++;
+ if (acpi_gbl_leave_wake_gpes_disabled)
+ continue;
}
+
+ if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD))
+ continue;
+
+ gpe_number = gpe_index + gpe_block->block_base_number;
+ status = acpi_enable_gpe(gpe_device, gpe_number,
+ ACPI_GPE_TYPE_RUNTIME);
+ if (ACPI_FAILURE(status))
+ ACPI_ERROR((AE_INFO,
+ "Failed to enable GPE %02X\n",
+ gpe_number));
+ else
+ gpe_enabled_count++;
}
}
@@ -1062,15 +1049,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
"Found %u Wake, Enabled %u Runtime GPEs in this block\n",
wake_gpe_count, gpe_enabled_count));
- /* Enable all valid runtime GPEs found above */
-
- status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block, NULL);
- if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO, "Could not enable GPEs in GpeBlock %p",
- gpe_block));
- }
-
- return_ACPI_STATUS(status);
+ return_ACPI_STATUS(AE_OK);
}
/*******************************************************************************
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index ce224e1eaa8..8f0fac6c436 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -259,9 +259,15 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context)
handler_obj = notify_info->notify.handler_obj;
if (handler_obj) {
- handler_obj->notify.handler(notify_info->notify.node,
- notify_info->notify.value,
- handler_obj->notify.context);
+ struct acpi_object_notify_handler *notifier;
+
+ notifier = &handler_obj->notify;
+ while (notifier) {
+ notifier->handler(notify_info->notify.node,
+ notify_info->notify.value,
+ notifier->context);
+ notifier = notifier->next;
+ }
}
/* All done with the info object */
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 2fe0809d4eb..474e2cab603 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -218,6 +218,72 @@ ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler)
/*******************************************************************************
*
+ * FUNCTION: acpi_populate_handler_object
+ *
+ * PARAMETERS: handler_obj - Handler object to populate
+ * handler_type - The type of handler:
+ * ACPI_SYSTEM_NOTIFY: system_handler (00-7f)
+ * ACPI_DEVICE_NOTIFY: driver_handler (80-ff)
+ * ACPI_ALL_NOTIFY: both system and device
+ * handler - Address of the handler
+ * context - Value passed to the handler on each GPE
+ * next - Address of a handler object to link to
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Populate a handler object.
+ *
+ ******************************************************************************/
+static void
+acpi_populate_handler_object(struct acpi_object_notify_handler *handler_obj,
+ u32 handler_type,
+ acpi_notify_handler handler, void *context,
+ struct acpi_object_notify_handler *next)
+{
+ handler_obj->handler_type = handler_type;
+ handler_obj->handler = handler;
+ handler_obj->context = context;
+ handler_obj->next = next;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_add_handler_object
+ *
+ * PARAMETERS: parent_obj - Parent of the new object
+ * handler - Address of the handler
+ * context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new handler object and populate it.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj,
+ acpi_notify_handler handler, void *context)
+{
+ struct acpi_object_notify_handler *handler_obj;
+
+ /* The parent must not be a defice notify handler object. */
+ if (parent_obj->handler_type & ACPI_DEVICE_NOTIFY)
+ return AE_BAD_PARAMETER;
+
+ handler_obj = ACPI_ALLOCATE_ZEROED(sizeof(*handler_obj));
+ if (!handler_obj)
+ return AE_NO_MEMORY;
+
+ acpi_populate_handler_object(handler_obj,
+ ACPI_SYSTEM_NOTIFY,
+ handler, context,
+ parent_obj->next);
+ parent_obj->next = handler_obj;
+
+ return AE_OK;
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_install_notify_handler
*
* PARAMETERS: Device - The device for which notifies will be handled
@@ -316,15 +382,32 @@ acpi_install_notify_handler(acpi_handle device,
obj_desc = acpi_ns_get_attached_object(node);
if (obj_desc) {
- /* Object exists - make sure there's no handler */
+ /* Object exists. */
- if (((handler_type & ACPI_SYSTEM_NOTIFY) &&
- obj_desc->common_notify.system_notify) ||
- ((handler_type & ACPI_DEVICE_NOTIFY) &&
- obj_desc->common_notify.device_notify)) {
+ /* For a device notify, make sure there's no handler. */
+ if ((handler_type & ACPI_DEVICE_NOTIFY) &&
+ obj_desc->common_notify.device_notify) {
status = AE_ALREADY_EXISTS;
goto unlock_and_exit;
}
+
+ /* System notifies may have more handlers installed. */
+ notify_obj = obj_desc->common_notify.system_notify;
+
+ if ((handler_type & ACPI_SYSTEM_NOTIFY) && notify_obj) {
+ struct acpi_object_notify_handler *parent_obj;
+
+ if (handler_type & ACPI_DEVICE_NOTIFY) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock_and_exit;
+ }
+
+ parent_obj = &notify_obj->notify;
+ status = acpi_add_handler_object(parent_obj,
+ handler,
+ context);
+ goto unlock_and_exit;
+ }
} else {
/* Create a new object */
@@ -356,9 +439,10 @@ acpi_install_notify_handler(acpi_handle device,
goto unlock_and_exit;
}
- notify_obj->notify.node = node;
- notify_obj->notify.handler = handler;
- notify_obj->notify.context = context;
+ acpi_populate_handler_object(&notify_obj->notify,
+ handler_type,
+ handler, context,
+ NULL);
if (handler_type & ACPI_SYSTEM_NOTIFY) {
obj_desc->common_notify.system_notify = notify_obj;
@@ -418,6 +502,10 @@ acpi_remove_notify_handler(acpi_handle device,
goto exit;
}
+
+ /* Make sure all deferred tasks are completed */
+ acpi_os_wait_events_complete(NULL);
+
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
goto exit;
@@ -445,15 +533,6 @@ acpi_remove_notify_handler(acpi_handle device,
goto unlock_and_exit;
}
- /* Make sure all deferred tasks are completed */
-
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
- acpi_os_wait_events_complete(NULL);
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit;
- }
-
if (handler_type & ACPI_SYSTEM_NOTIFY) {
acpi_gbl_system_notify.node = NULL;
acpi_gbl_system_notify.handler = NULL;
@@ -488,28 +567,60 @@ acpi_remove_notify_handler(acpi_handle device,
/* Object exists - make sure there's an existing handler */
if (handler_type & ACPI_SYSTEM_NOTIFY) {
+ struct acpi_object_notify_handler *handler_obj;
+ struct acpi_object_notify_handler *parent_obj;
+
notify_obj = obj_desc->common_notify.system_notify;
if (!notify_obj) {
status = AE_NOT_EXIST;
goto unlock_and_exit;
}
- if (notify_obj->notify.handler != handler) {
+ handler_obj = &notify_obj->notify;
+ parent_obj = NULL;
+ while (handler_obj->handler != handler) {
+ if (handler_obj->next) {
+ parent_obj = handler_obj;
+ handler_obj = handler_obj->next;
+ } else {
+ break;
+ }
+ }
+
+ if (handler_obj->handler != handler) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
- /* Make sure all deferred tasks are completed */
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
- acpi_os_wait_events_complete(NULL);
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit;
+ /*
+ * Remove the handler. There are three possible cases.
+ * First, we may need to remove a non-embedded object.
+ * Second, we may need to remove the embedded object's
+ * handler data, while non-embedded objects exist.
+ * Finally, we may need to remove the embedded object
+ * entirely along with its container.
+ */
+ if (parent_obj) {
+ /* Non-embedded object is being removed. */
+ parent_obj->next = handler_obj->next;
+ ACPI_FREE(handler_obj);
+ } else if (notify_obj->notify.next) {
+ /*
+ * The handler matches the embedded object, but
+ * there are more handler objects in the list.
+ * Replace the embedded object's data with the
+ * first next object's data and remove that
+ * object.
+ */
+ parent_obj = &notify_obj->notify;
+ handler_obj = notify_obj->notify.next;
+ *parent_obj = *handler_obj;
+ ACPI_FREE(handler_obj);
+ } else {
+ /* No more handler objects in the list. */
+ obj_desc->common_notify.system_notify = NULL;
+ acpi_ut_remove_reference(notify_obj);
}
-
- /* Remove the handler */
- obj_desc->common_notify.system_notify = NULL;
- acpi_ut_remove_reference(notify_obj);
}
if (handler_type & ACPI_DEVICE_NOTIFY) {
@@ -523,14 +634,6 @@ acpi_remove_notify_handler(acpi_handle device,
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
- /* Make sure all deferred tasks are completed */
-
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
- acpi_os_wait_events_complete(NULL);
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit;
- }
/* Remove the handler */
obj_desc->common_notify.device_notify = NULL;
@@ -617,13 +720,6 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
handler->context = context;
handler->method_node = gpe_event_info->dispatch.method_node;
- /* Disable the GPE before installing the handler */
-
- status = acpi_ev_disable_gpe(gpe_event_info);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
-
/* Install the handler */
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
@@ -707,13 +803,6 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
goto unlock_and_exit;
}
- /* Disable the GPE before removing the handler */
-
- status = acpi_ev_disable_gpe(gpe_event_info);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
-
/* Make sure all deferred tasks are completed */
(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index eed7a38d25f..124c157215b 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -201,23 +201,27 @@ ACPI_EXPORT_SYMBOL(acpi_enable_event)
/*******************************************************************************
*
- * FUNCTION: acpi_set_gpe_type
+ * FUNCTION: acpi_set_gpe
*
* PARAMETERS: gpe_device - Parent GPE Device
* gpe_number - GPE level within the GPE block
- * Type - New GPE type
+ * action - Enable or disable
+ * Called from ISR or not
*
* RETURN: Status
*
- * DESCRIPTION: Set the type of an individual GPE
+ * DESCRIPTION: Enable or disable an ACPI event (general purpose)
*
******************************************************************************/
-acpi_status acpi_set_gpe_type(acpi_handle gpe_device, u32 gpe_number, u8 type)
+acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
{
acpi_status status = AE_OK;
+ acpi_cpu_flags flags;
struct acpi_gpe_event_info *gpe_event_info;
- ACPI_FUNCTION_TRACE(acpi_set_gpe_type);
+ ACPI_FUNCTION_TRACE(acpi_set_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
/* Ensure that we have a valid GPE number */
@@ -227,19 +231,29 @@ acpi_status acpi_set_gpe_type(acpi_handle gpe_device, u32 gpe_number, u8 type)
goto unlock_and_exit;
}
- if ((gpe_event_info->flags & ACPI_GPE_TYPE_MASK) == type) {
- return_ACPI_STATUS(AE_OK);
- }
+ /* Perform the action */
+
+ switch (action) {
+ case ACPI_GPE_ENABLE:
+ status = acpi_ev_enable_gpe(gpe_event_info);
+ break;
- /* Set the new type (will disable GPE if currently enabled) */
+ case ACPI_GPE_DISABLE:
+ status = acpi_ev_disable_gpe(gpe_event_info);
+ break;
- status = acpi_ev_set_gpe_type(gpe_event_info, type);
+ default:
+ ACPI_ERROR((AE_INFO, "Invalid action\n"));
+ status = AE_BAD_PARAMETER;
+ break;
+ }
unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
-ACPI_EXPORT_SYMBOL(acpi_set_gpe_type)
+ACPI_EXPORT_SYMBOL(acpi_set_gpe)
/*******************************************************************************
*
@@ -247,15 +261,14 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe_type)
*
* PARAMETERS: gpe_device - Parent GPE Device
* gpe_number - GPE level within the GPE block
- * Flags - Just enable, or also wake enable?
- * Called from ISR or not
+ * type - Purpose the GPE will be used for
*
* RETURN: Status
*
- * DESCRIPTION: Enable an ACPI event (general purpose)
+ * DESCRIPTION: Take a reference to a GPE and enable it if necessary
*
******************************************************************************/
-acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
+acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type)
{
acpi_status status = AE_OK;
acpi_cpu_flags flags;
@@ -263,6 +276,9 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
ACPI_FUNCTION_TRACE(acpi_enable_gpe);
+ if (type & ~ACPI_GPE_TYPE_WAKE_RUN)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
/* Ensure that we have a valid GPE number */
@@ -273,15 +289,32 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
goto unlock_and_exit;
}
- /* Perform the enable */
+ if (type & ACPI_GPE_TYPE_RUNTIME) {
+ if (++gpe_event_info->runtime_count == 1) {
+ status = acpi_ev_enable_gpe(gpe_event_info);
+ if (ACPI_FAILURE(status))
+ gpe_event_info->runtime_count--;
+ }
+ }
+
+ if (type & ACPI_GPE_TYPE_WAKE) {
+ if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
- status = acpi_ev_enable_gpe(gpe_event_info, TRUE);
+ /*
+ * Wake-up GPEs are only enabled right prior to putting the
+ * system into a sleep state.
+ */
+ if (++gpe_event_info->wakeup_count == 1)
+ acpi_ev_update_gpe_enable_masks(gpe_event_info);
+ }
- unlock_and_exit:
+unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
-
ACPI_EXPORT_SYMBOL(acpi_enable_gpe)
/*******************************************************************************
@@ -290,15 +323,14 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe)
*
* PARAMETERS: gpe_device - Parent GPE Device
* gpe_number - GPE level within the GPE block
- * Flags - Just disable, or also wake disable?
- * Called from ISR or not
+ * type - Purpose the GPE won't be used for any more
*
* RETURN: Status
*
- * DESCRIPTION: Disable an ACPI event (general purpose)
+ * DESCRIPTION: Release a reference to a GPE and disable it if necessary
*
******************************************************************************/
-acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
+acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type)
{
acpi_status status = AE_OK;
acpi_cpu_flags flags;
@@ -306,6 +338,9 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
ACPI_FUNCTION_TRACE(acpi_disable_gpe);
+ if (type & ~ACPI_GPE_TYPE_WAKE_RUN)
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
/* Ensure that we have a valid GPE number */
@@ -315,13 +350,24 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
goto unlock_and_exit;
}
- status = acpi_ev_disable_gpe(gpe_event_info);
+ if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->runtime_count) {
+ if (--gpe_event_info->runtime_count == 0)
+ status = acpi_ev_disable_gpe(gpe_event_info);
+ }
+
+ if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->wakeup_count) {
+ /*
+ * Wake-up GPEs are not enabled after leaving system sleep
+ * states, so we don't need to disable them here.
+ */
+ if (--gpe_event_info->wakeup_count == 0)
+ acpi_ev_update_gpe_enable_masks(gpe_event_info);
+ }
unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
-
ACPI_EXPORT_SYMBOL(acpi_disable_gpe)
/*******************************************************************************
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 8a95e8329df..f53fbe307c9 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -422,11 +422,10 @@ static int acpi_button_add(struct acpi_device *device)
if (device->wakeup.flags.valid) {
/* Button's GPE is run-wake GPE */
- acpi_set_gpe_type(device->wakeup.gpe_device,
- device->wakeup.gpe_number,
- ACPI_GPE_TYPE_WAKE_RUN);
acpi_enable_gpe(device->wakeup.gpe_device,
- device->wakeup.gpe_number);
+ device->wakeup.gpe_number,
+ ACPI_GPE_TYPE_WAKE_RUN);
+ device->wakeup.run_wake_count++;
device->wakeup.state.enabled = 1;
}
@@ -446,6 +445,14 @@ static int acpi_button_remove(struct acpi_device *device, int type)
{
struct acpi_button *button = acpi_driver_data(device);
+ if (device->wakeup.flags.valid) {
+ acpi_disable_gpe(device->wakeup.gpe_device,
+ device->wakeup.gpe_number,
+ ACPI_GPE_TYPE_WAKE_RUN);
+ device->wakeup.run_wake_count--;
+ device->wakeup.state.enabled = 0;
+ }
+
acpi_button_remove_fs(device);
input_unregister_device(button->input);
kfree(button);
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index bbc2c1315c4..b2586f57e1f 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -935,6 +935,7 @@ static int dock_add(acpi_handle handle)
struct platform_device *dd;
id = dock_station_count;
+ memset(&ds, 0, sizeof(ds));
dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds));
if (IS_ERR(dd))
return PTR_ERR(dd);
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index d6471bb6852..27e0b92b2e3 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -307,7 +307,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
pr_debug(PREFIX "transaction start\n");
/* disable GPE during transaction if storm is detected */
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
- acpi_disable_gpe(NULL, ec->gpe);
+ /*
+ * It has to be disabled at the hardware level regardless of the
+ * GPE reference counting, so that it doesn't trigger.
+ */
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
}
status = acpi_ec_transaction_unlocked(ec, t);
@@ -316,8 +320,12 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
ec_check_sci_sync(ec, acpi_ec_read_status(ec));
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
msleep(1);
- /* it is safe to enable GPE outside of transaction */
- acpi_enable_gpe(NULL, ec->gpe);
+ /*
+ * It is safe to enable the GPE outside of the transaction. Use
+ * acpi_set_gpe() for that, since we used it to disable the GPE
+ * above.
+ */
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
} else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) {
pr_info(PREFIX "GPE storm detected, "
"transactions will use polling mode\n");
@@ -788,8 +796,8 @@ static int ec_install_handlers(struct acpi_ec *ec)
&acpi_ec_gpe_handler, ec);
if (ACPI_FAILURE(status))
return -ENODEV;
- acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
- acpi_enable_gpe(NULL, ec->gpe);
+
+ acpi_enable_gpe(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
status = acpi_install_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler,
@@ -806,6 +814,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
} else {
acpi_remove_gpe_handler(NULL, ec->gpe,
&acpi_ec_gpe_handler);
+ acpi_disable_gpe(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
return -ENODEV;
}
}
@@ -816,6 +825,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
static void ec_remove_handlers(struct acpi_ec *ec)
{
+ acpi_disable_gpe(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
pr_err(PREFIX "failed to remove space handler\n");
@@ -1057,16 +1067,16 @@ error:
static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state)
{
struct acpi_ec *ec = acpi_driver_data(device);
- /* Stop using GPE */
- acpi_disable_gpe(NULL, ec->gpe);
+ /* Stop using the GPE, but keep it reference counted. */
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
return 0;
}
static int acpi_ec_resume(struct acpi_device *device)
{
struct acpi_ec *ec = acpi_driver_data(device);
- /* Enable use of GPE back */
- acpi_enable_gpe(NULL, ec->gpe);
+ /* Enable the GPE again, but don't reference count it once more. */
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
return 0;
}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index cb28e0502ac..9c4c962e46e 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -36,8 +36,6 @@ static inline int acpi_debug_init(void) { return 0; }
int acpi_power_init(void);
int acpi_device_sleep_wake(struct acpi_device *dev,
int enable, int sleep_state, int dev_state);
-int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state);
-int acpi_disable_wakeup_device_power(struct acpi_device *dev);
int acpi_power_get_inferred_state(struct acpi_device *device);
int acpi_power_transition(struct acpi_device *device, int state);
extern int acpi_power_nocheck;
diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c
index a5a77b78a72..2ef04098cc1 100644
--- a/drivers/acpi/pci_bind.c
+++ b/drivers/acpi/pci_bind.c
@@ -26,7 +26,9 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/pci-acpi.h>
#include <linux/acpi.h>
+#include <linux/pm_runtime.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -38,7 +40,13 @@ static int acpi_pci_unbind(struct acpi_device *device)
struct pci_dev *dev;
dev = acpi_get_pci_dev(device->handle);
- if (!dev || !dev->subordinate)
+ if (!dev)
+ goto out;
+
+ device_set_run_wake(&dev->dev, false);
+ pci_acpi_remove_pm_notifier(device);
+
+ if (!dev->subordinate)
goto out;
acpi_pci_irq_del_prt(dev->subordinate);
@@ -62,6 +70,10 @@ static int acpi_pci_bind(struct acpi_device *device)
if (!dev)
return 0;
+ pci_acpi_add_pm_notifier(device, dev);
+ if (device->wakeup.flags.run_wake)
+ device_set_run_wake(&dev->dev, true);
+
/*
* Install the 'bind' function to facilitate callbacks for
* children of the P2P bridge.
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 64f55b6db73..d724736d56c 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -30,6 +30,7 @@
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/pci.h>
#include <linux/pci-acpi.h>
#include <linux/acpi.h>
@@ -528,6 +529,10 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
if (flags != base_flags)
acpi_pci_osc_support(root, flags);
+ pci_acpi_add_bus_pm_notifier(device, root->bus);
+ if (device->wakeup.flags.run_wake)
+ device_set_run_wake(root->bus->bridge, true);
+
return 0;
end:
@@ -549,6 +554,9 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type)
{
struct acpi_pci_root *root = acpi_driver_data(device);
+ device_set_run_wake(root->bus->bridge, false);
+ pci_acpi_remove_bus_pm_notifier(device);
+
kfree(root);
return 0;
}
@@ -558,6 +566,7 @@ static int __init acpi_pci_root_init(void)
if (acpi_pci_disabled)
return 0;
+ pci_acpi_crs_quirks();
if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0)
return -ENODEV;
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 7c0441f63b3..cc978a8c00b 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -110,6 +110,14 @@ static struct dmi_system_id __cpuinitdata processor_power_dmi_table[] = {
DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
(void *)2},
+ { set_max_cstate, "Pavilion zv5000", {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")},
+ (void *)1},
+ { set_max_cstate, "Asus L8400B", {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")},
+ (void *)1},
{},
};
@@ -872,12 +880,14 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
return(acpi_idle_enter_c1(dev, state));
local_irq_disable();
- current_thread_info()->status &= ~TS_POLLING;
- /*
- * TS_POLLING-cleared state must be visible before we test
- * NEED_RESCHED:
- */
- smp_mb();
+ if (cx->entry_method != ACPI_CSTATE_FFH) {
+ current_thread_info()->status &= ~TS_POLLING;
+ /*
+ * TS_POLLING-cleared state must be visible before we test
+ * NEED_RESCHED:
+ */
+ smp_mb();
+ }
if (unlikely(need_resched())) {
current_thread_info()->status |= TS_POLLING;
@@ -957,12 +967,14 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
}
local_irq_disable();
- current_thread_info()->status &= ~TS_POLLING;
- /*
- * TS_POLLING-cleared state must be visible before we test
- * NEED_RESCHED:
- */
- smp_mb();
+ if (cx->entry_method != ACPI_CSTATE_FFH) {
+ current_thread_info()->status &= ~TS_POLLING;
+ /*
+ * TS_POLLING-cleared state must be visible before we test
+ * NEED_RESCHED:
+ */
+ smp_mb();
+ }
if (unlikely(need_resched())) {
current_thread_info()->status |= TS_POLLING;
diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c
index 7247819dbd8..e306ba9aa34 100644
--- a/drivers/acpi/processor_pdc.c
+++ b/drivers/acpi/processor_pdc.c
@@ -125,6 +125,8 @@ acpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in)
return status;
}
+static int early_pdc_done;
+
void acpi_processor_set_pdc(acpi_handle handle)
{
struct acpi_object_list *obj_list;
@@ -132,6 +134,9 @@ void acpi_processor_set_pdc(acpi_handle handle)
if (arch_has_acpi_pdc() == false)
return;
+ if (early_pdc_done)
+ return;
+
obj_list = acpi_processor_alloc_pdc();
if (!obj_list)
return;
@@ -151,6 +156,13 @@ static int set_early_pdc_optin(const struct dmi_system_id *id)
return 0;
}
+static int param_early_pdc_optin(char *s)
+{
+ early_pdc_optin = 1;
+ return 1;
+}
+__setup("acpi_early_pdc_eval", param_early_pdc_optin);
+
static struct dmi_system_id __cpuinitdata early_pdc_optin_table[] = {
{
set_early_pdc_optin, "HP Envy", {
@@ -192,4 +204,6 @@ void __init acpi_early_processor_set_pdc(void)
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
early_init_pdc, NULL, NULL, NULL);
+
+ early_pdc_done = 1;
}
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 2cabadcc4d8..a959f6a0750 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -413,7 +413,11 @@ static int acpi_processor_get_performance_info(struct acpi_processor *pr)
if (result)
goto update_bios;
- return 0;
+ /* We need to call _PPC once when cpufreq starts */
+ if (ignore_ppc != 1)
+ result = acpi_processor_get_platform_limit(pr);
+
+ return result;
/*
* Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index ff9f6226085..fb7fc24fe72 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -741,19 +741,40 @@ acpi_bus_extract_wakeup_device_power_package(struct acpi_device *device,
return AE_OK;
}
-static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
+static void acpi_bus_set_run_wake_flags(struct acpi_device *device)
{
- acpi_status status = 0;
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *package = NULL;
- int psw_error;
-
struct acpi_device_id button_device_ids[] = {
{"PNP0C0D", 0},
{"PNP0C0C", 0},
{"PNP0C0E", 0},
{"", 0},
};
+ acpi_status status;
+ acpi_event_status event_status;
+
+ device->wakeup.run_wake_count = 0;
+ device->wakeup.flags.notifier_present = 0;
+
+ /* Power button, Lid switch always enable wakeup */
+ if (!acpi_match_device_ids(device, button_device_ids)) {
+ device->wakeup.flags.run_wake = 1;
+ device->wakeup.flags.always_enabled = 1;
+ return;
+ }
+
+ status = acpi_get_gpe_status(NULL, device->wakeup.gpe_number,
+ ACPI_NOT_ISR, &event_status);
+ if (status == AE_OK)
+ device->wakeup.flags.run_wake =
+ !!(event_status & ACPI_EVENT_FLAG_HANDLE);
+}
+
+static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
+{
+ acpi_status status = 0;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *package = NULL;
+ int psw_error;
/* _PRW */
status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer);
@@ -773,6 +794,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
device->wakeup.flags.valid = 1;
device->wakeup.prepare_count = 0;
+ acpi_bus_set_run_wake_flags(device);
/* Call _PSW/_DSW object to disable its ability to wake the sleeping
* system for the ACPI device with the _PRW object.
* The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
@@ -784,10 +806,6 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"error in _DSW or _PSW evaluation\n"));
- /* Power button, Lid switch always enable wakeup */
- if (!acpi_match_device_ids(device, button_device_ids))
- device->wakeup.flags.run_wake = 1;
-
end:
if (ACPI_FAILURE(status))
device->flags.wake_capable = 0;
@@ -1336,9 +1354,25 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
if (child)
*child = device;
- return 0;
+
+ if (device)
+ return 0;
+ else
+ return -ENODEV;
}
+/*
+ * acpi_bus_add and acpi_bus_start
+ *
+ * scan a given ACPI tree and (probably recently hot-plugged)
+ * create and add or starts found devices.
+ *
+ * If no devices were found -ENODEV is returned which does not
+ * mean that this is a real error, there just have been no suitable
+ * ACPI objects in the table trunk from which the kernel could create
+ * a device and add/start an appropriate driver.
+ */
+
int
acpi_bus_add(struct acpi_device **child,
struct acpi_device *parent, acpi_handle handle, int type)
@@ -1348,8 +1382,7 @@ acpi_bus_add(struct acpi_device **child,
memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1;
- acpi_bus_scan(handle, &ops, child);
- return 0;
+ return acpi_bus_scan(handle, &ops, child);
}
EXPORT_SYMBOL(acpi_bus_add);
@@ -1357,11 +1390,13 @@ int acpi_bus_start(struct acpi_device *device)
{
struct acpi_bus_ops ops;
+ if (!device)
+ return -EINVAL;
+
memset(&ops, 0, sizeof(ops));
ops.acpi_op_start = 1;
- acpi_bus_scan(device->handle, &ops, NULL);
- return 0;
+ return acpi_bus_scan(device->handle, &ops, NULL);
}
EXPORT_SYMBOL(acpi_bus_start);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 79d33d908b5..3bde594a997 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -745,9 +745,18 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
return -ENODEV;
}
- error = enable ?
- acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) :
- acpi_disable_wakeup_device_power(adev);
+ if (enable) {
+ error = acpi_enable_wakeup_device_power(adev,
+ acpi_target_sleep_state);
+ if (!error)
+ acpi_enable_gpe(adev->wakeup.gpe_device,
+ adev->wakeup.gpe_number,
+ ACPI_GPE_TYPE_WAKE);
+ } else {
+ acpi_disable_gpe(adev->wakeup.gpe_device, adev->wakeup.gpe_number,
+ ACPI_GPE_TYPE_WAKE);
+ error = acpi_disable_wakeup_device_power(adev);
+ }
if (!error)
dev_info(dev, "wake-up capability %s by ACPI\n",
enable ? "enabled" : "disabled");
diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c
index d11282975f3..a206a12da78 100644
--- a/drivers/acpi/system.c
+++ b/drivers/acpi/system.c
@@ -387,10 +387,10 @@ static ssize_t counter_set(struct kobject *kobj,
if (index < num_gpes) {
if (!strcmp(buf, "disable\n") &&
(status & ACPI_EVENT_FLAG_ENABLED))
- result = acpi_disable_gpe(handle, index);
+ result = acpi_set_gpe(handle, index, ACPI_GPE_DISABLE);
else if (!strcmp(buf, "enable\n") &&
!(status & ACPI_EVENT_FLAG_ENABLED))
- result = acpi_enable_gpe(handle, index);
+ result = acpi_set_gpe(handle, index, ACPI_GPE_ENABLE);
else if (!strcmp(buf, "clear\n") &&
(status & ACPI_EVENT_FLAG_SET))
result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR);
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index f336bca7c45..8a0ed2800e6 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -213,7 +213,7 @@ acpi_table_parse_entries(char *id,
unsigned long table_end;
acpi_size tbl_size;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_ht)
return -ENODEV;
if (!handler)
@@ -280,7 +280,7 @@ int __init acpi_table_parse(char *id, acpi_table_handler handler)
struct acpi_table_header *table = NULL;
acpi_size tbl_size;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_ht)
return -ENODEV;
if (!handler)
diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c
index e0ee0c036f5..4b9d339a6e2 100644
--- a/drivers/acpi/wakeup.c
+++ b/drivers/acpi/wakeup.c
@@ -21,12 +21,12 @@
ACPI_MODULE_NAME("wakeup_devices")
/**
- * acpi_enable_wakeup_device_prep - prepare wakeup devices
- * @sleep_state: ACPI state
- * Enable all wakup devices power if the devices' wakeup level
- * is higher than requested sleep level
+ * acpi_enable_wakeup_device_prep - Prepare wake-up devices.
+ * @sleep_state: ACPI system sleep state.
+ *
+ * Enable all wake-up devices' power, unless the requested system sleep state is
+ * too deep.
*/
-
void acpi_enable_wakeup_device_prep(u8 sleep_state)
{
struct list_head *node, *next;
@@ -36,9 +36,8 @@ void acpi_enable_wakeup_device_prep(u8 sleep_state)
struct acpi_device,
wakeup_list);
- if (!dev->wakeup.flags.valid ||
- !dev->wakeup.state.enabled ||
- (sleep_state > (u32) dev->wakeup.sleep_state))
+ if (!dev->wakeup.flags.valid || !dev->wakeup.state.enabled
+ || (sleep_state > (u32) dev->wakeup.sleep_state))
continue;
acpi_enable_wakeup_device_power(dev, sleep_state);
@@ -46,9 +45,12 @@ void acpi_enable_wakeup_device_prep(u8 sleep_state)
}
/**
- * acpi_enable_wakeup_device - enable wakeup devices
- * @sleep_state: ACPI state
- * Enable all wakup devices's GPE
+ * acpi_enable_wakeup_device - Enable wake-up device GPEs.
+ * @sleep_state: ACPI system sleep state.
+ *
+ * Enable all wake-up devices' GPEs, with the assumption that
+ * acpi_disable_all_gpes() was executed before, so we don't need to disable any
+ * GPEs here.
*/
void acpi_enable_wakeup_device(u8 sleep_state)
{
@@ -65,29 +67,22 @@ void acpi_enable_wakeup_device(u8 sleep_state)
if (!dev->wakeup.flags.valid)
continue;
- /* If users want to disable run-wake GPE,
- * we only disable it for wake and leave it for runtime
- */
if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count)
- || sleep_state > (u32) dev->wakeup.sleep_state) {
- if (dev->wakeup.flags.run_wake) {
- /* set_gpe_type will disable GPE, leave it like that */
- acpi_set_gpe_type(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number,
- ACPI_GPE_TYPE_RUNTIME);
- }
+ || sleep_state > (u32) dev->wakeup.sleep_state)
continue;
- }
- if (!dev->wakeup.flags.run_wake)
- acpi_enable_gpe(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number);
+
+ /* The wake-up power should have been enabled already. */
+ acpi_set_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
+ ACPI_GPE_ENABLE);
}
}
/**
- * acpi_disable_wakeup_device - disable devices' wakeup capability
- * @sleep_state: ACPI state
- * Disable all wakup devices's GPE and wakeup capability
+ * acpi_disable_wakeup_device - Disable devices' wakeup capability.
+ * @sleep_state: ACPI system sleep state.
+ *
+ * This function only affects devices with wakeup.state.enabled set, which means
+ * that it reverses the changes made by acpi_enable_wakeup_device_prep().
*/
void acpi_disable_wakeup_device(u8 sleep_state)
{
@@ -97,30 +92,11 @@ void acpi_disable_wakeup_device(u8 sleep_state)
struct acpi_device *dev =
container_of(node, struct acpi_device, wakeup_list);
- if (!dev->wakeup.flags.valid)
- continue;
-
- if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count)
- || sleep_state > (u32) dev->wakeup.sleep_state) {
- if (dev->wakeup.flags.run_wake) {
- acpi_set_gpe_type(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number,
- ACPI_GPE_TYPE_WAKE_RUN);
- /* Re-enable it, since set_gpe_type will disable it */
- acpi_enable_gpe(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number);
- }
+ if (!dev->wakeup.flags.valid || !dev->wakeup.state.enabled
+ || (sleep_state > (u32) dev->wakeup.sleep_state))
continue;
- }
acpi_disable_wakeup_device_power(dev);
- /* Never disable run-wake GPE */
- if (!dev->wakeup.flags.run_wake) {
- acpi_disable_gpe(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number);
- acpi_clear_gpe(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number, ACPI_NOT_ISR);
- }
}
}
@@ -134,13 +110,11 @@ int __init acpi_wakeup_device_init(void)
struct acpi_device,
wakeup_list);
/* In case user doesn't load button driver */
- if (!dev->wakeup.flags.run_wake || dev->wakeup.state.enabled)
+ if (!dev->wakeup.flags.always_enabled ||
+ dev->wakeup.state.enabled)
continue;
- acpi_set_gpe_type(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number,
- ACPI_GPE_TYPE_WAKE_RUN);
- acpi_enable_gpe(dev->wakeup.gpe_device,
- dev->wakeup.gpe_number);
+ acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
+ ACPI_GPE_TYPE_WAKE);
dev->wakeup.state.enabled = 1;
}
mutex_unlock(&acpi_device_lock);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index b34390347c1..a6a736a7dbf 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -3082,8 +3082,16 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
ahci_save_initial_config(pdev, hpriv);
/* prepare host */
- if (hpriv->cap & HOST_CAP_NCQ)
- pi.flags |= ATA_FLAG_NCQ | ATA_FLAG_FPDMA_AA;
+ if (hpriv->cap & HOST_CAP_NCQ) {
+ pi.flags |= ATA_FLAG_NCQ;
+ /* Auto-activate optimization is supposed to be supported on
+ all AHCI controllers indicating NCQ support, but it seems
+ to be broken at least on some NVIDIA MCP79 chipsets.
+ Until we get info on which NVIDIA chipsets don't have this
+ issue, if any, disable AA on all NVIDIA AHCIs. */
+ if (pdev->vendor != PCI_VENDOR_ID_NVIDIA)
+ pi.flags |= ATA_FLAG_FPDMA_AA;
+ }
if (hpriv->cap & HOST_CAP_PMP)
pi.flags |= ATA_FLAG_PMP;
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index d096fbcbc77..bea003a24d2 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1097,7 +1097,7 @@ static int ata_scsi_dev_config(struct scsi_device *sdev,
dev->flags |= ATA_DFLAG_NO_UNLOAD;
/* configure max sectors */
- blk_queue_max_sectors(sdev->request_queue, dev->max_sectors);
+ blk_queue_max_hw_sectors(sdev->request_queue, dev->max_sectors);
if (dev->class == ATA_DEV_ATAPI) {
struct request_queue *q = sdev->request_queue;
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 0c82d335c55..684fe04dbbb 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -772,7 +772,7 @@ static int nv_adma_slave_config(struct scsi_device *sdev)
}
blk_queue_segment_boundary(sdev->request_queue, segment_boundary);
- blk_queue_max_hw_segments(sdev->request_queue, sg_tablesize);
+ blk_queue_max_segments(sdev->request_queue, sg_tablesize);
ata_port_printk(ap, KERN_INFO,
"DMA mask 0x%llX, segment boundary 0x%lX, hw segs %hu\n",
(unsigned long long)*ap->host->dev->dma_mask,
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 161746deab4..6e2c3b064f5 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -59,6 +59,8 @@ static void class_release(struct kobject *kobj)
else
pr_debug("class '%s' does not have a release() function, "
"be careful\n", class->name);
+
+ kfree(cp);
}
static struct sysfs_ops class_sysfs_ops = {
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a5142bddef4..0e26a6f6fd4 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -25,6 +25,7 @@
#include <linux/resume-trace.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/async.h>
#include "../base.h"
#include "power.h"
@@ -42,6 +43,7 @@
LIST_HEAD(dpm_list);
static DEFINE_MUTEX(dpm_list_mtx);
+static pm_message_t pm_transition;
/*
* Set once the preparation of devices for a PM transition has started, reset
@@ -56,6 +58,7 @@ static bool transition_started;
void device_pm_init(struct device *dev)
{
dev->power.status = DPM_ON;
+ init_completion(&dev->power.completion);
pm_runtime_init(dev);
}
@@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev)
pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
+ complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
@@ -188,6 +192,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
}
/**
+ * dpm_wait - Wait for a PM operation to complete.
+ * @dev: Device to wait for.
+ * @async: If unset, wait only if the device's power.async_suspend flag is set.
+ */
+static void dpm_wait(struct device *dev, bool async)
+{
+ if (!dev)
+ return;
+
+ if (async || (pm_async_enabled && dev->power.async_suspend))
+ wait_for_completion(&dev->power.completion);
+}
+
+static int dpm_wait_fn(struct device *dev, void *async_ptr)
+{
+ dpm_wait(dev, *((bool *)async_ptr));
+ return 0;
+}
+
+static void dpm_wait_for_children(struct device *dev, bool async)
+{
+ device_for_each_child(dev, &async, dpm_wait_fn);
+}
+
+/**
* pm_op - Execute the PM operation appropriate for given PM event.
* @dev: Device to handle.
* @ops: PM operations to choose from.
@@ -271,8 +300,9 @@ static int pm_noirq_op(struct device *dev,
ktime_t calltime, delta, rettime;
if (initcall_debug) {
- pr_info("calling %s_i+ @ %i\n",
- dev_name(dev), task_pid_nr(current));
+ pr_info("calling %s+ @ %i, parent: %s\n",
+ dev_name(dev), task_pid_nr(current),
+ dev->parent ? dev_name(dev->parent) : "none");
calltime = ktime_get();
}
@@ -468,16 +498,20 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
* device_resume - Execute "resume" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
+ * @async: If true, the device is being resumed asynchronously.
*/
-static int device_resume(struct device *dev, pm_message_t state)
+static int device_resume(struct device *dev, pm_message_t state, bool async)
{
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
+ dpm_wait(dev->parent, async);
down(&dev->sem);
+ dev->power.status = DPM_RESUMING;
+
if (dev->bus) {
if (dev->bus->pm) {
pm_dev_dbg(dev, state, "");
@@ -510,11 +544,29 @@ static int device_resume(struct device *dev, pm_message_t state)
}
End:
up(&dev->sem);
+ complete_all(&dev->power.completion);
TRACE_RESUME(error);
return error;
}
+static void async_resume(void *data, async_cookie_t cookie)
+{
+ struct device *dev = (struct device *)data;
+ int error;
+
+ error = device_resume(dev, pm_transition, true);
+ if (error)
+ pm_dev_err(dev, pm_transition, " async", error);
+ put_device(dev);
+}
+
+static bool is_async(struct device *dev)
+{
+ return dev->power.async_suspend && pm_async_enabled
+ && !pm_trace_is_enabled();
+}
+
/**
* dpm_resume - Execute "resume" callbacks for non-sysdev devices.
* @state: PM transition of the system being carried out.
@@ -525,21 +577,33 @@ static int device_resume(struct device *dev, pm_message_t state)
static void dpm_resume(pm_message_t state)
{
struct list_head list;
+ struct device *dev;
ktime_t starttime = ktime_get();
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
- while (!list_empty(&dpm_list)) {
- struct device *dev = to_device(dpm_list.next);
+ pm_transition = state;
+
+ list_for_each_entry(dev, &dpm_list, power.entry) {
+ if (dev->power.status < DPM_OFF)
+ continue;
+
+ INIT_COMPLETION(dev->power.completion);
+ if (is_async(dev)) {
+ get_device(dev);
+ async_schedule(async_resume, dev);
+ }
+ }
+ while (!list_empty(&dpm_list)) {
+ dev = to_device(dpm_list.next);
get_device(dev);
- if (dev->power.status >= DPM_OFF) {
+ if (dev->power.status >= DPM_OFF && !is_async(dev)) {
int error;
- dev->power.status = DPM_RESUMING;
mutex_unlock(&dpm_list_mtx);
- error = device_resume(dev, state);
+ error = device_resume(dev, state, false);
mutex_lock(&dpm_list_mtx);
if (error)
@@ -554,6 +618,7 @@ static void dpm_resume(pm_message_t state)
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
+ async_synchronize_full();
dpm_show_time(starttime, state, NULL);
}
@@ -731,17 +796,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
return error;
}
+static int async_error;
+
/**
* device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
+ * @async: If true, the device is being suspended asynchronously.
*/
-static int device_suspend(struct device *dev, pm_message_t state)
+static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{
int error = 0;
+ dpm_wait_for_children(dev, async);
down(&dev->sem);
+ if (async_error)
+ goto End;
+
if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
@@ -772,12 +844,44 @@ static int device_suspend(struct device *dev, pm_message_t state)
error = legacy_suspend(dev, state, dev->bus->suspend);
}
}
+
+ if (!error)
+ dev->power.status = DPM_OFF;
+
End:
up(&dev->sem);
+ complete_all(&dev->power.completion);
return error;
}
+static void async_suspend(void *data, async_cookie_t cookie)
+{
+ struct device *dev = (struct device *)data;
+ int error;
+
+ error = __device_suspend(dev, pm_transition, true);
+ if (error) {
+ pm_dev_err(dev, pm_transition, " async", error);
+ async_error = error;
+ }
+
+ put_device(dev);
+}
+
+static int device_suspend(struct device *dev)
+{
+ INIT_COMPLETION(dev->power.completion);
+
+ if (pm_async_enabled && dev->power.async_suspend) {
+ get_device(dev);
+ async_schedule(async_suspend, dev);
+ return 0;
+ }
+
+ return __device_suspend(dev, pm_transition, false);
+}
+
/**
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
* @state: PM transition of the system being carried out.
@@ -790,13 +894,15 @@ static int dpm_suspend(pm_message_t state)
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
+ pm_transition = state;
+ async_error = 0;
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.prev);
get_device(dev);
mutex_unlock(&dpm_list_mtx);
- error = device_suspend(dev, state);
+ error = device_suspend(dev);
mutex_lock(&dpm_list_mtx);
if (error) {
@@ -804,13 +910,17 @@ static int dpm_suspend(pm_message_t state)
put_device(dev);
break;
}
- dev->power.status = DPM_OFF;
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &list);
put_device(dev);
+ if (async_error)
+ break;
}
list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx);
+ async_synchronize_full();
+ if (!error)
+ error = async_error;
if (!error)
dpm_show_time(starttime, state, NULL);
return error;
@@ -936,3 +1046,14 @@ void __suspend_report_result(const char *function, void *fn, int ret)
printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
}
EXPORT_SYMBOL_GPL(__suspend_report_result);
+
+/**
+ * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete.
+ * @dev: Device to wait for.
+ * @subordinate: Device that needs to wait for @dev.
+ */
+void device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
+{
+ dpm_wait(dev, subordinate->power.async_suspend);
+}
+EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b8fa1aa5225..c0bd03c83b9 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -12,10 +12,10 @@ static inline void pm_runtime_remove(struct device *dev) {}
#ifdef CONFIG_PM_SLEEP
-/*
- * main.c
- */
+/* kernel/power/main.c */
+extern int pm_async_enabled;
+/* drivers/base/power/main.c */
extern struct list_head dpm_list; /* The active device list */
static inline struct device *to_device(struct list_head *entry)
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index f8b044e8aef..626dd147b75 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1011,6 +1011,50 @@ void pm_runtime_enable(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_enable);
/**
+ * pm_runtime_forbid - Block run-time PM of a device.
+ * @dev: Device to handle.
+ *
+ * Increase the device's usage count and clear its power.runtime_auto flag,
+ * so that it cannot be suspended at run time until pm_runtime_allow() is called
+ * for it.
+ */
+void pm_runtime_forbid(struct device *dev)
+{
+ spin_lock_irq(&dev->power.lock);
+ if (!dev->power.runtime_auto)
+ goto out;
+
+ dev->power.runtime_auto = false;
+ atomic_inc(&dev->power.usage_count);
+ __pm_runtime_resume(dev, false);
+
+ out:
+ spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_forbid);
+
+/**
+ * pm_runtime_allow - Unblock run-time PM of a device.
+ * @dev: Device to handle.
+ *
+ * Decrease the device's usage count and set its power.runtime_auto flag.
+ */
+void pm_runtime_allow(struct device *dev)
+{
+ spin_lock_irq(&dev->power.lock);
+ if (dev->power.runtime_auto)
+ goto out;
+
+ dev->power.runtime_auto = true;
+ if (atomic_dec_and_test(&dev->power.usage_count))
+ __pm_runtime_idle(dev);
+
+ out:
+ spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_allow);
+
+/**
* pm_runtime_init - Initialize run-time PM fields in given device object.
* @dev: Device object to initialize.
*/
@@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev)
atomic_set(&dev->power.child_count, 0);
pm_suspend_ignore_children(dev, false);
+ dev->power.runtime_auto = true;
dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE;
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 596aeecfdff..86fd9373447 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -4,9 +4,25 @@
#include <linux/device.h>
#include <linux/string.h>
+#include <linux/pm_runtime.h>
#include "power.h"
/*
+ * control - Report/change current runtime PM setting of the device
+ *
+ * Runtime power management of a device can be blocked with the help of
+ * this attribute. All devices have one of the following two values for
+ * the power/control file:
+ *
+ * + "auto\n" to allow the device to be power managed at run time;
+ * + "on\n" to prevent the device from being power managed at run time;
+ *
+ * The default for all devices is "auto", which means that devices may be
+ * subject to automatic power management, depending on their drivers.
+ * Changing this attribute to "on" prevents the driver from power managing
+ * the device at run time. Doing that while the device is suspended causes
+ * it to be woken up.
+ *
* wakeup - Report/change current wakeup option for device
*
* Some devices support "wakeup" events, which are hardware signals
@@ -38,11 +54,61 @@
* wakeup events internally (unless they are disabled), keeping
* their hardware in low power modes whenever they're unused. This
* saves runtime power, without requiring system-wide sleep states.
+ *
+ * async - Report/change current async suspend setting for the device
+ *
+ * Asynchronous suspend and resume of the device during system-wide power
+ * state transitions can be enabled by writing "enabled" to this file.
+ * Analogously, if "disabled" is written to this file, the device will be
+ * suspended and resumed synchronously.
+ *
+ * All devices have one of the following two values for power/async:
+ *
+ * + "enabled\n" to permit the asynchronous suspend/resume of the device;
+ * + "disabled\n" to forbid it;
+ *
+ * NOTE: It generally is unsafe to permit the asynchronous suspend/resume
+ * of a device unless it is certain that all of the PM dependencies of the
+ * device are known to the PM core. However, for some devices this
+ * attribute is set to "enabled" by bus type code or device drivers and in
+ * that cases it should be safe to leave the default value.
*/
static const char enabled[] = "enabled";
static const char disabled[] = "disabled";
+#ifdef CONFIG_PM_RUNTIME
+static const char ctrl_auto[] = "auto";
+static const char ctrl_on[] = "on";
+
+static ssize_t control_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n",
+ dev->power.runtime_auto ? ctrl_auto : ctrl_on);
+}
+
+static ssize_t control_store(struct device * dev, struct device_attribute *attr,
+ const char * buf, size_t n)
+{
+ char *cp;
+ int len = n;
+
+ cp = memchr(buf, '\n', n);
+ if (cp)
+ len = cp - buf;
+ if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)
+ pm_runtime_allow(dev);
+ else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)
+ pm_runtime_forbid(dev);
+ else
+ return -EINVAL;
+ return n;
+}
+
+static DEVICE_ATTR(control, 0644, control_show, control_store);
+#endif
+
static ssize_t
wake_show(struct device * dev, struct device_attribute *attr, char * buf)
{
@@ -77,9 +143,43 @@ wake_store(struct device * dev, struct device_attribute *attr,
static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
+#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
+static ssize_t async_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n",
+ device_async_suspend_enabled(dev) ? enabled : disabled);
+}
+
+static ssize_t async_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ char *cp;
+ int len = n;
+
+ cp = memchr(buf, '\n', n);
+ if (cp)
+ len = cp - buf;
+ if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
+ device_enable_async_suspend(dev);
+ else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
+ device_disable_async_suspend(dev);
+ else
+ return -EINVAL;
+ return n;
+}
+
+static DEVICE_ATTR(async, 0644, async_show, async_store);
+#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */
static struct attribute * power_attrs[] = {
+#ifdef CONFIG_PM_RUNTIME
+ &dev_attr_control.attr,
+#endif
&dev_attr_wakeup.attr,
+#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
+ &dev_attr_async.attr,
+#endif
NULL,
};
static struct attribute_group pm_attr_group = {
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index ce1fa923c41..459f1bc25a7 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -2534,8 +2534,8 @@ static bool DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
blk_queue_bounce_limit(RequestQueue, Controller->BounceBufferLimit);
RequestQueue->queuedata = Controller;
blk_queue_max_hw_segments(RequestQueue, Controller->DriverScatterGatherLimit);
- blk_queue_max_phys_segments(RequestQueue, Controller->DriverScatterGatherLimit);
- blk_queue_max_sectors(RequestQueue, Controller->MaxBlocksPerCommand);
+ blk_queue_max_segments(RequestQueue, Controller->DriverScatterGatherLimit);
+ blk_queue_max_hw_sectors(RequestQueue, Controller->MaxBlocksPerCommand);
disk->queue = RequestQueue;
sprintf(disk->disk_name, "rd/c%dd%d", Controller->ControllerNumber, n);
disk->major = MajorNumber;
@@ -7134,7 +7134,7 @@ static struct DAC960_privdata DAC960_P_privdata = {
.MemoryWindowSize = DAC960_PD_RegisterWindowSize,
};
-static struct pci_device_id DAC960_id_table[] = {
+static const struct pci_device_id DAC960_id_table[] = {
{
.vendor = PCI_VENDOR_ID_MYLEX,
.device = PCI_DEVICE_ID_MYLEX_DAC960_GEM,
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index a5af1d6dda8..e35cf59cbfd 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -1470,8 +1470,6 @@ repeat:
void do_fd_request(struct request_queue * q)
{
- unsigned long flags;
-
DPRINT(("do_fd_request for pid %d\n",current->pid));
while( fdc_busy ) sleep_on( &fdc_wait );
fdc_busy = 1;
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 4f688434daf..c6ddeacb77f 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -434,7 +434,7 @@ static struct brd_device *brd_alloc(int i)
goto out_free_dev;
blk_queue_make_request(brd->brd_queue, brd_make_request);
blk_queue_ordered(brd->brd_queue, QUEUE_ORDERED_TAG, NULL);
- blk_queue_max_sectors(brd->brd_queue, 1024);
+ blk_queue_max_hw_sectors(brd->brd_queue, 1024);
blk_queue_bounce_limit(brd->brd_queue, BLK_BOUNCE_ANY);
disk = brd->brd_disk = alloc_disk(1 << part_shift);
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 9291614ac6b..9e3af307aae 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -257,6 +257,79 @@ static inline void removeQ(CommandList_struct *c)
hlist_del_init(&c->list);
}
+static void cciss_free_sg_chain_blocks(SGDescriptor_struct **cmd_sg_list,
+ int nr_cmds)
+{
+ int i;
+
+ if (!cmd_sg_list)
+ return;
+ for (i = 0; i < nr_cmds; i++) {
+ kfree(cmd_sg_list[i]);
+ cmd_sg_list[i] = NULL;
+ }
+ kfree(cmd_sg_list);
+}
+
+static SGDescriptor_struct **cciss_allocate_sg_chain_blocks(
+ ctlr_info_t *h, int chainsize, int nr_cmds)
+{
+ int j;
+ SGDescriptor_struct **cmd_sg_list;
+
+ if (chainsize <= 0)
+ return NULL;
+
+ cmd_sg_list = kmalloc(sizeof(*cmd_sg_list) * nr_cmds, GFP_KERNEL);
+ if (!cmd_sg_list)
+ return NULL;
+
+ /* Build up chain blocks for each command */
+ for (j = 0; j < nr_cmds; j++) {
+ /* Need a block of chainsized s/g elements. */
+ cmd_sg_list[j] = kmalloc((chainsize *
+ sizeof(*cmd_sg_list[j])), GFP_KERNEL);
+ if (!cmd_sg_list[j]) {
+ dev_err(&h->pdev->dev, "Cannot get memory "
+ "for s/g chains.\n");
+ goto clean;
+ }
+ }
+ return cmd_sg_list;
+clean:
+ cciss_free_sg_chain_blocks(cmd_sg_list, nr_cmds);
+ return NULL;
+}
+
+static void cciss_unmap_sg_chain_block(ctlr_info_t *h, CommandList_struct *c)
+{
+ SGDescriptor_struct *chain_sg;
+ u64bit temp64;
+
+ if (c->Header.SGTotal <= h->max_cmd_sgentries)
+ return;
+
+ chain_sg = &c->SG[h->max_cmd_sgentries - 1];
+ temp64.val32.lower = chain_sg->Addr.lower;
+ temp64.val32.upper = chain_sg->Addr.upper;
+ pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE);
+}
+
+static void cciss_map_sg_chain_block(ctlr_info_t *h, CommandList_struct *c,
+ SGDescriptor_struct *chain_block, int len)
+{
+ SGDescriptor_struct *chain_sg;
+ u64bit temp64;
+
+ chain_sg = &c->SG[h->max_cmd_sgentries - 1];
+ chain_sg->Ext = CCISS_SG_CHAIN;
+ chain_sg->Len = len;
+ temp64.val = pci_map_single(h->pdev, chain_block, len,
+ PCI_DMA_TODEVICE);
+ chain_sg->Addr.lower = temp64.val32.lower;
+ chain_sg->Addr.upper = temp64.val32.upper;
+}
+
#include "cciss_scsi.c" /* For SCSI tape support */
static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
@@ -1344,26 +1417,27 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
kfree(buff);
return -ENOMEM;
}
- // Fill in the command type
+ /* Fill in the command type */
c->cmd_type = CMD_IOCTL_PEND;
- // Fill in Command Header
- c->Header.ReplyQueue = 0; // unused in simple mode
- if (iocommand.buf_size > 0) // buffer to fill
+ /* Fill in Command Header */
+ c->Header.ReplyQueue = 0; /* unused in simple mode */
+ if (iocommand.buf_size > 0) /* buffer to fill */
{
c->Header.SGList = 1;
c->Header.SGTotal = 1;
- } else // no buffers to fill
+ } else /* no buffers to fill */
{
c->Header.SGList = 0;
c->Header.SGTotal = 0;
}
c->Header.LUN = iocommand.LUN_info;
- c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag
+ /* use the kernel address the cmd block for tag */
+ c->Header.Tag.lower = c->busaddr;
- // Fill in Request block
+ /* Fill in Request block */
c->Request = iocommand.Request;
- // Fill in the scatter gather information
+ /* Fill in the scatter gather information */
if (iocommand.buf_size > 0) {
temp64.val = pci_map_single(host->pdev, buff,
iocommand.buf_size,
@@ -1371,7 +1445,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
c->SG[0].Addr.lower = temp64.val32.lower;
c->SG[0].Addr.upper = temp64.val32.upper;
c->SG[0].Len = iocommand.buf_size;
- c->SG[0].Ext = 0; // we are not chaining
+ c->SG[0].Ext = 0; /* we are not chaining */
}
c->waiting = &wait;
@@ -1670,14 +1744,9 @@ static void cciss_softirq_done(struct request *rq)
/* unmap the DMA mapping for all the scatter gather elements */
for (i = 0; i < cmd->Header.SGList; i++) {
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);
+ cciss_unmap_sg_chain_block(h, cmd);
/* Point to the next block */
- curr_sg = h->cmd_sg_list[cmd->cmdindex]->sgchain;
+ curr_sg = h->cmd_sg_list[cmd->cmdindex];
sg_index = 0;
}
temp64.val32.lower = curr_sg[sg_index].Addr.lower;
@@ -1796,12 +1865,9 @@ 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, h->maxsgentries);
-
- /* This is a limit in the driver and could be eliminated. */
- blk_queue_max_phys_segments(disk->queue, h->maxsgentries);
+ blk_queue_max_segments(disk->queue, h->maxsgentries);
- blk_queue_max_sectors(disk->queue, h->cciss_max_sectors);
+ blk_queue_max_hw_sectors(disk->queue, h->cciss_max_sectors);
blk_queue_softirq_done(disk->queue, cciss_softirq_done);
@@ -2425,7 +2491,7 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
c->Request.Type.Direction = XFER_READ;
c->Request.Timeout = 0;
c->Request.CDB[0] = cmd;
- c->Request.CDB[6] = (size >> 24) & 0xFF; //MSB
+ c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */
c->Request.CDB[7] = (size >> 16) & 0xFF;
c->Request.CDB[8] = (size >> 8) & 0xFF;
c->Request.CDB[9] = size & 0xFF;
@@ -2694,7 +2760,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
"cciss: reading geometry failed, volume "
"does not support reading geometry\n");
drv->heads = 255;
- drv->sectors = 32; // Sectors per track
+ drv->sectors = 32; /* Sectors per track */
drv->cylinders = total_size + 1;
drv->raid_level = RAID_UNKNOWN;
} else {
@@ -3082,7 +3148,6 @@ static void do_cciss_request(struct request_queue *q)
SGDescriptor_struct *curr_sg;
drive_info_struct *drv;
int i, dir;
- int nseg = 0;
int sg_index = 0;
int chained = 0;
@@ -3112,19 +3177,19 @@ static void do_cciss_request(struct request_queue *q)
/* fill in the request */
drv = creq->rq_disk->private_data;
- c->Header.ReplyQueue = 0; // unused in simple mode
+ c->Header.ReplyQueue = 0; /* unused in simple mode */
/* got command from pool, so use the command block index instead */
/* for direct lookups. */
/* The first 2 bits are reserved for controller error reporting. */
c->Header.Tag.lower = (c->cmdindex << 3);
c->Header.Tag.lower |= 0x04; /* flag for direct lookup. */
memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID));
- c->Request.CDBLen = 10; // 12 byte commands not in FW yet;
- c->Request.Type.Type = TYPE_CMD; // It is a command.
+ c->Request.CDBLen = 10; /* 12 byte commands not in FW yet; */
+ c->Request.Type.Type = TYPE_CMD; /* It is a command. */
c->Request.Type.Attribute = ATTR_SIMPLE;
c->Request.Type.Direction =
(rq_data_dir(creq) == READ) ? XFER_READ : XFER_WRITE;
- c->Request.Timeout = 0; // Don't time out
+ c->Request.Timeout = 0; /* Don't time out */
c->Request.CDB[0] =
(rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
start_blk = blk_rq_pos(creq);
@@ -3149,13 +3214,8 @@ static void do_cciss_request(struct request_queue *q)
for (i = 0; i < seg; i++) {
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;
+ curr_sg = h->cmd_sg_list[c->cmdindex];
sg_index = 0;
chained = 1;
}
@@ -3166,31 +3226,12 @@ static void do_cciss_request(struct request_queue *q)
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);
- }
+ if (chained)
+ cciss_map_sg_chain_block(h, c, h->cmd_sg_list[c->cmdindex],
+ (seg - (h->max_cmd_sgentries - 1)) *
+ sizeof(SGDescriptor_struct));
/* track how many SG entries we are using */
if (seg > h->maxSG)
@@ -3209,11 +3250,11 @@ static void do_cciss_request(struct request_queue *q)
if (likely(blk_fs_request(creq))) {
if(h->cciss_read == CCISS_READ_10) {
c->Request.CDB[1] = 0;
- c->Request.CDB[2] = (start_blk >> 24) & 0xff; //MSB
+ c->Request.CDB[2] = (start_blk >> 24) & 0xff; /* MSB */
c->Request.CDB[3] = (start_blk >> 16) & 0xff;
c->Request.CDB[4] = (start_blk >> 8) & 0xff;
c->Request.CDB[5] = start_blk & 0xff;
- c->Request.CDB[6] = 0; // (sect >> 24) & 0xff; MSB
+ c->Request.CDB[6] = 0; /* (sect >> 24) & 0xff; MSB */
c->Request.CDB[7] = (blk_rq_sectors(creq) >> 8) & 0xff;
c->Request.CDB[8] = blk_rq_sectors(creq) & 0xff;
c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
@@ -3222,7 +3263,7 @@ static void do_cciss_request(struct request_queue *q)
c->Request.CDBLen = 16;
c->Request.CDB[1]= 0;
- c->Request.CDB[2]= (upper32 >> 24) & 0xff; //MSB
+ c->Request.CDB[2]= (upper32 >> 24) & 0xff; /* MSB */
c->Request.CDB[3]= (upper32 >> 16) & 0xff;
c->Request.CDB[4]= (upper32 >> 8) & 0xff;
c->Request.CDB[5]= upper32 & 0xff;
@@ -4240,37 +4281,10 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
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);
+ hba[i]->cmd_sg_list = cciss_allocate_sg_chain_blocks(hba[i],
+ hba[i]->chainsize, hba[i]->nr_cmds);
+ if (!hba[i]->cmd_sg_list && hba[i]->chainsize > 0)
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);
@@ -4329,16 +4343,7 @@ clean4:
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);
- }
+ cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
if (hba[i]->cmd_pool)
pci_free_consistent(hba[i]->pdev,
hba[i]->nr_cmds * sizeof(CommandList_struct),
@@ -4456,16 +4461,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
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);
- }
+ cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
@@ -4498,7 +4494,7 @@ static int __init cciss_init(void)
* boundary. Given that we use pci_alloc_consistent() to allocate an
* array of them, the size must be a multiple of 8 bytes.
*/
- BUILD_BUG_ON(sizeof(CommandList_struct) % 8);
+ BUILD_BUG_ON(sizeof(CommandList_struct) % COMMANDLIST_ALIGNMENT);
printk(KERN_INFO DRIVER_NAME "\n");
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 1d95db25406..c5d411174db 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -55,18 +55,12 @@ typedef struct _drive_info_struct
char device_initialized; /* indicates whether dev is initialized */
} drive_info_struct;
-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];
char *product_name;
- char firm_ver[4]; // Firmware version
+ char firm_ver[4]; /* Firmware version */
struct pci_dev *pdev;
__u32 board_id;
void __iomem *vaddr;
@@ -89,7 +83,7 @@ struct ctlr_info
int maxsgentries;
int chainsize;
int max_cmd_sgentries;
- struct Cmd_sg_list **cmd_sg_list;
+ SGDescriptor_struct **cmd_sg_list;
# define DOORBELL_INT 0
# define PERF_MODE_INT 1
@@ -103,7 +97,7 @@ struct ctlr_info
BYTE cciss_write;
BYTE cciss_read_capacity;
- // information about each logical volume
+ /* information about each logical volume */
drive_info_struct *drv[CISS_MAX_LUN];
struct access_method access;
@@ -116,7 +110,7 @@ struct ctlr_info
unsigned int maxSG;
spinlock_t lock;
- //* pointers to command and error info pool */
+ /* pointers to command and error info pool */
CommandList_struct *cmd_pool;
dma_addr_t cmd_pool_dhandle;
ErrorInfo_struct *errinfo_pool;
@@ -134,12 +128,10 @@ struct ctlr_info
*/
int next_to_run;
- // Disk structures we need to pass back
+ /* Disk structures we need to pass back */
struct gendisk *gendisk[CISS_MAX_LUN];
#ifdef CONFIG_CISS_SCSI_TAPE
- void *scsi_ctlr; /* ptr to structure containing scsi related stuff */
- /* list of block side commands the scsi error handling sucked up */
- /* and saved for later processing */
+ struct cciss_scsi_adapter_data_t *scsi_ctlr;
#endif
unsigned char alive;
struct list_head scan_list;
@@ -315,4 +307,3 @@ struct board_type {
#define CCISS_LOCK(i) (&hba[i]->lock)
#endif /* CCISS_H */
-
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index 6afa700890f..e624ff959cb 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -1,31 +1,16 @@
#ifndef CCISS_CMD_H
#define CCISS_CMD_H
-//###########################################################################
-//DEFINES
-//###########################################################################
+
+#include <linux/cciss_defs.h>
+
+/* DEFINES */
#define CISS_VERSION "1.00"
-//general boundary definitions
-#define SENSEINFOBYTES 32//note that this value may vary between host implementations
+/* general boundary definitions */
#define MAXSGENTRIES 32
#define CCISS_SG_CHAIN 0x80000000
#define MAXREPLYQS 256
-//Command Status value
-#define CMD_SUCCESS 0x0000
-#define CMD_TARGET_STATUS 0x0001
-#define CMD_DATA_UNDERRUN 0x0002
-#define CMD_DATA_OVERRUN 0x0003
-#define CMD_INVALID 0x0004
-#define CMD_PROTOCOL_ERR 0x0005
-#define CMD_HARDWARE_ERR 0x0006
-#define CMD_CONNECTION_LOST 0x0007
-#define CMD_ABORTED 0x0008
-#define CMD_ABORT_FAILED 0x0009
-#define CMD_UNSOLICITED_ABORT 0x000A
-#define CMD_TIMEOUT 0x000B
-#define CMD_UNABORTABLE 0x000C
-
/* Unit Attentions ASC's as defined for the MSA2012sa */
#define POWER_OR_RESET 0x29
#define STATE_CHANGED 0x2a
@@ -49,30 +34,13 @@
#define ASYM_ACCESS_CHANGED 0x06
#define LUN_CAPACITY_CHANGED 0x09
-//transfer direction
-#define XFER_NONE 0x00
-#define XFER_WRITE 0x01
-#define XFER_READ 0x02
-#define XFER_RSVD 0x03
-
-//task attribute
-#define ATTR_UNTAGGED 0x00
-#define ATTR_SIMPLE 0x04
-#define ATTR_HEADOFQUEUE 0x05
-#define ATTR_ORDERED 0x06
-#define ATTR_ACA 0x07
-
-//cdb type
-#define TYPE_CMD 0x00
-#define TYPE_MSG 0x01
-
-//config space register offsets
+/* config space register offsets */
#define CFG_VENDORID 0x00
#define CFG_DEVICEID 0x02
#define CFG_I2OBAR 0x10
#define CFG_MEM1BAR 0x14
-//i2o space register offsets
+/* i2o space register offsets */
#define I2O_IBDB_SET 0x20
#define I2O_IBDB_CLEAR 0x70
#define I2O_INT_STATUS 0x30
@@ -81,7 +49,7 @@
#define I2O_OBPOST_Q 0x44
#define I2O_DMA1_CFG 0x214
-//Configuration Table
+/* Configuration Table */
#define CFGTBL_ChangeReq 0x00000001l
#define CFGTBL_AccCmds 0x00000001l
@@ -103,24 +71,17 @@ typedef union _u64bit
__u64 val;
} u64bit;
-// Type defs used in the following structs
-#define BYTE __u8
-#define WORD __u16
-#define HWORD __u16
-#define DWORD __u32
+/* Type defs used in the following structs */
#define QWORD vals32
-//###########################################################################
-//STRUCTURES
-//###########################################################################
-#define CISS_MAX_LUN 1024
+/* STRUCTURES */
#define CISS_MAX_PHYS_LUN 1024
-// SCSI-3 Cmmands
+/* SCSI-3 Cmmands */
#pragma pack(1)
#define CISS_INQUIRY 0x12
-//Date returned
+/* Date returned */
typedef struct _InquiryData_struct
{
BYTE data_byte[36];
@@ -128,7 +89,7 @@ typedef struct _InquiryData_struct
#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */
#define CISS_REPORT_PHYS 0xc3 /* Report Physical LUNs */
-// Data returned
+/* Data returned */
typedef struct _ReportLUNdata_struct
{
BYTE LUNListLength[4];
@@ -139,8 +100,8 @@ typedef struct _ReportLUNdata_struct
#define CCISS_READ_CAPACITY 0x25 /* Read Capacity */
typedef struct _ReadCapdata_struct
{
- BYTE total_size[4]; // Total size in blocks
- BYTE block_size[4]; // Size of blocks in bytes
+ BYTE total_size[4]; /* Total size in blocks */
+ BYTE block_size[4]; /* Size of blocks in bytes */
} ReadCapdata_struct;
#define CCISS_READ_CAPACITY_16 0x9e /* Read Capacity 16 */
@@ -172,52 +133,13 @@ typedef struct _ReadCapdata_struct_16
#define CDB_LEN10 10
#define CDB_LEN16 16
-// BMIC commands
+/* BMIC commands */
#define BMIC_READ 0x26
#define BMIC_WRITE 0x27
#define BMIC_CACHE_FLUSH 0xc2
-#define CCISS_CACHE_FLUSH 0x01 //C2 was already being used by CCISS
-
-//Command List Structure
-typedef union _SCSI3Addr_struct {
- struct {
- BYTE Dev;
- BYTE Bus:6;
- BYTE Mode:2; // b00
- } PeripDev;
- struct {
- BYTE DevLSB;
- BYTE DevMSB:6;
- BYTE Mode:2; // b01
- } LogDev;
- struct {
- BYTE Dev:5;
- BYTE Bus:3;
- BYTE Targ:6;
- BYTE Mode:2; // b10
- } LogUnit;
-} SCSI3Addr_struct;
-
-typedef struct _PhysDevAddr_struct {
- DWORD TargetId:24;
- DWORD Bus:6;
- DWORD Mode:2;
- SCSI3Addr_struct Target[2]; //2 level target device addr
-} PhysDevAddr_struct;
-
-typedef struct _LogDevAddr_struct {
- DWORD VolId:30;
- DWORD Mode:2;
- BYTE reserved[4];
-} LogDevAddr_struct;
-
-typedef union _LUNAddr_struct {
- BYTE LunAddrBytes[8];
- SCSI3Addr_struct SCSI3Lun[4];
- PhysDevAddr_struct PhysDev;
- LogDevAddr_struct LogDev;
-} LUNAddr_struct;
+#define CCISS_CACHE_FLUSH 0x01 /* C2 was already being used by CCISS */
+/* Command List Structure */
#define CTLR_LUNID "\0\0\0\0\0\0\0\0"
typedef struct _CommandListHeader_struct {
@@ -227,16 +149,6 @@ typedef struct _CommandListHeader_struct {
QWORD Tag;
LUNAddr_struct LUN;
} CommandListHeader_struct;
-typedef struct _RequestBlock_struct {
- BYTE CDBLen;
- struct {
- BYTE Type:3;
- BYTE Attribute:3;
- BYTE Direction:2;
- } Type;
- HWORD Timeout;
- BYTE CDB[16];
-} RequestBlock_struct;
typedef struct _ErrDescriptor_struct {
QWORD Addr;
DWORD Len;
@@ -247,28 +159,6 @@ typedef struct _SGDescriptor_struct {
DWORD Ext;
} SGDescriptor_struct;
-typedef union _MoreErrInfo_struct{
- struct {
- BYTE Reserved[3];
- BYTE Type;
- DWORD ErrorInfo;
- }Common_Info;
- struct{
- BYTE Reserved[2];
- BYTE offense_size;//size of offending entry
- BYTE offense_num; //byte # of offense 0-base
- DWORD offense_value;
- }Invalid_Cmd;
-}MoreErrInfo_struct;
-typedef struct _ErrorInfo_struct {
- BYTE ScsiStatus;
- BYTE SenseLen;
- HWORD CommandStatus;
- DWORD ResidualCnt;
- MoreErrInfo_struct MoreErrInfo;
- BYTE SenseInfo[SENSEINFOBYTES];
-} ErrorInfo_struct;
-
/* Command types */
#define CMD_RWREQ 0x00
#define CMD_IOCTL_PEND 0x01
@@ -277,10 +167,18 @@ typedef struct _ErrorInfo_struct {
#define CMD_MSG_TIMEOUT 0x05
#define CMD_MSG_STALE 0xff
-/* This structure needs to be divisible by 8 for new
- * indexing method.
+/* This structure needs to be divisible by COMMANDLIST_ALIGNMENT
+ * because low bits of the address are used to to indicate that
+ * whether the tag contains an index or an address. PAD_32 and
+ * PAD_64 can be adjusted independently as needed for 32-bit
+ * and 64-bits systems.
*/
-#define PADSIZE (sizeof(long) - 4)
+#define COMMANDLIST_ALIGNMENT (8)
+#define IS_64_BIT ((sizeof(long) - 4)/4)
+#define IS_32_BIT (!IS_64_BIT)
+#define PAD_32 (0)
+#define PAD_64 (4)
+#define PADSIZE (IS_32_BIT * PAD_32 + IS_64_BIT * PAD_64)
typedef struct _CommandList_struct {
CommandListHeader_struct Header;
RequestBlock_struct Request;
@@ -300,7 +198,7 @@ typedef struct _CommandList_struct {
char pad[PADSIZE];
} CommandList_struct;
-//Configuration Table Structure
+/* Configuration Table Structure */
typedef struct _HostWrite_struct {
DWORD TransportRequest;
DWORD Reserved;
@@ -326,4 +224,4 @@ typedef struct _CfgTable_struct {
DWORD MaxPhysicalDrivesPerLogicalUnit;
} CfgTable_struct;
#pragma pack()
-#endif // CCISS_CMD_H
+#endif /* CCISS_CMD_H */
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 5d0e46dc363..e1d0e2cfec7 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -84,7 +84,6 @@ static struct scsi_host_template cciss_driver_template = {
.queuecommand = cciss_scsi_queue_command,
.can_queue = SCSI_CCISS_CAN_QUEUE,
.this_id = 7,
- .sg_tablesize = MAXSGENTRIES,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
/* Can't have eh_bus_reset_handler or eh_host_reset_handler for cciss */
@@ -93,11 +92,16 @@ static struct scsi_host_template cciss_driver_template = {
};
#pragma pack(1)
+
+#define SCSI_PAD_32 0
+#define SCSI_PAD_64 0
+
struct cciss_scsi_cmd_stack_elem_t {
CommandList_struct cmd;
ErrorInfo_struct Err;
__u32 busaddr;
- __u32 pad;
+ int cmdindex;
+ u8 pad[IS_32_BIT * SCSI_PAD_32 + IS_64_BIT * SCSI_PAD_64];
};
#pragma pack()
@@ -118,16 +122,15 @@ struct cciss_scsi_cmd_stack_t {
struct cciss_scsi_adapter_data_t {
struct Scsi_Host *scsi_host;
struct cciss_scsi_cmd_stack_t cmd_stack;
+ SGDescriptor_struct **cmd_sg_list;
int registered;
spinlock_t lock; // to protect ccissscsi[ctlr];
};
#define CPQ_TAPE_LOCK(ctlr, flags) spin_lock_irqsave( \
- &(((struct cciss_scsi_adapter_data_t *) \
- hba[ctlr]->scsi_ctlr)->lock), flags);
+ &hba[ctlr]->scsi_ctlr->lock, flags);
#define CPQ_TAPE_UNLOCK(ctlr, flags) spin_unlock_irqrestore( \
- &(((struct cciss_scsi_adapter_data_t *) \
- hba[ctlr]->scsi_ctlr)->lock), flags);
+ &hba[ctlr]->scsi_ctlr->lock, flags);
static CommandList_struct *
scsi_cmd_alloc(ctlr_info_t *h)
@@ -143,7 +146,7 @@ scsi_cmd_alloc(ctlr_info_t *h)
struct cciss_scsi_cmd_stack_t *stk;
u64bit temp64;
- sa = (struct cciss_scsi_adapter_data_t *) h->scsi_ctlr;
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
if (stk->top < 0)
@@ -154,6 +157,7 @@ scsi_cmd_alloc(ctlr_info_t *h)
memset(&c->Err, 0, sizeof(c->Err));
/* set physical addr of cmd and addr of scsi parameters */
c->cmd.busaddr = c->busaddr;
+ c->cmd.cmdindex = c->cmdindex;
/* (__u32) (stk->cmd_pool_handle +
(sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top)); */
@@ -182,7 +186,7 @@ scsi_cmd_free(ctlr_info_t *h, CommandList_struct *cmd)
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
- sa = (struct cciss_scsi_adapter_data_t *) h->scsi_ctlr;
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
if (stk->top >= CMD_STACK_SIZE) {
printk("cciss: scsi_cmd_free called too many times.\n");
@@ -199,24 +203,31 @@ scsi_cmd_stack_setup(int ctlr, struct cciss_scsi_adapter_data_t *sa)
struct cciss_scsi_cmd_stack_t *stk;
size_t size;
+ sa->cmd_sg_list = cciss_allocate_sg_chain_blocks(hba[ctlr],
+ hba[ctlr]->chainsize, CMD_STACK_SIZE);
+ if (!sa->cmd_sg_list && hba[ctlr]->chainsize > 0)
+ return -ENOMEM;
+
stk = &sa->cmd_stack;
size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * CMD_STACK_SIZE;
- // pci_alloc_consistent guarantees 32-bit DMA address will
- // be used
-
+ /* Check alignment, see cciss_cmd.h near CommandList_struct def. */
+ BUILD_BUG_ON((sizeof(*stk->pool) % COMMANDLIST_ALIGNMENT) != 0);
+ /* pci_alloc_consistent guarantees 32-bit DMA address will be used */
stk->pool = (struct cciss_scsi_cmd_stack_elem_t *)
pci_alloc_consistent(hba[ctlr]->pdev, size, &stk->cmd_pool_handle);
if (stk->pool == NULL) {
- printk("stk->pool is null\n");
- return -1;
+ cciss_free_sg_chain_blocks(sa->cmd_sg_list, CMD_STACK_SIZE);
+ sa->cmd_sg_list = NULL;
+ return -ENOMEM;
}
for (i=0; i<CMD_STACK_SIZE; i++) {
stk->elem[i] = &stk->pool[i];
stk->elem[i]->busaddr = (__u32) (stk->cmd_pool_handle +
(sizeof(struct cciss_scsi_cmd_stack_elem_t) * i));
+ stk->elem[i]->cmdindex = i;
}
stk->top = CMD_STACK_SIZE-1;
return 0;
@@ -229,7 +240,7 @@ scsi_cmd_stack_free(int ctlr)
struct cciss_scsi_cmd_stack_t *stk;
size_t size;
- sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr;
+ sa = hba[ctlr]->scsi_ctlr;
stk = &sa->cmd_stack;
if (stk->top != CMD_STACK_SIZE-1) {
printk( "cciss: %d scsi commands are still outstanding.\n",
@@ -241,6 +252,7 @@ scsi_cmd_stack_free(int ctlr)
pci_free_consistent(hba[ctlr]->pdev, size, stk->pool, stk->cmd_pool_handle);
stk->pool = NULL;
+ cciss_free_sg_chain_blocks(sa->cmd_sg_list, CMD_STACK_SIZE);
}
#if 0
@@ -530,8 +542,7 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
CPQ_TAPE_LOCK(ctlr, flags);
if (hostno != -1) /* if it's not the first time... */
- sh = ((struct cciss_scsi_adapter_data_t *)
- hba[ctlr]->scsi_ctlr)->scsi_host;
+ sh = hba[ctlr]->scsi_ctlr->scsi_host;
/* find any devices in ccissscsi[] that are not in
sd[] and remove them from ccissscsi[] */
@@ -702,7 +713,7 @@ cciss_scsi_setup(int cntl_num)
kfree(shba);
shba = NULL;
}
- hba[cntl_num]->scsi_ctlr = (void *) shba;
+ hba[cntl_num]->scsi_ctlr = shba;
return;
}
@@ -725,6 +736,8 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
ctlr = hba[cp->ctlr];
scsi_dma_unmap(cmd);
+ if (cp->Header.SGTotal > ctlr->max_cmd_sgentries)
+ cciss_unmap_sg_chain_block(ctlr, cp);
cmd->result = (DID_OK << 16); /* host byte */
cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
@@ -847,9 +860,10 @@ cciss_scsi_detect(int ctlr)
sh->io_port = 0; // good enough? FIXME,
sh->n_io_port = 0; // I don't think we use these two...
sh->this_id = SELF_SCSI_ID;
+ sh->sg_tablesize = hba[ctlr]->maxsgentries;
((struct cciss_scsi_adapter_data_t *)
- hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh;
+ hba[ctlr]->scsi_ctlr)->scsi_host = sh;
sh->hostdata[0] = (unsigned long) hba[ctlr];
sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT];
sh->unique_id = sh->irq;
@@ -1364,34 +1378,54 @@ cciss_scsi_proc_info(struct Scsi_Host *sh,
dma mapping and fills in the scatter gather entries of the
cciss command, cp. */
-static void
-cciss_scatter_gather(struct pci_dev *pdev,
- CommandList_struct *cp,
- struct scsi_cmnd *cmd)
+static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
+ struct scsi_cmnd *cmd)
{
unsigned int len;
struct scatterlist *sg;
__u64 addr64;
- int use_sg, i;
-
- BUG_ON(scsi_sg_count(cmd) > MAXSGENTRIES);
-
- use_sg = scsi_dma_map(cmd);
- if (use_sg) { /* not too many addrs? */
- scsi_for_each_sg(cmd, sg, use_sg, i) {
+ int request_nsgs, i, chained, sg_index;
+ struct cciss_scsi_adapter_data_t *sa = h->scsi_ctlr;
+ SGDescriptor_struct *curr_sg;
+
+ BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
+
+ chained = 0;
+ sg_index = 0;
+ curr_sg = cp->SG;
+ request_nsgs = scsi_dma_map(cmd);
+ if (request_nsgs) {
+ scsi_for_each_sg(cmd, sg, request_nsgs, i) {
+ if (sg_index + 1 == h->max_cmd_sgentries &&
+ !chained && request_nsgs - i > 1) {
+ chained = 1;
+ sg_index = 0;
+ curr_sg = sa->cmd_sg_list[cp->cmdindex];
+ }
addr64 = (__u64) sg_dma_address(sg);
len = sg_dma_len(sg);
- cp->SG[i].Addr.lower =
- (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF);
- cp->SG[i].Addr.upper =
- (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF);
- cp->SG[i].Len = len;
- cp->SG[i].Ext = 0; // we are not chaining
+ curr_sg[sg_index].Addr.lower =
+ (__u32) (addr64 & 0x0FFFFFFFFULL);
+ curr_sg[sg_index].Addr.upper =
+ (__u32) ((addr64 >> 32) & 0x0FFFFFFFFULL);
+ curr_sg[sg_index].Len = len;
+ curr_sg[sg_index].Ext = 0;
+ ++sg_index;
}
+ if (chained)
+ cciss_map_sg_chain_block(h, cp,
+ sa->cmd_sg_list[cp->cmdindex],
+ (request_nsgs - (h->max_cmd_sgentries - 1)) *
+ sizeof(SGDescriptor_struct));
}
-
- cp->Header.SGList = (__u8) use_sg; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (__u16) use_sg; /* total sgs in this cmd list */
+ /* track how many SG entries we are using */
+ if (request_nsgs > h->maxSG)
+ h->maxSG = request_nsgs;
+ cp->Header.SGTotal = (__u8) request_nsgs + chained;
+ if (request_nsgs > h->max_cmd_sgentries)
+ cp->Header.SGList = h->max_cmd_sgentries;
+ else
+ cp->Header.SGList = cp->Header.SGTotal;
return;
}
@@ -1399,7 +1433,7 @@ cciss_scatter_gather(struct pci_dev *pdev,
static int
cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
{
- ctlr_info_t **c;
+ ctlr_info_t *c;
int ctlr, rc;
unsigned char scsi3addr[8];
CommandList_struct *cp;
@@ -1407,8 +1441,8 @@ cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd
// Get the ptr to our adapter structure (hba[i]) out of cmd->host.
// We violate cmd->host privacy here. (Is there another way?)
- c = (ctlr_info_t **) &cmd->device->host->hostdata[0];
- ctlr = (*c)->ctlr;
+ c = (ctlr_info_t *) cmd->device->host->hostdata[0];
+ ctlr = c->ctlr;
rc = lookup_scsi3addr(ctlr, cmd->device->channel, cmd->device->id,
cmd->device->lun, scsi3addr);
@@ -1431,7 +1465,7 @@ cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd
see what the device thinks of it. */
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- cp = scsi_cmd_alloc(*c);
+ cp = scsi_cmd_alloc(c);
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
if (cp == NULL) { /* trouble... */
printk("scsi_cmd_alloc returned NULL!\n");
@@ -1489,15 +1523,14 @@ cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd
BUG();
break;
}
-
- cciss_scatter_gather((*c)->pdev, cp, cmd); // Fill the SG list
+ cciss_scatter_gather(c, cp, cmd);
/* Put the request on the tail of the request queue */
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- addQ(&(*c)->reqQ, cp);
- (*c)->Qdepth++;
- start_io(*c);
+ addQ(&c->reqQ, cp);
+ c->Qdepth++;
+ start_io(c);
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
/* the cmd'll come back via intr handler in complete_scsi_command() */
@@ -1514,7 +1547,7 @@ cciss_unregister_scsi(int ctlr)
/* we are being forcibly unloaded, and may not refuse. */
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr;
+ sa = hba[ctlr]->scsi_ctlr;
stk = &sa->cmd_stack;
/* if we weren't ever actually registered, don't unregister */
@@ -1541,7 +1574,7 @@ cciss_engage_scsi(int ctlr)
unsigned long flags;
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr;
+ sa = hba[ctlr]->scsi_ctlr;
stk = &sa->cmd_stack;
if (sa->registered) {
@@ -1654,14 +1687,14 @@ static int cciss_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
int rc;
CommandList_struct *cmd_in_trouble;
unsigned char lunaddr[8];
- ctlr_info_t **c;
+ ctlr_info_t *c;
int ctlr;
/* find the controller to which the command to be aborted was sent */
- c = (ctlr_info_t **) &scsicmd->device->host->hostdata[0];
+ c = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
if (c == NULL) /* paranoia */
return FAILED;
- ctlr = (*c)->ctlr;
+ ctlr = c->ctlr;
printk(KERN_WARNING "cciss%d: resetting tape drive or medium changer.\n", ctlr);
/* find the command that's giving us trouble */
cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble;
@@ -1671,7 +1704,7 @@ static int cciss_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
/* send a reset to the SCSI LUN which the command was sent to */
rc = sendcmd_withirq(CCISS_RESET_MSG, ctlr, NULL, 0, 0, lunaddr,
TYPE_MSG);
- if (rc == 0 && wait_for_device_to_become_ready(*c, lunaddr) == 0)
+ if (rc == 0 && wait_for_device_to_become_ready(c, lunaddr) == 0)
return SUCCESS;
printk(KERN_WARNING "cciss%d: resetting device failed.\n", ctlr);
return FAILED;
@@ -1682,14 +1715,14 @@ static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd)
int rc;
CommandList_struct *cmd_to_abort;
unsigned char lunaddr[8];
- ctlr_info_t **c;
+ ctlr_info_t *c;
int ctlr;
/* find the controller to which the command to be aborted was sent */
- c = (ctlr_info_t **) &scsicmd->device->host->hostdata[0];
+ c = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
if (c == NULL) /* paranoia */
return FAILED;
- ctlr = (*c)->ctlr;
+ ctlr = c->ctlr;
printk(KERN_WARNING "cciss%d: aborting tardy SCSI cmd\n", ctlr);
/* find the command to be aborted */
diff --git a/drivers/block/cciss_scsi.h b/drivers/block/cciss_scsi.h
index 7b750245ae7..6d5822fe851 100644
--- a/drivers/block/cciss_scsi.h
+++ b/drivers/block/cciss_scsi.h
@@ -25,16 +25,16 @@
#include <scsi/scsicam.h> /* possibly irrelevant, since we don't show disks */
- // the scsi id of the adapter...
+ /* the scsi id of the adapter... */
#define SELF_SCSI_ID 15
- // 15 is somewhat arbitrary, since the scsi-2 bus
- // that's presented by the driver to the OS is
- // fabricated. The "real" scsi-3 bus the
- // hardware presents is fabricated too.
- // The actual, honest-to-goodness physical
- // bus that the devices are attached to is not
- // addressible natively, and may in fact turn
- // out to be not scsi at all.
+ /* 15 is somewhat arbitrary, since the scsi-2 bus
+ that's presented by the driver to the OS is
+ fabricated. The "real" scsi-3 bus the
+ hardware presents is fabricated too.
+ The actual, honest-to-goodness physical
+ bus that the devices are attached to is not
+ addressible natively, and may in fact turn
+ out to be not scsi at all. */
#define SCSI_CCISS_CAN_QUEUE 2
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index 6422651ec36..91d11631cec 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -448,11 +448,8 @@ static int __init cpqarray_register_ctlr( int i, struct pci_dev *pdev)
blk_queue_bounce_limit(q, hba[i]->pci_dev->dma_mask);
/* This is a hardware imposed limit. */
- blk_queue_max_hw_segments(q, SG_MAX);
+ blk_queue_max_segments(q, SG_MAX);
- /* This is a driver limit and could be eliminated. */
- blk_queue_max_phys_segments(q, SG_MAX);
-
init_timer(&hba[i]->timer);
hba[i]->timer.expires = jiffies + IDA_TIMER;
hba[i]->timer.data = (unsigned long)hba[i];
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 1292e062066..4df3b40b105 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -709,9 +709,8 @@ void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_seg_s) __mu
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_hw_sectors(q, max_seg_s >> 9);
+ blk_queue_max_segments(q, max_segments ? max_segments : BLK_MAX_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);
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 3266b4f65da..b9b117059b6 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -4234,7 +4234,7 @@ static int __init floppy_init(void)
err = -ENOMEM;
goto out_unreg_driver;
}
- blk_queue_max_sectors(floppy_queue, 64);
+ blk_queue_max_hw_sectors(floppy_queue, 64);
blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
floppy_find, NULL, NULL);
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index d5cdce08ffd..5116c65c07c 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -719,7 +719,7 @@ static int __init hd_init(void)
return -ENOMEM;
}
- blk_queue_max_sectors(hd_queue, 255);
+ blk_queue_max_hw_sectors(hd_queue, 255);
init_timer(&device_timer);
device_timer.function = hd_times_out;
blk_queue_logical_block_size(hd_queue, 512);
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
index 02b2583df7f..5416c9a606e 100644
--- a/drivers/block/mg_disk.c
+++ b/drivers/block/mg_disk.c
@@ -980,7 +980,7 @@ static int mg_probe(struct platform_device *plat_dev)
__func__, __LINE__);
goto probe_err_6;
}
- blk_queue_max_sectors(host->breq, MG_MAX_SECTS);
+ blk_queue_max_hw_sectors(host->breq, MG_MAX_SECTS);
blk_queue_logical_block_size(host->breq, MG_SECTOR_SIZE);
init_timer(&host->timer);
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 569e39e8f11..e712cd51af1 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -906,7 +906,7 @@ static int __init pd_init(void)
if (!pd_queue)
goto out1;
- blk_queue_max_sectors(pd_queue, cluster);
+ blk_queue_max_hw_sectors(pd_queue, cluster);
if (register_blkdev(major, name))
goto out2;
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index ea54ea39355..ddb4f9abd48 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -956,8 +956,7 @@ static int __init pf_init(void)
return -ENOMEM;
}
- blk_queue_max_phys_segments(pf_queue, cluster);
- blk_queue_max_hw_segments(pf_queue, cluster);
+ blk_queue_max_segments(pf_queue, cluster);
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
struct gendisk *disk = pf->disk;
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 68b5957f107..b72935b8f20 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -569,6 +569,7 @@ static struct packet_data *pkt_alloc_packet_data(int frames)
}
spin_lock_init(&pkt->lock);
+ bio_list_init(&pkt->orig_bios);
for (i = 0; i < frames; i++) {
struct bio *bio = pkt_bio_alloc(1);
@@ -721,43 +722,6 @@ static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *nod
}
/*
- * Add a bio to a single linked list defined by its head and tail pointers.
- */
-static void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
-{
- bio->bi_next = NULL;
- if (*list_tail) {
- BUG_ON((*list_head) == NULL);
- (*list_tail)->bi_next = bio;
- (*list_tail) = bio;
- } else {
- BUG_ON((*list_head) != NULL);
- (*list_head) = bio;
- (*list_tail) = bio;
- }
-}
-
-/*
- * Remove and return the first bio from a single linked list defined by its
- * head and tail pointers.
- */
-static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio **list_tail)
-{
- struct bio *bio;
-
- if (*list_head == NULL)
- return NULL;
-
- bio = *list_head;
- *list_head = bio->bi_next;
- if (*list_head == NULL)
- *list_tail = NULL;
-
- bio->bi_next = NULL;
- return bio;
-}
-
-/*
* Send a packet_command to the underlying block device and
* wait for completion.
*/
@@ -876,13 +840,10 @@ static noinline_for_stack int pkt_set_speed(struct pktcdvd_device *pd,
static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio)
{
spin_lock(&pd->iosched.lock);
- if (bio_data_dir(bio) == READ) {
- pkt_add_list_last(bio, &pd->iosched.read_queue,
- &pd->iosched.read_queue_tail);
- } else {
- pkt_add_list_last(bio, &pd->iosched.write_queue,
- &pd->iosched.write_queue_tail);
- }
+ if (bio_data_dir(bio) == READ)
+ bio_list_add(&pd->iosched.read_queue, bio);
+ else
+ bio_list_add(&pd->iosched.write_queue, bio);
spin_unlock(&pd->iosched.lock);
atomic_set(&pd->iosched.attention, 1);
@@ -917,8 +878,8 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
int reads_queued, writes_queued;
spin_lock(&pd->iosched.lock);
- reads_queued = (pd->iosched.read_queue != NULL);
- writes_queued = (pd->iosched.write_queue != NULL);
+ reads_queued = !bio_list_empty(&pd->iosched.read_queue);
+ writes_queued = !bio_list_empty(&pd->iosched.write_queue);
spin_unlock(&pd->iosched.lock);
if (!reads_queued && !writes_queued)
@@ -927,7 +888,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
if (pd->iosched.writing) {
int need_write_seek = 1;
spin_lock(&pd->iosched.lock);
- bio = pd->iosched.write_queue;
+ bio = bio_list_peek(&pd->iosched.write_queue);
spin_unlock(&pd->iosched.lock);
if (bio && (bio->bi_sector == pd->iosched.last_write))
need_write_seek = 0;
@@ -950,13 +911,10 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
}
spin_lock(&pd->iosched.lock);
- if (pd->iosched.writing) {
- bio = pkt_get_list_first(&pd->iosched.write_queue,
- &pd->iosched.write_queue_tail);
- } else {
- bio = pkt_get_list_first(&pd->iosched.read_queue,
- &pd->iosched.read_queue_tail);
- }
+ if (pd->iosched.writing)
+ bio = bio_list_pop(&pd->iosched.write_queue);
+ else
+ bio = bio_list_pop(&pd->iosched.read_queue);
spin_unlock(&pd->iosched.lock);
if (!bio)
@@ -992,14 +950,14 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
static int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_queue *q)
{
if ((pd->settings.size << 9) / CD_FRAMESIZE
- <= queue_max_phys_segments(q)) {
+ <= queue_max_segments(q)) {
/*
* The cdrom device can handle one segment/frame
*/
clear_bit(PACKET_MERGE_SEGS, &pd->flags);
return 0;
} else if ((pd->settings.size << 9) / PAGE_SIZE
- <= queue_max_phys_segments(q)) {
+ <= queue_max_segments(q)) {
/*
* We can handle this case at the expense of some extra memory
* copies during write operations
@@ -1114,7 +1072,7 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
int f;
char written[PACKET_MAX_SIZE];
- BUG_ON(!pkt->orig_bios);
+ BUG_ON(bio_list_empty(&pkt->orig_bios));
atomic_set(&pkt->io_wait, 0);
atomic_set(&pkt->io_errors, 0);
@@ -1124,7 +1082,7 @@ static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
*/
memset(written, 0, sizeof(written));
spin_lock(&pkt->lock);
- for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+ bio_list_for_each(bio, &pkt->orig_bios) {
int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
int num_frames = bio->bi_size / CD_FRAMESIZE;
pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9);
@@ -1363,7 +1321,7 @@ try_next_bio:
break;
pkt_rbtree_erase(pd, node);
spin_lock(&pkt->lock);
- pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail);
+ bio_list_add(&pkt->orig_bios, bio);
pkt->write_size += bio->bi_size / CD_FRAMESIZE;
spin_unlock(&pkt->lock);
}
@@ -1409,7 +1367,7 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
*/
frames_write = 0;
spin_lock(&pkt->lock);
- for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+ bio_list_for_each(bio, &pkt->orig_bios) {
int segment = bio->bi_idx;
int src_offs = 0;
int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
@@ -1472,20 +1430,14 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
static void pkt_finish_packet(struct packet_data *pkt, int uptodate)
{
- struct bio *bio, *next;
+ struct bio *bio;
if (!uptodate)
pkt->cache_valid = 0;
/* Finish all bios corresponding to this packet */
- bio = pkt->orig_bios;
- while (bio) {
- next = bio->bi_next;
- bio->bi_next = NULL;
+ while ((bio = bio_list_pop(&pkt->orig_bios)))
bio_endio(bio, uptodate ? 0 : -EIO);
- bio = next;
- }
- pkt->orig_bios = pkt->orig_bios_tail = NULL;
}
static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
@@ -2360,7 +2312,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, fmode_t write)
* even if the size is a multiple of the packet size.
*/
spin_lock_irq(q->queue_lock);
- blk_queue_max_sectors(q, pd->settings.size);
+ blk_queue_max_hw_sectors(q, pd->settings.size);
spin_unlock_irq(q->queue_lock);
set_bit(PACKET_WRITABLE, &pd->flags);
} else {
@@ -2567,8 +2519,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
spin_lock(&pkt->lock);
if ((pkt->state == PACKET_WAITING_STATE) ||
(pkt->state == PACKET_READ_WAIT_STATE)) {
- pkt_add_list_last(bio, &pkt->orig_bios,
- &pkt->orig_bios_tail);
+ bio_list_add(&pkt->orig_bios, bio);
pkt->write_size += bio->bi_size / CD_FRAMESIZE;
if ((pkt->write_size >= pkt->frames) &&
(pkt->state == PACKET_WAITING_STATE)) {
@@ -2662,7 +2613,7 @@ static void pkt_init_queue(struct pktcdvd_device *pd)
blk_queue_make_request(q, pkt_make_request);
blk_queue_logical_block_size(q, CD_FRAMESIZE);
- blk_queue_max_sectors(q, PACKET_MAX_SECTORS);
+ blk_queue_max_hw_sectors(q, PACKET_MAX_SECTORS);
blk_queue_merge_bvec(q, pkt_merge_bvec);
q->queuedata = pd;
}
@@ -2898,6 +2849,8 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev)
spin_lock_init(&pd->lock);
spin_lock_init(&pd->iosched.lock);
+ bio_list_init(&pd->iosched.read_queue);
+ bio_list_init(&pd->iosched.write_queue);
sprintf(pd->name, DRIVER_NAME"%d", idx);
init_waitqueue_head(&pd->wqueue);
pd->bio_queue = RB_ROOT;
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 03a130dca8a..bc95469d33c 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -474,7 +474,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
- blk_queue_max_sectors(queue, dev->bounce_size >> 9);
+ blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
blk_queue_segment_boundary(queue, -1UL);
blk_queue_dma_alignment(queue, dev->blk_size-1);
blk_queue_logical_block_size(queue, dev->blk_size);
@@ -482,8 +482,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH,
ps3disk_prepare_flush);
- blk_queue_max_phys_segments(queue, -1);
- blk_queue_max_hw_segments(queue, -1);
+ blk_queue_max_segments(queue, -1);
blk_queue_max_segment_size(queue, dev->bounce_size);
gendisk = alloc_disk(PS3DISK_MINORS);
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index 1fb6c3135fc..e4460822997 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -751,10 +751,9 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
priv->queue = queue;
queue->queuedata = dev;
blk_queue_make_request(queue, ps3vram_make_request);
- blk_queue_max_phys_segments(queue, MAX_PHYS_SEGMENTS);
- blk_queue_max_hw_segments(queue, MAX_HW_SEGMENTS);
- blk_queue_max_segment_size(queue, MAX_SEGMENT_SIZE);
- blk_queue_max_sectors(queue, SAFE_MAX_SECTORS);
+ blk_queue_max_segments(queue, BLK_MAX_SEGMENTS);
+ blk_queue_max_segment_size(queue, BLK_MAX_SEGMENT_SIZE);
+ blk_queue_max_hw_sectors(queue, BLK_SAFE_MAX_SECTORS);
gendisk = alloc_disk(1);
if (!gendisk) {
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
index 411f064760b..48e8fee9f2d 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -691,9 +691,8 @@ static int probe_disk(struct vdc_port *port)
port->disk = g;
- blk_queue_max_hw_segments(q, port->ring_cookies);
- blk_queue_max_phys_segments(q, port->ring_cookies);
- blk_queue_max_sectors(q, port->max_xfer_size);
+ blk_queue_max_segments(q, port->ring_cookies);
+ blk_queue_max_hw_sectors(q, port->max_xfer_size);
g->major = vdc_major;
g->first_minor = port->vio.vdev->dev_no << PARTITION_SHIFT;
strcpy(g->disk_name, port->disk_name);
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index 8f569e3df89..821c2833f9c 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -864,7 +864,7 @@ static int __devinit swim_probe(struct platform_device *dev)
struct swim_priv *swd;
int ret;
- res = platform_get_resource_byname(dev, IORESOURCE_MEM, "swim-regs");
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto out;
@@ -942,7 +942,7 @@ static int __devexit swim_remove(struct platform_device *dev)
iounmap(swd->base);
- res = platform_get_resource_byname(dev, IORESOURCE_MEM, "swim-regs");
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c
index a7c4184f4a6..b70f0fca9a4 100644
--- a/drivers/block/sx8.c
+++ b/drivers/block/sx8.c
@@ -409,7 +409,7 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
static void carm_remove_one (struct pci_dev *pdev);
static int carm_bdev_getgeo(struct block_device *bdev, struct hd_geometry *geo);
-static struct pci_device_id carm_pci_tbl[] = {
+static const struct pci_device_id carm_pci_tbl[] = {
{ PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
{ PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
{ } /* terminate list */
@@ -1518,8 +1518,7 @@ static int carm_init_disks(struct carm_host *host)
break;
}
disk->queue = q;
- blk_queue_max_hw_segments(q, CARM_MAX_REQ_SG);
- blk_queue_max_phys_segments(q, CARM_MAX_REQ_SG);
+ blk_queue_max_segments(q, CARM_MAX_REQ_SG);
blk_queue_segment_boundary(q, CARM_SG_BOUNDARY);
q->queuedata = port;
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index c739b203fe9..2e889838e81 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -393,7 +393,7 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum);
#define ub_usb_ids usb_storage_usb_ids
#else
-static struct usb_device_id ub_usb_ids[] = {
+static const struct usb_device_id ub_usb_ids[] = {
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
{ }
};
@@ -2320,10 +2320,9 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum)
disk->queue = q;
blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
- blk_queue_max_hw_segments(q, UB_MAX_REQ_SG);
- blk_queue_max_phys_segments(q, UB_MAX_REQ_SG);
+ blk_queue_max_segments(q, UB_MAX_REQ_SG);
blk_queue_segment_boundary(q, 0xffffffff); /* Dubious. */
- blk_queue_max_sectors(q, UB_MAX_SECTORS);
+ blk_queue_max_hw_sectors(q, UB_MAX_SECTORS);
blk_queue_logical_block_size(q, lun->capacity.bsize);
lun->disk = disk;
diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c
index a8c8b56b275..788d93882ab 100644
--- a/drivers/block/viodasd.c
+++ b/drivers/block/viodasd.c
@@ -28,6 +28,9 @@
* All disk operations are performed by sending messages back and forth to
* the OS/400 partition.
*/
+
+#define pr_fmt(fmt) "viod: " fmt
+
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/module.h>
@@ -63,9 +66,6 @@ MODULE_LICENSE("GPL");
#define VIOD_VERS "1.64"
-#define VIOD_KERN_WARNING KERN_WARNING "viod: "
-#define VIOD_KERN_INFO KERN_INFO "viod: "
-
enum {
PARTITION_SHIFT = 3,
MAX_DISKNO = HVMAXARCHITECTEDVIRTUALDISKS,
@@ -156,7 +156,7 @@ static int viodasd_open(struct block_device *bdev, fmode_t mode)
((u64)DEVICE_NO(d) << 48) | ((u64)flags << 32),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOD_KERN_WARNING "HV open failed %d\n", (int)hvrc);
+ pr_warning("HV open failed %d\n", (int)hvrc);
return -EIO;
}
@@ -167,9 +167,8 @@ static int viodasd_open(struct block_device *bdev, fmode_t mode)
const struct vio_error_entry *err =
vio_lookup_rc(viodasd_err_table, we.sub_result);
- printk(VIOD_KERN_WARNING
- "bad rc opening disk: %d:0x%04x (%s)\n",
- (int)we.rc, we.sub_result, err->msg);
+ pr_warning("bad rc opening disk: %d:0x%04x (%s)\n",
+ (int)we.rc, we.sub_result, err->msg);
return -EIO;
}
@@ -195,8 +194,7 @@ static int viodasd_release(struct gendisk *disk, fmode_t mode)
((u64)DEVICE_NO(d) << 48) /* | ((u64)flags << 32) */,
0, 0, 0);
if (hvrc != 0)
- printk(VIOD_KERN_WARNING "HV close call failed %d\n",
- (int)hvrc);
+ pr_warning("HV close call failed %d\n", (int)hvrc);
return 0;
}
@@ -288,8 +286,7 @@ static int send_request(struct request *req)
bevent = (struct vioblocklpevent *)
vio_get_event_buffer(viomajorsubtype_blockio);
if (bevent == NULL) {
- printk(VIOD_KERN_WARNING
- "error allocating disk event buffer\n");
+ pr_warning("error allocating disk event buffer\n");
goto error_ret;
}
@@ -333,9 +330,8 @@ static int send_request(struct request *req)
}
if (hvrc != HvLpEvent_Rc_Good) {
- printk(VIOD_KERN_WARNING
- "error sending disk event to OS/400 (rc %d)\n",
- (int)hvrc);
+ pr_warning("error sending disk event to OS/400 (rc %d)\n",
+ (int)hvrc);
goto error_ret;
}
spin_unlock_irqrestore(&viodasd_spinlock, flags);
@@ -402,7 +398,7 @@ retry:
((u64)dev_no << 48) | ((u64)flags<< 32),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc);
+ pr_warning("bad rc on HV open %d\n", (int)hvrc);
return 0;
}
@@ -416,9 +412,8 @@ retry:
goto retry;
}
if (we.max_disk > (MAX_DISKNO - 1)) {
- printk_once(VIOD_KERN_INFO
- "Only examining the first %d of %d disks connected\n",
- MAX_DISKNO, we.max_disk + 1);
+ printk_once(KERN_INFO pr_fmt("Only examining the first %d of %d disks connected\n"),
+ MAX_DISKNO, we.max_disk + 1);
}
/* Send the close event to OS/400. We DON'T expect a response */
@@ -432,17 +427,15 @@ retry:
((u64)dev_no << 48) | ((u64)flags << 32),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOD_KERN_WARNING
- "bad rc sending event to OS/400 %d\n", (int)hvrc);
+ pr_warning("bad rc sending event to OS/400 %d\n", (int)hvrc);
return 0;
}
if (d->dev == NULL) {
/* this is when we reprobe for new disks */
if (vio_create_viodasd(dev_no) == NULL) {
- printk(VIOD_KERN_WARNING
- "cannot allocate virtual device for disk %d\n",
- dev_no);
+ pr_warning("cannot allocate virtual device for disk %d\n",
+ dev_no);
return 0;
}
/*
@@ -457,23 +450,20 @@ retry:
spin_lock_init(&d->q_lock);
q = blk_init_queue(do_viodasd_request, &d->q_lock);
if (q == NULL) {
- printk(VIOD_KERN_WARNING "cannot allocate queue for disk %d\n",
- dev_no);
+ pr_warning("cannot allocate queue for disk %d\n", dev_no);
return 0;
}
g = alloc_disk(1 << PARTITION_SHIFT);
if (g == NULL) {
- printk(VIOD_KERN_WARNING
- "cannot allocate disk structure for disk %d\n",
- dev_no);
+ pr_warning("cannot allocate disk structure for disk %d\n",
+ dev_no);
blk_cleanup_queue(q);
return 0;
}
d->disk = g;
- blk_queue_max_hw_segments(q, VIOMAXBLOCKDMA);
- blk_queue_max_phys_segments(q, VIOMAXBLOCKDMA);
- blk_queue_max_sectors(q, VIODASD_MAXSECTORS);
+ blk_queue_max_segments(q, VIOMAXBLOCKDMA);
+ blk_queue_max_hw_sectors(q, VIODASD_MAXSECTORS);
g->major = VIODASD_MAJOR;
g->first_minor = dev_no << PARTITION_SHIFT;
if (dev_no >= 26)
@@ -489,13 +479,12 @@ retry:
g->driverfs_dev = d->dev;
set_capacity(g, d->size >> 9);
- printk(VIOD_KERN_INFO "disk %d: %lu sectors (%lu MB) "
- "CHS=%d/%d/%d sector size %d%s\n",
- dev_no, (unsigned long)(d->size >> 9),
- (unsigned long)(d->size >> 20),
- (int)d->cylinders, (int)d->tracks,
- (int)d->sectors, (int)d->bytes_per_sector,
- d->read_only ? " (RO)" : "");
+ pr_info("disk %d: %lu sectors (%lu MB) CHS=%d/%d/%d sector size %d%s\n",
+ dev_no, (unsigned long)(d->size >> 9),
+ (unsigned long)(d->size >> 20),
+ (int)d->cylinders, (int)d->tracks,
+ (int)d->sectors, (int)d->bytes_per_sector,
+ d->read_only ? " (RO)" : "");
/* register us in the global list */
add_disk(g);
@@ -580,8 +569,8 @@ static int viodasd_handle_read_write(struct vioblocklpevent *bevent)
if (error) {
const struct vio_error_entry *err;
err = vio_lookup_rc(viodasd_err_table, bevent->sub_result);
- printk(VIOD_KERN_WARNING "read/write error %d:0x%04x (%s)\n",
- event->xRc, bevent->sub_result, err->msg);
+ pr_warning("read/write error %d:0x%04x (%s)\n",
+ event->xRc, bevent->sub_result, err->msg);
num_sect = blk_rq_sectors(req);
}
qlock = req->q->queue_lock;
@@ -606,8 +595,7 @@ static void handle_block_event(struct HvLpEvent *event)
return;
/* First, we should NEVER get an int here...only acks */
if (hvlpevent_is_int(event)) {
- printk(VIOD_KERN_WARNING
- "Yikes! got an int in viodasd event handler!\n");
+ pr_warning("Yikes! got an int in viodasd event handler!\n");
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
@@ -650,7 +638,7 @@ static void handle_block_event(struct HvLpEvent *event)
break;
default:
- printk(VIOD_KERN_WARNING "invalid subtype!");
+ pr_warning("invalid subtype!");
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
@@ -739,29 +727,26 @@ static int __init viodasd_init(void)
vio_set_hostlp();
if (viopath_hostLp == HvLpIndexInvalid) {
- printk(VIOD_KERN_WARNING "invalid hosting partition\n");
+ pr_warning("invalid hosting partition\n");
rc = -EIO;
goto early_fail;
}
- printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n",
- viopath_hostLp);
+ pr_info("vers " VIOD_VERS ", hosting partition %d\n", viopath_hostLp);
/* register the block device */
rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
if (rc) {
- printk(VIOD_KERN_WARNING
- "Unable to get major number %d for %s\n",
- VIODASD_MAJOR, VIOD_GENHD_NAME);
+ pr_warning("Unable to get major number %d for %s\n",
+ VIODASD_MAJOR, VIOD_GENHD_NAME);
goto early_fail;
}
/* Actually open the path to the hosting partition */
rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio,
VIOMAXREQ + 2);
if (rc) {
- printk(VIOD_KERN_WARNING
- "error opening path to host partition %d\n",
- viopath_hostLp);
+ pr_warning("error opening path to host partition %d\n",
+ viopath_hostLp);
goto unregister_blk;
}
@@ -770,7 +755,7 @@ static int __init viodasd_init(void)
rc = vio_register_driver(&viodasd_driver);
if (rc) {
- printk(VIOD_KERN_WARNING "vio_register_driver failed\n");
+ pr_warning("vio_register_driver failed\n");
goto unset_handler;
}
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 51042f0ba7e..3c64af05fa8 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -243,10 +243,12 @@ static int index_to_minor(int index)
static int __devinit virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
+ struct request_queue *q;
int err;
u64 cap;
- u32 v;
- u32 blk_size, sg_elems;
+ u32 v, blk_size, sg_elems, opt_io_size;
+ u16 min_io_size;
+ u8 physical_block_exp, alignment_offset;
if (index_to_minor(index) >= 1 << MINORBITS)
return -ENOSPC;
@@ -293,13 +295,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
goto out_mempool;
}
- vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock);
- if (!vblk->disk->queue) {
+ q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock);
+ if (!q) {
err = -ENOMEM;
goto out_put_disk;
}
- vblk->disk->queue->queuedata = vblk;
+ q->queuedata = vblk;
if (index < 26) {
sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26);
@@ -323,10 +325,10 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
/* If barriers are supported, tell block layer that queue is ordered */
if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
- blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_DRAIN_FLUSH,
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH,
virtblk_prepare_flush);
else if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER))
- blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL);
+ blk_queue_ordered(q, QUEUE_ORDERED_TAG, NULL);
/* If disk is read-only in the host, the guest should obey */
if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))
@@ -345,14 +347,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
set_capacity(vblk->disk, cap);
/* We can handle whatever the host told us to handle. */
- blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2);
- blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2);
+ blk_queue_max_phys_segments(q, vblk->sg_elems-2);
+ blk_queue_max_hw_segments(q, vblk->sg_elems-2);
/* No need to bounce any requests */
- blk_queue_bounce_limit(vblk->disk->queue, BLK_BOUNCE_ANY);
+ blk_queue_bounce_limit(q, BLK_BOUNCE_ANY);
/* No real sector limit. */
- blk_queue_max_sectors(vblk->disk->queue, -1U);
+ blk_queue_max_sectors(q, -1U);
/* Host can optionally specify maximum segment size and number of
* segments. */
@@ -360,16 +362,45 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
offsetof(struct virtio_blk_config, size_max),
&v);
if (!err)
- blk_queue_max_segment_size(vblk->disk->queue, v);
+ blk_queue_max_segment_size(q, v);
else
- blk_queue_max_segment_size(vblk->disk->queue, -1U);
+ blk_queue_max_segment_size(q, -1U);
/* Host can optionally specify the block size of the device */
err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE,
offsetof(struct virtio_blk_config, blk_size),
&blk_size);
if (!err)
- blk_queue_logical_block_size(vblk->disk->queue, blk_size);
+ blk_queue_logical_block_size(q, blk_size);
+ else
+ blk_size = queue_logical_block_size(q);
+
+ /* Use topology information if available */
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ offsetof(struct virtio_blk_config, physical_block_exp),
+ &physical_block_exp);
+ if (!err && physical_block_exp)
+ blk_queue_physical_block_size(q,
+ blk_size * (1 << physical_block_exp));
+
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ offsetof(struct virtio_blk_config, alignment_offset),
+ &alignment_offset);
+ if (!err && alignment_offset)
+ blk_queue_alignment_offset(q, blk_size * alignment_offset);
+
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ offsetof(struct virtio_blk_config, min_io_size),
+ &min_io_size);
+ if (!err && min_io_size)
+ blk_queue_io_min(q, blk_size * min_io_size);
+
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ offsetof(struct virtio_blk_config, opt_io_size),
+ &opt_io_size);
+ if (!err && opt_io_size)
+ blk_queue_io_opt(q, blk_size * opt_io_size);
+
add_disk(vblk->disk);
return 0;
@@ -404,7 +435,7 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
kfree(vblk);
}
-static struct virtio_device_id id_table[] = {
+static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
{ 0 },
};
@@ -412,7 +443,7 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX,
VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
- VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_FLUSH
+ VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY
};
/*
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index d1fd032e751..1a325fb05c9 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -242,7 +242,7 @@ static int __init xd_init(void)
}
/* xd_maxsectors depends on controller - so set after detection */
- blk_queue_max_sectors(xd_queue, xd_maxsectors);
+ blk_queue_max_hw_sectors(xd_queue, xd_maxsectors);
for (i = 0; i < xd_drives; i++)
add_disk(xd_gendisk[i]);
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 05a31e55d27..9c09694b252 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -346,15 +346,14 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
/* Hard sector size and max sectors impersonate the equiv. hardware. */
blk_queue_logical_block_size(rq, sector_size);
- blk_queue_max_sectors(rq, 512);
+ blk_queue_max_hw_sectors(rq, 512);
/* Each segment in a request is up to an aligned page in size. */
blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
blk_queue_max_segment_size(rq, PAGE_SIZE);
/* Ensure a merged request will fit in a single I/O ring slot. */
- blk_queue_max_phys_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
- blk_queue_max_hw_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+ blk_queue_max_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
/* Make sure buffer addresses are sector-aligned. */
blk_queue_dma_alignment(rq, 511);
@@ -1050,7 +1049,7 @@ static const struct block_device_operations xlvbd_block_fops =
};
-static struct xenbus_device_id blkfront_ids[] = {
+static const struct xenbus_device_id blkfront_ids[] = {
{ "vbd" },
{ "" }
};
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index e5c5415eb45..e1c95e208a6 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -1227,7 +1227,7 @@ static int __devexit ace_of_remove(struct of_device *op)
}
/* Match table for of_platform binding */
-static struct of_device_id ace_of_match[] __devinitdata = {
+static const struct of_device_id ace_of_match[] __devinitconst = {
{ .compatible = "xlnx,opb-sysace-1.00.b", },
{ .compatible = "xlnx,opb-sysace-1.00.c", },
{ .compatible = "xlnx,xps-sysace-1.00.a", },
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index e789e6c9a42..03c71f7698c 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -741,7 +741,7 @@ static int __devinit probe_gdrom_setupqueue(void)
{
blk_queue_logical_block_size(gd.gdrom_rq, GDROM_HARD_SECTOR);
/* using DMA so memory will need to be contiguous */
- blk_queue_max_hw_segments(gd.gdrom_rq, 1);
+ blk_queue_max_segments(gd.gdrom_rq, 1);
/* set a large max size to get most from DMA */
blk_queue_max_segment_size(gd.gdrom_rq, 0x40000);
gd.disk->queue = gd.gdrom_rq;
diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c
index 57ca69e0ac5..cc435be0bc1 100644
--- a/drivers/cdrom/viocd.c
+++ b/drivers/cdrom/viocd.c
@@ -616,9 +616,8 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
gendisk->first_minor = deviceno;
strncpy(gendisk->disk_name, c->name,
sizeof(gendisk->disk_name));
- blk_queue_max_hw_segments(q, 1);
- blk_queue_max_phys_segments(q, 1);
- blk_queue_max_sectors(q, 4096 / 512);
+ blk_queue_max_segments(q, 1);
+ blk_queue_max_hw_sectors(q, 4096 / 512);
gendisk->queue = q;
gendisk->fops = &viocd_fops;
gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e023682be2c..3141dd3b6e5 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -666,6 +666,14 @@ config VIRTIO_CONSOLE
help
Virtio console for use with lguest and other hypervisors.
+ Also serves as a general-purpose serial device for data
+ transfer between the guest and host. Character devices at
+ /dev/vportNpn will be created when corresponding ports are
+ found, where N is the device number and n is the port number
+ within that device. If specified by the host, a sysfs
+ attribute called 'name' will be populated with a name for
+ the port which can be used by udev scripts to create a
+ symlink to the device.
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 3999a5f25f3..8a713f1e965 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/pagemap.h>
#include <linux/agp_backend.h>
+#include <asm/smp.h>
#include "agp.h"
/*
@@ -815,12 +816,6 @@ static void intel_i830_setup_flush(void)
intel_i830_fini_flush();
}
-static void
-do_wbinvd(void *null)
-{
- wbinvd();
-}
-
/* The chipset_flush interface needs to get data that has already been
* flushed out of the CPU all the way out to main memory, because the GPU
* doesn't snoop those buffers.
@@ -837,12 +832,10 @@ static void intel_i830_chipset_flush(struct agp_bridge_data *bridge)
memset(pg, 0, 1024);
- if (cpu_has_clflush) {
+ if (cpu_has_clflush)
clflush_cache_range(pg, 1024);
- } else {
- if (on_each_cpu(do_wbinvd, NULL, 1) != 0)
- printk(KERN_ERR "Timed out waiting for cache flush.\n");
- }
+ else if (wbinvd_on_all_cpus() != 0)
+ printk(KERN_ERR "Timed out waiting for cache flush.\n");
}
/* The intel i830 automatically initializes the agp aperture during POST.
diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c
index 0afc8b82212..5fe4631e2a6 100644
--- a/drivers/char/hvc_beat.c
+++ b/drivers/char/hvc_beat.c
@@ -84,7 +84,7 @@ static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt)
return cnt;
}
-static struct hv_ops hvc_beat_get_put_ops = {
+static const struct hv_ops hvc_beat_get_put_ops = {
.get_chars = hvc_beat_get_chars,
.put_chars = hvc_beat_put_chars,
};
@@ -99,7 +99,7 @@ static int hvc_beat_config(char *p)
static int __init hvc_beat_console_init(void)
{
- if (hvc_beat_useit && machine_is_compatible("Beat")) {
+ if (hvc_beat_useit && of_machine_is_compatible("Beat")) {
hvc_instantiate(0, 0, &hvc_beat_get_put_ops);
}
return 0;
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 416d3423150..4c3b59be286 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -125,7 +125,7 @@ static struct hvc_struct *hvc_get_by_index(int index)
* console interfaces but can still be used as a tty device. This has to be
* static because kmalloc will not work during early console init.
*/
-static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
+static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =
{[0 ... MAX_NR_HVC_CONSOLES - 1] = -1};
@@ -247,7 +247,7 @@ static void destroy_hvc_struct(struct kref *kref)
* vty adapters do NOT get an hvc_instantiate() callback since they
* appear after early console init.
*/
-int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
+int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
{
struct hvc_struct *hp;
@@ -748,8 +748,9 @@ static const struct tty_operations hvc_ops = {
.chars_in_buffer = hvc_chars_in_buffer,
};
-struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
- struct hv_ops *ops, int outbuf_size)
+struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
+ const struct hv_ops *ops,
+ int outbuf_size)
{
struct hvc_struct *hp;
int i;
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 10950ca706d..54381eba4e4 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -55,7 +55,7 @@ struct hvc_struct {
int outbuf_size;
int n_outbuf;
uint32_t vtermno;
- struct hv_ops *ops;
+ const struct hv_ops *ops;
int irq_requested;
int data;
struct winsize ws;
@@ -76,11 +76,12 @@ struct hv_ops {
};
/* Register a vterm and a slot index for use as a console (console_init) */
-extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
+extern int hvc_instantiate(uint32_t vtermno, int index,
+ const struct hv_ops *ops);
/* register a vterm for hvc tty operation (module_init or hotplug add) */
-extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
- struct hv_ops *ops, int outbuf_size);
+extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data,
+ const struct hv_ops *ops, int outbuf_size);
/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
extern int hvc_remove(struct hvc_struct *hp);
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index 936d05bf37f..fd0242676a2 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -197,7 +197,7 @@ done:
return sent;
}
-static struct hv_ops hvc_get_put_ops = {
+static const struct hv_ops hvc_get_put_ops = {
.get_chars = get_chars,
.put_chars = put_chars,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index fe62bd0e17b..21681a81cc3 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -922,7 +922,7 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev)
/* HVC operations */
-static struct hv_ops hvc_iucv_ops = {
+static const struct hv_ops hvc_iucv_ops = {
.get_chars = hvc_iucv_get_chars,
.put_chars = hvc_iucv_put_chars,
.notifier_add = hvc_iucv_notifier_add,
diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c
index 88590d04004..61c4a61558d 100644
--- a/drivers/char/hvc_rtas.c
+++ b/drivers/char/hvc_rtas.c
@@ -71,7 +71,7 @@ static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count)
return i;
}
-static struct hv_ops hvc_rtas_get_put_ops = {
+static const struct hv_ops hvc_rtas_get_put_ops = {
.get_chars = hvc_rtas_read_console,
.put_chars = hvc_rtas_write_console,
};
diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c
index bd63ba878a5..b0957e61a7b 100644
--- a/drivers/char/hvc_udbg.c
+++ b/drivers/char/hvc_udbg.c
@@ -58,7 +58,7 @@ static int hvc_udbg_get(uint32_t vtermno, char *buf, int count)
return i;
}
-static struct hv_ops hvc_udbg_ops = {
+static const struct hv_ops hvc_udbg_ops = {
.get_chars = hvc_udbg_get,
.put_chars = hvc_udbg_put,
};
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index 10be343d6ae..27370e99c66 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -77,7 +77,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
return got;
}
-static struct hv_ops hvc_get_put_ops = {
+static const struct hv_ops hvc_get_put_ops = {
.get_chars = filtered_get_chars,
.put_chars = hvc_put_chars,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index b1a71638c77..60446f82a3f 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -122,7 +122,7 @@ static int read_console(uint32_t vtermno, char *buf, int len)
return recv;
}
-static struct hv_ops hvc_ops = {
+static const struct hv_ops hvc_ops = {
.get_chars = read_console,
.put_chars = write_console,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index c0df30faa66..d31483c5488 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -186,3 +186,15 @@ config HW_RANDOM_MXC_RNGA
module will be called mxc-rnga.
If unsure, say Y.
+
+config HW_RANDOM_NOMADIK
+ tristate "ST-Ericsson Nomadik Random Number Generator support"
+ depends on HW_RANDOM && PLAT_NOMADIK
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on ST-Ericsson SoCs (8815 and 8500).
+
+ To compile this driver as a module, choose M here: the
+ module will be called nomadik-rng.
+
+ If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 5eeb1303f0d..4273308aa1e 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o
obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o
obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o
+obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
new file mode 100644
index 00000000000..a8b4c401014
--- /dev/null
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -0,0 +1,103 @@
+/*
+ * Nomadik RNG support
+ * Copyright 2009 Alessandro Rubini
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/amba/bus.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+
+static int nmk_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ void __iomem *base = (void __iomem *)rng->priv;
+
+ /*
+ * The register is 32 bits and gives 16 random bits (low half).
+ * A subsequent read will delay the core for 400ns, so we just read
+ * once and accept the very unlikely very small delay, even if wait==0.
+ */
+ *(u16 *)data = __raw_readl(base + 8) & 0xffff;
+ return 2;
+}
+
+/* we have at most one RNG per machine, granted */
+static struct hwrng nmk_rng = {
+ .name = "nomadik",
+ .read = nmk_rng_read,
+};
+
+static int nmk_rng_probe(struct amba_device *dev, struct amba_id *id)
+{
+ void __iomem *base;
+ int ret;
+
+ ret = amba_request_regions(dev, dev->dev.init_name);
+ if (ret)
+ return ret;
+ ret = -ENOMEM;
+ base = ioremap(dev->res.start, resource_size(&dev->res));
+ if (!base)
+ goto out_release;
+ nmk_rng.priv = (unsigned long)base;
+ ret = hwrng_register(&nmk_rng);
+ if (ret)
+ goto out_unmap;
+ return 0;
+
+out_unmap:
+ iounmap(base);
+out_release:
+ amba_release_regions(dev);
+ return ret;
+}
+
+static int nmk_rng_remove(struct amba_device *dev)
+{
+ void __iomem *base = (void __iomem *)nmk_rng.priv;
+ hwrng_unregister(&nmk_rng);
+ iounmap(base);
+ amba_release_regions(dev);
+ return 0;
+}
+
+static struct amba_id nmk_rng_ids[] = {
+ {
+ .id = 0x000805e1,
+ .mask = 0x000fffff, /* top bits are rev and cfg: accept all */
+ },
+ {0, 0},
+};
+
+static struct amba_driver nmk_rng_driver = {
+ .drv = {
+ .owner = THIS_MODULE,
+ .name = "rng",
+ },
+ .probe = nmk_rng_probe,
+ .remove = nmk_rng_remove,
+ .id_table = nmk_rng_ids,
+};
+
+static int __init nmk_rng_init(void)
+{
+ return amba_driver_register(&nmk_rng_driver);
+}
+
+static void __devexit nmk_rng_exit(void)
+{
+ amba_driver_unregister(&nmk_rng_driver);
+}
+
+module_init(nmk_rng_init);
+module_exit(nmk_rng_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index fdbcc9fd6d3..5eb83c3ca20 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -336,14 +336,12 @@ static int nvram_ioctl(struct inode *inode, struct file *file,
static int nvram_open(struct inode *inode, struct file *file)
{
- lock_kernel();
spin_lock(&nvram_state_lock);
if ((nvram_open_cnt && (file->f_flags & O_EXCL)) ||
(nvram_open_mode & NVRAM_EXCL) ||
((file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE))) {
spin_unlock(&nvram_state_lock);
- unlock_kernel();
return -EBUSY;
}
@@ -354,7 +352,6 @@ static int nvram_open(struct inode *inode, struct file *file)
nvram_open_cnt++;
spin_unlock(&nvram_state_lock);
- unlock_kernel();
return 0;
}
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index 2db4c0a29b0..c9bc896d68a 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -1047,7 +1047,7 @@ release_io:
static ssize_t cmm_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct cm4000_dev *dev = (struct cm4000_dev *) filp->private_data;
+ struct cm4000_dev *dev = filp->private_data;
unsigned int iobase = dev->p_dev->io.BasePort1;
unsigned short s;
unsigned char tmp;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index a035ae39a35..213373b5f17 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1,18 +1,6 @@
-/*D:300
- * The Guest console driver
- *
- * Writing console drivers is one of the few remaining Dark Arts in Linux.
- * Fortunately for us, the path of virtual consoles has been well-trodden by
- * the PowerPC folks, who wrote "hvc_console.c" to generically support any
- * virtual console. We use that infrastructure which only requires us to write
- * the basic put_chars and get_chars functions and call the right register
- * functions.
- :*/
-
-/*M:002 The console can be flooded: while the Guest is processing input the
- * Host can send more. Buffering in the Host could alleviate this, but it is a
- * difficult problem in general. :*/
-/* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
+/*
+ * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation
+ * Copyright (C) 2009, 2010 Red Hat, 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
@@ -28,142 +16,694 @@
* 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/cdev.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
#include <linux/err.h>
+#include <linux/fs.h>
#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
#include <linux/virtio.h>
#include <linux/virtio_console.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
#include "hvc_console.h"
-/*D:340 These represent our input and output console queues, and the virtio
- * operations for them. */
-static struct virtqueue *in_vq, *out_vq;
-static struct virtio_device *vdev;
+/*
+ * This is a global struct for storing common data for all the devices
+ * this driver handles.
+ *
+ * Mainly, it has a linked list for all the consoles in one place so
+ * that callbacks from hvc for get_chars(), put_chars() work properly
+ * across multiple devices and multiple ports per device.
+ */
+struct ports_driver_data {
+ /* Used for registering chardevs */
+ struct class *class;
+
+ /* Used for exporting per-port information to debugfs */
+ struct dentry *debugfs_dir;
+
+ /* Number of devices this driver is handling */
+ unsigned int index;
+
+ /*
+ * This is used to keep track of the number of hvc consoles
+ * spawned by this driver. This number is given as the first
+ * argument to hvc_alloc(). To correctly map an initial
+ * console spawned via hvc_instantiate to the console being
+ * hooked up via hvc_alloc, we need to pass the same vtermno.
+ *
+ * We also just assume the first console being initialised was
+ * the first one that got used as the initial console.
+ */
+ unsigned int next_vtermno;
+
+ /* All the console devices handled by this driver */
+ struct list_head consoles;
+};
+static struct ports_driver_data pdrvdata;
+
+DEFINE_SPINLOCK(pdrvdata_lock);
+
+/* This struct holds information that's relevant only for console ports */
+struct console {
+ /* We'll place all consoles in a list in the pdrvdata struct */
+ struct list_head list;
+
+ /* The hvc device associated with this console port */
+ struct hvc_struct *hvc;
+
+ /*
+ * This number identifies the number that we used to register
+ * with hvc in hvc_instantiate() and hvc_alloc(); this is the
+ * number passed on by the hvc callbacks to us to
+ * differentiate between the other console ports handled by
+ * this driver
+ */
+ u32 vtermno;
+};
+
+struct port_buffer {
+ char *buf;
+
+ /* size of the buffer in *buf above */
+ size_t size;
+
+ /* used length of the buffer */
+ size_t len;
+ /* offset in the buf from which to consume data */
+ size_t offset;
+};
+
+/*
+ * This is a per-device struct that stores data common to all the
+ * ports for that device (vdev->priv).
+ */
+struct ports_device {
+ /*
+ * Workqueue handlers where we process deferred work after
+ * notification
+ */
+ struct work_struct control_work;
+ struct work_struct config_work;
+
+ struct list_head ports;
+
+ /* To protect the list of ports */
+ spinlock_t ports_lock;
+
+ /* To protect the vq operations for the control channel */
+ spinlock_t cvq_lock;
+
+ /* The current config space is stored here */
+ struct virtio_console_config config;
+
+ /* The virtio device we're associated with */
+ struct virtio_device *vdev;
+
+ /*
+ * A couple of virtqueues for the control channel: one for
+ * guest->host transfers, one for host->guest transfers
+ */
+ struct virtqueue *c_ivq, *c_ovq;
+
+ /* Array of per-port IO virtqueues */
+ struct virtqueue **in_vqs, **out_vqs;
+
+ /* Used for numbering devices for sysfs and debugfs */
+ unsigned int drv_index;
+
+ /* Major number for this device. Ports will be created as minors. */
+ int chr_major;
+};
+
+/* This struct holds the per-port data */
+struct port {
+ /* Next port in the list, head is in the ports_device */
+ struct list_head list;
+
+ /* Pointer to the parent virtio_console device */
+ struct ports_device *portdev;
+
+ /* The current buffer from which data has to be fed to readers */
+ struct port_buffer *inbuf;
+
+ /*
+ * To protect the operations on the in_vq associated with this
+ * port. Has to be a spinlock because it can be called from
+ * interrupt context (get_char()).
+ */
+ spinlock_t inbuf_lock;
+
+ /* The IO vqs for this port */
+ struct virtqueue *in_vq, *out_vq;
+
+ /* File in the debugfs directory that exposes this port's information */
+ struct dentry *debugfs_file;
+
+ /*
+ * The entries in this struct will be valid if this port is
+ * hooked up to an hvc console
+ */
+ struct console cons;
+
+ /* Each port associates with a separate char device */
+ struct cdev cdev;
+ struct device *dev;
+
+ /* A waitqueue for poll() or blocking read operations */
+ wait_queue_head_t waitqueue;
+
+ /* The 'name' of the port that we expose via sysfs properties */
+ char *name;
+
+ /* The 'id' to identify the port with the Host */
+ u32 id;
+
+ /* Is the host device open */
+ bool host_connected;
+
+ /* We should allow only one process to open a port */
+ bool guest_connected;
+};
+
+/* This is the very early arch-specified put chars function. */
+static int (*early_put_chars)(u32, const char *, int);
+
+static struct port *find_port_by_vtermno(u32 vtermno)
+{
+ struct port *port;
+ struct console *cons;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdrvdata_lock, flags);
+ list_for_each_entry(cons, &pdrvdata.consoles, list) {
+ if (cons->vtermno == vtermno) {
+ port = container_of(cons, struct port, cons);
+ goto out;
+ }
+ }
+ port = NULL;
+out:
+ spin_unlock_irqrestore(&pdrvdata_lock, flags);
+ return port;
+}
+
+static struct port *find_port_by_id(struct ports_device *portdev, u32 id)
+{
+ struct port *port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&portdev->ports_lock, flags);
+ list_for_each_entry(port, &portdev->ports, list)
+ if (port->id == id)
+ goto out;
+ port = NULL;
+out:
+ spin_unlock_irqrestore(&portdev->ports_lock, flags);
+
+ return port;
+}
+
+static struct port *find_port_by_vq(struct ports_device *portdev,
+ struct virtqueue *vq)
+{
+ struct port *port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&portdev->ports_lock, flags);
+ list_for_each_entry(port, &portdev->ports, list)
+ if (port->in_vq == vq || port->out_vq == vq)
+ goto out;
+ port = NULL;
+out:
+ spin_unlock_irqrestore(&portdev->ports_lock, flags);
+ return port;
+}
+
+static bool is_console_port(struct port *port)
+{
+ if (port->cons.hvc)
+ return true;
+ return false;
+}
+
+static inline bool use_multiport(struct ports_device *portdev)
+{
+ /*
+ * This condition can be true when put_chars is called from
+ * early_init
+ */
+ if (!portdev->vdev)
+ return 0;
+ return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+}
-/* This is our input buffer, and how much data is left in it. */
-static unsigned int in_len;
-static char *in, *inbuf;
+static void free_buf(struct port_buffer *buf)
+{
+ kfree(buf->buf);
+ kfree(buf);
+}
+
+static struct port_buffer *alloc_buf(size_t buf_size)
+{
+ struct port_buffer *buf;
-/* The operations for our console. */
-static struct hv_ops virtio_cons;
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ goto fail;
+ buf->buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf->buf)
+ goto free_buf;
+ buf->len = 0;
+ buf->offset = 0;
+ buf->size = buf_size;
+ return buf;
+
+free_buf:
+ kfree(buf);
+fail:
+ return NULL;
+}
+
+/* Callers should take appropriate locks */
+static void *get_inbuf(struct port *port)
+{
+ struct port_buffer *buf;
+ struct virtqueue *vq;
+ unsigned int len;
-/* The hvc device */
-static struct hvc_struct *hvc;
+ vq = port->in_vq;
+ buf = vq->vq_ops->get_buf(vq, &len);
+ if (buf) {
+ buf->len = len;
+ buf->offset = 0;
+ }
+ return buf;
+}
-/*D:310 The put_chars() callback is pretty straightforward.
+/*
+ * Create a scatter-gather list representing our input buffer and put
+ * it in the queue.
*
- * We turn the characters into a scatter-gather list, add it to the output
- * queue and then kick the Host. Then we sit here waiting for it to finish:
- * inefficient in theory, but in practice implementations will do it
- * immediately (lguest's Launcher does). */
-static int put_chars(u32 vtermno, const char *buf, int count)
+ * Callers should take appropriate locks.
+ */
+static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
{
struct scatterlist sg[1];
+ int ret;
+
+ sg_init_one(sg, buf->buf, buf->size);
+
+ ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf);
+ vq->vq_ops->kick(vq);
+ return ret;
+}
+
+/* Discard any unread data this port has. Callers lockers. */
+static void discard_port_data(struct port *port)
+{
+ struct port_buffer *buf;
+ struct virtqueue *vq;
unsigned int len;
+ int ret;
- /* This is a convenient routine to initialize a single-elem sg list */
- sg_init_one(sg, buf, count);
+ vq = port->in_vq;
+ if (port->inbuf)
+ buf = port->inbuf;
+ else
+ buf = vq->vq_ops->get_buf(vq, &len);
- /* add_buf wants a token to identify this buffer: we hand it any
- * non-NULL pointer, since there's only ever one buffer. */
- if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) {
- /* Tell Host to go! */
- out_vq->vq_ops->kick(out_vq);
- /* Chill out until it's done with the buffer. */
- while (!out_vq->vq_ops->get_buf(out_vq, &len))
- cpu_relax();
+ ret = 0;
+ while (buf) {
+ if (add_inbuf(vq, buf) < 0) {
+ ret++;
+ free_buf(buf);
+ }
+ buf = vq->vq_ops->get_buf(vq, &len);
}
+ port->inbuf = NULL;
+ if (ret)
+ dev_warn(port->dev, "Errors adding %d buffers back to vq\n",
+ ret);
+}
- /* We're expected to return the amount of data we wrote: all of it. */
- return count;
+static bool port_has_data(struct port *port)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ if (port->inbuf) {
+ ret = true;
+ goto out;
+ }
+ port->inbuf = get_inbuf(port);
+ if (port->inbuf) {
+ ret = true;
+ goto out;
+ }
+ ret = false;
+out:
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+ return ret;
}
-/* Create a scatter-gather list representing our input buffer and put it in the
- * queue. */
-static void add_inbuf(void)
+static ssize_t send_control_msg(struct port *port, unsigned int event,
+ unsigned int value)
{
struct scatterlist sg[1];
- sg_init_one(sg, inbuf, PAGE_SIZE);
+ struct virtio_console_control cpkt;
+ struct virtqueue *vq;
+ int len;
+
+ if (!use_multiport(port->portdev))
+ return 0;
+
+ cpkt.id = port->id;
+ cpkt.event = event;
+ cpkt.value = value;
+
+ vq = port->portdev->c_ovq;
- /* We should always be able to add one buffer to an empty queue. */
- if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0)
- BUG();
- in_vq->vq_ops->kick(in_vq);
+ sg_init_one(sg, &cpkt, sizeof(cpkt));
+ if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) {
+ vq->vq_ops->kick(vq);
+ while (!vq->vq_ops->get_buf(vq, &len))
+ cpu_relax();
+ }
+ return 0;
}
-/*D:350 get_chars() is the callback from the hvc_console infrastructure when
- * an interrupt is received.
- *
- * Most of the code deals with the fact that the hvc_console() infrastructure
- * only asks us for 16 bytes at a time. We keep in_offset and in_used fields
- * for partially-filled buffers. */
-static int get_chars(u32 vtermno, char *buf, int count)
+static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count)
{
- /* If we don't have an input queue yet, we can't get input. */
- BUG_ON(!in_vq);
+ struct scatterlist sg[1];
+ struct virtqueue *out_vq;
+ ssize_t ret;
+ unsigned int len;
+
+ out_vq = port->out_vq;
+
+ sg_init_one(sg, in_buf, in_count);
+ ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, in_buf);
+
+ /* Tell Host to go! */
+ out_vq->vq_ops->kick(out_vq);
+
+ if (ret < 0) {
+ len = 0;
+ goto fail;
+ }
+
+ /*
+ * Wait till the host acknowledges it pushed out the data we
+ * sent. Also ensure we return to userspace the number of
+ * bytes that were successfully consumed by the host.
+ */
+ while (!out_vq->vq_ops->get_buf(out_vq, &len))
+ cpu_relax();
+fail:
+ /* We're expected to return the amount of data we wrote */
+ return len;
+}
+
+/*
+ * Give out the data that's requested from the buffer that we have
+ * queued up.
+ */
+static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count,
+ bool to_user)
+{
+ struct port_buffer *buf;
+ unsigned long flags;
+
+ if (!out_count || !port_has_data(port))
+ return 0;
+
+ buf = port->inbuf;
+ out_count = min(out_count, buf->len - buf->offset);
+
+ if (to_user) {
+ ssize_t ret;
+
+ ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count);
+ if (ret)
+ return -EFAULT;
+ } else {
+ memcpy(out_buf, buf->buf + buf->offset, out_count);
+ }
+
+ buf->offset += out_count;
+
+ if (buf->offset == buf->len) {
+ /*
+ * We're done using all the data in this buffer.
+ * Re-queue so that the Host can send us more data.
+ */
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ port->inbuf = NULL;
+
+ if (add_inbuf(port->in_vq, buf) < 0)
+ dev_warn(port->dev, "failed add_buf\n");
+
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+ }
+ /* Return the number of bytes actually copied */
+ return out_count;
+}
- /* No buffer? Try to get one. */
- if (!in_len) {
- in = in_vq->vq_ops->get_buf(in_vq, &in_len);
- if (!in)
+/* The condition that must be true for polling to end */
+static bool wait_is_over(struct port *port)
+{
+ return port_has_data(port) || !port->host_connected;
+}
+
+static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ struct port *port;
+ ssize_t ret;
+
+ port = filp->private_data;
+
+ if (!port_has_data(port)) {
+ /*
+ * If nothing's connected on the host just return 0 in
+ * case of list_empty; this tells the userspace app
+ * that there's no connection
+ */
+ if (!port->host_connected)
return 0;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(port->waitqueue,
+ wait_is_over(port));
+ if (ret < 0)
+ return ret;
+ }
+ /*
+ * We could've received a disconnection message while we were
+ * waiting for more data.
+ *
+ * This check is not clubbed in the if() statement above as we
+ * might receive some data as well as the host could get
+ * disconnected after we got woken up from our wait. So we
+ * really want to give off whatever data we have and only then
+ * check for host_connected.
+ */
+ if (!port_has_data(port) && !port->host_connected)
+ return 0;
+
+ return fill_readbuf(port, ubuf, count, true);
+}
+
+static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ struct port *port;
+ char *buf;
+ ssize_t ret;
+
+ port = filp->private_data;
+
+ count = min((size_t)(32 * 1024), count);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = copy_from_user(buf, ubuf, count);
+ if (ret) {
+ ret = -EFAULT;
+ goto free_buf;
}
- /* You want more than we have to give? Well, try wanting less! */
- if (in_len < count)
- count = in_len;
+ ret = send_buf(port, buf, count);
+free_buf:
+ kfree(buf);
+ return ret;
+}
+
+static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
+{
+ struct port *port;
+ unsigned int ret;
+
+ port = filp->private_data;
+ poll_wait(filp, &port->waitqueue, wait);
+
+ ret = 0;
+ if (port->inbuf)
+ ret |= POLLIN | POLLRDNORM;
+ if (port->host_connected)
+ ret |= POLLOUT;
+ if (!port->host_connected)
+ ret |= POLLHUP;
+
+ return ret;
+}
+
+static int port_fops_release(struct inode *inode, struct file *filp)
+{
+ struct port *port;
+
+ port = filp->private_data;
+
+ /* Notify host of port being closed */
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
+
+ spin_lock_irq(&port->inbuf_lock);
+ port->guest_connected = false;
+
+ discard_port_data(port);
+
+ spin_unlock_irq(&port->inbuf_lock);
+
+ return 0;
+}
+
+static int port_fops_open(struct inode *inode, struct file *filp)
+{
+ struct cdev *cdev = inode->i_cdev;
+ struct port *port;
+
+ port = container_of(cdev, struct port, cdev);
+ filp->private_data = port;
+
+ /*
+ * Don't allow opening of console port devices -- that's done
+ * via /dev/hvc
+ */
+ if (is_console_port(port))
+ return -ENXIO;
+
+ /* Allow only one process to open a particular port at a time */
+ spin_lock_irq(&port->inbuf_lock);
+ if (port->guest_connected) {
+ spin_unlock_irq(&port->inbuf_lock);
+ return -EMFILE;
+ }
- /* Copy across to their buffer and increment offset. */
- memcpy(buf, in, count);
- in += count;
- in_len -= count;
+ port->guest_connected = true;
+ spin_unlock_irq(&port->inbuf_lock);
- /* Finished? Re-register buffer so Host will use it again. */
- if (in_len == 0)
- add_inbuf();
+ /* Notify host of port being opened */
+ send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1);
- return count;
+ return 0;
}
-/*:*/
-/*D:320 Console drivers are initialized very early so boot messages can go out,
- * so we do things slightly differently from the generic virtio initialization
- * of the net and block drivers.
+/*
+ * The file operations that we support: programs in the guest can open
+ * a console device, read from it, write to it, poll for data and
+ * close it. The devices are at
+ * /dev/vport<device number>p<port number>
+ */
+static const struct file_operations port_fops = {
+ .owner = THIS_MODULE,
+ .open = port_fops_open,
+ .read = port_fops_read,
+ .write = port_fops_write,
+ .poll = port_fops_poll,
+ .release = port_fops_release,
+};
+
+/*
+ * The put_chars() callback is pretty straightforward.
*
- * At this stage, the console is output-only. It's too early to set up a
- * virtqueue, so we let the drivers do some boutique early-output thing. */
-int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
+ * We turn the characters into a scatter-gather list, add it to the
+ * output queue and then kick the Host. Then we sit here waiting for
+ * it to finish: inefficient in theory, but in practice
+ * implementations will do it immediately (lguest's Launcher does).
+ */
+static int put_chars(u32 vtermno, const char *buf, int count)
{
- virtio_cons.put_chars = put_chars;
- return hvc_instantiate(0, 0, &virtio_cons);
+ struct port *port;
+
+ port = find_port_by_vtermno(vtermno);
+ if (!port)
+ return 0;
+
+ if (unlikely(early_put_chars))
+ return early_put_chars(vtermno, buf, count);
+
+ return send_buf(port, (void *)buf, count);
}
/*
- * virtio console configuration. This supports:
- * - console resize
+ * get_chars() is the callback from the hvc_console infrastructure
+ * when an interrupt is received.
+ *
+ * We call out to fill_readbuf that gets us the required data from the
+ * buffers that are queued up.
*/
-static void virtcons_apply_config(struct virtio_device *dev)
+static int get_chars(u32 vtermno, char *buf, int count)
{
+ struct port *port;
+
+ port = find_port_by_vtermno(vtermno);
+ if (!port)
+ return 0;
+
+ /* If we don't have an input queue yet, we can't get input. */
+ BUG_ON(!port->in_vq);
+
+ return fill_readbuf(port, buf, count, false);
+}
+
+static void resize_console(struct port *port)
+{
+ struct virtio_device *vdev;
struct winsize ws;
- if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
- dev->config->get(dev,
- offsetof(struct virtio_console_config, cols),
- &ws.ws_col, sizeof(u16));
- dev->config->get(dev,
- offsetof(struct virtio_console_config, rows),
- &ws.ws_row, sizeof(u16));
- hvc_resize(hvc, ws);
+ vdev = port->portdev->vdev;
+ if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) {
+ vdev->config->get(vdev,
+ offsetof(struct virtio_console_config, cols),
+ &ws.ws_col, sizeof(u16));
+ vdev->config->get(vdev,
+ offsetof(struct virtio_console_config, rows),
+ &ws.ws_row, sizeof(u16));
+ hvc_resize(port->cons.hvc, ws);
}
}
-/*
- * we support only one console, the hvc struct is a global var
- * We set the configuration at this point, since we now have a tty
- */
+/* We set the configuration at this point, since we now have a tty */
static int notifier_add_vio(struct hvc_struct *hp, int data)
{
+ struct port *port;
+
+ port = find_port_by_vtermno(hp->vtermno);
+ if (!port)
+ return -EINVAL;
+
hp->irq_requested = 1;
- virtcons_apply_config(vdev);
+ resize_console(port);
return 0;
}
@@ -173,79 +713,797 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
hp->irq_requested = 0;
}
-static void hvc_handle_input(struct virtqueue *vq)
+/* The operations for console ports. */
+static const struct hv_ops hv_ops = {
+ .get_chars = get_chars,
+ .put_chars = put_chars,
+ .notifier_add = notifier_add_vio,
+ .notifier_del = notifier_del_vio,
+ .notifier_hangup = notifier_del_vio,
+};
+
+/*
+ * Console drivers are initialized very early so boot messages can go
+ * out, so we do things slightly differently from the generic virtio
+ * initialization of the net and block drivers.
+ *
+ * At this stage, the console is output-only. It's too early to set
+ * up a virtqueue, so we let the drivers do some boutique early-output
+ * thing.
+ */
+int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
{
- if (hvc_poll(hvc))
+ early_put_chars = put_chars;
+ return hvc_instantiate(0, 0, &hv_ops);
+}
+
+int init_port_console(struct port *port)
+{
+ int ret;
+
+ /*
+ * The Host's telling us this port is a console port. Hook it
+ * up with an hvc console.
+ *
+ * To set up and manage our virtual console, we call
+ * hvc_alloc().
+ *
+ * The first argument of hvc_alloc() is the virtual console
+ * number. The second argument is the parameter for the
+ * notification mechanism (like irq number). We currently
+ * leave this as zero, virtqueues have implicit notifications.
+ *
+ * The third argument is a "struct hv_ops" containing the
+ * put_chars() get_chars(), notifier_add() and notifier_del()
+ * pointers. The final argument is the output buffer size: we
+ * can do any size, so we put PAGE_SIZE here.
+ */
+ port->cons.vtermno = pdrvdata.next_vtermno;
+
+ port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
+ if (IS_ERR(port->cons.hvc)) {
+ ret = PTR_ERR(port->cons.hvc);
+ dev_err(port->dev,
+ "error %d allocating hvc for port\n", ret);
+ port->cons.hvc = NULL;
+ return ret;
+ }
+ spin_lock_irq(&pdrvdata_lock);
+ pdrvdata.next_vtermno++;
+ list_add_tail(&port->cons.list, &pdrvdata.consoles);
+ spin_unlock_irq(&pdrvdata_lock);
+ port->guest_connected = true;
+
+ /* Notify host of port being opened */
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
+
+ return 0;
+}
+
+static ssize_t show_port_name(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ struct port *port;
+
+ port = dev_get_drvdata(dev);
+
+ return sprintf(buffer, "%s\n", port->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL);
+
+static struct attribute *port_sysfs_entries[] = {
+ &dev_attr_name.attr,
+ NULL
+};
+
+static struct attribute_group port_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = port_sysfs_entries,
+};
+
+static int debugfs_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t debugfs_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *offp)
+{
+ struct port *port;
+ char *buf;
+ ssize_t ret, out_offset, out_count;
+
+ out_count = 1024;
+ buf = kmalloc(out_count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ port = filp->private_data;
+ out_offset = 0;
+ out_offset += snprintf(buf + out_offset, out_count,
+ "name: %s\n", port->name ? port->name : "");
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "guest_connected: %d\n", port->guest_connected);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "host_connected: %d\n", port->host_connected);
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "is_console: %s\n",
+ is_console_port(port) ? "yes" : "no");
+ out_offset += snprintf(buf + out_offset, out_count - out_offset,
+ "console_vtermno: %u\n", port->cons.vtermno);
+
+ ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations port_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_open,
+ .read = debugfs_read,
+};
+
+/* Remove all port-specific data. */
+static int remove_port(struct port *port)
+{
+ struct port_buffer *buf;
+
+ spin_lock_irq(&port->portdev->ports_lock);
+ list_del(&port->list);
+ spin_unlock_irq(&port->portdev->ports_lock);
+
+ if (is_console_port(port)) {
+ spin_lock_irq(&pdrvdata_lock);
+ list_del(&port->cons.list);
+ spin_unlock_irq(&pdrvdata_lock);
+ hvc_remove(port->cons.hvc);
+ }
+ if (port->guest_connected)
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
+
+ sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+ device_destroy(pdrvdata.class, port->dev->devt);
+ cdev_del(&port->cdev);
+
+ /* Remove unused data this port might have received. */
+ discard_port_data(port);
+
+ /* Remove buffers we queued up for the Host to send us data in. */
+ while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq)))
+ free_buf(buf);
+
+ kfree(port->name);
+
+ debugfs_remove(port->debugfs_file);
+
+ kfree(port);
+ return 0;
+}
+
+/* Any private messages that the Host and Guest want to share */
+static void handle_control_message(struct ports_device *portdev,
+ struct port_buffer *buf)
+{
+ struct virtio_console_control *cpkt;
+ struct port *port;
+ size_t name_size;
+ int err;
+
+ cpkt = (struct virtio_console_control *)(buf->buf + buf->offset);
+
+ port = find_port_by_id(portdev, cpkt->id);
+ if (!port) {
+ /* No valid header at start of buffer. Drop it. */
+ dev_dbg(&portdev->vdev->dev,
+ "Invalid index %u in control packet\n", cpkt->id);
+ return;
+ }
+
+ switch (cpkt->event) {
+ case VIRTIO_CONSOLE_CONSOLE_PORT:
+ if (!cpkt->value)
+ break;
+ if (is_console_port(port))
+ break;
+
+ init_port_console(port);
+ /*
+ * Could remove the port here in case init fails - but
+ * have to notify the host first.
+ */
+ break;
+ case VIRTIO_CONSOLE_RESIZE:
+ if (!is_console_port(port))
+ break;
+ port->cons.hvc->irq_requested = 1;
+ resize_console(port);
+ break;
+ case VIRTIO_CONSOLE_PORT_OPEN:
+ port->host_connected = cpkt->value;
+ wake_up_interruptible(&port->waitqueue);
+ break;
+ case VIRTIO_CONSOLE_PORT_NAME:
+ /*
+ * Skip the size of the header and the cpkt to get the size
+ * of the name that was sent
+ */
+ name_size = buf->len - buf->offset - sizeof(*cpkt) + 1;
+
+ port->name = kmalloc(name_size, GFP_KERNEL);
+ if (!port->name) {
+ dev_err(port->dev,
+ "Not enough space to store port name\n");
+ break;
+ }
+ strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt),
+ name_size - 1);
+ port->name[name_size - 1] = 0;
+
+ /*
+ * Since we only have one sysfs attribute, 'name',
+ * create it only if we have a name for the port.
+ */
+ err = sysfs_create_group(&port->dev->kobj,
+ &port_attribute_group);
+ if (err)
+ dev_err(port->dev,
+ "Error %d creating sysfs device attributes\n",
+ err);
+
+ break;
+ case VIRTIO_CONSOLE_PORT_REMOVE:
+ /*
+ * Hot unplug the port. We don't decrement nr_ports
+ * since we don't want to deal with extra complexities
+ * of using the lowest-available port id: We can just
+ * pick up the nr_ports number as the id and not have
+ * userspace send it to us. This helps us in two
+ * ways:
+ *
+ * - We don't need to have a 'port_id' field in the
+ * config space when a port is hot-added. This is a
+ * good thing as we might queue up multiple hotplug
+ * requests issued in our workqueue.
+ *
+ * - Another way to deal with this would have been to
+ * use a bitmap of the active ports and select the
+ * lowest non-active port from that map. That
+ * bloats the already tight config space and we
+ * would end up artificially limiting the
+ * max. number of ports to sizeof(bitmap). Right
+ * now we can support 2^32 ports (as the port id is
+ * stored in a u32 type).
+ *
+ */
+ remove_port(port);
+ break;
+ }
+}
+
+static void control_work_handler(struct work_struct *work)
+{
+ struct ports_device *portdev;
+ struct virtqueue *vq;
+ struct port_buffer *buf;
+ unsigned int len;
+
+ portdev = container_of(work, struct ports_device, control_work);
+ vq = portdev->c_ivq;
+
+ spin_lock(&portdev->cvq_lock);
+ while ((buf = vq->vq_ops->get_buf(vq, &len))) {
+ spin_unlock(&portdev->cvq_lock);
+
+ buf->len = len;
+ buf->offset = 0;
+
+ handle_control_message(portdev, buf);
+
+ spin_lock(&portdev->cvq_lock);
+ if (add_inbuf(portdev->c_ivq, buf) < 0) {
+ dev_warn(&portdev->vdev->dev,
+ "Error adding buffer to queue\n");
+ free_buf(buf);
+ }
+ }
+ spin_unlock(&portdev->cvq_lock);
+}
+
+static void in_intr(struct virtqueue *vq)
+{
+ struct port *port;
+ unsigned long flags;
+
+ port = find_port_by_vq(vq->vdev->priv, vq);
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ if (!port->inbuf)
+ port->inbuf = get_inbuf(port);
+
+ /*
+ * Don't queue up data when port is closed. This condition
+ * can be reached when a console port is not yet connected (no
+ * tty is spawned) and the host sends out data to console
+ * ports. For generic serial ports, the host won't
+ * (shouldn't) send data till the guest is connected.
+ */
+ if (!port->guest_connected)
+ discard_port_data(port);
+
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+
+ wake_up_interruptible(&port->waitqueue);
+
+ if (is_console_port(port) && hvc_poll(port->cons.hvc))
hvc_kick();
}
-/*D:370 Once we're further in boot, we get probed like any other virtio device.
- * At this stage we set up the output virtqueue.
- *
- * To set up and manage our virtual console, we call hvc_alloc(). Since we
- * never remove the console device we never need this pointer again.
+static void control_intr(struct virtqueue *vq)
+{
+ struct ports_device *portdev;
+
+ portdev = vq->vdev->priv;
+ schedule_work(&portdev->control_work);
+}
+
+static void config_intr(struct virtio_device *vdev)
+{
+ struct ports_device *portdev;
+
+ portdev = vdev->priv;
+ if (use_multiport(portdev)) {
+ /* Handle port hot-add */
+ schedule_work(&portdev->config_work);
+ }
+ /*
+ * We'll use this way of resizing only for legacy support.
+ * For newer userspace (VIRTIO_CONSOLE_F_MULTPORT+), use
+ * control messages to indicate console size changes so that
+ * it can be done per-port
+ */
+ resize_console(find_port_by_id(portdev, 0));
+}
+
+static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
+{
+ struct port_buffer *buf;
+ unsigned int ret;
+ int err;
+
+ ret = 0;
+ do {
+ buf = alloc_buf(PAGE_SIZE);
+ if (!buf)
+ break;
+
+ spin_lock_irq(lock);
+ err = add_inbuf(vq, buf);
+ if (err < 0) {
+ spin_unlock_irq(lock);
+ free_buf(buf);
+ break;
+ }
+ ret++;
+ spin_unlock_irq(lock);
+ } while (err > 0);
+
+ return ret;
+}
+
+static int add_port(struct ports_device *portdev, u32 id)
+{
+ char debugfs_name[16];
+ struct port *port;
+ struct port_buffer *buf;
+ dev_t devt;
+ int err;
+
+ port = kmalloc(sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ port->portdev = portdev;
+ port->id = id;
+
+ port->name = NULL;
+ port->inbuf = NULL;
+ port->cons.hvc = NULL;
+
+ port->host_connected = port->guest_connected = false;
+
+ port->in_vq = portdev->in_vqs[port->id];
+ port->out_vq = portdev->out_vqs[port->id];
+
+ cdev_init(&port->cdev, &port_fops);
+
+ devt = MKDEV(portdev->chr_major, id);
+ err = cdev_add(&port->cdev, devt, 1);
+ if (err < 0) {
+ dev_err(&port->portdev->vdev->dev,
+ "Error %d adding cdev for port %u\n", err, id);
+ goto free_port;
+ }
+ port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
+ devt, port, "vport%up%u",
+ port->portdev->drv_index, id);
+ if (IS_ERR(port->dev)) {
+ err = PTR_ERR(port->dev);
+ dev_err(&port->portdev->vdev->dev,
+ "Error %d creating device for port %u\n",
+ err, id);
+ goto free_cdev;
+ }
+
+ spin_lock_init(&port->inbuf_lock);
+ init_waitqueue_head(&port->waitqueue);
+
+ /* Fill the in_vq with buffers so the host can send us data. */
+ err = fill_queue(port->in_vq, &port->inbuf_lock);
+ if (!err) {
+ dev_err(port->dev, "Error allocating inbufs\n");
+ err = -ENOMEM;
+ goto free_device;
+ }
+
+ /*
+ * If we're not using multiport support, this has to be a console port
+ */
+ if (!use_multiport(port->portdev)) {
+ err = init_port_console(port);
+ if (err)
+ goto free_inbufs;
+ }
+
+ spin_lock_irq(&portdev->ports_lock);
+ list_add_tail(&port->list, &port->portdev->ports);
+ spin_unlock_irq(&portdev->ports_lock);
+
+ /*
+ * Tell the Host we're set so that it can send us various
+ * configuration parameters for this port (eg, port name,
+ * caching, whether this is a console port, etc.)
+ */
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
+
+ if (pdrvdata.debugfs_dir) {
+ /*
+ * Finally, create the debugfs file that we can use to
+ * inspect a port's state at any time
+ */
+ sprintf(debugfs_name, "vport%up%u",
+ port->portdev->drv_index, id);
+ port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
+ pdrvdata.debugfs_dir,
+ port,
+ &port_debugfs_ops);
+ }
+ return 0;
+
+free_inbufs:
+ while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq)))
+ free_buf(buf);
+free_device:
+ device_destroy(pdrvdata.class, port->dev->devt);
+free_cdev:
+ cdev_del(&port->cdev);
+free_port:
+ kfree(port);
+fail:
+ return err;
+}
+
+/*
+ * The workhandler for config-space updates.
*
- * Finally we put our input buffer in the input queue, ready to receive. */
-static int __devinit virtcons_probe(struct virtio_device *dev)
+ * This is called when ports are hot-added.
+ */
+static void config_work_handler(struct work_struct *work)
+{
+ struct virtio_console_config virtconconf;
+ struct ports_device *portdev;
+ struct virtio_device *vdev;
+ int err;
+
+ portdev = container_of(work, struct ports_device, config_work);
+
+ vdev = portdev->vdev;
+ vdev->config->get(vdev,
+ offsetof(struct virtio_console_config, nr_ports),
+ &virtconconf.nr_ports,
+ sizeof(virtconconf.nr_ports));
+
+ if (portdev->config.nr_ports == virtconconf.nr_ports) {
+ /*
+ * Port 0 got hot-added. Since we already did all the
+ * other initialisation for it, just tell the Host
+ * that the port is ready if we find the port. In
+ * case the port was hot-removed earlier, we call
+ * add_port to add the port.
+ */
+ struct port *port;
+
+ port = find_port_by_id(portdev, 0);
+ if (!port)
+ add_port(portdev, 0);
+ else
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
+ return;
+ }
+ if (virtconconf.nr_ports > portdev->config.max_nr_ports) {
+ dev_warn(&vdev->dev,
+ "More ports specified (%u) than allowed (%u)",
+ portdev->config.nr_ports + 1,
+ portdev->config.max_nr_ports);
+ return;
+ }
+ if (virtconconf.nr_ports < portdev->config.nr_ports)
+ return;
+
+ /* Hot-add ports */
+ while (virtconconf.nr_ports - portdev->config.nr_ports) {
+ err = add_port(portdev, portdev->config.nr_ports);
+ if (err)
+ break;
+ portdev->config.nr_ports++;
+ }
+}
+
+static int init_vqs(struct ports_device *portdev)
{
- vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
- const char *names[] = { "input", "output" };
- struct virtqueue *vqs[2];
+ vq_callback_t **io_callbacks;
+ char **io_names;
+ struct virtqueue **vqs;
+ u32 i, j, nr_ports, nr_queues;
int err;
- vdev = dev;
+ nr_ports = portdev->config.max_nr_ports;
+ nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2;
- /* This is the scratch page we use to receive console input */
- inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!inbuf) {
+ vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL);
+ if (!vqs) {
err = -ENOMEM;
goto fail;
}
+ io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL);
+ if (!io_callbacks) {
+ err = -ENOMEM;
+ goto free_vqs;
+ }
+ io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL);
+ if (!io_names) {
+ err = -ENOMEM;
+ goto free_callbacks;
+ }
+ portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *),
+ GFP_KERNEL);
+ if (!portdev->in_vqs) {
+ err = -ENOMEM;
+ goto free_names;
+ }
+ portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *),
+ GFP_KERNEL);
+ if (!portdev->out_vqs) {
+ err = -ENOMEM;
+ goto free_invqs;
+ }
+
+ /*
+ * For backward compat (newer host but older guest), the host
+ * spawns a console port first and also inits the vqs for port
+ * 0 before others.
+ */
+ j = 0;
+ io_callbacks[j] = in_intr;
+ io_callbacks[j + 1] = NULL;
+ io_names[j] = "input";
+ io_names[j + 1] = "output";
+ j += 2;
+ if (use_multiport(portdev)) {
+ io_callbacks[j] = control_intr;
+ io_callbacks[j + 1] = NULL;
+ io_names[j] = "control-i";
+ io_names[j + 1] = "control-o";
+
+ for (i = 1; i < nr_ports; i++) {
+ j += 2;
+ io_callbacks[j] = in_intr;
+ io_callbacks[j + 1] = NULL;
+ io_names[j] = "input";
+ io_names[j + 1] = "output";
+ }
+ }
/* Find the queues. */
- /* FIXME: This is why we want to wean off hvc: we do nothing
- * when input comes in. */
- err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+ err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs,
+ io_callbacks,
+ (const char **)io_names);
if (err)
+ goto free_outvqs;
+
+ j = 0;
+ portdev->in_vqs[0] = vqs[0];
+ portdev->out_vqs[0] = vqs[1];
+ j += 2;
+ if (use_multiport(portdev)) {
+ portdev->c_ivq = vqs[j];
+ portdev->c_ovq = vqs[j + 1];
+
+ for (i = 1; i < nr_ports; i++) {
+ j += 2;
+ portdev->in_vqs[i] = vqs[j];
+ portdev->out_vqs[i] = vqs[j + 1];
+ }
+ }
+ kfree(io_callbacks);
+ kfree(io_names);
+ kfree(vqs);
+
+ return 0;
+
+free_names:
+ kfree(io_names);
+free_callbacks:
+ kfree(io_callbacks);
+free_outvqs:
+ kfree(portdev->out_vqs);
+free_invqs:
+ kfree(portdev->in_vqs);
+free_vqs:
+ kfree(vqs);
+fail:
+ return err;
+}
+
+static const struct file_operations portdev_fops = {
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Once we're further in boot, we get probed like any other virtio
+ * device.
+ *
+ * If the host also supports multiple console ports, we check the
+ * config space to see how many ports the host has spawned. We
+ * initialize each port found.
+ */
+static int __devinit virtcons_probe(struct virtio_device *vdev)
+{
+ struct ports_device *portdev;
+ u32 i;
+ int err;
+ bool multiport;
+
+ portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
+ if (!portdev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Attach this portdev to this virtio_device, and vice-versa. */
+ portdev->vdev = vdev;
+ vdev->priv = portdev;
+
+ spin_lock_irq(&pdrvdata_lock);
+ portdev->drv_index = pdrvdata.index++;
+ spin_unlock_irq(&pdrvdata_lock);
+
+ portdev->chr_major = register_chrdev(0, "virtio-portsdev",
+ &portdev_fops);
+ if (portdev->chr_major < 0) {
+ dev_err(&vdev->dev,
+ "Error %d registering chrdev for device %u\n",
+ portdev->chr_major, portdev->drv_index);
+ err = portdev->chr_major;
goto free;
+ }
- in_vq = vqs[0];
- out_vq = vqs[1];
+ multiport = false;
+ portdev->config.nr_ports = 1;
+ portdev->config.max_nr_ports = 1;
+ if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+ multiport = true;
+ vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT;
- /* Start using the new console output. */
- virtio_cons.get_chars = get_chars;
- virtio_cons.put_chars = put_chars;
- virtio_cons.notifier_add = notifier_add_vio;
- virtio_cons.notifier_del = notifier_del_vio;
- virtio_cons.notifier_hangup = notifier_del_vio;
-
- /* The first argument of hvc_alloc() is the virtual console number, so
- * we use zero. The second argument is the parameter for the
- * notification mechanism (like irq number). We currently leave this
- * as zero, virtqueues have implicit notifications.
- *
- * The third argument is a "struct hv_ops" containing the put_chars()
- * get_chars(), notifier_add() and notifier_del() pointers.
- * The final argument is the output buffer size: we can do any size,
- * so we put PAGE_SIZE here. */
- hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE);
- if (IS_ERR(hvc)) {
- err = PTR_ERR(hvc);
- goto free_vqs;
+ vdev->config->get(vdev, offsetof(struct virtio_console_config,
+ nr_ports),
+ &portdev->config.nr_ports,
+ sizeof(portdev->config.nr_ports));
+ vdev->config->get(vdev, offsetof(struct virtio_console_config,
+ max_nr_ports),
+ &portdev->config.max_nr_ports,
+ sizeof(portdev->config.max_nr_ports));
+ if (portdev->config.nr_ports > portdev->config.max_nr_ports) {
+ dev_warn(&vdev->dev,
+ "More ports (%u) specified than allowed (%u). Will init %u ports.",
+ portdev->config.nr_ports,
+ portdev->config.max_nr_ports,
+ portdev->config.max_nr_ports);
+
+ portdev->config.nr_ports = portdev->config.max_nr_ports;
+ }
+ }
+
+ /* Let the Host know we support multiple ports.*/
+ vdev->config->finalize_features(vdev);
+
+ err = init_vqs(portdev);
+ if (err < 0) {
+ dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
+ goto free_chrdev;
+ }
+
+ spin_lock_init(&portdev->ports_lock);
+ INIT_LIST_HEAD(&portdev->ports);
+
+ if (multiport) {
+ spin_lock_init(&portdev->cvq_lock);
+ INIT_WORK(&portdev->control_work, &control_work_handler);
+ INIT_WORK(&portdev->config_work, &config_work_handler);
+
+ err = fill_queue(portdev->c_ivq, &portdev->cvq_lock);
+ if (!err) {
+ dev_err(&vdev->dev,
+ "Error allocating buffers for control queue\n");
+ err = -ENOMEM;
+ goto free_vqs;
+ }
}
- /* Register the input buffer the first time. */
- add_inbuf();
+ for (i = 0; i < portdev->config.nr_ports; i++)
+ add_port(portdev, i);
+
+ /* Start using the new console output. */
+ early_put_chars = NULL;
return 0;
free_vqs:
vdev->config->del_vqs(vdev);
+ kfree(portdev->in_vqs);
+ kfree(portdev->out_vqs);
+free_chrdev:
+ unregister_chrdev(portdev->chr_major, "virtio-portsdev");
free:
- kfree(inbuf);
+ kfree(portdev);
fail:
return err;
}
+static void virtcons_remove(struct virtio_device *vdev)
+{
+ struct ports_device *portdev;
+ struct port *port, *port2;
+ struct port_buffer *buf;
+ unsigned int len;
+
+ portdev = vdev->priv;
+
+ cancel_work_sync(&portdev->control_work);
+ cancel_work_sync(&portdev->config_work);
+
+ list_for_each_entry_safe(port, port2, &portdev->ports, list)
+ remove_port(port);
+
+ unregister_chrdev(portdev->chr_major, "virtio-portsdev");
+
+ while ((buf = portdev->c_ivq->vq_ops->get_buf(portdev->c_ivq, &len)))
+ free_buf(buf);
+
+ while ((buf = portdev->c_ivq->vq_ops->detach_unused_buf(portdev->c_ivq)))
+ free_buf(buf);
+
+ vdev->config->del_vqs(vdev);
+ kfree(portdev->in_vqs);
+ kfree(portdev->out_vqs);
+
+ kfree(portdev);
+}
+
static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID },
{ 0 },
@@ -253,6 +1511,7 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_CONSOLE_F_SIZE,
+ VIRTIO_CONSOLE_F_MULTIPORT,
};
static struct virtio_driver virtio_console = {
@@ -262,14 +1521,41 @@ static struct virtio_driver virtio_console = {
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtcons_probe,
- .config_changed = virtcons_apply_config,
+ .remove = virtcons_remove,
+ .config_changed = config_intr,
};
static int __init init(void)
{
+ int err;
+
+ pdrvdata.class = class_create(THIS_MODULE, "virtio-ports");
+ if (IS_ERR(pdrvdata.class)) {
+ err = PTR_ERR(pdrvdata.class);
+ pr_err("Error %d creating virtio-ports class\n", err);
+ return err;
+ }
+
+ pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
+ if (!pdrvdata.debugfs_dir) {
+ pr_warning("Error %ld creating debugfs dir for virtio-ports\n",
+ PTR_ERR(pdrvdata.debugfs_dir));
+ }
+ INIT_LIST_HEAD(&pdrvdata.consoles);
+
return register_virtio_driver(&virtio_console);
}
+
+static void __exit fini(void)
+{
+ unregister_virtio_driver(&virtio_console);
+
+ class_destroy(pdrvdata.class);
+ if (pdrvdata.debugfs_dir)
+ debugfs_remove_recursive(pdrvdata.debugfs_dir);
+}
module_init(init);
+module_exit(fini);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Virtio console driver");
diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c
index 994e1a58b98..8b24729fec8 100644
--- a/drivers/char/vme_scc.c
+++ b/drivers/char/vme_scc.c
@@ -136,7 +136,7 @@ static const struct tty_port_operations scc_port_ops = {
* vme_scc_init() and support functions
*---------------------------------------------------------------------------*/
-static int scc_init_drivers(void)
+static int __init scc_init_drivers(void)
{
int error;
@@ -172,7 +172,7 @@ static int scc_init_drivers(void)
/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1).
*/
-static void scc_init_portstructs(void)
+static void __init scc_init_portstructs(void)
{
struct scc_port *port;
int i;
@@ -195,7 +195,7 @@ static void scc_init_portstructs(void)
#ifdef CONFIG_MVME147_SCC
-static int mvme147_scc_init(void)
+static int __init mvme147_scc_init(void)
{
struct scc_port *port;
int error;
@@ -298,7 +298,7 @@ fail:
#ifdef CONFIG_MVME162_SCC
-static int mvme162_scc_init(void)
+static int __init mvme162_scc_init(void)
{
struct scc_port *port;
int error;
@@ -404,7 +404,7 @@ fail:
#ifdef CONFIG_BVME6000_SCC
-static int bvme6000_scc_init(void)
+static int __init bvme6000_scc_init(void)
{
struct scc_port *port;
int error;
@@ -503,7 +503,7 @@ fail_free_b_rx:
#endif
-static int vme_scc_init(void)
+static int __init vme_scc_init(void)
{
int res = -ENODEV;
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
index 27d20fac19d..b314a999aab 100644
--- a/drivers/clocksource/cs5535-clockevt.c
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -21,7 +21,7 @@
#define DRV_NAME "cs5535-clockevt"
-static int timer_irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
+static int timer_irq;
module_param_named(irq, timer_irq, int, 0644);
MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 6b3e0c2f33e..578595c4425 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -40,7 +40,6 @@ struct sh_cmt_priv {
struct platform_device *pdev;
unsigned long flags;
- unsigned long flags_suspend;
unsigned long match_value;
unsigned long next_match_value;
unsigned long max_match_value;
@@ -432,6 +431,11 @@ static void sh_cmt_clocksource_disable(struct clocksource *cs)
sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
}
+static void sh_cmt_clocksource_resume(struct clocksource *cs)
+{
+ sh_cmt_start(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
+}
+
static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
char *name, unsigned long rating)
{
@@ -442,6 +446,8 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
cs->read = sh_cmt_clocksource_read;
cs->enable = sh_cmt_clocksource_enable;
cs->disable = sh_cmt_clocksource_disable;
+ cs->suspend = sh_cmt_clocksource_disable;
+ cs->resume = sh_cmt_clocksource_resume;
cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
pr_info("sh_cmt: %s used as clock source\n", cs->name);
@@ -603,18 +609,13 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->irqaction.handler = sh_cmt_interrupt;
p->irqaction.dev_id = p;
p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
- ret = setup_irq(irq, &p->irqaction);
- if (ret) {
- pr_err("sh_cmt: failed to request irq %d\n", irq);
- goto err1;
- }
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, cfg->clk);
if (IS_ERR(p->clk)) {
pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
ret = PTR_ERR(p->clk);
- goto err2;
+ goto err1;
}
if (resource_size(res) == 6) {
@@ -627,14 +628,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->clear_bits = ~0xc000;
}
- return sh_cmt_register(p, cfg->name,
- cfg->clockevent_rating,
- cfg->clocksource_rating);
- err2:
- remove_irq(irq, &p->irqaction);
- err1:
+ ret = sh_cmt_register(p, cfg->name,
+ cfg->clockevent_rating,
+ cfg->clocksource_rating);
+ if (ret) {
+ pr_err("sh_cmt: registration failed\n");
+ goto err1;
+ }
+
+ ret = setup_irq(irq, &p->irqaction);
+ if (ret) {
+ pr_err("sh_cmt: failed to request irq %d\n", irq);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
iounmap(p->mapbase);
- err0:
+err0:
return ret;
}
@@ -668,38 +680,11 @@ static int __devexit sh_cmt_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
-static int sh_cmt_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_cmt_priv *p = platform_get_drvdata(pdev);
-
- /* save flag state and stop CMT channel */
- p->flags_suspend = p->flags;
- sh_cmt_stop(p, p->flags);
- return 0;
-}
-
-static int sh_cmt_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sh_cmt_priv *p = platform_get_drvdata(pdev);
-
- /* start CMT channel from saved state */
- sh_cmt_start(p, p->flags_suspend);
- return 0;
-}
-
-static struct dev_pm_ops sh_cmt_dev_pm_ops = {
- .suspend = sh_cmt_suspend,
- .resume = sh_cmt_resume,
-};
-
static struct platform_driver sh_cmt_device_driver = {
.probe = sh_cmt_probe,
.remove = __devexit_p(sh_cmt_remove),
.driver = {
.name = "sh_cmt",
- .pm = &sh_cmt_dev_pm_ops,
}
};
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index 973e714d605..4c8a759e60c 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -221,15 +221,15 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
ced->cpumask = cpumask_of(0);
ced->set_mode = sh_mtu2_clock_event_mode;
+ pr_info("sh_mtu2: %s used for clock events\n", ced->name);
+ clockevents_register_device(ced);
+
ret = setup_irq(p->irqaction.irq, &p->irqaction);
if (ret) {
pr_err("sh_mtu2: failed to request irq %d\n",
p->irqaction.irq);
return;
}
-
- pr_info("sh_mtu2: %s used for clock events\n", ced->name);
- clockevents_register_device(ced);
}
static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index 93c2322feab..961f5b5ef6a 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -323,15 +323,15 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p,
ced->set_next_event = sh_tmu_clock_event_next;
ced->set_mode = sh_tmu_clock_event_mode;
+ pr_info("sh_tmu: %s used for clock events\n", ced->name);
+ clockevents_register_device(ced);
+
ret = setup_irq(p->irqaction.irq, &p->irqaction);
if (ret) {
pr_err("sh_tmu: failed to request irq %d\n",
p->irqaction.irq);
return;
}
-
- pr_info("sh_tmu: %s used for clock events\n", ced->name);
- clockevents_register_device(ced);
}
static int sh_tmu_register(struct sh_tmu_priv *p, char *name,
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 46e899ac924..1c3849f6b7a 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -1274,7 +1274,7 @@ static int __exit crypto4xx_remove(struct of_device *ofdev)
return 0;
}
-static struct of_device_id crypto4xx_match[] = {
+static const struct of_device_id crypto4xx_match[] = {
{ .compatible = "amcc,ppc4xx-crypto",},
{ },
};
diff --git a/drivers/crypto/geode-aes.c b/drivers/crypto/geode-aes.c
index 4801162919d..c7a5a43ba69 100644
--- a/drivers/crypto/geode-aes.c
+++ b/drivers/crypto/geode-aes.c
@@ -135,13 +135,13 @@ static int geode_setkey_cip(struct crypto_tfm *tfm, const u8 *key,
/*
* The requested key size is not supported by HW, do a fallback
*/
- op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
- op->fallback.blk->base.crt_flags |= (tfm->crt_flags & CRYPTO_TFM_REQ_MASK);
+ op->fallback.cip->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.cip->base.crt_flags |= (tfm->crt_flags & CRYPTO_TFM_REQ_MASK);
ret = crypto_cipher_setkey(op->fallback.cip, key, len);
if (ret) {
tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
- tfm->crt_flags |= (op->fallback.blk->base.crt_flags & CRYPTO_TFM_RES_MASK);
+ tfm->crt_flags |= (op->fallback.cip->base.crt_flags & CRYPTO_TFM_RES_MASK);
}
return ret;
}
@@ -263,7 +263,7 @@ static int fallback_init_cip(struct crypto_tfm *tfm)
if (IS_ERR(op->fallback.cip)) {
printk(KERN_ERR "Error allocating fallback algo %s\n", name);
- return PTR_ERR(op->fallback.blk);
+ return PTR_ERR(op->fallback.cip);
}
return 0;
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index c47ffe8a73e..fd529d68c5b 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -1958,7 +1958,7 @@ err_out:
return err;
}
-static struct of_device_id talitos_match[] = {
+static const struct of_device_id talitos_match[] = {
{
.compatible = "fsl,sec2.0",
},
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index d10cc899c46..b75ce8b84c4 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -48,23 +48,20 @@ enum sh_dmae_desc_status {
*/
#define RS_DEFAULT (RS_DUAL)
+/* A bitmask with bits enough for enum sh_dmae_slave_chan_id */
+static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SHDMA_SLAVE_NUMBER)];
+
static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
#define SH_DMAC_CHAN_BASE(id) (dma_base_addr[id])
static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
{
- ctrl_outl(data, (SH_DMAC_CHAN_BASE(sh_dc->id) + reg));
+ ctrl_outl(data, SH_DMAC_CHAN_BASE(sh_dc->id) + reg);
}
static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
{
- return ctrl_inl((SH_DMAC_CHAN_BASE(sh_dc->id) + reg));
-}
-
-static void dmae_init(struct sh_dmae_chan *sh_chan)
-{
- u32 chcr = RS_DEFAULT; /* default is DUAL mode */
- sh_dmae_writel(sh_chan, chcr, CHCR);
+ return ctrl_inl(SH_DMAC_CHAN_BASE(sh_dc->id) + reg);
}
/*
@@ -95,27 +92,30 @@ static int sh_dmae_rst(int id)
return 0;
}
-static int dmae_is_busy(struct sh_dmae_chan *sh_chan)
+static bool dmae_is_busy(struct sh_dmae_chan *sh_chan)
{
u32 chcr = sh_dmae_readl(sh_chan, CHCR);
- if (chcr & CHCR_DE) {
- if (!(chcr & CHCR_TE))
- return -EBUSY; /* working */
- }
- return 0; /* waiting */
+
+ if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE)
+ return true; /* working */
+
+ return false; /* waiting */
}
-static inline unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan)
+static unsigned int ts_shift[] = TS_SHIFT;
+static inline unsigned int calc_xmit_shift(u32 chcr)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
- return ts_shift[(chcr & CHCR_TS_MASK) >> CHCR_TS_SHIFT];
+ int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
+ ((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
+
+ return ts_shift[cnt];
}
static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw)
{
sh_dmae_writel(sh_chan, hw->sar, SAR);
sh_dmae_writel(sh_chan, hw->dar, DAR);
- sh_dmae_writel(sh_chan, hw->tcr >> calc_xmit_shift(sh_chan), TCR);
+ sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR);
}
static void dmae_start(struct sh_dmae_chan *sh_chan)
@@ -123,7 +123,7 @@ static void dmae_start(struct sh_dmae_chan *sh_chan)
u32 chcr = sh_dmae_readl(sh_chan, CHCR);
chcr |= CHCR_DE | CHCR_IE;
- sh_dmae_writel(sh_chan, chcr, CHCR);
+ sh_dmae_writel(sh_chan, chcr & ~CHCR_TE, CHCR);
}
static void dmae_halt(struct sh_dmae_chan *sh_chan)
@@ -134,55 +134,50 @@ static void dmae_halt(struct sh_dmae_chan *sh_chan)
sh_dmae_writel(sh_chan, chcr, CHCR);
}
+static void dmae_init(struct sh_dmae_chan *sh_chan)
+{
+ u32 chcr = RS_DEFAULT; /* default is DUAL mode */
+ sh_chan->xmit_shift = calc_xmit_shift(chcr);
+ sh_dmae_writel(sh_chan, chcr, CHCR);
+}
+
static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val)
{
- int ret = dmae_is_busy(sh_chan);
/* When DMA was working, can not set data to CHCR */
- if (ret)
- return ret;
+ if (dmae_is_busy(sh_chan))
+ return -EBUSY;
+ sh_chan->xmit_shift = calc_xmit_shift(val);
sh_dmae_writel(sh_chan, val, CHCR);
+
return 0;
}
-#define DMARS1_ADDR 0x04
-#define DMARS2_ADDR 0x08
-#define DMARS_SHIFT 8
-#define DMARS_CHAN_MSK 0x01
+#define DMARS_SHIFT 8
+#define DMARS_CHAN_MSK 0x01
static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
{
u32 addr;
int shift = 0;
- int ret = dmae_is_busy(sh_chan);
- if (ret)
- return ret;
+
+ if (dmae_is_busy(sh_chan))
+ return -EBUSY;
if (sh_chan->id & DMARS_CHAN_MSK)
shift = DMARS_SHIFT;
- switch (sh_chan->id) {
- /* DMARS0 */
- case 0:
- case 1:
- addr = SH_DMARS_BASE;
- break;
- /* DMARS1 */
- case 2:
- case 3:
- addr = (SH_DMARS_BASE + DMARS1_ADDR);
- break;
- /* DMARS2 */
- case 4:
- case 5:
- addr = (SH_DMARS_BASE + DMARS2_ADDR);
- break;
- default:
+ if (sh_chan->id < 6)
+ /* DMA0RS0 - DMA0RS2 */
+ addr = SH_DMARS_BASE0 + (sh_chan->id / 2) * 4;
+#ifdef SH_DMARS_BASE1
+ else if (sh_chan->id < 12)
+ /* DMA1RS0 - DMA1RS2 */
+ addr = SH_DMARS_BASE1 + ((sh_chan->id - 6) / 2) * 4;
+#endif
+ else
return -EINVAL;
- }
- ctrl_outw((val << shift) |
- (ctrl_inw(addr) & (shift ? 0xFF00 : 0x00FF)),
- addr);
+ ctrl_outw((val << shift) | (ctrl_inw(addr) & (0xFF00 >> shift)), addr);
return 0;
}
@@ -250,10 +245,53 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan)
return NULL;
}
+static struct sh_dmae_slave_config *sh_dmae_find_slave(
+ struct sh_dmae_chan *sh_chan, enum sh_dmae_slave_chan_id slave_id)
+{
+ struct dma_device *dma_dev = sh_chan->common.device;
+ struct sh_dmae_device *shdev = container_of(dma_dev,
+ struct sh_dmae_device, common);
+ struct sh_dmae_pdata *pdata = &shdev->pdata;
+ int i;
+
+ if ((unsigned)slave_id >= SHDMA_SLAVE_NUMBER)
+ return NULL;
+
+ for (i = 0; i < pdata->config_num; i++)
+ if (pdata->config[i].slave_id == slave_id)
+ return pdata->config + i;
+
+ return NULL;
+}
+
static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
{
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
struct sh_desc *desc;
+ struct sh_dmae_slave *param = chan->private;
+
+ /*
+ * This relies on the guarantee from dmaengine that alloc_chan_resources
+ * never runs concurrently with itself or free_chan_resources.
+ */
+ if (param) {
+ struct sh_dmae_slave_config *cfg;
+
+ cfg = sh_dmae_find_slave(sh_chan, param->slave_id);
+ if (!cfg)
+ return -EINVAL;
+
+ if (test_and_set_bit(param->slave_id, sh_dmae_slave_used))
+ return -EBUSY;
+
+ param->config = cfg;
+
+ dmae_set_dmars(sh_chan, cfg->mid_rid);
+ dmae_set_chcr(sh_chan, cfg->chcr);
+ } else {
+ if ((sh_dmae_readl(sh_chan, CHCR) & 0x700) != 0x400)
+ dmae_set_chcr(sh_chan, RS_DEFAULT);
+ }
spin_lock_bh(&sh_chan->desc_lock);
while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) {
@@ -286,10 +324,18 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
struct sh_desc *desc, *_desc;
LIST_HEAD(list);
+ dmae_halt(sh_chan);
+
/* Prepared and not submitted descriptors can still be on the queue */
if (!list_empty(&sh_chan->ld_queue))
sh_dmae_chan_ld_cleanup(sh_chan, true);
+ if (chan->private) {
+ /* The caller is holding dma_list_mutex */
+ struct sh_dmae_slave *param = chan->private;
+ clear_bit(param->slave_id, sh_dmae_slave_used);
+ }
+
spin_lock_bh(&sh_chan->desc_lock);
list_splice_init(&sh_chan->ld_free, &list);
@@ -301,23 +347,97 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
kfree(desc);
}
-static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
- struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
- size_t len, unsigned long flags)
+/**
+ * sh_dmae_add_desc - get, set up and return one transfer descriptor
+ * @sh_chan: DMA channel
+ * @flags: DMA transfer flags
+ * @dest: destination DMA address, incremented when direction equals
+ * DMA_FROM_DEVICE or DMA_BIDIRECTIONAL
+ * @src: source DMA address, incremented when direction equals
+ * DMA_TO_DEVICE or DMA_BIDIRECTIONAL
+ * @len: DMA transfer length
+ * @first: if NULL, set to the current descriptor and cookie set to -EBUSY
+ * @direction: needed for slave DMA to decide which address to keep constant,
+ * equals DMA_BIDIRECTIONAL for MEMCPY
+ * Returns 0 or an error
+ * Locks: called with desc_lock held
+ */
+static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
+ unsigned long flags, dma_addr_t *dest, dma_addr_t *src, size_t *len,
+ struct sh_desc **first, enum dma_data_direction direction)
{
- struct sh_dmae_chan *sh_chan;
- struct sh_desc *first = NULL, *prev = NULL, *new;
+ struct sh_desc *new;
size_t copy_size;
- LIST_HEAD(tx_list);
- int chunks = (len + SH_DMA_TCR_MAX) / (SH_DMA_TCR_MAX + 1);
- if (!chan)
+ if (!*len)
return NULL;
- if (!len)
+ /* Allocate the link descriptor from the free list */
+ new = sh_dmae_get_desc(sh_chan);
+ if (!new) {
+ dev_err(sh_chan->dev, "No free link descriptor available\n");
return NULL;
+ }
- sh_chan = to_sh_chan(chan);
+ copy_size = min(*len, (size_t)SH_DMA_TCR_MAX + 1);
+
+ new->hw.sar = *src;
+ new->hw.dar = *dest;
+ new->hw.tcr = copy_size;
+
+ if (!*first) {
+ /* First desc */
+ new->async_tx.cookie = -EBUSY;
+ *first = new;
+ } else {
+ /* Other desc - invisible to the user */
+ new->async_tx.cookie = -EINVAL;
+ }
+
+ dev_dbg(sh_chan->dev,
+ "chaining (%u/%u)@%x -> %x with %p, cookie %d, shift %d\n",
+ copy_size, *len, *src, *dest, &new->async_tx,
+ new->async_tx.cookie, sh_chan->xmit_shift);
+
+ new->mark = DESC_PREPARED;
+ new->async_tx.flags = flags;
+ new->direction = direction;
+
+ *len -= copy_size;
+ if (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE)
+ *src += copy_size;
+ if (direction == DMA_BIDIRECTIONAL || direction == DMA_FROM_DEVICE)
+ *dest += copy_size;
+
+ return new;
+}
+
+/*
+ * sh_dmae_prep_sg - prepare transfer descriptors from an SG list
+ *
+ * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also
+ * converted to scatter-gather to guarantee consistent locking and a correct
+ * list manipulation. For slave DMA direction carries the usual meaning, and,
+ * logically, the SG list is RAM and the addr variable contains slave address,
+ * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_BIDIRECTIONAL
+ * and the SG list contains only one element and points at the source buffer.
+ */
+static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_chan,
+ struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr,
+ enum dma_data_direction direction, unsigned long flags)
+{
+ struct scatterlist *sg;
+ struct sh_desc *first = NULL, *new = NULL /* compiler... */;
+ LIST_HEAD(tx_list);
+ int chunks = 0;
+ int i;
+
+ if (!sg_len)
+ return NULL;
+
+ for_each_sg(sgl, sg, sg_len, i)
+ chunks += (sg_dma_len(sg) + SH_DMA_TCR_MAX) /
+ (SH_DMA_TCR_MAX + 1);
/* Have to lock the whole loop to protect against concurrent release */
spin_lock_bh(&sh_chan->desc_lock);
@@ -333,49 +453,32 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
* only during this function, then they are immediately spliced
* back onto the free list in form of a chain
*/
- do {
- /* Allocate the link descriptor from the free list */
- new = sh_dmae_get_desc(sh_chan);
- if (!new) {
- dev_err(sh_chan->dev,
- "No free memory for link descriptor\n");
- list_for_each_entry(new, &tx_list, node)
- new->mark = DESC_IDLE;
- list_splice(&tx_list, &sh_chan->ld_free);
- spin_unlock_bh(&sh_chan->desc_lock);
- return NULL;
- }
-
- copy_size = min(len, (size_t)SH_DMA_TCR_MAX + 1);
-
- new->hw.sar = dma_src;
- new->hw.dar = dma_dest;
- new->hw.tcr = copy_size;
- if (!first) {
- /* First desc */
- new->async_tx.cookie = -EBUSY;
- first = new;
- } else {
- /* Other desc - invisible to the user */
- new->async_tx.cookie = -EINVAL;
- }
-
- dev_dbg(sh_chan->dev,
- "chaining %u of %u with %p, dst %x, cookie %d\n",
- copy_size, len, &new->async_tx, dma_dest,
- new->async_tx.cookie);
-
- new->mark = DESC_PREPARED;
- new->async_tx.flags = flags;
- new->chunks = chunks--;
-
- prev = new;
- len -= copy_size;
- dma_src += copy_size;
- dma_dest += copy_size;
- /* Insert the link descriptor to the LD ring */
- list_add_tail(&new->node, &tx_list);
- } while (len);
+ for_each_sg(sgl, sg, sg_len, i) {
+ dma_addr_t sg_addr = sg_dma_address(sg);
+ size_t len = sg_dma_len(sg);
+
+ if (!len)
+ goto err_get_desc;
+
+ do {
+ dev_dbg(sh_chan->dev, "Add SG #%d@%p[%d], dma %llx\n",
+ i, sg, len, (unsigned long long)sg_addr);
+
+ if (direction == DMA_FROM_DEVICE)
+ new = sh_dmae_add_desc(sh_chan, flags,
+ &sg_addr, addr, &len, &first,
+ direction);
+ else
+ new = sh_dmae_add_desc(sh_chan, flags,
+ addr, &sg_addr, &len, &first,
+ direction);
+ if (!new)
+ goto err_get_desc;
+
+ new->chunks = chunks--;
+ list_add_tail(&new->node, &tx_list);
+ } while (len);
+ }
if (new != first)
new->async_tx.cookie = -ENOSPC;
@@ -386,6 +489,77 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
spin_unlock_bh(&sh_chan->desc_lock);
return &first->async_tx;
+
+err_get_desc:
+ list_for_each_entry(new, &tx_list, node)
+ new->mark = DESC_IDLE;
+ list_splice(&tx_list, &sh_chan->ld_free);
+
+ spin_unlock_bh(&sh_chan->desc_lock);
+
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
+ struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
+ size_t len, unsigned long flags)
+{
+ struct sh_dmae_chan *sh_chan;
+ struct scatterlist sg;
+
+ if (!chan || !len)
+ return NULL;
+
+ chan->private = NULL;
+
+ sh_chan = to_sh_chan(chan);
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_src)), len,
+ offset_in_page(dma_src));
+ sg_dma_address(&sg) = dma_src;
+ sg_dma_len(&sg) = len;
+
+ return sh_dmae_prep_sg(sh_chan, &sg, 1, &dma_dest, DMA_BIDIRECTIONAL,
+ flags);
+}
+
+static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
+ enum dma_data_direction direction, unsigned long flags)
+{
+ struct sh_dmae_slave *param;
+ struct sh_dmae_chan *sh_chan;
+
+ if (!chan)
+ return NULL;
+
+ sh_chan = to_sh_chan(chan);
+ param = chan->private;
+
+ /* Someone calling slave DMA on a public channel? */
+ if (!param || !sg_len) {
+ dev_warn(sh_chan->dev, "%s: bad parameter: %p, %d, %d\n",
+ __func__, param, sg_len, param ? param->slave_id : -1);
+ return NULL;
+ }
+
+ /*
+ * if (param != NULL), this is a successfully requested slave channel,
+ * therefore param->config != NULL too.
+ */
+ return sh_dmae_prep_sg(sh_chan, sgl, sg_len, &param->config->addr,
+ direction, flags);
+}
+
+static void sh_dmae_terminate_all(struct dma_chan *chan)
+{
+ struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
+
+ if (!chan)
+ return;
+
+ sh_dmae_chan_ld_cleanup(sh_chan, true);
}
static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
@@ -419,7 +593,11 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
cookie = tx->cookie;
if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
- BUG_ON(sh_chan->completed_cookie != desc->cookie - 1);
+ if (sh_chan->completed_cookie != desc->cookie - 1)
+ dev_dbg(sh_chan->dev,
+ "Completing cookie %d, expected %d\n",
+ desc->cookie,
+ sh_chan->completed_cookie + 1);
sh_chan->completed_cookie = desc->cookie;
}
@@ -492,7 +670,7 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
return;
}
- /* Find the first un-transfer desciptor */
+ /* Find the first not transferred desciptor */
list_for_each_entry(sd, &sh_chan->ld_queue, node)
if (sd->mark == DESC_SUBMITTED) {
/* Get the ld start address from ld_queue */
@@ -559,7 +737,7 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
/* IRQ Multi */
if (shdev->pdata.mode & SHDMA_MIX_IRQ) {
- int cnt = 0;
+ int __maybe_unused cnt = 0;
switch (irq) {
#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ)
case DMTE6_IRQ:
@@ -596,11 +774,14 @@ static void dmae_do_tasklet(unsigned long data)
struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data;
struct sh_desc *desc;
u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
+ u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
spin_lock(&sh_chan->desc_lock);
list_for_each_entry(desc, &sh_chan->ld_queue, node) {
- if ((desc->hw.sar + desc->hw.tcr) == sar_buf &&
- desc->mark == DESC_SUBMITTED) {
+ if (desc->mark == DESC_SUBMITTED &&
+ ((desc->direction == DMA_FROM_DEVICE &&
+ (desc->hw.dar + desc->hw.tcr) == dar_buf) ||
+ (desc->hw.sar + desc->hw.tcr) == sar_buf)) {
dev_dbg(sh_chan->dev, "done #%d@%p dst %u\n",
desc->async_tx.cookie, &desc->async_tx,
desc->hw.dar);
@@ -673,7 +854,7 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id)
}
snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
- "sh-dmae%d", new_sh_chan->id);
+ "sh-dmae%d", new_sh_chan->id);
/* set up channel irq */
err = request_irq(irq, &sh_dmae_interrupt, irqflags,
@@ -684,11 +865,6 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id)
goto err_no_irq;
}
- /* CHCR register control function */
- new_sh_chan->set_chcr = dmae_set_chcr;
- /* DMARS register control function */
- new_sh_chan->set_dmars = dmae_set_dmars;
-
shdev->chan[id] = new_sh_chan;
return 0;
@@ -759,12 +935,19 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&shdev->common.channels);
dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask);
+ dma_cap_set(DMA_SLAVE, shdev->common.cap_mask);
+
shdev->common.device_alloc_chan_resources
= sh_dmae_alloc_chan_resources;
shdev->common.device_free_chan_resources = sh_dmae_free_chan_resources;
shdev->common.device_prep_dma_memcpy = sh_dmae_prep_memcpy;
shdev->common.device_is_tx_complete = sh_dmae_is_complete;
shdev->common.device_issue_pending = sh_dmae_memcpy_issue_pending;
+
+ /* Compulsory for DMA_SLAVE fields */
+ shdev->common.device_prep_slave_sg = sh_dmae_prep_slave_sg;
+ shdev->common.device_terminate_all = sh_dmae_terminate_all;
+
shdev->common.dev = &pdev->dev;
/* Default transfer size of 32 bytes requires 32-byte alignment */
shdev->common.copy_align = 5;
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 108f1cffb6f..7e227f3c87c 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -29,6 +29,7 @@ struct sh_desc {
struct sh_dmae_regs hw;
struct list_head node;
struct dma_async_tx_descriptor async_tx;
+ enum dma_data_direction direction;
dma_cookie_t cookie;
int chunks;
int mark;
@@ -45,13 +46,9 @@ struct sh_dmae_chan {
struct device *dev; /* Channel device */
struct tasklet_struct tasklet; /* Tasklet */
int descs_allocated; /* desc count */
+ int xmit_shift; /* log_2(bytes_per_xfer) */
int id; /* Raw id of this channel */
char dev_id[16]; /* unique name per DMAC of channel */
-
- /* Set chcr */
- int (*set_chcr)(struct sh_dmae_chan *sh_chan, u32 regs);
- /* Set DMA resource */
- int (*set_dmars)(struct sh_dmae_chan *sh_chan, u16 res);
};
struct sh_dmae_device {
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index cbaf420c36c..2d3dc7ded0a 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -893,20 +893,31 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
static struct kmem_cache *fwnet_packet_task_cache;
+static void fwnet_free_ptask(struct fwnet_packet_task *ptask)
+{
+ dev_kfree_skb_any(ptask->skb);
+ kmem_cache_free(fwnet_packet_task_cache, ptask);
+}
+
static int fwnet_send_packet(struct fwnet_packet_task *ptask);
static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
{
- struct fwnet_device *dev;
+ struct fwnet_device *dev = ptask->dev;
unsigned long flags;
-
- dev = ptask->dev;
+ bool free;
spin_lock_irqsave(&dev->lock, flags);
- list_del(&ptask->pt_link);
- spin_unlock_irqrestore(&dev->lock, flags);
- ptask->outstanding_pkts--; /* FIXME access inside lock */
+ ptask->outstanding_pkts--;
+
+ /* Check whether we or the networking TX soft-IRQ is last user. */
+ free = (ptask->outstanding_pkts == 0 && !list_empty(&ptask->pt_link));
+
+ if (ptask->outstanding_pkts == 0)
+ list_del(&ptask->pt_link);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
if (ptask->outstanding_pkts > 0) {
u16 dg_size;
@@ -951,10 +962,10 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE;
}
fwnet_send_packet(ptask);
- } else {
- dev_kfree_skb_any(ptask->skb);
- kmem_cache_free(fwnet_packet_task_cache, ptask);
}
+
+ if (free)
+ fwnet_free_ptask(ptask);
}
static void fwnet_write_complete(struct fw_card *card, int rcode,
@@ -977,6 +988,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
unsigned tx_len;
struct rfc2734_header *bufhdr;
unsigned long flags;
+ bool free;
dev = ptask->dev;
tx_len = ptask->max_payload;
@@ -1022,12 +1034,16 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
generation, SCODE_100, 0ULL, ptask->skb->data,
tx_len + 8, fwnet_write_complete, ptask);
- /* FIXME race? */
spin_lock_irqsave(&dev->lock, flags);
- list_add_tail(&ptask->pt_link, &dev->broadcasted_list);
+
+ /* If the AT tasklet already ran, we may be last user. */
+ free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link));
+ if (!free)
+ list_add_tail(&ptask->pt_link, &dev->broadcasted_list);
+
spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
+ goto out;
}
fw_send_request(dev->card, &ptask->transaction,
@@ -1035,12 +1051,19 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
ptask->generation, ptask->speed, ptask->fifo_addr,
ptask->skb->data, tx_len, fwnet_write_complete, ptask);
- /* FIXME race? */
spin_lock_irqsave(&dev->lock, flags);
- list_add_tail(&ptask->pt_link, &dev->sent_list);
+
+ /* If the AT tasklet already ran, we may be last user. */
+ free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link));
+ if (!free)
+ list_add_tail(&ptask->pt_link, &dev->sent_list);
+
spin_unlock_irqrestore(&dev->lock, flags);
dev->netdev->trans_start = jiffies;
+ out:
+ if (free)
+ fwnet_free_ptask(ptask);
return 0;
}
@@ -1298,6 +1321,8 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
spin_unlock_irqrestore(&dev->lock, flags);
ptask->max_payload = max_payload;
+ INIT_LIST_HEAD(&ptask->pt_link);
+
fwnet_send_packet(ptask);
return NETDEV_TX_OK;
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 2345d4103fe..43ebf337b13 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -2101,11 +2101,6 @@ static int ohci_queue_iso_transmit(struct fw_iso_context *base,
u32 payload_index, payload_end_index, next_page_index;
int page, end_page, i, length, offset;
- /*
- * FIXME: Cycle lost behavior should be configurable: lose
- * packet, retransmit or terminate..
- */
-
p = packet;
payload_index = payload;
@@ -2135,6 +2130,14 @@ static int ohci_queue_iso_transmit(struct fw_iso_context *base,
if (!p->skip) {
d[0].control = cpu_to_le16(DESCRIPTOR_KEY_IMMEDIATE);
d[0].req_count = cpu_to_le16(8);
+ /*
+ * Link the skip address to this descriptor itself. This causes
+ * a context to skip a cycle whenever lost cycles or FIFO
+ * overruns occur, without dropping the data. The application
+ * should then decide whether this is an error condition or not.
+ * FIXME: Make the context's cycle-lost behaviour configurable?
+ */
+ d[0].branch_address = cpu_to_le32(d_bus | z);
header = (__le32 *) &d[1];
header[0] = cpu_to_le32(IT_HEADER_SY(p->sy) |
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index d485cdd8cba..70fef40cd22 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -1571,7 +1571,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
sdev->start_stop_pwr_cond = 1;
if (lu->tgt->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
- blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
+ blk_queue_max_hw_sectors(sdev->request_queue, 128 * 1024 / 512);
blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE);
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 051d1ebbd28..f82bcdae130 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -381,7 +381,7 @@ static ssize_t ibft_attr_show_nic(struct ibft_kobject *entry,
void *ibft_loc = entry->header;
char *str = buf;
char *mac;
- int val;
+ __be32 val;
if (!nic)
return 0;
@@ -397,10 +397,8 @@ static ssize_t ibft_attr_show_nic(struct ibft_kobject *entry,
str += sprintf_ipaddr(str, nic->ip_addr);
break;
case ibft_eth_subnet_mask:
- val = ~((1 << (32-nic->subnet_mask_prefix))-1);
- str += sprintf(str, NIPQUAD_FMT,
- (u8)(val >> 24), (u8)(val >> 16),
- (u8)(val >> 8), (u8)(val));
+ val = cpu_to_be32(~((1 << (32-nic->subnet_mask_prefix))-1));
+ str += sprintf(str, "%pI4", &val);
break;
case ibft_eth_origin:
str += sprintf(str, "%d\n", nic->origin);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index f665b05592f..ab6c9733041 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -598,6 +598,50 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
return mode;
}
+/*
+ * EDID is delightfully ambiguous about how interlaced modes are to be
+ * encoded. Our internal representation is of frame height, but some
+ * HDTV detailed timings are encoded as field height.
+ *
+ * The format list here is from CEA, in frame size. Technically we
+ * should be checking refresh rate too. Whatever.
+ */
+static void
+drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
+ struct detailed_pixel_timing *pt)
+{
+ int i;
+ static const struct {
+ int w, h;
+ } cea_interlaced[] = {
+ { 1920, 1080 },
+ { 720, 480 },
+ { 1440, 480 },
+ { 2880, 480 },
+ { 720, 576 },
+ { 1440, 576 },
+ { 2880, 576 },
+ };
+ static const int n_sizes =
+ sizeof(cea_interlaced)/sizeof(cea_interlaced[0]);
+
+ if (!(pt->misc & DRM_EDID_PT_INTERLACED))
+ return;
+
+ for (i = 0; i < n_sizes; i++) {
+ if ((mode->hdisplay == cea_interlaced[i].w) &&
+ (mode->vdisplay == cea_interlaced[i].h / 2)) {
+ mode->vdisplay *= 2;
+ mode->vsync_start *= 2;
+ mode->vsync_end *= 2;
+ mode->vtotal *= 2;
+ mode->vtotal |= 1;
+ }
+ }
+
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+}
+
/**
* drm_mode_detailed - create a new mode from an EDID detailed timing section
* @dev: DRM device (needed to create new mode)
@@ -680,8 +724,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
drm_mode_set_name(mode);
- if (pt->misc & DRM_EDID_PT_INTERLACED)
- mode->flags |= DRM_MODE_FLAG_INTERLACE;
+ drm_mode_do_interlace_quirk(mode, pt);
if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index cdec3297712..2ac074c8f5d 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -405,7 +405,8 @@ struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
wasted += alignment - tmp;
}
- if (entry->size >= size + wasted) {
+ if (entry->size >= size + wasted &&
+ (entry->start + wasted + size) <= end) {
if (!best_match)
return entry;
if (entry->size < best_size) {
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 79beffcf593..cf4cb3e9a0c 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -176,6 +176,8 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
static int i915_drm_freeze(struct drm_device *dev)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
pci_save_state(dev->pdev);
/* If KMS is active, we do the leavevt stuff here */
@@ -191,17 +193,12 @@ static int i915_drm_freeze(struct drm_device *dev)
i915_save_state(dev);
- return 0;
-}
-
-static void i915_drm_suspend(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
intel_opregion_free(dev, 1);
/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
+
+ return 0;
}
static int i915_suspend(struct drm_device *dev, pm_message_t state)
@@ -221,8 +218,6 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
if (error)
return error;
- i915_drm_suspend(dev);
-
if (state.event == PM_EVENT_SUSPEND) {
/* Shut down the device */
pci_disable_device(dev->pdev);
@@ -237,6 +232,10 @@ static int i915_drm_thaw(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int error = 0;
+ i915_restore_state(dev);
+
+ intel_opregion_init(dev, 1);
+
/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
mutex_lock(&dev->struct_mutex);
@@ -263,10 +262,6 @@ static int i915_resume(struct drm_device *dev)
pci_set_master(dev->pdev);
- i915_restore_state(dev);
-
- intel_opregion_init(dev, 1);
-
return i915_drm_thaw(dev);
}
@@ -423,8 +418,6 @@ static int i915_pm_suspend(struct device *dev)
if (error)
return error;
- i915_drm_suspend(drm_dev);
-
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
@@ -464,13 +457,8 @@ static int i915_pm_poweroff(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- int error;
-
- error = i915_drm_freeze(drm_dev);
- if (!error)
- i915_drm_suspend(drm_dev);
- return error;
+ return i915_drm_freeze(drm_dev);
}
const struct dev_pm_ops i915_pm_ops = {
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index b1d0acbae4e..c2e8a45780d 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -636,6 +636,13 @@ static const struct dmi_system_id bad_lid_status[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"),
},
},
+ {
+ .ident = "Clevo M5x0N",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
+ DMI_MATCH(DMI_BOARD_NAME, "M5x0N"),
+ },
+ },
{ }
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 2cd0fad17da..0e9cd1d4913 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -5861,13 +5861,12 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->VBIOS;
struct init_exec iexec = { true, false };
- unsigned long flags;
- spin_lock_irqsave(&bios->lock, flags);
+ mutex_lock(&bios->lock);
bios->display.output = dcbent;
parse_init_table(bios, table, &iexec);
bios->display.output = NULL;
- spin_unlock_irqrestore(&bios->lock, flags);
+ mutex_unlock(&bios->lock);
}
static bool NVInitVBIOS(struct drm_device *dev)
@@ -5876,7 +5875,7 @@ static bool NVInitVBIOS(struct drm_device *dev)
struct nvbios *bios = &dev_priv->VBIOS;
memset(bios, 0, sizeof(struct nvbios));
- spin_lock_init(&bios->lock);
+ mutex_init(&bios->lock);
bios->dev = dev;
if (!NVShadowVBIOS(dev, bios->data))
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 68446fd4146..fd94bd6dc26 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -205,7 +205,7 @@ struct nvbios {
struct drm_device *dev;
struct nouveau_bios_info pub;
- spinlock_t lock;
+ struct mutex lock;
uint8_t data[NV_PROM_SIZE];
unsigned int length;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 5445cefdd03..1c15ef37b71 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -583,6 +583,7 @@ struct drm_nouveau_private {
uint64_t vm_end;
struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
int vm_vram_pt_nr;
+ uint64_t vram_sys_base;
/* the mtrr covering the FB */
int fb_mtrr;
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 8f3a12f614e..2dc09dbd817 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -285,53 +285,50 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
uint32_t flags, uint64_t phys)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj **pgt;
- unsigned psz, pfl, pages;
-
- if (virt >= dev_priv->vm_gart_base &&
- (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
- psz = 12;
- pgt = &dev_priv->gart_info.sg_ctxdma;
- pfl = 0x21;
- virt -= dev_priv->vm_gart_base;
- } else
- if (virt >= dev_priv->vm_vram_base &&
- (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
- psz = 16;
- pgt = dev_priv->vm_vram_pt;
- pfl = 0x01;
- virt -= dev_priv->vm_vram_base;
- } else {
- NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
- virt, virt + size - 1);
- return -EINVAL;
- }
+ struct nouveau_gpuobj *pgt;
+ unsigned block;
+ int i;
- pages = size >> psz;
+ virt = ((virt - dev_priv->vm_vram_base) >> 16) << 1;
+ size = (size >> 16) << 1;
+
+ phys |= ((uint64_t)flags << 32);
+ phys |= 1;
+ if (dev_priv->vram_sys_base) {
+ phys += dev_priv->vram_sys_base;
+ phys |= 0x30;
+ }
dev_priv->engine.instmem.prepare_access(dev, true);
- if (flags & 0x80000000) {
- while (pages--) {
- struct nouveau_gpuobj *pt = pgt[virt >> 29];
- unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
+ while (size) {
+ unsigned offset_h = upper_32_bits(phys);
+ unsigned offset_l = lower_32_bits(phys);
+ unsigned pte, end;
+
+ for (i = 7; i >= 0; i--) {
+ block = 1 << (i + 1);
+ if (size >= block && !(virt & (block - 1)))
+ break;
+ }
+ offset_l |= (i << 7);
- nv_wo32(dev, pt, pte++, 0x00000000);
- nv_wo32(dev, pt, pte++, 0x00000000);
+ phys += block << 15;
+ size -= block;
- virt += (1 << psz);
- }
- } else {
- while (pages--) {
- struct nouveau_gpuobj *pt = pgt[virt >> 29];
- unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
- unsigned offset_h = upper_32_bits(phys) & 0xff;
- unsigned offset_l = lower_32_bits(phys);
+ while (block) {
+ pgt = dev_priv->vm_vram_pt[virt >> 14];
+ pte = virt & 0x3ffe;
- nv_wo32(dev, pt, pte++, offset_l | pfl);
- nv_wo32(dev, pt, pte++, offset_h | flags);
+ end = pte + block;
+ if (end > 16384)
+ end = 16384;
+ block -= (end - pte);
+ virt += (end - pte);
- phys += (1 << psz);
- virt += (1 << psz);
+ while (pte < end) {
+ nv_wo32(dev, pgt, pte++, offset_l);
+ nv_wo32(dev, pgt, pte++, offset_h);
+ }
}
}
dev_priv->engine.instmem.finish_access(dev);
@@ -356,7 +353,41 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
void
nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
{
- nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *pgt;
+ unsigned pages, pte, end;
+
+ virt -= dev_priv->vm_vram_base;
+ pages = (size >> 16) << 1;
+
+ dev_priv->engine.instmem.prepare_access(dev, true);
+ while (pages) {
+ pgt = dev_priv->vm_vram_pt[virt >> 29];
+ pte = (virt & 0x1ffe0000ULL) >> 15;
+
+ end = pte + pages;
+ if (end > 16384)
+ end = 16384;
+ pages -= (end - pte);
+ virt += (end - pte) << 15;
+
+ while (pte < end)
+ nv_wo32(dev, pgt, pte++, 0);
+ }
+ dev_priv->engine.instmem.finish_access(dev);
+
+ nv_wr32(dev, 0x100c80, 0x00050001);
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+ return;
+ }
+
+ nv_wr32(dev, 0x100c80, 0x00000001);
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+ }
}
/*
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
index d0e038d2894..1d73b15d70d 100644
--- a/drivers/gpu/drm/nouveau/nv04_dac.c
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -119,7 +119,7 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct drm_device *dev = encoder->dev;
- uint8_t saved_seq1, saved_pi, saved_rpc1;
+ uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode;
uint8_t saved_palette0[3], saved_palette_mask;
uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
int i;
@@ -135,6 +135,9 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
/* only implemented for head A for now */
NVSetOwner(dev, 0);
+ saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80);
+
saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
@@ -203,6 +206,7 @@ out:
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode);
if (blue == 0x18) {
NV_INFO(dev, "Load detected on head A\n");
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
index 58b917c3341..21ac6e49b6e 100644
--- a/drivers/gpu/drm/nouveau/nv17_tv.c
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
@@ -579,6 +579,8 @@ static void nv17_tv_restore(struct drm_encoder *encoder)
nouveau_encoder(encoder)->restore.output);
nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
+
+ nouveau_encoder(encoder)->last_dpms = NV_DPMS_CLEARED;
}
static int nv17_tv_create_resources(struct drm_encoder *encoder,
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index 94400f777e7..f0dc4e36ef0 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -76,6 +76,11 @@ nv50_instmem_init(struct drm_device *dev)
for (i = 0x1700; i <= 0x1710; i += 4)
priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
+ if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac)
+ dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12;
+ else
+ dev_priv->vram_sys_base = 0;
+
/* Reserve the last MiB of VRAM, we should probably try to avoid
* setting up the below tables over the top of the VBIOS image at
* some point.
@@ -172,16 +177,28 @@ nv50_instmem_init(struct drm_device *dev)
* We map the entire fake channel into the start of the PRAMIN BAR
*/
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
- 0, &priv->pramin_pt);
+ 0, &priv->pramin_pt);
if (ret)
return ret;
- for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
- if (v < (c_offset + c_size))
- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
- else
- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
+ v = c_offset | 1;
+ if (dev_priv->vram_sys_base) {
+ v += dev_priv->vram_sys_base;
+ v |= 0x30;
+ }
+
+ i = 0;
+ while (v < dev_priv->vram_sys_base + c_offset + c_size) {
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v);
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
+ v += 0x1000;
+ i += 8;
+ }
+
+ while (i < pt_size) {
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000000);
BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
+ i += 8;
}
BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
@@ -416,7 +433,9 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
- uint32_t pte, pte_end, vram;
+ struct nouveau_gpuobj *pramin_pt = priv->pramin_pt->gpuobj;
+ uint32_t pte, pte_end;
+ uint64_t vram;
if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
return -EINVAL;
@@ -424,20 +443,24 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
gpuobj->im_pramin->start, gpuobj->im_pramin->size);
- pte = (gpuobj->im_pramin->start >> 12) << 3;
- pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
+ pte = (gpuobj->im_pramin->start >> 12) << 1;
+ pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
vram = gpuobj->im_backing_start;
NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
gpuobj->im_pramin->start, pte, pte_end);
NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+ vram |= 1;
+ if (dev_priv->vram_sys_base) {
+ vram += dev_priv->vram_sys_base;
+ vram |= 0x30;
+ }
+
dev_priv->engine.instmem.prepare_access(dev, true);
while (pte < pte_end) {
- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
-
- pte += 8;
+ nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram));
+ nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram));
vram += NV50_INSTMEM_PAGE_SIZE;
}
dev_priv->engine.instmem.finish_access(dev);
@@ -470,14 +493,13 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
if (gpuobj->im_bound == 0)
return -EINVAL;
- pte = (gpuobj->im_pramin->start >> 12) << 3;
- pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
+ pte = (gpuobj->im_pramin->start >> 12) << 1;
+ pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
dev_priv->engine.instmem.prepare_access(dev, true);
while (pte < pte_end) {
- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
- pte += 8;
+ nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
+ nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
}
dev_priv->engine.instmem.finish_access(dev);
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index e3b44562d26..7f152f66f19 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/sched.h>
+#include <asm/unaligned.h>
#define ATOM_DEBUG
@@ -212,7 +213,9 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
case ATOM_ARG_PS:
idx = U8(*ptr);
(*ptr)++;
- val = le32_to_cpu(ctx->ps[idx]);
+ /* get_unaligned_le32 avoids unaligned accesses from atombios
+ * tables, noticed on a DEC Alpha. */
+ val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
if (print)
DEBUG("PS[0x%02X,0x%04X]", idx, val);
break;
@@ -640,7 +643,7 @@ static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
uint8_t count = U8((*ptr)++);
SDEBUG(" count: %d\n", count);
if (arg == ATOM_UNIT_MICROSEC)
- schedule_timeout_uninterruptible(usecs_to_jiffies(count));
+ udelay(count);
else
schedule_timeout_uninterruptible(msecs_to_jiffies(count));
}
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index b32eeea5bb8..99915a682d5 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -350,7 +350,7 @@ retry:
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
if (args.ucReplyStatus && !args.ucDataOutLen) {
- if (args.ucReplyStatus == 0x20 && retry_count < 10)
+ if (args.ucReplyStatus == 0x20 && retry_count++ < 10)
goto retry;
DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n",
req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
index af1c3ca8a4c..446b765ac72 100644
--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
+++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
@@ -543,9 +543,6 @@ int r600_vb_ib_get(struct radeon_device *rdev)
void r600_vb_ib_put(struct radeon_device *rdev)
{
radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
- mutex_lock(&rdev->ib_pool.mutex);
- list_add_tail(&rdev->r600_blit.vb_ib->list, &rdev->ib_pool.scheduled_ibs);
- mutex_unlock(&rdev->ib_pool.mutex);
radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
}
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index 6d5a711c2e9..75bcf35a093 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -1428,9 +1428,12 @@ static void r700_gfx_init(struct drm_device *dev,
gb_tiling_config |= R600_BANK_SWAPS(1);
- backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
- dev_priv->r600_max_backends,
- (0xff << dev_priv->r600_max_backends) & 0xff);
+ if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740)
+ backend_map = 0x28;
+ else
+ backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
+ dev_priv->r600_max_backends,
+ (0xff << dev_priv->r600_max_backends) & 0xff);
gb_tiling_config |= R600_BACKEND_MAP(backend_map);
cc_gc_shader_pipe_config =
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index f57480ba135..c0356bb193e 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -96,6 +96,7 @@ extern int radeon_audio;
* symbol;
*/
#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */
+/* RADEON_IB_POOL_SIZE must be a power of 2 */
#define RADEON_IB_POOL_SIZE 16
#define RADEON_DEBUGFS_MAX_NUM_FILES 32
#define RADEONFB_CONN_LIMIT 4
@@ -363,11 +364,12 @@ void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev);
*/
struct radeon_ib {
struct list_head list;
- unsigned long idx;
+ unsigned idx;
uint64_t gpu_addr;
struct radeon_fence *fence;
- uint32_t *ptr;
+ uint32_t *ptr;
uint32_t length_dw;
+ bool free;
};
/*
@@ -377,10 +379,9 @@ struct radeon_ib {
struct radeon_ib_pool {
struct mutex mutex;
struct radeon_bo *robj;
- struct list_head scheduled_ibs;
struct radeon_ib ibs[RADEON_IB_POOL_SIZE];
bool ready;
- DECLARE_BITMAP(alloc_bm, RADEON_IB_POOL_SIZE);
+ unsigned head_id;
};
struct radeon_cp {
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 2dcda611587..4d8831548a5 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -206,6 +206,15 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
*connector_type = DRM_MODE_CONNECTOR_DVID;
}
+ /* Asrock RS600 board lists the DVI port as HDMI */
+ if ((dev->pdev->device == 0x7941) &&
+ (dev->pdev->subsystem_vendor == 0x1849) &&
+ (dev->pdev->subsystem_device == 0x7941)) {
+ if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) &&
+ (supported_device == ATOM_DEVICE_DFP3_SUPPORT))
+ *connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+
/* a-bit f-i90hd - ciaranm on #radeonhd - this board has no DVI */
if ((dev->pdev->device == 0x7941) &&
(dev->pdev->subsystem_vendor == 0x147b) &&
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index e7b19440102..22d476160d5 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -1279,47 +1279,47 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
rdev->mode_info.connector_table = radeon_connector_table;
if (rdev->mode_info.connector_table == CT_NONE) {
#ifdef CONFIG_PPC_PMAC
- if (machine_is_compatible("PowerBook3,3")) {
+ if (of_machine_is_compatible("PowerBook3,3")) {
/* powerbook with VGA */
rdev->mode_info.connector_table = CT_POWERBOOK_VGA;
- } else if (machine_is_compatible("PowerBook3,4") ||
- machine_is_compatible("PowerBook3,5")) {
+ } else if (of_machine_is_compatible("PowerBook3,4") ||
+ of_machine_is_compatible("PowerBook3,5")) {
/* powerbook with internal tmds */
rdev->mode_info.connector_table = CT_POWERBOOK_INTERNAL;
- } else if (machine_is_compatible("PowerBook5,1") ||
- machine_is_compatible("PowerBook5,2") ||
- machine_is_compatible("PowerBook5,3") ||
- machine_is_compatible("PowerBook5,4") ||
- machine_is_compatible("PowerBook5,5")) {
+ } else if (of_machine_is_compatible("PowerBook5,1") ||
+ of_machine_is_compatible("PowerBook5,2") ||
+ of_machine_is_compatible("PowerBook5,3") ||
+ of_machine_is_compatible("PowerBook5,4") ||
+ of_machine_is_compatible("PowerBook5,5")) {
/* powerbook with external single link tmds (sil164) */
rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL;
- } else if (machine_is_compatible("PowerBook5,6")) {
+ } else if (of_machine_is_compatible("PowerBook5,6")) {
/* powerbook with external dual or single link tmds */
rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL;
- } else if (machine_is_compatible("PowerBook5,7") ||
- machine_is_compatible("PowerBook5,8") ||
- machine_is_compatible("PowerBook5,9")) {
+ } else if (of_machine_is_compatible("PowerBook5,7") ||
+ of_machine_is_compatible("PowerBook5,8") ||
+ of_machine_is_compatible("PowerBook5,9")) {
/* PowerBook6,2 ? */
/* powerbook with external dual link tmds (sil1178?) */
rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL;
- } else if (machine_is_compatible("PowerBook4,1") ||
- machine_is_compatible("PowerBook4,2") ||
- machine_is_compatible("PowerBook4,3") ||
- machine_is_compatible("PowerBook6,3") ||
- machine_is_compatible("PowerBook6,5") ||
- machine_is_compatible("PowerBook6,7")) {
+ } else if (of_machine_is_compatible("PowerBook4,1") ||
+ of_machine_is_compatible("PowerBook4,2") ||
+ of_machine_is_compatible("PowerBook4,3") ||
+ of_machine_is_compatible("PowerBook6,3") ||
+ of_machine_is_compatible("PowerBook6,5") ||
+ of_machine_is_compatible("PowerBook6,7")) {
/* ibook */
rdev->mode_info.connector_table = CT_IBOOK;
- } else if (machine_is_compatible("PowerMac4,4")) {
+ } else if (of_machine_is_compatible("PowerMac4,4")) {
/* emac */
rdev->mode_info.connector_table = CT_EMAC;
- } else if (machine_is_compatible("PowerMac10,1")) {
+ } else if (of_machine_is_compatible("PowerMac10,1")) {
/* mini with internal tmds */
rdev->mode_info.connector_table = CT_MINI_INTERNAL;
- } else if (machine_is_compatible("PowerMac10,2")) {
+ } else if (of_machine_is_compatible("PowerMac10,2")) {
/* mini with external tmds */
rdev->mode_info.connector_table = CT_MINI_EXTERNAL;
- } else if (machine_is_compatible("PowerMac12,1")) {
+ } else if (of_machine_is_compatible("PowerMac12,1")) {
/* PowerMac8,1 ? */
/* imac g5 isight */
rdev->mode_info.connector_table = CT_IMAC_G5_ISIGHT;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 23818854001..65f81942f39 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -780,7 +780,7 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
* connected and the DVI port disconnected. If the edid doesn't
* say HDMI, vice versa.
*/
- if (radeon_connector->shared_ddc && connector_status_connected) {
+ if (radeon_connector->shared_ddc && (ret == connector_status_connected)) {
struct drm_device *dev = connector->dev;
struct drm_connector *list_connector;
struct radeon_connector *list_radeon_connector;
@@ -1060,8 +1060,7 @@ radeon_add_atom_connector(struct drm_device *dev,
return;
}
if (radeon_connector->ddc_bus && i2c_bus->valid) {
- if (memcmp(&radeon_connector->ddc_bus->rec, i2c_bus,
- sizeof(struct radeon_i2c_bus_rec)) == 0) {
+ if (radeon_connector->ddc_bus->rec.i2c_id == i2c_bus->i2c_id) {
radeon_connector->shared_ddc = true;
shared_ddc = true;
}
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 1190148cf5e..e9d085021c1 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -86,7 +86,7 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
&p->validated);
}
}
- return radeon_bo_list_validate(&p->validated, p->ib->fence);
+ return radeon_bo_list_validate(&p->validated);
}
int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
@@ -189,12 +189,10 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
{
unsigned i;
- if (error && parser->ib) {
- radeon_bo_list_unvalidate(&parser->validated,
- parser->ib->fence);
- } else {
- radeon_bo_list_unreserve(&parser->validated);
+ if (!error && parser->ib) {
+ radeon_bo_list_fence(&parser->validated, parser->ib->fence);
}
+ radeon_bo_list_unreserve(&parser->validated);
for (i = 0; i < parser->nrelocs; i++) {
if (parser->relocs[i].gobj) {
mutex_lock(&parser->rdev->ddev->struct_mutex);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index e13785282a8..c57ad606504 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -106,9 +106,10 @@
* 1.29- R500 3D cmd buffer support
* 1.30- Add support for occlusion queries
* 1.31- Add support for num Z pipes from GET_PARAM
+ * 1.32- fixes for rv740 setup
*/
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 31
+#define DRIVER_MINOR 32
#define DRIVER_PATCHLEVEL 0
enum radeon_cp_microcode_version {
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index d72a71bff21..f1da370928e 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -306,11 +306,10 @@ void radeon_bo_list_unreserve(struct list_head *head)
}
}
-int radeon_bo_list_validate(struct list_head *head, void *fence)
+int radeon_bo_list_validate(struct list_head *head)
{
struct radeon_bo_list *lobj;
struct radeon_bo *bo;
- struct radeon_fence *old_fence = NULL;
int r;
r = radeon_bo_list_reserve(head);
@@ -334,32 +333,27 @@ int radeon_bo_list_validate(struct list_head *head, void *fence)
}
lobj->gpu_offset = radeon_bo_gpu_offset(bo);
lobj->tiling_flags = bo->tiling_flags;
- if (fence) {
- old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
- bo->tbo.sync_obj = radeon_fence_ref(fence);
- bo->tbo.sync_obj_arg = NULL;
- }
- if (old_fence) {
- radeon_fence_unref(&old_fence);
- }
}
return 0;
}
-void radeon_bo_list_unvalidate(struct list_head *head, void *fence)
+void radeon_bo_list_fence(struct list_head *head, void *fence)
{
struct radeon_bo_list *lobj;
- struct radeon_fence *old_fence;
-
- if (fence)
- list_for_each_entry(lobj, head, list) {
- old_fence = to_radeon_fence(lobj->bo->tbo.sync_obj);
- if (old_fence == fence) {
- lobj->bo->tbo.sync_obj = NULL;
- radeon_fence_unref(&old_fence);
- }
+ struct radeon_bo *bo;
+ struct radeon_fence *old_fence = NULL;
+
+ list_for_each_entry(lobj, head, list) {
+ bo = lobj->bo;
+ spin_lock(&bo->tbo.lock);
+ old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
+ bo->tbo.sync_obj = radeon_fence_ref(fence);
+ bo->tbo.sync_obj_arg = NULL;
+ spin_unlock(&bo->tbo.lock);
+ if (old_fence) {
+ radeon_fence_unref(&old_fence);
}
- radeon_bo_list_unreserve(head);
+ }
}
int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index a02f18011ad..7ab43de1e24 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -156,8 +156,8 @@ extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
struct list_head *head);
extern int radeon_bo_list_reserve(struct list_head *head);
extern void radeon_bo_list_unreserve(struct list_head *head);
-extern int radeon_bo_list_validate(struct list_head *head, void *fence);
-extern void radeon_bo_list_unvalidate(struct list_head *head, void *fence);
+extern int radeon_bo_list_validate(struct list_head *head);
+extern void radeon_bo_list_fence(struct list_head *head, void *fence);
extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
struct vm_area_struct *vma);
extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 4d12b2d17b4..6579eb4c1f2 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -41,68 +41,55 @@ int radeon_ib_get(struct radeon_device *rdev, struct radeon_ib **ib)
{
struct radeon_fence *fence;
struct radeon_ib *nib;
- unsigned long i;
- int r = 0;
+ int r = 0, i, c;
*ib = NULL;
r = radeon_fence_create(rdev, &fence);
if (r) {
- DRM_ERROR("failed to create fence for new IB\n");
+ dev_err(rdev->dev, "failed to create fence for new IB\n");
return r;
}
mutex_lock(&rdev->ib_pool.mutex);
- i = find_first_zero_bit(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
- if (i < RADEON_IB_POOL_SIZE) {
- set_bit(i, rdev->ib_pool.alloc_bm);
- rdev->ib_pool.ibs[i].length_dw = 0;
- *ib = &rdev->ib_pool.ibs[i];
- mutex_unlock(&rdev->ib_pool.mutex);
- goto out;
+ for (i = rdev->ib_pool.head_id, c = 0, nib = NULL; c < RADEON_IB_POOL_SIZE; c++, i++) {
+ i &= (RADEON_IB_POOL_SIZE - 1);
+ if (rdev->ib_pool.ibs[i].free) {
+ nib = &rdev->ib_pool.ibs[i];
+ break;
+ }
}
- if (list_empty(&rdev->ib_pool.scheduled_ibs)) {
- /* we go do nothings here */
+ if (nib == NULL) {
+ /* This should never happen, it means we allocated all
+ * IB and haven't scheduled one yet, return EBUSY to
+ * userspace hoping that on ioctl recall we get better
+ * luck
+ */
+ dev_err(rdev->dev, "no free indirect buffer !\n");
mutex_unlock(&rdev->ib_pool.mutex);
- DRM_ERROR("all IB allocated none scheduled.\n");
- r = -EINVAL;
- goto out;
+ radeon_fence_unref(&fence);
+ return -EBUSY;
}
- /* get the first ib on the scheduled list */
- nib = list_entry(rdev->ib_pool.scheduled_ibs.next,
- struct radeon_ib, list);
- if (nib->fence == NULL) {
- /* we go do nothings here */
+ rdev->ib_pool.head_id = (nib->idx + 1) & (RADEON_IB_POOL_SIZE - 1);
+ nib->free = false;
+ if (nib->fence) {
mutex_unlock(&rdev->ib_pool.mutex);
- DRM_ERROR("IB %lu scheduled without a fence.\n", nib->idx);
- r = -EINVAL;
- goto out;
- }
- mutex_unlock(&rdev->ib_pool.mutex);
-
- r = radeon_fence_wait(nib->fence, false);
- if (r) {
- DRM_ERROR("radeon: IB(%lu:0x%016lX:%u)\n", nib->idx,
- (unsigned long)nib->gpu_addr, nib->length_dw);
- DRM_ERROR("radeon: GPU lockup detected, fail to get a IB\n");
- goto out;
+ r = radeon_fence_wait(nib->fence, false);
+ if (r) {
+ dev_err(rdev->dev, "error waiting fence of IB(%u:0x%016lX:%u)\n",
+ nib->idx, (unsigned long)nib->gpu_addr, nib->length_dw);
+ mutex_lock(&rdev->ib_pool.mutex);
+ nib->free = true;
+ mutex_unlock(&rdev->ib_pool.mutex);
+ radeon_fence_unref(&fence);
+ return r;
+ }
+ mutex_lock(&rdev->ib_pool.mutex);
}
radeon_fence_unref(&nib->fence);
-
+ nib->fence = fence;
nib->length_dw = 0;
-
- /* scheduled list is accessed here */
- mutex_lock(&rdev->ib_pool.mutex);
- list_del(&nib->list);
- INIT_LIST_HEAD(&nib->list);
mutex_unlock(&rdev->ib_pool.mutex);
-
*ib = nib;
-out:
- if (r) {
- radeon_fence_unref(&fence);
- } else {
- (*ib)->fence = fence;
- }
- return r;
+ return 0;
}
void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
@@ -113,19 +100,10 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
if (tmp == NULL) {
return;
}
- mutex_lock(&rdev->ib_pool.mutex);
- if (!list_empty(&tmp->list) && !radeon_fence_signaled(tmp->fence)) {
- /* IB is scheduled & not signaled don't do anythings */
- mutex_unlock(&rdev->ib_pool.mutex);
- return;
- }
- list_del(&tmp->list);
- INIT_LIST_HEAD(&tmp->list);
- if (tmp->fence)
+ if (!tmp->fence->emited)
radeon_fence_unref(&tmp->fence);
-
- tmp->length_dw = 0;
- clear_bit(tmp->idx, rdev->ib_pool.alloc_bm);
+ mutex_lock(&rdev->ib_pool.mutex);
+ tmp->free = true;
mutex_unlock(&rdev->ib_pool.mutex);
}
@@ -135,7 +113,7 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib)
if (!ib->length_dw || !rdev->cp.ready) {
/* TODO: Nothings in the ib we should report. */
- DRM_ERROR("radeon: couldn't schedule IB(%lu).\n", ib->idx);
+ DRM_ERROR("radeon: couldn't schedule IB(%u).\n", ib->idx);
return -EINVAL;
}
@@ -148,7 +126,8 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib)
radeon_ring_ib_execute(rdev, ib);
radeon_fence_emit(rdev, ib->fence);
mutex_lock(&rdev->ib_pool.mutex);
- list_add_tail(&ib->list, &rdev->ib_pool.scheduled_ibs);
+ /* once scheduled IB is considered free and protected by the fence */
+ ib->free = true;
mutex_unlock(&rdev->ib_pool.mutex);
radeon_ring_unlock_commit(rdev);
return 0;
@@ -164,7 +143,6 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
if (rdev->ib_pool.robj)
return 0;
/* Allocate 1M object buffer */
- INIT_LIST_HEAD(&rdev->ib_pool.scheduled_ibs);
r = radeon_bo_create(rdev, NULL, RADEON_IB_POOL_SIZE*64*1024,
true, RADEON_GEM_DOMAIN_GTT,
&rdev->ib_pool.robj);
@@ -195,9 +173,9 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
rdev->ib_pool.ibs[i].ptr = ptr + offset;
rdev->ib_pool.ibs[i].idx = i;
rdev->ib_pool.ibs[i].length_dw = 0;
- INIT_LIST_HEAD(&rdev->ib_pool.ibs[i].list);
+ rdev->ib_pool.ibs[i].free = true;
}
- bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
+ rdev->ib_pool.head_id = 0;
rdev->ib_pool.ready = true;
DRM_INFO("radeon: ib pool ready.\n");
if (radeon_debugfs_ib_init(rdev)) {
@@ -214,7 +192,6 @@ void radeon_ib_pool_fini(struct radeon_device *rdev)
return;
}
mutex_lock(&rdev->ib_pool.mutex);
- bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
if (rdev->ib_pool.robj) {
r = radeon_bo_reserve(rdev->ib_pool.robj, false);
if (likely(r == 0)) {
@@ -363,7 +340,7 @@ static int radeon_debugfs_ib_info(struct seq_file *m, void *data)
if (ib == NULL) {
return 0;
}
- seq_printf(m, "IB %04lu\n", ib->idx);
+ seq_printf(m, "IB %04u\n", ib->idx);
seq_printf(m, "IB fence %p\n", ib->fence);
seq_printf(m, "IB size %05u dwords\n", ib->length_dw);
for (i = 0; i < ib->length_dw; i++) {
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 5943d561fd1..03021674d09 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -549,9 +549,12 @@ static void rv770_gpu_init(struct radeon_device *rdev)
gb_tiling_config |= BANK_SWAPS(1);
- backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes,
- rdev->config.rv770.max_backends,
- (0xff << rdev->config.rv770.max_backends) & 0xff);
+ if (rdev->family == CHIP_RV740)
+ backend_map = 0x28;
+ else
+ backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes,
+ rdev->config.rv770.max_backends,
+ (0xff << rdev->config.rv770.max_backends) & 0xff);
gb_tiling_config |= BACKEND_MAP(backend_map);
cc_gc_shader_pipe_config =
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 1a3e909b7bb..c7320ce4567 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1020,6 +1020,12 @@ static int ttm_bo_mem_compat(struct ttm_placement *placement,
struct ttm_mem_reg *mem)
{
int i;
+ struct drm_mm_node *node = mem->mm_node;
+
+ if (node && placement->lpfn != 0 &&
+ (node->start < placement->fpfn ||
+ node->start + node->size > placement->lpfn))
+ return -1;
for (i = 0; i < placement->num_placement; i++) {
if ((placement->placement[i] & mem->placement &
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index e2123af7775..3d47a2c1232 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -196,14 +196,15 @@ EXPORT_SYMBOL(ttm_tt_populate);
#ifdef CONFIG_X86
static inline int ttm_tt_set_page_caching(struct page *p,
- enum ttm_caching_state c_state)
+ enum ttm_caching_state c_old,
+ enum ttm_caching_state c_new)
{
int ret = 0;
if (PageHighMem(p))
return 0;
- if (get_page_memtype(p) != -1) {
+ if (c_old != tt_cached) {
/* p isn't in the default caching state, set it to
* writeback first to free its current memtype. */
@@ -212,16 +213,17 @@ static inline int ttm_tt_set_page_caching(struct page *p,
return ret;
}
- if (c_state == tt_wc)
+ if (c_new == tt_wc)
ret = set_memory_wc((unsigned long) page_address(p), 1);
- else if (c_state == tt_uncached)
+ else if (c_new == tt_uncached)
ret = set_pages_uc(p, 1);
return ret;
}
#else /* CONFIG_X86 */
static inline int ttm_tt_set_page_caching(struct page *p,
- enum ttm_caching_state c_state)
+ enum ttm_caching_state c_old,
+ enum ttm_caching_state c_new)
{
return 0;
}
@@ -254,7 +256,9 @@ static int ttm_tt_set_caching(struct ttm_tt *ttm,
for (i = 0; i < ttm->num_pages; ++i) {
cur_page = ttm->pages[i];
if (likely(cur_page != NULL)) {
- ret = ttm_tt_set_page_caching(cur_page, c_state);
+ ret = ttm_tt_set_page_caching(cur_page,
+ ttm->caching_state,
+ c_state);
if (unlikely(ret != 0))
goto out_err;
}
@@ -268,7 +272,7 @@ out_err:
for (j = 0; j < i; ++j) {
cur_page = ttm->pages[j];
if (likely(cur_page != NULL)) {
- (void)ttm_tt_set_page_caching(cur_page,
+ (void)ttm_tt_set_page_caching(cur_page, c_state,
ttm->caching_state);
}
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index a6e8f687fa6..0c9c0811f42 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -348,22 +348,19 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
*/
DRM_INFO("It appears like vesafb is loaded. "
- "Ignore above error if any. Entering stealth mode.\n");
+ "Ignore above error if any.\n");
ret = pci_request_region(dev->pdev, 2, "vmwgfx stealth probe");
if (unlikely(ret != 0)) {
DRM_ERROR("Failed reserving the SVGA MMIO resource.\n");
goto out_no_device;
}
- vmw_kms_init(dev_priv);
- vmw_overlay_init(dev_priv);
- } else {
- ret = vmw_request_device(dev_priv);
- if (unlikely(ret != 0))
- goto out_no_device;
- vmw_kms_init(dev_priv);
- vmw_overlay_init(dev_priv);
- vmw_fb_init(dev_priv);
}
+ ret = vmw_request_device(dev_priv);
+ if (unlikely(ret != 0))
+ goto out_no_device;
+ vmw_kms_init(dev_priv);
+ vmw_overlay_init(dev_priv);
+ vmw_fb_init(dev_priv);
dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier;
register_pm_notifier(&dev_priv->pm_nb);
@@ -406,17 +403,15 @@ static int vmw_driver_unload(struct drm_device *dev)
unregister_pm_notifier(&dev_priv->pm_nb);
- if (!dev_priv->stealth) {
- vmw_fb_close(dev_priv);
- vmw_kms_close(dev_priv);
- vmw_overlay_close(dev_priv);
- vmw_release_device(dev_priv);
- pci_release_regions(dev->pdev);
- } else {
- vmw_kms_close(dev_priv);
- vmw_overlay_close(dev_priv);
+ vmw_fb_close(dev_priv);
+ vmw_kms_close(dev_priv);
+ vmw_overlay_close(dev_priv);
+ vmw_release_device(dev_priv);
+ if (dev_priv->stealth)
pci_release_region(dev->pdev, 2);
- }
+ else
+ pci_release_regions(dev->pdev);
+
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
drm_irq_uninstall(dev_priv->dev);
if (dev->devname == vmw_devname)
@@ -585,11 +580,6 @@ static int vmw_master_set(struct drm_device *dev,
int ret = 0;
DRM_INFO("Master set.\n");
- if (dev_priv->stealth) {
- ret = vmw_request_device(dev_priv);
- if (unlikely(ret != 0))
- return ret;
- }
if (active) {
BUG_ON(active != &dev_priv->fbdev_master);
@@ -649,18 +639,11 @@ static void vmw_master_drop(struct drm_device *dev,
ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
- if (dev_priv->stealth) {
- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
- if (unlikely(ret != 0))
- DRM_ERROR("Unable to clean VRAM on master drop.\n");
- vmw_release_device(dev_priv);
- }
dev_priv->active_master = &dev_priv->fbdev_master;
ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
ttm_vt_unlock(&dev_priv->fbdev_master.lock);
- if (!dev_priv->stealth)
- vmw_fb_on(dev_priv);
+ vmw_fb_on(dev_priv);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index d69caf92ffe..0897359b3e4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -182,25 +182,19 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv,
return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid);
}
-static int vmw_cmd_dma(struct vmw_private *dev_priv,
- struct vmw_sw_context *sw_context,
- SVGA3dCmdHeader *header)
+static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ SVGAGuestPtr *ptr,
+ struct vmw_dma_buffer **vmw_bo_p)
{
- uint32_t handle;
struct vmw_dma_buffer *vmw_bo = NULL;
struct ttm_buffer_object *bo;
- struct vmw_surface *srf = NULL;
- struct vmw_dma_cmd {
- SVGA3dCmdHeader header;
- SVGA3dCmdSurfaceDMA dma;
- } *cmd;
+ uint32_t handle = ptr->gmrId;
struct vmw_relocation *reloc;
- int ret;
uint32_t cur_validate_node;
struct ttm_validate_buffer *val_buf;
+ int ret;
- cmd = container_of(header, struct vmw_dma_cmd, header);
- handle = cmd->dma.guest.ptr.gmrId;
ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use GMR region.\n");
@@ -209,14 +203,14 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
bo = &vmw_bo->base;
if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) {
- DRM_ERROR("Max number of DMA commands per submission"
+ DRM_ERROR("Max number relocations per submission"
" exceeded\n");
ret = -EINVAL;
goto out_no_reloc;
}
reloc = &sw_context->relocs[sw_context->cur_reloc++];
- reloc->location = &cmd->dma.guest.ptr;
+ reloc->location = ptr;
cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) {
@@ -234,7 +228,89 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
list_add_tail(&val_buf->head, &sw_context->validate_nodes);
++sw_context->cur_val_buf;
}
+ *vmw_bo_p = vmw_bo;
+ return 0;
+
+out_no_reloc:
+ vmw_dmabuf_unreference(&vmw_bo);
+ vmw_bo_p = NULL;
+ return ret;
+}
+
+static int vmw_cmd_end_query(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ SVGA3dCmdHeader *header)
+{
+ struct vmw_dma_buffer *vmw_bo;
+ struct vmw_query_cmd {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdEndQuery q;
+ } *cmd;
+ int ret;
+
+ cmd = container_of(header, struct vmw_query_cmd, header);
+ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+ &cmd->q.guestResult,
+ &vmw_bo);
+ if (unlikely(ret != 0))
+ return ret;
+
+ vmw_dmabuf_unreference(&vmw_bo);
+ return 0;
+}
+static int vmw_cmd_wait_query(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ SVGA3dCmdHeader *header)
+{
+ struct vmw_dma_buffer *vmw_bo;
+ struct vmw_query_cmd {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdWaitForQuery q;
+ } *cmd;
+ int ret;
+
+ cmd = container_of(header, struct vmw_query_cmd, header);
+ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+ &cmd->q.guestResult,
+ &vmw_bo);
+ if (unlikely(ret != 0))
+ return ret;
+
+ vmw_dmabuf_unreference(&vmw_bo);
+ return 0;
+}
+
+
+static int vmw_cmd_dma(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ SVGA3dCmdHeader *header)
+{
+ struct vmw_dma_buffer *vmw_bo = NULL;
+ struct ttm_buffer_object *bo;
+ struct vmw_surface *srf = NULL;
+ struct vmw_dma_cmd {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdSurfaceDMA dma;
+ } *cmd;
+ int ret;
+
+ cmd = container_of(header, struct vmw_dma_cmd, header);
+ ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+ &cmd->dma.guest.ptr,
+ &vmw_bo);
+ if (unlikely(ret != 0))
+ return ret;
+
+ bo = &vmw_bo->base;
ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile,
cmd->dma.host.sid, &srf);
if (ret) {
@@ -379,8 +455,8 @@ static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = {
VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw),
VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check),
VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check),
- VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_cid_check),
- VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_cid_check),
+ VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_end_query),
+ VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_wait_query),
VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok),
VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN,
&vmw_cmd_blt_surf_screen_check)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 4f4f6432be8..a93367041cd 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -559,6 +559,9 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
info->pixmap.scan_align = 1;
#endif
+ info->aperture_base = vmw_priv->vram_start;
+ info->aperture_size = vmw_priv->vram_size;
+
/*
* Dirty & Deferred IO
*/
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig
index 790e675b13e..0920492cea0 100644
--- a/drivers/gpu/vga/Kconfig
+++ b/drivers/gpu/vga/Kconfig
@@ -8,3 +8,11 @@ config VGA_ARB
are accessed at same time they need some kind of coordination. Please
see Documentation/vgaarbiter.txt for more details. Select this to
enable VGA arbiter.
+
+config VGA_ARB_MAX_GPUS
+ int "Maximum number of GPUs"
+ default 16
+ depends on VGA_ARB
+ help
+ Reserves space in the kernel to maintain resource locking for
+ multiple GPUS. The overhead for each GPU is very small.
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index 24b56dc5459..8827814d073 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -688,7 +688,7 @@ EXPORT_SYMBOL(vga_client_register);
* the arbiter.
*/
-#define MAX_USER_CARDS 16
+#define MAX_USER_CARDS CONFIG_VGA_ARB_MAX_GPUS
#define PCI_INVALID_CARD ((struct pci_dev *)-1UL)
/*
@@ -954,6 +954,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
}
} else if (strncmp(curr_pos, "target ", 7) == 0) {
+ struct pci_bus *pbus;
unsigned int domain, bus, devfn;
struct vga_device *vgadev;
@@ -961,7 +962,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
remaining -= 7;
pr_devel("client 0x%p called 'target'\n", priv);
/* if target is default */
- if (!strncmp(kbuf, "default", 7))
+ if (!strncmp(curr_pos, "default", 7))
pdev = pci_dev_get(vga_default_device());
else {
if (!vga_pci_str_to_vars(curr_pos, remaining,
@@ -969,18 +970,31 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
ret_val = -EPROTO;
goto done;
}
-
- pdev = pci_get_bus_and_slot(bus, devfn);
+ pr_devel("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos,
+ domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+ pbus = pci_find_bus(domain, bus);
+ pr_devel("vgaarb: pbus %p\n", pbus);
+ if (pbus == NULL) {
+ pr_err("vgaarb: invalid PCI domain and/or bus address %x:%x\n",
+ domain, bus);
+ ret_val = -ENODEV;
+ goto done;
+ }
+ pdev = pci_get_slot(pbus, devfn);
+ pr_devel("vgaarb: pdev %p\n", pdev);
if (!pdev) {
- pr_info("vgaarb: invalid PCI address!\n");
+ pr_err("vgaarb: invalid PCI address %x:%x\n",
+ bus, devfn);
ret_val = -ENODEV;
goto done;
}
}
vgadev = vgadev_find(pdev);
+ pr_devel("vgaarb: vgadev %p\n", vgadev);
if (vgadev == NULL) {
- pr_info("vgaarb: this pci device is not a vga device\n");
+ pr_err("vgaarb: this pci device is not a vga device\n");
pci_dev_put(pdev);
ret_val = -ENODEV;
goto done;
@@ -998,7 +1012,8 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
}
}
if (i == MAX_USER_CARDS) {
- pr_err("vgaarb: maximum user cards number reached!\n");
+ pr_err("vgaarb: maximum user cards (%d) number reached!\n",
+ MAX_USER_CARDS);
pci_dev_put(pdev);
/* XXX: which value to return? */
ret_val = -ENOMEM;
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 24d90ea246c..71d4c070362 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -55,6 +55,12 @@ source "drivers/hid/usbhid/Kconfig"
menu "Special HID drivers"
depends on HID
+config HID_3M_PCT
+ tristate "3M PCT"
+ depends on USB_HID
+ ---help---
+ Support for 3M PCT touch screens.
+
config HID_A4TECH
tristate "A4 tech" if EMBEDDED
depends on USB_HID
@@ -183,6 +189,23 @@ config LOGIRUMBLEPAD2_FF
Say Y here if you want to enable force feedback support for Logitech
Rumblepad 2 devices.
+config LOGIG940_FF
+ bool "Logitech Flight System G940 force feedback support"
+ depends on HID_LOGITECH
+ select INPUT_FF_MEMLESS
+ help
+ Say Y here if you want to enable force feedback support for Logitech
+ Flight System G940 devices.
+
+config HID_MAGICMOUSE
+ tristate "Apple MagicMouse multi-touch support"
+ depends on BT_HIDP
+ ---help---
+ Support for the Apple Magic Mouse multi-touch.
+
+ Say Y here if you want support for the multi-touch features of the
+ Apple Wireless "Magic" Mouse.
+
config HID_MICROSOFT
tristate "Microsoft" if EMBEDDED
depends on USB_HID
@@ -190,6 +213,12 @@ config HID_MICROSOFT
---help---
Support for Microsoft devices that are not fully compliant with HID standard.
+config HID_MOSART
+ tristate "MosArt"
+ depends on USB_HID
+ ---help---
+ Support for MosArt dual-touch panels.
+
config HID_MONTEREY
tristate "Monterey" if EMBEDDED
depends on USB_HID
@@ -198,12 +227,18 @@ config HID_MONTEREY
Support for Monterey Genius KB29E.
config HID_NTRIG
- tristate "NTrig" if EMBEDDED
+ tristate "NTrig"
depends on USB_HID
- default !EMBEDDED
---help---
Support for N-Trig touch screen.
+config HID_ORTEK
+ tristate "Ortek" if EMBEDDED
+ depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
+
config HID_PANTHERLORD
tristate "Pantherlord support" if EMBEDDED
depends on USB_HID
@@ -227,6 +262,12 @@ config HID_PETALYNX
---help---
Support for Petalynx Maxter remote control.
+config HID_QUANTA
+ tristate "Quanta Optical Touch"
+ depends on USB_HID
+ ---help---
+ Support for Quanta Optical Touch dual-touch panels.
+
config HID_SAMSUNG
tristate "Samsung" if EMBEDDED
depends on USB_HID
@@ -241,6 +282,12 @@ config HID_SONY
---help---
Support for Sony PS3 controller.
+config HID_STANTUM
+ tristate "Stantum"
+ depends on USB_HID
+ ---help---
+ Support for Stantum multitouch panel.
+
config HID_SUNPLUS
tristate "Sunplus" if EMBEDDED
depends on USB_HID
@@ -305,9 +352,8 @@ config THRUSTMASTER_FF
Rumble Force or Force Feedback Wheel.
config HID_WACOM
- tristate "Wacom Bluetooth devices support" if EMBEDDED
+ tristate "Wacom Bluetooth devices support"
depends on BT_HIDP
- default !EMBEDDED
---help---
Support for Wacom Graphire Bluetooth tablet.
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 0de2dff5542..0b2618f092c 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -18,7 +18,11 @@ endif
ifdef CONFIG_LOGIRUMBLEPAD2_FF
hid-logitech-objs += hid-lg2ff.o
endif
+ifdef CONFIG_LOGIG940_FF
+ hid-logitech-objs += hid-lg3ff.o
+endif
+obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
@@ -31,14 +35,19 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
+obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
+obj-$(CONFIG_HID_MOSART) += hid-mosart.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
+obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
+obj-$(CONFIG_HID_QUANTA) += hid-quanta.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
obj-$(CONFIG_HID_SONY) += hid-sony.o
+obj-$(CONFIG_HID_STANTUM) += hid-stantum.o
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c
new file mode 100644
index 00000000000..2370aefc86b
--- /dev/null
+++ b/drivers/hid/hid-3m-pct.c
@@ -0,0 +1,290 @@
+/*
+ * HID driver for 3M PCT multitouch panels
+ *
+ * Copyright (c) 2009 Stephane Chatty <chatty@enac.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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("3M PCT multitouch panels");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+struct mmm_finger {
+ __s32 x, y;
+ __u8 rank;
+ bool touch, valid;
+};
+
+struct mmm_data {
+ struct mmm_finger f[10];
+ __u8 curid, num;
+ bool touch, valid;
+};
+
+static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_BUTTON:
+ return -1;
+
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_X);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_X,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_Y);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_Y,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ /* we do not want to map these: no input-oriented meaning */
+ case 0x14:
+ case 0x23:
+ case HID_DG_INPUTMODE:
+ case HID_DG_DEVICEINDEX:
+ case HID_DG_CONTACTCOUNT:
+ case HID_DG_CONTACTMAX:
+ case HID_DG_INRANGE:
+ case HID_DG_CONFIDENCE:
+ return -1;
+ case HID_DG_TIPSWITCH:
+ /* touchscreen emulation */
+ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+ return 1;
+ case HID_DG_CONTACTID:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TRACKING_ID);
+ return 1;
+ }
+ /* let hid-input decide for the others */
+ return 0;
+
+ case 0xff000000:
+ /* we do not want to map these: no input-oriented meaning */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY || usage->type == EV_ABS)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+
+/*
+ * this function is called when a whole packet has been received and processed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
+{
+ struct mmm_finger *oldest = 0;
+ bool pressed = false, released = false;
+ int i;
+
+ /*
+ * we need to iterate on all fingers to decide if we have a press
+ * or a release event in our touchscreen emulation.
+ */
+ for (i = 0; i < 10; ++i) {
+ struct mmm_finger *f = &md->f[i];
+ if (!f->valid) {
+ /* this finger is just placeholder data, ignore */
+ } else if (f->touch) {
+ /* this finger is on the screen */
+ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
+ input_mt_sync(input);
+ /*
+ * touchscreen emulation: maintain the age rank
+ * of this finger, decide if we have a press
+ */
+ if (f->rank == 0) {
+ f->rank = ++(md->num);
+ if (f->rank == 1)
+ pressed = true;
+ }
+ if (f->rank == 1)
+ oldest = f;
+ } else {
+ /* this finger took off the screen */
+ /* touchscreen emulation: maintain age rank of others */
+ int j;
+
+ for (j = 0; j < 10; ++j) {
+ struct mmm_finger *g = &md->f[j];
+ if (g->rank > f->rank) {
+ g->rank--;
+ if (g->rank == 1)
+ oldest = g;
+ }
+ }
+ f->rank = 0;
+ --(md->num);
+ if (md->num == 0)
+ released = true;
+ }
+ f->valid = 0;
+ }
+
+ /* touchscreen emulation */
+ if (oldest) {
+ if (pressed)
+ input_event(input, EV_KEY, BTN_TOUCH, 1);
+ input_event(input, EV_ABS, ABS_X, oldest->x);
+ input_event(input, EV_ABS, ABS_Y, oldest->y);
+ } else if (released) {
+ input_event(input, EV_KEY, BTN_TOUCH, 0);
+ }
+}
+
+/*
+ * this function is called upon all reports
+ * so that we can accumulate contact point information,
+ * and call input_mt_sync after each point.
+ */
+static int mmm_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct mmm_data *md = hid_get_drvdata(hid);
+ /*
+ * strangely, this function can be called before
+ * field->hidinput is initialized!
+ */
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ struct input_dev *input = field->hidinput->input;
+ switch (usage->hid) {
+ case HID_DG_TIPSWITCH:
+ md->touch = value;
+ break;
+ case HID_DG_CONFIDENCE:
+ md->valid = value;
+ break;
+ case HID_DG_CONTACTID:
+ if (md->valid) {
+ md->curid = value;
+ md->f[value].touch = md->touch;
+ md->f[value].valid = 1;
+ }
+ break;
+ case HID_GD_X:
+ if (md->valid)
+ md->f[md->curid].x = value;
+ break;
+ case HID_GD_Y:
+ if (md->valid)
+ md->f[md->curid].y = value;
+ break;
+ case HID_DG_CONTACTCOUNT:
+ mmm_filter_event(md, input);
+ break;
+ }
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct mmm_data *md;
+
+ md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
+ if (!md) {
+ dev_err(&hdev->dev, "cannot allocate 3M data\n");
+ return -ENOMEM;
+ }
+ hid_set_drvdata(hdev, md);
+
+ ret = hid_parse(hdev);
+ if (!ret)
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ if (ret)
+ kfree(md);
+ return ret;
+}
+
+static void mmm_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id mmm_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, mmm_devices);
+
+static const struct hid_usage_id mmm_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver mmm_driver = {
+ .name = "3m-pct",
+ .id_table = mmm_devices,
+ .probe = mmm_probe,
+ .remove = mmm_remove,
+ .input_mapping = mmm_input_mapping,
+ .input_mapped = mmm_input_mapped,
+ .usage_table = mmm_grabbed_usages,
+ .event = mmm_event,
+};
+
+static int __init mmm_init(void)
+{
+ return hid_register_driver(&mmm_driver);
+}
+
+static void __exit mmm_exit(void)
+{
+ hid_unregister_driver(&mmm_driver);
+}
+
+module_init(mmm_init);
+module_exit(mmm_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 5b4d66dc1a0..78286b184ac 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -40,6 +40,11 @@ module_param(fnmode, uint, 0644);
MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
"[1] = fkeyslast, 2 = fkeysfirst)");
+static unsigned int iso_layout = 1;
+module_param(iso_layout, uint, 0644);
+MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. "
+ "(0 = disabled, [1] = enabled)");
+
struct apple_sc {
unsigned long quirks;
unsigned int fn_on;
@@ -199,11 +204,13 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
}
}
- if (asc->quirks & APPLE_ISO_KEYBOARD) {
- trans = apple_find_translation(apple_iso_keyboard, usage->code);
- if (trans) {
- input_event(input, usage->type, trans->to, value);
- return 1;
+ if (iso_layout) {
+ if (asc->quirks & APPLE_ISO_KEYBOARD) {
+ trans = apple_find_translation(apple_iso_keyboard, usage->code);
+ if (trans) {
+ input_event(input, usage->type, trans->to, value);
+ return 1;
+ }
}
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index eabe5f87c6c..368fbb0c4ca 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -4,7 +4,7 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2006-2007 Jiri Kosina
+ * Copyright (c) 2006-2010 Jiri Kosina
*/
/*
@@ -51,7 +51,7 @@ EXPORT_SYMBOL_GPL(hid_debug);
* Register a new report for a device.
*/
-static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
+struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
{
struct hid_report_enum *report_enum = device->report_enum + type;
struct hid_report *report;
@@ -75,6 +75,7 @@ static struct hid_report *hid_register_report(struct hid_device *device, unsigne
return report;
}
+EXPORT_SYMBOL_GPL(hid_register_report);
/*
* Register a new field for this report.
@@ -387,7 +388,8 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
__u32 data;
unsigned n;
- if (item->size == 0) {
+ /* Local delimiter could have value 0, which allows size to be 0 */
+ if (item->size == 0 && item->tag != HID_LOCAL_ITEM_TAG_DELIMITER) {
dbg_hid("item data expected for local item\n");
return -1;
}
@@ -1248,11 +1250,13 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
/* a list of devices for which there is a specialized driver on HID bus */
static const struct hid_device_id hid_blacklist[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
@@ -1324,6 +1328,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
{ 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) },
@@ -1337,10 +1342,15 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
@@ -1543,8 +1553,9 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
- { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM2)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
{ HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },
@@ -1661,8 +1672,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) },
- { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 6abd0369aed..cd4ece6fdfb 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -864,13 +864,13 @@ static const char **names[EV_MAX + 1] = {
[EV_SND] = sounds, [EV_REP] = repeats,
};
-void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) {
-
+static void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f)
+{
seq_printf(f, "%s.%s", events[type] ? events[type] : "?",
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
}
-void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
+static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
{
int i, j, k;
struct hid_report *report;
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 010368e649e..72c05f90553 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -18,6 +18,9 @@
#ifndef HID_IDS_H_FILE
#define HID_IDS_H_FILE
+#define USB_VENDOR_ID_3M 0x0596
+#define USB_DEVICE_ID_3M1968 0x0500
+
#define USB_VENDOR_ID_A4TECH 0x09da
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a
@@ -56,6 +59,7 @@
#define USB_VENDOR_ID_APPLE 0x05ac
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
+#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
@@ -96,9 +100,12 @@
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
-#define USB_VENDOR_ID_ASUS 0x0b05
-#define USB_DEVICE_ID_ASUS_LCM 0x1726
-#define USB_DEVICE_ID_ASUS_LCM2 0x175b
+#define USB_VENDOR_ID_ASUS 0x0486
+#define USB_DEVICE_ID_ASUS_T91MT 0x0185
+
+#define USB_VENDOR_ID_ASUSTEK 0x0b05
+#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
+#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
#define USB_VENDOR_ID_ATEN 0x0557
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
@@ -169,6 +176,9 @@
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9
+#define USB_DEVICE_ID_ETURBOTOUCH 0x0006
+
#define USB_VENDOR_ID_ETT 0x0664
#define USB_DEVICE_ID_TC5UH 0x0309
@@ -303,6 +313,7 @@
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
+#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
@@ -365,6 +376,9 @@
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
+#define USB_VENDOR_ID_ORTEK 0x05a4
+#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000
+
#define USB_VENDOR_ID_PANJIT 0x134c
#define USB_VENDOR_ID_PANTHERLORD 0x0810
@@ -382,9 +396,16 @@
#define USB_VENDOR_ID_POWERCOM 0x0d9f
#define USB_DEVICE_ID_POWERCOM_UPS 0x0002
+#define USB_VENDOR_ID_PRODIGE 0x05af
+#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
+
#define USB_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
+#define USB_VENDOR_ID_QUANTA 0x0408
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
+#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
+
#define USB_VENDOR_ID_SAMSUNG 0x0419
#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
@@ -396,18 +417,20 @@
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
+#define USB_VENDOR_ID_STANTUM 0x1f87
+#define USB_DEVICE_ID_MTP 0x0002
+
#define USB_VENDOR_ID_SUN 0x0430
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
#define USB_VENDOR_ID_SUNPLUS 0x04fc
#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
-#define USB_VENDOR_ID_TENX 0x1130
-#define USB_DEVICE_ID_TENX_IBUDDY1 0x0001
-#define USB_DEVICE_ID_TENX_IBUDDY2 0x0002
-
#define USB_VENDOR_ID_THRUSTMASTER 0x044f
+#define USB_VENDOR_ID_TOUCHPACK 0x1bfd
+#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688
+
#define USB_VENDOR_ID_TOPMAX 0x0663
#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 5862b0f3b55..79d9edd0bdf 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000-2001 Vojtech Pavlik
- * Copyright (c) 2006-2007 Jiri Kosina
+ * Copyright (c) 2006-2010 Jiri Kosina
*
* HID to Linux Input mapping
*/
@@ -193,12 +193,17 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
case HID_UP_BUTTON:
- code = ((usage->hid - 1) & 0xf);
+ code = ((usage->hid - 1) & HID_USAGE);
switch (field->application) {
case HID_GD_MOUSE:
case HID_GD_POINTER: code += 0x110; break;
- case HID_GD_JOYSTICK: code += 0x120; break;
+ case HID_GD_JOYSTICK:
+ if (code <= 0xf)
+ code += BTN_JOYSTICK;
+ else
+ code += BTN_TRIGGER_HAPPY;
+ break;
case HID_GD_GAMEPAD: code += 0x130; break;
default:
switch (field->physical) {
@@ -400,6 +405,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x192: map_key_clear(KEY_CALC); break;
case 0x194: map_key_clear(KEY_FILE); break;
case 0x196: map_key_clear(KEY_WWW); break;
+ case 0x199: map_key_clear(KEY_CHAT); break;
case 0x19c: map_key_clear(KEY_LOGOFF); break;
case 0x19e: map_key_clear(KEY_COFFEE); break;
case 0x1a6: map_key_clear(KEY_HELP); break;
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 9fcd3d017ab..3677c9037a1 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -34,6 +34,7 @@
#define LG_FF 0x200
#define LG_FF2 0x400
#define LG_RDESC_REL_ABS 0x800
+#define LG_FF3 0x1000
/*
* Certain Logitech keyboards send in report #3 keys which are far
@@ -266,7 +267,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
- if (quirks & (LG_FF | LG_FF2))
+ if (quirks & (LG_FF | LG_FF2 | LG_FF3))
connect_mask &= ~HID_CONNECT_FF;
ret = hid_hw_start(hdev, connect_mask);
@@ -279,6 +280,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
lgff_init(hdev);
if (quirks & LG_FF2)
lg2ff_init(hdev);
+ if (quirks & LG_FF3)
+ lg3ff_init(hdev);
return 0;
err_free:
@@ -331,6 +334,8 @@ 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_LOGITECH_FLIGHT_SYSTEM_G940),
+ .driver_data = LG_FF3 },
{ 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),
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index bf31592eaf7..ce2ac867262 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -13,4 +13,10 @@ int lg2ff_init(struct hid_device *hdev);
static inline int lg2ff_init(struct hid_device *hdev) { return -1; }
#endif
+#ifdef CONFIG_LOGIG940_FF
+int lg3ff_init(struct hid_device *hdev);
+#else
+static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
+#endif
+
#endif
diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c
new file mode 100644
index 00000000000..4002832ee4a
--- /dev/null
+++ b/drivers/hid/hid-lg3ff.c
@@ -0,0 +1,176 @@
+/*
+ * Force feedback support for Logitech Flight System G940
+ *
+ * Copyright (c) 2009 Gary Stein <LordCnidarian@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
+
+/*
+ * G940 Theory of Operation (from experimentation)
+ *
+ * There are 63 fields (only 3 of them currently used)
+ * 0 - seems to be command field
+ * 1 - 30 deal with the x axis
+ * 31 -60 deal with the y axis
+ *
+ * Field 1 is x axis constant force
+ * Field 31 is y axis constant force
+ *
+ * other interesting fields 1,2,3,4 on x axis
+ * (same for 31,32,33,34 on y axis)
+ *
+ * 0 0 127 127 makes the joystick autocenter hard
+ *
+ * 127 0 127 127 makes the joystick loose on the right,
+ * but stops all movemnt left
+ *
+ * -127 0 -127 -127 makes the joystick loose on the left,
+ * but stops all movement right
+ *
+ * 0 0 -127 -127 makes the joystick rattle very hard
+ *
+ * I'm sure these are effects that I don't know enough about them
+ */
+
+struct lg3ff_device {
+ struct hid_report *report;
+};
+
+static int hid_lg3ff_play(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ int x, y;
+
+/*
+ * Maxusage should always be 63 (maximum fields)
+ * likely a better way to ensure this data is clean
+ */
+ memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage);
+
+ switch (effect->type) {
+ case FF_CONSTANT:
+/*
+ * Already clamped in ff_memless
+ * 0 is center (different then other logitech)
+ */
+ x = effect->u.ramp.start_level;
+ y = effect->u.ramp.end_level;
+
+ /* send command byte */
+ report->field[0]->value[0] = 0x51;
+
+/*
+ * Sign backwards from other Force3d pro
+ * which get recast here in two's complement 8 bits
+ */
+ report->field[0]->value[1] = (unsigned char)(-x);
+ report->field[0]->value[31] = (unsigned char)(-y);
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+ break;
+ }
+ return 0;
+}
+static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+
+/*
+ * Auto Centering probed from device
+ * NOTE: deadman's switch on G940 must be covered
+ * for effects to work
+ */
+ report->field[0]->value[0] = 0x51;
+ report->field[0]->value[1] = 0x00;
+ report->field[0]->value[2] = 0x00;
+ report->field[0]->value[3] = 0x7F;
+ report->field[0]->value[4] = 0x7F;
+ report->field[0]->value[31] = 0x00;
+ report->field[0]->value[32] = 0x00;
+ report->field[0]->value[33] = 0x7F;
+ report->field[0]->value[34] = 0x7F;
+
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+
+static const signed short ff3_joystick_ac[] = {
+ FF_CONSTANT,
+ FF_AUTOCENTER,
+ -1
+};
+
+int lg3ff_init(struct hid_device *hid)
+{
+ struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct input_dev *dev = hidinput->input;
+ struct hid_report *report;
+ struct hid_field *field;
+ const signed short *ff_bits = ff3_joystick_ac;
+ int error;
+ int i;
+
+ /* Find the report to use */
+ if (list_empty(report_list)) {
+ err_hid("No output report found");
+ return -1;
+ }
+
+ /* Check that the report looks ok */
+ report = list_entry(report_list->next, struct hid_report, list);
+ if (!report) {
+ err_hid("NULL output report");
+ return -1;
+ }
+
+ field = report->field[0];
+ if (!field) {
+ err_hid("NULL field");
+ return -1;
+ }
+
+ /* Assume single fixed device G940 */
+ for (i = 0; ff_bits[i] >= 0; i++)
+ set_bit(ff_bits[i], dev->ffbit);
+
+ error = input_ff_create_memless(dev, NULL, hid_lg3ff_play);
+ if (error)
+ return error;
+
+ if (test_bit(FF_AUTOCENTER, dev->ffbit))
+ dev->ff->set_autocenter = hid_lg3ff_set_autocenter;
+
+ dev_info(&hid->dev, "Force feedback for Logitech Flight System G940 by "
+ "Gary Stein <LordCnidarian@gmail.com>\n");
+ return 0;
+}
+
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index 987abebe082..61142b76a9b 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -67,6 +67,7 @@ static const struct dev_type devices[] = {
{ 0x046d, 0xc219, ff_rumble },
{ 0x046d, 0xc283, ff_joystick },
{ 0x046d, 0xc286, ff_joystick_ac },
+ { 0x046d, 0xc287, ff_joystick_ac },
{ 0x046d, 0xc293, ff_joystick },
{ 0x046d, 0xc294, ff_wheel },
{ 0x046d, 0xc295, ff_joystick },
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
new file mode 100644
index 00000000000..4a3a94f2b10
--- /dev/null
+++ b/drivers/hid/hid-magicmouse.c
@@ -0,0 +1,449 @@
+/*
+ * Apple "Magic" Wireless Mouse driver
+ *
+ * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+static bool emulate_3button = true;
+module_param(emulate_3button, bool, 0644);
+MODULE_PARM_DESC(emulate_3button, "Emulate a middle button");
+
+static int middle_button_start = -350;
+static int middle_button_stop = +350;
+
+static bool emulate_scroll_wheel = true;
+module_param(emulate_scroll_wheel, bool, 0644);
+MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
+
+static bool report_touches = true;
+module_param(report_touches, bool, 0644);
+MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)");
+
+static bool report_undeciphered;
+module_param(report_undeciphered, bool, 0644);
+MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
+
+#define TOUCH_REPORT_ID 0x29
+/* These definitions are not precise, but they're close enough. (Bits
+ * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
+ * to be some kind of bit mask -- 0x20 may be a near-field reading,
+ * and 0x40 is actual contact, and 0x10 may be a start/stop or change
+ * indication.)
+ */
+#define TOUCH_STATE_MASK 0xf0
+#define TOUCH_STATE_NONE 0x00
+#define TOUCH_STATE_START 0x30
+#define TOUCH_STATE_DRAG 0x40
+
+/**
+ * struct magicmouse_sc - Tracks Magic Mouse-specific data.
+ * @input: Input device through which we report events.
+ * @quirks: Currently unused.
+ * @last_timestamp: Timestamp from most recent (18-bit) touch report
+ * (units of milliseconds over short windows, but seems to
+ * increase faster when there are no touches).
+ * @delta_time: 18-bit difference between the two most recent touch
+ * reports from the mouse.
+ * @ntouches: Number of touches in most recent touch report.
+ * @scroll_accel: Number of consecutive scroll motions.
+ * @scroll_jiffies: Time of last scroll motion.
+ * @touches: Most recent data for a touch, indexed by tracking ID.
+ * @tracking_ids: Mapping of current touch input data to @touches.
+ */
+struct magicmouse_sc {
+ struct input_dev *input;
+ unsigned long quirks;
+
+ int last_timestamp;
+ int delta_time;
+ int ntouches;
+ int scroll_accel;
+ unsigned long scroll_jiffies;
+
+ struct {
+ short x;
+ short y;
+ short scroll_y;
+ u8 size;
+ } touches[16];
+ int tracking_ids[16];
+};
+
+static int magicmouse_firm_touch(struct magicmouse_sc *msc)
+{
+ int touch = -1;
+ int ii;
+
+ /* If there is only one "firm" touch, set touch to its
+ * tracking ID.
+ */
+ for (ii = 0; ii < msc->ntouches; ii++) {
+ int idx = msc->tracking_ids[ii];
+ if (msc->touches[idx].size < 8) {
+ /* Ignore this touch. */
+ } else if (touch >= 0) {
+ touch = -1;
+ break;
+ } else {
+ touch = idx;
+ }
+ }
+
+ return touch;
+}
+
+static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
+{
+ int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 |
+ test_bit(BTN_RIGHT, msc->input->key) << 1 |
+ test_bit(BTN_MIDDLE, msc->input->key) << 2;
+
+ if (emulate_3button) {
+ int id;
+
+ /* If some button was pressed before, keep it held
+ * down. Otherwise, if there's exactly one firm
+ * touch, use that to override the mouse's guess.
+ */
+ if (state == 0) {
+ /* The button was released. */
+ } else if (last_state != 0) {
+ state = last_state;
+ } else if ((id = magicmouse_firm_touch(msc)) >= 0) {
+ int x = msc->touches[id].x;
+ if (x < middle_button_start)
+ state = 1;
+ else if (x > middle_button_stop)
+ state = 2;
+ else
+ state = 4;
+ } /* else: we keep the mouse's guess */
+
+ input_report_key(msc->input, BTN_MIDDLE, state & 4);
+ }
+
+ input_report_key(msc->input, BTN_LEFT, state & 1);
+ input_report_key(msc->input, BTN_RIGHT, state & 2);
+
+ if (state != last_state)
+ msc->scroll_accel = 0;
+}
+
+static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
+{
+ struct input_dev *input = msc->input;
+ __s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24;
+ int misc = tdata[5] | tdata[6] << 8;
+ int id = (misc >> 6) & 15;
+ int x = x_y << 12 >> 20;
+ int y = -(x_y >> 20);
+
+ /* Store tracking ID and other fields. */
+ msc->tracking_ids[raw_id] = id;
+ msc->touches[id].x = x;
+ msc->touches[id].y = y;
+ msc->touches[id].size = misc & 63;
+
+ /* If requested, emulate a scroll wheel by detecting small
+ * vertical touch motions along the middle of the mouse.
+ */
+ if (emulate_scroll_wheel &&
+ middle_button_start < x && x < middle_button_stop) {
+ static const int accel_profile[] = {
+ 256, 228, 192, 160, 128, 96, 64, 32,
+ };
+ unsigned long now = jiffies;
+ int step = msc->touches[id].scroll_y - y;
+
+ /* Reset acceleration after half a second. */
+ if (time_after(now, msc->scroll_jiffies + HZ / 2))
+ msc->scroll_accel = 0;
+
+ /* Calculate and apply the scroll motion. */
+ switch (tdata[7] & TOUCH_STATE_MASK) {
+ case TOUCH_STATE_START:
+ msc->touches[id].scroll_y = y;
+ msc->scroll_accel = min_t(int, msc->scroll_accel + 1,
+ ARRAY_SIZE(accel_profile) - 1);
+ break;
+ case TOUCH_STATE_DRAG:
+ step = step / accel_profile[msc->scroll_accel];
+ if (step != 0) {
+ msc->touches[id].scroll_y = y;
+ msc->scroll_jiffies = now;
+ input_report_rel(input, REL_WHEEL, step);
+ }
+ break;
+ }
+ }
+
+ /* Generate the input events for this touch. */
+ if (report_touches) {
+ int orientation = (misc >> 10) - 32;
+
+ input_report_abs(input, ABS_MT_TRACKING_ID, id);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]);
+ input_report_abs(input, ABS_MT_ORIENTATION, orientation);
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+
+ if (report_undeciphered)
+ input_event(input, EV_MSC, MSC_RAW, tdata[7]);
+
+ input_mt_sync(input);
+ }
+}
+
+static int magicmouse_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ struct magicmouse_sc *msc = hid_get_drvdata(hdev);
+ struct input_dev *input = msc->input;
+ int x, y, ts, ii, clicks;
+
+ switch (data[0]) {
+ case 0x10:
+ if (size != 6)
+ return 0;
+ x = (__s16)(data[2] | data[3] << 8);
+ y = (__s16)(data[4] | data[5] << 8);
+ clicks = data[1];
+ break;
+ case TOUCH_REPORT_ID:
+ /* Expect six bytes of prefix, and N*8 bytes of touch data. */
+ if (size < 6 || ((size - 6) % 8) != 0)
+ return 0;
+ ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
+ msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
+ msc->last_timestamp = ts;
+ msc->ntouches = (size - 6) / 8;
+ for (ii = 0; ii < msc->ntouches; ii++)
+ magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
+ /* When emulating three-button mode, it is important
+ * to have the current touch information before
+ * generating a click event.
+ */
+ x = (signed char)data[1];
+ y = (signed char)data[2];
+ clicks = data[3];
+ break;
+ case 0x20: /* Theoretically battery status (0-100), but I have
+ * never seen it -- maybe it is only upon request.
+ */
+ case 0x60: /* Unknown, maybe laser on/off. */
+ case 0x61: /* Laser reflection status change.
+ * data[1]: 0 = spotted, 1 = lost
+ */
+ default:
+ return 0;
+ }
+
+ magicmouse_emit_buttons(msc, clicks & 3);
+ input_report_rel(input, REL_X, x);
+ input_report_rel(input, REL_Y, y);
+ input_sync(input);
+ return 1;
+}
+
+static int magicmouse_input_open(struct input_dev *dev)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+
+ return hid->ll_driver->open(hid);
+}
+
+static void magicmouse_input_close(struct input_dev *dev)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+
+ hid->ll_driver->close(hid);
+}
+
+static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
+{
+ input_set_drvdata(input, hdev);
+ input->event = hdev->ll_driver->hidinput_input_event;
+ input->open = magicmouse_input_open;
+ input->close = magicmouse_input_close;
+
+ input->name = hdev->name;
+ input->phys = hdev->phys;
+ input->uniq = hdev->uniq;
+ input->id.bustype = hdev->bus;
+ input->id.vendor = hdev->vendor;
+ input->id.product = hdev->product;
+ input->id.version = hdev->version;
+ input->dev.parent = hdev->dev.parent;
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ if (emulate_3button)
+ __set_bit(BTN_MIDDLE, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+
+ __set_bit(EV_REL, input->evbit);
+ __set_bit(REL_X, input->relbit);
+ __set_bit(REL_Y, input->relbit);
+ if (emulate_scroll_wheel)
+ __set_bit(REL_WHEEL, input->relbit);
+
+ if (report_touches) {
+ __set_bit(EV_ABS, input->evbit);
+
+ input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0);
+ input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358,
+ 4, 0);
+ /* Note: Touch Y position from the device is inverted relative
+ * to how pointer motion is reported (and relative to how USB
+ * HID recommends the coordinates work). This driver keeps
+ * the origin at the same position, and just uses the additive
+ * inverse of the reported Y.
+ */
+ input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047,
+ 4, 0);
+ }
+
+ if (report_undeciphered) {
+ __set_bit(EV_MSC, input->evbit);
+ __set_bit(MSC_RAW, input->mscbit);
+ }
+}
+
+static int magicmouse_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ __u8 feature_1[] = { 0xd7, 0x01 };
+ __u8 feature_2[] = { 0xf8, 0x01, 0x32 };
+ struct input_dev *input;
+ struct magicmouse_sc *msc;
+ struct hid_report *report;
+ int ret;
+
+ msc = kzalloc(sizeof(*msc), GFP_KERNEL);
+ if (msc == NULL) {
+ dev_err(&hdev->dev, "can't alloc magicmouse descriptor\n");
+ return -ENOMEM;
+ }
+
+ msc->quirks = id->driver_data;
+ hid_set_drvdata(hdev, msc);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ dev_err(&hdev->dev, "magicmouse hid parse failed\n");
+ goto err_free;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ dev_err(&hdev->dev, "magicmouse hw start failed\n");
+ goto err_free;
+ }
+
+ report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
+ if (!report) {
+ dev_err(&hdev->dev, "unable to register touch report\n");
+ ret = -ENOMEM;
+ goto err_stop_hw;
+ }
+ report->size = 6;
+
+ ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1),
+ HID_FEATURE_REPORT);
+ if (ret != sizeof(feature_1)) {
+ dev_err(&hdev->dev, "unable to request touch data (1:%d)\n",
+ ret);
+ goto err_stop_hw;
+ }
+ ret = hdev->hid_output_raw_report(hdev, feature_2,
+ sizeof(feature_2), HID_FEATURE_REPORT);
+ if (ret != sizeof(feature_2)) {
+ dev_err(&hdev->dev, "unable to request touch data (2:%d)\n",
+ ret);
+ goto err_stop_hw;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&hdev->dev, "can't alloc input device\n");
+ ret = -ENOMEM;
+ goto err_stop_hw;
+ }
+ magicmouse_setup_input(input, hdev);
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&hdev->dev, "input device registration failed\n");
+ goto err_input;
+ }
+ msc->input = input;
+
+ return 0;
+err_input:
+ input_free_device(input);
+err_stop_hw:
+ hid_hw_stop(hdev);
+err_free:
+ kfree(msc);
+ return ret;
+}
+
+static void magicmouse_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id magic_mice[] = {
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE),
+ .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, magic_mice);
+
+static struct hid_driver magicmouse_driver = {
+ .name = "magicmouse",
+ .id_table = magic_mice,
+ .probe = magicmouse_probe,
+ .remove = magicmouse_remove,
+ .raw_event = magicmouse_raw_event,
+};
+
+static int __init magicmouse_init(void)
+{
+ int ret;
+
+ ret = hid_register_driver(&magicmouse_driver);
+ if (ret)
+ printk(KERN_ERR "can't register magicmouse driver\n");
+
+ return ret;
+}
+
+static void __exit magicmouse_exit(void)
+{
+ hid_unregister_driver(&magicmouse_driver);
+}
+
+module_init(magicmouse_init);
+module_exit(magicmouse_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c
new file mode 100644
index 00000000000..c8718168fe4
--- /dev/null
+++ b/drivers/hid/hid-mosart.c
@@ -0,0 +1,273 @@
+/*
+ * HID driver for the multitouch panel on the ASUS EeePC T91MT
+ *
+ * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
+ * Copyright (c) 2010 Teemu Tuominen <teemu.tuominen@cybercom.com>
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "usbhid/usbhid.h"
+
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("MosArt dual-touch panel");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+struct mosart_data {
+ __u16 x, y;
+ __u8 id;
+ bool valid; /* valid finger data, or just placeholder? */
+ bool first; /* is this the first finger in this frame? */
+ bool activity_now; /* at least one active finger in this frame? */
+ bool activity; /* at least one active finger previously? */
+};
+
+static int mosart_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_X);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_X,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_Y);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_Y,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ case HID_DG_CONFIDENCE:
+ case HID_DG_TIPSWITCH:
+ case HID_DG_INPUTMODE:
+ case HID_DG_DEVICEINDEX:
+ case HID_DG_CONTACTCOUNT:
+ case HID_DG_CONTACTMAX:
+ case HID_DG_TIPPRESSURE:
+ case HID_DG_WIDTH:
+ case HID_DG_HEIGHT:
+ return -1;
+ case HID_DG_INRANGE:
+ /* touchscreen emulation */
+ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+ return 1;
+
+ case HID_DG_CONTACTID:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TRACKING_ID);
+ return 1;
+
+ }
+ return 0;
+
+ case 0xff000000:
+ /* ignore HID features */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mosart_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY || usage->type == EV_ABS)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+
+/*
+ * this function is called when a whole finger has been parsed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mosart_filter_event(struct mosart_data *td, struct input_dev *input)
+{
+ td->first = !td->first; /* touchscreen emulation */
+
+ if (!td->valid) {
+ /*
+ * touchscreen emulation: if no finger in this frame is valid
+ * and there previously was finger activity, this is a release
+ */
+ if (!td->first && !td->activity_now && td->activity) {
+ input_event(input, EV_KEY, BTN_TOUCH, 0);
+ td->activity = false;
+ }
+ return;
+ }
+
+ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
+
+ input_mt_sync(input);
+ td->valid = false;
+
+ /* touchscreen emulation: if first active finger in this frame... */
+ if (!td->activity_now) {
+ /* if there was no previous activity, emit touch event */
+ if (!td->activity) {
+ input_event(input, EV_KEY, BTN_TOUCH, 1);
+ td->activity = true;
+ }
+ td->activity_now = true;
+ /* and in any case this is our preferred finger */
+ input_event(input, EV_ABS, ABS_X, td->x);
+ input_event(input, EV_ABS, ABS_Y, td->y);
+ }
+}
+
+
+static int mosart_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct mosart_data *td = hid_get_drvdata(hid);
+
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ struct input_dev *input = field->hidinput->input;
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ td->valid = !!value;
+ break;
+ case HID_GD_X:
+ td->x = value;
+ break;
+ case HID_GD_Y:
+ td->y = value;
+ mosart_filter_event(td, input);
+ break;
+ case HID_DG_CONTACTID:
+ td->id = value;
+ break;
+ case HID_DG_CONTACTCOUNT:
+ /* touch emulation: this is the last field in a frame */
+ td->first = false;
+ td->activity_now = false;
+ break;
+ case HID_DG_CONFIDENCE:
+ case HID_DG_TIPSWITCH:
+ /* avoid interference from generic hidinput handling */
+ break;
+
+ default:
+ /* fallback to the generic hidinput handling */
+ return 0;
+ }
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+static int mosart_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct mosart_data *td;
+
+
+ td = kmalloc(sizeof(struct mosart_data), GFP_KERNEL);
+ if (!td) {
+ dev_err(&hdev->dev, "cannot allocate MosArt data\n");
+ return -ENOMEM;
+ }
+ td->valid = false;
+ td->activity = false;
+ td->activity_now = false;
+ td->first = false;
+ hid_set_drvdata(hdev, td);
+
+ /* currently, it's better to have one evdev device only */
+#if 0
+ hdev->quirks |= HID_QUIRK_MULTI_INPUT;
+#endif
+
+ ret = hid_parse(hdev);
+ if (ret == 0)
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ if (ret == 0) {
+ struct hid_report_enum *re = hdev->report_enum
+ + HID_FEATURE_REPORT;
+ struct hid_report *r = re->report_id_hash[7];
+
+ r->field[0]->value[0] = 0x02;
+ usbhid_submit_report(hdev, r, USB_DIR_OUT);
+ } else
+ kfree(td);
+
+ return ret;
+}
+
+static void mosart_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id mosart_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, mosart_devices);
+
+static const struct hid_usage_id mosart_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver mosart_driver = {
+ .name = "mosart",
+ .id_table = mosart_devices,
+ .probe = mosart_probe,
+ .remove = mosart_remove,
+ .input_mapping = mosart_input_mapping,
+ .input_mapped = mosart_input_mapped,
+ .usage_table = mosart_grabbed_usages,
+ .event = mosart_event,
+};
+
+static int __init mosart_init(void)
+{
+ return hid_register_driver(&mosart_driver);
+}
+
+static void __exit mosart_exit(void)
+{
+ hid_unregister_driver(&mosart_driver);
+}
+
+module_init(mosart_init);
+module_exit(mosart_exit);
+
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 49ce69d7bba..3234c729a89 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -25,11 +25,16 @@
EV_KEY, (c))
struct ntrig_data {
- __s32 x, y, id, w, h;
- char reading_a_point, found_contact_id;
- char pen_active;
- char finger_active;
- char inverted;
+ /* Incoming raw values for a single contact */
+ __u16 x, y, w, h;
+ __u16 id;
+ __u8 confidence;
+
+ bool reading_mt;
+ __u8 first_contact_confidence;
+
+ __u8 mt_footer[4];
+ __u8 mt_foot_count;
};
/*
@@ -42,8 +47,11 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- switch (usage->hid & HID_USAGE_PAGE) {
+ /* No special mappings needed for the pen and single touch */
+ if (field->physical)
+ return 0;
+ switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_GENDESK:
switch (usage->hid) {
case HID_GD_X:
@@ -66,18 +74,12 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER:
switch (usage->hid) {
/* we do not want to map these for now */
- case HID_DG_CONTACTID: /* value is useless */
+ case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */
case HID_DG_INPUTMODE:
case HID_DG_DEVICEINDEX:
- case HID_DG_CONTACTCOUNT:
case HID_DG_CONTACTMAX:
return -1;
- /* original mapping by Rafi Rubin */
- case HID_DG_CONFIDENCE:
- nt_map_key_clear(BTN_TOOL_DOUBLETAP);
- return 1;
-
/* width/height mapped on TouchMajor/TouchMinor/Orientation */
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
@@ -104,6 +106,10 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
+ /* No special mappings needed for the pen and single touch */
+ if (field->physical)
+ return 0;
+
if (usage->type == EV_KEY || usage->type == EV_REL
|| usage->type == EV_ABS)
clear_bit(usage->code, *bit);
@@ -123,31 +129,30 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
struct input_dev *input = field->hidinput->input;
struct ntrig_data *nd = hid_get_drvdata(hid);
+ /* No special handling needed for the pen */
+ if (field->application == HID_DG_PEN)
+ return 0;
+
if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) {
-
- case HID_DG_INRANGE:
- if (field->application & 0x3)
- nd->pen_active = (value != 0);
- else
- nd->finger_active = (value != 0);
- return 0;
-
- case HID_DG_INVERT:
- nd->inverted = value;
- return 0;
-
+ case 0xff000001:
+ /* Tag indicating the start of a multitouch group */
+ nd->reading_mt = 1;
+ nd->first_contact_confidence = 0;
+ break;
+ case HID_DG_CONFIDENCE:
+ nd->confidence = value;
+ break;
case HID_GD_X:
nd->x = value;
- nd->reading_a_point = 1;
+ /* Clear the contact footer */
+ nd->mt_foot_count = 0;
break;
case HID_GD_Y:
nd->y = value;
break;
case HID_DG_CONTACTID:
nd->id = value;
- /* we receive this only when in multitouch mode */
- nd->found_contact_id = 1;
break;
case HID_DG_WIDTH:
nd->w = value;
@@ -159,35 +164,13 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* report received in a finger event. We want
* to emit a normal (X, Y) position
*/
- if (!nd->found_contact_id) {
- if (nd->pen_active && nd->finger_active) {
- input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
- input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
- }
+ if (!nd->reading_mt) {
+ input_report_key(input, BTN_TOOL_DOUBLETAP,
+ (nd->confidence != 0));
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
}
break;
- case HID_DG_TIPPRESSURE:
- /*
- * when in single touch mode, this is the last
- * report received in a pen event. We want
- * to emit a normal (X, Y) position
- */
- if (! nd->found_contact_id) {
- if (nd->pen_active && nd->finger_active) {
- input_report_key(input,
- nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
- , 0);
- input_report_key(input,
- nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
- , 1);
- }
- input_event(input, EV_ABS, ABS_X, nd->x);
- input_event(input, EV_ABS, ABS_Y, nd->y);
- input_event(input, EV_ABS, ABS_PRESSURE, value);
- }
- break;
case 0xff000002:
/*
* we receive this when the device is in multitouch
@@ -195,10 +178,34 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* this usage tells if the contact point is real
* or a placeholder
*/
- if (!nd->reading_a_point || value != 1)
+
+ /* Shouldn't get more than 4 footer packets, so skip */
+ if (nd->mt_foot_count >= 4)
break;
+
+ nd->mt_footer[nd->mt_foot_count++] = value;
+
+ /* if the footer isn't complete break */
+ if (nd->mt_foot_count != 4)
+ break;
+
+ /* Pen activity signal, trigger end of touch. */
+ if (nd->mt_footer[2]) {
+ nd->confidence = 0;
+ break;
+ }
+
+ /* If the contact was invalid */
+ if (!(nd->confidence && nd->mt_footer[0])
+ || nd->w <= 250
+ || nd->h <= 190) {
+ nd->confidence = 0;
+ break;
+ }
+
/* emit a normal (X, Y) for the first point only */
if (nd->id == 0) {
+ nd->first_contact_confidence = nd->confidence;
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
}
@@ -220,8 +227,39 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
ABS_MT_TOUCH_MINOR, nd->w);
}
input_mt_sync(field->hidinput->input);
- nd->reading_a_point = 0;
- nd->found_contact_id = 0;
+ break;
+
+ case HID_DG_CONTACTCOUNT: /* End of a multitouch group */
+ if (!nd->reading_mt)
+ break;
+
+ nd->reading_mt = 0;
+
+ if (nd->first_contact_confidence) {
+ switch (value) {
+ case 0: /* for single touch devices */
+ case 1:
+ input_report_key(input,
+ BTN_TOOL_DOUBLETAP, 1);
+ break;
+ case 2:
+ input_report_key(input,
+ BTN_TOOL_TRIPLETAP, 1);
+ break;
+ case 3:
+ default:
+ input_report_key(input,
+ BTN_TOOL_QUADTAP, 1);
+ }
+ input_report_key(input, BTN_TOUCH, 1);
+ } else {
+ input_report_key(input,
+ BTN_TOOL_DOUBLETAP, 0);
+ input_report_key(input,
+ BTN_TOOL_TRIPLETAP, 0);
+ input_report_key(input,
+ BTN_TOOL_QUADTAP, 0);
+ }
break;
default:
@@ -231,8 +269,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
}
/* we have handled the hidinput part, now remains hiddev */
- if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
- hid->hiddev_hid_event(hid, field, usage, value);
+ if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
return 1;
}
@@ -241,23 +279,67 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct ntrig_data *nd;
+ struct hid_input *hidinput;
+ struct input_dev *input;
+
+ if (id->driver_data)
+ hdev->quirks |= HID_QUIRK_MULTI_INPUT;
nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL);
if (!nd) {
dev_err(&hdev->dev, "cannot allocate N-Trig data\n");
return -ENOMEM;
}
- nd->reading_a_point = 0;
- nd->found_contact_id = 0;
+
+ nd->reading_mt = 0;
hid_set_drvdata(hdev, nd);
ret = hid_parse(hdev);
- if (!ret)
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ dev_err(&hdev->dev, "parse failed\n");
+ goto err_free;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if (ret) {
+ dev_err(&hdev->dev, "hw start failed\n");
+ goto err_free;
+ }
- if (ret)
- kfree (nd);
+ list_for_each_entry(hidinput, &hdev->inputs, list) {
+ input = hidinput->input;
+ switch (hidinput->report->field[0]->application) {
+ case HID_DG_PEN:
+ input->name = "N-Trig Pen";
+ break;
+ case HID_DG_TOUCHSCREEN:
+ __clear_bit(BTN_TOOL_PEN, input->keybit);
+ /*
+ * A little something special to enable
+ * two and three finger taps.
+ */
+ __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+ /*
+ * The physical touchscreen (single touch)
+ * input has a value for physical, whereas
+ * the multitouch only has logical input
+ * fields.
+ */
+ input->name =
+ (hidinput->report->field[0]
+ ->physical) ?
+ "N-Trig Touchscreen" :
+ "N-Trig MultiTouch";
+ break;
+ }
+ }
+
+ return 0;
+err_free:
+ kfree(nd);
return ret;
}
@@ -276,7 +358,7 @@ MODULE_DEVICE_TABLE(hid, ntrig_devices);
static const struct hid_usage_id ntrig_grabbed_usages[] = {
{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
- { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 }
};
static struct hid_driver ntrig_driver = {
diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c
new file mode 100644
index 00000000000..aa9a960f73a
--- /dev/null
+++ b/drivers/hid/hid-ortek.c
@@ -0,0 +1,56 @@
+/*
+ * HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad).
+ * Fixes LogicalMaximum error in USB report description, see
+ * http://bugzilla.kernel.org/show_bug.cgi?id=14787
+ *
+ * Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int rsize)
+{
+ if (rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) {
+ dev_info(&hdev->dev, "Fixing up Ortek WKB-2000 "
+ "report descriptor.\n");
+ rdesc[55] = 0x92;
+ }
+}
+
+static const struct hid_device_id ortek_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ortek_devices);
+
+static struct hid_driver ortek_driver = {
+ .name = "ortek",
+ .id_table = ortek_devices,
+ .report_fixup = ortek_report_fixup
+};
+
+static int __init ortek_init(void)
+{
+ return hid_register_driver(&ortek_driver);
+}
+
+static void __exit ortek_exit(void)
+{
+ hid_unregister_driver(&ortek_driver);
+}
+
+module_init(ortek_init);
+module_exit(ortek_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c
new file mode 100644
index 00000000000..01dd51c4986
--- /dev/null
+++ b/drivers/hid/hid-quanta.c
@@ -0,0 +1,260 @@
+/*
+ * HID driver for Quanta Optical Touch dual-touch panels
+ *
+ * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("Quanta dual-touch panel");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+struct quanta_data {
+ __u16 x, y;
+ __u8 id;
+ bool valid; /* valid finger data, or just placeholder? */
+ bool first; /* is this the first finger in this frame? */
+ bool activity_now; /* at least one active finger in this frame? */
+ bool activity; /* at least one active finger previously? */
+};
+
+static int quanta_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_X);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_X,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_Y);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_Y,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ case HID_DG_CONFIDENCE:
+ case HID_DG_TIPSWITCH:
+ case HID_DG_INPUTMODE:
+ case HID_DG_DEVICEINDEX:
+ case HID_DG_CONTACTCOUNT:
+ case HID_DG_CONTACTMAX:
+ case HID_DG_TIPPRESSURE:
+ case HID_DG_WIDTH:
+ case HID_DG_HEIGHT:
+ return -1;
+ case HID_DG_INRANGE:
+ /* touchscreen emulation */
+ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+ return 1;
+ case HID_DG_CONTACTID:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TRACKING_ID);
+ return 1;
+ }
+ return 0;
+
+ case 0xff000000:
+ /* ignore vendor-specific features */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int quanta_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY || usage->type == EV_ABS)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+
+/*
+ * this function is called when a whole finger has been parsed,
+ * so that it can decide what to send to the input layer.
+ */
+static void quanta_filter_event(struct quanta_data *td, struct input_dev *input)
+{
+
+ td->first = !td->first; /* touchscreen emulation */
+
+ if (!td->valid) {
+ /*
+ * touchscreen emulation: if no finger in this frame is valid
+ * and there previously was finger activity, this is a release
+ */
+ if (!td->first && !td->activity_now && td->activity) {
+ input_event(input, EV_KEY, BTN_TOUCH, 0);
+ td->activity = false;
+ }
+ return;
+ }
+
+ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
+
+ input_mt_sync(input);
+ td->valid = false;
+
+ /* touchscreen emulation: if first active finger in this frame... */
+ if (!td->activity_now) {
+ /* if there was no previous activity, emit touch event */
+ if (!td->activity) {
+ input_event(input, EV_KEY, BTN_TOUCH, 1);
+ td->activity = true;
+ }
+ td->activity_now = true;
+ /* and in any case this is our preferred finger */
+ input_event(input, EV_ABS, ABS_X, td->x);
+ input_event(input, EV_ABS, ABS_Y, td->y);
+ }
+}
+
+
+static int quanta_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct quanta_data *td = hid_get_drvdata(hid);
+
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ struct input_dev *input = field->hidinput->input;
+
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ td->valid = !!value;
+ break;
+ case HID_GD_X:
+ td->x = value;
+ break;
+ case HID_GD_Y:
+ td->y = value;
+ quanta_filter_event(td, input);
+ break;
+ case HID_DG_CONTACTID:
+ td->id = value;
+ break;
+ case HID_DG_CONTACTCOUNT:
+ /* touch emulation: this is the last field in a frame */
+ td->first = false;
+ td->activity_now = false;
+ break;
+ case HID_DG_CONFIDENCE:
+ case HID_DG_TIPSWITCH:
+ /* avoid interference from generic hidinput handling */
+ break;
+
+ default:
+ /* fallback to the generic hidinput handling */
+ return 0;
+ }
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+static int quanta_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct quanta_data *td;
+
+ td = kmalloc(sizeof(struct quanta_data), GFP_KERNEL);
+ if (!td) {
+ dev_err(&hdev->dev, "cannot allocate Quanta Touch data\n");
+ return -ENOMEM;
+ }
+ td->valid = false;
+ td->activity = false;
+ td->activity_now = false;
+ td->first = false;
+ hid_set_drvdata(hdev, td);
+
+ ret = hid_parse(hdev);
+ if (!ret)
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ if (ret)
+ kfree(td);
+
+ return ret;
+}
+
+static void quanta_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id quanta_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
+ USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
+ USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, quanta_devices);
+
+static const struct hid_usage_id quanta_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver quanta_driver = {
+ .name = "quanta-touch",
+ .id_table = quanta_devices,
+ .probe = quanta_probe,
+ .remove = quanta_remove,
+ .input_mapping = quanta_input_mapping,
+ .input_mapped = quanta_input_mapped,
+ .usage_table = quanta_grabbed_usages,
+ .event = quanta_event,
+};
+
+static int __init quanta_init(void)
+{
+ return hid_register_driver(&quanta_driver);
+}
+
+static void __exit quanta_exit(void)
+{
+ hid_unregister_driver(&quanta_driver);
+}
+
+module_init(quanta_init);
+module_exit(quanta_exit);
+
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 4e8450228a2..9bf00d77d92 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -48,7 +48,7 @@ static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
* to "operational". Without this, the ps3 controller will not report any
* events.
*/
-static int sony_set_operational(struct hid_device *hdev)
+static int sony_set_operational_usb(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *dev = interface_to_usbdev(intf);
@@ -73,6 +73,12 @@ static int sony_set_operational(struct hid_device *hdev)
return ret;
}
+static int sony_set_operational_bt(struct hid_device *hdev)
+{
+ unsigned char buf[] = { 0x53, 0xf4, 0x42, 0x03, 0x00, 0x00 };
+ return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+}
+
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
@@ -81,7 +87,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
sc = kzalloc(sizeof(*sc), GFP_KERNEL);
if (sc == NULL) {
- dev_err(&hdev->dev, "can't alloc apple descriptor\n");
+ dev_err(&hdev->dev, "can't alloc sony descriptor\n");
return -ENOMEM;
}
@@ -101,7 +107,17 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
- ret = sony_set_operational(hdev);
+ switch (hdev->bus) {
+ case BUS_USB:
+ ret = sony_set_operational_usb(hdev);
+ break;
+ case BUS_BLUETOOTH:
+ ret = sony_set_operational_bt(hdev);
+ break;
+ default:
+ ret = 0;
+ }
+
if (ret < 0)
goto err_stop;
@@ -121,6 +137,7 @@ static void sony_remove(struct hid_device *hdev)
static const struct hid_device_id sony_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
.driver_data = VAIO_RDESC_CONSTANT },
{ }
diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c
new file mode 100644
index 00000000000..2e592a06654
--- /dev/null
+++ b/drivers/hid/hid-stantum.c
@@ -0,0 +1,283 @@
+/*
+ * HID driver for Stantum multitouch panels
+ *
+ * Copyright (c) 2009 Stephane Chatty <chatty@enac.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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("Stantum HID multitouch panels");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+struct stantum_data {
+ __s32 x, y, z, w, h; /* x, y, pressure, width, height */
+ __u16 id; /* touch id */
+ bool valid; /* valid finger data, or just placeholder? */
+ bool first; /* first finger in the HID packet? */
+ bool activity; /* at least one active finger so far? */
+};
+
+static int stantum_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_X);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_X,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_Y);
+ /* touchscreen emulation */
+ input_set_abs_params(hi->input, ABS_Y,
+ field->logical_minimum,
+ field->logical_maximum, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ case HID_DG_CONFIDENCE:
+ case HID_DG_INPUTMODE:
+ case HID_DG_DEVICEINDEX:
+ case HID_DG_CONTACTCOUNT:
+ case HID_DG_CONTACTMAX:
+ return -1;
+
+ case HID_DG_TIPSWITCH:
+ /* touchscreen emulation */
+ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+ return 1;
+
+ case HID_DG_WIDTH:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOUCH_MAJOR);
+ return 1;
+ case HID_DG_HEIGHT:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOUCH_MINOR);
+ input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
+ 1, 1, 0, 0);
+ return 1;
+ case HID_DG_TIPPRESSURE:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_PRESSURE);
+ return 1;
+
+ case HID_DG_CONTACTID:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TRACKING_ID);
+ return 1;
+
+ }
+ return 0;
+
+ case 0xff000000:
+ /* no input-oriented meaning */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int stantum_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY || usage->type == EV_ABS)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+
+/*
+ * this function is called when a whole finger has been parsed,
+ * so that it can decide what to send to the input layer.
+ */
+static void stantum_filter_event(struct stantum_data *sd,
+ struct input_dev *input)
+{
+ bool wide;
+
+ if (!sd->valid) {
+ /*
+ * touchscreen emulation: if the first finger is not valid and
+ * there previously was finger activity, this is a release
+ */
+ if (sd->first && sd->activity) {
+ input_event(input, EV_KEY, BTN_TOUCH, 0);
+ sd->activity = false;
+ }
+ return;
+ }
+
+ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, sd->id);
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, sd->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, sd->y);
+
+ wide = (sd->w > sd->h);
+ input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, wide ? sd->w : sd->h);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, wide ? sd->h : sd->w);
+
+ input_event(input, EV_ABS, ABS_MT_PRESSURE, sd->z);
+
+ input_mt_sync(input);
+ sd->valid = false;
+
+ /* touchscreen emulation */
+ if (sd->first) {
+ if (!sd->activity) {
+ input_event(input, EV_KEY, BTN_TOUCH, 1);
+ sd->activity = true;
+ }
+ input_event(input, EV_ABS, ABS_X, sd->x);
+ input_event(input, EV_ABS, ABS_Y, sd->y);
+ }
+ sd->first = false;
+}
+
+
+static int stantum_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct stantum_data *sd = hid_get_drvdata(hid);
+
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ struct input_dev *input = field->hidinput->input;
+
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ /* this is the last field in a finger */
+ stantum_filter_event(sd, input);
+ break;
+ case HID_DG_WIDTH:
+ sd->w = value;
+ break;
+ case HID_DG_HEIGHT:
+ sd->h = value;
+ break;
+ case HID_GD_X:
+ sd->x = value;
+ break;
+ case HID_GD_Y:
+ sd->y = value;
+ break;
+ case HID_DG_TIPPRESSURE:
+ sd->z = value;
+ break;
+ case HID_DG_CONTACTID:
+ sd->id = value;
+ break;
+ case HID_DG_CONFIDENCE:
+ sd->valid = !!value;
+ break;
+ case 0xff000002:
+ /* this comes only before the first finger */
+ sd->first = true;
+ break;
+
+ default:
+ /* ignore the others */
+ return 1;
+ }
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+static int stantum_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+ struct stantum_data *sd;
+
+ sd = kmalloc(sizeof(struct stantum_data), GFP_KERNEL);
+ if (!sd) {
+ dev_err(&hdev->dev, "cannot allocate Stantum data\n");
+ return -ENOMEM;
+ }
+ sd->valid = false;
+ sd->first = false;
+ sd->activity = false;
+ hid_set_drvdata(hdev, sd);
+
+ ret = hid_parse(hdev);
+ if (!ret)
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+ if (ret)
+ kfree(sd);
+
+ return ret;
+}
+
+static void stantum_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id stantum_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, stantum_devices);
+
+static const struct hid_usage_id stantum_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver stantum_driver = {
+ .name = "stantum",
+ .id_table = stantum_devices,
+ .probe = stantum_probe,
+ .remove = stantum_remove,
+ .input_mapping = stantum_input_mapping,
+ .input_mapped = stantum_input_mapped,
+ .usage_table = stantum_grabbed_usages,
+ .event = stantum_event,
+};
+
+static int __init stantum_init(void)
+{
+ return hid_register_driver(&stantum_driver);
+}
+
+static void __exit stantum_exit(void)
+{
+ hid_unregister_driver(&stantum_driver);
+}
+
+module_init(stantum_init);
+module_exit(stantum_exit);
+
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 12dcda52920..8d3b46f5d14 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -156,7 +156,9 @@ static int wacom_probe(struct hid_device *hdev,
struct hid_input *hidinput;
struct input_dev *input;
struct wacom_data *wdata;
+ char rep_data[2];
int ret;
+ int limit;
wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
if (wdata == NULL) {
@@ -166,6 +168,7 @@ static int wacom_probe(struct hid_device *hdev,
hid_set_drvdata(hdev, wdata);
+ /* Parse the HID report now */
ret = hid_parse(hdev);
if (ret) {
dev_err(&hdev->dev, "parse failed\n");
@@ -178,6 +181,31 @@ static int wacom_probe(struct hid_device *hdev,
goto err_free;
}
+ /*
+ * Note that if the raw queries fail, it's not a hard failure and it
+ * is safe to continue
+ */
+
+ /* Set Wacom mode2 */
+ rep_data[0] = 0x03; rep_data[1] = 0x00;
+ limit = 3;
+ do {
+ ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+ HID_FEATURE_REPORT);
+ } while (ret < 0 && limit-- > 0);
+ if (ret < 0)
+ dev_warn(&hdev->dev, "failed to poke device #1, %d\n", ret);
+
+ /* 0x06 - high reporting speed, 0x05 - low speed */
+ rep_data[0] = 0x06; rep_data[1] = 0x00;
+ limit = 3;
+ do {
+ ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+ HID_FEATURE_REPORT);
+ } while (ret < 0 && limit-- > 0);
+ if (ret < 0)
+ dev_warn(&hdev->dev, "failed to poke device #2, %d\n", ret);
+
hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
input = hidinput->input;
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index cdd136942bc..d04476700b7 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -134,7 +134,7 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
goto out;
}
- ret = dev->hid_output_raw_report(dev, buf, count);
+ ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT);
out:
kfree(buf);
return ret;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index e2997a8d5e1..56d06cd8075 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -5,7 +5,7 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2007-2008 Oliver Neukum
- * Copyright (c) 2006-2009 Jiri Kosina
+ * Copyright (c) 2006-2010 Jiri Kosina
*/
/*
@@ -316,6 +316,7 @@ static int hid_submit_out(struct hid_device *hid)
err_hid("usb_submit_urb(out) failed");
return -1;
}
+ usbhid->last_out = jiffies;
} else {
/*
* queue work to wake up the device.
@@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid)
err_hid("usb_submit_urb(ctrl) failed");
return -1;
}
+ usbhid->last_ctrl = jiffies;
} else {
/*
* queue work to wake up the device.
@@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
usbhid->out[usbhid->outhead].report = report;
usbhid->outhead = head;
- if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
+ if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
if (hid_submit_out(hid))
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+ } else {
+ /*
+ * the queue is known to run
+ * but an earlier request may be stuck
+ * we may need to time out
+ * no race because this is called under
+ * spinlock
+ */
+ if (time_after(jiffies, usbhid->last_out + HZ * 5))
+ usb_unlink_urb(usbhid->urbout);
+ }
return;
}
@@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
usbhid->ctrl[usbhid->ctrlhead].dir = dir;
usbhid->ctrlhead = head;
- if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+ if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
if (hid_submit_ctrl(hid))
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+ } else {
+ /*
+ * the queue is known to run
+ * but an earlier request may be stuck
+ * we may need to time out
+ * no race because this is called under
+ * spinlock
+ */
+ if (time_after(jiffies, usbhid->last_ctrl + HZ * 5))
+ usb_unlink_urb(usbhid->urbctrl);
+ }
}
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
@@ -774,7 +798,8 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
return 0;
}
-static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count)
+static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,
+ unsigned char report_type)
{
struct usbhid_device *usbhid = hid->driver_data;
struct usb_device *dev = hid_to_usb_dev(hid);
@@ -785,7 +810,7 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- ((HID_OUTPUT_REPORT + 1) << 8) | *buf,
+ ((report_type + 1) << 8) | *buf,
interface->desc.bInterfaceNumber, buf + 1, count - 1,
USB_CTRL_SET_TIMEOUT);
@@ -981,9 +1006,6 @@ static int usbhid_start(struct hid_device *hid)
spin_lock_init(&usbhid->lock);
- usbhid->intf = intf;
- usbhid->ifnum = interface->desc.bInterfaceNumber;
-
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
if (!usbhid->urbctrl) {
ret = -ENOMEM;
@@ -1154,6 +1176,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
hid->driver_data = usbhid;
usbhid->hid = hid;
+ usbhid->intf = intf;
+ usbhid->ifnum = interface->desc.bInterfaceNumber;
ret = hid_add_device(hid);
if (ret) {
@@ -1342,7 +1366,7 @@ static int hid_reset_resume(struct usb_interface *intf)
#endif /* CONFIG_PM */
-static struct usb_device_id hid_usb_ids [] = {
+static const struct usb_device_id hid_usb_ids[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
{ } /* Terminating entry */
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 38773dc2821..7844280897d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -43,8 +43,10 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
+ { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
@@ -57,6 +59,7 @@ static const struct hid_blacklist {
{ 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_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 08f505ca2e3..ec20400c7f2 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -80,12 +80,14 @@ struct usbhid_device {
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
char *ctrlbuf; /* Control buffer */
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
+ unsigned long last_ctrl; /* record of last output for timeouts */
struct urb *urbout; /* Output URB */
struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
char *outbuf; /* Output buffer */
dma_addr_t outbuf_dma; /* Output buffer dma */
+ unsigned long last_out; /* record of last output for timeouts */
spinlock_t lock; /* fifo spinlock */
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c
index 6c9ace1b76f..2ad62c339cd 100644
--- a/drivers/hwmon/ams/ams-core.c
+++ b/drivers/hwmon/ams/ams-core.c
@@ -213,7 +213,7 @@ int __init ams_init(void)
return -ENODEV;
}
-void ams_exit(void)
+void ams_sensor_detach(void)
{
/* Remove input device */
ams_input_exit();
@@ -221,9 +221,6 @@ void ams_exit(void)
/* Remove attributes */
device_remove_file(&ams_info.of_dev->dev, &dev_attr_current);
- /* Shut down implementation */
- ams_info.exit();
-
/* Flush interrupt worker
*
* We do this after ams_info.exit(), because an interrupt might
@@ -239,6 +236,12 @@ void ams_exit(void)
pmf_unregister_irq_client(&ams_freefall_client);
}
+static void __exit ams_exit(void)
+{
+ /* Shut down implementation */
+ ams_info.exit();
+}
+
MODULE_AUTHOR("Stelian Pop, Michael Hanselmann");
MODULE_DESCRIPTION("Apple Motion Sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c
index 2cbf8a6506c..abeecd27b48 100644
--- a/drivers/hwmon/ams/ams-i2c.c
+++ b/drivers/hwmon/ams/ams-i2c.c
@@ -238,6 +238,8 @@ static int ams_i2c_probe(struct i2c_client *client,
static int ams_i2c_remove(struct i2c_client *client)
{
if (ams_info.has_device) {
+ ams_sensor_detach();
+
/* Disable interrupts */
ams_i2c_set_irq(AMS_IRQ_ALL, 0);
diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c
index fb18b3d3162..4f61b3ee1b0 100644
--- a/drivers/hwmon/ams/ams-pmu.c
+++ b/drivers/hwmon/ams/ams-pmu.c
@@ -133,6 +133,8 @@ static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z)
static void ams_pmu_exit(void)
{
+ ams_sensor_detach();
+
/* Disable interrupts */
ams_pmu_set_irq(AMS_IRQ_ALL, 0);
diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h
index 5ed387b0bd9..b28d7e27a03 100644
--- a/drivers/hwmon/ams/ams.h
+++ b/drivers/hwmon/ams/ams.h
@@ -61,6 +61,7 @@ extern struct ams ams_info;
extern void ams_sensors(s8 *x, s8 *y, s8 *z);
extern int ams_sensor_attach(void);
+extern void ams_sensor_detach(void);
extern int ams_pmu_init(struct device_node *np);
extern int ams_i2c_init(struct device_node *np);
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce2977..737f05200b1 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -564,6 +564,16 @@ config I2C_VERSATILE
This driver can also be built as a module. If so, the module
will be called i2c-versatile.
+config I2C_OCTEON
+ tristate "Cavium OCTEON I2C bus support"
+ depends on CPU_CAVIUM_OCTEON
+ help
+ Say yes if you want to support the I2C serial bus on Cavium
+ OCTEON SOC.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-octeon.
+
comment "External I2C/SMBus adapter drivers"
config I2C_PARPORT
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 302c551977b..c2c4ea1908d 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
+obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c
new file mode 100644
index 00000000000..60375504fa4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-octeon.c
@@ -0,0 +1,651 @@
+/*
+ * (C) Copyright 2009-2010
+ * Nokia Siemens Networks, michael.lawnick.ext@nsn.com
+ *
+ * Portions Copyright (C) 2010 Cavium Networks, Inc.
+ *
+ * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <asm/octeon/octeon.h>
+
+#define DRV_NAME "i2c-octeon"
+
+/* The previous out-of-tree version was implicitly version 1.0. */
+#define DRV_VERSION "2.0"
+
+/* register offsets */
+#define SW_TWSI 0x00
+#define TWSI_INT 0x10
+
+/* Controller command patterns */
+#define SW_TWSI_V 0x8000000000000000ull
+#define SW_TWSI_EOP_TWSI_DATA 0x0C00000100000000ull
+#define SW_TWSI_EOP_TWSI_CTL 0x0C00000200000000ull
+#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C00000300000000ull
+#define SW_TWSI_EOP_TWSI_STAT 0x0C00000300000000ull
+#define SW_TWSI_EOP_TWSI_RST 0x0C00000700000000ull
+#define SW_TWSI_OP_TWSI_CLK 0x0800000000000000ull
+#define SW_TWSI_R 0x0100000000000000ull
+
+/* Controller command and status bits */
+#define TWSI_CTL_CE 0x80
+#define TWSI_CTL_ENAB 0x40
+#define TWSI_CTL_STA 0x20
+#define TWSI_CTL_STP 0x10
+#define TWSI_CTL_IFLG 0x08
+#define TWSI_CTL_AAK 0x04
+
+/* Some status values */
+#define STAT_START 0x08
+#define STAT_RSTART 0x10
+#define STAT_TXADDR_ACK 0x18
+#define STAT_TXDATA_ACK 0x28
+#define STAT_RXADDR_ACK 0x40
+#define STAT_RXDATA_ACK 0x50
+#define STAT_IDLE 0xF8
+
+struct octeon_i2c {
+ wait_queue_head_t queue;
+ struct i2c_adapter adap;
+ int irq;
+ int twsi_freq;
+ int sys_freq;
+ resource_size_t twsi_phys;
+ void __iomem *twsi_base;
+ resource_size_t regsize;
+ struct device *dev;
+};
+
+/**
+ * octeon_i2c_write_sw - write an I2C core register.
+ * @i2c: The struct octeon_i2c.
+ * @eop_reg: Register selector.
+ * @data: Value to be written.
+ *
+ * The I2C core registers are accessed indirectly via the SW_TWSI CSR.
+ */
+static void octeon_i2c_write_sw(struct octeon_i2c *i2c,
+ u64 eop_reg,
+ u8 data)
+{
+ u64 tmp;
+
+ __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI);
+ do {
+ tmp = __raw_readq(i2c->twsi_base + SW_TWSI);
+ } while ((tmp & SW_TWSI_V) != 0);
+}
+
+/**
+ * octeon_i2c_read_sw - write an I2C core register.
+ * @i2c: The struct octeon_i2c.
+ * @eop_reg: Register selector.
+ *
+ * Returns the data.
+ *
+ * The I2C core registers are accessed indirectly via the SW_TWSI CSR.
+ */
+static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg)
+{
+ u64 tmp;
+
+ __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI);
+ do {
+ tmp = __raw_readq(i2c->twsi_base + SW_TWSI);
+ } while ((tmp & SW_TWSI_V) != 0);
+
+ return tmp & 0xFF;
+}
+
+/**
+ * octeon_i2c_write_int - write the TWSI_INT register
+ * @i2c: The struct octeon_i2c.
+ * @data: Value to be written.
+ */
+static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
+{
+ u64 tmp;
+
+ __raw_writeq(data, i2c->twsi_base + TWSI_INT);
+ tmp = __raw_readq(i2c->twsi_base + TWSI_INT);
+}
+
+/**
+ * octeon_i2c_int_enable - enable the TS interrupt.
+ * @i2c: The struct octeon_i2c.
+ *
+ * The interrupt will be asserted when there is non-STAT_IDLE state in
+ * the SW_TWSI_EOP_TWSI_STAT register.
+ */
+static void octeon_i2c_int_enable(struct octeon_i2c *i2c)
+{
+ octeon_i2c_write_int(i2c, 0x40);
+}
+
+/**
+ * octeon_i2c_int_disable - disable the TS interrupt.
+ * @i2c: The struct octeon_i2c.
+ */
+static void octeon_i2c_int_disable(struct octeon_i2c *i2c)
+{
+ octeon_i2c_write_int(i2c, 0);
+}
+
+/**
+ * octeon_i2c_unblock - unblock the bus.
+ * @i2c: The struct octeon_i2c.
+ *
+ * If there was a reset while a device was driving 0 to bus,
+ * bus is blocked. We toggle it free manually by some clock
+ * cycles and send a stop.
+ */
+static void octeon_i2c_unblock(struct octeon_i2c *i2c)
+{
+ int i;
+
+ dev_dbg(i2c->dev, "%s\n", __func__);
+ for (i = 0; i < 9; i++) {
+ octeon_i2c_write_int(i2c, 0x0);
+ udelay(5);
+ octeon_i2c_write_int(i2c, 0x200);
+ udelay(5);
+ }
+ octeon_i2c_write_int(i2c, 0x300);
+ udelay(5);
+ octeon_i2c_write_int(i2c, 0x100);
+ udelay(5);
+ octeon_i2c_write_int(i2c, 0x0);
+}
+
+/**
+ * octeon_i2c_isr - the interrupt service routine.
+ * @int: The irq, unused.
+ * @dev_id: Our struct octeon_i2c.
+ */
+static irqreturn_t octeon_i2c_isr(int irq, void *dev_id)
+{
+ struct octeon_i2c *i2c = dev_id;
+
+ octeon_i2c_int_disable(i2c);
+ wake_up_interruptible(&i2c->queue);
+
+ return IRQ_HANDLED;
+}
+
+
+static int octeon_i2c_test_iflg(struct octeon_i2c *i2c)
+{
+ return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0;
+}
+
+/**
+ * octeon_i2c_wait - wait for the IFLG to be set.
+ * @i2c: The struct octeon_i2c.
+ *
+ * Returns 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_wait(struct octeon_i2c *i2c)
+{
+ int result;
+
+ octeon_i2c_int_enable(i2c);
+
+ result = wait_event_interruptible_timeout(i2c->queue,
+ octeon_i2c_test_iflg(i2c),
+ i2c->adap.timeout);
+
+ octeon_i2c_int_disable(i2c);
+
+ if (result < 0) {
+ dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__);
+ return result;
+ } else if (result == 0) {
+ dev_dbg(i2c->dev, "%s: timeout\n", __func__);
+ result = -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/**
+ * octeon_i2c_start - send START to the bus.
+ * @i2c: The struct octeon_i2c.
+ *
+ * Returns 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_start(struct octeon_i2c *i2c)
+{
+ u8 data;
+ int result;
+
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
+ TWSI_CTL_ENAB | TWSI_CTL_STA);
+
+ result = octeon_i2c_wait(i2c);
+ if (result) {
+ if (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) {
+ /*
+ * Controller refused to send start flag May
+ * be a client is holding SDA low - let's try
+ * to free it.
+ */
+ octeon_i2c_unblock(i2c);
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
+ TWSI_CTL_ENAB | TWSI_CTL_STA);
+
+ result = octeon_i2c_wait(i2c);
+ }
+ if (result)
+ return result;
+ }
+
+ data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
+ if ((data != STAT_START) && (data != STAT_RSTART)) {
+ dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * octeon_i2c_stop - send STOP to the bus.
+ * @i2c: The struct octeon_i2c.
+ *
+ * Returns 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_stop(struct octeon_i2c *i2c)
+{
+ u8 data;
+
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
+ TWSI_CTL_ENAB | TWSI_CTL_STP);
+
+ data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
+
+ if (data != STAT_IDLE) {
+ dev_err(i2c->dev, "%s: bad status(0x%x)\n", __func__, data);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * octeon_i2c_write - send data to the bus.
+ * @i2c: The struct octeon_i2c.
+ * @target: Target address.
+ * @data: Pointer to the data to be sent.
+ * @length: Length of the data.
+ *
+ * The address is sent over the bus, then the data.
+ *
+ * Returns 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_write(struct octeon_i2c *i2c, int target,
+ const u8 *data, int length)
+{
+ int i, result;
+ u8 tmp;
+
+ result = octeon_i2c_start(i2c);
+ if (result)
+ return result;
+
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1);
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);
+
+ result = octeon_i2c_wait(i2c);
+ if (result)
+ return result;
+
+ for (i = 0; i < length; i++) {
+ tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
+ if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) {
+ dev_err(i2c->dev,
+ "%s: bad status before write (0x%x)\n",
+ __func__, tmp);
+ return -EIO;
+ }
+
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]);
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);
+
+ result = octeon_i2c_wait(i2c);
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * octeon_i2c_read - receive data from the bus.
+ * @i2c: The struct octeon_i2c.
+ * @target: Target address.
+ * @data: Pointer to the location to store the datae .
+ * @length: Length of the data.
+ *
+ * The address is sent over the bus, then the data is read.
+ *
+ * Returns 0 on success, otherwise a negative errno.
+ */
+static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
+ u8 *data, int length)
+{
+ int i, result;
+ u8 tmp;
+
+ if (length < 1)
+ return -EINVAL;
+
+ result = octeon_i2c_start(i2c);
+ if (result)
+ return result;
+
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target<<1) | 1);
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);
+
+ result = octeon_i2c_wait(i2c);
+ if (result)
+ return result;
+
+ for (i = 0; i < length; i++) {
+ tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
+ if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) {
+ dev_err(i2c->dev,
+ "%s: bad status before read (0x%x)\n",
+ __func__, tmp);
+ return -EIO;
+ }
+
+ if (i+1 < length)
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
+ TWSI_CTL_ENAB | TWSI_CTL_AAK);
+ else
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
+ TWSI_CTL_ENAB);
+
+ result = octeon_i2c_wait(i2c);
+ if (result)
+ return result;
+
+ data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA);
+ }
+ return 0;
+}
+
+/**
+ * octeon_i2c_xfer - The driver's master_xfer function.
+ * @adap: Pointer to the i2c_adapter structure.
+ * @msgs: Pointer to the messages to be processed.
+ * @num: Length of the MSGS array.
+ *
+ * Returns the number of messages processed, or a negative errno on
+ * failure.
+ */
+static int octeon_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_msg *pmsg;
+ int i;
+ int ret = 0;
+ struct octeon_i2c *i2c = i2c_get_adapdata(adap);
+
+ for (i = 0; ret == 0 && i < num; i++) {
+ pmsg = &msgs[i];
+ dev_dbg(i2c->dev,
+ "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n",
+ pmsg->flags & I2C_M_RD ? "read" : "write",
+ pmsg->len, pmsg->addr, i + 1, num);
+ if (pmsg->flags & I2C_M_RD)
+ ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf,
+ pmsg->len);
+ else
+ ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf,
+ pmsg->len);
+ }
+ octeon_i2c_stop(i2c);
+
+ return (ret != 0) ? ret : num;
+}
+
+static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm octeon_i2c_algo = {
+ .master_xfer = octeon_i2c_xfer,
+ .functionality = octeon_i2c_functionality,
+};
+
+static struct i2c_adapter octeon_i2c_ops = {
+ .owner = THIS_MODULE,
+ .name = "OCTEON adapter",
+ .algo = &octeon_i2c_algo,
+ .timeout = 2,
+};
+
+/**
+ * octeon_i2c_setclock - Calculate and set clock divisors.
+ */
+static int __init octeon_i2c_setclock(struct octeon_i2c *i2c)
+{
+ int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff;
+ int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000;
+
+ for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) {
+ /*
+ * An mdiv value of less than 2 seems to not work well
+ * with ds1337 RTCs, so we constrain it to larger
+ * values.
+ */
+ for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) {
+ /*
+ * For given ndiv and mdiv values check the
+ * two closest thp values.
+ */
+ tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10;
+ tclk *= (1 << ndiv_idx);
+ thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
+ for (inc = 0; inc <= 1; inc++) {
+ thp_idx = thp_base + inc;
+ if (thp_idx < 5 || thp_idx > 0xff)
+ continue;
+
+ foscl = i2c->sys_freq / (2 * (thp_idx + 1));
+ foscl = foscl / (1 << ndiv_idx);
+ foscl = foscl / (mdiv_idx + 1) / 10;
+ diff = abs(foscl - i2c->twsi_freq);
+ if (diff < delta_hz) {
+ delta_hz = diff;
+ thp = thp_idx;
+ mdiv = mdiv_idx;
+ ndiv = ndiv_idx;
+ }
+ }
+ }
+ }
+ octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp);
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv);
+
+ return 0;
+}
+
+static int __init octeon_i2c_initlowlevel(struct octeon_i2c *i2c)
+{
+ u8 status;
+ int tries;
+
+ /* disable high level controller, enable bus access */
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);
+
+ /* reset controller */
+ octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0);
+
+ for (tries = 10; tries; tries--) {
+ udelay(1);
+ status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
+ if (status == STAT_IDLE)
+ return 0;
+ }
+ dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status);
+ return -EIO;
+}
+
+static int __devinit octeon_i2c_probe(struct platform_device *pdev)
+{
+ int irq, result = 0;
+ struct octeon_i2c *i2c;
+ struct octeon_i2c_data *i2c_data;
+ struct resource *res_mem;
+
+ /* All adaptors have an irq. */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ if (!i2c) {
+ dev_err(&pdev->dev, "kzalloc failed\n");
+ result = -ENOMEM;
+ goto out;
+ }
+ i2c->dev = &pdev->dev;
+ i2c_data = pdev->dev.platform_data;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (res_mem == NULL) {
+ dev_err(i2c->dev, "found no memory resource\n");
+ result = -ENXIO;
+ goto fail_region;
+ }
+
+ if (i2c_data == NULL) {
+ dev_err(i2c->dev, "no I2C frequency data\n");
+ result = -ENXIO;
+ goto fail_region;
+ }
+
+ i2c->twsi_phys = res_mem->start;
+ i2c->regsize = resource_size(res_mem);
+ i2c->twsi_freq = i2c_data->i2c_freq;
+ i2c->sys_freq = i2c_data->sys_freq;
+
+ if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) {
+ dev_err(i2c->dev, "request_mem_region failed\n");
+ goto fail_region;
+ }
+ i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize);
+
+ init_waitqueue_head(&i2c->queue);
+
+ i2c->irq = irq;
+
+ result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c);
+ if (result < 0) {
+ dev_err(i2c->dev, "failed to attach interrupt\n");
+ goto fail_irq;
+ }
+
+ result = octeon_i2c_initlowlevel(i2c);
+ if (result) {
+ dev_err(i2c->dev, "init low level failed\n");
+ goto fail_add;
+ }
+
+ result = octeon_i2c_setclock(i2c);
+ if (result) {
+ dev_err(i2c->dev, "clock init failed\n");
+ goto fail_add;
+ }
+
+ i2c->adap = octeon_i2c_ops;
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0;
+ i2c_set_adapdata(&i2c->adap, i2c);
+ platform_set_drvdata(pdev, i2c);
+
+ result = i2c_add_numbered_adapter(&i2c->adap);
+ if (result < 0) {
+ dev_err(i2c->dev, "failed to add adapter\n");
+ goto fail_add;
+ }
+
+ dev_info(i2c->dev, "version %s\n", DRV_VERSION);
+
+ return result;
+
+fail_add:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(i2c->irq, i2c);
+fail_irq:
+ iounmap(i2c->twsi_base);
+ release_mem_region(i2c->twsi_phys, i2c->regsize);
+fail_region:
+ kfree(i2c);
+out:
+ return result;
+};
+
+static int __devexit octeon_i2c_remove(struct platform_device *pdev)
+{
+ struct octeon_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+ platform_set_drvdata(pdev, NULL);
+ free_irq(i2c->irq, i2c);
+ iounmap(i2c->twsi_base);
+ release_mem_region(i2c->twsi_phys, i2c->regsize);
+ kfree(i2c);
+ return 0;
+};
+
+static struct platform_driver octeon_i2c_driver = {
+ .probe = octeon_i2c_probe,
+ .remove = __devexit_p(octeon_i2c_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init octeon_i2c_init(void)
+{
+ int rv;
+
+ rv = platform_driver_register(&octeon_i2c_driver);
+ return rv;
+}
+
+static void __exit octeon_i2c_exit(void)
+{
+ platform_driver_unregister(&octeon_i2c_driver);
+}
+
+MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
+MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_NAME);
+
+module_init(octeon_i2c_init);
+module_exit(octeon_i2c_exit);
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index 5d1c2603a13..2b0bd0b042d 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -20,15 +20,15 @@
#include <linux/platform_device.h>
#include <linux/i2c-pnx.h>
#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
#include <mach/hardware.h>
#include <mach/i2c.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
#define I2C_PNX_TIMEOUT 10 /* msec */
#define I2C_PNX_SPEED_KHZ 100
#define I2C_PNX_REGION_SIZE 0x100
-#define PNX_DEFAULT_FREQ 13 /* MHz */
static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data)
{
@@ -50,22 +50,21 @@ static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data)
return (timeout <= 0);
}
-static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap)
+static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *data = adap->algo_data;
- struct timer_list *timer = &data->mif.timer;
- int expires = I2C_PNX_TIMEOUT / (1000 / HZ);
+ struct timer_list *timer = &alg_data->mif.timer;
+ unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT);
if (expires <= 1)
expires = 2;
del_timer_sync(timer);
- dev_dbg(&adap->dev, "Timer armed at %lu plus %u jiffies.\n",
+ dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n",
jiffies, expires);
timer->expires = jiffies + expires;
- timer->data = (unsigned long)adap;
+ timer->data = (unsigned long)&alg_data;
add_timer(timer);
}
@@ -77,34 +76,34 @@ static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap)
*
* Generate a START signal in the desired mode.
*/
-static int i2c_pnx_start(unsigned char slave_addr, struct i2c_adapter *adap)
+static int i2c_pnx_start(unsigned char slave_addr,
+ struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
-
- dev_dbg(&adap->dev, "%s(): addr 0x%x mode %d\n", __func__,
+ dev_dbg(&alg_data->adapter.dev, "%s(): addr 0x%x mode %d\n", __func__,
slave_addr, alg_data->mif.mode);
/* Check for 7 bit slave addresses only */
if (slave_addr & ~0x7f) {
- dev_err(&adap->dev, "%s: Invalid slave address %x. "
- "Only 7-bit addresses are supported\n",
- adap->name, slave_addr);
+ dev_err(&alg_data->adapter.dev,
+ "%s: Invalid slave address %x. Only 7-bit addresses are supported\n",
+ alg_data->adapter.name, slave_addr);
return -EINVAL;
}
/* First, make sure bus is idle */
if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) {
/* Somebody else is monopolizing the bus */
- dev_err(&adap->dev, "%s: Bus busy. Slave addr = %02x, "
- "cntrl = %x, stat = %x\n",
- adap->name, slave_addr,
- ioread32(I2C_REG_CTL(alg_data)),
- ioread32(I2C_REG_STS(alg_data)));
+ dev_err(&alg_data->adapter.dev,
+ "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n",
+ alg_data->adapter.name, slave_addr,
+ ioread32(I2C_REG_CTL(alg_data)),
+ ioread32(I2C_REG_STS(alg_data)));
return -EBUSY;
} else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) {
/* Sorry, we lost the bus */
- dev_err(&adap->dev, "%s: Arbitration failure. "
- "Slave addr = %02x\n", adap->name, slave_addr);
+ dev_err(&alg_data->adapter.dev,
+ "%s: Arbitration failure. Slave addr = %02x\n",
+ alg_data->adapter.name, slave_addr);
return -EIO;
}
@@ -115,14 +114,14 @@ static int i2c_pnx_start(unsigned char slave_addr, struct i2c_adapter *adap)
iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi,
I2C_REG_STS(alg_data));
- dev_dbg(&adap->dev, "%s(): sending %#x\n", __func__,
+ dev_dbg(&alg_data->adapter.dev, "%s(): sending %#x\n", __func__,
(slave_addr << 1) | start_bit | alg_data->mif.mode);
/* Write the slave address, START bit and R/W bit */
iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode,
I2C_REG_TX(alg_data));
- dev_dbg(&adap->dev, "%s(): exit\n", __func__);
+ dev_dbg(&alg_data->adapter.dev, "%s(): exit\n", __func__);
return 0;
}
@@ -133,13 +132,12 @@ static int i2c_pnx_start(unsigned char slave_addr, struct i2c_adapter *adap)
*
* Generate a STOP signal to terminate the master transaction.
*/
-static void i2c_pnx_stop(struct i2c_adapter *adap)
+static void i2c_pnx_stop(struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
/* Only 1 msec max timeout due to interrupt context */
long timeout = 1000;
- dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
/* Write a STOP bit to TX FIFO */
@@ -153,7 +151,7 @@ static void i2c_pnx_stop(struct i2c_adapter *adap)
timeout--;
}
- dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
}
@@ -163,12 +161,11 @@ static void i2c_pnx_stop(struct i2c_adapter *adap)
*
* Sends one byte of data to the slave
*/
-static int i2c_pnx_master_xmit(struct i2c_adapter *adap)
+static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
u32 val;
- dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
if (alg_data->mif.len > 0) {
@@ -184,15 +181,15 @@ static int i2c_pnx_master_xmit(struct i2c_adapter *adap)
alg_data->mif.len--;
iowrite32(val, I2C_REG_TX(alg_data));
- dev_dbg(&adap->dev, "%s(): xmit %#x [%d]\n", __func__,
- val, alg_data->mif.len + 1);
+ dev_dbg(&alg_data->adapter.dev, "%s(): xmit %#x [%d]\n",
+ __func__, val, alg_data->mif.len + 1);
if (alg_data->mif.len == 0) {
if (alg_data->last) {
/* Wait until the STOP is seen. */
if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))
- dev_err(&adap->dev, "The bus is still "
- "active after timeout\n");
+ dev_err(&alg_data->adapter.dev,
+ "The bus is still active after timeout\n");
}
/* Disable master interrupts */
iowrite32(ioread32(I2C_REG_CTL(alg_data)) &
@@ -201,14 +198,15 @@ static int i2c_pnx_master_xmit(struct i2c_adapter *adap)
del_timer_sync(&alg_data->mif.timer);
- dev_dbg(&adap->dev, "%s(): Waking up xfer routine.\n",
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): Waking up xfer routine.\n",
__func__);
complete(&alg_data->mif.complete);
}
} else if (alg_data->mif.len == 0) {
/* zero-sized transfer */
- i2c_pnx_stop(adap);
+ i2c_pnx_stop(alg_data);
/* Disable master interrupts. */
iowrite32(ioread32(I2C_REG_CTL(alg_data)) &
@@ -217,13 +215,14 @@ static int i2c_pnx_master_xmit(struct i2c_adapter *adap)
/* Stop timer. */
del_timer_sync(&alg_data->mif.timer);
- dev_dbg(&adap->dev, "%s(): Waking up xfer routine after "
- "zero-xfer.\n", __func__);
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): Waking up xfer routine after zero-xfer.\n",
+ __func__);
complete(&alg_data->mif.complete);
}
- dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
return 0;
@@ -235,21 +234,21 @@ static int i2c_pnx_master_xmit(struct i2c_adapter *adap)
*
* Reads one byte data from the slave
*/
-static int i2c_pnx_master_rcv(struct i2c_adapter *adap)
+static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
unsigned int val = 0;
u32 ctl = 0;
- dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
/* Check, whether there is already data,
* or we didn't 'ask' for it yet.
*/
if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {
- dev_dbg(&adap->dev, "%s(): Write dummy data to fill "
- "Rx-fifo...\n", __func__);
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): Write dummy data to fill Rx-fifo...\n",
+ __func__);
if (alg_data->mif.len == 1) {
/* Last byte, do not acknowledge next rcv. */
@@ -281,16 +280,16 @@ static int i2c_pnx_master_rcv(struct i2c_adapter *adap)
if (alg_data->mif.len > 0) {
val = ioread32(I2C_REG_RX(alg_data));
*alg_data->mif.buf++ = (u8) (val & 0xff);
- dev_dbg(&adap->dev, "%s(): rcv 0x%x [%d]\n", __func__, val,
- alg_data->mif.len);
+ dev_dbg(&alg_data->adapter.dev, "%s(): rcv 0x%x [%d]\n",
+ __func__, val, alg_data->mif.len);
alg_data->mif.len--;
if (alg_data->mif.len == 0) {
if (alg_data->last)
/* Wait until the STOP is seen. */
if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))
- dev_err(&adap->dev, "The bus is still "
- "active after timeout\n");
+ dev_err(&alg_data->adapter.dev,
+ "The bus is still active after timeout\n");
/* Disable master interrupts */
ctl = ioread32(I2C_REG_CTL(alg_data));
@@ -304,7 +303,7 @@ static int i2c_pnx_master_rcv(struct i2c_adapter *adap)
}
}
- dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
return 0;
@@ -312,11 +311,11 @@ static int i2c_pnx_master_rcv(struct i2c_adapter *adap)
static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
{
+ struct i2c_pnx_algo_data *alg_data = dev_id;
u32 stat, ctl;
- struct i2c_adapter *adap = dev_id;
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
- dev_dbg(&adap->dev, "%s(): mstat = %x mctrl = %x, mode = %d\n",
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): mstat = %x mctrl = %x, mode = %d\n",
__func__,
ioread32(I2C_REG_STS(alg_data)),
ioread32(I2C_REG_CTL(alg_data)),
@@ -339,10 +338,10 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
complete(&alg_data->mif.complete);
} else if (stat & mstatus_nai) {
/* Slave did not acknowledge, generate a STOP */
- dev_dbg(&adap->dev, "%s(): "
- "Slave did not acknowledge, generating a STOP.\n",
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): Slave did not acknowledge, generating a STOP.\n",
__func__);
- i2c_pnx_stop(adap);
+ i2c_pnx_stop(alg_data);
/* Disable master interrupts. */
ctl = ioread32(I2C_REG_CTL(alg_data));
@@ -368,9 +367,9 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
*/
if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) {
if (alg_data->mif.mode == I2C_SMBUS_WRITE) {
- i2c_pnx_master_xmit(adap);
+ i2c_pnx_master_xmit(alg_data);
} else if (alg_data->mif.mode == I2C_SMBUS_READ) {
- i2c_pnx_master_rcv(adap);
+ i2c_pnx_master_rcv(alg_data);
}
}
}
@@ -379,7 +378,8 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
stat = ioread32(I2C_REG_STS(alg_data));
iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data));
- dev_dbg(&adap->dev, "%s(): exiting, stat = %x ctrl = %x.\n",
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): exiting, stat = %x ctrl = %x.\n",
__func__, ioread32(I2C_REG_STS(alg_data)),
ioread32(I2C_REG_CTL(alg_data)));
@@ -388,14 +388,13 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
static void i2c_pnx_timeout(unsigned long data)
{
- struct i2c_adapter *adap = (struct i2c_adapter *)data;
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+ struct i2c_pnx_algo_data *alg_data = (struct i2c_pnx_algo_data *)data;
u32 ctl;
- dev_err(&adap->dev, "Master timed out. stat = %04x, cntrl = %04x. "
- "Resetting master...\n",
- ioread32(I2C_REG_STS(alg_data)),
- ioread32(I2C_REG_CTL(alg_data)));
+ dev_err(&alg_data->adapter.dev,
+ "Master timed out. stat = %04x, cntrl = %04x. Resetting master...\n",
+ ioread32(I2C_REG_STS(alg_data)),
+ ioread32(I2C_REG_CTL(alg_data)));
/* Reset master and disable interrupts */
ctl = ioread32(I2C_REG_CTL(alg_data));
@@ -409,15 +408,14 @@ static void i2c_pnx_timeout(unsigned long data)
complete(&alg_data->mif.complete);
}
-static inline void bus_reset_if_active(struct i2c_adapter *adap)
+static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)
{
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
u32 stat;
if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) {
- dev_err(&adap->dev,
+ dev_err(&alg_data->adapter.dev,
"%s: Bus is still active after xfer. Reset it...\n",
- adap->name);
+ alg_data->adapter.name);
iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
I2C_REG_CTL(alg_data));
wait_reset(I2C_PNX_TIMEOUT, alg_data);
@@ -451,10 +449,11 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct i2c_pnx_algo_data *alg_data = adap->algo_data;
u32 stat = ioread32(I2C_REG_STS(alg_data));
- dev_dbg(&adap->dev, "%s(): entering: %d messages, stat = %04x.\n",
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): entering: %d messages, stat = %04x.\n",
__func__, num, ioread32(I2C_REG_STS(alg_data)));
- bus_reset_if_active(adap);
+ bus_reset_if_active(alg_data);
/* Process transactions in a loop. */
for (i = 0; rc >= 0 && i < num; i++) {
@@ -464,9 +463,9 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
addr = pmsg->addr;
if (pmsg->flags & I2C_M_TEN) {
- dev_err(&adap->dev,
+ dev_err(&alg_data->adapter.dev,
"%s: 10 bits addr not supported!\n",
- adap->name);
+ alg_data->adapter.name);
rc = -EINVAL;
break;
}
@@ -478,11 +477,10 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
alg_data->mif.ret = 0;
alg_data->last = (i == num - 1);
- dev_dbg(&adap->dev, "%s(): mode %d, %d bytes\n", __func__,
- alg_data->mif.mode,
- alg_data->mif.len);
+ dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n",
+ __func__, alg_data->mif.mode, alg_data->mif.len);
- i2c_pnx_arm_timer(adap);
+ i2c_pnx_arm_timer(alg_data);
/* initialize the completion var */
init_completion(&alg_data->mif.complete);
@@ -493,7 +491,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
I2C_REG_CTL(alg_data));
/* Put start-code and slave-address on the bus. */
- rc = i2c_pnx_start(addr, adap);
+ rc = i2c_pnx_start(addr, alg_data);
if (rc < 0)
break;
@@ -502,31 +500,32 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (!(rc = alg_data->mif.ret))
completed++;
- dev_dbg(&adap->dev, "%s(): Complete, return code = %d.\n",
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): Complete, return code = %d.\n",
__func__, rc);
/* Clear TDI and AFI bits in case they are set. */
if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) {
- dev_dbg(&adap->dev,
+ dev_dbg(&alg_data->adapter.dev,
"%s: TDI still set... clearing now.\n",
- adap->name);
+ alg_data->adapter.name);
iowrite32(stat, I2C_REG_STS(alg_data));
}
if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) {
- dev_dbg(&adap->dev,
+ dev_dbg(&alg_data->adapter.dev,
"%s: AFI still set... clearing now.\n",
- adap->name);
+ alg_data->adapter.name);
iowrite32(stat, I2C_REG_STS(alg_data));
}
}
- bus_reset_if_active(adap);
+ bus_reset_if_active(alg_data);
/* Cleanup to be sure... */
alg_data->mif.buf = NULL;
alg_data->mif.len = 0;
- dev_dbg(&adap->dev, "%s(): exiting, stat = %x\n",
+ dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
if (completed != num)
@@ -545,69 +544,92 @@ static struct i2c_algorithm pnx_algorithm = {
.functionality = i2c_pnx_func,
};
+#ifdef CONFIG_PM
static int i2c_pnx_controller_suspend(struct platform_device *pdev,
pm_message_t state)
{
- struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);
- return i2c_pnx->suspend(pdev, state);
+ struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+
+ /* FIXME: shouldn't this be clk_disable? */
+ clk_enable(alg_data->clk);
+
+ return 0;
}
static int i2c_pnx_controller_resume(struct platform_device *pdev)
{
- struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);
- return i2c_pnx->resume(pdev);
+ struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+
+ return clk_enable(alg_data->clk);
}
+#else
+#define i2c_pnx_controller_suspend NULL
+#define i2c_pnx_controller_resume NULL
+#endif
static int __devinit i2c_pnx_probe(struct platform_device *pdev)
{
unsigned long tmp;
int ret = 0;
struct i2c_pnx_algo_data *alg_data;
- int freq_mhz;
+ unsigned long freq;
struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data;
- if (!i2c_pnx || !i2c_pnx->adapter) {
+ if (!i2c_pnx || !i2c_pnx->name) {
dev_err(&pdev->dev, "%s: no platform data supplied\n",
__func__);
ret = -EINVAL;
goto out;
}
- platform_set_drvdata(pdev, i2c_pnx);
-
- if (i2c_pnx->calculate_input_freq)
- freq_mhz = i2c_pnx->calculate_input_freq(pdev);
- else {
- freq_mhz = PNX_DEFAULT_FREQ;
- dev_info(&pdev->dev, "Setting bus frequency to default value: "
- "%d MHz\n", freq_mhz);
+ alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL);
+ if (!alg_data) {
+ ret = -ENOMEM;
+ goto err_kzalloc;
}
- i2c_pnx->adapter->algo = &pnx_algorithm;
+ platform_set_drvdata(pdev, alg_data);
+
+ strlcpy(alg_data->adapter.name, i2c_pnx->name,
+ sizeof(alg_data->adapter.name));
+ alg_data->adapter.dev.parent = &pdev->dev;
+ alg_data->adapter.algo = &pnx_algorithm;
+ alg_data->adapter.algo_data = alg_data;
+ alg_data->adapter.nr = pdev->id;
+ alg_data->i2c_pnx = i2c_pnx;
+
+ alg_data->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(alg_data->clk)) {
+ ret = PTR_ERR(alg_data->clk);
+ goto out_drvdata;
+ }
- alg_data = i2c_pnx->adapter->algo_data;
init_timer(&alg_data->mif.timer);
alg_data->mif.timer.function = i2c_pnx_timeout;
- alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter;
+ alg_data->mif.timer.data = (unsigned long)alg_data;
/* Register I/O resource */
- if (!request_mem_region(alg_data->base, I2C_PNX_REGION_SIZE,
+ if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE,
pdev->name)) {
dev_err(&pdev->dev,
"I/O region 0x%08x for I2C already in use.\n",
- alg_data->base);
+ i2c_pnx->base);
ret = -ENODEV;
- goto out_drvdata;
+ goto out_clkget;
}
- if (!(alg_data->ioaddr =
- (u32)ioremap(alg_data->base, I2C_PNX_REGION_SIZE))) {
+ alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+ if (!alg_data->ioaddr) {
dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n");
ret = -ENOMEM;
goto out_release;
}
- i2c_pnx->set_clock_run(pdev);
+ ret = clk_enable(alg_data->clk);
+ if (ret)
+ goto out_unmap;
+
+ freq = clk_get_rate(alg_data->clk);
/*
* Clock Divisor High This value is the number of system clocks
@@ -620,45 +642,47 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
* the deglitching filter length.
*/
- tmp = ((freq_mhz * 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2;
+ tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2;
iowrite32(tmp, I2C_REG_CKH(alg_data));
iowrite32(tmp, I2C_REG_CKL(alg_data));
iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data));
if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) {
ret = -ENODEV;
- goto out_unmap;
+ goto out_clock;
}
init_completion(&alg_data->mif.complete);
- ret = request_irq(alg_data->irq, i2c_pnx_interrupt,
- 0, pdev->name, i2c_pnx->adapter);
+ ret = request_irq(i2c_pnx->irq, i2c_pnx_interrupt,
+ 0, pdev->name, alg_data);
if (ret)
goto out_clock;
/* Register this adapter with the I2C subsystem */
- i2c_pnx->adapter->dev.parent = &pdev->dev;
- i2c_pnx->adapter->nr = pdev->id;
- ret = i2c_add_numbered_adapter(i2c_pnx->adapter);
+ ret = i2c_add_numbered_adapter(&alg_data->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "I2C: Failed to add bus\n");
goto out_irq;
}
dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n",
- i2c_pnx->adapter->name, alg_data->base, alg_data->irq);
+ alg_data->adapter.name, i2c_pnx->base, i2c_pnx->irq);
return 0;
out_irq:
- free_irq(alg_data->irq, i2c_pnx->adapter);
+ free_irq(i2c_pnx->irq, alg_data);
out_clock:
- i2c_pnx->set_clock_stop(pdev);
+ clk_disable(alg_data->clk);
out_unmap:
- iounmap((void *)alg_data->ioaddr);
+ iounmap(alg_data->ioaddr);
out_release:
- release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE);
+ release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+out_clkget:
+ clk_put(alg_data->clk);
out_drvdata:
+ kfree(alg_data);
+err_kzalloc:
platform_set_drvdata(pdev, NULL);
out:
return ret;
@@ -666,15 +690,16 @@ out:
static int __devexit i2c_pnx_remove(struct platform_device *pdev)
{
- struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);
- struct i2c_adapter *adap = i2c_pnx->adapter;
- struct i2c_pnx_algo_data *alg_data = adap->algo_data;
-
- free_irq(alg_data->irq, i2c_pnx->adapter);
- i2c_del_adapter(adap);
- i2c_pnx->set_clock_stop(pdev);
- iounmap((void *)alg_data->ioaddr);
- release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE);
+ struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+ struct i2c_pnx_data *i2c_pnx = alg_data->i2c_pnx;
+
+ free_irq(i2c_pnx->irq, alg_data);
+ i2c_del_adapter(&alg_data->adapter);
+ clk_disable(alg_data->clk);
+ iounmap(alg_data->ioaddr);
+ release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+ clk_put(alg_data->clk);
+ kfree(alg_data);
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c
index 87cef0c440a..349a67bf1a3 100644
--- a/drivers/ide/au1xxx-ide.c
+++ b/drivers/ide/au1xxx-ide.c
@@ -56,8 +56,8 @@ static inline void auide_insw(unsigned long port, void *addr, u32 count)
chan_tab_t *ctp;
au1x_ddma_desc_t *dp;
- if(!put_dest_flags(ahwif->rx_chan, (void*)addr, count << 1,
- DDMA_FLAGS_NOIE)) {
+ if (!au1xxx_dbdma_put_dest(ahwif->rx_chan, virt_to_phys(addr),
+ count << 1, DDMA_FLAGS_NOIE)) {
printk(KERN_ERR "%s failed %d\n", __func__, __LINE__);
return;
}
@@ -74,8 +74,8 @@ static inline void auide_outsw(unsigned long port, void *addr, u32 count)
chan_tab_t *ctp;
au1x_ddma_desc_t *dp;
- if(!put_source_flags(ahwif->tx_chan, (void*)addr,
- count << 1, DDMA_FLAGS_NOIE)) {
+ if (!au1xxx_dbdma_put_source(ahwif->tx_chan, virt_to_phys(addr),
+ count << 1, DDMA_FLAGS_NOIE)) {
printk(KERN_ERR "%s failed %d\n", __func__, __LINE__);
return;
}
@@ -246,17 +246,14 @@ static int auide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
flags = DDMA_FLAGS_NOIE;
if (iswrite) {
- if(!put_source_flags(ahwif->tx_chan,
- (void*) sg_virt(sg),
- tc, flags)) {
+ if (!au1xxx_dbdma_put_source(ahwif->tx_chan,
+ sg_phys(sg), tc, flags)) {
printk(KERN_ERR "%s failed %d\n",
__func__, __LINE__);
}
- } else
- {
- if(!put_dest_flags(ahwif->rx_chan,
- (void*) sg_virt(sg),
- tc, flags)) {
+ } else {
+ if (!au1xxx_dbdma_put_dest(ahwif->rx_chan,
+ sg_phys(sg), tc, flags)) {
printk(KERN_ERR "%s failed %d\n",
__func__, __LINE__);
}
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 7f878017b73..3b128dce9c3 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -679,7 +679,7 @@ static void ide_disk_setup(ide_drive_t *drive)
if (max_s > hwif->rqsize)
max_s = hwif->rqsize;
- blk_queue_max_sectors(q, max_s);
+ blk_queue_max_hw_sectors(q, max_s);
}
printk(KERN_INFO "%s: max request size: %dKiB\n", drive->name,
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index fefbdfc8db0..efd90762346 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -486,7 +486,7 @@ static void ide_floppy_setup(ide_drive_t *drive)
drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE;
/* This value will be visible in the /proc/ide/hdx/settings */
drive->pc_delay = IDEFLOPPY_PC_DELAY;
- blk_queue_max_sectors(drive->queue, 64);
+ blk_queue_max_hw_sectors(drive->queue, 64);
}
/*
@@ -494,7 +494,7 @@ static void ide_floppy_setup(ide_drive_t *drive)
* nasty clicking noises without it, so please don't remove this.
*/
if (strncmp((char *)&id[ATA_ID_PROD], "IOMEGA Clik!", 11) == 0) {
- blk_queue_max_sectors(drive->queue, 64);
+ blk_queue_max_hw_sectors(drive->queue, 64);
drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE;
/* IOMEGA Clik! drives do not support lock/unlock commands */
drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING;
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 4d76ba47309..f8c1ae6ad74 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -774,7 +774,7 @@ static int ide_init_queue(ide_drive_t *drive)
if (hwif->rqsize < max_sectors)
max_sectors = hwif->rqsize;
- blk_queue_max_sectors(q, max_sectors);
+ blk_queue_max_hw_sectors(q, max_sectors);
#ifdef CONFIG_PCI
/* When we have an IOMMU, we may have a problem where pci_map_sg()
@@ -790,8 +790,7 @@ static int ide_init_queue(ide_drive_t *drive)
max_sg_entries >>= 1;
#endif /* CONFIG_PCI */
- blk_queue_max_hw_segments(q, max_sg_entries);
- blk_queue_max_phys_segments(q, max_sg_entries);
+ blk_queue_max_segments(q, max_sg_entries);
/* assign drive queue */
drive->queue = q;
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index f199896c411..c88696a6cf8 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -2020,7 +2020,7 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev)
if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION)
sdev->start_stop_pwr_cond = 1;
if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
- blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
+ blk_queue_max_hw_sectors(sdev->request_queue, 128 * 1024 / 512);
blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE);
return 0;
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index aa6713b4a98..291d9393d35 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -100,6 +100,12 @@ static void input_close_polled_device(struct input_dev *input)
struct input_polled_dev *dev = input_get_drvdata(input);
cancel_delayed_work_sync(&dev->work);
+ /*
+ * Clean up work struct to remove references to the workqueue.
+ * It may be destroyed by the next call. This causes problems
+ * at next device open-close in case of poll_interval == 0.
+ */
+ INIT_DELAYED_WORK(&dev->work, dev->work.work.func);
input_polldev_stop_workqueue();
if (dev->close)
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index d84a36e545f..b54aee7cd9e 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1161,9 +1161,17 @@ static int i8042_pm_restore(struct device *dev)
return 0;
}
+static int i8042_pm_thaw(struct device *dev)
+{
+ i8042_interrupt(0, NULL);
+
+ return 0;
+}
+
static const struct dev_pm_ops i8042_pm_ops = {
.suspend = i8042_pm_reset,
.resume = i8042_pm_restore,
+ .thaw = i8042_pm_thaw,
.poweroff = i8042_pm_reset,
.restore = i8042_pm_restore,
};
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index 09a5e7341bd..5256123a522 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -618,8 +618,8 @@ static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
static int general_touch_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->x = (pkt[2] << 8) | pkt[1];
+ dev->y = (pkt[4] << 8) | pkt[3];
dev->press = pkt[5] & 0xff;
dev->touch = pkt[0] & 0x01;
@@ -809,9 +809,9 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
[DEVTYPE_GENERAL_TOUCH] = {
.min_xc = 0x0,
- .max_xc = 0x0500,
+ .max_xc = 0x7fff,
.min_yc = 0x0,
- .max_yc = 0x0500,
+ .max_yc = 0x7fff,
.rept_size = 7,
.read_data = general_touch_read_data,
},
diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig
index 3464ebc4cdb..452fde9edf8 100644
--- a/drivers/isdn/hisax/Kconfig
+++ b/drivers/isdn/hisax/Kconfig
@@ -109,7 +109,7 @@ config HISAX_16_3
config HISAX_TELESPCI
bool "Teles PCI"
- depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the Teles PCI.
See <file:Documentation/isdn/README.HiSax> on how to configure it.
@@ -237,7 +237,7 @@ config HISAX_MIC
config HISAX_NETJET
bool "NETjet card"
- depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the NetJet from Traverse
Technologies.
@@ -248,7 +248,7 @@ config HISAX_NETJET
config HISAX_NETJET_U
bool "NETspider U card"
- depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the Netspider U interface ISDN card
from Traverse Technologies.
@@ -287,7 +287,7 @@ config HISAX_HSTSAPHIR
config HISAX_BKM_A4T
bool "Telekom A4T card"
- depends on PCI && PCI_LEGACY
+ depends on PCI
help
This enables HiSax support for the Telekom A4T card.
@@ -297,7 +297,7 @@ config HISAX_BKM_A4T
config HISAX_SCT_QUADRO
bool "Scitel Quadro card"
- depends on PCI && PCI_LEGACY
+ depends on PCI
help
This enables HiSax support for the Scitel Quadro card.
@@ -316,7 +316,7 @@ config HISAX_GAZEL
config HISAX_HFC_PCI
bool "HFC PCI-Bus cards"
- depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
@@ -325,7 +325,7 @@ config HISAX_HFC_PCI
config HISAX_W6692
bool "Winbond W6692 based cards"
- depends on PCI && PCI_LEGACY
+ depends on PCI
help
This enables HiSax support for Winbond W6692 based PCI ISDN cards.
@@ -341,7 +341,7 @@ config HISAX_HFC_SX
config HISAX_ENTERNOW_PCI
bool "Formula-n enter:now PCI card"
- depends on HISAX_NETJET && PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
help
This enables HiSax support for the Formula-n enter:now PCI
ISDN card.
@@ -412,7 +412,7 @@ config HISAX_HFC4S8S
config HISAX_FRITZ_PCIPNP
tristate "AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)"
- depends on PCI && PCI_LEGACY && EXPERIMENTAL
+ depends on PCI && EXPERIMENTAL
help
This enables the driver for the AVM Fritz!Card PCI,
Fritz!Card PCI v2 and Fritz!Card PnP.
diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c
index 7cabc5a1949..14295a155e7 100644
--- a/drivers/isdn/hisax/avm_pci.c
+++ b/drivers/isdn/hisax/avm_pci.c
@@ -822,7 +822,7 @@ static int __devinit avm_pnp_setup(struct IsdnCardState *cs)
#endif /* __ISAPNP__ */
-#ifndef CONFIG_PCI_LEGACY
+#ifndef CONFIG_PCI
static int __devinit avm_pci_setup(struct IsdnCardState *cs)
{
@@ -835,7 +835,7 @@ static struct pci_dev *dev_avm __devinitdata = NULL;
static int __devinit avm_pci_setup(struct IsdnCardState *cs)
{
- if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM,
+ if ((dev_avm = hisax_find_pci_device(PCI_VENDOR_ID_AVM,
PCI_DEVICE_ID_AVM_A1, dev_avm))) {
if (pci_enable_device(dev_avm))
@@ -864,7 +864,7 @@ static int __devinit avm_pci_setup(struct IsdnCardState *cs)
return (1);
}
-#endif /* CONFIG_PCI_LEGACY */
+#endif /* CONFIG_PCI */
int __devinit
setup_avm_pcipnp(struct IsdnCard *card)
diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c
index 9ca2ee54cc9..9f2009c0b69 100644
--- a/drivers/isdn/hisax/bkm_a4t.c
+++ b/drivers/isdn/hisax/bkm_a4t.c
@@ -340,7 +340,7 @@ setup_bkm_a4t(struct IsdnCard *card)
} else
return (0);
- while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN,
+ while ((dev_a4t = hisax_find_pci_device(PCI_VENDOR_ID_ZORAN,
PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) {
ret = a4t_pci_probe(dev_a4t, cs, &found, &pci_memaddr);
if (!ret)
diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c
index e1ff4717a8a..e775706c60e 100644
--- a/drivers/isdn/hisax/bkm_a8.c
+++ b/drivers/isdn/hisax/bkm_a8.c
@@ -301,7 +301,7 @@ setup_sct_quadro(struct IsdnCard *card)
(sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
return (0);
if (cs->subtyp == SCT_1) {
- while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX,
+ while ((dev_a8 = hisax_find_pci_device(PCI_VENDOR_ID_PLX,
PCI_DEVICE_ID_PLX_9050, dev_a8))) {
sub_vendor_id = dev_a8->subsystem_vendor;
diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c
index 0b0c2e5d806..780da9bda91 100644
--- a/drivers/isdn/hisax/diva.c
+++ b/drivers/isdn/hisax/diva.c
@@ -1148,7 +1148,7 @@ static int __devinit setup_diva_isapnp(struct IsdnCard *card)
#endif /* ISAPNP */
-#ifdef CONFIG_PCI_LEGACY
+#ifdef CONFIG_PCI
static struct pci_dev *dev_diva __devinitdata = NULL;
static struct pci_dev *dev_diva_u __devinitdata = NULL;
static struct pci_dev *dev_diva201 __devinitdata = NULL;
@@ -1159,21 +1159,21 @@ static int __devinit setup_diva_pci(struct IsdnCard *card)
struct IsdnCardState *cs = card->cs;
cs->subtyp = 0;
- if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON,
+ if ((dev_diva = hisax_find_pci_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) {
if (pci_enable_device(dev_diva))
return(0);
cs->subtyp = DIVA_PCI;
cs->irq = dev_diva->irq;
cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2);
- } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON,
+ } else if ((dev_diva_u = hisax_find_pci_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) {
if (pci_enable_device(dev_diva_u))
return(0);
cs->subtyp = DIVA_PCI;
cs->irq = dev_diva_u->irq;
cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2);
- } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON,
+ } else if ((dev_diva201 = hisax_find_pci_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) {
if (pci_enable_device(dev_diva201))
return(0);
@@ -1183,7 +1183,7 @@ static int __devinit setup_diva_pci(struct IsdnCard *card)
(ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096);
cs->hw.diva.cfg_reg =
(ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096);
- } else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON,
+ } else if ((dev_diva202 = hisax_find_pci_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) {
if (pci_enable_device(dev_diva202))
return(0);
@@ -1229,14 +1229,14 @@ static int __devinit setup_diva_pci(struct IsdnCard *card)
return (1); /* card found */
}
-#else /* if !CONFIG_PCI_LEGACY */
+#else /* if !CONFIG_PCI */
static int __devinit setup_diva_pci(struct IsdnCard *card)
{
return (-1); /* card not found; continue search */
}
-#endif /* CONFIG_PCI_LEGACY */
+#endif /* CONFIG_PCI */
int __devinit
setup_diva(struct IsdnCard *card)
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
index aa29d1cf16a..23c41fcd864 100644
--- a/drivers/isdn/hisax/elsa.c
+++ b/drivers/isdn/hisax/elsa.c
@@ -1025,7 +1025,7 @@ setup_elsa_pcmcia(struct IsdnCard *card)
cs->irq);
}
-#ifdef CONFIG_PCI_LEGACY
+#ifdef CONFIG_PCI
static struct pci_dev *dev_qs1000 __devinitdata = NULL;
static struct pci_dev *dev_qs3000 __devinitdata = NULL;
@@ -1035,7 +1035,7 @@ setup_elsa_pci(struct IsdnCard *card)
struct IsdnCardState *cs = card->cs;
cs->subtyp = 0;
- if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+ if ((dev_qs1000 = hisax_find_pci_device(PCI_VENDOR_ID_ELSA,
PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) {
if (pci_enable_device(dev_qs1000))
return(0);
@@ -1043,7 +1043,7 @@ setup_elsa_pci(struct IsdnCard *card)
cs->irq = dev_qs1000->irq;
cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1);
cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3);
- } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+ } else if ((dev_qs3000 = hisax_find_pci_device(PCI_VENDOR_ID_ELSA,
PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) {
if (pci_enable_device(dev_qs3000))
return(0);
@@ -1093,7 +1093,7 @@ setup_elsa_pci(struct IsdnCard *card)
{
return (1);
}
-#endif /* CONFIG_PCI_LEGACY */
+#endif /* CONFIG_PCI */
static int __devinit
setup_elsa_common(struct IsdnCard *card)
diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c
index 39f421ed8de..26264abf1f5 100644
--- a/drivers/isdn/hisax/enternow_pci.c
+++ b/drivers/isdn/hisax/enternow_pci.c
@@ -406,7 +406,7 @@ setup_enternow_pci(struct IsdnCard *card)
for ( ;; )
{
- if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET,
PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
ret = en_pci_probe(dev_netjet, cs);
if (!ret)
diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c
index 0ea3b460768..353982fc143 100644
--- a/drivers/isdn/hisax/gazel.c
+++ b/drivers/isdn/hisax/gazel.c
@@ -531,7 +531,7 @@ setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs)
return (0);
}
-#ifdef CONFIG_PCI_LEGACY
+#ifdef CONFIG_PCI
static struct pci_dev *dev_tel __devinitdata = NULL;
static int __devinit
@@ -546,7 +546,7 @@ setup_gazelpci(struct IsdnCardState *cs)
found = 0;
seekcard = PCI_DEVICE_ID_PLX_R685;
for (nbseek = 0; nbseek < 4; nbseek++) {
- if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX,
+ if ((dev_tel = hisax_find_pci_device(PCI_VENDOR_ID_PLX,
seekcard, dev_tel))) {
if (pci_enable_device(dev_tel))
return 1;
@@ -620,7 +620,7 @@ setup_gazelpci(struct IsdnCardState *cs)
return (0);
}
-#endif /* CONFIG_PCI_LEGACY */
+#endif /* CONFIG_PCI */
int __devinit
setup_gazel(struct IsdnCard *card)
@@ -640,7 +640,7 @@ setup_gazel(struct IsdnCard *card)
return (0);
} else {
-#ifdef CONFIG_PCI_LEGACY
+#ifdef CONFIG_PCI
if (setup_gazelpci(cs))
return (0);
#else
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
index 10914731b30..917cc84065b 100644
--- a/drivers/isdn/hisax/hfc_pci.c
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -1658,7 +1658,7 @@ setup_hfcpci(struct IsdnCard *card)
i = 0;
while (id_list[i].vendor_id) {
- tmp_hfcpci = pci_find_device(id_list[i].vendor_id,
+ tmp_hfcpci = hisax_find_pci_device(id_list[i].vendor_id,
id_list[i].device_id,
dev_hfcpci);
i++;
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
index 0685c194696..832a87855ff 100644
--- a/drivers/isdn/hisax/hisax.h
+++ b/drivers/isdn/hisax/hisax.h
@@ -1323,3 +1323,26 @@ void release_tei(struct IsdnCardState *cs);
char *HiSax_getrev(const char *revision);
int TeiNew(void);
void TeiFree(void);
+
+#ifdef CONFIG_PCI
+
+#include <linux/pci.h>
+
+/* adaptation wrapper for old usage
+ * WARNING! This is unfit for use in a PCI hotplug environment,
+ * as the returned PCI device can disappear at any moment in time.
+ * Callers should be converted to use pci_get_device() instead.
+ */
+static inline struct pci_dev *hisax_find_pci_device(unsigned int vendor,
+ unsigned int device,
+ struct pci_dev *from)
+{
+ struct pci_dev *pdev;
+
+ pci_dev_get(from);
+ pdev = pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
+ pci_dev_put(pdev);
+ return pdev;
+}
+
+#endif
diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c
index ef00633e1d2..ccaa6e13310 100644
--- a/drivers/isdn/hisax/niccy.c
+++ b/drivers/isdn/hisax/niccy.c
@@ -297,12 +297,12 @@ int __devinit setup_niccy(struct IsdnCard *card)
return 0;
}
} else {
-#ifdef CONFIG_PCI_LEGACY
+#ifdef CONFIG_PCI
static struct pci_dev *niccy_dev __devinitdata;
u_int pci_ioaddr;
cs->subtyp = 0;
- if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM,
+ if ((niccy_dev = hisax_find_pci_device(PCI_VENDOR_ID_SATSAGEM,
PCI_DEVICE_ID_SATSAGEM_NICCY,
niccy_dev))) {
if (pci_enable_device(niccy_dev))
@@ -354,7 +354,7 @@ int __devinit setup_niccy(struct IsdnCard *card)
printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
return 0;
-#endif /* CONFIG_PCI_LEGACY */
+#endif /* CONFIG_PCI */
}
printk(KERN_INFO "HiSax: NICCY %s config irq:%d data:0x%X ale:0x%X\n",
(cs->subtyp == 1) ? "PnP" : "PCI",
diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c
index 8d36ccc87d8..2344e7b3344 100644
--- a/drivers/isdn/hisax/nj_s.c
+++ b/drivers/isdn/hisax/nj_s.c
@@ -276,7 +276,7 @@ setup_netjet_s(struct IsdnCard *card)
for ( ;; )
{
- if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET,
PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
ret = njs_pci_probe(dev_netjet, cs);
if (!ret)
diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c
index d306c946ffb..095e974aed8 100644
--- a/drivers/isdn/hisax/nj_u.c
+++ b/drivers/isdn/hisax/nj_u.c
@@ -240,7 +240,7 @@ setup_netjet_u(struct IsdnCard *card)
for ( ;; )
{
- if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET,
PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
ret = nju_pci_probe(dev_netjet, cs);
if (!ret)
diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c
index 5569a522e2a..69dfc8d2901 100644
--- a/drivers/isdn/hisax/sedlbauer.c
+++ b/drivers/isdn/hisax/sedlbauer.c
@@ -598,7 +598,7 @@ setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt)
}
#endif /* __ISAPNP__ */
-#ifdef CONFIG_PCI_LEGACY
+#ifdef CONFIG_PCI
static struct pci_dev *dev_sedl __devinitdata = NULL;
static int __devinit
@@ -607,7 +607,7 @@ setup_sedlbauer_pci(struct IsdnCard *card)
struct IsdnCardState *cs = card->cs;
u16 sub_vendor_id, sub_id;
- if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ if ((dev_sedl = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET,
PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) {
if (pci_enable_device(dev_sedl))
return(0);
@@ -673,7 +673,7 @@ setup_sedlbauer_pci(struct IsdnCard *card)
return (1);
}
-#endif /* CONFIG_PCI_LEGACY */
+#endif /* CONFIG_PCI */
int __devinit
setup_sedlbauer(struct IsdnCard *card)
diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c
index 28b08de4673..b85ceb3746c 100644
--- a/drivers/isdn/hisax/telespci.c
+++ b/drivers/isdn/hisax/telespci.c
@@ -300,7 +300,7 @@ setup_telespci(struct IsdnCard *card)
if (cs->typ != ISDN_CTYPE_TELESPCI)
return (0);
- if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) {
+ if ((dev_tel = hisax_find_pci_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) {
if (pci_enable_device(dev_tel))
return(0);
cs->irq = dev_tel->irq;
diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c
index c4d862c11a6..9d6e864023f 100644
--- a/drivers/isdn/hisax/w6692.c
+++ b/drivers/isdn/hisax/w6692.c
@@ -1007,7 +1007,7 @@ setup_w6692(struct IsdnCard *card)
return (0);
while (id_list[id_idx].vendor_id) {
- dev_w6692 = pci_find_device(id_list[id_idx].vendor_id,
+ dev_w6692 = hisax_find_pci_device(id_list[id_idx].vendor_id,
id_list[id_idx].device_id,
dev_w6692);
if (dev_w6692) {
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 23741cec45e..1c4ee6e7793 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -317,13 +317,15 @@ static int __init adb_init(void)
break;
}
}
- if ((adb_controller == NULL) || adb_controller->init()) {
- printk(KERN_WARNING "Warning: no ADB interface detected\n");
+ if (adb_controller != NULL && adb_controller->init &&
+ adb_controller->init())
adb_controller = NULL;
+ if (adb_controller == NULL) {
+ printk(KERN_WARNING "Warning: no ADB interface detected\n");
} else {
#ifdef CONFIG_PPC
- if (machine_is_compatible("AAPL,PowerBook1998") ||
- machine_is_compatible("PowerBook1,1"))
+ if (of_machine_is_compatible("AAPL,PowerBook1998") ||
+ of_machine_is_compatible("PowerBook1,1"))
sleepy_trackpad = 1;
#endif /* CONFIG_PPC */
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index 5ff47ba7f2d..c42eeb43042 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -90,6 +90,8 @@ static struct task_struct *thread_therm = NULL;
static void write_both_fan_speed(struct thermostat *th, int speed);
static void write_fan_speed(struct thermostat *th, int speed, int fan);
+static void thermostat_create_files(void);
+static void thermostat_remove_files(void);
static int
write_reg(struct thermostat* th, int reg, u8 data)
@@ -161,6 +163,8 @@ remove_thermostat(struct i2c_client *client)
struct thermostat *th = i2c_get_clientdata(client);
int i;
+ thermostat_remove_files();
+
if (thread_therm != NULL) {
kthread_stop(thread_therm);
}
@@ -312,7 +316,7 @@ static void update_fans_speed (struct thermostat *th)
if (verbose)
printk(KERN_DEBUG "adt746x: Setting fans speed to %d "
- "(limit exceeded by %d on %s) \n",
+ "(limit exceeded by %d on %s)\n",
new_speed, var,
sensor_location[fan_number+1]);
write_both_fan_speed(th, new_speed);
@@ -449,6 +453,8 @@ static int probe_thermostat(struct i2c_client *client,
return -ENOMEM;
}
+ thermostat_create_files();
+
return 0;
}
@@ -566,7 +572,6 @@ thermostat_init(void)
struct device_node* np;
const u32 *prop;
int i = 0, offset = 0;
- int err;
np = of_find_node_by_name(NULL, "fan");
if (!np)
@@ -633,6 +638,17 @@ thermostat_init(void)
return -ENODEV;
}
+#ifndef CONFIG_I2C_POWERMAC
+ request_module("i2c-powermac");
+#endif
+
+ return i2c_add_driver(&thermostat_driver);
+}
+
+static void thermostat_create_files(void)
+{
+ int err;
+
err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature);
err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature);
err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit);
@@ -647,16 +663,9 @@ thermostat_init(void)
if (err)
printk(KERN_WARNING
"Failed to create tempertaure attribute file(s).\n");
-
-#ifndef CONFIG_I2C_POWERMAC
- request_module("i2c-powermac");
-#endif
-
- return i2c_add_driver(&thermostat_driver);
}
-static void __exit
-thermostat_exit(void)
+static void thermostat_remove_files(void)
{
if (of_dev) {
device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature);
@@ -673,9 +682,14 @@ thermostat_exit(void)
device_remove_file(&of_dev->dev,
&dev_attr_sensor2_fan_speed);
- of_device_unregister(of_dev);
}
+}
+
+static void __exit
+thermostat_exit(void)
+{
i2c_del_driver(&thermostat_driver);
+ of_device_unregister(of_dev);
}
module_init(thermostat_init);
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index 454bc501df3..5738d8bf2d9 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -1899,7 +1899,7 @@ static int create_control_loops(void)
*/
if (rackmac)
cpu_pid_type = CPU_PID_TYPE_RACKMAC;
- else if (machine_is_compatible("PowerMac7,3")
+ else if (of_machine_is_compatible("PowerMac7,3")
&& (cpu_count > 1)
&& fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID
&& fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) {
@@ -2234,10 +2234,10 @@ static int __init therm_pm72_init(void)
{
struct device_node *np;
- rackmac = machine_is_compatible("RackMac3,1");
+ rackmac = of_machine_is_compatible("RackMac3,1");
- if (!machine_is_compatible("PowerMac7,2") &&
- !machine_is_compatible("PowerMac7,3") &&
+ if (!of_machine_is_compatible("PowerMac7,2") &&
+ !of_machine_is_compatible("PowerMac7,3") &&
!rackmac)
return -ENODEV;
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index ba48fd76396..7fb8b4da35a 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -490,7 +490,7 @@ g4fan_init( void )
info = of_get_property(np, "thermal-info", NULL);
of_node_put(np);
- if( !info || !machine_is_compatible("PowerMac3,6") )
+ if( !info || !of_machine_is_compatible("PowerMac3,6") )
return -ENODEV;
if( info->id != 3 ) {
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index 62dd1fdafec..971bc9582a5 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -89,7 +89,6 @@ static int cuda_fully_inited;
#ifdef CONFIG_ADB
static int cuda_probe(void);
-static int cuda_init(void);
static int cuda_send_request(struct adb_request *req, int sync);
static int cuda_adb_autopoll(int devs);
static int cuda_reset_adb_bus(void);
@@ -107,17 +106,42 @@ int cuda_request(struct adb_request *req,
#ifdef CONFIG_ADB
struct adb_driver via_cuda_driver = {
- "CUDA",
- cuda_probe,
- cuda_init,
- cuda_send_request,
- cuda_adb_autopoll,
- cuda_poll,
- cuda_reset_adb_bus
+ .name = "CUDA",
+ .probe = cuda_probe,
+ .send_request = cuda_send_request,
+ .autopoll = cuda_adb_autopoll,
+ .poll = cuda_poll,
+ .reset_bus = cuda_reset_adb_bus,
};
#endif /* CONFIG_ADB */
-#ifdef CONFIG_PPC
+#ifdef CONFIG_MAC
+int __init find_via_cuda(void)
+{
+ struct adb_request req;
+ int err;
+
+ if (macintosh_config->adb_type != MAC_ADB_CUDA)
+ return 0;
+
+ via = via1;
+ cuda_state = idle;
+
+ err = cuda_init_via();
+ if (err) {
+ printk(KERN_ERR "cuda_init_via() failed\n");
+ via = NULL;
+ return 0;
+ }
+
+ /* enable autopoll */
+ cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
+ while (!req.complete)
+ cuda_poll();
+
+ return 1;
+}
+#else
int __init find_via_cuda(void)
{
struct adb_request req;
@@ -175,7 +199,7 @@ int __init find_via_cuda(void)
vias = NULL;
return 0;
}
-#endif /* CONFIG_PPC */
+#endif /* !defined CONFIG_MAC */
static int __init via_cuda_start(void)
{
@@ -184,14 +208,14 @@ static int __init via_cuda_start(void)
#ifdef CONFIG_MAC
cuda_irq = IRQ_MAC_ADB;
-#else /* CONFIG_MAC */
+#else
cuda_irq = irq_of_parse_and_map(vias, 0);
if (cuda_irq == NO_IRQ) {
printk(KERN_ERR "via-cuda: can't map interrupts for %s\n",
vias->full_name);
return -ENODEV;
}
-#endif /* CONFIG_MAC */
+#endif
if (request_irq(cuda_irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
printk(KERN_ERR "via-cuda: can't request irq %d\n", cuda_irq);
@@ -216,28 +240,10 @@ cuda_probe(void)
#else
if (macintosh_config->adb_type != MAC_ADB_CUDA)
return -ENODEV;
- via = via1;
#endif
- return 0;
-}
-
-static int __init
-cuda_init(void)
-{
-#ifdef CONFIG_PPC
if (via == NULL)
return -ENODEV;
return 0;
-#else
- int err = cuda_init_via();
- if (err) {
- printk(KERN_ERR "cuda_init_via() failed\n");
- return -ENODEV;
- }
- out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
-
- return via_cuda_start();
-#endif
}
#endif /* CONFIG_ADB */
@@ -430,9 +436,11 @@ cuda_poll(void)
/* cuda_interrupt only takes a normal lock, we disable
* interrupts here to avoid re-entering and thus deadlocking.
*/
- disable_irq(cuda_irq);
+ if (cuda_irq)
+ disable_irq(cuda_irq);
cuda_interrupt(0, NULL);
- enable_irq(cuda_irq);
+ if (cuda_irq)
+ enable_irq(cuda_irq);
}
static irqreturn_t
@@ -446,7 +454,7 @@ cuda_interrupt(int irq, void *arg)
spin_lock(&cuda_lock);
- /* On powermacs, this handler is registered for the VIA IRQ. But it uses
+ /* On powermacs, this handler is registered for the VIA IRQ. But they use
* just the shift register IRQ -- other VIA interrupt sources are disabled.
* On m68k macs, the VIA IRQ sources are dispatched individually. Unless
* we are polling, the shift register IRQ flag has already been cleared.
diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c
index a348bb0791d..4f3c4479c16 100644
--- a/drivers/macintosh/via-pmu-backlight.c
+++ b/drivers/macintosh/via-pmu-backlight.c
@@ -150,13 +150,13 @@ void __init pmu_backlight_init()
/* Special case for the old PowerBook since I can't test on it */
autosave =
- machine_is_compatible("AAPL,3400/2400") ||
- machine_is_compatible("AAPL,3500");
+ of_machine_is_compatible("AAPL,3400/2400") ||
+ of_machine_is_compatible("AAPL,3500");
if (!autosave &&
!pmac_has_backlight_type("pmu") &&
- !machine_is_compatible("AAPL,PowerBook1998") &&
- !machine_is_compatible("PowerBook1,1"))
+ !of_machine_is_compatible("AAPL,PowerBook1998") &&
+ !of_machine_is_compatible("PowerBook1,1"))
return;
snprintf(name, sizeof(name), "pmubl");
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index db379c38143..42764849eb7 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -463,8 +463,8 @@ static int __init via_pmu_dev_init(void)
#endif
#ifdef CONFIG_PPC32
- if (machine_is_compatible("AAPL,3400/2400") ||
- machine_is_compatible("AAPL,3500")) {
+ if (of_machine_is_compatible("AAPL,3400/2400") ||
+ of_machine_is_compatible("AAPL,3500")) {
int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
NULL, PMAC_MB_INFO_MODEL, 0);
pmu_battery_count = 1;
@@ -472,8 +472,8 @@ static int __init via_pmu_dev_init(void)
pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET;
else
pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER;
- } else if (machine_is_compatible("AAPL,PowerBook1998") ||
- machine_is_compatible("PowerBook1,1")) {
+ } else if (of_machine_is_compatible("AAPL,PowerBook1998") ||
+ of_machine_is_compatible("PowerBook1,1")) {
pmu_battery_count = 2;
pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c
index 075b4d99e35..437f55c5d18 100644
--- a/drivers/macintosh/windfarm_core.c
+++ b/drivers/macintosh/windfarm_core.c
@@ -468,9 +468,9 @@ static int __init windfarm_core_init(void)
DBG("wf: core loaded\n");
/* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
+ if (of_machine_is_compatible("PowerMac7,2") ||
+ of_machine_is_compatible("PowerMac7,3") ||
+ of_machine_is_compatible("RackMac3,1"))
return -ENODEV;
platform_device_register(&wf_platform_device);
return 0;
diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c
index 900aade0619..1a77a7c97d0 100644
--- a/drivers/macintosh/windfarm_cpufreq_clamp.c
+++ b/drivers/macintosh/windfarm_cpufreq_clamp.c
@@ -76,9 +76,9 @@ static int __init wf_cpufreq_clamp_init(void)
struct wf_control *clamp;
/* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
+ if (of_machine_is_compatible("PowerMac7,2") ||
+ of_machine_is_compatible("PowerMac7,3") ||
+ of_machine_is_compatible("RackMac3,1"))
return -ENODEV;
clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index ed6426a1077..d8257d35afd 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -239,9 +239,9 @@ static struct i2c_driver wf_lm75_driver = {
static int __init wf_lm75_sensor_init(void)
{
/* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
+ if (of_machine_is_compatible("PowerMac7,2") ||
+ of_machine_is_compatible("PowerMac7,3") ||
+ of_machine_is_compatible("RackMac3,1"))
return -ENODEV;
return i2c_add_driver(&wf_lm75_driver);
}
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index a67b349319e..b486eb929fd 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -188,9 +188,9 @@ static struct i2c_driver wf_max6690_driver = {
static int __init wf_max6690_sensor_init(void)
{
/* Don't register on old machines that use therm_pm72 for now */
- if (machine_is_compatible("PowerMac7,2") ||
- machine_is_compatible("PowerMac7,3") ||
- machine_is_compatible("RackMac3,1"))
+ if (of_machine_is_compatible("PowerMac7,2") ||
+ of_machine_is_compatible("PowerMac7,3") ||
+ of_machine_is_compatible("RackMac3,1"))
return -ENODEV;
return i2c_add_driver(&wf_max6690_driver);
}
diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c
index 73d695dc9e5..e0ee80700cd 100644
--- a/drivers/macintosh/windfarm_pm112.c
+++ b/drivers/macintosh/windfarm_pm112.c
@@ -676,7 +676,7 @@ static int __init wf_pm112_init(void)
{
struct device_node *cpu;
- if (!machine_is_compatible("PowerMac11,2"))
+ if (!of_machine_is_compatible("PowerMac11,2"))
return -ENODEV;
/* Count the number of CPU cores */
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
index 66ec4fb115b..947d4afa25c 100644
--- a/drivers/macintosh/windfarm_pm121.c
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -1008,7 +1008,7 @@ static int __init pm121_init(void)
{
int rc = -ENODEV;
- if (machine_is_compatible("PowerMac12,1"))
+ if (of_machine_is_compatible("PowerMac12,1"))
rc = pm121_init_pm();
if (rc == 0) {
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
index abbe206474f..565d5b2adc9 100644
--- a/drivers/macintosh/windfarm_pm81.c
+++ b/drivers/macintosh/windfarm_pm81.c
@@ -779,8 +779,8 @@ static int __init wf_smu_init(void)
{
int rc = -ENODEV;
- if (machine_is_compatible("PowerMac8,1") ||
- machine_is_compatible("PowerMac8,2"))
+ if (of_machine_is_compatible("PowerMac8,1") ||
+ of_machine_is_compatible("PowerMac8,2"))
rc = wf_init_pm();
if (rc == 0) {
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
index 764c525b211..bea99168ff3 100644
--- a/drivers/macintosh/windfarm_pm91.c
+++ b/drivers/macintosh/windfarm_pm91.c
@@ -711,7 +711,7 @@ static int __init wf_smu_init(void)
{
int rc = -ENODEV;
- if (machine_is_compatible("PowerMac9,1"))
+ if (of_machine_is_compatible("PowerMac9,1"))
rc = wf_init_pm();
if (rc == 0) {
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c
index 9c567b93f41..3c193504bb8 100644
--- a/drivers/macintosh/windfarm_smu_sensors.c
+++ b/drivers/macintosh/windfarm_smu_sensors.c
@@ -363,9 +363,9 @@ smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps)
* I yet have to figure out what's up with 8,2 and will have to
* adjust for later, unless we can 100% trust the SDB partition...
*/
- if ((machine_is_compatible("PowerMac8,1") ||
- machine_is_compatible("PowerMac8,2") ||
- machine_is_compatible("PowerMac9,1")) &&
+ if ((of_machine_is_compatible("PowerMac8,1") ||
+ of_machine_is_compatible("PowerMac8,2") ||
+ of_machine_is_compatible("PowerMac9,1")) &&
cpuvcp_version >= 2) {
pow->quadratic = 1;
DBG("windfarm: CPU Power using quadratic transform\n");
diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c
index 54abf9e303b..f1c8cae70b4 100644
--- a/drivers/md/dm-log-userspace-transfer.c
+++ b/drivers/md/dm-log-userspace-transfer.c
@@ -172,11 +172,15 @@ int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type,
{
int r = 0;
size_t dummy = 0;
- int overhead_size =
- sizeof(struct dm_ulog_request *) + sizeof(struct cn_msg);
+ int overhead_size = sizeof(struct dm_ulog_request) + sizeof(struct cn_msg);
struct dm_ulog_request *tfr = prealloced_ulog_tfr;
struct receiving_pkg pkg;
+ /*
+ * Given the space needed to hold the 'struct cn_msg' and
+ * 'struct dm_ulog_request' - do we have enough payload
+ * space remaining?
+ */
if (data_size > (DM_ULOG_PREALLOCED_SIZE - overhead_size)) {
DMINFO("Size of tfr exceeds preallocated size");
return -EINVAL;
@@ -191,7 +195,7 @@ resend:
*/
mutex_lock(&dm_ulog_lock);
- memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - overhead_size);
+ memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - sizeof(struct cn_msg));
memcpy(tfr->uuid, uuid, DM_UUID_LEN);
tfr->luid = luid;
tfr->seq = dm_ulog_seq++;
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index ad779bd13ae..6c1046df81f 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -724,7 +724,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
/*
* Dispatch io.
*/
- if (unlikely(ms->log_failure)) {
+ if (unlikely(ms->log_failure) && errors_handled(ms)) {
spin_lock_irq(&ms->lock);
bio_list_merge(&ms->failures, &sync);
spin_unlock_irq(&ms->lock);
diff --git a/drivers/md/dm-region-hash.c b/drivers/md/dm-region-hash.c
index 5f19ceb6fe9..168bd38f500 100644
--- a/drivers/md/dm-region-hash.c
+++ b/drivers/md/dm-region-hash.c
@@ -660,10 +660,9 @@ void dm_rh_recovery_end(struct dm_region *reg, int success)
spin_lock_irq(&rh->region_lock);
if (success)
list_add(&reg->list, &reg->rh->recovered_regions);
- else {
- reg->state = DM_RH_NOSYNC;
+ else
list_add(&reg->list, &reg->rh->failed_recovered_regions);
- }
+
spin_unlock_irq(&rh->region_lock);
rh->wakeup_workers(rh->context);
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 7d08879689a..c097d8a4823 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -254,7 +254,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
* Issue the synchronous I/O from a different thread
* to avoid generic_make_request recursion.
*/
- INIT_WORK(&req.work, do_metadata);
+ INIT_WORK_ON_STACK(&req.work, do_metadata);
queue_work(ps->metadata_wq, &req.work);
flush_workqueue(ps->metadata_wq);
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index e0efc1adcaf..bd58703ee8f 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -110,7 +110,7 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
stripes = simple_strtoul(argv[0], &end, 10);
- if (*end) {
+ if (!stripes || *end) {
ti->error = "Invalid stripe count";
return -EINVAL;
}
diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c
index f53392df7b9..f91b40942e0 100644
--- a/drivers/md/dm-sysfs.c
+++ b/drivers/md/dm-sysfs.c
@@ -80,20 +80,12 @@ static struct sysfs_ops dm_sysfs_ops = {
};
/*
- * The sysfs structure is embedded in md struct, nothing to do here
- */
-static void dm_sysfs_release(struct kobject *kobj)
-{
-}
-
-/*
* dm kobject is embedded in mapped_device structure
* no need to define release function here
*/
static struct kobj_type dm_ktype = {
.sysfs_ops = &dm_sysfs_ops,
.default_attrs = dm_attrs,
- .release = dm_sysfs_release
};
/*
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 3167480b532..aa4e2aa86d4 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1595,10 +1595,15 @@ static int dm_prep_fn(struct request_queue *q, struct request *rq)
return BLKPREP_OK;
}
-static void map_request(struct dm_target *ti, struct request *clone,
- struct mapped_device *md)
+/*
+ * Returns:
+ * 0 : the request has been processed (not requeued)
+ * !0 : the request has been requeued
+ */
+static int map_request(struct dm_target *ti, struct request *clone,
+ struct mapped_device *md)
{
- int r;
+ int r, requeued = 0;
struct dm_rq_target_io *tio = clone->end_io_data;
/*
@@ -1625,6 +1630,7 @@ static void map_request(struct dm_target *ti, struct request *clone,
case DM_MAPIO_REQUEUE:
/* The target wants to requeue the I/O */
dm_requeue_unmapped_request(clone);
+ requeued = 1;
break;
default:
if (r > 0) {
@@ -1636,6 +1642,8 @@ static void map_request(struct dm_target *ti, struct request *clone,
dm_kill_unmapped_request(clone, r);
break;
}
+
+ return requeued;
}
/*
@@ -1677,12 +1685,17 @@ static void dm_request_fn(struct request_queue *q)
atomic_inc(&md->pending[rq_data_dir(clone)]);
spin_unlock(q->queue_lock);
- map_request(ti, clone, md);
+ if (map_request(ti, clone, md))
+ goto requeued;
+
spin_lock_irq(q->queue_lock);
}
goto out;
+requeued:
+ spin_lock_irq(q->queue_lock);
+
plug_and_out:
if (!elv_queue_empty(q))
/* Some requests still remain, retry later */
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 00435bd2069..af2d39d603c 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -177,7 +177,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
conf->array_sectors += rdev->sectors;
cnt++;
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 32a662fc55c..4b323f45ad7 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -308,7 +308,7 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
*/
if (q->merge_bvec_fn &&
queue_max_sectors(q) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
conf->working_disks++;
mddev->degraded--;
@@ -478,7 +478,7 @@ static int multipath_run (mddev_t *mddev)
* a merge_bvec_fn to be involved in multipath */
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
if (!test_bit(Faulty, &rdev->flags))
conf->working_disks++;
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 77605cdceaf..a1f7147b757 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -182,7 +182,7 @@ static int create_strip_zones(mddev_t *mddev)
if (rdev1->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
if (!smallest || (rdev1->sectors < smallest->sectors))
smallest = rdev1;
@@ -325,7 +325,7 @@ static int raid0_run(mddev_t *mddev)
}
if (md_check_no_bitmap(mddev))
return -EINVAL;
- blk_queue_max_sectors(mddev->queue, mddev->chunk_sectors);
+ blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors);
mddev->queue->queue_lock = &mddev->queue->__queue_lock;
ret = create_strip_zones(mddev);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 859bd3ffe43..5a06122abd3 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1158,7 +1158,7 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
p->head_position = 0;
rdev->raid_disk = mirror;
@@ -2103,7 +2103,7 @@ static int run(mddev_t *mddev)
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
}
mddev->degraded = 0;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index d119b7b75e7..7584f9ab9bc 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1161,7 +1161,7 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
p->head_position = 0;
rdev->raid_disk = mirror;
@@ -2260,7 +2260,7 @@ static int run(mddev_t *mddev)
*/
if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
+ blk_queue_max_hw_sectors(mddev->queue, PAGE_SIZE>>9);
disk->head_position = 0;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index ceb24afdc14..509c8f3dd9a 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3739,7 +3739,7 @@ static int bio_fits_rdev(struct bio *bi)
if ((bi->bi_size>>9) > queue_max_sectors(q))
return 0;
blk_recount_segments(q, bi);
- if (bi->bi_phys_segments > queue_max_phys_segments(q))
+ if (bi->bi_phys_segments > queue_max_segments(q))
return 0;
if (q->merge_bvec_fn)
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index df5ddb4bbbf..171890e7a41 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -1,5 +1,5 @@
ir-common-objs := ir-functions.o ir-keymaps.o
-ir-core-objs := ir-keytable.o
+ir-core-objs := ir-keytable.o ir-sysfs.o
obj-$(CONFIG_IR_CORE) += ir-core.o
obj-$(CONFIG_VIDEO_IR) += ir-common.o
diff --git a/drivers/media/IR/ir-functions.c b/drivers/media/IR/ir-functions.c
index 776a136616d..ab06919ad5f 100644
--- a/drivers/media/IR/ir-functions.c
+++ b/drivers/media/IR/ir-functions.c
@@ -52,7 +52,7 @@ static void ir_input_key_event(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)
+ const u64 ir_type)
{
ir->ir_type = ir_type;
diff --git a/drivers/media/IR/ir-keymaps.c b/drivers/media/IR/ir-keymaps.c
index 9bbe6b1e987..0efdefe75f3 100644
--- a/drivers/media/IR/ir-keymaps.c
+++ b/drivers/media/IR/ir-keymaps.c
@@ -3393,3 +3393,102 @@ struct ir_scancode_table ir_codes_nec_terratec_cinergy_xs_table = {
};
EXPORT_SYMBOL_GPL(ir_codes_nec_terratec_cinergy_xs_table);
+
+/* Leadtek Winfast TV USB II Deluxe remote
+ Magnus Alm <magnus.alm@gmail.com>
+ */
+static struct ir_scancode ir_codes_winfast_usbii_deluxe[] = {
+ { 0x62, KEY_0},
+ { 0x75, KEY_1},
+ { 0x76, KEY_2},
+ { 0x77, KEY_3},
+ { 0x79, KEY_4},
+ { 0x7a, KEY_5},
+ { 0x7b, KEY_6},
+ { 0x7d, KEY_7},
+ { 0x7e, KEY_8},
+ { 0x7f, KEY_9},
+
+ { 0x38, KEY_CAMERA}, /* SNAPSHOT */
+ { 0x37, KEY_RECORD}, /* RECORD */
+ { 0x35, KEY_TIME}, /* TIMESHIFT */
+
+ { 0x74, KEY_VOLUMEUP}, /* VOLUMEUP */
+ { 0x78, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */
+ { 0x64, KEY_MUTE}, /* MUTE */
+
+ { 0x21, KEY_CHANNEL}, /* SURF */
+ { 0x7c, KEY_CHANNELUP}, /* CHANNELUP */
+ { 0x60, KEY_CHANNELDOWN}, /* CHANNELDOWN */
+ { 0x61, KEY_LAST}, /* LAST CHANNEL (RECALL) */
+
+ { 0x72, KEY_VIDEO}, /* INPUT MODES (TV/FM) */
+
+ { 0x70, KEY_POWER2}, /* TV ON/OFF */
+
+ { 0x39, KEY_CYCLEWINDOWS}, /* MINIMIZE (BOSS) */
+ { 0x3a, KEY_NEW}, /* PIP */
+ { 0x73, KEY_ZOOM}, /* FULLSECREEN */
+
+ { 0x66, KEY_INFO}, /* OSD (DISPLAY) */
+
+ { 0x31, KEY_DOT}, /* '.' */
+ { 0x63, KEY_ENTER}, /* ENTER */
+
+};
+struct ir_scancode_table ir_codes_winfast_usbii_deluxe_table = {
+ .scan = ir_codes_winfast_usbii_deluxe,
+ .size = ARRAY_SIZE(ir_codes_winfast_usbii_deluxe),
+};
+EXPORT_SYMBOL_GPL(ir_codes_winfast_usbii_deluxe_table);
+
+/* Kworld 315U
+ */
+static struct ir_scancode ir_codes_kworld_315u[] = {
+ { 0x6143, KEY_POWER },
+ { 0x6101, KEY_TUNER }, /* source */
+ { 0x610b, KEY_ZOOM },
+ { 0x6103, KEY_POWER2 }, /* shutdown */
+
+ { 0x6104, KEY_1 },
+ { 0x6108, KEY_2 },
+ { 0x6102, KEY_3 },
+ { 0x6109, KEY_CHANNELUP },
+
+ { 0x610f, KEY_4 },
+ { 0x6105, KEY_5 },
+ { 0x6106, KEY_6 },
+ { 0x6107, KEY_CHANNELDOWN },
+
+ { 0x610c, KEY_7 },
+ { 0x610d, KEY_8 },
+ { 0x610a, KEY_9 },
+ { 0x610e, KEY_VOLUMEUP },
+
+ { 0x6110, KEY_LAST },
+ { 0x6111, KEY_0 },
+ { 0x6112, KEY_ENTER },
+ { 0x6113, KEY_VOLUMEDOWN },
+
+ { 0x6114, KEY_RECORD },
+ { 0x6115, KEY_STOP },
+ { 0x6116, KEY_PLAY },
+ { 0x6117, KEY_MUTE },
+
+ { 0x6118, KEY_UP },
+ { 0x6119, KEY_DOWN },
+ { 0x611a, KEY_LEFT },
+ { 0x611b, KEY_RIGHT },
+
+ { 0x611c, KEY_RED },
+ { 0x611d, KEY_GREEN },
+ { 0x611e, KEY_YELLOW },
+ { 0x611f, KEY_BLUE },
+};
+
+struct ir_scancode_table ir_codes_kworld_315u_table = {
+ .scan = ir_codes_kworld_315u,
+ .size = ARRAY_SIZE(ir_codes_kworld_315u),
+ .ir_type = IR_TYPE_NEC,
+};
+EXPORT_SYMBOL_GPL(ir_codes_kworld_315u_table);
diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c
index b521ed9d6e2..0903f539bf6 100644
--- a/drivers/media/IR/ir-keytable.c
+++ b/drivers/media/IR/ir-keytable.c
@@ -65,7 +65,7 @@ exit:
* 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)
+static int ir_roundup_tablesize(int n_elems)
{
size_t size;
@@ -81,7 +81,6 @@ int ir_roundup_tablesize(int n_elems)
return n_elems;
}
-EXPORT_SYMBOL_GPL(ir_roundup_tablesize);
/**
* ir_copy_table() - copies a keytable, discarding the unused entries
@@ -89,9 +88,11 @@ EXPORT_SYMBOL_GPL(ir_roundup_tablesize);
* @origin: origin table
*
* Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED
+ * Also copies table size and table protocol.
+ * NOTE: It shouldn't copy the lock field
*/
-int ir_copy_table(struct ir_scancode_table *destin,
+static int ir_copy_table(struct ir_scancode_table *destin,
const struct ir_scancode_table *origin)
{
int i, j = 0;
@@ -105,12 +106,12 @@ int ir_copy_table(struct ir_scancode_table *destin,
j++;
}
destin->size = j;
+ destin->ir_type = origin->ir_type;
IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size);
return 0;
}
-EXPORT_SYMBOL_GPL(ir_copy_table);
/**
* ir_getkeycode() - get a keycode at the evdev scancode ->keycode table
@@ -184,18 +185,14 @@ static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem)
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;
+ struct ir_scancode *newkeymap = NULL;
- if (resize) {
+ 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) {
+ /* There's no memory for resize. Keep the old table */
+ if (!resize || !newkeymap) {
newkeymap = oldkeymap;
/* We'll modify the live table. Lock it */
@@ -399,12 +396,14 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table);
* @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.
+ * This routine is used to initialize the input infrastructure
+ * to work with an IR.
+ * It will register the input/evdev interface for the device and
+ * register the syfs code for IR class
*/
int ir_input_register(struct input_dev *input_dev,
- struct ir_scancode_table *rc_tab)
+ const struct ir_scancode_table *rc_tab,
+ const struct ir_dev_props *props)
{
struct ir_input_dev *ir_dev;
struct ir_scancode *keymap = rc_tab->scan;
@@ -417,19 +416,22 @@ int ir_input_register(struct input_dev *input_dev,
if (!ir_dev)
return -ENOMEM;
- spin_lock_init(&rc_tab->lock);
+ spin_lock_init(&ir_dev->rc_tab.lock);
ir_dev->rc_tab.size = ir_roundup_tablesize(rc_tab->size);
ir_dev->rc_tab.scan = kzalloc(ir_dev->rc_tab.size *
sizeof(struct ir_scancode), GFP_KERNEL);
- if (!ir_dev->rc_tab.scan)
+ if (!ir_dev->rc_tab.scan) {
+ kfree(ir_dev);
return -ENOMEM;
+ }
IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n",
ir_dev->rc_tab.size,
ir_dev->rc_tab.size * sizeof(ir_dev->rc_tab.scan));
ir_copy_table(&ir_dev->rc_tab, rc_tab);
+ ir_dev->props = props;
/* set the bits for the keys */
IR_dprintk(1, "key map size: %d\n", rc_tab->size);
@@ -447,16 +449,31 @@ int ir_input_register(struct input_dev *input_dev,
input_set_drvdata(input_dev, ir_dev);
rc = input_register_device(input_dev);
+ if (rc < 0)
+ goto err;
+
+ rc = ir_register_class(input_dev);
if (rc < 0) {
- kfree(rc_tab->scan);
- kfree(ir_dev);
- input_set_drvdata(input_dev, NULL);
+ input_unregister_device(input_dev);
+ goto err;
}
+ return 0;
+
+err:
+ kfree(rc_tab->scan);
+ kfree(ir_dev);
+ input_set_drvdata(input_dev, NULL);
return rc;
}
EXPORT_SYMBOL_GPL(ir_input_register);
+/**
+ * ir_input_unregister() - unregisters IR and frees resources
+ * @input_dev: the struct input_dev descriptor of the device
+
+ * This routine is used to free memory and de-register interfaces.
+ */
void ir_input_unregister(struct input_dev *dev)
{
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
@@ -472,6 +489,8 @@ void ir_input_unregister(struct input_dev *dev)
kfree(rc_tab->scan);
rc_tab->scan = NULL;
+ ir_unregister_class(dev);
+
kfree(ir_dev);
input_unregister_device(dev);
}
diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c
new file mode 100644
index 00000000000..bf5fbcd8423
--- /dev/null
+++ b/drivers/media/IR/ir-sysfs.c
@@ -0,0 +1,211 @@
+/* ir-register.c - handle IR scancode->keycode tables
+ *
+ * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <media/ir-core.h>
+
+#define IRRCV_NUM_DEVICES 256
+
+/* bit array to represent IR sysfs device number */
+static unsigned long ir_core_dev_number;
+
+/* class for /sys/class/irrcv */
+static struct class *ir_input_class;
+
+/**
+ * show_protocol() - shows the current IR protocol
+ * @d: the device descriptor
+ * @mattr: the device attribute struct (unused)
+ * @buf: a pointer to the output buffer
+ *
+ * This routine is a callback routine for input read the IR protocol type.
+ * it is trigged by reading /sys/class/irrcv/irrcv?/current_protocol.
+ * It returns the protocol name, as understood by the driver.
+ */
+static ssize_t show_protocol(struct device *d,
+ struct device_attribute *mattr, char *buf)
+{
+ char *s;
+ struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+ u64 ir_type = ir_dev->rc_tab.ir_type;
+
+ IR_dprintk(1, "Current protocol is %lld\n", (long long)ir_type);
+
+ /* FIXME: doesn't support multiple protocols at the same time */
+ if (ir_type == IR_TYPE_UNKNOWN)
+ s = "Unknown";
+ else if (ir_type == IR_TYPE_RC5)
+ s = "RC-5";
+ else if (ir_type == IR_TYPE_PD)
+ s = "Pulse/distance";
+ else if (ir_type == IR_TYPE_NEC)
+ s = "NEC";
+ else
+ s = "Other";
+
+ return sprintf(buf, "%s\n", s);
+}
+
+/**
+ * store_protocol() - shows the current IR protocol
+ * @d: the device descriptor
+ * @mattr: the device attribute struct (unused)
+ * @buf: a pointer to the input buffer
+ * @len: length of the input buffer
+ *
+ * This routine is a callback routine for changing the IR protocol type.
+ * it is trigged by reading /sys/class/irrcv/irrcv?/current_protocol.
+ * It changes the IR the protocol name, if the IR type is recognized
+ * by the driver.
+ * If an unknown protocol name is used, returns -EINVAL.
+ */
+static ssize_t store_protocol(struct device *d,
+ struct device_attribute *mattr,
+ const char *data,
+ size_t len)
+{
+ struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+ u64 ir_type = IR_TYPE_UNKNOWN;
+ int rc = -EINVAL;
+ unsigned long flags;
+ char *buf;
+
+ buf = strsep((char **) &data, "\n");
+
+ if (!strcasecmp(buf, "rc-5"))
+ ir_type = IR_TYPE_RC5;
+ else if (!strcasecmp(buf, "pd"))
+ ir_type = IR_TYPE_PD;
+ else if (!strcasecmp(buf, "nec"))
+ ir_type = IR_TYPE_NEC;
+
+ if (ir_type == IR_TYPE_UNKNOWN) {
+ IR_dprintk(1, "Error setting protocol to %lld\n",
+ (long long)ir_type);
+ return -EINVAL;
+ }
+
+ if (ir_dev->props && ir_dev->props->change_protocol)
+ rc = ir_dev->props->change_protocol(ir_dev->props->priv,
+ ir_type);
+
+ if (rc < 0) {
+ IR_dprintk(1, "Error setting protocol to %lld\n",
+ (long long)ir_type);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ir_dev->rc_tab.lock, flags);
+ ir_dev->rc_tab.ir_type = ir_type;
+ spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags);
+
+ IR_dprintk(1, "Current protocol is %lld\n",
+ (long long)ir_type);
+
+ return len;
+}
+
+/*
+ * Static device attribute struct with the sysfs attributes for IR's
+ */
+static DEVICE_ATTR(current_protocol, S_IRUGO | S_IWUSR,
+ show_protocol, store_protocol);
+
+static struct attribute *ir_dev_attrs[] = {
+ &dev_attr_current_protocol.attr,
+ NULL,
+};
+
+/**
+ * ir_register_class() - creates the sysfs for /sys/class/irrcv/irrcv?
+ * @input_dev: the struct input_dev descriptor of the device
+ *
+ * This routine is used to register the syfs code for IR class
+ */
+int ir_register_class(struct input_dev *input_dev)
+{
+ int rc;
+ struct kobject *kobj;
+
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+ int devno = find_first_zero_bit(&ir_core_dev_number,
+ IRRCV_NUM_DEVICES);
+
+ if (unlikely(devno < 0))
+ return devno;
+
+ ir_dev->attr.attrs = ir_dev_attrs;
+ ir_dev->class_dev = device_create(ir_input_class, NULL,
+ input_dev->dev.devt, ir_dev,
+ "irrcv%d", devno);
+ kobj = &ir_dev->class_dev->kobj;
+
+ printk(KERN_WARNING "Creating IR device %s\n", kobject_name(kobj));
+ rc = sysfs_create_group(kobj, &ir_dev->attr);
+ if (unlikely(rc < 0)) {
+ device_destroy(ir_input_class, input_dev->dev.devt);
+ return -ENOMEM;
+ }
+
+ ir_dev->devno = devno;
+ set_bit(devno, &ir_core_dev_number);
+
+ return 0;
+};
+
+/**
+ * ir_unregister_class() - removes the sysfs for sysfs for
+ * /sys/class/irrcv/irrcv?
+ * @input_dev: the struct input_dev descriptor of the device
+ *
+ * This routine is used to unregister the syfs code for IR class
+ */
+void ir_unregister_class(struct input_dev *input_dev)
+{
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+ struct kobject *kobj;
+
+ clear_bit(ir_dev->devno, &ir_core_dev_number);
+
+ kobj = &ir_dev->class_dev->kobj;
+
+ sysfs_remove_group(kobj, &ir_dev->attr);
+ device_destroy(ir_input_class, input_dev->dev.devt);
+
+ kfree(ir_dev->attr.name);
+}
+
+/*
+ * Init/exit code for the module. Basically, creates/removes /sys/class/irrcv
+ */
+
+static int __init ir_core_init(void)
+{
+ ir_input_class = class_create(THIS_MODULE, "irrcv");
+ if (IS_ERR(ir_input_class)) {
+ printk(KERN_ERR "ir_core: unable to register irrcv class\n");
+ return PTR_ERR(ir_input_class);
+ }
+
+ return 0;
+}
+
+static void __exit ir_core_exit(void)
+{
+ class_destroy(ir_input_class);
+}
+
+module_init(ir_core_init);
+module_exit(ir_core_exit);
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 7364b9642d0..fd8e1f45be3 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -423,14 +423,15 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
}
}
+int saa7146_vv_devinit(struct saa7146_dev *dev)
+{
+ return v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_devinit);
+
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
{
struct saa7146_vv *vv;
- int err;
-
- err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
- if (err)
- return err;
vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
if (vv == NULL) {
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index 2b876f3988c..d9aaaca620c 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -1337,6 +1337,22 @@ static struct tuner_params tuner_philips_cu1216l_params[] = {
},
};
+/* ---------------------- TUNER_SONY_BTF_PXN01Z ------------------------ */
+
+static struct tuner_range tuner_sony_btf_pxn01z_ranges[] = {
+ { 16 * 137.25 /*MHz*/, 0x8e, 0x01, },
+ { 16 * 367.25 /*MHz*/, 0x8e, 0x02, },
+ { 16 * 999.99 , 0x8e, 0x04, },
+};
+
+static struct tuner_params tuner_sony_btf_pxn01z_params[] = {
+ {
+ .type = TUNER_PARAM_TYPE_NTSC,
+ .ranges = tuner_sony_btf_pxn01z_ranges,
+ .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_ranges),
+ },
+};
+
/* --------------------------------------------------------------------- */
struct tunertype tuners[] = {
@@ -1805,6 +1821,11 @@ struct tunertype tuners[] = {
.name = "NXP TDA18271",
/* see tda18271-fe.c for details */
},
+ [TUNER_SONY_BTF_PXN01Z] = {
+ .name = "Sony BTF-Pxn01Z",
+ .params = tuner_sony_btf_pxn01z_params,
+ .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_params),
+ },
};
EXPORT_SYMBOL(tuners);
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index f270e605da8..be51c294b37 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -917,30 +917,68 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
* that xc2028 will be in a safe state.
* Maybe this might also be needed for DTV.
*/
- if (new_mode == T_ANALOG_TV)
+ if (new_mode == T_ANALOG_TV) {
rc = send_seq(priv, {0x00, 0x00});
- /*
- * Digital modes require an offset to adjust to the
- * proper frequency.
- * Analog modes require offset = 0
- */
- if (new_mode == T_DIGITAL_TV) {
- /* Sets the offset according with firmware */
+ /* Analog modes require offset = 0 */
+ } else {
+ /*
+ * Digital modes require an offset to adjust to the
+ * proper frequency. The offset depends on what
+ * firmware version is used.
+ */
+
+ /*
+ * Adjust to the center frequency. This is calculated by the
+ * formula: offset = 1.25MHz - BW/2
+ * For DTV 7/8, the firmware uses BW = 8000, so it needs a
+ * further adjustment to get the frequency center on VHF
+ */
if (priv->cur_fw.type & DTV6)
offset = 1750000;
else if (priv->cur_fw.type & DTV7)
offset = 2250000;
else /* DTV8 or DTV78 */
offset = 2750000;
+ if ((priv->cur_fw.type & DTV78) && freq < 470000000)
+ offset -= 500000;
/*
- * We must adjust the offset by 500kHz when
- * tuning a 7MHz VHF channel with DTV78 firmware
- * (used in Australia, Italy and Germany)
+ * xc3028 additional "magic"
+ * Depending on the firmware version, it needs some adjustments
+ * to properly centralize the frequency. This seems to be
+ * needed to compensate the SCODE table adjustments made by
+ * newer firmwares
*/
- if ((priv->cur_fw.type & DTV78) && freq < 470000000)
- offset -= 500000;
+
+#if 1
+ /*
+ * The proper adjustment would be to do it at s-code table.
+ * However, this didn't work, as reported by
+ * Robert Lowery <rglowery@exemail.com.au>
+ */
+
+ if (priv->cur_fw.type & DTV7)
+ offset += 500000;
+
+#else
+ /*
+ * Still need tests for XC3028L (firmware 3.2 or upper)
+ * So, for now, let's just comment the per-firmware
+ * version of this change. Reports with xc3028l working
+ * with and without the lines bellow are welcome
+ */
+
+ if (priv->firm_version < 0x0302) {
+ if (priv->cur_fw.type & DTV7)
+ offset += 500000;
+ } else {
+ if (priv->cur_fw.type & DTV7)
+ offset -= 300000;
+ else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */
+ offset += 200000;
+ }
+#endif
}
div = (freq - offset + DIV / 2) / DIV;
@@ -1097,17 +1135,24 @@ static int xc2028_set_params(struct dvb_frontend *fe,
/* All S-code tables need a 200kHz shift */
if (priv->ctrl.demod) {
- demod = priv->ctrl.demod + 200;
+ demod = priv->ctrl.demod;
+
+ /*
+ * Newer firmwares require a 200 kHz offset only for ATSC
+ */
+ if (type == ATSC || priv->firm_version < 0x0302)
+ demod += 200;
/*
* The DTV7 S-code table needs a 700 kHz shift.
- * Thanks to Terry Wu <terrywu2009@gmail.com> for reporting this
*
* DTV7 is only used in Australia. Germany or Italy may also
* use this firmware after initialization, but a tune to a UHF
* channel should then cause DTV78 to be used.
+ *
+ * Unfortunately, on real-field tests, the s-code offset
+ * didn't work as expected, as reported by
+ * Robert Lowery <rglowery@exemail.com.au>
*/
- if (type & DTV7)
- demod += 500;
}
return generic_set_freq(fe, p->frequency,
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index cf8f65f309d..161ccfd471c 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -76,6 +76,10 @@ comment "Supported Mantis Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/mantis/Kconfig"
+comment "Supported nGene Adapters"
+ depends on DVB_CORE && PCI && I2C
+ source "drivers/media/dvb/ngene/Kconfig"
+
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index c12922c3659..a1a08758a6f 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -14,6 +14,7 @@ obj-y := dvb-core/ \
siano/ \
dm1105/ \
pt1/ \
- mantis/
+ mantis/ \
+ ngene/
obj-$(CONFIG_DVB_FIREDTV) += firewire/
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c
index a24c125331f..99d62094f90 100644
--- a/drivers/media/dvb/bt8xx/bt878.c
+++ b/drivers/media/dvb/bt8xx/bt878.c
@@ -576,43 +576,30 @@ static struct pci_driver bt878_pci_driver = {
.remove = __devexit_p(bt878_remove),
};
-static int bt878_pci_driver_registered;
-
/*******************************/
/* Module management functions */
/*******************************/
-static int bt878_init_module(void)
+static int __init bt878_init_module(void)
{
bt878_num = 0;
- bt878_pci_driver_registered = 0;
printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n",
(BT878_VERSION_CODE >> 16) & 0xff,
(BT878_VERSION_CODE >> 8) & 0xff,
BT878_VERSION_CODE & 0xff);
-/*
- bt878_check_chipset();
-*/
- /* later we register inside of bt878_find_audio_dma()
- * because we may want to ignore certain cards */
- bt878_pci_driver_registered = 1;
+
return pci_register_driver(&bt878_pci_driver);
}
-static void bt878_cleanup_module(void)
+static void __exit bt878_cleanup_module(void)
{
- if (bt878_pci_driver_registered) {
- bt878_pci_driver_registered = 0;
- pci_unregister_driver(&bt878_pci_driver);
- }
- return;
+ pci_unregister_driver(&bt878_pci_driver);
}
module_init(bt878_init_module);
module_exit(bt878_cleanup_module);
-//MODULE_AUTHOR("XXX");
MODULE_LICENSE("GPL");
/*
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
index 91353a6faf1..8b0cde38984 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -1352,8 +1352,7 @@ static int dst_get_tuna(struct dst_state *state)
return retval;
}
if ((state->type_flags & DST_TYPE_HAS_VLF) &&
- !(state->dst_type == DST_TYPE_IS_CABLE) &&
- !(state->dst_type == DST_TYPE_IS_ATSC)) {
+ !(state->dst_type == DST_TYPE_IS_ATSC)) {
if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) {
dprintk(verbose, DST_INFO, 1, "checksum failure ? ");
@@ -1820,8 +1819,13 @@ static struct dvb_frontend_ops dst_dvbc_ops = {
.frequency_max = 858000000,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
- /* . symbol_rate_tolerance = ???,*/
- .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256
},
.release = dst_release,
diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig
index de3eeb0a8d6..695239227cb 100644
--- a/drivers/media/dvb/dm1105/Kconfig
+++ b/drivers/media/dvb/dm1105/Kconfig
@@ -8,6 +8,7 @@ config DVB_DM1105
select DVB_STB6000 if !DVB_FE_CUSTOMISE
select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_SI21XX if !DVB_FE_CUSTOMISE
+ select DVB_DS3000 if !DVB_FE_CUSTOMISE
select VIDEO_IR
help
Support for cards based on the SDMC DM1105 PCI chip like
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index f0f483ac8b8..383cca378b8 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -43,6 +43,7 @@
#include "si21xx.h"
#include "cx24116.h"
#include "z0194a.h"
+#include "ds3000.h"
#define UNSET (-1U)
@@ -269,7 +270,7 @@ struct infrared {
u32 ir_command;
};
-struct dm1105dvb {
+struct dm1105_dev {
/* pci */
struct pci_dev *pdev;
u8 __iomem *io_mem;
@@ -308,31 +309,47 @@ struct dm1105dvb {
spinlock_t lock;
};
-#define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg]))
+#define dm_io_mem(reg) ((unsigned long)(&dev->io_mem[reg]))
+
+#define dm_readb(reg) inb(dm_io_mem(reg))
+#define dm_writeb(reg, value) outb((value), (dm_io_mem(reg)))
+
+#define dm_readw(reg) inw(dm_io_mem(reg))
+#define dm_writew(reg, value) outw((value), (dm_io_mem(reg)))
+
+#define dm_readl(reg) inl(dm_io_mem(reg))
+#define dm_writel(reg, value) outl((value), (dm_io_mem(reg)))
+
+#define dm_andorl(reg, mask, value) \
+ outl((inl(dm_io_mem(reg)) & ~(mask)) |\
+ ((value) & (mask)), (dm_io_mem(reg)))
+
+#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit))
+#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0)
static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, int num)
{
- struct dm1105dvb *dm1105dvb ;
+ struct dm1105_dev *dev ;
int addr, rc, i, j, k, len, byte, data;
u8 status;
- dm1105dvb = i2c_adap->algo_data;
+ dev = i2c_adap->algo_data;
for (i = 0; i < num; i++) {
- outb(0x00, dm_io_mem(DM1105_I2CCTR));
+ dm_writeb(DM1105_I2CCTR, 0x00);
if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
addr = msgs[i].addr << 1;
addr |= 1;
- outb(addr, dm_io_mem(DM1105_I2CDAT));
+ dm_writeb(DM1105_I2CDAT, addr);
for (byte = 0; byte < msgs[i].len; byte++)
- outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1));
+ dm_writeb(DM1105_I2CDAT + byte + 1, 0);
- outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+ dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len);
for (j = 0; j < 55; j++) {
mdelay(10);
- status = inb(dm_io_mem(DM1105_I2CSTS));
+ status = dm_readb(DM1105_I2CSTS);
if ((status & 0xc0) == 0x40)
break;
}
@@ -340,56 +357,54 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
return -1;
for (byte = 0; byte < msgs[i].len; byte++) {
- rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1));
+ rc = dm_readb(DM1105_I2CDAT + byte + 1);
if (rc < 0)
goto err;
msgs[i].buf[byte] = rc;
}
- } else {
- if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
- /* prepaired for cx24116 firmware */
- /* Write in small blocks */
- len = msgs[i].len - 1;
- k = 1;
- do {
- outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT));
- outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1));
- for (byte = 0; byte < (len > 48 ? 48 : len); byte++) {
- data = msgs[i].buf[k+byte];
- outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2));
- }
- outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR));
- for (j = 0; j < 25; j++) {
- mdelay(10);
- status = inb(dm_io_mem(DM1105_I2CSTS));
- if ((status & 0xc0) == 0x40)
- break;
- }
-
- if (j >= 25)
- return -1;
-
- k += 48;
- len -= 48;
- } while (len > 0);
- } else {
- /* write bytes */
- outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT));
- for (byte = 0; byte < msgs[i].len; byte++) {
- data = msgs[i].buf[byte];
- outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1));
+ } else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
+ /* prepaired for cx24116 firmware */
+ /* Write in small blocks */
+ len = msgs[i].len - 1;
+ k = 1;
+ do {
+ dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1);
+ dm_writeb(DM1105_I2CDAT + 1, 0xf7);
+ for (byte = 0; byte < (len > 48 ? 48 : len); byte++) {
+ data = msgs[i].buf[k + byte];
+ dm_writeb(DM1105_I2CDAT + byte + 2, data);
}
- outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+ dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len));
for (j = 0; j < 25; j++) {
mdelay(10);
- status = inb(dm_io_mem(DM1105_I2CSTS));
+ status = dm_readb(DM1105_I2CSTS);
if ((status & 0xc0) == 0x40)
break;
}
if (j >= 25)
return -1;
+
+ k += 48;
+ len -= 48;
+ } while (len > 0);
+ } else {
+ /* write bytes */
+ dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1);
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ data = msgs[i].buf[byte];
+ dm_writeb(DM1105_I2CDAT + byte + 1, data);
}
+ dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len);
+ for (j = 0; j < 25; j++) {
+ mdelay(10);
+ status = dm_readb(DM1105_I2CSTS);
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+
+ if (j >= 25)
+ return -1;
}
}
return num;
@@ -407,22 +422,22 @@ static struct i2c_algorithm dm1105_algo = {
.functionality = functionality,
};
-static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed)
+static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed)
{
- return container_of(feed->demux, struct dm1105dvb, demux);
+ return container_of(feed->demux, struct dm1105_dev, demux);
}
-static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe)
+static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe)
{
- return container_of(fe->dvb, struct dm1105dvb, dvb_adapter);
+ return container_of(fe->dvb, struct dm1105_dev, dvb_adapter);
}
-static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
- struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe);
+ struct dm1105_dev *dev = frontend_to_dm1105_dev(fe);
u32 lnb_mask, lnb_13v, lnb_18v, lnb_off;
- switch (dm1105dvb->boardnr) {
+ switch (dev->boardnr) {
case DM1105_BOARD_AXESS_DM05:
lnb_mask = DM05_LNB_MASK;
lnb_off = DM05_LNB_OFF;
@@ -438,62 +453,67 @@ static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volta
lnb_18v = DM1105_LNB_18V;
}
- outl(lnb_mask, dm_io_mem(DM1105_GPIOCTR));
+ dm_writel(DM1105_GPIOCTR, lnb_mask);
if (voltage == SEC_VOLTAGE_18)
- outl(lnb_18v , dm_io_mem(DM1105_GPIOVAL));
+ dm_writel(DM1105_GPIOVAL, lnb_18v);
else if (voltage == SEC_VOLTAGE_13)
- outl(lnb_13v, dm_io_mem(DM1105_GPIOVAL));
+ dm_writel(DM1105_GPIOVAL, lnb_13v);
else
- outl(lnb_off, dm_io_mem(DM1105_GPIOVAL));
+ dm_writel(DM1105_GPIOVAL, lnb_off);
return 0;
}
-static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb)
+static void dm1105_set_dma_addr(struct dm1105_dev *dev)
{
- outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR));
+ dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr));
}
-static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb)
+static int __devinit dm1105_dma_map(struct dm1105_dev *dev)
{
- dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr);
+ dev->ts_buf = pci_alloc_consistent(dev->pdev,
+ 6 * DM1105_DMA_BYTES,
+ &dev->dma_addr);
- return !dm1105dvb->ts_buf;
+ return !dev->ts_buf;
}
-static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb)
+static void dm1105_dma_unmap(struct dm1105_dev *dev)
{
- pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr);
+ pci_free_consistent(dev->pdev,
+ 6 * DM1105_DMA_BYTES,
+ dev->ts_buf,
+ dev->dma_addr);
}
-static void dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb)
+static void dm1105_enable_irqs(struct dm1105_dev *dev)
{
- outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK));
- outb(1, dm_io_mem(DM1105_CR));
+ dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK);
+ dm_writeb(DM1105_CR, 1);
}
-static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb)
+static void dm1105_disable_irqs(struct dm1105_dev *dev)
{
- outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK));
- outb(0, dm_io_mem(DM1105_CR));
+ dm_writeb(DM1105_INTMAK, INTMAK_IRM);
+ dm_writeb(DM1105_CR, 0);
}
-static int dm1105dvb_start_feed(struct dvb_demux_feed *f)
+static int dm1105_start_feed(struct dvb_demux_feed *f)
{
- struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+ struct dm1105_dev *dev = feed_to_dm1105_dev(f);
- if (dm1105dvb->full_ts_users++ == 0)
- dm1105dvb_enable_irqs(dm1105dvb);
+ if (dev->full_ts_users++ == 0)
+ dm1105_enable_irqs(dev);
return 0;
}
-static int dm1105dvb_stop_feed(struct dvb_demux_feed *f)
+static int dm1105_stop_feed(struct dvb_demux_feed *f)
{
- struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+ struct dm1105_dev *dev = feed_to_dm1105_dev(f);
- if (--dm1105dvb->full_ts_users == 0)
- dm1105dvb_disable_irqs(dm1105dvb);
+ if (--dev->full_ts_users == 0)
+ dm1105_disable_irqs(dev);
return 0;
}
@@ -517,68 +537,64 @@ static void dm1105_emit_key(struct work_struct *work)
/* work handler */
static void dm1105_dmx_buffer(struct work_struct *work)
{
- struct dm1105dvb *dm1105dvb =
- container_of(work, struct dm1105dvb, work);
+ struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work);
unsigned int nbpackets;
- u32 oldwrp = dm1105dvb->wrp;
- u32 nextwrp = dm1105dvb->nextwrp;
+ u32 oldwrp = dev->wrp;
+ u32 nextwrp = dev->nextwrp;
- if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
- (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
- (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
- dm1105dvb->PacketErrorCount++;
+ if (!((dev->ts_buf[oldwrp] == 0x47) &&
+ (dev->ts_buf[oldwrp + 188] == 0x47) &&
+ (dev->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+ dev->PacketErrorCount++;
/* bad packet found */
- if ((dm1105dvb->PacketErrorCount >= 2) &&
- (dm1105dvb->dmarst == 0)) {
- outb(1, dm_io_mem(DM1105_RST));
- dm1105dvb->wrp = 0;
- dm1105dvb->PacketErrorCount = 0;
- dm1105dvb->dmarst = 0;
+ if ((dev->PacketErrorCount >= 2) &&
+ (dev->dmarst == 0)) {
+ dm_writeb(DM1105_RST, 1);
+ dev->wrp = 0;
+ dev->PacketErrorCount = 0;
+ dev->dmarst = 0;
return;
}
}
if (nextwrp < oldwrp) {
- memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size,
- dm1105dvb->ts_buf, nextwrp);
- nbpackets = ((dm1105dvb->buffer_size - oldwrp) + nextwrp) / 188;
+ memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp);
+ nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188;
} else
nbpackets = (nextwrp - oldwrp) / 188;
- dm1105dvb->wrp = nextwrp;
- dvb_dmx_swfilter_packets(&dm1105dvb->demux,
- &dm1105dvb->ts_buf[oldwrp], nbpackets);
+ dev->wrp = nextwrp;
+ dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets);
}
-static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
+static irqreturn_t dm1105_irq(int irq, void *dev_id)
{
- struct dm1105dvb *dm1105dvb = dev_id;
+ struct dm1105_dev *dev = dev_id;
/* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
- unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS));
- outb(intsts, dm_io_mem(DM1105_INTSTS));
+ unsigned int intsts = dm_readb(DM1105_INTSTS);
+ dm_writeb(DM1105_INTSTS, intsts);
switch (intsts) {
case INTSTS_TSIRQ:
case (INTSTS_TSIRQ | INTSTS_IR):
- dm1105dvb->nextwrp = inl(dm_io_mem(DM1105_WRP)) -
- inl(dm_io_mem(DM1105_STADR));
- queue_work(dm1105dvb->wq, &dm1105dvb->work);
+ dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR);
+ queue_work(dev->wq, &dev->work);
break;
case INTSTS_IR:
- dm1105dvb->ir.ir_command = inl(dm_io_mem(DM1105_IRCODE));
- schedule_work(&dm1105dvb->ir.work);
+ dev->ir.ir_command = dm_readl(DM1105_IRCODE);
+ schedule_work(&dev->ir.work);
break;
}
return IRQ_HANDLED;
}
-int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+int __devinit dm1105_ir_init(struct dm1105_dev *dm1105)
{
struct input_dev *input_dev;
struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table;
- int ir_type = IR_TYPE_OTHER;
+ u64 ir_type = IR_TYPE_OTHER;
int err = -ENOMEM;
input_dev = input_allocate_device();
@@ -611,51 +627,51 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
INIT_WORK(&dm1105->ir.work, dm1105_emit_key);
- err = ir_input_register(input_dev, ir_codes);
+ err = ir_input_register(input_dev, ir_codes, NULL);
return err;
}
-void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
+void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105)
{
ir_input_unregister(dm1105->ir.input_dev);
}
-static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
+static int __devinit dm1105_hw_init(struct dm1105_dev *dev)
{
- dm1105dvb_disable_irqs(dm1105dvb);
+ dm1105_disable_irqs(dev);
- outb(0, dm_io_mem(DM1105_HOST_CTR));
+ dm_writeb(DM1105_HOST_CTR, 0);
/*DATALEN 188,*/
- outb(188, dm_io_mem(DM1105_DTALENTH));
+ dm_writeb(DM1105_DTALENTH, 188);
/*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/
- outw(0xc10a, dm_io_mem(DM1105_TSCTR));
+ dm_writew(DM1105_TSCTR, 0xc10a);
/* map DMA and set address */
- dm1105dvb_dma_map(dm1105dvb);
- dm1105dvb_set_dma_addr(dm1105dvb);
+ dm1105_dma_map(dev);
+ dm1105_set_dma_addr(dev);
/* big buffer */
- outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN));
- outb(47, dm_io_mem(DM1105_INTCNT));
+ dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES);
+ dm_writeb(DM1105_INTCNT, 47);
/* IR NEC mode enable */
- outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR));
- outb(0, dm_io_mem(DM1105_IRMODE));
- outw(0, dm_io_mem(DM1105_SYSTEMCODE));
+ dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK));
+ dm_writeb(DM1105_IRMODE, 0);
+ dm_writew(DM1105_SYSTEMCODE, 0);
return 0;
}
-static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb)
+static void dm1105_hw_exit(struct dm1105_dev *dev)
{
- dm1105dvb_disable_irqs(dm1105dvb);
+ dm1105_disable_irqs(dev);
/* IR disable */
- outb(0, dm_io_mem(DM1105_IRCTR));
- outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK));
+ dm_writeb(DM1105_IRCTR, 0);
+ dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK);
- dm1105dvb_dma_unmap(dm1105dvb);
+ dm1105_dma_unmap(dev);
}
static struct stv0299_config sharp_z0194a_config = {
@@ -685,70 +701,79 @@ static struct cx24116_config serit_sp2633_config = {
.demod_address = 0x55,
};
-static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
+static struct ds3000_config dvbworld_ds3000_config = {
+ .demod_address = 0x68,
+};
+
+static int __devinit frontend_init(struct dm1105_dev *dev)
{
int ret;
- switch (dm1105dvb->boardnr) {
+ switch (dev->boardnr) {
case DM1105_BOARD_DVBWORLD_2004:
- dm1105dvb->fe = dvb_attach(
+ dev->fe = dvb_attach(
cx24116_attach, &serit_sp2633_config,
- &dm1105dvb->i2c_adap);
- if (dm1105dvb->fe)
- dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage;
+ &dev->i2c_adap);
+ if (dev->fe) {
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
+ break;
+ }
+
+ dev->fe = dvb_attach(
+ ds3000_attach, &dvbworld_ds3000_config,
+ &dev->i2c_adap);
+ if (dev->fe)
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
break;
case DM1105_BOARD_DVBWORLD_2002:
case DM1105_BOARD_AXESS_DM05:
default:
- dm1105dvb->fe = dvb_attach(
+ dev->fe = dvb_attach(
stv0299_attach, &sharp_z0194a_config,
- &dm1105dvb->i2c_adap);
- if (dm1105dvb->fe) {
- dm1105dvb->fe->ops.set_voltage =
- dm1105dvb_set_voltage;
- dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60,
- &dm1105dvb->i2c_adap, DVB_PLL_OPERA1);
+ &dev->i2c_adap);
+ if (dev->fe) {
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
+ dvb_attach(dvb_pll_attach, dev->fe, 0x60,
+ &dev->i2c_adap, DVB_PLL_OPERA1);
break;
}
- dm1105dvb->fe = dvb_attach(
+ dev->fe = dvb_attach(
stv0288_attach, &earda_config,
- &dm1105dvb->i2c_adap);
- if (dm1105dvb->fe) {
- dm1105dvb->fe->ops.set_voltage =
- dm1105dvb_set_voltage;
- dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61,
- &dm1105dvb->i2c_adap);
+ &dev->i2c_adap);
+ if (dev->fe) {
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
+ dvb_attach(stb6000_attach, dev->fe, 0x61,
+ &dev->i2c_adap);
break;
}
- dm1105dvb->fe = dvb_attach(
+ dev->fe = dvb_attach(
si21xx_attach, &serit_config,
- &dm1105dvb->i2c_adap);
- if (dm1105dvb->fe)
- dm1105dvb->fe->ops.set_voltage =
- dm1105dvb_set_voltage;
+ &dev->i2c_adap);
+ if (dev->fe)
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
}
- if (!dm1105dvb->fe) {
- dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n");
+ if (!dev->fe) {
+ dev_err(&dev->pdev->dev, "could not attach frontend\n");
return -ENODEV;
}
- ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe);
+ ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe);
if (ret < 0) {
- if (dm1105dvb->fe->ops.release)
- dm1105dvb->fe->ops.release(dm1105dvb->fe);
- dm1105dvb->fe = NULL;
+ if (dev->fe->ops.release)
+ dev->fe->ops.release(dev->fe);
+ dev->fe = NULL;
return ret;
}
return 0;
}
-static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac)
+static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac)
{
static u8 command[1] = { 0x28 };
@@ -766,47 +791,47 @@ static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac)
},
};
- dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2);
- dev_info(&dm1105dvb->pdev->dev, "MAC %pM\n", mac);
+ dm1105_i2c_xfer(&dev->i2c_adap, msg , 2);
+ dev_info(&dev->pdev->dev, "MAC %pM\n", mac);
}
static int __devinit dm1105_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- struct dm1105dvb *dm1105dvb;
+ struct dm1105_dev *dev;
struct dvb_adapter *dvb_adapter;
struct dvb_demux *dvbdemux;
struct dmx_demux *dmx;
int ret = -ENOMEM;
int i;
- dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
- if (!dm1105dvb)
+ dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL);
+ if (!dev)
return -ENOMEM;
/* board config */
- dm1105dvb->nr = dm1105_devcount;
- dm1105dvb->boardnr = UNSET;
- if (card[dm1105dvb->nr] < ARRAY_SIZE(dm1105_boards))
- dm1105dvb->boardnr = card[dm1105dvb->nr];
- for (i = 0; UNSET == dm1105dvb->boardnr &&
+ dev->nr = dm1105_devcount;
+ dev->boardnr = UNSET;
+ if (card[dev->nr] < ARRAY_SIZE(dm1105_boards))
+ dev->boardnr = card[dev->nr];
+ for (i = 0; UNSET == dev->boardnr &&
i < ARRAY_SIZE(dm1105_subids); i++)
if (pdev->subsystem_vendor ==
dm1105_subids[i].subvendor &&
pdev->subsystem_device ==
dm1105_subids[i].subdevice)
- dm1105dvb->boardnr = dm1105_subids[i].card;
+ dev->boardnr = dm1105_subids[i].card;
- if (UNSET == dm1105dvb->boardnr) {
- dm1105dvb->boardnr = DM1105_BOARD_UNKNOWN;
+ if (UNSET == dev->boardnr) {
+ dev->boardnr = DM1105_BOARD_UNKNOWN;
dm1105_card_list(pdev);
}
dm1105_devcount++;
- dm1105dvb->pdev = pdev;
- dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
- dm1105dvb->PacketErrorCount = 0;
- dm1105dvb->dmarst = 0;
+ dev->pdev = pdev;
+ dev->buffer_size = 5 * DM1105_DMA_BYTES;
+ dev->PacketErrorCount = 0;
+ dev->dmarst = 0;
ret = pci_enable_device(pdev);
if (ret < 0)
@@ -822,47 +847,47 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
if (ret < 0)
goto err_pci_disable_device;
- dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
- if (!dm1105dvb->io_mem) {
+ dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ if (!dev->io_mem) {
ret = -EIO;
goto err_pci_release_regions;
}
- spin_lock_init(&dm1105dvb->lock);
- pci_set_drvdata(pdev, dm1105dvb);
+ spin_lock_init(&dev->lock);
+ pci_set_drvdata(pdev, dev);
- ret = dm1105dvb_hw_init(dm1105dvb);
+ ret = dm1105_hw_init(dev);
if (ret < 0)
goto err_pci_iounmap;
/* i2c */
- i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb);
- strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME);
- dm1105dvb->i2c_adap.owner = THIS_MODULE;
- dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
- dm1105dvb->i2c_adap.dev.parent = &pdev->dev;
- dm1105dvb->i2c_adap.algo = &dm1105_algo;
- dm1105dvb->i2c_adap.algo_data = dm1105dvb;
- ret = i2c_add_adapter(&dm1105dvb->i2c_adap);
+ i2c_set_adapdata(&dev->i2c_adap, dev);
+ strcpy(dev->i2c_adap.name, DRIVER_NAME);
+ dev->i2c_adap.owner = THIS_MODULE;
+ dev->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+ dev->i2c_adap.dev.parent = &pdev->dev;
+ dev->i2c_adap.algo = &dm1105_algo;
+ dev->i2c_adap.algo_data = dev;
+ ret = i2c_add_adapter(&dev->i2c_adap);
if (ret < 0)
- goto err_dm1105dvb_hw_exit;
+ goto err_dm1105_hw_exit;
/* dvb */
- ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME,
+ ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME,
THIS_MODULE, &pdev->dev, adapter_nr);
if (ret < 0)
goto err_i2c_del_adapter;
- dvb_adapter = &dm1105dvb->dvb_adapter;
+ dvb_adapter = &dev->dvb_adapter;
- dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac);
+ dm1105_read_mac(dev, dvb_adapter->proposed_mac);
- dvbdemux = &dm1105dvb->demux;
+ dvbdemux = &dev->demux;
dvbdemux->filternum = 256;
dvbdemux->feednum = 256;
- dvbdemux->start_feed = dm1105dvb_start_feed;
- dvbdemux->stop_feed = dm1105dvb_stop_feed;
+ dvbdemux->start_feed = dm1105_start_feed;
+ dvbdemux->stop_feed = dm1105_stop_feed;
dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
ret = dvb_dmx_init(dvbdemux);
@@ -870,113 +895,113 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
goto err_dvb_unregister_adapter;
dmx = &dvbdemux->dmx;
- dm1105dvb->dmxdev.filternum = 256;
- dm1105dvb->dmxdev.demux = dmx;
- dm1105dvb->dmxdev.capabilities = 0;
+ dev->dmxdev.filternum = 256;
+ dev->dmxdev.demux = dmx;
+ dev->dmxdev.capabilities = 0;
- ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter);
+ ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter);
if (ret < 0)
goto err_dvb_dmx_release;
- dm1105dvb->hw_frontend.source = DMX_FRONTEND_0;
+ dev->hw_frontend.source = DMX_FRONTEND_0;
- ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend);
+ ret = dmx->add_frontend(dmx, &dev->hw_frontend);
if (ret < 0)
goto err_dvb_dmxdev_release;
- dm1105dvb->mem_frontend.source = DMX_MEMORY_FE;
+ dev->mem_frontend.source = DMX_MEMORY_FE;
- ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend);
+ ret = dmx->add_frontend(dmx, &dev->mem_frontend);
if (ret < 0)
goto err_remove_hw_frontend;
- ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend);
+ ret = dmx->connect_frontend(dmx, &dev->hw_frontend);
if (ret < 0)
goto err_remove_mem_frontend;
- ret = frontend_init(dm1105dvb);
+ ret = frontend_init(dev);
if (ret < 0)
goto err_disconnect_frontend;
- dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx);
- dm1105_ir_init(dm1105dvb);
+ dvb_net_init(dvb_adapter, &dev->dvbnet, dmx);
+ dm1105_ir_init(dev);
- INIT_WORK(&dm1105dvb->work, dm1105_dmx_buffer);
- sprintf(dm1105dvb->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num);
- dm1105dvb->wq = create_singlethread_workqueue(dm1105dvb->wqn);
- if (!dm1105dvb->wq)
+ INIT_WORK(&dev->work, dm1105_dmx_buffer);
+ sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num);
+ dev->wq = create_singlethread_workqueue(dev->wqn);
+ if (!dev->wq)
goto err_dvb_net;
- ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED,
- DRIVER_NAME, dm1105dvb);
+ ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED,
+ DRIVER_NAME, dev);
if (ret < 0)
goto err_workqueue;
return 0;
err_workqueue:
- destroy_workqueue(dm1105dvb->wq);
+ destroy_workqueue(dev->wq);
err_dvb_net:
- dvb_net_release(&dm1105dvb->dvbnet);
+ dvb_net_release(&dev->dvbnet);
err_disconnect_frontend:
dmx->disconnect_frontend(dmx);
err_remove_mem_frontend:
- dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+ dmx->remove_frontend(dmx, &dev->mem_frontend);
err_remove_hw_frontend:
- dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+ dmx->remove_frontend(dmx, &dev->hw_frontend);
err_dvb_dmxdev_release:
- dvb_dmxdev_release(&dm1105dvb->dmxdev);
+ dvb_dmxdev_release(&dev->dmxdev);
err_dvb_dmx_release:
dvb_dmx_release(dvbdemux);
err_dvb_unregister_adapter:
dvb_unregister_adapter(dvb_adapter);
err_i2c_del_adapter:
- i2c_del_adapter(&dm1105dvb->i2c_adap);
-err_dm1105dvb_hw_exit:
- dm1105dvb_hw_exit(dm1105dvb);
+ i2c_del_adapter(&dev->i2c_adap);
+err_dm1105_hw_exit:
+ dm1105_hw_exit(dev);
err_pci_iounmap:
- pci_iounmap(pdev, dm1105dvb->io_mem);
+ pci_iounmap(pdev, dev->io_mem);
err_pci_release_regions:
pci_release_regions(pdev);
err_pci_disable_device:
pci_disable_device(pdev);
err_kfree:
pci_set_drvdata(pdev, NULL);
- kfree(dm1105dvb);
+ kfree(dev);
return ret;
}
static void __devexit dm1105_remove(struct pci_dev *pdev)
{
- struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev);
- struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter;
- struct dvb_demux *dvbdemux = &dm1105dvb->demux;
+ struct dm1105_dev *dev = pci_get_drvdata(pdev);
+ struct dvb_adapter *dvb_adapter = &dev->dvb_adapter;
+ struct dvb_demux *dvbdemux = &dev->demux;
struct dmx_demux *dmx = &dvbdemux->dmx;
- dm1105_ir_exit(dm1105dvb);
+ dm1105_ir_exit(dev);
dmx->close(dmx);
- dvb_net_release(&dm1105dvb->dvbnet);
- if (dm1105dvb->fe)
- dvb_unregister_frontend(dm1105dvb->fe);
+ dvb_net_release(&dev->dvbnet);
+ if (dev->fe)
+ dvb_unregister_frontend(dev->fe);
dmx->disconnect_frontend(dmx);
- dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
- dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
- dvb_dmxdev_release(&dm1105dvb->dmxdev);
+ dmx->remove_frontend(dmx, &dev->mem_frontend);
+ dmx->remove_frontend(dmx, &dev->hw_frontend);
+ dvb_dmxdev_release(&dev->dmxdev);
dvb_dmx_release(dvbdemux);
dvb_unregister_adapter(dvb_adapter);
- if (&dm1105dvb->i2c_adap)
- i2c_del_adapter(&dm1105dvb->i2c_adap);
+ if (&dev->i2c_adap)
+ i2c_del_adapter(&dev->i2c_adap);
- dm1105dvb_hw_exit(dm1105dvb);
+ dm1105_hw_exit(dev);
synchronize_irq(pdev->irq);
- free_irq(pdev->irq, dm1105dvb);
- pci_iounmap(pdev, dm1105dvb->io_mem);
+ free_irq(pdev->irq, dev);
+ pci_iounmap(pdev, dev->io_mem);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
dm1105_devcount--;
- kfree(dm1105dvb);
+ kfree(dev);
}
static struct pci_device_id dm1105_id_table[] __devinitdata = {
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 07461222a7f..55ea260572b 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -1199,8 +1199,6 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
{
int r = 0;
- dtv_property_dump(tvp);
-
/* Allow the frontend to validate incoming properties */
if (fe->ops.get_property)
r = fe->ops.get_property(fe, tvp);
@@ -1323,6 +1321,8 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
r = -1;
}
+ dtv_property_dump(tvp);
+
return r;
}
@@ -1488,7 +1488,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
struct dvb_frontend_private *fepriv = fe->frontend_priv;
int err = -EOPNOTSUPP;
- dprintk ("%s\n", __func__);
+ dprintk("%s (%d)\n", __func__, _IOC_NR(cmd));
if (fepriv->exit)
return -ENODEV;
@@ -1536,8 +1536,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
return -EINVAL;
- tvp = (struct dtv_property *) kmalloc(tvps->num *
- sizeof(struct dtv_property), GFP_KERNEL);
+ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
if (!tvp) {
err = -ENOMEM;
goto out;
@@ -1569,8 +1568,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
return -EINVAL;
- tvp = (struct dtv_property *) kmalloc(tvps->num *
- sizeof(struct dtv_property), GFP_KERNEL);
+ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
if (!tvp) {
err = -ENOMEM;
goto out;
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index 8b8558fcb04..b11533f7619 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -504,6 +504,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
"bytes left in TS. Resyncing.\n", ts_remain);
priv->ule_sndu_len = 0;
priv->need_pusi = 1;
+ ts += TS_SZ;
continue;
}
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index 584bbd194dc..a5712cd7c65 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -89,6 +89,7 @@ void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
rbuf->pread = rbuf->pwrite;
rbuf->error = 0;
}
+EXPORT_SYMBOL(dvb_ringbuffer_flush);
void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
{
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 1b249897c9f..e5f91f16ffa 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -112,11 +112,13 @@ config DVB_USB_CXUSB
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select DVB_DIB7000P if !DVB_FE_CUSTOMISE
- select DVB_LGS8GL5 if !DVB_FE_CUSTOMISE
select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
+ select DVB_ATBM8830 if !DVB_FE_CUSTOMISE
+ select DVB_LGS8GXX if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the Conexant USB2.0 hybrid reference design.
Currently, only DVB and ATSC modes are supported, analog mode
@@ -334,3 +336,11 @@ config DVB_USB_EC168
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
+
+config DVB_USB_AZ6027
+ tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support"
+ depends on DVB_USB
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the AZ6027 device
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 72c92cb69a2..1a192453b0e 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -85,6 +85,9 @@ obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
dvb-usb-ec168-objs = ec168.o
obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
+dvb-usb-az6027-objs = az6027.o
+obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.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 8b60a601fb8..d7975383d31 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -21,6 +21,8 @@
*
*/
+#include <linux/hash.h>
+
#include "af9015.h"
#include "af9013.h"
#include "mt2060.h"
@@ -553,26 +555,45 @@ exit:
return ret;
}
-/* dump eeprom */
-static int af9015_eeprom_dump(struct dvb_usb_device *d)
+/* hash (and dump) eeprom */
+static int af9015_eeprom_hash(struct usb_device *udev)
{
- u8 reg, val;
+ static const unsigned int eeprom_size = 256;
+ unsigned int reg;
+ int ret;
+ u8 val, *eeprom;
+ struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
- for (reg = 0; ; reg++) {
- if (reg % 16 == 0) {
- if (reg)
- deb_info(KERN_CONT "\n");
- deb_info(KERN_DEBUG "%02x:", reg);
- }
- if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
- deb_info(KERN_CONT " %02x", val);
- else
- deb_info(KERN_CONT " --");
- if (reg == 0xff)
- break;
+ eeprom = kmalloc(eeprom_size, GFP_KERNEL);
+ if (eeprom == NULL)
+ return -ENOMEM;
+
+ for (reg = 0; reg < eeprom_size; reg++) {
+ req.addr = reg;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto free;
+ eeprom[reg] = val;
}
- deb_info(KERN_CONT "\n");
- return 0;
+
+ if (dvb_usb_af9015_debug & 0x01)
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom,
+ eeprom_size);
+
+ BUG_ON(eeprom_size % 4);
+
+ af9015_config.eeprom_sum = 0;
+ for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) {
+ af9015_config.eeprom_sum *= GOLDEN_RATIO_PRIME_32;
+ af9015_config.eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]);
+ }
+
+ deb_info("%s: eeprom sum=%.8x\n", __func__, af9015_config.eeprom_sum);
+
+ ret = 0;
+free:
+ kfree(eeprom);
+ return ret;
}
static int af9015_download_ir_table(struct dvb_usb_device *d)
@@ -711,12 +732,132 @@ error:
return ret;
}
+struct af9015_setup {
+ unsigned int id;
+ struct dvb_usb_rc_key *rc_key_map;
+ unsigned int rc_key_map_size;
+ u8 *ir_table;
+ unsigned int ir_table_size;
+};
+
+static const struct af9015_setup *af9015_setup_match(unsigned int id,
+ const struct af9015_setup *table)
+{
+ for (; table->rc_key_map; table++)
+ if (table->id == id)
+ return table;
+ return NULL;
+}
+
+static const struct af9015_setup af9015_setup_modparam[] = {
+ { AF9015_REMOTE_A_LINK_DTU_M,
+ af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link),
+ af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) },
+ { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi),
+ af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) },
+ { AF9015_REMOTE_MYGICTV_U718,
+ af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv),
+ af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) },
+ { AF9015_REMOTE_DIGITTRADE_DVB_T,
+ af9015_rc_keys_digittrade, ARRAY_SIZE(af9015_rc_keys_digittrade),
+ af9015_ir_table_digittrade, ARRAY_SIZE(af9015_ir_table_digittrade) },
+ { AF9015_REMOTE_AVERMEDIA_KS,
+ af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia),
+ af9015_ir_table_avermedia_ks, ARRAY_SIZE(af9015_ir_table_avermedia_ks) },
+ { }
+};
+
+/* don't add new entries here anymore, use hashes instead */
+static const struct af9015_setup af9015_setup_usbids[] = {
+ { USB_VID_LEADTEK,
+ af9015_rc_keys_leadtek, ARRAY_SIZE(af9015_rc_keys_leadtek),
+ af9015_ir_table_leadtek, ARRAY_SIZE(af9015_ir_table_leadtek) },
+ { USB_VID_VISIONPLUS,
+ af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan),
+ af9015_ir_table_twinhan, ARRAY_SIZE(af9015_ir_table_twinhan) },
+ { USB_VID_KWORLD_2, /* TODO: use correct rc keys */
+ af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan),
+ af9015_ir_table_kworld, ARRAY_SIZE(af9015_ir_table_kworld) },
+ { USB_VID_AVERMEDIA,
+ af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia),
+ af9015_ir_table_avermedia, ARRAY_SIZE(af9015_ir_table_avermedia) },
+ { USB_VID_MSI_2,
+ af9015_rc_keys_msi_digivox_iii, ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii),
+ af9015_ir_table_msi_digivox_iii, ARRAY_SIZE(af9015_ir_table_msi_digivox_iii) },
+ { }
+};
+
+static const struct af9015_setup af9015_setup_hashes[] = {
+ { 0xb8feb708,
+ af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi),
+ af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) },
+ { 0xa3703d00,
+ af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link),
+ af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) },
+ { 0x9b7dc64e,
+ af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv),
+ af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) },
+ { }
+};
+
+static void af9015_set_remote_config(struct usb_device *udev,
+ struct dvb_usb_device_properties *props)
+{
+ const struct af9015_setup *table = NULL;
+
+ if (dvb_usb_af9015_remote) {
+ /* load remote defined as module param */
+ table = af9015_setup_match(dvb_usb_af9015_remote,
+ af9015_setup_modparam);
+ } else {
+ u16 vendor = le16_to_cpu(udev->descriptor.idVendor);
+
+ table = af9015_setup_match(af9015_config.eeprom_sum,
+ af9015_setup_hashes);
+
+ if (!table && vendor == USB_VID_AFATECH) {
+ /* Check USB manufacturer and product strings and try
+ to determine correct remote in case of chip vendor
+ reference IDs are used.
+ DO NOT ADD ANYTHING NEW HERE. Use hashes instead.
+ */
+ char manufacturer[10];
+ memset(manufacturer, 0, sizeof(manufacturer));
+ usb_string(udev, udev->descriptor.iManufacturer,
+ manufacturer, sizeof(manufacturer));
+ if (!strcmp("MSI", manufacturer)) {
+ /* iManufacturer 1 MSI
+ iProduct 2 MSI K-VOX */
+ table = af9015_setup_match(
+ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ af9015_setup_modparam);
+ } else if (udev->descriptor.idProduct ==
+ cpu_to_le16(USB_PID_TREKSTOR_DVBT)) {
+ table = &(const struct af9015_setup){ 0,
+ af9015_rc_keys_trekstor,
+ ARRAY_SIZE(af9015_rc_keys_trekstor),
+ af9015_ir_table_trekstor,
+ ARRAY_SIZE(af9015_ir_table_trekstor)
+ };
+ }
+ } else if (!table)
+ table = af9015_setup_match(vendor, af9015_setup_usbids);
+ }
+
+ if (table) {
+ props->rc_key_map = table->rc_key_map;
+ props->rc_key_map_size = table->rc_key_map_size;
+ af9015_config.ir_table = table->ir_table;
+ af9015_config.ir_table_size = table->ir_table_size;
+ }
+}
+
static int af9015_read_config(struct usb_device *udev)
{
int ret;
u8 val, i, offset = 0;
struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
- char manufacturer[10];
/* IR remote controller */
req.addr = AF9015_EEPROM_IR_MODE;
@@ -728,158 +869,18 @@ static int af9015_read_config(struct usb_device *udev)
}
if (ret)
goto error;
+
+ ret = af9015_eeprom_hash(udev);
+ if (ret)
+ 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) {
af9015_properties[i].rc_key_map = NULL;
af9015_properties[i].rc_key_map_size = 0;
- } else if (dvb_usb_af9015_remote) {
- /* load remote defined as module param */
- switch (dvb_usb_af9015_remote) {
- case AF9015_REMOTE_A_LINK_DTU_M:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_a_link;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_a_link);
- af9015_config.ir_table = af9015_ir_table_a_link;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_a_link);
- break;
- case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_msi;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_msi);
- af9015_config.ir_table = af9015_ir_table_msi;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_msi);
- break;
- case AF9015_REMOTE_MYGICTV_U718:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_mygictv;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_mygictv);
- af9015_config.ir_table =
- af9015_ir_table_mygictv;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_mygictv);
- break;
- case AF9015_REMOTE_DIGITTRADE_DVB_T:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_digittrade;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_digittrade);
- af9015_config.ir_table =
- af9015_ir_table_digittrade;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_digittrade);
- break;
- case AF9015_REMOTE_AVERMEDIA_KS:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_avermedia;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_avermedia);
- af9015_config.ir_table =
- af9015_ir_table_avermedia_ks;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_avermedia_ks);
- break;
- }
- } else {
- switch (le16_to_cpu(udev->descriptor.idVendor)) {
- case USB_VID_LEADTEK:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_leadtek;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_leadtek);
- af9015_config.ir_table =
- af9015_ir_table_leadtek;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_leadtek);
- break;
- case USB_VID_VISIONPLUS:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_twinhan;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_twinhan);
- af9015_config.ir_table =
- af9015_ir_table_twinhan;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_twinhan);
- break;
- case USB_VID_KWORLD_2:
- /* TODO: use correct rc keys */
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_twinhan;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_twinhan);
- af9015_config.ir_table = af9015_ir_table_kworld;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_kworld);
- break;
- /* Check USB manufacturer and product strings and try
- to determine correct remote in case of chip vendor
- reference IDs are used. */
- case USB_VID_AFATECH:
- memset(manufacturer, 0, sizeof(manufacturer));
- usb_string(udev, udev->descriptor.iManufacturer,
- manufacturer, sizeof(manufacturer));
- if (!strcmp("Geniatech", manufacturer)) {
- /* iManufacturer 1 Geniatech
- iProduct 2 AF9015 */
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_mygictv;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_mygictv);
- af9015_config.ir_table =
- af9015_ir_table_mygictv;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_mygictv);
- } else if (!strcmp("MSI", manufacturer)) {
- /* iManufacturer 1 MSI
- iProduct 2 MSI K-VOX */
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_msi;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_msi);
- af9015_config.ir_table =
- af9015_ir_table_msi;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_msi);
- } else if (udev->descriptor.idProduct ==
- cpu_to_le16(USB_PID_TREKSTOR_DVBT)) {
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_trekstor;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_trekstor);
- af9015_config.ir_table =
- af9015_ir_table_trekstor;
- af9015_config.ir_table_size =
- ARRAY_SIZE(af9015_ir_table_trekstor);
- }
- break;
- case USB_VID_AVERMEDIA:
- af9015_properties[i].rc_key_map =
- af9015_rc_keys_avermedia;
- af9015_properties[i].rc_key_map_size =
- ARRAY_SIZE(af9015_rc_keys_avermedia);
- af9015_config.ir_table =
- af9015_ir_table_avermedia;
- 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;
- }
- }
+ } else
+ af9015_set_remote_config(udev, &af9015_properties[i]);
}
/* TS mode - one or two receivers */
@@ -1001,6 +1002,9 @@ static int af9015_read_config(struct usb_device *udev)
af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO;
af9015_af9013_config[i].rf_spec_inv = 1;
break;
+ case AF9013_TUNER_TDA18218:
+ warn("tuner NXP TDA18218 not supported yet");
+ return -ENODEV;
default:
warn("tuner id:%d not supported, please report!", val);
return -ENODEV;
@@ -1125,11 +1129,6 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
deb_info("%s: init I2C\n", __func__);
ret = af9015_i2c_init(adap->dev);
-
- /* dump eeprom (debug) */
- ret = af9015_eeprom_dump(adap->dev);
- if (ret)
- return ret;
} else {
/* select I2C adapter */
i2c_adap = &state->i2c_adap;
@@ -1295,6 +1294,8 @@ static struct usb_device_id af9015_usb_table[] = {
/* 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)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)},
+ {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1381,7 +1382,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
},
{
.name = "DigitalNow TinyTwin DVB-T Receiver",
- .cold_ids = {&af9015_usb_table[5], NULL},
+ .cold_ids = {&af9015_usb_table[5],
+ &af9015_usb_table[28], NULL},
.warm_ids = {NULL},
},
{
@@ -1566,7 +1568,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.i2c_algo = &af9015_i2c_algo,
- .num_device_descs = 6, /* max 9 */
+ .num_device_descs = 7, /* max 9 */
.devices = {
{
.name = "AverMedia AVerTV Volar GPS 805 (A805)",
@@ -1600,6 +1602,11 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.cold_ids = {&af9015_usb_table[27], NULL},
.warm_ids = {NULL},
},
+ {
+ .name = "Leadtek WinFast DTV2000DS",
+ .cold_ids = {&af9015_usb_table[29], NULL},
+ .warm_ids = {NULL},
+ },
}
},
};
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index 931c8515830..ef36b183149 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -107,6 +107,7 @@ struct af9015_config {
u16 mt2060_if1[2];
u16 firmware_size;
u16 firmware_checksum;
+ u32 eeprom_sum;
u8 *ir_table;
u16 ir_table_size;
};
diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c
new file mode 100644
index 00000000000..d7290b2c091
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/az6027.c
@@ -0,0 +1,1151 @@
+/* DVB USB compliant Linux driver for the AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)
+ * receiver.
+ *
+ * Copyright (C) 2009 Adams.Xu <adams.xu@azwave.com.cn>
+ *
+ * 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.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#include "az6027.h"
+
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+#include "dvb_ca_en50221.h"
+
+int dvb_usb_az6027_debug;
+module_param_named(debug, dvb_usb_az6027_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct az6027_device_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
+ u8 power_state;
+};
+
+static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = {
+
+ /* 0x0000000b, SYSREG */
+ { STB0899_DEV_ID , 0x30 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISFIFO , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x99 },
+ { STB0899_DISF22RX , 0xa8 },
+ /* SYSREG ? */
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0xfe },
+ { STB0899_IRQSTATUS_2 , 0x03 },
+ { STB0899_IRQSTATUS_1 , 0x7c },
+ { STB0899_IRQSTATUS_0 , 0xf4 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x58 },
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x33 },
+ { STB0899_IOPVALUE3 , 0x6d },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x60 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x01 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x01 },
+ { STB0899_AGC1REF , 0x10 },
+ { STB0899_RTC , 0x23 },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x34 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xf7 },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xf1 },
+ { STB0899_LDT2 , 0xe3 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfd },
+ { STB0899_QDCCOMP , 0xff },
+ { STB0899_POWERI , 0x0c },
+ { STB0899_POWERQ , 0x0f },
+ { STB0899_RCOMP , 0x6c },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x06 },
+ { STB0899_AGC2I2 , 0x00 },
+ { STB0899_TLIR , 0x30 },
+ { STB0899_RTF , 0x7f },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xbc },
+ { STB0899_CFRM , 0xea },
+ { STB0899_CFRL , 0x31 },
+ { STB0899_NIRM , 0x2b },
+ { STB0899_NIRL , 0x80 },
+ { STB0899_ISYMB , 0x1d },
+ { STB0899_QSYMB , 0xa6 },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0x02 },
+ { STB0899_EQUAQ1 , 0xff },
+ { STB0899_EQUAI2 , 0x04 },
+ { STB0899_EQUAQ2 , 0x05 },
+ { STB0899_EQUAI3 , 0x02 },
+ { STB0899_EQUAQ3 , 0xfd },
+ { STB0899_EQUAI4 , 0x03 },
+ { STB0899_EQUAQ4 , 0x07 },
+ { STB0899_EQUAI5 , 0x08 },
+ { STB0899_EQUAQ5 , 0xf5 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0x86 },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x0a },
+ { STB0899_ECNT3L , 0xad },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xb0 },
+ { STB0899_VTH23 , 0x7a },
+ { STB0899_VTH34 , 0x58 },
+ { STB0899_VTH56 , 0x38 },
+ { STB0899_VTH67 , 0x34 },
+ { STB0899_VTH78 , 0x24 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x41 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x00 },
+ { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x1b },
+ { STB0899_TSLLSTKL , 0xb3 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0x00 },
+ { STB0899_PCKLENUL , 0xbc },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xbd },
+ { STB0899_TSSTATUS , 0x90 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x95 },
+ { STB0899_ERRCTRL3 , 0x8d },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x19 },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0xf0 },
+ { STB0899_MATSTRL , 0x02 },
+ { STB0899_UPLSTRM , 0x45 },
+ { STB0899_UPLSTRL , 0x60 },
+ { STB0899_DFLSTRM , 0xe3 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x47 },
+ { STB0899_SYNCDSTRM , 0x05 },
+ { STB0899_SYNCDSTRL , 0x18 },
+ { STB0899_CFGPDELSTATUS1 , 0x19 },
+ { STB0899_CFGPDELSTATUS2 , 0x2b },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x01 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+
+
+
+struct stb0899_config az6027_stb0899_config = {
+ .init_dev = az6027_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = az6027_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .demod_address = 0xd0, /* 0x68, 0xd0 >> 1 */
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_ON, /* 1 */
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL,
+};
+
+struct stb6100_config az6027_stb6100_config = {
+ .tuner_address = 0xc0,
+ .refclock = 27000000,
+};
+
+
+/* check for mutex FIXME */
+int az6027_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
+{
+ int ret = -1;
+ if (mutex_lock_interruptible(&d->usb_mutex))
+ return -EAGAIN;
+
+ ret = usb_control_msg(d->udev,
+ usb_rcvctrlpipe(d->udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value,
+ index,
+ b,
+ blen,
+ 2000);
+
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else
+ ret = 0;
+
+ deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index);
+ debug_dump(b, blen, deb_xfer);
+
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+static int az6027_usb_out_op(struct dvb_usb_device *d,
+ u8 req,
+ u16 value,
+ u16 index,
+ u8 *b,
+ int blen)
+{
+ int ret;
+
+ deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index);
+ debug_dump(b, blen, deb_xfer);
+
+ if (mutex_lock_interruptible(&d->usb_mutex))
+ return -EAGAIN;
+
+ ret = usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ value,
+ index,
+ b,
+ blen,
+ 2000);
+
+ if (ret != blen) {
+ warn("usb out operation failed. (%d)", ret);
+ mutex_unlock(&d->usb_mutex);
+ return -EIO;
+ } else{
+ mutex_unlock(&d->usb_mutex);
+ return 0;
+ }
+}
+
+static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ deb_info("%s %d", __func__, onoff);
+
+ req = 0xBC;
+ value = onoff;
+ index = 0;
+ blen = 0;
+
+ ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen);
+ if (ret != 0)
+ warn("usb out operation failed. (%d)", ret);
+
+ return ret;
+}
+
+/* keys for the enclosed remote control */
+static struct dvb_usb_rc_key az6027_rc_keys[] = {
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+};
+
+/* remote control stuff (does not work with my box) */
+static int az6027_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+ return 0;
+}
+
+/*
+int az6027_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ u8 v = onoff;
+ return az6027_usb_out_op(d,0xBC,v,3,NULL,1);
+}
+*/
+
+static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 b[12];
+
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC1;
+ value = address;
+ index = 0;
+ blen = 1;
+
+ ret = az6027_usb_in_op(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EINVAL;
+ } else {
+ ret = b[0];
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6027_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ deb_info("%s %d", __func__, slot);
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC2;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6027_usb_out_op(d, req, value1, index, NULL, blen);
+ if (ret != 0)
+ warn("usb out operation failed. (%d)", ret);
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 b[12];
+
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC3;
+ value = address;
+ index = 0;
+ blen = 2;
+
+ ret = az6027_usb_in_op(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EINVAL;
+ } else {
+ if (b[0] == 0)
+ warn("Read CI IO error");
+
+ ret = b[1];
+ deb_info("read cam data = %x from 0x%x", b[1], value);
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6027_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC4;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6027_usb_out_op(d, req, value1, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 b[12];
+
+ req = 0xC8;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6027_usb_in_op(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else{
+ ret = b[0];
+ }
+ return ret;
+}
+
+static int az6027_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+
+ int ret, i;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC6;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6027_usb_out_op(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+ msleep(500);
+ req = 0xC6;
+ value = 0;
+ index = 0;
+ blen = 0;
+
+ ret = az6027_usb_out_op(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ if (CI_CamReady(ca, slot)) {
+ deb_info("CAM Ready");
+ break;
+ }
+ }
+ msleep(5000);
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6027_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return 0;
+}
+
+static int az6027_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ deb_info("%s", __func__);
+ mutex_lock(&state->ca_mutex);
+ req = 0xC7;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6027_usb_out_op(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 b[12];
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC5;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6027_usb_in_op(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else
+ ret = 0;
+
+ if (b[0] == 0) {
+ ret = 0;
+
+ } else if (b[0] == 1) {
+ ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+
+static void az6027_ci_uninit(struct dvb_usb_device *d)
+{
+ struct az6027_device_state *state;
+
+ deb_info("%s", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = (struct az6027_device_state *)d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+
+static int az6027_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct az6027_device_state *state = (struct az6027_device_state *)d->priv;
+ int ret;
+
+ deb_info("%s", __func__);
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = az6027_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = az6027_ci_write_attribute_mem;
+ state->ca.read_cam_control = az6027_ci_read_cam_control;
+ state->ca.write_cam_control = az6027_ci_write_cam_control;
+ state->ca.slot_reset = az6027_ci_slot_reset;
+ state->ca.slot_shutdown = az6027_ci_slot_shutdown;
+ state->ca.slot_ts_enable = az6027_ci_slot_ts_enable;
+ state->ca.poll_slot_status = az6027_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ 0, /* flags */
+ 1);/* n_slots */
+ if (ret != 0) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ deb_info("CI initialized.");
+
+ return 0;
+}
+
+/*
+static int az6027_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
+{
+ az6027_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6);
+ return 0;
+}
+*/
+
+static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+
+ u8 buf;
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ struct i2c_msg i2c_msg = {
+ .addr = 0x99,
+ .flags = 0,
+ .buf = &buf,
+ .len = 1
+ };
+
+ /*
+ * 2 --18v
+ * 1 --13v
+ * 0 --off
+ */
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ buf = 1;
+ ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ break;
+
+ case SEC_VOLTAGE_18:
+ buf = 2;
+ ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ break;
+
+ case SEC_VOLTAGE_OFF:
+ buf = 0;
+ ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int az6027_frontend_poweron(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ req = 0xBC;
+ value = 1; /* power on */
+ index = 3;
+ blen = 0;
+
+ ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen);
+ if (ret != 0)
+ return -EIO;
+
+ return 0;
+}
+static int az6027_frontend_reset(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ /* reset demodulator */
+ req = 0xC0;
+ value = 1; /* high */
+ index = 3;
+ blen = 0;
+
+ ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen);
+ if (ret != 0)
+ return -EIO;
+
+ req = 0xC0;
+ value = 0; /* low */
+ index = 3;
+ blen = 0;
+ msleep_interruptible(200);
+
+ ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen);
+ if (ret != 0)
+ return -EIO;
+
+ msleep_interruptible(200);
+
+ req = 0xC0;
+ value = 1; /*high */
+ index = 3;
+ blen = 0;
+
+ ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen);
+ if (ret != 0)
+ return -EIO;
+
+ msleep_interruptible(200);
+ return 0;
+}
+
+static int az6027_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ /* TS passthrough */
+ req = 0xC7;
+ value = onoff;
+ index = 0;
+ blen = 0;
+
+ ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen);
+ if (ret != 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int az6027_frontend_attach(struct dvb_usb_adapter *adap)
+{
+
+ az6027_frontend_poweron(adap);
+ az6027_frontend_reset(adap);
+
+ deb_info("adap = %p, dev = %p\n", adap, adap->dev);
+ adap->fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap);
+
+ if (adap->fe) {
+ deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address);
+ if (stb6100_attach(adap->fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) {
+ deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address);
+ adap->fe->ops.set_voltage = az6027_set_voltage;
+ az6027_ci_init(adap);
+ } else {
+ adap->fe = NULL;
+ }
+ } else
+ warn("no front-end attached\n");
+
+ az6027_frontend_tsbypass(adap, 0);
+
+ return 0;
+}
+
+static struct dvb_usb_device_properties az6027_properties;
+
+static void az6027_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ az6027_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+
+static int az6027_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return dvb_usb_device_init(intf,
+ &az6027_properties,
+ THIS_MODULE,
+ NULL,
+ adapter_nr);
+}
+
+/* I2C */
+static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int i = 0, j = 0, len = 0;
+ int ret;
+ u16 index;
+ u16 value;
+ int length;
+ u8 req;
+ u8 data[256];
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num > 2)
+ warn("more than 2 i2c messages at a time is not handled yet. TODO.");
+
+ for (i = 0; i < num; i++) {
+
+ if (msg[i].addr == 0x99) {
+ req = 0xBE;
+ index = 0;
+ value = msg[i].buf[0] & 0x00ff;
+ length = 1;
+ az6027_usb_out_op(d, req, value, index, data, length);
+ }
+
+ if (msg[i].addr == 0xd0) {
+ /* write/read request */
+ if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) {
+ req = 0xB9;
+ index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff));
+ value = msg[i].addr + (msg[i].len << 8);
+ length = msg[i + 1].len + 6;
+ ret = az6027_usb_in_op(d, req, value, index, data, length);
+ len = msg[i + 1].len;
+ for (j = 0; j < len; j++)
+ msg[i + 1].buf[j] = data[j + 5];
+
+ i++;
+ } else {
+
+ if (msg[i].addr == 0xd0) {
+ /* demod 16bit addr */
+ req = 0xBD;
+ index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff));
+ value = msg[i].addr + (2 << 8);
+ length = msg[i].len - 2;
+ len = msg[i].len - 2;
+ for (j = 0; j < len; j++)
+ data[j] = msg[i].buf[j + 2];
+
+ }
+ az6027_usb_out_op(d, req, value, index, data, length);
+ }
+ }
+
+ if (msg[i].addr == 0xc0) {
+ if (msg[i].flags & I2C_M_RD) {
+
+ req = 0xB9;
+ index = 0x0;
+ value = msg[i].addr;
+ length = msg[i].len + 6;
+ ret = az6027_usb_in_op(d, req, value, index, data, length);
+ len = msg[i].len;
+ for (j = 0; j < len; j++)
+ msg[i].buf[j] = data[j + 5];
+
+ } else {
+
+ req = 0xBD;
+ index = msg[i].buf[0] & 0x00FF;
+ value = msg[i].addr + (1 << 8);
+ length = msg[i].len - 1;
+ len = msg[i].len - 1;
+
+ for (j = 0; j < len; j++)
+ data[j] = msg[i].buf[j + 1];
+
+ az6027_usb_out_op(d, req, value, index, data, length);
+ }
+ }
+ }
+ mutex_unlock(&d->i2c_mutex);
+
+ return i;
+}
+
+
+static u32 az6027_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm az6027_i2c_algo = {
+ .master_xfer = az6027_i2c_xfer,
+ .functionality = az6027_i2c_func,
+};
+
+int az6027_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ u8 b[16];
+ s16 ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0xb7,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ 6,
+ 0,
+ b,
+ 6,
+ USB_CTRL_GET_TIMEOUT);
+
+ *cold = ret <= 0;
+
+ deb_info("cold: %d\n", *cold);
+ return 0;
+}
+
+
+static struct usb_device_id az6027_usb_table[] = {
+ { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_AZ6027) },
+ { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI) },
+ { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI) },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, az6027_usb_table);
+
+static struct dvb_usb_device_properties az6027_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-az6027-03.fw",
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct az6027_device_state),
+ .identify_state = az6027_identify_state,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = az6027_streaming_ctrl,
+ .frontend_attach = az6027_frontend_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 10,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ }
+ },
+/*
+ .power_ctrl = az6027_power_ctrl,
+ .read_mac_address = az6027_read_mac_addr,
+ */
+ .rc_key_map = az6027_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(az6027_rc_keys),
+ .rc_interval = 400,
+ .rc_query = az6027_rc_query,
+ .i2c_algo = &az6027_i2c_algo,
+
+ .num_device_descs = 1,
+ .devices = {
+ {
+ .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)",
+ .cold_ids = { &az6027_usb_table[0], NULL },
+ .warm_ids = { NULL },
+ },
+ { NULL },
+ }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver az6027_usb_driver = {
+ .name = "dvb_usb_az6027",
+ .probe = az6027_usb_probe,
+ .disconnect = az6027_usb_disconnect,
+ .id_table = az6027_usb_table,
+};
+
+/* module stuff */
+static int __init az6027_usb_module_init(void)
+{
+ int result;
+
+ result = usb_register(&az6027_usb_driver);
+ if (result) {
+ err("usb_register failed. (%d)", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit az6027_usb_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&az6027_usb_driver);
+}
+
+module_init(az6027_usb_module_init);
+module_exit(az6027_usb_module_exit);
+
+MODULE_AUTHOR("Adams Xu <Adams.xu@azwave.com.cn>");
+MODULE_DESCRIPTION("Driver for AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/az6027.h b/drivers/media/dvb/dvb-usb/az6027.h
new file mode 100644
index 00000000000..f3afe17f3f3
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/az6027.h
@@ -0,0 +1,14 @@
+#ifndef _DVB_USB_VP6027_H_
+#define _DVB_USB_VP6027_H_
+
+#define DVB_USB_LOG_PREFIX "az6027"
+#include "dvb-usb.h"
+
+
+extern int dvb_usb_az6027_debug;
+#define deb_info(args...) dprintk(dvb_usb_az6027_debug, 0x01, args)
+#define deb_xfer(args...) dprintk(dvb_usb_az6027_debug, 0x02, args)
+#define deb_rc(args...) dprintk(dvb_usb_az6027_debug, 0x04, args)
+#define deb_fe(args...) dprintk(dvb_usb_az6027_debug, 0x08, args)
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 05fb28e9c69..a7b8405c291 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -1184,6 +1184,9 @@ static struct atbm8830_config mygica_d689_atbm8830_cfg = {
.osc_clk_freq = 30400, /* in kHz */
.if_freq = 0, /* zero IF */
.zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0x90,
+ .agc_hold_loop = 0,
};
static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h
index 495a90577c5..83fc24a6c31 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/dvb/dvb-usb/dib0700.h
@@ -42,7 +42,6 @@ struct dib0700_state {
u16 mt2060_if1[2];
u8 rc_toggle;
u8 rc_counter;
- u8 rc_func_version;
u8 is_dib7000pc;
u8 fw_use_new_i2c_api;
u8 disable_streaming_master_mode;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 0d3c9a9a33b..4f961d2d181 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -471,14 +471,209 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
return dib0700_ctrl_wr(adap->dev, b, 4);
}
+/* Number of keypresses to ignore before start repeating */
+#define RC_REPEAT_DELAY_V1_20 10
+
+/* This is the structure of the RC response packet starting in firmware 1.20 */
+struct dib0700_rc_response {
+ u8 report_id;
+ u8 data_state;
+ u16 system;
+ u8 data;
+ u8 not_data;
+};
+#define RC_MSG_SIZE_V1_20 6
+
+static void dib0700_rc_urb_completion(struct urb *purb)
+{
+ struct dvb_usb_device *d = purb->context;
+ struct dvb_usb_rc_key *keymap;
+ struct dib0700_state *st;
+ struct dib0700_rc_response poll_reply;
+ u8 *buf;
+ int found = 0;
+ u32 event;
+ int state;
+ int i;
+
+ deb_info("%s()\n", __func__);
+ if (d == NULL)
+ return;
+
+ if (d->rc_input_dev == NULL) {
+ /* This will occur if disable_rc_polling=1 */
+ usb_free_urb(purb);
+ return;
+ }
+
+ keymap = d->props.rc_key_map;
+ st = d->priv;
+ buf = (u8 *)purb->transfer_buffer;
+
+ if (purb->status < 0) {
+ deb_info("discontinuing polling\n");
+ usb_free_urb(purb);
+ return;
+ }
+
+ if (purb->actual_length != RC_MSG_SIZE_V1_20) {
+ deb_info("malformed rc msg size=%d\n", purb->actual_length);
+ goto resubmit;
+ }
+
+ /* Set initial results in case we exit the function early */
+ event = 0;
+ state = REMOTE_NO_KEY_PRESSED;
+
+ deb_data("IR raw %02X %02X %02X %02X %02X %02X (len %d)\n", buf[0],
+ buf[1], buf[2], buf[3], buf[4], buf[5], purb->actual_length);
+
+ switch (dvb_usb_dib0700_ir_proto) {
+ case 0:
+ /* NEC Protocol */
+ 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:
+ /* RC5 Protocol */
+ 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];
+ break;
+ }
+
+ if ((poll_reply.data + poll_reply.not_data) != 0xff) {
+ /* Key failed integrity check */
+ err("key failed integrity check: %04x %02x %02x",
+ poll_reply.system,
+ poll_reply.data, poll_reply.not_data);
+ goto resubmit;
+ }
+
+ deb_data("rid=%02x ds=%02x sm=%04x d=%02x nd=%02x\n",
+ poll_reply.report_id, poll_reply.data_state,
+ poll_reply.system, poll_reply.data, poll_reply.not_data);
+
+ /* 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 & 0xff) &&
+ rc5_data(&keymap[i]) == poll_reply.data) {
+ event = keymap[i].event;
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ err("Unknown remote controller key: %04x %02x %02x",
+ poll_reply.system, poll_reply.data, poll_reply.not_data);
+ d->last_event = 0;
+ goto resubmit;
+ }
+
+ if (poll_reply.data_state == 1) {
+ /* New key hit */
+ st->rc_counter = 0;
+ event = keymap[i].event;
+ state = REMOTE_KEY_PRESSED;
+ d->last_event = keymap[i].event;
+ } else if (poll_reply.data_state == 2) {
+ /* Key repeated */
+ st->rc_counter++;
+
+ /* prevents unwanted double hits */
+ if (st->rc_counter > RC_REPEAT_DELAY_V1_20) {
+ event = d->last_event;
+ state = REMOTE_KEY_PRESSED;
+ st->rc_counter = RC_REPEAT_DELAY_V1_20;
+ }
+ } else {
+ err("Unknown data state [%d]", poll_reply.data_state);
+ }
+
+ switch (state) {
+ case REMOTE_NO_KEY_PRESSED:
+ break;
+ case REMOTE_KEY_PRESSED:
+ deb_info("key pressed\n");
+ d->last_event = event;
+ case REMOTE_KEY_REPEAT:
+ deb_info("key repeated\n");
+ input_event(d->rc_input_dev, EV_KEY, event, 1);
+ input_sync(d->rc_input_dev);
+ input_event(d->rc_input_dev, EV_KEY, d->last_event, 0);
+ input_sync(d->rc_input_dev);
+ break;
+ default:
+ break;
+ }
+
+resubmit:
+ /* Clean the buffer before we requeue */
+ memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20);
+
+ /* Requeue URB */
+ usb_submit_urb(purb, GFP_ATOMIC);
+}
+
int dib0700_rc_setup(struct dvb_usb_device *d)
{
+ struct dib0700_state *st = d->priv;
u8 rc_setup[3] = {REQUEST_SET_RC, dvb_usb_dib0700_ir_proto, 0};
- int i = dib0700_ctrl_wr(d, rc_setup, 3);
+ struct urb *purb;
+ int ret;
+ int i;
+
+ if (d->props.rc_key_map == NULL)
+ return 0;
+
+ /* Set the IR mode */
+ i = dib0700_ctrl_wr(d, rc_setup, 3);
if (i<0) {
err("ir protocol setup failed");
return -1;
}
+
+ if (st->fw_version < 0x10200)
+ return 0;
+
+ /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
+ purb = usb_alloc_urb(0, GFP_KERNEL);
+ if (purb == NULL) {
+ err("rc usb alloc urb failed\n");
+ return -1;
+ }
+
+ purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
+ if (purb->transfer_buffer == NULL) {
+ err("rc kzalloc failed\n");
+ usb_free_urb(purb);
+ return -1;
+ }
+
+ purb->status = -EINPROGRESS;
+ usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1),
+ purb->transfer_buffer, RC_MSG_SIZE_V1_20,
+ dib0700_rc_urb_completion, d);
+
+ ret = usb_submit_urb(purb, GFP_ATOMIC);
+ if (ret != 0) {
+ err("rc submit urb failed\n");
+ return -1;
+ }
+
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 44972d01bbd..34eab05afc6 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -472,20 +472,25 @@ static u8 rc_request[] = { REQUEST_POLL_RC, 0 };
/* Number of keypresses to ignore before start repeating */
#define RC_REPEAT_DELAY 6
-#define RC_REPEAT_DELAY_V1_20 10
-
-
-/* Used by firmware versions < 1.20 (deprecated) */
-static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event,
- int *state)
+static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key[4];
int i;
struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
struct dib0700_state *st = d->priv;
+
*event = 0;
*state = REMOTE_NO_KEY_PRESSED;
+
+ if (st->fw_version >= 0x10200) {
+ /* For 1.20 firmware , We need to keep the RC polling
+ callback so we can reuse the input device setup in
+ dvb-usb-remote.c. However, the actual work is being done
+ in the bulk URB completion handler. */
+ return 0;
+ }
+
i=dib0700_ctrl_rd(d,rc_request,2,key,4);
if (i<=0) {
err("RC Query Failed");
@@ -557,149 +562,6 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event,
return 0;
}
-/* This is the structure of the RC response packet starting in firmware 1.20 */
-struct dib0700_rc_response {
- u8 report_id;
- u8 data_state;
- u16 system;
- u8 data;
- u8 not_data;
-};
-
-/* This supports the new IR response format for firmware v1.20 */
-static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
- int *state)
-{
- struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
- struct dib0700_state *st = d->priv;
- struct dib0700_rc_response poll_reply;
- u8 buf[6];
- int i;
- int status;
- int actlen;
- int found = 0;
-
- /* Set initial results in case we exit the function early */
- *event = 0;
- *state = REMOTE_NO_KEY_PRESSED;
-
- /* Firmware v1.20 provides RC data via bulk endpoint 1 */
- status = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, 1), buf,
- sizeof(buf), &actlen, 50);
- if (status < 0) {
- /* No data available (meaning no key press) */
- return 0;
- }
-
-
- 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];
-
- break;
- }
-
- if ((poll_reply.data + poll_reply.not_data) != 0xff) {
- /* Key failed integrity check */
- 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 & 0xff) &&
- rc5_data(&keymap[i]) == poll_reply.data) {
- *event = keymap[i].event;
- found = 1;
- break;
- }
- }
-
- if (found == 0) {
- err("Unknown remote controller key: %04x %02x %02x",
- poll_reply.system,
- poll_reply.data, poll_reply.not_data);
- d->last_event = 0;
- return 0;
- }
-
- if (poll_reply.data_state == 1) {
- /* New key hit */
- st->rc_counter = 0;
- *event = keymap[i].event;
- *state = REMOTE_KEY_PRESSED;
- d->last_event = keymap[i].event;
- } else if (poll_reply.data_state == 2) {
- /* Key repeated */
- st->rc_counter++;
-
- /* prevents unwanted double hits */
- if (st->rc_counter > RC_REPEAT_DELAY_V1_20) {
- *event = d->last_event;
- *state = REMOTE_KEY_PRESSED;
- st->rc_counter = RC_REPEAT_DELAY_V1_20;
- }
- } else {
- err("Unknown data state [%d]", poll_reply.data_state);
- }
-
- return 0;
-}
-
-static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
-{
- struct dib0700_state *st = d->priv;
-
- /* Because some people may have improperly named firmware files,
- let's figure out whether to use the new firmware call or the legacy
- call based on the firmware version embedded in the file */
- if (st->rc_func_version == 0) {
- u32 hwver, romver, ramver, fwtype;
- int ret = dib0700_get_version(d, &hwver, &romver, &ramver,
- &fwtype);
- if (ret < 0) {
- err("Could not determine version info");
- return -1;
- }
- if (ramver < 0x10200)
- st->rc_func_version = 1;
- else
- st->rc_func_version = 2;
- }
-
- if (st->rc_func_version == 2)
- return dib0700_rc_query_v1_20(d, event, state);
- else
- return dib0700_rc_query_legacy(d, event, state);
-}
-
static struct dvb_usb_rc_key dib0700_rc_keys[] = {
/* Key codes for the tiny Pinnacle remote*/
{ 0x0700, KEY_MUTE },
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index bc3581d58ce..ae8b57acfe0 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -64,6 +64,8 @@
#define USB_VID_HUMAX_COEX 0x10b9
#define USB_VID_774 0x7a69
#define USB_VID_EVOLUTEPC 0x1e59
+#define USB_VID_AZUREWAVE 0x13d3
+#define USB_VID_TECHNISAT 0x14f7
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
@@ -138,6 +140,7 @@
#define USB_PID_TWINHAN_VP7021_COLD 0x3207
#define USB_PID_TWINHAN_VP7021_WARM 0x3208
#define USB_PID_TINYTWIN 0x3226
+#define USB_PID_TINYTWIN_2 0xe402
#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
@@ -209,6 +212,7 @@
#define USB_PID_PINNACLE_PCTV71E 0x022b
#define USB_PID_PINNACLE_PCTV72E 0x0236
#define USB_PID_PINNACLE_PCTV73E 0x0237
+#define USB_PID_PINNACLE_PCTV310E 0x3211
#define USB_PID_PINNACLE_PCTV801E 0x023a
#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
#define USB_PID_PINNACLE_PCTV73A 0x0243
@@ -248,6 +252,7 @@
#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361
#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6
#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
+#define USB_PID_WINFAST_DTV2000DS 0x6a04
#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
@@ -290,5 +295,7 @@
#define USB_PID_FRIIO_WHITE 0x0001
#define USB_PID_TVWAY_PLUS 0x0002
#define USB_PID_SVEON_STV20 0xe39d
-
+#define USB_PID_AZUREWAVE_AZ6027 0x3275
+#define USB_PID_TERRATEC_DVBS2CI 0x3275
+#define USB_PID_TECHNISAT_USB2_HDCI 0x0002
#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c
index e331db8c77b..5d91f70d2d2 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c
@@ -243,7 +243,7 @@ int dvb_usb_device_init(struct usb_interface *intf,
d = kzalloc(sizeof(struct dvb_usb_device),GFP_KERNEL);
if (d == NULL) {
err("no memory for 'struct dvb_usb_device'");
- return ret;
+ return -ENOMEM;
}
d->udev = udev;
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index 6b5ded9e7d5..a03ef7efec9 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -107,6 +107,7 @@ static void dvb_usb_read_remote_control(struct work_struct *work)
case REMOTE_KEY_REPEAT:
deb_rc("key repeated\n");
input_event(d->rc_input_dev, EV_KEY, event, 1);
+ input_sync(d->rc_input_dev);
input_event(d->rc_input_dev, EV_KEY, d->last_event, 0);
input_sync(d->rc_input_dev);
break;
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 64132c0cf80..accc65509b0 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -1,6 +1,7 @@
/* DVB USB framework compliant Linux driver for the
* DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
-* TeVii S600, S630, S650 Cards
+* TeVii S600, S630, S650,
+* Prof 1100, 7500 Cards
* Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by)
*
* This program is free software; you can redistribute it and/or modify it
@@ -469,11 +470,13 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct usb_device *udev;
int ret = 0;
int len, i, j;
if (!d)
return -ENODEV;
+ udev = d->udev;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
@@ -488,8 +491,13 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
case (DW2102_VOLTAGE_CTRL): {
u8 obuf[2];
+
+ obuf[0] = 1;
+ obuf[1] = msg[j].buf[1];/* off-on */
+ ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
+ obuf, 2, DW210X_WRITE_MSG);
obuf[0] = 3;
- obuf[1] = msg[j].buf[0];
+ obuf[1] = msg[j].buf[0];/* 13v-18v */
ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
@@ -527,6 +535,17 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
i += 16;
len -= 16;
} while (len > 0);
+ } else if ((udev->descriptor.idProduct == 0x7500)
+ && (j < (num - 1))) {
+ /* write register addr before read */
+ u8 obuf[msg[j].len + 2];
+ obuf[0] = msg[j + 1].len;
+ obuf[1] = (msg[j].addr << 1);
+ memcpy(obuf + 2, msg[j].buf, msg[j].len);
+ ret = dw210x_op_rw(d->udev, 0x92, 0, 0,
+ obuf, msg[j].len + 2,
+ DW210X_WRITE_MSG);
+ break;
} else {
/* write registers */
u8 obuf[msg[j].len + 2];
@@ -651,18 +670,25 @@ static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
- static u8 command_13v[1] = {0x00};
- static u8 command_18v[1] = {0x01};
- struct i2c_msg msg[] = {
- {.addr = DW2102_VOLTAGE_CTRL, .flags = 0,
- .buf = command_13v, .len = 1},
+ static u8 command_13v[] = {0x00, 0x01};
+ static u8 command_18v[] = {0x01, 0x01};
+ static u8 command_off[] = {0x00, 0x00};
+ struct i2c_msg msg = {
+ .addr = DW2102_VOLTAGE_CTRL,
+ .flags = 0,
+ .buf = command_off,
+ .len = 2,
};
struct dvb_usb_adapter *udev_adap =
(struct dvb_usb_adapter *)(fe->dvb->priv);
if (voltage == SEC_VOLTAGE_18)
- msg[0].buf = command_18v;
- i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1);
+ msg.buf = command_18v;
+ else if (voltage == SEC_VOLTAGE_13)
+ msg.buf = command_13v;
+
+ i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1);
+
return 0;
}
@@ -735,6 +761,18 @@ static struct stv6110_config dw2104_stv6110_config = {
.clk_div = 1,
};
+static struct stv0900_config prof_7500_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,
+ .tun1_type = 3,
+};
+
static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
{
struct dvb_tuner_ops *tuner_ops = NULL;
@@ -882,6 +920,19 @@ static int s6x0_frontend_attach(struct dvb_usb_adapter *d)
return -EIO;
}
+static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
+{
+ d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config,
+ &d->dev->i2c_adap, 0);
+ if (d->fe == NULL)
+ return -EIO;
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+
+ info("Attached STV0900+STB6100A!\n");
+
+ return 0;
+}
+
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -1073,6 +1124,7 @@ static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(0x9022, USB_PID_TEVII_S630)},
{USB_DEVICE(0x3011, USB_PID_PROF_1100)},
{USB_DEVICE(0x9022, USB_PID_TEVII_S660)},
+ {USB_DEVICE(0x3034, 0x7500)},
{ }
};
@@ -1387,9 +1439,30 @@ static struct dvb_usb_device_properties s6x0_properties = {
}
};
+struct dvb_usb_device_properties *p7500;
+static struct dvb_usb_device_description d7500 = {
+ "Prof 7500 USB DVB-S2",
+ {&dw2102_table[9], NULL},
+ {NULL},
+};
+
static int dw2102_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+
+ p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL);
+ if (!p7500)
+ return -ENOMEM;
+ /* copy default structure */
+ memcpy(p7500, &s6x0_properties,
+ sizeof(struct dvb_usb_device_properties));
+ /* fill only different fields */
+ p7500->firmware = "dvb-usb-p7500.fw";
+ p7500->devices[0] = d7500;
+ p7500->rc_key_map = tbs_rc_keys;
+ p7500->rc_key_map_size = ARRAY_SIZE(tbs_rc_keys);
+ p7500->adapter->frontend_attach = prof_7500_frontend_attach;
+
if (0 == dvb_usb_device_init(intf, &dw2102_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &dw2104_properties,
@@ -1397,6 +1470,8 @@ static int dw2102_probe(struct usb_interface *intf,
0 == dvb_usb_device_init(intf, &dw3101_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &s6x0_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, p7500,
THIS_MODULE, NULL, adapter_nr))
return 0;
@@ -1431,6 +1506,6 @@ MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
" DVB-C 3101 USB2.0,"
" TeVii S600, S630, S650, S660 USB2.0,"
- " Prof 1100 USB2.0 devices");
+ " Prof 1100, 7500 USB2.0 devices");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c
index ebb7b9fd115..d14bd227b50 100644
--- a/drivers/media/dvb/dvb-usb/friio-fe.c
+++ b/drivers/media/dvb/dvb-usb/friio-fe.c
@@ -366,7 +366,7 @@ static u8 init_code[][2] = {
{0x76, 0x0C},
};
-const static int init_code_len = sizeof(init_code) / sizeof(u8[2]);
+static const int init_code_len = sizeof(init_code) / sizeof(u8[2]);
static int jdvbt90502_init(struct dvb_frontend *fe)
{
diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c
index ef9b7bed13f..737ffa36ac9 100644
--- a/drivers/media/dvb/dvb-usb/m920x.c
+++ b/drivers/media/dvb/dvb-usb/m920x.c
@@ -16,6 +16,9 @@
#include "qt1010.h"
#include "tda1004x.h"
#include "tda827x.h"
+
+#include <media/tuner.h>
+#include "tuner-simple.h"
#include <asm/unaligned.h>
/* debug */
@@ -158,11 +161,14 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
case 0x93:
case 0x92:
+ case 0x83: /* pinnacle PCTV310e */
+ case 0x82:
m->rep_count = 0;
*state = REMOTE_KEY_PRESSED;
goto unlock;
case 0x91:
+ case 0x81: /* pinnacle PCTV310e */
/* prevent immediate auto-repeat */
if (++m->rep_count > 2)
*state = REMOTE_KEY_REPEAT;
@@ -546,6 +552,14 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216ME_MK3);
+ return 0;
+}
+
/* device-specific initialization */
static struct m920x_inits megasky_rc_init [] = {
{ M9206_RC_INIT2, 0xa8 },
@@ -562,6 +576,18 @@ static struct m920x_inits tvwalkertwin_rc_init [] = {
{ } /* terminating entry */
};
+static struct m920x_inits pinnacle310e_init[] = {
+ /* without these the tuner don't work */
+ { 0xff20, 0x9b },
+ { 0xff22, 0x70 },
+
+ /* rc settings */
+ { 0xff50, 0x80 },
+ { M9206_RC_INIT1, 0x00 },
+ { M9206_RC_INIT2, 0xff },
+ { } /* terminating entry */
+};
+
/* ir keymaps */
static struct dvb_usb_rc_key megasky_rc_keys [] = {
{ 0x0012, KEY_POWER },
@@ -602,11 +628,68 @@ static struct dvb_usb_rc_key tvwalkertwin_rc_keys [] = {
{ 0x001e, KEY_VOLUMEUP },
};
+static struct dvb_usb_rc_key pinnacle310e_rc_keys[] = {
+ { 0x16, KEY_POWER },
+ { 0x17, KEY_FAVORITES },
+ { 0x0f, KEY_TEXT },
+ { 0x48, KEY_MEDIA }, /* preview */
+ { 0x1c, KEY_EPG },
+ { 0x04, KEY_LIST }, /* record list */
+ { 0x03, KEY_1 },
+ { 0x01, KEY_2 },
+ { 0x06, KEY_3 },
+ { 0x09, KEY_4 },
+ { 0x1d, KEY_5 },
+ { 0x1f, KEY_6 },
+ { 0x0d, KEY_7 },
+ { 0x19, KEY_8 },
+ { 0x1b, KEY_9 },
+ { 0x15, KEY_0 },
+ { 0x0c, KEY_CANCEL },
+ { 0x4a, KEY_CLEAR },
+ { 0x13, KEY_BACK },
+ { 0x00, KEY_TAB },
+ { 0x4b, KEY_UP },
+ { 0x4e, KEY_LEFT },
+ { 0x52, KEY_RIGHT },
+ { 0x51, KEY_DOWN },
+ { 0x4f, KEY_ENTER }, /* could also be KEY_OK */
+ { 0x1e, KEY_VOLUMEUP },
+ { 0x0a, KEY_VOLUMEDOWN },
+ { 0x05, KEY_CHANNELUP },
+ { 0x02, KEY_CHANNELDOWN },
+ { 0x11, KEY_RECORD },
+ { 0x14, KEY_PLAY },
+ { 0x4c, KEY_PAUSE },
+ { 0x1a, KEY_STOP },
+ { 0x40, KEY_REWIND },
+ { 0x12, KEY_FASTFORWARD },
+ { 0x41, KEY_PREVIOUSSONG }, /* Replay */
+ { 0x42, KEY_NEXTSONG }, /* Skip */
+ { 0x54, KEY_CAMERA }, /* Capture */
+/* { 0x50, KEY_SAP }, */ /* Sap */
+ { 0x47, KEY_CYCLEWINDOWS }, /* Pip */
+ { 0x4d, KEY_SCREEN }, /* FullScreen */
+ { 0x08, KEY_SUBTITLE },
+ { 0x0e, KEY_MUTE },
+/* { 0x49, KEY_LR }, */ /* L/R */
+ { 0x07, KEY_SLEEP }, /* Hibernate */
+ { 0x08, KEY_MEDIA }, /* A/V */
+ { 0x0e, KEY_MENU }, /* Recall */
+ { 0x45, KEY_ZOOMIN },
+ { 0x46, KEY_ZOOMOUT },
+ { 0x18, KEY_TV }, /* Red */
+ { 0x53, KEY_VCR }, /* Green */
+ { 0x5e, KEY_SAT }, /* Yellow */
+ { 0x5f, KEY_PLAYER }, /* Blue */
+};
+
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties megasky_properties;
static struct dvb_usb_device_properties digivox_mini_ii_properties;
static struct dvb_usb_device_properties tvwalkertwin_properties;
static struct dvb_usb_device_properties dposh_properties;
+static struct dvb_usb_device_properties pinnacle_pctv310e_properties;
static int m920x_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -652,6 +735,13 @@ static int m920x_probe(struct usb_interface *intf,
goto found;
}
+ ret = dvb_usb_device_init(intf, &pinnacle_pctv310e_properties,
+ THIS_MODULE, &d, adapter_nr);
+ if (ret == 0) {
+ rc_init_seq = pinnacle310e_init;
+ goto found;
+ }
+
return ret;
} else {
/* Another interface on a multi-tuner device */
@@ -682,6 +772,7 @@ static struct usb_device_id m920x_table [] = {
USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM) },
{ USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) },
{ USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) },
+ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, m920x_table);
@@ -895,6 +986,56 @@ static struct dvb_usb_device_properties dposh_properties = {
}
};
+static struct dvb_usb_device_properties pinnacle_pctv310e_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = NULL,
+
+ .rc_interval = 100,
+ .rc_key_map = pinnacle310e_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(pinnacle310e_rc_keys),
+ .rc_query = m920x_rc_query,
+
+ .size_of_priv = sizeof(struct m920x_state),
+
+ .identify_state = m920x_identify_state,
+ .num_adapters = 1,
+ .adapter = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 8,
+ .pid_filter = m920x_pid_filter,
+ .pid_filter_ctrl = m920x_pid_filter_ctrl,
+
+ .frontend_attach = m920x_mt352_frontend_attach,
+ .tuner_attach = m920x_fmd1216me_tuner_attach,
+
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x84,
+ .u = {
+ .isoc = {
+ .framesperurb = 128,
+ .framesize = 564,
+ .interval = 1,
+ }
+ }
+ },
+ } },
+ .i2c_algo = &m920x_i2c_algo,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "Pinnacle PCTV 310e",
+ { &m920x_table[6], NULL },
+ { NULL },
+ }
+ }
+};
+
static struct usb_driver m920x_driver = {
.name = "dvb_usb_m920x",
.probe = m920x_probe,
diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h
index 37532890acc..3c061518ffc 100644
--- a/drivers/media/dvb/dvb-usb/m920x.h
+++ b/drivers/media/dvb/dvb-usb/m920x.h
@@ -18,7 +18,7 @@
#define M9206_FW 0x30
#define M9206_MAX_FILTERS 8
-#define M9206_MAX_ADAPTERS 2
+#define M9206_MAX_ADAPTERS 4
/*
sequences found in logs:
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c
index d4e23094167..830557696ae 100644
--- a/drivers/media/dvb/dvb-usb/opera1.c
+++ b/drivers/media/dvb/dvb-usb/opera1.c
@@ -138,7 +138,7 @@ static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
(msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0),
msg[i].buf,
msg[i].len
- )!= msg[i].len)) {
+ )) != msg[i].len) {
break;
}
if (dvb_usb_opera1_debug & 0x10)
diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c
index 7c5459c27b7..c3e0ec2dcfc 100644
--- a/drivers/media/dvb/firewire/firedtv-1394.c
+++ b/drivers/media/dvb/firewire/firedtv-1394.c
@@ -90,13 +90,14 @@ 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, __be32 data[])
+static int node_lock(struct firedtv *fdtv, u64 addr, void *data)
{
+ quadlet_t *d = data;
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];
+ ret = hpsb_node_lock(node_of(fdtv), addr,
+ EXTCODE_COMPARE_SWAP, &d[1], d[0]);
+ d[0] = d[1];
return ret;
}
@@ -192,9 +193,13 @@ static int node_probe(struct device *dev)
int kv_len, err;
void *kv_str;
- kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t);
- kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv);
-
+ if (ud->model_name_kv) {
+ kv_len = (ud->model_name_kv->value.leaf.len - 2) * 4;
+ kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv);
+ } else {
+ kv_len = 0;
+ kv_str = NULL;
+ }
fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len);
if (!fdtv)
return -ENOMEM;
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
index 50c42a4b972..1b31bebc27d 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/dvb/firewire/firedtv-avc.c
@@ -74,7 +74,6 @@
#define EN50221_TAG_CA_INFO 0x9f8031
struct avc_command_frame {
- int length;
u8 ctype;
u8 subunit;
u8 opcode;
@@ -82,13 +81,27 @@ struct avc_command_frame {
};
struct avc_response_frame {
- int length;
u8 response;
u8 subunit;
u8 opcode;
u8 operand[509];
};
+#define LAST_OPERAND (509 - 1)
+
+static inline void clear_operands(struct avc_command_frame *c, int from, int to)
+{
+ memset(&c->operand[from], 0, to - from + 1);
+}
+
+static void pad_operands(struct avc_command_frame *c, int from)
+{
+ int to = ALIGN(from, 4);
+
+ if (from <= to && to <= LAST_OPERAND)
+ clear_operands(c, from, to);
+}
+
#define AVC_DEBUG_READ_DESCRIPTOR 0x0001
#define AVC_DEBUG_DSIT 0x0002
#define AVC_DEBUG_DSD 0x0004
@@ -202,78 +215,65 @@ static void debug_pmt(char *msg, int length)
16, 1, msg, length, false);
}
-static int __avc_write(struct firedtv *fdtv,
- const struct avc_command_frame *c, struct avc_response_frame *r)
+static int avc_write(struct firedtv *fdtv)
{
int err, retry;
- if (r)
- fdtv->avc_reply_received = false;
+ fdtv->avc_reply_received = false;
for (retry = 0; retry < 6; retry++) {
if (unlikely(avc_debug))
- debug_fcp(&c->ctype, c->length);
+ debug_fcp(fdtv->avc_data, fdtv->avc_data_length);
err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER,
- (void *)&c->ctype, c->length);
+ fdtv->avc_data, fdtv->avc_data_length);
if (err) {
- fdtv->avc_reply_received = true;
dev_err(fdtv->device, "FCP command write failed\n");
+
return err;
}
- if (!r)
- return 0;
-
/*
* AV/C specs say that answers should be sent within 150 ms.
* Time out after 200 ms.
*/
if (wait_event_timeout(fdtv->avc_wait,
fdtv->avc_reply_received,
- msecs_to_jiffies(200)) != 0) {
- r->length = fdtv->response_length;
- memcpy(&r->response, fdtv->response, r->length);
-
+ msecs_to_jiffies(200)) != 0)
return 0;
- }
}
dev_err(fdtv->device, "FCP response timed out\n");
+
return -ETIMEDOUT;
}
-static int avc_write(struct firedtv *fdtv,
- const struct avc_command_frame *c, struct avc_response_frame *r)
+static bool is_register_rc(struct avc_response_frame *r)
{
- int ret;
-
- if (mutex_lock_interruptible(&fdtv->avc_mutex))
- return -EINTR;
-
- ret = __avc_write(fdtv, c, r);
-
- mutex_unlock(&fdtv->avc_mutex);
- return ret;
+ return r->opcode == AVC_OPCODE_VENDOR &&
+ r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 &&
+ r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 &&
+ r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 &&
+ r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL;
}
int avc_recv(struct firedtv *fdtv, void *data, size_t length)
{
- struct avc_response_frame *r =
- data - offsetof(struct avc_response_frame, response);
+ struct avc_response_frame *r = data;
if (unlikely(avc_debug))
debug_fcp(data, length);
- if (length >= 8 &&
- r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 &&
- r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 &&
- r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 &&
- r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) {
- if (r->response == AVC_RESPONSE_CHANGED) {
- fdtv_handle_rc(fdtv,
- r->operand[4] << 8 | r->operand[5]);
+ if (length >= 8 && is_register_rc(r)) {
+ switch (r->response) {
+ case AVC_RESPONSE_CHANGED:
+ fdtv_handle_rc(fdtv, r->operand[4] << 8 | r->operand[5]);
schedule_work(&fdtv->remote_ctrl_work);
- } else if (r->response != AVC_RESPONSE_INTERIM) {
+ break;
+ case AVC_RESPONSE_INTERIM:
+ if (is_register_rc((void *)fdtv->avc_data))
+ goto wake;
+ break;
+ default:
dev_info(fdtv->device,
"remote control result = %d\n", r->response);
}
@@ -285,9 +285,9 @@ int avc_recv(struct firedtv *fdtv, void *data, size_t length)
return -EIO;
}
- memcpy(fdtv->response, data, length);
- fdtv->response_length = length;
-
+ memcpy(fdtv->avc_data, data, length);
+ fdtv->avc_data_length = length;
+wake:
fdtv->avc_reply_received = true;
wake_up(&fdtv->avc_wait);
@@ -318,10 +318,11 @@ static int add_pid_filter(struct firedtv *fdtv, u8 *operand)
* tuning command for setting the relative LNB frequency
* (not supported by the AVC standard)
*/
-static void avc_tuner_tuneqpsk(struct firedtv *fdtv,
- struct dvb_frontend_parameters *params,
- struct avc_command_frame *c)
+static int avc_tuner_tuneqpsk(struct firedtv *fdtv,
+ struct dvb_frontend_parameters *params)
{
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+
c->opcode = AVC_OPCODE_VENDOR;
c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;
@@ -370,16 +371,18 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv,
c->operand[13] = 0x1;
c->operand[14] = 0xff;
c->operand[15] = 0xff;
- c->length = 20;
+
+ return 16;
} else {
- c->length = 16;
+ return 13;
}
}
-static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv,
- struct dvb_frontend_parameters *params,
- struct avc_command_frame *c)
+static int avc_tuner_dsd_dvb_c(struct firedtv *fdtv,
+ struct dvb_frontend_parameters *params)
{
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+
c->opcode = AVC_OPCODE_DSD;
c->operand[0] = 0; /* source plug */
@@ -440,15 +443,14 @@ static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv,
c->operand[20] = 0x00;
c->operand[21] = 0x00;
- /* Add PIDs to filter */
- c->length = ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4);
+ return 22 + add_pid_filter(fdtv, &c->operand[22]);
}
-static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv,
- struct dvb_frontend_parameters *params,
- struct avc_command_frame *c)
+static int avc_tuner_dsd_dvb_t(struct firedtv *fdtv,
+ struct dvb_frontend_parameters *params)
{
struct dvb_ofdm_parameters *ofdm = &params->u.ofdm;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
c->opcode = AVC_OPCODE_DSD;
@@ -543,55 +545,58 @@ static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv,
c->operand[15] = 0x00; /* network_ID[0] */
c->operand[16] = 0x00; /* network_ID[1] */
- /* Add PIDs to filter */
- c->length = ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4);
+ return 17 + add_pid_filter(fdtv, &c->operand[17]);
}
int avc_tuner_dsd(struct firedtv *fdtv,
struct dvb_frontend_parameters *params)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int pos, ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
switch (fdtv->type) {
case FIREDTV_DVB_S:
- case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break;
- case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params, c); break;
- case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params, c); break;
+ case FIREDTV_DVB_S2: pos = avc_tuner_tuneqpsk(fdtv, params); break;
+ case FIREDTV_DVB_C: pos = avc_tuner_dsd_dvb_c(fdtv, params); break;
+ case FIREDTV_DVB_T: pos = avc_tuner_dsd_dvb_t(fdtv, params); break;
default:
BUG();
}
+ pad_operands(c, pos);
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
-
- msleep(500);
+ fdtv->avc_data_length = ALIGN(3 + pos, 4);
+ ret = avc_write(fdtv);
#if 0
- /* FIXME: */
- /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */
+ /*
+ * FIXME:
+ * u8 *status was an out-parameter of avc_tuner_dsd, unused by caller.
+ * Check for AVC_RESPONSE_ACCEPTED here instead?
+ */
if (status)
*status = r->operand[2];
#endif
- return 0;
+ mutex_unlock(&fdtv->avc_mutex);
+
+ if (ret == 0)
+ msleep(500);
+
+ return ret;
}
int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[])
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */
- int pos, k;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int ret, pos, k;
if (pidc > 16 && pidc != 0xff)
return -EINVAL;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -614,24 +619,27 @@ int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[])
c->operand[pos++] = 0x00; /* tableID */
c->operand[pos++] = 0x00; /* filter_length */
}
+ pad_operands(c, pos);
- c->length = ALIGN(3 + pos, 4);
+ fdtv->avc_data_length = ALIGN(3 + pos, 4);
+ ret = avc_write(fdtv);
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /* FIXME: check response code? */
- msleep(50);
- return 0;
+ mutex_unlock(&fdtv->avc_mutex);
+
+ if (ret == 0)
+ msleep(50);
+
+ return ret;
}
int avc_tuner_get_ts(struct firedtv *fdtv)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */
- int sl;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int ret, sl;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -646,26 +654,33 @@ int avc_tuner_get_ts(struct firedtv *fdtv)
c->operand[4] = 0x00; /* antenna number */
c->operand[5] = 0x0; /* system_specific_search_flags */
c->operand[6] = sl; /* system_specific_multiplex selection_length */
- c->operand[7] = 0x00; /* valid_flags [0] */
- c->operand[8] = 0x00; /* valid_flags [1] */
- c->operand[7 + sl] = 0x00; /* nr_of_dsit_sel_specs (always 0) */
+ /*
+ * operand[7]: valid_flags[0]
+ * operand[8]: valid_flags[1]
+ * operand[7 + sl]: nr_of_dsit_sel_specs (always 0)
+ */
+ clear_operands(c, 7, 24);
- c->length = fdtv->type == FIREDTV_DVB_T ? 24 : 28;
+ fdtv->avc_data_length = fdtv->type == FIREDTV_DVB_T ? 24 : 28;
+ ret = avc_write(fdtv);
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /* FIXME: check response code? */
- msleep(250);
- return 0;
+ mutex_unlock(&fdtv->avc_mutex);
+
+ if (ret == 0)
+ msleep(250);
+
+ return ret;
}
int avc_identify_subunit(struct firedtv *fdtv)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -678,31 +693,34 @@ int avc_identify_subunit(struct firedtv *fdtv)
c->operand[4] = 0x08; /* length lowbyte */
c->operand[5] = 0x00; /* offset highbyte */
c->operand[6] = 0x0d; /* offset lowbyte */
+ clear_operands(c, 7, 8); /* padding */
- c->length = 12;
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
if ((r->response != AVC_RESPONSE_STABLE &&
r->response != AVC_RESPONSE_ACCEPTED) ||
(r->operand[3] << 8) + r->operand[4] != 8) {
dev_err(fdtv->device, "cannot read subunit identifier\n");
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+out:
+ mutex_unlock(&fdtv->avc_mutex);
+
+ return ret;
}
#define SIZEOF_ANTENNA_INPUT_INFO 22
int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
- int length;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int length, ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -710,27 +728,30 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat)
c->operand[0] = DESCRIPTOR_TUNER_STATUS;
c->operand[1] = 0xff; /* read_result_status */
- c->operand[2] = 0x00; /* reserved */
- c->operand[3] = 0; /* SIZEOF_ANTENNA_INPUT_INFO >> 8; */
- c->operand[4] = 0; /* SIZEOF_ANTENNA_INPUT_INFO & 0xff; */
- c->operand[5] = 0x00;
- c->operand[6] = 0x00;
-
- c->length = 12;
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /*
+ * operand[2]: reserved
+ * operand[3]: SIZEOF_ANTENNA_INPUT_INFO >> 8
+ * operand[4]: SIZEOF_ANTENNA_INPUT_INFO & 0xff
+ */
+ clear_operands(c, 2, 31);
+
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
if (r->response != AVC_RESPONSE_STABLE &&
r->response != AVC_RESPONSE_ACCEPTED) {
dev_err(fdtv->device, "cannot read tuner status\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
length = r->operand[9];
if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) {
dev_err(fdtv->device, "got invalid tuner status\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
stat->active_system = r->operand[10];
@@ -766,20 +787,21 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat)
stat->ca_dvb_flag = r->operand[31] >> 3 & 1;
stat->ca_error_flag = r->operand[31] >> 2 & 1;
stat->ca_initialization_status = r->operand[31] >> 1 & 1;
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst,
char conttone, char nrdiseq,
struct dvb_diseqc_master_cmd *diseqcmd)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
- int i, j, k;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int pos, j, k, ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -789,41 +811,41 @@ int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst,
c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;
c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;
c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL;
-
c->operand[4] = voltage;
c->operand[5] = nrdiseq;
- i = 6;
-
+ pos = 6;
for (j = 0; j < nrdiseq; j++) {
- c->operand[i++] = diseqcmd[j].msg_len;
+ c->operand[pos++] = diseqcmd[j].msg_len;
for (k = 0; k < diseqcmd[j].msg_len; k++)
- c->operand[i++] = diseqcmd[j].msg[k];
+ c->operand[pos++] = diseqcmd[j].msg[k];
}
+ c->operand[pos++] = burst;
+ c->operand[pos++] = conttone;
+ pad_operands(c, pos);
- c->operand[i++] = burst;
- c->operand[i++] = conttone;
-
- c->length = ALIGN(3 + i, 4);
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ fdtv->avc_data_length = ALIGN(3 + pos, 4);
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
if (r->response != AVC_RESPONSE_ACCEPTED) {
dev_err(fdtv->device, "LNB control failed\n");
- return -EINVAL;
+ ret = -EINVAL;
}
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
int avc_register_remote_control(struct firedtv *fdtv)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_NOTIFY;
c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7;
@@ -833,10 +855,16 @@ int avc_register_remote_control(struct firedtv *fdtv)
c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;
c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;
c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL;
+ c->operand[4] = 0; /* padding */
+
+ fdtv->avc_data_length = 8;
+ ret = avc_write(fdtv);
- c->length = 8;
+ /* FIXME: check response code? */
- return avc_write(fdtv, c, NULL);
+ mutex_unlock(&fdtv->avc_mutex);
+
+ return ret;
}
void avc_remote_ctrl_work(struct work_struct *work)
@@ -851,11 +879,10 @@ void avc_remote_ctrl_work(struct work_struct *work)
#if 0 /* FIXME: unused */
int avc_tuner_host2ca(struct firedtv *fdtv)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -867,15 +894,16 @@ int avc_tuner_host2ca(struct firedtv *fdtv)
c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA;
c->operand[4] = 0; /* slot */
c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */
- c->operand[6] = 0; /* more/last */
- c->operand[7] = 0; /* length */
+ clear_operands(c, 6, 8);
- c->length = 12;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /* FIXME: check response code? */
- return 0;
+ mutex_unlock(&fdtv->avc_mutex);
+
+ return ret;
}
#endif
@@ -906,12 +934,11 @@ static int get_ca_object_length(struct avc_response_frame *r)
int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
- int pos;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int pos, ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_STATUS;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -923,11 +950,12 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len)
c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;
c->operand[4] = 0; /* slot */
c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */
+ clear_operands(c, 6, LAST_OPERAND);
- c->length = 12;
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
/* FIXME: check response code and validate response data */
@@ -939,18 +967,19 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len)
app_info[4] = 0x01;
memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]);
*len = app_info[3] + 4;
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
- int pos;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int pos, ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_STATUS;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -962,11 +991,14 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len)
c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;
c->operand[4] = 0; /* slot */
c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */
+ clear_operands(c, 6, LAST_OPERAND);
- c->length = 12;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /* FIXME: check response code and validate response data */
pos = get_ca_object_pos(r);
app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff;
@@ -976,17 +1008,18 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len)
app_info[4] = r->operand[pos + 0];
app_info[5] = r->operand[pos + 1];
*len = app_info[3] + 4;
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
int avc_ca_reset(struct firedtv *fdtv)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -1002,19 +1035,20 @@ int avc_ca_reset(struct firedtv *fdtv)
c->operand[7] = 1; /* length */
c->operand[8] = 0; /* force hardware reset */
- c->length = 12;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /* FIXME: check response code? */
- return 0;
+ mutex_unlock(&fdtv->avc_mutex);
+
+ return ret;
}
int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
int list_management;
int program_info_length;
int pmt_cmd_id;
@@ -1022,11 +1056,12 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
int write_pos;
int es_info_length;
int crc32_csum;
+ int ret;
if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT))
debug_pmt(msg, length);
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_CONTROL;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -1058,7 +1093,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
c->operand[12] = 0x02; /* Table id=2 */
c->operand[13] = 0x80; /* Section syntax + length */
- /* c->operand[14] = XXXprogram_info_length + 12; */
+
c->operand[15] = msg[1]; /* Program number */
c->operand[16] = msg[2];
c->operand[17] = 0x01; /* Version number=0 + current/next=1 */
@@ -1106,12 +1141,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
write_pos += es_info_length;
}
}
-
- /* CRC */
- c->operand[write_pos++] = 0x00;
- c->operand[write_pos++] = 0x00;
- c->operand[write_pos++] = 0x00;
- c->operand[write_pos++] = 0x00;
+ write_pos += 4; /* CRC */
c->operand[7] = 0x82;
c->operand[8] = (write_pos - 10) >> 8;
@@ -1123,28 +1153,31 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff;
c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff;
c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff;
+ pad_operands(c, write_pos);
- c->length = ALIGN(3 + write_pos, 4);
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ fdtv->avc_data_length = ALIGN(3 + write_pos, 4);
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
if (r->response != AVC_RESPONSE_ACCEPTED) {
dev_err(fdtv->device,
"CA PMT failed with response 0x%x\n", r->response);
- return -EFAULT;
+ ret = -EFAULT;
}
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
int avc_ca_get_time_date(struct firedtv *fdtv, int *interval)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_STATUS;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -1156,28 +1189,28 @@ int avc_ca_get_time_date(struct firedtv *fdtv, int *interval)
c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;
c->operand[4] = 0; /* slot */
c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */
- c->operand[6] = 0; /* more/last */
- c->operand[7] = 0; /* length */
+ clear_operands(c, 6, LAST_OPERAND);
- c->length = 12;
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
/* FIXME: check response code and validate response data */
*interval = r->operand[get_ca_object_pos(r)];
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
int avc_ca_enter_menu(struct firedtv *fdtv)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_STATUS;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -1189,24 +1222,25 @@ int avc_ca_enter_menu(struct firedtv *fdtv)
c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA;
c->operand[4] = 0; /* slot */
c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU;
- c->operand[6] = 0; /* more/last */
- c->operand[7] = 0; /* length */
+ clear_operands(c, 6, 8);
- c->length = 12;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ /* FIXME: check response code? */
- return 0;
+ mutex_unlock(&fdtv->avc_mutex);
+
+ return ret;
}
int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len)
{
- char buffer[sizeof(struct avc_command_frame)];
- struct avc_command_frame *c = (void *)buffer;
- struct avc_response_frame *r = (void *)buffer;
+ struct avc_command_frame *c = (void *)fdtv->avc_data;
+ struct avc_response_frame *r = (void *)fdtv->avc_data;
+ int ret;
- memset(c, 0, sizeof(*c));
+ mutex_lock(&fdtv->avc_mutex);
c->ctype = AVC_CTYPE_STATUS;
c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;
@@ -1218,20 +1252,21 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len)
c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;
c->operand[4] = 0; /* slot */
c->operand[5] = SFE_VENDOR_TAG_CA_MMI;
- c->operand[6] = 0; /* more/last */
- c->operand[7] = 0; /* length */
+ clear_operands(c, 6, LAST_OPERAND);
- c->length = 12;
-
- if (avc_write(fdtv, c, r) < 0)
- return -EIO;
+ fdtv->avc_data_length = 12;
+ ret = avc_write(fdtv);
+ if (ret < 0)
+ goto out;
/* FIXME: check response code and validate response data */
*len = get_ca_object_length(r);
memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len);
+out:
+ mutex_unlock(&fdtv->avc_mutex);
- return 0;
+ return ret;
}
#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL
@@ -1240,14 +1275,14 @@ static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data)
{
int ret;
- if (mutex_lock_interruptible(&fdtv->avc_mutex))
- return -EINTR;
+ mutex_lock(&fdtv->avc_mutex);
ret = fdtv->backend->read(fdtv, addr, data);
if (ret < 0)
dev_err(fdtv->device, "CMP: read I/O error\n");
mutex_unlock(&fdtv->avc_mutex);
+
return ret;
}
@@ -1255,14 +1290,19 @@ static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
{
int ret;
- if (mutex_lock_interruptible(&fdtv->avc_mutex))
- return -EINTR;
+ mutex_lock(&fdtv->avc_mutex);
+
+ /* data[] is stack-allocated and should not be DMA-mapped. */
+ memcpy(fdtv->avc_data, data, 8);
- ret = fdtv->backend->lock(fdtv, addr, data);
+ ret = fdtv->backend->lock(fdtv, addr, fdtv->avc_data);
if (ret < 0)
dev_err(fdtv->device, "CMP: lock I/O error\n");
+ else
+ memcpy(data, fdtv->avc_data, 8);
mutex_unlock(&fdtv->avc_mutex);
+
return ret;
}
diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c
index fc9996c13e1..079e8c5b047 100644
--- a/drivers/media/dvb/firewire/firedtv-dvb.c
+++ b/drivers/media/dvb/firewire/firedtv-dvb.c
@@ -277,7 +277,6 @@ struct firedtv *fdtv_alloc(struct device *dev,
mutex_init(&fdtv->avc_mutex);
init_waitqueue_head(&fdtv->avc_wait);
- fdtv->avc_reply_received = true;
mutex_init(&fdtv->demux_mutex);
INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work);
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
index 6223bf01efe..7a3de16fba0 100644
--- a/drivers/media/dvb/firewire/firedtv-fw.c
+++ b/drivers/media/dvb/firewire/firedtv-fw.c
@@ -41,7 +41,7 @@ static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len,
return rcode != RCODE_COMPLETE ? -EIO : 0;
}
-static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
+static int node_lock(struct firedtv *fdtv, u64 addr, void *data)
{
return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP);
}
diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h
index 35080dbb3c6..78cc28f3691 100644
--- a/drivers/media/dvb/firewire/firedtv.h
+++ b/drivers/media/dvb/firewire/firedtv.h
@@ -73,7 +73,7 @@ struct input_dev;
struct firedtv;
struct firedtv_backend {
- int (*lock)(struct firedtv *fdtv, u64 addr, __be32 data[]);
+ int (*lock)(struct firedtv *fdtv, u64 addr, void *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);
@@ -114,8 +114,8 @@ struct firedtv {
unsigned long channel_active;
u16 channel_pid[16];
- size_t response_length;
- u8 response[512];
+ int avc_data_length;
+ u8 avc_data[512];
};
/* firedtv-1394.c */
diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h
index 28b90c91c76..e90fa92b1c1 100644
--- a/drivers/media/dvb/frontends/af9013.h
+++ b/drivers/media/dvb/frontends/af9013.h
@@ -44,6 +44,7 @@ enum af9013_tuner {
AF9013_TUNER_MT2060_2 = 147, /* Microtune */
AF9013_TUNER_TDA18271 = 156, /* NXP */
AF9013_TUNER_QT1010A = 162, /* Quantek */
+ AF9013_TUNER_TDA18218 = 179, /* NXP */
};
/* AF9013/5 GPIOs (mostly guessed)
diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c
index 59881a5944e..43aac2f85c2 100644
--- a/drivers/media/dvb/frontends/atbm8830.c
+++ b/drivers/media/dvb/frontends/atbm8830.c
@@ -170,6 +170,19 @@ static int is_locked(struct atbm_state *priv, u8 *locked)
return 0;
}
+static int set_agc_config(struct atbm_state *priv,
+ u8 min, u8 max, u8 hold_loop)
+{
+ /* no effect if both min and max are zero */
+ if (!min && !max)
+ return 0;
+
+ atbm8830_write_reg(priv, REG_AGC_MIN, min);
+ atbm8830_write_reg(priv, REG_AGC_MAX, max);
+ atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop);
+
+ return 0;
+}
static int set_static_channel_mode(struct atbm_state *priv)
{
@@ -227,6 +240,9 @@ static int atbm8830_init(struct dvb_frontend *fe)
/*Set IF frequency*/
set_if_freq(priv, cfg->if_freq);
+ /*Set AGC Config*/
+ set_agc_config(priv, cfg->agc_min, cfg->agc_max,
+ cfg->agc_hold_loop);
/*Set static channel mode*/
set_static_channel_mode(priv);
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
index 614552709a6..7eac178f57b 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -283,7 +283,7 @@ static int dib0090_sleep(struct dvb_frontend *fe)
return 0;
}
-extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
+void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
{
struct dib0090_state *state = fe->tuner_priv;
if (fast)
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index 6f6fa29d9ea..2aa97dd6a8a 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -1999,6 +1999,8 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
struct dib8000_state *state = fe->demodulator_priv;
int time, ret;
+ fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+
dib8000_set_output_mode(state, OUTMODE_HIGH_Z);
if (fe->ops.tuner_ops.set_params)
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
index e6f3d73db9d..980e02f1575 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -174,7 +174,7 @@ void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
EXPORT_SYMBOL(dibx000_exit_i2c_master);
-u32 systime()
+u32 systime(void)
{
struct timespec t;
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
index 3051b64aa17..445fa106806 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -192,8 +192,8 @@ static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_pa
spi_bias *= qam_tab[p->constellation];
spi_bias /= p->code_rate_HP + 1;
spi_bias /= (guard_tab[p->guard_interval] + 32);
- spi_bias *= 1000ULL;
- spi_bias /= 1000ULL + ppm/1000;
+ spi_bias *= 1000;
+ spi_bias /= 1000 + ppm/1000;
spi_bias *= p->code_rate_HP;
val0x04 = (p->transmission_mode << 2) | p->guard_interval;
diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c
index b181bf023ad..13437259eea 100644
--- a/drivers/media/dvb/frontends/lnbp21.c
+++ b/drivers/media/dvb/frontends/lnbp21.c
@@ -158,7 +158,8 @@ static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe,
/* override frontend ops */
fe->ops.set_voltage = lnbp21_set_voltage;
fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage;
- fe->ops.set_tone = lnbp21_set_tone;
+ if (!(override_clear & LNBH24_TEN)) /*22kHz logic controlled by demod*/
+ fe->ops.set_tone = lnbp21_set_tone;
printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr);
return fe;
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
index 9552a22ccff..d21a327db62 100644
--- a/drivers/media/dvb/frontends/si21xx.c
+++ b/drivers/media/dvb/frontends/si21xx.c
@@ -97,8 +97,6 @@
#define LNB_SUPPLY_CTRL_REG_4 0xce
#define LNB_SUPPLY_STATUS_REG 0xcf
-#define FALSE 0
-#define TRUE 1
#define FAIL -1
#define PASS 0
@@ -718,7 +716,7 @@ static int si21xx_set_frontend(struct dvb_frontend *fe,
int fine_tune_freq;
unsigned char sample_rate = 0;
/* boolean */
- unsigned int inband_interferer_ind;
+ bool inband_interferer_ind;
/* INTERMEDIATE VALUES */
int icoarse_tune_freq; /* MHz */
@@ -728,15 +726,8 @@ static int si21xx_set_frontend(struct dvb_frontend *fe,
unsigned int x1;
unsigned int x2;
int i;
- unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = {
- FALSE, FALSE, FALSE, FALSE, FALSE,
- FALSE, FALSE, FALSE, FALSE, FALSE
- };
- unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = {
- FALSE, FALSE, FALSE, FALSE, FALSE,
- FALSE, FALSE, FALSE, FALSE, FALSE
- };
-
+ bool inband_interferer_div2[ALLOWABLE_FS_COUNT];
+ bool inband_interferer_div4[ALLOWABLE_FS_COUNT];
int status;
/* allowable sample rates for ADC in MHz */
@@ -762,7 +753,7 @@ static int si21xx_set_frontend(struct dvb_frontend *fe,
}
for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
- inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE;
+ inband_interferer_div2[i] = inband_interferer_div4[i] = false;
if_limit_high = -700000;
if_limit_low = -100000;
@@ -798,7 +789,7 @@ static int si21xx_set_frontend(struct dvb_frontend *fe,
if (((band_low < x1) && (x1 < band_high)) ||
((band_low < x2) && (x2 < band_high)))
- inband_interferer_div4[i] = TRUE;
+ inband_interferer_div4[i] = true;
}
@@ -811,25 +802,28 @@ static int si21xx_set_frontend(struct dvb_frontend *fe,
if (((band_low < x1) && (x1 < band_high)) ||
((band_low < x2) && (x2 < band_high)))
- inband_interferer_div2[i] = TRUE;
+ inband_interferer_div2[i] = true;
}
- inband_interferer_ind = TRUE;
- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
- inband_interferer_ind &= inband_interferer_div2[i] |
- inband_interferer_div4[i];
+ inband_interferer_ind = true;
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ if (inband_interferer_div2[i] || inband_interferer_div4[i]) {
+ inband_interferer_ind = false;
+ break;
+ }
+ }
if (inband_interferer_ind) {
for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
- if (inband_interferer_div2[i] == FALSE) {
+ if (!inband_interferer_div2[i]) {
sample_rate = (u8) afs[i];
break;
}
}
} else {
for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
- if ((inband_interferer_div2[i] |
- inband_interferer_div4[i]) == FALSE) {
+ if ((inband_interferer_div2[i] ||
+ !inband_interferer_div4[i])) {
sample_rate = (u8) afs[i];
break;
}
diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h
index 29c3fa85c22..e3e35d1ce83 100644
--- a/drivers/media/dvb/frontends/stv0900.h
+++ b/drivers/media/dvb/frontends/stv0900.h
@@ -49,6 +49,8 @@ struct stv0900_config {
u8 tun2_maddress;
u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */
u8 tun2_adc;
+ u8 tun1_type;/* for now 3 for stb6100 auto, else - software */
+ u8 tun2_type;
/* Set device param to start dma */
int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
};
diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c
index 8762c86044a..01f8f1f802f 100644
--- a/drivers/media/dvb/frontends/stv0900_core.c
+++ b/drivers/media/dvb/frontends/stv0900_core.c
@@ -177,7 +177,7 @@ u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg)
return buf;
}
-void extract_mask_pos(u32 label, u8 *mask, u8 *pos)
+static void extract_mask_pos(u32 label, u8 *mask, u8 *pos)
{
u8 position = 0, i = 0;
@@ -218,7 +218,7 @@ u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label)
return val;
}
-enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp)
+static enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp)
{
s32 i;
@@ -282,7 +282,7 @@ enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp)
return STV0900_NO_ERROR;
}
-u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk)
+static u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk)
{
u32 mclk = 90000000, div = 0, ad_div = 0;
@@ -296,7 +296,7 @@ u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk)
return mclk;
}
-enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk)
+static enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk)
{
u32 m_div, clk_sel;
@@ -334,7 +334,7 @@ enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk)
return STV0900_NO_ERROR;
}
-u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr,
+static u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr,
enum fe_stv0900_demod_num demod)
{
u32 lsb, msb, hsb, err_val;
@@ -567,6 +567,46 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
}
}
+u32 stv0900_get_freq_auto(struct stv0900_internal *intp, int demod)
+{
+ u32 freq, round;
+ /* Formulat :
+ Tuner_Frequency(MHz) = Regs / 64
+ Tuner_granularity(MHz) = Regs / 2048
+ real_Tuner_Frequency = Tuner_Frequency(MHz) - Tuner_granularity(MHz)
+ */
+ freq = (stv0900_get_bits(intp, TUN_RFFREQ2) << 10) +
+ (stv0900_get_bits(intp, TUN_RFFREQ1) << 2) +
+ stv0900_get_bits(intp, TUN_RFFREQ0);
+
+ freq = (freq * 1000) / 64;
+
+ round = (stv0900_get_bits(intp, TUN_RFRESTE1) >> 2) +
+ stv0900_get_bits(intp, TUN_RFRESTE0);
+
+ round = (round * 1000) / 2048;
+
+ return freq + round;
+}
+
+void stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency,
+ u32 Bandwidth, int demod)
+{
+ u32 tunerFrequency;
+ /* Formulat:
+ Tuner_frequency_reg= Frequency(MHz)*64
+ */
+ tunerFrequency = (Frequency * 64) / 1000;
+
+ stv0900_write_bits(intp, TUN_RFFREQ2, (tunerFrequency >> 10));
+ stv0900_write_bits(intp, TUN_RFFREQ1, (tunerFrequency >> 2) & 0xff);
+ stv0900_write_bits(intp, TUN_RFFREQ0, (tunerFrequency & 0x03));
+ /* Low Pass Filter = BW /2 (MHz)*/
+ stv0900_write_bits(intp, TUN_BW, Bandwidth / 2000000);
+ /* Tuner Write trig */
+ stv0900_write_reg(intp, TNRLD, 1);
+}
+
static s32 stv0900_get_rf_level(struct stv0900_internal *intp,
const struct stv0900_table *lookup,
enum fe_stv0900_demod_num demod)
@@ -1329,7 +1369,6 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
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,
@@ -1345,7 +1384,14 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
} else {
state->internal = kmalloc(sizeof(struct stv0900_internal),
GFP_KERNEL);
+ if (state->internal == NULL)
+ return STV0900_INVALID_HANDLE;
temp_int = append_internal(state->internal);
+ if (temp_int == NULL) {
+ kfree(state->internal);
+ state->internal = NULL;
+ return STV0900_INVALID_HANDLE;
+ }
state->internal->dmds_used = 1;
state->internal->i2c_adap = state->i2c_adap;
state->internal->i2c_addr = state->config->demod_address;
@@ -1371,11 +1417,6 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
return error;
}
- if (state->internal == NULL) {
- error = STV0900_INVALID_HANDLE;
- return error;
- }
-
intp = state->internal;
intp->demod_mode = p_init->demod_mode;
@@ -1404,6 +1445,27 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0);
}
+ intp->tuner_type[0] = p_init->tuner1_type;
+ intp->tuner_type[1] = p_init->tuner2_type;
+ /* tuner init */
+ switch (p_init->tuner1_type) {
+ case 3: /*FE_AUTO_STB6100:*/
+ stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x3c);
+ stv0900_write_reg(intp, R0900_P1_TNRCFG2, 0x86);
+ stv0900_write_reg(intp, R0900_P1_TNRCFG3, 0x18);
+ stv0900_write_reg(intp, R0900_P1_TNRXTAL, 27); /* 27MHz */
+ stv0900_write_reg(intp, R0900_P1_TNRSTEPS, 0x05);
+ stv0900_write_reg(intp, R0900_P1_TNRGAIN, 0x17);
+ stv0900_write_reg(intp, R0900_P1_TNRADJ, 0x1f);
+ stv0900_write_reg(intp, R0900_P1_TNRCTL2, 0x0);
+ stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 3);
+ break;
+ /* case FE_SW_TUNER: */
+ default:
+ stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 6);
+ break;
+ }
+
stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress);
switch (p_init->tuner1_adc) {
case 1:
@@ -1413,6 +1475,27 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
break;
}
+ stv0900_write_reg(intp, R0900_P1_TNRLD, 1); /* hw tuner */
+
+ /* tuner init */
+ switch (p_init->tuner2_type) {
+ case 3: /*FE_AUTO_STB6100:*/
+ stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x3c);
+ stv0900_write_reg(intp, R0900_P2_TNRCFG2, 0x86);
+ stv0900_write_reg(intp, R0900_P2_TNRCFG3, 0x18);
+ stv0900_write_reg(intp, R0900_P2_TNRXTAL, 27); /* 27MHz */
+ stv0900_write_reg(intp, R0900_P2_TNRSTEPS, 0x05);
+ stv0900_write_reg(intp, R0900_P2_TNRGAIN, 0x17);
+ stv0900_write_reg(intp, R0900_P2_TNRADJ, 0x1f);
+ stv0900_write_reg(intp, R0900_P2_TNRCTL2, 0x0);
+ stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 3);
+ break;
+ /* case FE_SW_TUNER: */
+ default:
+ stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 6);
+ break;
+ }
+
stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress);
switch (p_init->tuner2_adc) {
case 1:
@@ -1422,6 +1505,8 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
break;
}
+ stv0900_write_reg(intp, R0900_P2_TNRLD, 1); /* hw tuner */
+
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);
@@ -1824,10 +1909,12 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
init_params.tun1_maddress = config->tun1_maddress;
init_params.tun1_iq_inv = STV0900_IQ_NORMAL;
init_params.tuner1_adc = config->tun1_adc;
+ init_params.tuner1_type = config->tun1_type;
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.tuner2_type = config->tun2_type;
init_params.tun2_iq_inv = STV0900_IQ_SWAPPED;
err_stv0900 = stv0900_init_internal(&state->frontend,
diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb/frontends/stv0900_priv.h
index d8ba8a984ab..b62b0f0a4fe 100644
--- a/drivers/media/dvb/frontends/stv0900_priv.h
+++ b/drivers/media/dvb/frontends/stv0900_priv.h
@@ -247,6 +247,7 @@ struct stv0900_init_params{
u8 tun1_maddress;
int tuner1_adc;
+ int tuner1_type;
/* IQ from the tuner1 to the demod */
enum stv0900_iq_inversion tun1_iq_inv;
@@ -254,6 +255,7 @@ struct stv0900_init_params{
u8 tun2_maddress;
int tuner2_adc;
+ int tuner2_type;
/* IQ from the tuner2 to the demod */
enum stv0900_iq_inversion tun2_iq_inv;
@@ -309,6 +311,8 @@ struct stv0900_internal{
s32 bw[2];
s32 symbol_rate[2];
s32 srch_range[2];
+ /* for software/auto tuner */
+ int tuner_type[2];
/* algorithm for search Blind, Cold or Warm*/
enum fe_stv0900_search_algo srch_algo[2];
@@ -394,4 +398,11 @@ extern enum
fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
enum fe_stv0900_demod_num demod);
+extern u32
+stv0900_get_freq_auto(struct stv0900_internal *intp, int demod);
+
+extern void
+stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency,
+ u32 Bandwidth, int demod);
+
#endif
diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb/frontends/stv0900_reg.h
index 7b8edf192e9..731afe93a82 100644
--- a/drivers/media/dvb/frontends/stv0900_reg.h
+++ b/drivers/media/dvb/frontends/stv0900_reg.h
@@ -3174,17 +3174,21 @@ extern s32 shiftx(s32 x, int demod, s32 shift);
#define R0900_P1_TNRRF1 0xf4e9
#define TNRRF1 REGx(R0900_P1_TNRRF1)
#define F0900_P1_TUN_RFFREQ2 0xf4e900ff
+#define TUN_RFFREQ2 FLDx(F0900_P1_TUN_RFFREQ2)
/*P1_TNRRF0*/
#define R0900_P1_TNRRF0 0xf4ea
#define TNRRF0 REGx(R0900_P1_TNRRF0)
#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff
+#define TUN_RFFREQ1 FLDx(F0900_P1_TUN_RFFREQ1)
/*P1_TNRBW*/
#define R0900_P1_TNRBW 0xf4eb
#define TNRBW REGx(R0900_P1_TNRBW)
#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0
+#define TUN_RFFREQ0 FLDx(F0900_P1_TUN_RFFREQ0)
#define F0900_P1_TUN_BW 0xf4eb003f
+#define TUN_BW FLDx(F0900_P1_TUN_BW)
/*P1_TNRADJ*/
#define R0900_P1_TNRADJ 0xf4ec
@@ -3234,11 +3238,13 @@ extern s32 shiftx(s32 x, int demod, s32 shift);
#define F0900_P1_TUN_I2CLOCKED 0xf4f60010
#define F0900_P1_TUN_PROGDONE 0xf4f6000c
#define F0900_P1_TUN_RFRESTE1 0xf4f60003
+#define TUN_RFRESTE1 FLDx(F0900_P1_TUN_RFRESTE1)
/*P1_TNRRESTE*/
#define R0900_P1_TNRRESTE 0xf4f7
#define TNRRESTE REGx(R0900_P1_TNRRESTE)
#define F0900_P1_TUN_RFRESTE0 0xf4f700ff
+#define TUN_RFRESTE0 FLDx(F0900_P1_TUN_RFRESTE0)
/*P1_SMAPCOEF7*/
#define R0900_P1_SMAPCOEF7 0xf500
diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c
index b8da87fa637..ba0709b2d43 100644
--- a/drivers/media/dvb/frontends/stv0900_sw.c
+++ b/drivers/media/dvb/frontends/stv0900_sw.c
@@ -193,7 +193,7 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp,
return lock;
}
-int stv0900_sw_algo(struct stv0900_internal *intp,
+static int stv0900_sw_algo(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
int lock = FALSE,
@@ -606,7 +606,12 @@ static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe,
tuner_freq -= (current_step * currier_step);
if (intp->chip_id <= 0x20) {
- stv0900_set_tuner(fe, tuner_freq, intp->bw[d]);
+ if (intp->tuner_type[d] == 3)
+ stv0900_set_tuner_auto(intp, tuner_freq,
+ intp->bw[d], demod);
+ else
+ 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);
@@ -790,7 +795,7 @@ static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp,
return prate;
}
-void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp,
+static void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod,
u32 srate)
{
@@ -976,8 +981,16 @@ static void stv0900_track_optimization(struct dvb_frontend *fe)
intp->rolloff) + 10000000;
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 (intp->srch_algo[demod] != STV0900_WARM_START) {
+ if (intp->tuner_type[demod] == 3)
+ stv0900_set_tuner_auto(intp,
+ intp->freq[demod],
+ intp->bw[demod],
+ demod);
+ else
+ stv0900_set_bandwidth(fe,
+ intp->bw[demod]);
+ }
}
if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) ||
@@ -1202,7 +1215,11 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
}
result->standard = stv0900_get_standard(fe, d);
- result->frequency = stv0900_get_tuner_freq(fe);
+ if (intp->tuner_type[demod] == 3)
+ result->frequency = stv0900_get_freq_auto(intp, d);
+ else
+ 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);
@@ -1213,6 +1230,9 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
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);
+
+ dprintk("%s: modcode=0x%x \n", __func__, result->modcode);
+
switch (result->standard) {
case STV0900_DVBS2_STANDARD:
result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD);
@@ -1239,7 +1259,11 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *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 (intp->tuner_type[demod] == 3)
+ intp->freq[d] = stv0900_get_freq_auto(intp, d);
+ else
+ intp->freq[d] = stv0900_get_tuner_freq(fe);
+
if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
range = STV0900_RANGEOK;
else if (ABS(offsetFreq) <=
@@ -1481,7 +1505,12 @@ static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe)
else
tuner_freq -= (current_step * currier_step);
- stv0900_set_tuner(fe, tuner_freq, intp->bw[demod]);
+ if (intp->tuner_type[demod] == 3)
+ stv0900_set_tuner_auto(intp, tuner_freq,
+ intp->bw[demod], demod);
+ else
+ stv0900_set_tuner(fe, tuner_freq,
+ intp->bw[demod]);
}
}
@@ -1608,7 +1637,8 @@ static int stv0900_blind_search_algo(struct dvb_frontend *fe)
agc2_int = stv0900_blind_check_agc2_min_level(intp, demod);
- if (agc2_int > STV0900_BLIND_SEARCH_AGC2_TH)
+ dprintk("%s agc2_int=%d agc2_th=%d \n", __func__, agc2_int, agc2_th);
+ if (agc2_int > agc2_th)
return FALSE;
if (intp->chip_id == 0x10)
@@ -1875,7 +1905,11 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
}
- stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]);
+ if (intp->tuner_type[demod] == 3)
+ stv0900_set_tuner_auto(intp, intp->freq[demod],
+ intp->bw[demod], demod);
+ else
+ stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]);
agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1),
stv0900_get_bits(intp, AGCIQ_VALUE0));
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 1573466a5c7..c52c3357dc5 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -37,7 +37,82 @@
static unsigned int verbose;
module_param(verbose, int, 0644);
-struct mutex demod_lock;
+/* internal params node */
+struct stv090x_dev {
+ /* pointer for internal params, one for each pair of demods */
+ struct stv090x_internal *internal;
+ struct stv090x_dev *next_dev;
+};
+
+/* first internal params */
+static struct stv090x_dev *stv090x_first_dev;
+
+/* find chip by i2c adapter and i2c address */
+static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr)
+{
+ struct stv090x_dev *temp_dev = stv090x_first_dev;
+
+ /*
+ Search of the last stv0900 chip or
+ find it by i2c adapter and i2c address */
+ while ((temp_dev != NULL) &&
+ ((temp_dev->internal->i2c_adap != i2c_adap) ||
+ (temp_dev->internal->i2c_addr != i2c_addr))) {
+
+ temp_dev = temp_dev->next_dev;
+ }
+
+ return temp_dev;
+}
+
+/* deallocating chip */
+static void remove_dev(struct stv090x_internal *internal)
+{
+ struct stv090x_dev *prev_dev = stv090x_first_dev;
+ struct stv090x_dev *del_dev = find_dev(internal->i2c_adap,
+ internal->i2c_addr);
+
+ if (del_dev != NULL) {
+ if (del_dev == stv090x_first_dev) {
+ stv090x_first_dev = del_dev->next_dev;
+ } else {
+ while (prev_dev->next_dev != del_dev)
+ prev_dev = prev_dev->next_dev;
+
+ prev_dev->next_dev = del_dev->next_dev;
+ }
+
+ kfree(del_dev);
+ }
+}
+
+/* allocating new chip */
+static struct stv090x_dev *append_internal(struct stv090x_internal *internal)
+{
+ struct stv090x_dev *new_dev;
+ struct stv090x_dev *temp_dev;
+
+ new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL);
+ if (new_dev != NULL) {
+ new_dev->internal = internal;
+ new_dev->next_dev = NULL;
+
+ /* append to list */
+ if (stv090x_first_dev == NULL) {
+ stv090x_first_dev = new_dev;
+ } else {
+ temp_dev = stv090x_first_dev;
+ while (temp_dev->next_dev != NULL)
+ temp_dev = temp_dev->next_dev;
+
+ temp_dev->next_dev = new_dev;
+ }
+ }
+
+ return new_dev;
+}
+
/* DVBS1 and DSS C/N Lookup table */
static const struct stv090x_tab stv090x_s1cn_tab[] = {
@@ -683,6 +758,9 @@ static int stv090x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
struct stv090x_state *state = fe->demodulator_priv;
u32 reg;
+ if (enable)
+ mutex_lock(&state->internal->tuner_lock);
+
reg = STV090x_READ_DEMOD(state, I2CRPT);
if (enable) {
dprintk(FE_DEBUG, 1, "Enable Gate");
@@ -696,9 +774,14 @@ static int stv090x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0)
goto err;
}
+
+ if (!enable)
+ mutex_unlock(&state->internal->tuner_lock);
+
return 0;
err:
dprintk(FE_ERROR, 1, "I/O error");
+ mutex_unlock(&state->internal->tuner_lock);
return -1;
}
@@ -755,13 +838,13 @@ static int stv090x_set_srate(struct stv090x_state *state, u32 srate)
if (srate > 60000000) {
sym = (srate << 4); /* SR * 2^16 / master_clk */
- sym /= (state->mclk >> 12);
+ sym /= (state->internal->mclk >> 12);
} else if (srate > 6000000) {
sym = (srate << 6);
- sym /= (state->mclk >> 10);
+ sym /= (state->internal->mclk >> 10);
} else {
sym = (srate << 9);
- sym /= (state->mclk >> 7);
+ sym /= (state->internal->mclk >> 7);
}
if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */
@@ -782,13 +865,13 @@ static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate
srate = 105 * (srate / 100);
if (srate > 60000000) {
sym = (srate << 4); /* SR * 2^16 / master_clk */
- sym /= (state->mclk >> 12);
+ sym /= (state->internal->mclk >> 12);
} else if (srate > 6000000) {
sym = (srate << 6);
- sym /= (state->mclk >> 10);
+ sym /= (state->internal->mclk >> 10);
} else {
sym = (srate << 9);
- sym /= (state->mclk >> 7);
+ sym /= (state->internal->mclk >> 7);
}
if (sym < 0x7fff) {
@@ -816,13 +899,13 @@ static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate
srate = 95 * (srate / 100);
if (srate > 60000000) {
sym = (srate << 4); /* SR * 2^16 / master_clk */
- sym /= (state->mclk >> 12);
+ sym /= (state->internal->mclk >> 12);
} else if (srate > 6000000) {
sym = (srate << 6);
- sym /= (state->mclk >> 10);
+ sym /= (state->internal->mclk >> 10);
} else {
sym = (srate << 9);
- sym /= (state->mclk >> 7);
+ sym /= (state->internal->mclk >> 7);
}
if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */
@@ -1103,21 +1186,21 @@ static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable)
switch (state->demod) {
case STV090x_DEMODULATOR_0:
- mutex_lock(&demod_lock);
+ mutex_lock(&state->internal->demod_lock);
reg = stv090x_read_reg(state, STV090x_STOPCLK2);
STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable);
if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
goto err;
- mutex_unlock(&demod_lock);
+ mutex_unlock(&state->internal->demod_lock);
break;
case STV090x_DEMODULATOR_1:
- mutex_lock(&demod_lock);
+ mutex_lock(&state->internal->demod_lock);
reg = stv090x_read_reg(state, STV090x_STOPCLK2);
STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable);
if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
goto err;
- mutex_unlock(&demod_lock);
+ mutex_unlock(&state->internal->demod_lock);
break;
default:
@@ -1126,14 +1209,14 @@ static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable)
}
return 0;
err:
- mutex_unlock(&demod_lock);
+ mutex_unlock(&state->internal->demod_lock);
dprintk(FE_ERROR, 1, "I/O error");
return -1;
}
static int stv090x_dvbs_track_crl(struct stv090x_state *state)
{
- if (state->dev_ver >= 0x30) {
+ if (state->internal->dev_ver >= 0x30) {
/* Set ACLC BCLC optimised value vs SR */
if (state->srate >= 15000000) {
if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0)
@@ -1215,7 +1298,7 @@ static int stv090x_delivery_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0)
goto err;
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
/* enable S2 carrier loop */
if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0)
goto err;
@@ -1246,6 +1329,10 @@ static int stv090x_delivery_search(struct stv090x_state *state)
default:
/* enable DVB-S2 and DVB-S2 in Auto MODE */
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)
@@ -1257,7 +1344,7 @@ static int stv090x_delivery_search(struct stv090x_state *state)
if (stv090x_dvbs_track_crl(state) < 0)
goto err;
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
/* enable S2 carrier loop */
if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0)
goto err;
@@ -1304,7 +1391,7 @@ static int stv090x_start_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
goto err;
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
if (state->srate <= 5000000) {
if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0)
goto err;
@@ -1348,7 +1435,7 @@ static int stv090x_start_search(struct stv090x_state *state)
* CFR max = +1MHz
*/
freq_abs = 1000 << 16;
- freq_abs /= (state->mclk / 1000);
+ freq_abs /= (state->internal->mclk / 1000);
freq = (s16) freq_abs;
} else {
/* COLD Start
@@ -1358,7 +1445,7 @@ static int stv090x_start_search(struct stv090x_state *state)
*/
freq_abs = (state->search_range / 2000) + 600;
freq_abs = freq_abs << 16;
- freq_abs /= (state->mclk / 1000);
+ freq_abs /= (state->internal->mclk / 1000);
freq = (s16) freq_abs;
}
@@ -1381,7 +1468,7 @@ static int stv090x_start_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0)
goto err;
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0)
@@ -1418,10 +1505,10 @@ static int stv090x_start_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0)
goto err;
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
/*Frequency offset detector setting*/
if (state->srate < 2000000) {
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
/* Cut 2 */
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0)
goto err;
@@ -1512,7 +1599,7 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
steps = 1;
dir = 1;
- freq_step = (1000000 * 256) / (state->mclk / 256);
+ freq_step = (1000000 * 256) / (state->internal->mclk / 256);
freq_init = 0;
for (i = 0; i < steps; i++) {
@@ -1583,7 +1670,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg;
u32 agc2th;
- if (state->dev_ver >= 0x30)
+ if (state->internal->dev_ver >= 0x30)
agc2th = 0x2e00;
else
agc2th = 0x1f00;
@@ -1619,13 +1706,13 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0)
goto err;
- if (state->dev_ver >= 0x30) {
+ if (state->internal->dev_ver >= 0x30) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0)
goto err;
- } else if (state->dev_ver >= 0x20) {
+ } else if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0)
@@ -1677,7 +1764,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
STV090x_READ_DEMOD(state, AGC2I0);
}
agc2 /= 10;
- srate_coarse = stv090x_get_srate(state, state->mclk);
+ srate_coarse = stv090x_get_srate(state, state->internal->mclk);
cur_step++;
dir *= -1;
if ((tmg_cpt >= 5) && (agc2 < agc2th) &&
@@ -1695,12 +1782,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
if (state->config->tuner_set_frequency) {
if (state->config->tuner_set_frequency(fe, freq) < 0)
- goto err;
+ goto err_gateoff;
}
if (state->config->tuner_set_bandwidth) {
if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
@@ -1713,7 +1800,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
if (state->config->tuner_get_status) {
if (state->config->tuner_get_status(fe, &reg) < 0)
- goto err;
+ goto err_gateoff;
}
if (reg)
@@ -1729,9 +1816,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
if (!tmg_lock)
srate_coarse = 0;
else
- srate_coarse = stv090x_get_srate(state, state->mclk);
+ srate_coarse = stv090x_get_srate(state, state->internal->mclk);
return srate_coarse;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(fe, 0);
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
@@ -1741,7 +1831,7 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
{
u32 srate_coarse, freq_coarse, sym, reg;
- srate_coarse = stv090x_get_srate(state, state->mclk);
+ srate_coarse = stv090x_get_srate(state, state->internal->mclk);
freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8;
freq_coarse |= STV090x_READ_DEMOD(state, CFR1);
sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
@@ -1767,10 +1857,10 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
goto err;
- if (state->dev_ver >= 0x30) {
+ if (state->internal->dev_ver >= 0x30) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0)
goto err;
- } else if (state->dev_ver >= 0x20) {
+ } else if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
goto err;
}
@@ -1778,20 +1868,20 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
if (srate_coarse > 3000000) {
sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
sym = (sym / 1000) * 65536;
- sym /= (state->mclk / 1000);
+ sym /= (state->internal->mclk / 1000);
if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0)
goto err;
sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */
sym = (sym / 1000) * 65536;
- sym /= (state->mclk / 1000);
+ sym /= (state->internal->mclk / 1000);
if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0)
goto err;
sym = (srate_coarse / 1000) * 65536;
- sym /= (state->mclk / 1000);
+ sym /= (state->internal->mclk / 1000);
if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0)
@@ -1799,20 +1889,20 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
} else {
sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
sym = (sym / 100) * 65536;
- sym /= (state->mclk / 100);
+ sym /= (state->internal->mclk / 100);
if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0)
goto err;
sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */
sym = (sym / 100) * 65536;
- sym /= (state->mclk / 100);
+ sym /= (state->internal->mclk / 100);
if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0)
goto err;
sym = (srate_coarse / 100) * 65536;
- sym /= (state->mclk / 100);
+ sym /= (state->internal->mclk / 100);
if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0)
@@ -1874,18 +1964,19 @@ static int stv090x_blind_search(struct stv090x_state *state)
u32 agc2, reg, srate_coarse;
s32 cpt_fail, agc2_ovflw, i;
u8 k_ref, k_max, k_min;
- int coarse_fail, lock;
+ int coarse_fail = 0;
+ int lock;
k_max = 110;
k_min = 10;
agc2 = stv090x_get_agc2_min_level(state);
- if (agc2 > STV090x_SEARCH_AGC2_TH(state->dev_ver)) {
+ if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) {
lock = 0;
} else {
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0)
goto err;
} else {
@@ -1897,7 +1988,7 @@ static int stv090x_blind_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0)
goto err;
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0)
@@ -1956,7 +2047,7 @@ static int stv090x_chk_tmg(struct stv090x_state *state)
u32 reg;
s32 tmg_cpt = 0, i;
u8 freq, tmg_thh, tmg_thl;
- int tmg_lock;
+ int tmg_lock = 0;
freq = STV090x_READ_DEMOD(state, CARFREQ);
tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE);
@@ -2080,12 +2171,12 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
if (state->config->tuner_set_frequency) {
if (state->config->tuner_set_frequency(fe, freq) < 0)
- goto err;
+ goto err_gateoff;
}
if (state->config->tuner_set_bandwidth) {
if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
@@ -2098,7 +2189,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
if (state->config->tuner_get_status) {
if (state->config->tuner_get_status(fe, &reg) < 0)
- goto err;
+ goto err_gateoff;
}
if (reg)
@@ -2129,6 +2220,8 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
return lock;
+err_gateoff:
+ stv090x_i2c_gate_ctrl(fe, 0);
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
@@ -2142,13 +2235,13 @@ static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s
car_max = state->search_range / 1000;
car_max += car_max / 10;
car_max = 65536 * (car_max / 2);
- car_max /= (state->mclk / 1000);
+ car_max /= (state->internal->mclk / 1000);
if (car_max > 0x4000)
car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */
inc = srate;
- inc /= state->mclk / 1000;
+ inc /= state->internal->mclk / 1000;
inc *= 256;
inc *= 256;
inc /= 1000;
@@ -2209,7 +2302,7 @@ static int stv090x_chk_signal(struct stv090x_state *state)
car_max += (car_max / 10); /* 10% margin */
car_max = (65536 * car_max / 2);
- car_max /= state->mclk / 1000;
+ car_max /= state->internal->mclk / 1000;
if (car_max > 0x4000)
car_max = 0x4000;
@@ -2234,7 +2327,7 @@ static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 tim
car_max = state->search_range / 1000;
car_max += (car_max / 10);
car_max = (65536 * car_max / 2);
- car_max /= (state->mclk / 1000);
+ car_max /= (state->internal->mclk / 1000);
if (car_max > 0x4000)
car_max = 0x4000;
@@ -2304,7 +2397,7 @@ static int stv090x_sw_algo(struct stv090x_state *state)
case STV090x_SEARCH_DVBS1:
case STV090x_SEARCH_DSS:
/* accelerate the frequency detector */
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0)
goto err;
}
@@ -2315,7 +2408,7 @@ static int stv090x_sw_algo(struct stv090x_state *state)
break;
case STV090x_SEARCH_DVBS2:
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
goto err;
}
@@ -2328,7 +2421,7 @@ static int stv090x_sw_algo(struct stv090x_state *state)
case STV090x_SEARCH_AUTO:
default:
/* accelerate the frequency detector */
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
@@ -2350,7 +2443,7 @@ static int stv090x_sw_algo(struct stv090x_state *state)
/*run the SW search 2 times maximum*/
if (lock || no_signal || (trials == 2)) {
/*Check if the demod is not losing lock in DVBS2*/
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
@@ -2372,7 +2465,7 @@ static int stv090x_sw_algo(struct stv090x_state *state)
/*FALSE lock, The demod is loosing lock */
lock = 0;
if (trials < 2) {
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
goto err;
}
@@ -2422,11 +2515,11 @@ static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk)
derot |= STV090x_READ_DEMOD(state, CFR0);
derot = comp2(derot, 24);
- int_1 = state->mclk >> 12;
+ int_1 = mclk >> 12;
int_2 = derot >> 12;
/* carrier_frequency = MasterClock * Reg / 2^24 */
- tmp_1 = state->mclk % 0x1000;
+ tmp_1 = mclk % 0x1000;
tmp_2 = derot % 0x1000;
derot = (int_1 * int_2) +
@@ -2502,13 +2595,13 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st
if (state->config->tuner_get_frequency) {
if (state->config->tuner_get_frequency(fe, &state->frequency) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
goto err;
- offst_freq = stv090x_get_car_freq(state, state->mclk) / 1000;
+ offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000;
state->frequency += offst_freq;
if (stv090x_get_viterbi(state) < 0)
@@ -2530,7 +2623,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st
if (state->config->tuner_get_frequency) {
if (state->config->tuner_get_frequency(fe, &state->frequency) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
@@ -2550,6 +2643,9 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st
}
return STV090x_OUTOFRANGE;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(fe, 0);
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
@@ -2579,7 +2675,7 @@ static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_mod
s32 i;
struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low;
- if (state->dev_ver == 0x20) {
+ if (state->internal->dev_ver == 0x20) {
car_loop = stv090x_s2_crl_cut20;
car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20;
car_loop_apsk_low = stv090x_s2_apsk_crl_cut20;
@@ -2700,7 +2796,7 @@ static u8 stv090x_optimize_carloop_short(struct stv090x_state *state)
break;
}
- if (state->dev_ver >= 0x30) {
+ if (state->internal->dev_ver >= 0x30) {
/* Cut 3.0 and up */
short_crl = stv090x_s2_short_crl_cut30;
} else {
@@ -2732,7 +2828,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0;
u32 reg;
- srate = stv090x_get_srate(state, state->mclk);
+ srate = stv090x_get_srate(state, state->internal->mclk);
srate += stv090x_get_tmgoffst(state, srate);
switch (state->delsys) {
@@ -2751,7 +2847,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
goto err;
- if (state->dev_ver >= 0x30) {
+ if (state->internal->dev_ver >= 0x30) {
if (stv090x_get_viterbi(state) < 0)
goto err;
@@ -2868,7 +2964,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
goto err;
}
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if ((state->search_mode == STV090x_SEARCH_DVBS1) ||
(state->search_mode == STV090x_SEARCH_DSS) ||
(state->search_mode == STV090x_SEARCH_AUTO)) {
@@ -2890,7 +2986,8 @@ static int stv090x_optimize_track(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0)
goto err;
- if ((state->dev_ver >= 0x20) || (blind_tune == 1) || (state->srate < 10000000)) {
+ if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) ||
+ (state->srate < 10000000)) {
/* update initial carrier freq with the found freq offset */
if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
goto err;
@@ -2898,7 +2995,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
goto err;
state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000;
- if ((state->dev_ver >= 0x20) || (blind_tune == 1)) {
+ if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) {
if (state->algo != STV090x_WARM_SEARCH) {
@@ -2907,7 +3004,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
if (state->config->tuner_set_bandwidth) {
if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
@@ -2950,7 +3047,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
}
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
goto err;
}
@@ -2959,6 +3056,9 @@ static int stv090x_optimize_track(struct stv090x_state *state)
stv090x_set_vit_thtracq(state);
return 0;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(fe, 0);
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
@@ -3026,7 +3126,7 @@ static int stv090x_set_s2rolloff(struct stv090x_state *state)
{
u32 reg;
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
/* rolloff to auto mode if DVBS2 */
reg = STV090x_READ_DEMOD(state, DEMOD);
STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00);
@@ -3062,7 +3162,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */
goto err;
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (state->srate > 5000000) {
if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
goto err;
@@ -3102,7 +3202,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
goto err;
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0)
goto err;
if (state->algo == STV090x_COLD_SEARCH)
@@ -3120,9 +3220,11 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
if (stv090x_set_srate(state, state->srate) < 0)
goto err;
- if (stv090x_set_max_srate(state, state->mclk, state->srate) < 0)
+ if (stv090x_set_max_srate(state, state->internal->mclk,
+ state->srate) < 0)
goto err;
- if (stv090x_set_min_srate(state, state->mclk, state->srate) < 0)
+ if (stv090x_set_min_srate(state, state->internal->mclk,
+ state->srate) < 0)
goto err;
if (state->srate >= 10000000)
@@ -3136,18 +3238,21 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
goto err;
if (state->config->tuner_set_bbgain) {
- if (state->config->tuner_set_bbgain(fe, 10) < 0) /* 10dB */
- goto err;
+ reg = state->config->tuner_bbgain;
+ if (reg == 0)
+ reg = 10; /* default: 10dB */
+ if (state->config->tuner_set_bbgain(fe, reg) < 0)
+ goto err_gateoff;
}
if (state->config->tuner_set_frequency) {
if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
- goto err;
+ goto err_gateoff;
}
if (state->config->tuner_set_bandwidth) {
if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
@@ -3155,21 +3260,21 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
msleep(50);
- if (stv090x_i2c_gate_ctrl(fe, 1) < 0)
- goto err;
-
if (state->config->tuner_get_status) {
+ if (stv090x_i2c_gate_ctrl(fe, 1) < 0)
+ goto err;
if (state->config->tuner_get_status(fe, &reg) < 0)
+ goto err_gateoff;
+ if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
goto err;
- }
- if (reg)
- dprintk(FE_DEBUG, 1, "Tuner phase locked");
- else
- dprintk(FE_DEBUG, 1, "Tuner unlocked");
-
- if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
- goto err;
+ if (reg)
+ dprintk(FE_DEBUG, 1, "Tuner phase locked");
+ else {
+ dprintk(FE_DEBUG, 1, "Tuner unlocked");
+ return STV090x_NOCARRIER;
+ }
+ }
msleep(10);
agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1),
@@ -3194,7 +3299,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
reg = STV090x_READ_DEMOD(state, DEMOD);
STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion);
- if (state->dev_ver <= 0x20) {
+ if (state->internal->dev_ver <= 0x20) {
/* rolloff to auto mode if DVBS2 */
STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1);
} else {
@@ -3238,7 +3343,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */
stv090x_optimize_track(state);
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
/* >= Cut 2.0 :release TS reset after
* demod lock and optimized Tracking
*/
@@ -3293,6 +3398,8 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
}
return signal_state;
+err_gateoff:
+ stv090x_i2c_gate_ctrl(fe, 0);
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
@@ -3303,6 +3410,9 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
struct stv090x_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *props = &fe->dtv_property_cache;
+ if (p->frequency == 0)
+ return DVBFE_ALGO_SEARCH_INVALID;
+
state->delsys = props->delivery_system;
state->frequency = p->frequency;
state->srate = p->u.qpsk.symbol_rate;
@@ -3353,7 +3463,8 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
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 |
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER |
FE_HAS_VITERBI |
FE_HAS_SYNC |
FE_HAS_LOCK;
@@ -3370,7 +3481,11 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_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;
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER |
+ FE_HAS_VITERBI |
+ FE_HAS_SYNC |
+ FE_HAS_LOCK;
}
}
}
@@ -3770,6 +3885,15 @@ static void stv090x_release(struct dvb_frontend *fe)
{
struct stv090x_state *state = fe->demodulator_priv;
+ state->internal->num_used--;
+ if (state->internal->num_used <= 0) {
+
+ dprintk(FE_ERROR, 1, "Actually removing");
+
+ remove_dev(state->internal);
+ kfree(state->internal);
+ }
+
kfree(state);
}
@@ -3901,10 +4025,10 @@ static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk)
if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0)
goto err;
- state->mclk = stv090x_get_mclk(state);
+ state->internal->mclk = stv090x_get_mclk(state);
/*Set the DiseqC frequency to 22KHz */
- div = state->mclk / 704000;
+ div = state->internal->mclk / 704000;
if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0)
@@ -3920,7 +4044,7 @@ static int stv090x_set_tspath(struct stv090x_state *state)
{
u32 reg;
- if (state->dev_ver >= 0x20) {
+ if (state->internal->dev_ver >= 0x20) {
switch (state->config->ts1_mode) {
case STV090x_TSMODE_PARALLEL_PUNCTURED:
case STV090x_TSMODE_DVBCI:
@@ -4092,6 +4216,71 @@ static int stv090x_set_tspath(struct stv090x_state *state)
default:
break;
}
+
+ if (state->config->ts1_clk > 0) {
+ u32 speed;
+
+ switch (state->config->ts1_mode) {
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ default:
+ speed = state->internal->mclk /
+ (state->config->ts1_clk / 4);
+ if (speed < 0x08)
+ speed = 0x08;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ speed = state->internal->mclk /
+ (state->config->ts1_clk / 32);
+ if (speed < 0x20)
+ speed = 0x20;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ }
+ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0)
+ goto err;
+ }
+
+ if (state->config->ts2_clk > 0) {
+ u32 speed;
+
+ switch (state->config->ts2_mode) {
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ default:
+ speed = state->internal->mclk /
+ (state->config->ts2_clk / 4);
+ if (speed < 0x08)
+ speed = 0x08;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ speed = state->internal->mclk /
+ (state->config->ts2_clk / 32);
+ if (speed < 0x20)
+ speed = 0x20;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ }
+ reg = stv090x_read_reg(state, STV090x_P2_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0)
+ goto err;
+ }
+
reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01);
if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
@@ -4120,6 +4309,15 @@ static int stv090x_init(struct dvb_frontend *fe)
const struct stv090x_config *config = state->config;
u32 reg;
+ if (state->internal->mclk == 0) {
+ stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */
+ msleep(5);
+ if (stv090x_write_reg(state, STV090x_SYNTCTRL,
+ 0x20 | config->clk_mode) < 0)
+ goto err;
+ stv090x_get_mclk(state);
+ }
+
if (stv090x_wakeup(fe) < 0) {
dprintk(FE_ERROR, 1, "Error waking device");
goto err;
@@ -4142,12 +4340,12 @@ static int stv090x_init(struct dvb_frontend *fe)
if (config->tuner_set_mode) {
if (config->tuner_set_mode(fe, TUNER_WAKE) < 0)
- goto err;
+ goto err_gateoff;
}
if (config->tuner_init) {
if (config->tuner_init(fe) < 0)
- goto err;
+ goto err_gateoff;
}
if (stv090x_i2c_gate_ctrl(fe, 0) < 0)
@@ -4157,6 +4355,9 @@ static int stv090x_init(struct dvb_frontend *fe)
goto err;
return 0;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(fe, 0);
err:
dprintk(FE_ERROR, 1, "I/O error");
return -1;
@@ -4188,16 +4389,26 @@ static int stv090x_setup(struct dvb_frontend *fe)
}
/* STV090x init */
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Stop Demod */
+
+ /* Stop Demod */
+ if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0)
goto err;
msleep(5);
- if (STV090x_WRITE_DEMOD(state, TNRCFG, 0x6c) < 0) /* check register ! (No Tuner Mode) */
+ /* Set No Tuner Mode */
+ if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0)
goto err;
+ /* I2C repeater OFF */
STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level);
- if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0) /* repeater OFF */
+ if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0)
goto err;
if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */
@@ -4216,8 +4427,8 @@ static int stv090x_setup(struct dvb_frontend *fe)
goto err;
}
- state->dev_ver = stv090x_read_reg(state, STV090x_MID);
- if (state->dev_ver >= 0x20) {
+ state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID);
+ if (state->internal->dev_ver >= 0x20) {
if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0)
goto err;
@@ -4228,27 +4439,35 @@ static int stv090x_setup(struct dvb_frontend *fe)
goto err;
}
- } else if (state->dev_ver < 0x20) {
+ } else if (state->internal->dev_ver < 0x20) {
dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!",
- state->dev_ver);
+ state->internal->dev_ver);
goto err;
- } else if (state->dev_ver > 0x30) {
+ } else if (state->internal->dev_ver > 0x30) {
/* we shouldn't bail out from here */
dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!",
- state->dev_ver);
+ state->internal->dev_ver);
}
- if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0)
+ /* ADC1 range */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR1);
+ STV090x_SETFIELD(reg, ADC1_INMODE_FIELD,
+ (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
goto err;
- if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0)
+
+ /* ADC2 range */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR3);
+ STV090x_SETFIELD(reg, ADC2_INMODE_FIELD,
+ (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0)
goto err;
- stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */
- msleep(5);
- if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0)
+ if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0)
goto err;
- stv090x_get_mclk(state);
return 0;
err:
@@ -4299,6 +4518,7 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
enum stv090x_demodulator demod)
{
struct stv090x_state *state = NULL;
+ struct stv090x_dev *temp_int;
state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL);
if (state == NULL)
@@ -4314,8 +4534,32 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
state->device = config->device;
state->rolloff = STV090x_RO_35; /* default */
- if (state->demod == STV090x_DEMODULATOR_0)
- mutex_init(&demod_lock);
+ temp_int = find_dev(state->i2c,
+ state->config->address);
+
+ if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) {
+ state->internal = temp_int->internal;
+ state->internal->num_used++;
+ dprintk(FE_INFO, 1, "Found Internal Structure!");
+ dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x",
+ state->device == STV0900 ? "STV0900" : "STV0903",
+ demod,
+ state->internal->dev_ver);
+ return &state->frontend;
+ } else {
+ state->internal = kmalloc(sizeof(struct stv090x_internal),
+ GFP_KERNEL);
+ temp_int = append_internal(state->internal);
+ state->internal->num_used = 1;
+ state->internal->mclk = 0;
+ state->internal->dev_ver = 0;
+ state->internal->i2c_adap = state->i2c;
+ state->internal->i2c_addr = state->config->address;
+ dprintk(FE_INFO, 1, "Create New Internal Structure!");
+ }
+
+ mutex_init(&state->internal->demod_lock);
+ mutex_init(&state->internal->tuner_lock);
if (stv090x_sleep(&state->frontend) < 0) {
dprintk(FE_ERROR, 1, "Error putting device to sleep");
@@ -4331,10 +4575,10 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
goto error;
}
- dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x\n",
+ dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x",
state->device == STV0900 ? "STV0900" : "STV0903",
demod,
- state->dev_ver);
+ state->internal->dev_ver);
return &state->frontend;
diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h
index b133807663e..30f01a6902a 100644
--- a/drivers/media/dvb/frontends/stv090x.h
+++ b/drivers/media/dvb/frontends/stv090x.h
@@ -60,6 +60,11 @@ enum stv090x_i2crpt {
STV090x_RPTLEVEL_2 = 7,
};
+enum stv090x_adc_range {
+ STV090x_ADC_2Vpp = 0,
+ STV090x_ADC_1Vpp = 1
+};
+
struct stv090x_config {
enum stv090x_device device;
enum stv090x_mode demod_mode;
@@ -68,13 +73,17 @@ struct stv090x_config {
u32 xtal; /* default: 8000000 */
u8 address; /* default: 0x68 */
- u32 ref_clk; /* default: 16000000 FIXME to tuner config */
-
u8 ts1_mode;
u8 ts2_mode;
+ u32 ts1_clk;
+ u32 ts2_clk;
enum stv090x_i2crpt repeater_level;
+ u8 tuner_bbgain; /* default: 10db */
+ enum stv090x_adc_range adc1_range; /* default: 2Vpp */
+ enum stv090x_adc_range adc2_range; /* default: 2Vpp */
+
bool diseqc_envelope_mode;
int (*tuner_init) (struct dvb_frontend *fe);
diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h
index 5921a8d6c89..5b780c80d49 100644
--- a/drivers/media/dvb/frontends/stv090x_priv.h
+++ b/drivers/media/dvb/frontends/stv090x_priv.h
@@ -230,11 +230,23 @@ struct stv090x_tab {
s32 read;
};
+struct stv090x_internal {
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+
+ struct mutex demod_lock; /* Lock access to shared register */
+ struct mutex tuner_lock; /* Lock access to tuners */
+ s32 mclk; /* Masterclock Divider factor */
+ u32 dev_ver;
+
+ int num_used;
+};
+
struct stv090x_state {
enum stv090x_device device;
enum stv090x_demodulator demod;
enum stv090x_mode demod_mode;
- u32 dev_ver;
+ struct stv090x_internal *internal;
struct i2c_adapter *i2c;
const struct stv090x_config *config;
@@ -256,11 +268,8 @@ struct stv090x_state {
u32 frequency;
u32 srate;
- s32 mclk; /* Masterclock Divider factor */
s32 tuner_bw;
- u32 tuner_refclk;
-
s32 search_range;
s32 DemodTimeout;
diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c
index bcfcb652464..f931ed07e92 100644
--- a/drivers/media/dvb/frontends/stv6110x.c
+++ b/drivers/media/dvb/frontends/stv6110x.c
@@ -35,8 +35,6 @@ static unsigned int verbose;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "Set Verbosity level");
-static u8 stv6110x_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e};
-
static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data)
{
int ret;
@@ -58,12 +56,23 @@ static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data)
return 0;
}
-static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data)
+static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 data[], int len)
{
int ret;
const struct stv6110x_config *config = stv6110x->config;
- u8 buf[] = { reg, data };
- struct i2c_msg msg = { .addr = config->addr, .flags = 0, . buf = buf, .len = 2 };
+ u8 buf[len + 1];
+ struct i2c_msg msg = {
+ .addr = config->addr,
+ .flags = 0,
+ .buf = buf,
+ .len = len + 1
+ };
+
+ if (start + len > 8)
+ return -EINVAL;
+
+ buf[0] = start;
+ memcpy(&buf[1], data, len);
ret = i2c_transfer(stv6110x->i2c, &msg, 1);
if (ret != 1) {
@@ -74,18 +83,21 @@ static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data)
return 0;
}
+static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data)
+{
+ return stv6110x_write_regs(stv6110x, reg, &data, 1);
+}
+
static int stv6110x_init(struct dvb_frontend *fe)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
int ret;
- u8 i;
- for (i = 0; i < ARRAY_SIZE(stv6110x_regs); i++) {
- ret = stv6110x_write_reg(stv6110x, i, stv6110x_regs[i]);
- if (ret < 0) {
- dprintk(FE_ERROR, 1, "Initialization failed");
- return -1;
- }
+ ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs,
+ ARRAY_SIZE(stv6110x->regs));
+ if (ret < 0) {
+ dprintk(FE_ERROR, 1, "Initialization failed");
+ return -1;
}
return 0;
@@ -98,23 +110,23 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000;
u8 i;
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16));
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16));
if (frequency <= 1023000) {
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 1);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0);
pVal = 40;
} else if (frequency <= 1300000) {
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 1);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1);
pVal = 40;
} else if (frequency <= 2046000) {
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 0);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0);
pVal = 20;
} else {
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 0);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1);
pVal = 20;
}
@@ -130,21 +142,21 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz;
divider = (divider + 5) / 10;
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider));
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider));
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider));
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider));
/* VCO Auto calibration */
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1);
- stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x_regs[STV6110x_CTRL1]);
- stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x_regs[STV6110x_TNG1]);
- stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x_regs[STV6110x_TNG0]);
- stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x_regs[STV6110x_STAT1]);
+ stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]);
+ stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x->regs[STV6110x_TNG1]);
+ stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x->regs[STV6110x_TNG0]);
+ stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]);
for (i = 0; i < TRIALS; i++) {
- stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x_regs[STV6110x_STAT1]);
- if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x_regs[STV6110x_STAT1]))
+ stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]);
+ if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x->regs[STV6110x_STAT1]))
break;
msleep(1);
}
@@ -156,14 +168,14 @@ static int stv6110x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
- stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x_regs[STV6110x_TNG1]);
- stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x_regs[STV6110x_TNG0]);
+ stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x->regs[STV6110x_TNG1]);
+ stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x->regs[STV6110x_TNG0]);
- *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x_regs[STV6110x_TNG1]),
- STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x_regs[STV6110x_TNG0]))) * REFCLOCK_kHz;
+ *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x->regs[STV6110x_TNG1]),
+ STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x->regs[STV6110x_TNG0]))) * REFCLOCK_kHz;
- *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x_regs[STV6110x_TNG1]) +
- STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x_regs[STV6110x_TNG1])));
+ *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x->regs[STV6110x_TNG1]) +
+ STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x->regs[STV6110x_TNG1])));
*frequency >>= 2;
@@ -179,27 +191,27 @@ static int stv6110x_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
halfbw = bandwidth >> 1;
if (halfbw > 36000000)
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */
else if (halfbw < 5000000)
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */
else
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */
- stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x_regs[STV6110x_CTRL3]);
- stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x_regs[STV6110x_STAT1]);
+ stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]);
+ stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]);
for (i = 0; i < TRIALS; i++) {
- stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x_regs[STV6110x_STAT1]);
- if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x_regs[STV6110x_STAT1]))
+ stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]);
+ if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x->regs[STV6110x_STAT1]))
break;
msleep(1);
}
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */
- stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x_regs[STV6110x_CTRL3]);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */
+ stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]);
return 0;
}
@@ -208,8 +220,8 @@ static int stv6110x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
- stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x_regs[STV6110x_CTRL3]);
- *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x_regs[STV6110x_CTRL3]) + 5) * 2000000;
+ stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x->regs[STV6110x_CTRL3]);
+ *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x->regs[STV6110x_CTRL3]) + 5) * 2000000;
return 0;
}
@@ -222,20 +234,20 @@ static int stv6110x_set_refclock(struct dvb_frontend *fe, u32 refclock)
switch (refclock) {
default:
case 1:
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0);
break;
case 2:
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1);
break;
case 4:
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2);
break;
case 8:
case 0:
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3);
break;
}
- stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x_regs[STV6110x_CTRL2]);
+ stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]);
return 0;
}
@@ -244,8 +256,8 @@ static int stv6110x_get_bbgain(struct dvb_frontend *fe, u32 *gain)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
- stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x_regs[STV6110x_CTRL2]);
- *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x_regs[STV6110x_CTRL2]);
+ stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x->regs[STV6110x_CTRL2]);
+ *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x->regs[STV6110x_CTRL2]);
return 0;
}
@@ -254,8 +266,8 @@ static int stv6110x_set_bbgain(struct dvb_frontend *fe, u32 gain)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2);
- stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x_regs[STV6110x_CTRL2]);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2);
+ stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]);
return 0;
}
@@ -267,19 +279,19 @@ static int stv6110x_set_mode(struct dvb_frontend *fe, enum tuner_mode mode)
switch (mode) {
case TUNER_SLEEP:
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_SYN, 0);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_RX, 0);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_LPT, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 0);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 0);
break;
case TUNER_WAKE:
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_SYN, 1);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_RX, 1);
- STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_LPT, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 1);
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 1);
break;
}
- ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x_regs[STV6110x_CTRL1]);
+ ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]);
if (ret < 0) {
dprintk(FE_ERROR, 1, "I/O Error");
return -EIO;
@@ -297,9 +309,9 @@ static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
- stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x_regs[STV6110x_STAT1]);
+ stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]);
- if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x_regs[STV6110x_STAT1]))
+ if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x->regs[STV6110x_STAT1]))
*status = TUNER_PHASELOCKED;
else
*status = 0;
@@ -349,6 +361,8 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c)
{
struct stv6110x_state *stv6110x;
+ u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e};
+ int ret;
stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL);
if (stv6110x == NULL)
@@ -357,6 +371,44 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
stv6110x->i2c = i2c;
stv6110x->config = config;
stv6110x->devctl = &stv6110x_ctl;
+ memcpy(stv6110x->regs, default_regs, 8);
+
+ /* setup divider */
+ switch (stv6110x->config->clk_div) {
+ default:
+ case 1:
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0);
+ break;
+ case 2:
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1);
+ break;
+ case 4:
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2);
+ break;
+ case 8:
+ case 0:
+ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3);
+ break;
+ }
+
+ if (fe->ops.i2c_gate_ctrl) {
+ ret = fe->ops.i2c_gate_ctrl(fe, 1);
+ if (ret < 0)
+ goto error;
+ }
+
+ ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs,
+ ARRAY_SIZE(stv6110x->regs));
+ if (ret < 0) {
+ dprintk(FE_ERROR, 1, "Initialization failed");
+ goto error;
+ }
+
+ if (fe->ops.i2c_gate_ctrl) {
+ ret = fe->ops.i2c_gate_ctrl(fe, 0);
+ if (ret < 0)
+ goto error;
+ }
fe->tuner_priv = stv6110x;
fe->ops.tuner_ops = stv6110x_ops;
diff --git a/drivers/media/dvb/frontends/stv6110x.h b/drivers/media/dvb/frontends/stv6110x.h
index a38257080e0..2429ae6d784 100644
--- a/drivers/media/dvb/frontends/stv6110x.h
+++ b/drivers/media/dvb/frontends/stv6110x.h
@@ -26,6 +26,7 @@
struct stv6110x_config {
u8 addr;
u32 refclk;
+ u8 clk_div; /* divisor value for the output clock */
};
enum tuner_mode {
diff --git a/drivers/media/dvb/frontends/stv6110x_priv.h b/drivers/media/dvb/frontends/stv6110x_priv.h
index 7260da633d4..0ec936a660a 100644
--- a/drivers/media/dvb/frontends/stv6110x_priv.h
+++ b/drivers/media/dvb/frontends/stv6110x_priv.h
@@ -68,6 +68,7 @@
struct stv6110x_state {
struct i2c_adapter *i2c;
const struct stv6110x_config *config;
+ u8 regs[8];
struct stv6110x_devctl *devctl;
};
diff --git a/drivers/media/dvb/frontends/tda665x.c b/drivers/media/dvb/frontends/tda665x.c
index 87d52739c82..c44fefe92d9 100644
--- a/drivers/media/dvb/frontends/tda665x.c
+++ b/drivers/media/dvb/frontends/tda665x.c
@@ -133,7 +133,7 @@ static int tda665x_set_state(struct dvb_frontend *fe,
frequency += config->ref_divider >> 1;
frequency /= config->ref_divider;
- buf[0] = (u8) (frequency & 0x7f00) >> 8;
+ buf[0] = (u8) ((frequency & 0x7f00) >> 8);
buf[1] = (u8) (frequency & 0x00ff) >> 0;
buf[2] = 0x80 | 0x40 | 0x02;
buf[3] = 0x00;
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c
index 320c3c36d8b..614afcec05f 100644
--- a/drivers/media/dvb/frontends/tda8261.c
+++ b/drivers/media/dvb/frontends/tda8261.c
@@ -39,7 +39,7 @@ static int tda8261_read(struct tda8261_state *state, u8 *buf)
{
const struct tda8261_config *config = state->config;
int err = 0;
- struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 2 };
+ struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 };
if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
printk("%s: read error, err=%d\n", __func__, err);
diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c
index 4e814ff22b2..34c5de491d2 100644
--- a/drivers/media/dvb/frontends/zl10036.c
+++ b/drivers/media/dvb/frontends/zl10036.c
@@ -411,7 +411,7 @@ static int zl10036_init_regs(struct zl10036_state *state)
state->bf = 0xff;
if (!state->config->rf_loop_enable)
- zl10036_init_tab[1][2] |= 0x01;
+ zl10036_init_tab[1][0] |= 0x01;
deb_info("%s\n", __func__);
diff --git a/drivers/media/dvb/frontends/zl10039.c b/drivers/media/dvb/frontends/zl10039.c
index 11b29cb883e..c085e58a94b 100644
--- a/drivers/media/dvb/frontends/zl10039.c
+++ b/drivers/media/dvb/frontends/zl10039.c
@@ -287,7 +287,6 @@ struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
break;
default:
dprintk("Chip ID=%x does not match a known type\n", state->id);
- break;
goto error;
}
diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c
index 7477dac628b..5772ebb3a69 100644
--- a/drivers/media/dvb/mantis/mantis_hif.c
+++ b/drivers/media/dvb/mantis/mantis_hif.c
@@ -22,8 +22,6 @@
#include <linux/signal.h>
#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
#include <linux/interrupt.h>
#include "dmxdev.h"
diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c
index 6a9df779441..4675a3b53c7 100644
--- a/drivers/media/dvb/mantis/mantis_input.c
+++ b/drivers/media/dvb/mantis/mantis_input.c
@@ -126,7 +126,7 @@ int mantis_input_init(struct mantis_pci *mantis)
rc->id.version = 1;
rc->dev = mantis->pdev->dev;
- err = ir_input_register(rc, &ir_mantis);
+ err = ir_input_register(rc, &ir_mantis, NULL);
if (err) {
dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err);
input_free_device(rc);
diff --git a/drivers/media/dvb/mantis/mantis_pci.c b/drivers/media/dvb/mantis/mantis_pci.c
index 6c7534af6b4..59feeb84aec 100644
--- a/drivers/media/dvb/mantis/mantis_pci.c
+++ b/drivers/media/dvb/mantis/mantis_pci.c
@@ -41,11 +41,6 @@
#include "dvb_frontend.h"
#include "dvb_net.h"
-#include <asm/irq.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-
#include "mantis_common.h"
#include "mantis_reg.h"
#include "mantis_pci.h"
diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig
new file mode 100644
index 00000000000..3ec8e6fcbb1
--- /dev/null
+++ b/drivers/media/dvb/ngene/Kconfig
@@ -0,0 +1,9 @@
+config DVB_NGENE
+ tristate "Micronas nGene support"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_STV6110x if !DVB_FE_CUSTOMISE
+ select DVB_STV090x if !DVB_FE_CUSTOMISE
+ ---help---
+ Support for Micronas PCI express cards with nGene bridge.
+
diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile
new file mode 100644
index 00000000000..40435cad481
--- /dev/null
+++ b/drivers/media/dvb/ngene/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the nGene device driver
+#
+
+ngene-objs := ngene-core.o
+
+obj-$(CONFIG_DVB_NGENE) += ngene.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
+
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
new file mode 100644
index 00000000000..0150dfe7cfb
--- /dev/null
+++ b/drivers/media/dvb/ngene/ngene-core.c
@@ -0,0 +1,2024 @@
+/*
+ * ngene.c: nGene PCIe bridge driver
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ * Modifications for new nGene firmware,
+ * support for EEPROM-copying,
+ * support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/byteorder/generic.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include "ngene.h"
+
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+
+static int one_adapter = 1;
+module_param(one_adapter, int, 0444);
+MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
+
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Print debugging information.");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define COMMAND_TIMEOUT_WORKAROUND
+
+#define dprintk if (debug) printk
+
+#define DEVICE_NAME "ngene"
+
+#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr)))
+#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngreadl(adr) readl(dev->iomem + (adr))
+#define ngreadb(adr) readb(dev->iomem + (adr))
+#define ngcpyto(adr, src, count) memcpy_toio((char *) \
+ (dev->iomem + (adr)), (src), (count))
+#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \
+ (dev->iomem + (adr)), (count))
+
+/****************************************************************************/
+/* nGene interrupt handler **************************************************/
+/****************************************************************************/
+
+static void event_tasklet(unsigned long data)
+{
+ struct ngene *dev = (struct ngene *)data;
+
+ while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) {
+ struct EVENT_BUFFER Event =
+ dev->EventQueue[dev->EventQueueReadIndex];
+ dev->EventQueueReadIndex =
+ (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1);
+
+ if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify))
+ dev->TxEventNotify(dev, Event.TimeStamp);
+ if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify))
+ dev->RxEventNotify(dev, Event.TimeStamp,
+ Event.RXCharacter);
+ }
+}
+
+static void demux_tasklet(unsigned long data)
+{
+ struct ngene_channel *chan = (struct ngene_channel *)data;
+ struct SBufferHeader *Cur = chan->nextBuffer;
+
+ spin_lock_irq(&chan->state_lock);
+
+ while (Cur->ngeneBuffer.SR.Flags & 0x80) {
+ if (chan->mode & NGENE_IO_TSOUT) {
+ u32 Flags = chan->DataFormatFlags;
+ if (Cur->ngeneBuffer.SR.Flags & 0x20)
+ Flags |= BEF_OVERFLOW;
+ if (chan->pBufferExchange) {
+ if (!chan->pBufferExchange(chan,
+ Cur->Buffer1,
+ chan->Capture1Length,
+ Cur->ngeneBuffer.SR.
+ Clock, Flags)) {
+ /*
+ We didn't get data
+ Clear in service flag to make sure we
+ get called on next interrupt again.
+ leave fill/empty (0x80) flag alone
+ to avoid hardware running out of
+ buffers during startup, we hold only
+ in run state ( the source may be late
+ delivering data )
+ */
+
+ if (chan->HWState == HWSTATE_RUN) {
+ Cur->ngeneBuffer.SR.Flags &=
+ ~0x40;
+ break;
+ /* Stop proccessing stream */
+ }
+ } else {
+ /* We got a valid buffer,
+ so switch to run state */
+ chan->HWState = HWSTATE_RUN;
+ }
+ } else {
+ printk(KERN_ERR DEVICE_NAME ": OOPS\n");
+ if (chan->HWState == HWSTATE_RUN) {
+ Cur->ngeneBuffer.SR.Flags &= ~0x40;
+ break; /* Stop proccessing stream */
+ }
+ }
+ if (chan->AudioDTOUpdated) {
+ printk(KERN_INFO DEVICE_NAME
+ ": Update AudioDTO = %d\n",
+ chan->AudioDTOValue);
+ Cur->ngeneBuffer.SR.DTOUpdate =
+ chan->AudioDTOValue;
+ chan->AudioDTOUpdated = 0;
+ }
+ } else {
+ if (chan->HWState == HWSTATE_RUN) {
+ u32 Flags = 0;
+ if (Cur->ngeneBuffer.SR.Flags & 0x01)
+ Flags |= BEF_EVEN_FIELD;
+ if (Cur->ngeneBuffer.SR.Flags & 0x20)
+ Flags |= BEF_OVERFLOW;
+ if (chan->pBufferExchange)
+ chan->pBufferExchange(chan,
+ Cur->Buffer1,
+ chan->
+ Capture1Length,
+ Cur->ngeneBuffer.
+ SR.Clock, Flags);
+ if (chan->pBufferExchange2)
+ chan->pBufferExchange2(chan,
+ Cur->Buffer2,
+ chan->
+ Capture2Length,
+ Cur->ngeneBuffer.
+ SR.Clock, Flags);
+ } else if (chan->HWState != HWSTATE_STOP)
+ chan->HWState = HWSTATE_RUN;
+ }
+ Cur->ngeneBuffer.SR.Flags = 0x00;
+ Cur = Cur->Next;
+ }
+ chan->nextBuffer = Cur;
+
+ spin_unlock_irq(&chan->state_lock);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+ struct ngene *dev = (struct ngene *)dev_id;
+ u32 icounts = 0;
+ irqreturn_t rc = IRQ_NONE;
+ u32 i = MAX_STREAM;
+ u8 *tmpCmdDoneByte;
+
+ if (dev->BootFirmware) {
+ icounts = ngreadl(NGENE_INT_COUNTS);
+ if (icounts != dev->icounts) {
+ ngwritel(0, FORCE_NMI);
+ dev->cmd_done = 1;
+ wake_up(&dev->cmd_wq);
+ dev->icounts = icounts;
+ rc = IRQ_HANDLED;
+ }
+ return rc;
+ }
+
+ ngwritel(0, FORCE_NMI);
+
+ spin_lock(&dev->cmd_lock);
+ tmpCmdDoneByte = dev->CmdDoneByte;
+ if (tmpCmdDoneByte &&
+ (*tmpCmdDoneByte ||
+ (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) {
+ dev->CmdDoneByte = NULL;
+ dev->cmd_done = 1;
+ wake_up(&dev->cmd_wq);
+ rc = IRQ_HANDLED;
+ }
+ spin_unlock(&dev->cmd_lock);
+
+ if (dev->EventBuffer->EventStatus & 0x80) {
+ u8 nextWriteIndex =
+ (dev->EventQueueWriteIndex + 1) &
+ (EVENT_QUEUE_SIZE - 1);
+ if (nextWriteIndex != dev->EventQueueReadIndex) {
+ dev->EventQueue[dev->EventQueueWriteIndex] =
+ *(dev->EventBuffer);
+ dev->EventQueueWriteIndex = nextWriteIndex;
+ } else {
+ printk(KERN_ERR DEVICE_NAME ": event overflow\n");
+ dev->EventQueueOverflowCount += 1;
+ dev->EventQueueOverflowFlag = 1;
+ }
+ dev->EventBuffer->EventStatus &= ~0x80;
+ tasklet_schedule(&dev->event_tasklet);
+ rc = IRQ_HANDLED;
+ }
+
+ while (i > 0) {
+ i--;
+ spin_lock(&dev->channel[i].state_lock);
+ /* if (dev->channel[i].State>=KSSTATE_RUN) { */
+ if (dev->channel[i].nextBuffer) {
+ if ((dev->channel[i].nextBuffer->
+ ngeneBuffer.SR.Flags & 0xC0) == 0x80) {
+ dev->channel[i].nextBuffer->
+ ngeneBuffer.SR.Flags |= 0x40;
+ tasklet_schedule(
+ &dev->channel[i].demux_tasklet);
+ rc = IRQ_HANDLED;
+ }
+ }
+ spin_unlock(&dev->channel[i].state_lock);
+ }
+
+ /* Request might have been processed by a previous call. */
+ return IRQ_HANDLED;
+}
+
+/****************************************************************************/
+/* nGene command interface **************************************************/
+/****************************************************************************/
+
+static void dump_command_io(struct ngene *dev)
+{
+ u8 buf[8], *b;
+
+ ngcpyfrom(buf, HOST_TO_NGENE, 8);
+ printk(KERN_ERR "host_to_ngene (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ HOST_TO_NGENE, buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ ngcpyfrom(buf, NGENE_TO_HOST, 8);
+ printk(KERN_ERR "ngene_to_host (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ NGENE_TO_HOST, buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ b = dev->hosttongene;
+ printk(KERN_ERR "dev->hosttongene (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+
+ b = dev->ngenetohost;
+ printk(KERN_ERR "dev->ngenetohost (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
+{
+ int ret;
+ u8 *tmpCmdDoneByte;
+
+ dev->cmd_done = 0;
+
+ if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) {
+ dev->BootFirmware = 1;
+ dev->icounts = ngreadl(NGENE_INT_COUNTS);
+ ngwritel(0, NGENE_COMMAND);
+ ngwritel(0, NGENE_COMMAND_HI);
+ ngwritel(0, NGENE_STATUS);
+ ngwritel(0, NGENE_STATUS_HI);
+ ngwritel(0, NGENE_EVENT);
+ ngwritel(0, NGENE_EVENT_HI);
+ } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) {
+ u64 fwio = dev->PAFWInterfaceBuffer;
+
+ ngwritel(fwio & 0xffffffff, NGENE_COMMAND);
+ ngwritel(fwio >> 32, NGENE_COMMAND_HI);
+ ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS);
+ ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI);
+ ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT);
+ ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI);
+ }
+
+ memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2);
+
+ if (dev->BootFirmware)
+ ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2);
+
+ spin_lock_irq(&dev->cmd_lock);
+ tmpCmdDoneByte = dev->ngenetohost + com->out_len;
+ if (!com->out_len)
+ tmpCmdDoneByte++;
+ *tmpCmdDoneByte = 0;
+ dev->ngenetohost[0] = 0;
+ dev->ngenetohost[1] = 0;
+ dev->CmdDoneByte = tmpCmdDoneByte;
+ spin_unlock_irq(&dev->cmd_lock);
+
+ /* Notify 8051. */
+ ngwritel(1, FORCE_INT);
+
+ ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ);
+ if (!ret) {
+ /*ngwritel(0, FORCE_NMI);*/
+
+ printk(KERN_ERR DEVICE_NAME
+ ": Command timeout cmd=%02x prev=%02x\n",
+ com->cmd.hdr.Opcode, dev->prev_cmd);
+ dump_command_io(dev);
+ return -1;
+ }
+ if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH)
+ dev->BootFirmware = 0;
+
+ dev->prev_cmd = com->cmd.hdr.Opcode;
+
+ if (!com->out_len)
+ return 0;
+
+ memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len);
+
+ return 0;
+}
+
+static int ngene_command(struct ngene *dev, struct ngene_command *com)
+{
+ int result;
+
+ down(&dev->cmd_mutex);
+ result = ngene_command_mutex(dev, com);
+ up(&dev->cmd_mutex);
+ return result;
+}
+
+
+static int ngene_command_i2c_read(struct ngene *dev, u8 adr,
+ u8 *out, u8 outlen, u8 *in, u8 inlen, int flag)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_I2C_READ;
+ com.cmd.hdr.Length = outlen + 3;
+ com.cmd.I2CRead.Device = adr << 1;
+ memcpy(com.cmd.I2CRead.Data, out, outlen);
+ com.cmd.I2CRead.Data[outlen] = inlen;
+ com.cmd.I2CRead.Data[outlen + 1] = 0;
+ com.in_len = outlen + 3;
+ com.out_len = inlen + 1;
+
+ if (ngene_command(dev, &com) < 0)
+ return -EIO;
+
+ if ((com.cmd.raw8[0] >> 1) != adr)
+ return -EIO;
+
+ if (flag)
+ memcpy(in, com.cmd.raw8, inlen + 1);
+ else
+ memcpy(in, com.cmd.raw8 + 1, inlen);
+ return 0;
+}
+
+static int ngene_command_i2c_write(struct ngene *dev, u8 adr,
+ u8 *out, u8 outlen)
+{
+ struct ngene_command com;
+
+
+ com.cmd.hdr.Opcode = CMD_I2C_WRITE;
+ com.cmd.hdr.Length = outlen + 1;
+ com.cmd.I2CRead.Device = adr << 1;
+ memcpy(com.cmd.I2CRead.Data, out, outlen);
+ com.in_len = outlen + 1;
+ com.out_len = 1;
+
+ if (ngene_command(dev, &com) < 0)
+ return -EIO;
+
+ if (com.cmd.raw8[0] == 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int ngene_command_load_firmware(struct ngene *dev,
+ u8 *ngene_fw, u32 size)
+{
+#define FIRSTCHUNK (1024)
+ u32 cleft;
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE;
+ com.cmd.hdr.Length = 0;
+ com.in_len = 0;
+ com.out_len = 0;
+
+ ngene_command(dev, &com);
+
+ cleft = (size + 3) & ~3;
+ if (cleft > FIRSTCHUNK) {
+ ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK,
+ cleft - FIRSTCHUNK);
+ cleft = FIRSTCHUNK;
+ }
+ ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft);
+
+ memset(&com, 0, sizeof(struct ngene_command));
+ com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH;
+ com.cmd.hdr.Length = 4;
+ com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA;
+ com.cmd.FWLoadFinish.Length = (unsigned short)cleft;
+ com.in_len = 4;
+ com.out_len = 0;
+
+ return ngene_command(dev, &com);
+}
+
+
+static int ngene_command_config_buf(struct ngene *dev, u8 config)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER;
+ com.cmd.hdr.Length = 1;
+ com.cmd.ConfigureBuffers.config = config;
+ com.in_len = 1;
+ com.out_len = 0;
+
+ if (ngene_command(dev, &com) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int ngene_command_config_free_buf(struct ngene *dev, u8 *config)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER;
+ com.cmd.hdr.Length = 6;
+ memcpy(&com.cmd.ConfigureBuffers.config, config, 6);
+ com.in_len = 6;
+ com.out_len = 0;
+
+ if (ngene_command(dev, &com) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN;
+ com.cmd.hdr.Length = 1;
+ com.cmd.SetGpioPin.select = select | (level << 7);
+ com.in_len = 1;
+ com.out_len = 0;
+
+ return ngene_command(dev, &com);
+}
+
+
+/*
+ 02000640 is sample on rising edge.
+ 02000740 is sample on falling edge.
+ 02000040 is ignore "valid" signal
+
+ 0: FD_CTL1 Bit 7,6 must be 0,1
+ 7 disable(fw controlled)
+ 6 0-AUX,1-TS
+ 5 0-par,1-ser
+ 4 0-lsb/1-msb
+ 3,2 reserved
+ 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both
+ 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge
+ 2: FD_STA is read-only. 0-sync
+ 3: FD_INSYNC is number of 47s to trigger "in sync".
+ 4: FD_OUTSYNC is number of 47s to trigger "out of sync".
+ 5: FD_MAXBYTE1 is low-order of bytes per packet.
+ 6: FD_MAXBYTE2 is high-order of bytes per packet.
+ 7: Top byte is unused.
+*/
+
+/****************************************************************************/
+
+static u8 TSFeatureDecoderSetup[8 * 4] = {
+ 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
+ 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */
+ 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */
+ 0x72, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */
+};
+
+/* Set NGENE I2S Config to 16 bit packed */
+static u8 I2SConfiguration[] = {
+ 0x00, 0x10, 0x00, 0x00,
+ 0x80, 0x10, 0x00, 0x00,
+};
+
+static u8 SPDIFConfiguration[10] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* Set NGENE I2S Config to transport stream compatible mode */
+
+static u8 TS_I2SConfiguration[4] = { 0x3E, 0x1A, 0x00, 0x00 }; /*3e 18 00 00 ?*/
+
+static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x20, 0x00, 0x00 };
+
+static u8 ITUDecoderSetup[4][16] = {
+ {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */
+ 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00},
+ {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+ {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */
+ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+ {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */
+ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+};
+
+/*
+ * 50 48 60 gleich
+ * 27p50 9f 00 22 80 42 69 18 ...
+ * 27p60 93 00 22 80 82 69 1c ...
+ */
+
+/* Maxbyte to 1144 (for raw data) */
+static u8 ITUFeatureDecoderSetup[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00
+};
+
+static void FillTSBuffer(void *Buffer, int Length, u32 Flags)
+{
+ u32 *ptr = Buffer;
+
+ memset(Buffer, 0xff, Length);
+ while (Length > 0) {
+ if (Flags & DF_SWAP32)
+ *ptr = 0x471FFF10;
+ else
+ *ptr = 0x10FF1F47;
+ ptr += (188 / 4);
+ Length -= 188;
+ }
+}
+
+
+static void flush_buffers(struct ngene_channel *chan)
+{
+ u8 val;
+
+ do {
+ msleep(1);
+ spin_lock_irq(&chan->state_lock);
+ val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80;
+ spin_unlock_irq(&chan->state_lock);
+ } while (val);
+}
+
+static void clear_buffers(struct ngene_channel *chan)
+{
+ struct SBufferHeader *Cur = chan->nextBuffer;
+
+ do {
+ memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR));
+ if (chan->mode & NGENE_IO_TSOUT)
+ FillTSBuffer(Cur->Buffer1,
+ chan->Capture1Length,
+ chan->DataFormatFlags);
+ Cur = Cur->Next;
+ } while (Cur != chan->nextBuffer);
+
+ if (chan->mode & NGENE_IO_TSOUT) {
+ chan->nextBuffer->ngeneBuffer.SR.DTOUpdate =
+ chan->AudioDTOValue;
+ chan->AudioDTOUpdated = 0;
+
+ Cur = chan->TSIdleBuffer.Head;
+
+ do {
+ memset(&Cur->ngeneBuffer.SR, 0,
+ sizeof(Cur->ngeneBuffer.SR));
+ FillTSBuffer(Cur->Buffer1,
+ chan->Capture1Length,
+ chan->DataFormatFlags);
+ Cur = Cur->Next;
+ } while (Cur != chan->TSIdleBuffer.Head);
+ }
+}
+
+static int ngene_command_stream_control(struct ngene *dev, u8 stream,
+ u8 control, u8 mode, u8 flags)
+{
+ struct ngene_channel *chan = &dev->channel[stream];
+ struct ngene_command com;
+ u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
+ u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500);
+ u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
+ u16 BsSDO = 0x9B00;
+
+ /* down(&dev->stream_mutex); */
+ while (down_trylock(&dev->stream_mutex)) {
+ printk(KERN_INFO DEVICE_NAME ": SC locked\n");
+ msleep(1);
+ }
+ memset(&com, 0, sizeof(com));
+ com.cmd.hdr.Opcode = CMD_CONTROL;
+ com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
+ com.cmd.StreamControl.Stream = stream | (control ? 8 : 0);
+ if (chan->mode & NGENE_IO_TSOUT)
+ com.cmd.StreamControl.Stream |= 0x07;
+ com.cmd.StreamControl.Control = control |
+ (flags & SFLAG_ORDER_LUMA_CHROMA);
+ com.cmd.StreamControl.Mode = mode;
+ com.in_len = sizeof(struct FW_STREAM_CONTROL);
+ com.out_len = 0;
+
+ dprintk(KERN_INFO DEVICE_NAME
+ ": Stream=%02x, Control=%02x, Mode=%02x\n",
+ com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
+ com.cmd.StreamControl.Mode);
+
+ chan->Mode = mode;
+
+ if (!(control & 0x80)) {
+ spin_lock_irq(&chan->state_lock);
+ if (chan->State == KSSTATE_RUN) {
+ chan->State = KSSTATE_ACQUIRE;
+ chan->HWState = HWSTATE_STOP;
+ spin_unlock_irq(&chan->state_lock);
+ if (ngene_command(dev, &com) < 0) {
+ up(&dev->stream_mutex);
+ return -1;
+ }
+ /* clear_buffers(chan); */
+ flush_buffers(chan);
+ up(&dev->stream_mutex);
+ return 0;
+ }
+ spin_unlock_irq(&chan->state_lock);
+ up(&dev->stream_mutex);
+ return 0;
+ }
+
+ if (mode & SMODE_AUDIO_CAPTURE) {
+ com.cmd.StreamControl.CaptureBlockCount =
+ chan->Capture1Length / AUDIO_BLOCK_SIZE;
+ com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
+ } else if (mode & SMODE_TRANSPORT_STREAM) {
+ com.cmd.StreamControl.CaptureBlockCount =
+ chan->Capture1Length / TS_BLOCK_SIZE;
+ com.cmd.StreamControl.MaxLinesPerField =
+ chan->Capture1Length / TS_BLOCK_SIZE;
+ com.cmd.StreamControl.Buffer_Address =
+ chan->TSRingBuffer.PAHead;
+ if (chan->mode & NGENE_IO_TSOUT) {
+ com.cmd.StreamControl.BytesPerVBILine =
+ chan->Capture1Length / TS_BLOCK_SIZE;
+ com.cmd.StreamControl.Stream |= 0x07;
+ }
+ } else {
+ com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine;
+ com.cmd.StreamControl.MaxLinesPerField = chan->nLines;
+ com.cmd.StreamControl.MinLinesPerField = 100;
+ com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
+
+ if (mode & SMODE_VBI_CAPTURE) {
+ com.cmd.StreamControl.MaxVBILinesPerField =
+ chan->nVBILines;
+ com.cmd.StreamControl.MinVBILinesPerField = 0;
+ com.cmd.StreamControl.BytesPerVBILine =
+ chan->nBytesPerVBILine;
+ }
+ if (flags & SFLAG_COLORBAR)
+ com.cmd.StreamControl.Stream |= 0x04;
+ }
+
+ spin_lock_irq(&chan->state_lock);
+ if (mode & SMODE_AUDIO_CAPTURE) {
+ chan->nextBuffer = chan->RingBuffer.Head;
+ if (mode & SMODE_AUDIO_SPDIF) {
+ com.cmd.StreamControl.SetupDataLen =
+ sizeof(SPDIFConfiguration);
+ com.cmd.StreamControl.SetupDataAddr = BsSPI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ SPDIFConfiguration, sizeof(SPDIFConfiguration));
+ } else {
+ com.cmd.StreamControl.SetupDataLen = 4;
+ com.cmd.StreamControl.SetupDataAddr = BsSDI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ I2SConfiguration +
+ 4 * dev->card_info->i2s[stream], 4);
+ }
+ } else if (mode & SMODE_TRANSPORT_STREAM) {
+ chan->nextBuffer = chan->TSRingBuffer.Head;
+ if (stream >= STREAM_AUDIOIN1) {
+ if (chan->mode & NGENE_IO_TSOUT) {
+ com.cmd.StreamControl.SetupDataLen =
+ sizeof(TS_I2SOutConfiguration);
+ com.cmd.StreamControl.SetupDataAddr = BsSDO;
+ memcpy(com.cmd.StreamControl.SetupData,
+ TS_I2SOutConfiguration,
+ sizeof(TS_I2SOutConfiguration));
+ } else {
+ com.cmd.StreamControl.SetupDataLen =
+ sizeof(TS_I2SConfiguration);
+ com.cmd.StreamControl.SetupDataAddr = BsSDI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ TS_I2SConfiguration,
+ sizeof(TS_I2SConfiguration));
+ }
+ } else {
+ com.cmd.StreamControl.SetupDataLen = 8;
+ com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10;
+ memcpy(com.cmd.StreamControl.SetupData,
+ TSFeatureDecoderSetup +
+ 8 * dev->card_info->tsf[stream], 8);
+ }
+ } else {
+ chan->nextBuffer = chan->RingBuffer.Head;
+ com.cmd.StreamControl.SetupDataLen =
+ 16 + sizeof(ITUFeatureDecoderSetup);
+ com.cmd.StreamControl.SetupDataAddr = BsUVI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ ITUDecoderSetup[chan->itumode], 16);
+ memcpy(com.cmd.StreamControl.SetupData + 16,
+ ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup));
+ }
+ clear_buffers(chan);
+ chan->State = KSSTATE_RUN;
+ if (mode & SMODE_TRANSPORT_STREAM)
+ chan->HWState = HWSTATE_RUN;
+ else
+ chan->HWState = HWSTATE_STARTUP;
+ spin_unlock_irq(&chan->state_lock);
+
+ if (ngene_command(dev, &com) < 0) {
+ up(&dev->stream_mutex);
+ return -1;
+ }
+ up(&dev->stream_mutex);
+ return 0;
+}
+
+
+/****************************************************************************/
+/* I2C **********************************************************************/
+/****************************************************************************/
+
+static void ngene_i2c_set_bus(struct ngene *dev, int bus)
+{
+ if (!(dev->card_info->i2c_access & 2))
+ return;
+ if (dev->i2c_current_bus == bus)
+ return;
+
+ switch (bus) {
+ case 0:
+ ngene_command_gpio_set(dev, 3, 0);
+ ngene_command_gpio_set(dev, 2, 1);
+ break;
+
+ case 1:
+ ngene_command_gpio_set(dev, 2, 0);
+ ngene_command_gpio_set(dev, 3, 1);
+ break;
+ }
+ dev->i2c_current_bus = bus;
+}
+
+static int ngene_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg msg[], int num)
+{
+ struct ngene_channel *chan =
+ (struct ngene_channel *)i2c_get_adapdata(adapter);
+ struct ngene *dev = chan->dev;
+
+ down(&dev->i2c_switch_mutex);
+ ngene_i2c_set_bus(dev, chan->number);
+
+ if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD))
+ if (!ngene_command_i2c_read(dev, msg[0].addr,
+ msg[0].buf, msg[0].len,
+ msg[1].buf, msg[1].len, 0))
+ goto done;
+
+ if (num == 1 && !(msg[0].flags & I2C_M_RD))
+ if (!ngene_command_i2c_write(dev, msg[0].addr,
+ msg[0].buf, msg[0].len))
+ goto done;
+ if (num == 1 && (msg[0].flags & I2C_M_RD))
+ if (!ngene_command_i2c_read(dev, msg[0].addr, 0, 0,
+ msg[0].buf, msg[0].len, 0))
+ goto done;
+
+ up(&dev->i2c_switch_mutex);
+ return -EIO;
+
+done:
+ up(&dev->i2c_switch_mutex);
+ return num;
+}
+
+
+static u32 ngene_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ngene_i2c_algo = {
+ .master_xfer = ngene_i2c_master_xfer,
+ .functionality = ngene_i2c_functionality,
+};
+
+static int ngene_i2c_init(struct ngene *dev, int dev_nr)
+{
+ struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter);
+
+ i2c_set_adapdata(adap, &(dev->channel[dev_nr]));
+ adap->class = I2C_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG;
+
+ strcpy(adap->name, "nGene");
+
+ adap->algo = &ngene_i2c_algo;
+ adap->algo_data = (void *)&(dev->channel[dev_nr]);
+ adap->dev.parent = &dev->pci_dev->dev;
+
+ return i2c_add_adapter(adap);
+}
+
+
+/****************************************************************************/
+/* DVB functions and API interface ******************************************/
+/****************************************************************************/
+
+static void swap_buffer(u32 *p, u32 len)
+{
+ while (len) {
+ *p = swab32(*p);
+ p++;
+ len -= 4;
+ }
+}
+
+
+static void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
+{
+ struct ngene_channel *chan = priv;
+
+
+#ifdef COMMAND_TIMEOUT_WORKAROUND
+ if (chan->users > 0)
+#endif
+ dvb_dmx_swfilter(&chan->demux, buf, len);
+ return 0;
+}
+
+u8 fill_ts[188] = { 0x47, 0x1f, 0xff, 0x10 };
+
+static void *tsout_exchange(void *priv, void *buf, u32 len,
+ u32 clock, u32 flags)
+{
+ struct ngene_channel *chan = priv;
+ struct ngene *dev = chan->dev;
+ u32 alen;
+
+ alen = dvb_ringbuffer_avail(&dev->tsout_rbuf);
+ alen -= alen % 188;
+
+ if (alen < len)
+ FillTSBuffer(buf + alen, len - alen, flags);
+ else
+ alen = len;
+ dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen);
+ if (flags & DF_SWAP32)
+ swap_buffer((u32 *)buf, alen);
+ wake_up_interruptible(&dev->tsout_rbuf.queue);
+ return buf;
+}
+
+
+static void set_transfer(struct ngene_channel *chan, int state)
+{
+ u8 control = 0, mode = 0, flags = 0;
+ struct ngene *dev = chan->dev;
+ int ret;
+
+ /*
+ printk(KERN_INFO DEVICE_NAME ": st %d\n", state);
+ msleep(100);
+ */
+
+ if (state) {
+ if (chan->running) {
+ printk(KERN_INFO DEVICE_NAME ": already running\n");
+ return;
+ }
+ } else {
+ if (!chan->running) {
+ printk(KERN_INFO DEVICE_NAME ": already stopped\n");
+ return;
+ }
+ }
+
+ if (dev->card_info->switch_ctrl)
+ dev->card_info->switch_ctrl(chan, 1, state ^ 1);
+
+ if (state) {
+ spin_lock_irq(&chan->state_lock);
+
+ /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ ngreadl(0x9310)); */
+ dvb_ringbuffer_flush(&dev->tsout_rbuf);
+ control = 0x80;
+ if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+ chan->Capture1Length = 512 * 188;
+ mode = SMODE_TRANSPORT_STREAM;
+ }
+ if (chan->mode & NGENE_IO_TSOUT) {
+ chan->pBufferExchange = tsout_exchange;
+ /* 0x66666666 = 50MHz *2^33 /250MHz */
+ chan->AudioDTOValue = 0x66666666;
+ /* set_dto(chan, 38810700+1000); */
+ /* set_dto(chan, 19392658); */
+ }
+ if (chan->mode & NGENE_IO_TSIN)
+ chan->pBufferExchange = tsin_exchange;
+ /* ngwritel(0, 0x9310); */
+ spin_unlock_irq(&chan->state_lock);
+ } else
+ ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ ngreadl(0x9310)); */
+
+ ret = ngene_command_stream_control(dev, chan->number,
+ control, mode, flags);
+ if (!ret)
+ chan->running = state;
+ else
+ printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n",
+ state);
+ if (!state) {
+ spin_lock_irq(&chan->state_lock);
+ chan->pBufferExchange = 0;
+ dvb_ringbuffer_flush(&dev->tsout_rbuf);
+ spin_unlock_irq(&chan->state_lock);
+ }
+}
+
+static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct ngene_channel *chan = dvbdmx->priv;
+
+ if (chan->users == 0) {
+#ifdef COMMAND_TIMEOUT_WORKAROUND
+ if (!chan->running)
+#endif
+ set_transfer(chan, 1);
+ /* msleep(10); */
+ }
+
+ return ++chan->users;
+}
+
+static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct ngene_channel *chan = dvbdmx->priv;
+
+ if (--chan->users)
+ return chan->users;
+
+#ifndef COMMAND_TIMEOUT_WORKAROUND
+ set_transfer(chan, 0);
+#endif
+
+ return 0;
+}
+
+
+
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+ int (*start_feed)(struct dvb_demux_feed *),
+ int (*stop_feed)(struct dvb_demux_feed *),
+ void *priv)
+{
+ dvbdemux->priv = priv;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = start_feed;
+ dvbdemux->stop_feed = stop_feed;
+ dvbdemux->write_to_decoder = 0;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
+ return dvb_dmx_init(dvbdemux);
+}
+
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+ struct dvb_demux *dvbdemux,
+ struct dmx_frontend *hw_frontend,
+ struct dmx_frontend *mem_frontend,
+ struct dvb_adapter *dvb_adapter)
+{
+ int ret;
+
+ dmxdev->filternum = 256;
+ dmxdev->demux = &dvbdemux->dmx;
+ dmxdev->capabilities = 0;
+ ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+ if (ret < 0)
+ return ret;
+
+ hw_frontend->source = DMX_FRONTEND_0;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+ mem_frontend->source = DMX_MEMORY_FE;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
+
+
+/****************************************************************************/
+/* nGene hardware init and release functions ********************************/
+/****************************************************************************/
+
+static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb)
+{
+ struct SBufferHeader *Cur = rb->Head;
+ u32 j;
+
+ if (!Cur)
+ return;
+
+ for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) {
+ if (Cur->Buffer1)
+ pci_free_consistent(dev->pci_dev,
+ rb->Buffer1Length,
+ Cur->Buffer1,
+ Cur->scList1->Address);
+
+ if (Cur->Buffer2)
+ pci_free_consistent(dev->pci_dev,
+ rb->Buffer2Length,
+ Cur->Buffer2,
+ Cur->scList2->Address);
+ }
+
+ if (rb->SCListMem)
+ pci_free_consistent(dev->pci_dev, rb->SCListMemSize,
+ rb->SCListMem, rb->PASCListMem);
+
+ pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead);
+}
+
+static void free_idlebuffer(struct ngene *dev,
+ struct SRingBufferDescriptor *rb,
+ struct SRingBufferDescriptor *tb)
+{
+ int j;
+ struct SBufferHeader *Cur = tb->Head;
+
+ if (!rb->Head)
+ return;
+ free_ringbuffer(dev, rb);
+ for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) {
+ Cur->Buffer2 = 0;
+ Cur->scList2 = 0;
+ Cur->ngeneBuffer.Address_of_first_entry_2 = 0;
+ Cur->ngeneBuffer.Number_of_entries_2 = 0;
+ }
+}
+
+static void free_common_buffers(struct ngene *dev)
+{
+ u32 i;
+ struct ngene_channel *chan;
+
+ for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
+ chan = &dev->channel[i];
+ free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer);
+ free_ringbuffer(dev, &chan->RingBuffer);
+ free_ringbuffer(dev, &chan->TSRingBuffer);
+ }
+
+ if (dev->OverflowBuffer)
+ pci_free_consistent(dev->pci_dev,
+ OVERFLOW_BUFFER_SIZE,
+ dev->OverflowBuffer, dev->PAOverflowBuffer);
+
+ if (dev->FWInterfaceBuffer)
+ pci_free_consistent(dev->pci_dev,
+ 4096,
+ dev->FWInterfaceBuffer,
+ dev->PAFWInterfaceBuffer);
+}
+
+/****************************************************************************/
+/* Ring buffer handling *****************************************************/
+/****************************************************************************/
+
+static int create_ring_buffer(struct pci_dev *pci_dev,
+ struct SRingBufferDescriptor *descr, u32 NumBuffers)
+{
+ dma_addr_t tmp;
+ struct SBufferHeader *Head;
+ u32 i;
+ u32 MemSize = SIZEOF_SBufferHeader * NumBuffers;
+ u64 PARingBufferHead;
+ u64 PARingBufferCur;
+ u64 PARingBufferNext;
+ struct SBufferHeader *Cur, *Next;
+
+ descr->Head = 0;
+ descr->MemSize = 0;
+ descr->PAHead = 0;
+ descr->NumBuffers = 0;
+
+ if (MemSize < 4096)
+ MemSize = 4096;
+
+ Head = pci_alloc_consistent(pci_dev, MemSize, &tmp);
+ PARingBufferHead = tmp;
+
+ if (!Head)
+ return -ENOMEM;
+
+ memset(Head, 0, MemSize);
+
+ PARingBufferCur = PARingBufferHead;
+ Cur = Head;
+
+ for (i = 0; i < NumBuffers - 1; i++) {
+ Next = (struct SBufferHeader *)
+ (((u8 *) Cur) + SIZEOF_SBufferHeader);
+ PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader;
+ Cur->Next = Next;
+ Cur->ngeneBuffer.Next = PARingBufferNext;
+ Cur = Next;
+ PARingBufferCur = PARingBufferNext;
+ }
+ /* Last Buffer points back to first one */
+ Cur->Next = Head;
+ Cur->ngeneBuffer.Next = PARingBufferHead;
+
+ descr->Head = Head;
+ descr->MemSize = MemSize;
+ descr->PAHead = PARingBufferHead;
+ descr->NumBuffers = NumBuffers;
+
+ return 0;
+}
+
+static int AllocateRingBuffers(struct pci_dev *pci_dev,
+ dma_addr_t of,
+ struct SRingBufferDescriptor *pRingBuffer,
+ u32 Buffer1Length, u32 Buffer2Length)
+{
+ dma_addr_t tmp;
+ u32 i, j;
+ int status = 0;
+ u32 SCListMemSize = pRingBuffer->NumBuffers
+ * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) :
+ NUM_SCATTER_GATHER_ENTRIES)
+ * sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+ u64 PASCListMem;
+ struct HW_SCATTER_GATHER_ELEMENT *SCListEntry;
+ u64 PASCListEntry;
+ struct SBufferHeader *Cur;
+ void *SCListMem;
+
+ if (SCListMemSize < 4096)
+ SCListMemSize = 4096;
+
+ SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp);
+
+ PASCListMem = tmp;
+ if (SCListMem == NULL)
+ return -ENOMEM;
+
+ memset(SCListMem, 0, SCListMemSize);
+
+ pRingBuffer->SCListMem = SCListMem;
+ pRingBuffer->PASCListMem = PASCListMem;
+ pRingBuffer->SCListMemSize = SCListMemSize;
+ pRingBuffer->Buffer1Length = Buffer1Length;
+ pRingBuffer->Buffer2Length = Buffer2Length;
+
+ SCListEntry = SCListMem;
+ PASCListEntry = PASCListMem;
+ Cur = pRingBuffer->Head;
+
+ for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) {
+ u64 PABuffer;
+
+ void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length,
+ &tmp);
+ PABuffer = tmp;
+
+ if (Buffer == NULL)
+ return -ENOMEM;
+
+ Cur->Buffer1 = Buffer;
+
+ SCListEntry->Address = PABuffer;
+ SCListEntry->Length = Buffer1Length;
+
+ Cur->scList1 = SCListEntry;
+ Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry;
+ Cur->ngeneBuffer.Number_of_entries_1 =
+ NUM_SCATTER_GATHER_ENTRIES;
+
+ SCListEntry += 1;
+ PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+#if NUM_SCATTER_GATHER_ENTRIES > 1
+ for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) {
+ SCListEntry->Address = of;
+ SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
+ SCListEntry += 1;
+ PASCListEntry +=
+ sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+ }
+#endif
+
+ if (!Buffer2Length)
+ continue;
+
+ Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp);
+ PABuffer = tmp;
+
+ if (Buffer == NULL)
+ return -ENOMEM;
+
+ Cur->Buffer2 = Buffer;
+
+ SCListEntry->Address = PABuffer;
+ SCListEntry->Length = Buffer2Length;
+
+ Cur->scList2 = SCListEntry;
+ Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry;
+ Cur->ngeneBuffer.Number_of_entries_2 =
+ NUM_SCATTER_GATHER_ENTRIES;
+
+ SCListEntry += 1;
+ PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+#if NUM_SCATTER_GATHER_ENTRIES > 1
+ for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) {
+ SCListEntry->Address = of;
+ SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
+ SCListEntry += 1;
+ PASCListEntry +=
+ sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+ }
+#endif
+
+ }
+
+ return status;
+}
+
+static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
+ struct SRingBufferDescriptor *pRingBuffer)
+{
+ int status = 0;
+
+ /* Copy pointer to scatter gather list in TSRingbuffer
+ structure for buffer 2
+ Load number of buffer
+ */
+ u32 n = pRingBuffer->NumBuffers;
+
+ /* Point to first buffer entry */
+ struct SBufferHeader *Cur = pRingBuffer->Head;
+ int i;
+ /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */
+ for (i = 0; i < n; i++) {
+ Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
+ Cur->scList2 = pIdleBuffer->Head->scList1;
+ Cur->ngeneBuffer.Address_of_first_entry_2 =
+ pIdleBuffer->Head->ngeneBuffer.
+ Address_of_first_entry_1;
+ Cur->ngeneBuffer.Number_of_entries_2 =
+ pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1;
+ Cur = Cur->Next;
+ }
+ return status;
+}
+
+static u32 RingBufferSizes[MAX_STREAM] = {
+ RING_SIZE_VIDEO,
+ RING_SIZE_VIDEO,
+ RING_SIZE_AUDIO,
+ RING_SIZE_AUDIO,
+ RING_SIZE_AUDIO,
+};
+
+static u32 Buffer1Sizes[MAX_STREAM] = {
+ MAX_VIDEO_BUFFER_SIZE,
+ MAX_VIDEO_BUFFER_SIZE,
+ MAX_AUDIO_BUFFER_SIZE,
+ MAX_AUDIO_BUFFER_SIZE,
+ MAX_AUDIO_BUFFER_SIZE
+};
+
+static u32 Buffer2Sizes[MAX_STREAM] = {
+ MAX_VBI_BUFFER_SIZE,
+ MAX_VBI_BUFFER_SIZE,
+ 0,
+ 0,
+ 0
+};
+
+
+static int AllocCommonBuffers(struct ngene *dev)
+{
+ int status = 0, i;
+
+ dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096,
+ &dev->PAFWInterfaceBuffer);
+ if (!dev->FWInterfaceBuffer)
+ return -ENOMEM;
+ dev->hosttongene = dev->FWInterfaceBuffer;
+ dev->ngenetohost = dev->FWInterfaceBuffer + 256;
+ dev->EventBuffer = dev->FWInterfaceBuffer + 512;
+
+ dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev,
+ OVERFLOW_BUFFER_SIZE,
+ &dev->PAOverflowBuffer);
+ if (!dev->OverflowBuffer)
+ return -ENOMEM;
+ memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE);
+
+ for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
+ int type = dev->card_info->io_type[i];
+
+ dev->channel[i].State = KSSTATE_STOP;
+
+ if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) {
+ status = create_ring_buffer(dev->pci_dev,
+ &dev->channel[i].RingBuffer,
+ RingBufferSizes[i]);
+ if (status < 0)
+ break;
+
+ if (type & (NGENE_IO_TV | NGENE_IO_AIN)) {
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->
+ PAOverflowBuffer,
+ &dev->channel[i].
+ RingBuffer,
+ Buffer1Sizes[i],
+ Buffer2Sizes[i]);
+ if (status < 0)
+ break;
+ } else if (type & NGENE_IO_HDTV) {
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->
+ PAOverflowBuffer,
+ &dev->channel[i].
+ RingBuffer,
+ MAX_HDTV_BUFFER_SIZE,
+ 0);
+ if (status < 0)
+ break;
+ }
+ }
+
+ if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+
+ status = create_ring_buffer(dev->pci_dev,
+ &dev->channel[i].
+ TSRingBuffer, RING_SIZE_TS);
+ if (status < 0)
+ break;
+
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->PAOverflowBuffer,
+ &dev->channel[i].
+ TSRingBuffer,
+ MAX_TS_BUFFER_SIZE, 0);
+ if (status)
+ break;
+ }
+
+ if (type & NGENE_IO_TSOUT) {
+ status = create_ring_buffer(dev->pci_dev,
+ &dev->channel[i].
+ TSIdleBuffer, 1);
+ if (status < 0)
+ break;
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->PAOverflowBuffer,
+ &dev->channel[i].
+ TSIdleBuffer,
+ MAX_TS_BUFFER_SIZE, 0);
+ if (status)
+ break;
+ FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer,
+ &dev->channel[i].TSRingBuffer);
+ }
+ }
+ return status;
+}
+
+static void ngene_release_buffers(struct ngene *dev)
+{
+ if (dev->iomem)
+ iounmap(dev->iomem);
+ free_common_buffers(dev);
+ vfree(dev->tsout_buf);
+ vfree(dev->ain_buf);
+ vfree(dev->vin_buf);
+ vfree(dev);
+}
+
+static int ngene_get_buffers(struct ngene *dev)
+{
+ if (AllocCommonBuffers(dev))
+ return -ENOMEM;
+ if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) {
+ dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE);
+ if (!dev->tsout_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->tsout_rbuf,
+ dev->tsout_buf, TSOUT_BUF_SIZE);
+ }
+ if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
+ dev->ain_buf = vmalloc(AIN_BUF_SIZE);
+ if (!dev->ain_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE);
+ }
+ if (dev->card_info->io_type[0] & NGENE_IO_HDTV) {
+ dev->vin_buf = vmalloc(VIN_BUF_SIZE);
+ if (!dev->vin_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE);
+ }
+ dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0),
+ pci_resource_len(dev->pci_dev, 0));
+ if (!dev->iomem)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ngene_init(struct ngene *dev)
+{
+ int i;
+
+ tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev);
+
+ memset_io(dev->iomem + 0xc000, 0x00, 0x220);
+ memset_io(dev->iomem + 0xc400, 0x00, 0x100);
+
+ for (i = 0; i < MAX_STREAM; i++) {
+ dev->channel[i].dev = dev;
+ dev->channel[i].number = i;
+ }
+
+ dev->fw_interface_version = 0;
+
+ ngwritel(0, NGENE_INT_ENABLE);
+
+ dev->icounts = ngreadl(NGENE_INT_COUNTS);
+
+ dev->device_version = ngreadl(DEV_VER) & 0x0f;
+ printk(KERN_INFO DEVICE_NAME ": Device version %d\n",
+ dev->device_version);
+}
+
+static int ngene_load_firm(struct ngene *dev)
+{
+ u32 size;
+ const struct firmware *fw = NULL;
+ u8 *ngene_fw;
+ char *fw_name;
+ int err, version;
+
+ version = dev->card_info->fw_version;
+
+ switch (version) {
+ default:
+ case 15:
+ version = 15;
+ size = 23466;
+ fw_name = "ngene_15.fw";
+ break;
+ case 16:
+ size = 23498;
+ fw_name = "ngene_16.fw";
+ break;
+ case 17:
+ size = 24446;
+ fw_name = "ngene_17.fw";
+ break;
+ }
+
+ if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
+ printk(KERN_ERR DEVICE_NAME
+ ": Could not load firmware file %s.\n", fw_name);
+ printk(KERN_INFO DEVICE_NAME
+ ": Copy %s to your hotplug directory!\n", fw_name);
+ return -1;
+ }
+ if (size != fw->size) {
+ printk(KERN_ERR DEVICE_NAME
+ ": Firmware %s has invalid size!", fw_name);
+ err = -1;
+ } else {
+ printk(KERN_INFO DEVICE_NAME
+ ": Loading firmware file %s.\n", fw_name);
+ ngene_fw = (u8 *) fw->data;
+ err = ngene_command_load_firmware(dev, ngene_fw, size);
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+
+static void ngene_stop(struct ngene *dev)
+{
+ down(&dev->cmd_mutex);
+ i2c_del_adapter(&(dev->channel[0].i2c_adapter));
+ i2c_del_adapter(&(dev->channel[1].i2c_adapter));
+ ngwritel(0, NGENE_INT_ENABLE);
+ ngwritel(0, NGENE_COMMAND);
+ ngwritel(0, NGENE_COMMAND_HI);
+ ngwritel(0, NGENE_STATUS);
+ ngwritel(0, NGENE_STATUS_HI);
+ ngwritel(0, NGENE_EVENT);
+ ngwritel(0, NGENE_EVENT_HI);
+ free_irq(dev->pci_dev->irq, dev);
+}
+
+static int ngene_start(struct ngene *dev)
+{
+ int stat;
+ int i;
+
+ pci_set_master(dev->pci_dev);
+ ngene_init(dev);
+
+ stat = request_irq(dev->pci_dev->irq, irq_handler,
+ IRQF_SHARED, "nGene",
+ (void *)dev);
+ if (stat < 0)
+ return stat;
+
+ init_waitqueue_head(&dev->cmd_wq);
+ init_waitqueue_head(&dev->tx_wq);
+ init_waitqueue_head(&dev->rx_wq);
+ sema_init(&dev->cmd_mutex, 1);
+ sema_init(&dev->stream_mutex, 1);
+ sema_init(&dev->pll_mutex, 1);
+ sema_init(&dev->i2c_switch_mutex, 1);
+ spin_lock_init(&dev->cmd_lock);
+ for (i = 0; i < MAX_STREAM; i++)
+ spin_lock_init(&dev->channel[i].state_lock);
+ ngwritel(1, TIMESTAMPS);
+
+ ngwritel(1, NGENE_INT_ENABLE);
+
+ stat = ngene_load_firm(dev);
+ if (stat < 0)
+ goto fail;
+
+ stat = ngene_i2c_init(dev, 0);
+ if (stat < 0)
+ goto fail;
+
+ stat = ngene_i2c_init(dev, 1);
+ if (stat < 0)
+ goto fail;
+
+ if (dev->card_info->fw_version == 17) {
+ u8 tsin4_config[6] = {
+ 3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0};
+ u8 default_config[6] = {
+ 4096 / 64, 4096 / 64, 0, 2048 / 64, 2048 / 64, 0};
+ u8 *bconf = default_config;
+
+ if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
+ bconf = tsin4_config;
+ dprintk(KERN_DEBUG DEVICE_NAME ": FW 17 buffer config\n");
+ stat = ngene_command_config_free_buf(dev, bconf);
+ } else {
+ int bconf = BUFFER_CONFIG_4422;
+ if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
+ bconf = BUFFER_CONFIG_3333;
+ stat = ngene_command_config_buf(dev, bconf);
+ }
+ return stat;
+fail:
+ ngwritel(0, NGENE_INT_ENABLE);
+ free_irq(dev->pci_dev->irq, dev);
+ return stat;
+}
+
+
+
+/****************************************************************************/
+/* Switch control (I2C gates, etc.) *****************************************/
+/****************************************************************************/
+
+
+/****************************************************************************/
+/* Demod/tuner attachment ***************************************************/
+/****************************************************************************/
+
+static int tuner_attach_stv6110(struct ngene_channel *chan)
+{
+ struct stv090x_config *feconf = (struct stv090x_config *)
+ chan->dev->card_info->fe_config[chan->number];
+ struct stv6110x_config *tunerconf = (struct stv6110x_config *)
+ chan->dev->card_info->tuner_config[chan->number];
+ struct stv6110x_devctl *ctl;
+
+ ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf,
+ &chan->i2c_adapter);
+ if (ctl == NULL) {
+ printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
+ return -ENODEV;
+ }
+
+ feconf->tuner_init = ctl->tuner_init;
+ feconf->tuner_set_mode = ctl->tuner_set_mode;
+ feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+ feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+ feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ feconf->tuner_set_bbgain = ctl->tuner_set_bbgain;
+ feconf->tuner_get_bbgain = ctl->tuner_get_bbgain;
+ feconf->tuner_set_refclk = ctl->tuner_set_refclk;
+ feconf->tuner_get_status = ctl->tuner_get_status;
+
+ return 0;
+}
+
+
+static int demod_attach_stv0900(struct ngene_channel *chan)
+{
+ struct stv090x_config *feconf = (struct stv090x_config *)
+ chan->dev->card_info->fe_config[chan->number];
+
+ chan->fe = dvb_attach(stv090x_attach,
+ feconf,
+ &chan->i2c_adapter,
+ chan->number == 0 ? STV090x_DEMODULATOR_0 :
+ STV090x_DEMODULATOR_1);
+ if (chan->fe == NULL) {
+ printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
+ return -ENODEV;
+ }
+
+ if (!dvb_attach(lnbh24_attach, chan->fe, &chan->i2c_adapter, 0,
+ 0, chan->dev->card_info->lnb[chan->number])) {
+ printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+ dvb_frontend_detach(chan->fe);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void release_channel(struct ngene_channel *chan)
+{
+ struct dvb_demux *dvbdemux = &chan->demux;
+ struct ngene *dev = chan->dev;
+ struct ngene_info *ni = dev->card_info;
+ int io = ni->io_type[chan->number];
+
+#ifdef COMMAND_TIMEOUT_WORKAROUND
+ if (chan->running)
+ set_transfer(chan, 0);
+#endif
+
+ tasklet_kill(&chan->demux_tasklet);
+
+ if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+ if (chan->fe) {
+ dvb_unregister_frontend(chan->fe);
+ dvb_frontend_detach(chan->fe);
+ chan->fe = 0;
+ }
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &chan->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &chan->mem_frontend);
+ dvb_dmxdev_release(&chan->dmxdev);
+ dvb_dmx_release(&chan->demux);
+
+ if (chan->number == 0 || !one_adapter)
+ dvb_unregister_adapter(&dev->adapter[chan->number]);
+ }
+}
+
+static int init_channel(struct ngene_channel *chan)
+{
+ int ret = 0, nr = chan->number;
+ struct dvb_adapter *adapter = NULL;
+ struct dvb_demux *dvbdemux = &chan->demux;
+ struct ngene *dev = chan->dev;
+ struct ngene_info *ni = dev->card_info;
+ int io = ni->io_type[nr];
+
+ tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan);
+ chan->users = 0;
+ chan->type = io;
+ chan->mode = chan->type; /* for now only one mode */
+
+ if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+ if (nr >= STREAM_AUDIOIN1)
+ chan->DataFormatFlags = DF_SWAP32;
+ if (nr == 0 || !one_adapter) {
+ adapter = &dev->adapter[nr];
+ ret = dvb_register_adapter(adapter, "nGene",
+ THIS_MODULE,
+ &chan->dev->pci_dev->dev,
+ adapter_nr);
+ if (ret < 0)
+ return ret;
+ } else {
+ adapter = &dev->adapter[0];
+ }
+
+ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+ ngene_start_feed,
+ ngene_stop_feed, chan);
+ ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
+ &chan->hw_frontend,
+ &chan->mem_frontend, adapter);
+ }
+
+ if (io & NGENE_IO_TSIN) {
+ chan->fe = NULL;
+ if (ni->demod_attach[nr])
+ ni->demod_attach[nr](chan);
+ if (chan->fe) {
+ if (dvb_register_frontend(adapter, chan->fe) < 0) {
+ if (chan->fe->ops.release)
+ chan->fe->ops.release(chan->fe);
+ chan->fe = NULL;
+ }
+ }
+ if (chan->fe && ni->tuner_attach[nr])
+ if (ni->tuner_attach[nr] (chan) < 0) {
+ printk(KERN_ERR DEVICE_NAME
+ ": Tuner attach failed on channel %d!\n",
+ nr);
+ }
+ }
+ return ret;
+}
+
+static int init_channels(struct ngene *dev)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_STREAM; i++) {
+ if (init_channel(&dev->channel[i]) < 0) {
+ for (j = i - 1; j >= 0; j--)
+ release_channel(&dev->channel[j]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/* device probe/remove calls ************************************************/
+/****************************************************************************/
+
+static void __devexit ngene_remove(struct pci_dev *pdev)
+{
+ struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
+ int i;
+
+ tasklet_kill(&dev->event_tasklet);
+ for (i = MAX_STREAM - 1; i >= 0; i--)
+ release_channel(&dev->channel[i]);
+ ngene_stop(dev);
+ ngene_release_buffers(dev);
+ pci_set_drvdata(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+static int __devinit ngene_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *id)
+{
+ struct ngene *dev;
+ int stat = 0;
+
+ if (pci_enable_device(pci_dev) < 0)
+ return -ENODEV;
+
+ dev = vmalloc(sizeof(struct ngene));
+ if (dev == NULL) {
+ stat = -ENOMEM;
+ goto fail0;
+ }
+ memset(dev, 0, sizeof(struct ngene));
+
+ dev->pci_dev = pci_dev;
+ dev->card_info = (struct ngene_info *)id->driver_data;
+ printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name);
+
+ pci_set_drvdata(pci_dev, dev);
+
+ /* Alloc buffers and start nGene */
+ stat = ngene_get_buffers(dev);
+ if (stat < 0)
+ goto fail1;
+ stat = ngene_start(dev);
+ if (stat < 0)
+ goto fail1;
+
+ dev->i2c_current_bus = -1;
+
+ /* Register DVB adapters and devices for both channels */
+ if (init_channels(dev) < 0)
+ goto fail2;
+
+ return 0;
+
+fail2:
+ ngene_stop(dev);
+fail1:
+ ngene_release_buffers(dev);
+fail0:
+ pci_disable_device(pci_dev);
+ pci_set_drvdata(pci_dev, 0);
+ return stat;
+}
+
+/****************************************************************************/
+/* Card configs *************************************************************/
+/****************************************************************************/
+
+static struct stv090x_config fe_cineS2 = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+};
+
+static struct stv6110x_config tuner_cineS2_0 = {
+ .addr = 0x60,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static struct stv6110x_config tuner_cineS2_1 = {
+ .addr = 0x63,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static struct ngene_info ngene_info_cineS2 = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Linux4Media cineS2 DVB-S2 Twin Tuner",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0b, 0x08},
+ .tsf = {3, 3},
+ .fw_version = 15,
+};
+
+static struct ngene_info ngene_info_satixs2 = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Mystique SaTiX-S2 Dual",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0b, 0x08},
+ .tsf = {3, 3},
+ .fw_version = 15,
+};
+
+/****************************************************************************/
+
+
+
+/****************************************************************************/
+/* PCI Subsystem ID *********************************************************/
+/****************************************************************************/
+
+#define NGENE_ID(_subvend, _subdev, _driverdata) { \
+ .vendor = NGENE_VID, .device = NGENE_PID, \
+ .subvendor = _subvend, .subdevice = _subdev, \
+ .driver_data = (unsigned long) &_driverdata }
+
+/****************************************************************************/
+
+static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
+ NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
+ NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
+ NGENE_ID(0x18c3, 0xdb01, ngene_info_satixs2),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
+
+/****************************************************************************/
+/* Init/Exit ****************************************************************/
+/****************************************************************************/
+
+static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
+ enum pci_channel_state state)
+{
+ printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+ if (state == pci_channel_io_perm_failure)
+ return PCI_ERS_RESULT_DISCONNECT;
+ if (state == pci_channel_io_frozen)
+ return PCI_ERS_RESULT_NEED_RESET;
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+static pci_ers_result_t ngene_link_reset(struct pci_dev *dev)
+{
+ printk(KERN_INFO DEVICE_NAME ": link reset\n");
+ return 0;
+}
+
+static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
+{
+ printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+ return 0;
+}
+
+static void ngene_resume(struct pci_dev *dev)
+{
+ printk(KERN_INFO DEVICE_NAME ": resume\n");
+}
+
+static struct pci_error_handlers ngene_errors = {
+ .error_detected = ngene_error_detected,
+ .link_reset = ngene_link_reset,
+ .slot_reset = ngene_slot_reset,
+ .resume = ngene_resume,
+};
+
+static struct pci_driver ngene_pci_driver = {
+ .name = "ngene",
+ .id_table = ngene_id_tbl,
+ .probe = ngene_probe,
+ .remove = __devexit_p(ngene_remove),
+ .err_handler = &ngene_errors,
+};
+
+static __init int module_init_ngene(void)
+{
+ printk(KERN_INFO
+ "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+ return pci_register_driver(&ngene_pci_driver);
+}
+
+static __exit void module_exit_ngene(void)
+{
+ pci_unregister_driver(&ngene_pci_driver);
+}
+
+module_init(module_init_ngene);
+module_exit(module_exit_ngene);
+
+MODULE_DESCRIPTION("nGene");
+MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h
new file mode 100644
index 00000000000..a7eb2984631
--- /dev/null
+++ b/drivers/media/dvb/ngene/ngene.h
@@ -0,0 +1,859 @@
+/*
+ * ngene.h: nGene PCIe bridge driver
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _NGENE_H_
+#define _NGENE_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <asm/dma.h>
+#include <linux/scatterlist.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_ringbuffer.h"
+
+#define NGENE_VID 0x18c3
+#define NGENE_PID 0x0720
+
+#ifndef VIDEO_CAP_VC1
+#define VIDEO_CAP_AVC 128
+#define VIDEO_CAP_H264 128
+#define VIDEO_CAP_VC1 256
+#define VIDEO_CAP_WMV9 256
+#define VIDEO_CAP_MPEG4 512
+#endif
+
+enum STREAM {
+ STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */
+ STREAM_VIDEOIN2,
+ STREAM_AUDIOIN1, /* I2S or SPI Input */
+ STREAM_AUDIOIN2,
+ STREAM_AUDIOOUT,
+ MAX_STREAM
+};
+
+enum SMODE_BITS {
+ SMODE_AUDIO_SPDIF = 0x20,
+ SMODE_AVSYNC = 0x10,
+ SMODE_TRANSPORT_STREAM = 0x08,
+ SMODE_AUDIO_CAPTURE = 0x04,
+ SMODE_VBI_CAPTURE = 0x02,
+ SMODE_VIDEO_CAPTURE = 0x01
+};
+
+enum STREAM_FLAG_BITS {
+ SFLAG_CHROMA_FORMAT_2COMP = 0x01, /* Chroma Format : 2's complement */
+ SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */
+ SFLAG_ORDER_LUMA_CHROMA = 0x02, /* Byte order: Y,Cb,Y,Cr */
+ SFLAG_ORDER_CHROMA_LUMA = 0x00, /* Byte order: Cb,Y,Cr,Y */
+ SFLAG_COLORBAR = 0x04, /* Select colorbar */
+};
+
+#define PROGRAM_ROM 0x0000
+#define PROGRAM_SRAM 0x1000
+#define PERIPHERALS0 0x8000
+#define PERIPHERALS1 0x9000
+#define SHARED_BUFFER 0xC000
+
+#define HOST_TO_NGENE (SHARED_BUFFER+0x0000)
+#define NGENE_TO_HOST (SHARED_BUFFER+0x0100)
+#define NGENE_COMMAND (SHARED_BUFFER+0x0200)
+#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204)
+#define NGENE_STATUS (SHARED_BUFFER+0x0208)
+#define NGENE_STATUS_HI (SHARED_BUFFER+0x020C)
+#define NGENE_EVENT (SHARED_BUFFER+0x0210)
+#define NGENE_EVENT_HI (SHARED_BUFFER+0x0214)
+#define VARIABLES (SHARED_BUFFER+0x0210)
+
+#define NGENE_INT_COUNTS (SHARED_BUFFER+0x0260)
+#define NGENE_INT_ENABLE (SHARED_BUFFER+0x0264)
+#define NGENE_VBI_LINE_COUNT (SHARED_BUFFER+0x0268)
+
+#define BUFFER_GP_XMIT (SHARED_BUFFER+0x0800)
+#define BUFFER_GP_RECV (SHARED_BUFFER+0x0900)
+#define EEPROM_AREA (SHARED_BUFFER+0x0A00)
+
+#define SG_V_IN_1 (SHARED_BUFFER+0x0A80)
+#define SG_VBI_1 (SHARED_BUFFER+0x0B00)
+#define SG_A_IN_1 (SHARED_BUFFER+0x0B80)
+#define SG_V_IN_2 (SHARED_BUFFER+0x0C00)
+#define SG_VBI_2 (SHARED_BUFFER+0x0C80)
+#define SG_A_IN_2 (SHARED_BUFFER+0x0D00)
+#define SG_V_OUT (SHARED_BUFFER+0x0D80)
+#define SG_A_OUT2 (SHARED_BUFFER+0x0E00)
+
+#define DATA_A_IN_1 (SHARED_BUFFER+0x0E80)
+#define DATA_A_IN_2 (SHARED_BUFFER+0x0F00)
+#define DATA_A_OUT (SHARED_BUFFER+0x0F80)
+#define DATA_V_IN_1 (SHARED_BUFFER+0x1000)
+#define DATA_V_IN_2 (SHARED_BUFFER+0x2000)
+#define DATA_V_OUT (SHARED_BUFFER+0x3000)
+
+#define DATA_FIFO_AREA (SHARED_BUFFER+0x1000)
+
+#define TIMESTAMPS 0xA000
+#define SCRATCHPAD 0xA080
+#define FORCE_INT 0xA088
+#define FORCE_NMI 0xA090
+#define INT_STATUS 0xA0A0
+
+#define DEV_VER 0x9004
+
+#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF)
+
+struct SG_ADDR {
+ u64 start;
+ u64 curr;
+ u16 curr_ptr;
+ u16 elements;
+ u32 pad[3];
+} __attribute__ ((__packed__));
+
+struct SHARED_MEMORY {
+ /* C000 */
+ u32 HostToNgene[64];
+
+ /* C100 */
+ u32 NgeneToHost[64];
+
+ /* C200 */
+ u64 NgeneCommand;
+ u64 NgeneStatus;
+ u64 NgeneEvent;
+
+ /* C210 */
+ u8 pad1[0xc260 - 0xc218];
+
+ /* C260 */
+ u32 IntCounts;
+ u32 IntEnable;
+
+ /* C268 */
+ u8 pad2[0xd000 - 0xc268];
+
+} __attribute__ ((__packed__));
+
+struct BUFFER_STREAM_RESULTS {
+ u32 Clock; /* Stream time in 100ns units */
+ u16 RemainingLines; /* Remaining lines in this field.
+ 0 for complete field */
+ u8 FieldCount; /* Video field number */
+ u8 Flags; /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow,
+ Bit 0 = FieldID */
+ u16 BlockCount; /* Audio block count (unused) */
+ u8 Reserved[2];
+ u32 DTOUpdate;
+} __attribute__ ((__packed__));
+
+struct HW_SCATTER_GATHER_ELEMENT {
+ u64 Address;
+ u32 Length;
+ u32 Reserved;
+} __attribute__ ((__packed__));
+
+struct BUFFER_HEADER {
+ u64 Next;
+ struct BUFFER_STREAM_RESULTS SR;
+
+ u32 Number_of_entries_1;
+ u32 Reserved5;
+ u64 Address_of_first_entry_1;
+
+ u32 Number_of_entries_2;
+ u32 Reserved7;
+ u64 Address_of_first_entry_2;
+} __attribute__ ((__packed__));
+
+struct EVENT_BUFFER {
+ u32 TimeStamp;
+ u8 GPIOStatus;
+ u8 UARTStatus;
+ u8 RXCharacter;
+ u8 EventStatus;
+ u32 Reserved[2];
+} __attribute__ ((__packed__));
+
+/* Firmware commands. */
+
+enum OPCODES {
+ CMD_NOP = 0,
+ CMD_FWLOAD_PREPARE = 0x01,
+ CMD_FWLOAD_FINISH = 0x02,
+ CMD_I2C_READ = 0x03,
+ CMD_I2C_WRITE = 0x04,
+
+ CMD_I2C_WRITE_NOSTOP = 0x05,
+ CMD_I2C_CONTINUE_WRITE = 0x06,
+ CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07,
+
+ CMD_DEBUG_OUTPUT = 0x09,
+
+ CMD_CONTROL = 0x10,
+ CMD_CONFIGURE_BUFFER = 0x11,
+ CMD_CONFIGURE_FREE_BUFFER = 0x12,
+
+ CMD_SPI_READ = 0x13,
+ CMD_SPI_WRITE = 0x14,
+
+ CMD_MEM_READ = 0x20,
+ CMD_MEM_WRITE = 0x21,
+ CMD_SFR_READ = 0x22,
+ CMD_SFR_WRITE = 0x23,
+ CMD_IRAM_READ = 0x24,
+ CMD_IRAM_WRITE = 0x25,
+ CMD_SET_GPIO_PIN = 0x26,
+ CMD_SET_GPIO_INT = 0x27,
+ CMD_CONFIGURE_UART = 0x28,
+ CMD_WRITE_UART = 0x29,
+ MAX_CMD
+};
+
+enum RESPONSES {
+ OK = 0,
+ ERROR = 1
+};
+
+struct FW_HEADER {
+ u8 Opcode;
+ u8 Length;
+} __attribute__ ((__packed__));
+
+struct FW_I2C_WRITE {
+ struct FW_HEADER hdr;
+ u8 Device;
+ u8 Data[250];
+} __attribute__ ((__packed__));
+
+struct FW_I2C_CONTINUE_WRITE {
+ struct FW_HEADER hdr;
+ u8 Data[250];
+} __attribute__ ((__packed__));
+
+struct FW_I2C_READ {
+ struct FW_HEADER hdr;
+ u8 Device;
+ u8 Data[252]; /* followed by two bytes of read data count */
+} __attribute__ ((__packed__));
+
+struct FW_SPI_WRITE {
+ struct FW_HEADER hdr;
+ u8 ModeSelect;
+ u8 Data[250];
+} __attribute__ ((__packed__));
+
+struct FW_SPI_READ {
+ struct FW_HEADER hdr;
+ u8 ModeSelect;
+ u8 Data[252]; /* followed by two bytes of read data count */
+} __attribute__ ((__packed__));
+
+struct FW_FWLOAD_PREPARE {
+ struct FW_HEADER hdr;
+} __attribute__ ((__packed__));
+
+struct FW_FWLOAD_FINISH {
+ struct FW_HEADER hdr;
+ u16 Address; /* address of final block */
+ u16 Length;
+} __attribute__ ((__packed__));
+
+/*
+ * Meaning of FW_STREAM_CONTROL::Mode bits:
+ * Bit 7: Loopback PEXin to PEXout using TVOut channel
+ * Bit 6: AVLOOP
+ * Bit 5: Audio select; 0=I2S, 1=SPDIF
+ * Bit 4: AVSYNC
+ * Bit 3: Enable transport stream
+ * Bit 2: Enable audio capture
+ * Bit 1: Enable ITU-Video VBI capture
+ * Bit 0: Enable ITU-Video capture
+ *
+ * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL)
+ * Bit 7: continuous capture
+ * Bit 6: capture one field
+ * Bit 5: capture one frame
+ * Bit 4: unused
+ * Bit 3: starting field; 0=odd, 1=even
+ * Bit 2: sample size; 0=8-bit, 1=10-bit
+ * Bit 1: data format; 0=UYVY, 1=YUY2
+ * Bit 0: resets buffer pointers
+*/
+
+enum FSC_MODE_BITS {
+ SMODE_LOOPBACK = 0x80,
+ SMODE_AVLOOP = 0x40,
+ _SMODE_AUDIO_SPDIF = 0x20,
+ _SMODE_AVSYNC = 0x10,
+ _SMODE_TRANSPORT_STREAM = 0x08,
+ _SMODE_AUDIO_CAPTURE = 0x04,
+ _SMODE_VBI_CAPTURE = 0x02,
+ _SMODE_VIDEO_CAPTURE = 0x01
+};
+
+
+/* Meaning of FW_STREAM_CONTROL::Stream bits:
+ * Bit 3: Audio sample count: 0 = relative, 1 = absolute
+ * Bit 2: color bar select; 1=color bars, 0=CV3 decoder
+ * Bits 1-0: stream select, UVI1, UVI2, TVOUT
+ */
+
+struct FW_STREAM_CONTROL {
+ struct FW_HEADER hdr;
+ u8 Stream; /* Stream number (UVI1, UVI2, TVOUT) */
+ u8 Control; /* Value written to UVI1_CTL */
+ u8 Mode; /* Controls clock source */
+ u8 SetupDataLen; /* Length of setup data, MSB=1 write
+ backwards */
+ u16 CaptureBlockCount; /* Blocks (a 256 Bytes) to capture per buffer
+ for TS and Audio */
+ u64 Buffer_Address; /* Address of first buffer header */
+ u16 BytesPerVideoLine;
+ u16 MaxLinesPerField;
+ u16 MinLinesPerField;
+ u16 Reserved_1;
+ u16 BytesPerVBILine;
+ u16 MaxVBILinesPerField;
+ u16 MinVBILinesPerField;
+ u16 SetupDataAddr; /* ngene relative address of setup data */
+ u8 SetupData[32]; /* setup data */
+} __attribute__((__packed__));
+
+#define AUDIO_BLOCK_SIZE 256
+#define TS_BLOCK_SIZE 256
+
+struct FW_MEM_READ {
+ struct FW_HEADER hdr;
+ u16 address;
+} __attribute__ ((__packed__));
+
+struct FW_MEM_WRITE {
+ struct FW_HEADER hdr;
+ u16 address;
+ u8 data;
+} __attribute__ ((__packed__));
+
+struct FW_SFR_IRAM_READ {
+ struct FW_HEADER hdr;
+ u8 address;
+} __attribute__ ((__packed__));
+
+struct FW_SFR_IRAM_WRITE {
+ struct FW_HEADER hdr;
+ u8 address;
+ u8 data;
+} __attribute__ ((__packed__));
+
+struct FW_SET_GPIO_PIN {
+ struct FW_HEADER hdr;
+ u8 select;
+} __attribute__ ((__packed__));
+
+struct FW_SET_GPIO_INT {
+ struct FW_HEADER hdr;
+ u8 select;
+} __attribute__ ((__packed__));
+
+struct FW_SET_DEBUGMODE {
+ struct FW_HEADER hdr;
+ u8 debug_flags;
+} __attribute__ ((__packed__));
+
+struct FW_CONFIGURE_BUFFERS {
+ struct FW_HEADER hdr;
+ u8 config;
+} __attribute__ ((__packed__));
+
+enum _BUFFER_CONFIGS {
+ /* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2 (standard usage) */
+ BUFFER_CONFIG_4422 = 0,
+ /* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2 (4x TS input usage) */
+ BUFFER_CONFIG_3333 = 1,
+ /* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut (HDTV decoder usage) */
+ BUFFER_CONFIG_8022 = 2,
+ BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */
+};
+
+struct FW_CONFIGURE_FREE_BUFFERS {
+ struct FW_HEADER hdr;
+ u8 UVI1_BufferLength;
+ u8 UVI2_BufferLength;
+ u8 TVO_BufferLength;
+ u8 AUD1_BufferLength;
+ u8 AUD2_BufferLength;
+ u8 TVA_BufferLength;
+} __attribute__ ((__packed__));
+
+struct FW_CONFIGURE_UART {
+ struct FW_HEADER hdr;
+ u8 UartControl;
+} __attribute__ ((__packed__));
+
+enum _UART_CONFIG {
+ _UART_BAUDRATE_19200 = 0,
+ _UART_BAUDRATE_9600 = 1,
+ _UART_BAUDRATE_4800 = 2,
+ _UART_BAUDRATE_2400 = 3,
+ _UART_RX_ENABLE = 0x40,
+ _UART_TX_ENABLE = 0x80,
+};
+
+struct FW_WRITE_UART {
+ struct FW_HEADER hdr;
+ u8 Data[252];
+} __attribute__ ((__packed__));
+
+
+struct ngene_command {
+ u32 in_len;
+ u32 out_len;
+ union {
+ u32 raw[64];
+ u8 raw8[256];
+ struct FW_HEADER hdr;
+ struct FW_I2C_WRITE I2CWrite;
+ struct FW_I2C_CONTINUE_WRITE I2CContinueWrite;
+ struct FW_I2C_READ I2CRead;
+ struct FW_STREAM_CONTROL StreamControl;
+ struct FW_FWLOAD_PREPARE FWLoadPrepare;
+ struct FW_FWLOAD_FINISH FWLoadFinish;
+ struct FW_MEM_READ MemoryRead;
+ struct FW_MEM_WRITE MemoryWrite;
+ struct FW_SFR_IRAM_READ SfrIramRead;
+ struct FW_SFR_IRAM_WRITE SfrIramWrite;
+ struct FW_SPI_WRITE SPIWrite;
+ struct FW_SPI_READ SPIRead;
+ struct FW_SET_GPIO_PIN SetGpioPin;
+ struct FW_SET_GPIO_INT SetGpioInt;
+ struct FW_SET_DEBUGMODE SetDebugMode;
+ struct FW_CONFIGURE_BUFFERS ConfigureBuffers;
+ struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers;
+ struct FW_CONFIGURE_UART ConfigureUart;
+ struct FW_WRITE_UART WriteUart;
+ } cmd;
+} __attribute__ ((__packed__));
+
+#define NGENE_INTERFACE_VERSION 0x103
+#define MAX_VIDEO_BUFFER_SIZE (417792) /* 288*1440 rounded up to next page */
+#define MAX_AUDIO_BUFFER_SIZE (8192) /* Gives room for about 23msec@48KHz */
+#define MAX_VBI_BUFFER_SIZE (28672) /* 1144*18 rounded up to next page */
+#define MAX_TS_BUFFER_SIZE (98304) /* 512*188 rounded up to next page */
+#define MAX_HDTV_BUFFER_SIZE (2080768) /* 541*1920*2 rounded up to next page
+ Max: (1920x1080i60) */
+
+#define OVERFLOW_BUFFER_SIZE (8192)
+
+#define RING_SIZE_VIDEO 4
+#define RING_SIZE_AUDIO 8
+#define RING_SIZE_TS 8
+
+#define NUM_SCATTER_GATHER_ENTRIES 8
+
+#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \
+ RING_SIZE_VIDEO * 2) + \
+ (MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \
+ (MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \
+ (RING_SIZE_VIDEO * PAGE_SIZE * 2) + \
+ (RING_SIZE_AUDIO * PAGE_SIZE * 2) + \
+ (RING_SIZE_TS * PAGE_SIZE * 4) + \
+ 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE)
+
+#define EVENT_QUEUE_SIZE 16
+
+/* Gathers the current state of a single channel. */
+
+struct SBufferHeader {
+ struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */
+ struct SBufferHeader *Next;
+ void *Buffer1;
+ struct HW_SCATTER_GATHER_ELEMENT *scList1;
+ void *Buffer2;
+ struct HW_SCATTER_GATHER_ELEMENT *scList2;
+};
+
+/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */
+#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63)
+
+enum HWSTATE {
+ HWSTATE_STOP,
+ HWSTATE_STARTUP,
+ HWSTATE_RUN,
+ HWSTATE_PAUSE,
+};
+
+enum KSSTATE {
+ KSSTATE_STOP,
+ KSSTATE_ACQUIRE,
+ KSSTATE_PAUSE,
+ KSSTATE_RUN,
+};
+
+struct SRingBufferDescriptor {
+ struct SBufferHeader *Head; /* Points to first buffer in ring buffer
+ structure*/
+ u64 PAHead; /* Physical address of first buffer */
+ u32 MemSize; /* Memory size of allocated ring buffers
+ (needed for freeing) */
+ u32 NumBuffers; /* Number of buffers in the ring */
+ u32 Buffer1Length; /* Allocated length of Buffer 1 */
+ u32 Buffer2Length; /* Allocated length of Buffer 2 */
+ void *SCListMem; /* Memory to hold scatter gather lists for this
+ ring */
+ u64 PASCListMem; /* Physical address .. */
+ u32 SCListMemSize; /* Size of this memory */
+};
+
+enum STREAMMODEFLAGS {
+ StreamMode_NONE = 0, /* Stream not used */
+ StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */
+ StreamMode_TSIN = 2, /* Transport stream input (all) */
+ StreamMode_HDTV = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60
+ (only stream 0) */
+ StreamMode_TSOUT = 8, /* Transport stream output (only stream 3) */
+};
+
+
+enum BufferExchangeFlags {
+ BEF_EVEN_FIELD = 0x00000001,
+ BEF_CONTINUATION = 0x00000002,
+ BEF_MORE_DATA = 0x00000004,
+ BEF_OVERFLOW = 0x00000008,
+ DF_SWAP32 = 0x00010000,
+};
+
+typedef void *(IBufferExchange)(void *, void *, u32, u32, u32);
+
+struct MICI_STREAMINFO {
+ IBufferExchange *pExchange;
+ IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */
+ u8 Stream;
+ u8 Flags;
+ u8 Mode;
+ u8 Reserved;
+ u16 nLinesVideo;
+ u16 nBytesPerLineVideo;
+ u16 nLinesVBI;
+ u16 nBytesPerLineVBI;
+ u32 CaptureLength; /* Used for audio and transport stream */
+};
+
+/****************************************************************************/
+/* STRUCTS ******************************************************************/
+/****************************************************************************/
+
+/* sound hardware definition */
+#define MIXER_ADDR_TVTUNER 0
+#define MIXER_ADDR_LAST 0
+
+struct ngene_channel;
+
+/*struct sound chip*/
+
+struct mychip {
+ struct ngene_channel *chan;
+ struct snd_card *card;
+ struct pci_dev *pci;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm *pcm;
+ unsigned long port;
+ int irq;
+ spinlock_t mixer_lock;
+ spinlock_t lock;
+ int mixer_volume[MIXER_ADDR_LAST + 1][2];
+ int capture_source[MIXER_ADDR_LAST + 1][2];
+};
+
+#ifdef NGENE_V4L
+struct ngene_overlay {
+ int tvnorm;
+ struct v4l2_rect w;
+ enum v4l2_field field;
+ struct v4l2_clip *clips;
+ int nclips;
+ int setup_ok;
+};
+
+struct ngene_tvnorm {
+ int v4l2_id;
+ char *name;
+ u16 swidth, sheight; /* scaled standard width, height */
+ int tuner_norm;
+ int soundstd;
+};
+
+struct ngene_vopen {
+ struct ngene_channel *ch;
+ enum v4l2_priority prio;
+ int width;
+ int height;
+ int depth;
+ struct videobuf_queue vbuf_q;
+ struct videobuf_queue vbi;
+ int fourcc;
+ int picxcount;
+ int resources;
+ enum v4l2_buf_type type;
+ const struct ngene_format *fmt;
+
+ const struct ngene_format *ovfmt;
+ struct ngene_overlay ov;
+};
+#endif
+
+struct ngene_channel {
+ struct device device;
+ struct i2c_adapter i2c_adapter;
+
+ struct ngene *dev;
+ int number;
+ int type;
+ int mode;
+
+ struct dvb_frontend *fe;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ int users;
+ struct video_device *v4l_dev;
+ struct tasklet_struct demux_tasklet;
+
+ struct SBufferHeader *nextBuffer;
+ enum KSSTATE State;
+ enum HWSTATE HWState;
+ u8 Stream;
+ u8 Flags;
+ u8 Mode;
+ IBufferExchange *pBufferExchange;
+ IBufferExchange *pBufferExchange2;
+
+ spinlock_t state_lock;
+ u16 nLines;
+ u16 nBytesPerLine;
+ u16 nVBILines;
+ u16 nBytesPerVBILine;
+ u16 itumode;
+ u32 Capture1Length;
+ u32 Capture2Length;
+ struct SRingBufferDescriptor RingBuffer;
+ struct SRingBufferDescriptor TSRingBuffer;
+ struct SRingBufferDescriptor TSIdleBuffer;
+
+ u32 DataFormatFlags;
+
+ int AudioDTOUpdated;
+ u32 AudioDTOValue;
+
+ int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t);
+ u8 lnbh;
+
+ /* stuff from analog driver */
+
+ int minor;
+ struct mychip *mychip;
+ struct snd_card *soundcard;
+ u8 *evenbuffer;
+ u8 dma_on;
+ int soundstreamon;
+ int audiomute;
+ int soundbuffisallocated;
+ int sndbuffflag;
+ int tun_rdy;
+ int dec_rdy;
+ int tun_dec_rdy;
+ int lastbufferflag;
+
+ struct ngene_tvnorm *tvnorms;
+ int tvnorm_num;
+ int tvnorm;
+
+#ifdef NGENE_V4L
+ int videousers;
+ struct v4l2_prio_state prio;
+ struct ngene_vopen init;
+ int resources;
+ struct v4l2_framebuffer fbuf;
+ struct ngene_buffer *screen; /* overlay */
+ struct list_head capture; /* video capture queue */
+ spinlock_t s_lock;
+ struct semaphore reslock;
+#endif
+
+ int running;
+};
+
+struct ngene;
+
+typedef void (rx_cb_t)(struct ngene *, u32, u8);
+typedef void (tx_cb_t)(struct ngene *, u32);
+
+struct ngene {
+ int nr;
+ struct pci_dev *pci_dev;
+ unsigned char *iomem;
+
+ /*struct i2c_adapter i2c_adapter;*/
+
+ u32 device_version;
+ u32 fw_interface_version;
+ u32 icounts;
+
+ u8 *CmdDoneByte;
+ int BootFirmware;
+ void *OverflowBuffer;
+ dma_addr_t PAOverflowBuffer;
+ void *FWInterfaceBuffer;
+ dma_addr_t PAFWInterfaceBuffer;
+ u8 *ngenetohost;
+ u8 *hosttongene;
+
+ struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE];
+ int EventQueueOverflowCount;
+ int EventQueueOverflowFlag;
+ struct tasklet_struct event_tasklet;
+ struct EVENT_BUFFER *EventBuffer;
+ int EventQueueWriteIndex;
+ int EventQueueReadIndex;
+
+ wait_queue_head_t cmd_wq;
+ int cmd_done;
+ struct semaphore cmd_mutex;
+ struct semaphore stream_mutex;
+ struct semaphore pll_mutex;
+ struct semaphore i2c_switch_mutex;
+ int i2c_current_channel;
+ int i2c_current_bus;
+ spinlock_t cmd_lock;
+
+ struct dvb_adapter adapter[MAX_STREAM];
+ struct ngene_channel channel[MAX_STREAM];
+
+ struct ngene_info *card_info;
+
+ tx_cb_t *TxEventNotify;
+ rx_cb_t *RxEventNotify;
+ int tx_busy;
+ wait_queue_head_t tx_wq;
+ wait_queue_head_t rx_wq;
+#define UART_RBUF_LEN 4096
+ u8 uart_rbuf[UART_RBUF_LEN];
+ int uart_rp, uart_wp;
+
+ u8 *tsout_buf;
+#define TSOUT_BUF_SIZE (512*188*8)
+ struct dvb_ringbuffer tsout_rbuf;
+
+ u8 *ain_buf;
+#define AIN_BUF_SIZE (128*1024)
+ struct dvb_ringbuffer ain_rbuf;
+
+
+ u8 *vin_buf;
+#define VIN_BUF_SIZE (4*1920*1080)
+ struct dvb_ringbuffer vin_rbuf;
+
+ unsigned long exp_val;
+ int prev_cmd;
+};
+
+struct ngene_info {
+ int type;
+#define NGENE_APP 0
+#define NGENE_TERRATEC 1
+#define NGENE_SIDEWINDER 2
+#define NGENE_RACER 3
+#define NGENE_VIPER 4
+#define NGENE_PYTHON 5
+#define NGENE_VBOX_V1 6
+#define NGENE_VBOX_V2 7
+
+ int fw_version;
+ char *name;
+
+ int io_type[MAX_STREAM];
+#define NGENE_IO_NONE 0
+#define NGENE_IO_TV 1
+#define NGENE_IO_HDTV 2
+#define NGENE_IO_TSIN 4
+#define NGENE_IO_TSOUT 8
+#define NGENE_IO_AIN 16
+
+ void *fe_config[4];
+ void *tuner_config[4];
+
+ int (*demod_attach[4])(struct ngene_channel *);
+ int (*tuner_attach[4])(struct ngene_channel *);
+
+ u8 avf[4];
+ u8 msp[4];
+ u8 demoda[4];
+ u8 lnb[4];
+ int i2c_access;
+ u8 ntsc;
+ u8 tsf[4];
+ u8 i2s[4];
+
+ int (*gate_ctrl)(struct dvb_frontend *, int);
+ int (*switch_ctrl)(struct ngene_channel *, int, int);
+};
+
+#ifdef NGENE_V4L
+struct ngene_format{
+ char *name;
+ int fourcc; /* video4linux 2 */
+ int btformat; /* BT848_COLOR_FMT_* */
+ int format;
+ int btswap; /* BT848_COLOR_CTL_* */
+ int depth; /* bit/pixel */
+ int flags;
+ int hshift, vshift; /* for planar modes */
+ int palette;
+};
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+struct ngene_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* ngene specific */
+ const struct ngene_format *fmt;
+ int tvnorm;
+ int btformat;
+ int btswap;
+};
+#endif
+
+
+#endif
+
+/* LocalWords: Endif
+ */
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
index 1067b22eb0c..cff77e2eb55 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/dvb/siano/sms-cards.c
@@ -62,6 +62,7 @@ static struct sms_board sms_boards[] = {
[SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = {
.name = "Hauppauge WinTV MiniStick",
.type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw",
.fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
.board_cfg.leds_power = 26,
.board_cfg.led0 = 27,
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index ca758bcb48c..4bfd3451b56 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -1459,8 +1459,10 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum,
if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) {
pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ;
if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum,
- &groupCfg) != 0)
- return -EINVAL;
+ &groupCfg) != 0) {
+ rc = -EINVAL;
+ goto free;
+ }
pMsg->msgData[1] = TranslatedPinNum;
pMsg->msgData[2] = GroupNum;
@@ -1490,6 +1492,7 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum,
else
sms_err("smscore_gpio_configure error");
}
+free:
kfree(buffer);
return rc;
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
index eec18aaf551..8ecadecaa9d 100644
--- a/drivers/media/dvb/siano/smscoreapi.h
+++ b/drivers/media/dvb/siano/smscoreapi.h
@@ -212,6 +212,8 @@ struct smscore_device_t {
#define MSG_SMS_DAB_CHANNEL 607
#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608
#define MSG_SMS_GET_PID_FILTER_LIST_RES 609
+#define MSG_SMS_GET_STATISTICS_RES 616
+#define MSG_SMS_GET_STATISTICS_REQ 615
#define MSG_SMS_HO_PER_SLICES_IND 630
#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651
#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652
@@ -339,7 +341,7 @@ struct SmsFirmware_ST {
/* Statistics information returned as response for
* SmsHostApiGetStatistics_Req */
-struct SMSHOSTLIB_STATISTICS_S {
+struct SMSHOSTLIB_STATISTICS_ST {
u32 Reserved; /* Reserved */
/* Common parameters */
@@ -424,6 +426,79 @@ struct SMSHOSTLIB_STATISTICS_S {
u32 ReservedFields[10]; /* Reserved */
};
+struct SmsMsgStatisticsInfo_ST {
+ u32 RequestResult;
+
+ struct SMSHOSTLIB_STATISTICS_ST Stat;
+
+ /* Split the calc of the SNR in DAB */
+ u32 Signal; /* dB */
+ u32 Noise; /* dB */
+
+};
+
+struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST {
+ /* Per-layer information */
+ u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET,
+ * 255 means layer does not exist */
+ u32 Constellation; /* Constellation from SMSHOSTLIB_CONSTELLATION_ET,
+ * 255 means layer does not exist */
+ u32 BER; /* Post Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */
+ u32 BERErrorCount; /* Post Viterbi Error Bits Count */
+ u32 BERBitCount; /* Post Viterbi Total Bits Count */
+ u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */
+ u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */
+ u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */
+ u32 TotalTSPackets; /* Total number of transport-stream packets */
+ u32 TILdepthI; /* Time interleaver depth I parameter,
+ * 255 means layer does not exist */
+ u32 NumberOfSegments; /* Number of segments in layer A,
+ * 255 means layer does not exist */
+ u32 TMCCErrors; /* TMCC errors */
+};
+
+struct SMSHOSTLIB_STATISTICS_ISDBT_ST {
+ u32 StatisticsType; /* Enumerator identifying the type of the
+ * structure. Values are the same as
+ * SMSHOSTLIB_DEVICE_MODES_E
+ *
+ * This field MUST always be first in any
+ * statistics structure */
+
+ u32 FullSize; /* Total size of the structure returned by the modem.
+ * If the size requested by the host is smaller than
+ * FullSize, the struct will be truncated */
+
+ /* Common parameters */
+ u32 IsRfLocked; /* 0 - not locked, 1 - locked */
+ u32 IsDemodLocked; /* 0 - not locked, 1 - locked */
+ u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ s32 RSSI; /* dBm */
+ s32 InBandPwr; /* In band power in dBM */
+ s32 CarrierOffset; /* Carrier Offset in Hz */
+
+ /* Transmission parameters */
+ u32 Frequency; /* Frequency in Hz */
+ u32 Bandwidth; /* Bandwidth in MHz */
+ u32 TransmissionMode; /* ISDB-T transmission mode */
+ u32 ModemState; /* 0 - Acquisition, 1 - Locked */
+ u32 GuardInterval; /* Guard Interval, 1 divided by value */
+ u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */
+ u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */
+ u32 NumOfLayers; /* Number of ISDB-T layers in the network */
+
+ /* Per-layer information */
+ /* Layers A, B and C */
+ struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3];
+ /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */
+
+ /* Interface information */
+ u32 SmsToHostTxErrors; /* Total number of transmission errors. */
+};
+
struct PID_STATISTICS_DATA_S {
struct PID_BURST_S {
u32 size;
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
index 68bf9fbd8fe..5f3939821ca 100644
--- a/drivers/media/dvb/siano/smsdvb.c
+++ b/drivers/media/dvb/siano/smsdvb.c
@@ -116,6 +116,118 @@ static void sms_board_dvb3_event(struct smsdvb_client_t *client,
}
}
+
+static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData,
+ struct SMSHOSTLIB_STATISTICS_ST *p)
+{
+ if (sms_dbg & 2) {
+ printk(KERN_DEBUG "Reserved = %d", p->Reserved);
+ printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked);
+ printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked);
+ printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn);
+ printk(KERN_DEBUG "SNR = %d", p->SNR);
+ printk(KERN_DEBUG "BER = %d", p->BER);
+ printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC);
+ printk(KERN_DEBUG "TS_PER = %d", p->TS_PER);
+ printk(KERN_DEBUG "MFER = %d", p->MFER);
+ printk(KERN_DEBUG "RSSI = %d", p->RSSI);
+ printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr);
+ printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset);
+ printk(KERN_DEBUG "Frequency = %d", p->Frequency);
+ printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth);
+ printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode);
+ printk(KERN_DEBUG "ModemState = %d", p->ModemState);
+ printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval);
+ printk(KERN_DEBUG "CodeRate = %d", p->CodeRate);
+ printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate);
+ printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy);
+ printk(KERN_DEBUG "Constellation = %d", p->Constellation);
+ printk(KERN_DEBUG "BurstSize = %d", p->BurstSize);
+ printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration);
+ printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime);
+ printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime);
+ printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows);
+ printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols);
+ printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols);
+ printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets);
+ printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets);
+ printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs);
+ printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs);
+ printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs);
+ printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount);
+ printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount);
+ printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors);
+ printk(KERN_DEBUG "PreBER = %d", p->PreBER);
+ printk(KERN_DEBUG "CellId = %d", p->CellId);
+ printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP);
+ printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP);
+ printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived);
+ }
+
+ pReceptionData->IsDemodLocked = p->IsDemodLocked;
+
+ pReceptionData->SNR = p->SNR;
+ pReceptionData->BER = p->BER;
+ pReceptionData->BERErrorCount = p->BERErrorCount;
+ pReceptionData->InBandPwr = p->InBandPwr;
+ pReceptionData->ErrorTSPackets = p->ErrorTSPackets;
+};
+
+
+static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData,
+ struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p)
+{
+ int i;
+
+ if (sms_dbg & 2) {
+ printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked);
+ printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked);
+ printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn);
+ printk(KERN_DEBUG "SNR = %d", p->SNR);
+ printk(KERN_DEBUG "RSSI = %d", p->RSSI);
+ printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr);
+ printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset);
+ printk(KERN_DEBUG "Frequency = %d", p->Frequency);
+ printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth);
+ printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode);
+ printk(KERN_DEBUG "ModemState = %d", p->ModemState);
+ printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval);
+ printk(KERN_DEBUG "SystemType = %d", p->SystemType);
+ printk(KERN_DEBUG "PartialReception = %d", p->PartialReception);
+ printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers);
+ printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors);
+
+ for (i = 0; i < 3; i++) {
+ printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate);
+ printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation);
+ printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER);
+ printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount);
+ printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount);
+ printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER);
+ printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER);
+ printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets);
+ printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets);
+ printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI);
+ printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments);
+ printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors);
+ }
+ }
+
+ pReceptionData->IsDemodLocked = p->IsDemodLocked;
+
+ pReceptionData->SNR = p->SNR;
+ pReceptionData->InBandPwr = p->InBandPwr;
+
+ pReceptionData->ErrorTSPackets = 0;
+ pReceptionData->BER = 0;
+ pReceptionData->BERErrorCount = 0;
+ for (i = 0; i < 3; i++) {
+ pReceptionData->BER += p->LayerInfo[i].BER;
+ pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount;
+ pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets;
+ }
+}
+
static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
{
struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
@@ -134,6 +246,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
break;
case MSG_SMS_RF_TUNE_RES:
+ case MSG_SMS_ISDBT_TUNE_RES:
complete(&client->tune_done);
break;
@@ -217,6 +330,40 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
is_status_update = true;
break;
}
+ case MSG_SMS_GET_STATISTICS_RES: {
+ union {
+ struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt;
+ struct SmsMsgStatisticsInfo_ST dvb;
+ } *p = (void *) (phdr + 1);
+ struct RECEPTION_STATISTICS_S *pReceptionData =
+ &client->sms_stat_dvb.ReceptionData;
+
+ sms_info("MSG_SMS_GET_STATISTICS_RES");
+
+ is_status_update = true;
+
+ switch (smscore_get_device_mode(client->coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt);
+ break;
+ default:
+ smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat);
+ }
+ if (!pReceptionData->IsDemodLocked) {
+ pReceptionData->SNR = 0;
+ pReceptionData->BER = 0;
+ pReceptionData->BERErrorCount = 0;
+ pReceptionData->InBandPwr = 0;
+ pReceptionData->ErrorTSPackets = 0;
+ }
+
+ complete(&client->tune_done);
+ break;
+ }
+ default:
+ sms_info("Unhandled message %d", phdr->msgType);
+
}
smscore_putbuffer(client->coredev, cb);
@@ -233,10 +380,10 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
DVB3_EVENT_UNC_ERR);
} else {
- /*client->fe_status =
- (phdr->msgType == MSG_SMS_NO_SIGNAL_IND) ?
- 0 : FE_HAS_SIGNAL;*/
- client->fe_status = 0;
+ if (client->sms_stat_dvb.ReceptionData.IsRfLocked)
+ client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ else
+ client->fe_status = 0;
sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK);
}
}
@@ -325,6 +472,20 @@ static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
0 : -ETIME;
}
+static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
+{
+ int rc;
+ struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ,
+ DVBT_BDA_CONTROL_MSG_ID,
+ HIF_TASK,
+ sizeof(struct SmsMsgHdr_ST), 0 };
+
+ rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->tune_done);
+
+ return rc;
+}
+
static inline int led_feedback(struct smsdvb_client_t *client)
{
if (client->fe_status & FE_HAS_LOCK)
@@ -337,33 +498,43 @@ static inline int led_feedback(struct smsdvb_client_t *client)
static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
{
+ int rc;
struct smsdvb_client_t *client;
client = container_of(fe, struct smsdvb_client_t, frontend);
+ rc = smsdvb_send_statistics_request(client);
+
*stat = client->fe_status;
led_feedback(client);
- return 0;
+ return rc;
}
static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
{
+ int rc;
struct smsdvb_client_t *client;
client = container_of(fe, struct smsdvb_client_t, frontend);
+ rc = smsdvb_send_statistics_request(client);
+
*ber = client->sms_stat_dvb.ReceptionData.BER;
led_feedback(client);
- return 0;
+ return rc;
}
static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
+ int rc;
+
struct smsdvb_client_t *client;
client = container_of(fe, struct smsdvb_client_t, frontend);
+ rc = smsdvb_send_statistics_request(client);
+
if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95)
*strength = 0;
else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29)
@@ -375,31 +546,37 @@ static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
led_feedback(client);
- return 0;
+ return rc;
}
static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
{
+ int rc;
struct smsdvb_client_t *client;
client = container_of(fe, struct smsdvb_client_t, frontend);
+ rc = smsdvb_send_statistics_request(client);
+
*snr = client->sms_stat_dvb.ReceptionData.SNR;
led_feedback(client);
- return 0;
+ return rc;
}
static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
+ int rc;
struct smsdvb_client_t *client;
client = container_of(fe, struct smsdvb_client_t, frontend);
+ rc = smsdvb_send_statistics_request(client);
+
*ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets;
led_feedback(client);
- return 0;
+ return rc;
}
static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
@@ -413,9 +590,10 @@ static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
return 0;
}
-static int smsdvb_set_frontend(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *fep)
+static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct smsdvb_client_t *client =
container_of(fe, struct smsdvb_client_t, frontend);
@@ -429,24 +607,33 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
client->fe_status = FE_HAS_SIGNAL;
client->event_fe_state = -1;
client->event_unc_state = -1;
+ fe->dtv_property_cache.delivery_system = SYS_DVBT;
Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
Msg.Msg.msgDstId = HIF_TASK;
Msg.Msg.msgFlags = 0;
Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ;
Msg.Msg.msgLength = sizeof(Msg);
- Msg.Data[0] = fep->frequency;
+ Msg.Data[0] = c->frequency;
Msg.Data[2] = 12000000;
- sms_debug("freq %d band %d",
- fep->frequency, fep->u.ofdm.bandwidth);
+ sms_info("%s: freq %d band %d", __func__, c->frequency,
+ c->bandwidth_hz);
- switch (fep->u.ofdm.bandwidth) {
- case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break;
- case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break;
- case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break;
- case BANDWIDTH_AUTO: return -EOPNOTSUPP;
- default: return -EINVAL;
+ switch (c->bandwidth_hz / 1000000) {
+ case 8:
+ Msg.Data[1] = BW_8_MHZ;
+ break;
+ case 7:
+ Msg.Data[1] = BW_7_MHZ;
+ break;
+ case 6:
+ Msg.Data[1] = BW_6_MHZ;
+ break;
+ case 0:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
}
/* Disable LNA, if any. An error is returned if no LNA is present */
ret = sms_board_lna_control(client->coredev, 0);
@@ -470,6 +657,90 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
&client->tune_done);
}
+static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ struct {
+ struct SmsMsgHdr_ST Msg;
+ u32 Data[4];
+ } Msg;
+
+ fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+
+ Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+ Msg.Msg.msgDstId = HIF_TASK;
+ Msg.Msg.msgFlags = 0;
+ Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ;
+ Msg.Msg.msgLength = sizeof(Msg);
+
+ if (c->isdbt_sb_segment_idx == -1)
+ c->isdbt_sb_segment_idx = 0;
+
+ switch (c->isdbt_sb_segment_count) {
+ case 3:
+ Msg.Data[1] = BW_ISDBT_3SEG;
+ break;
+ case 1:
+ Msg.Data[1] = BW_ISDBT_1SEG;
+ break;
+ case 0: /* AUTO */
+ switch (c->bandwidth_hz / 1000000) {
+ case 8:
+ case 7:
+ c->isdbt_sb_segment_count = 3;
+ Msg.Data[1] = BW_ISDBT_3SEG;
+ break;
+ case 6:
+ c->isdbt_sb_segment_count = 1;
+ Msg.Data[1] = BW_ISDBT_1SEG;
+ break;
+ default: /* Assumes 6 MHZ bw */
+ c->isdbt_sb_segment_count = 1;
+ c->bandwidth_hz = 6000;
+ Msg.Data[1] = BW_ISDBT_1SEG;
+ break;
+ }
+ break;
+ default:
+ sms_info("Segment count %d not supported", c->isdbt_sb_segment_count);
+ return -EINVAL;
+ }
+
+ Msg.Data[0] = c->frequency;
+ Msg.Data[2] = 12000000;
+ Msg.Data[3] = c->isdbt_sb_segment_idx;
+
+ sms_info("%s: freq %d segwidth %d segindex %d\n", __func__,
+ c->frequency, c->isdbt_sb_segment_count,
+ c->isdbt_sb_segment_idx);
+
+ return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->tune_done);
+}
+
+static int smsdvb_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ struct smscore_device_t *coredev = client->coredev;
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_DVBT:
+ case DEVICE_MODE_DVBT_BDA:
+ return smsdvb_dvbt_set_frontend(fe, fep);
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ return smsdvb_isdbt_set_frontend(fe, fep);
+ default:
+ return -EINVAL;
+ }
+}
+
static int smsdvb_get_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fep)
{
@@ -557,13 +828,6 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev,
/* device removal handled by onremove callback */
if (!arrival)
return 0;
-
- if (smscore_get_device_mode(coredev) != DEVICE_MODE_DVBT_BDA) {
- sms_err("SMS Device mode is not set for "
- "DVB operation.");
- return 0;
- }
-
client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
if (!client) {
sms_err("kmalloc() failed");
diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c
index e3d776feeac..a56eac76e0f 100644
--- a/drivers/media/dvb/siano/smsir.c
+++ b/drivers/media/dvb/siano/smsir.c
@@ -85,9 +85,9 @@ static struct keyboard_layout_map_t keyboard_layout_maps[] = {
{ } /* Terminating entry */
};
-u32 ir_pos;
-u32 ir_word;
-u32 ir_toggle;
+static u32 ir_pos;
+static u32 ir_word;
+static u32 ir_toggle;
#define RC5_PUSH_BIT(dst, bit, pos) \
{ dst <<= 1; dst |= bit; pos++; }
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c
index 23a1c6380d3..b070e88d8c6 100644
--- a/drivers/media/dvb/ttpci/av7110_ir.c
+++ b/drivers/media/dvb/ttpci/av7110_ir.c
@@ -268,8 +268,8 @@ int av7110_check_ir_config(struct av7110 *av7110, int force)
/* /proc/av7110_ir interface */
-static int av7110_ir_write_proc(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos)
{
char *page;
u32 ir_config;
@@ -309,6 +309,10 @@ static int av7110_ir_write_proc(struct file *file, const char __user *buffer,
return count;
}
+static const struct file_operations av7110_ir_proc_fops = {
+ .owner = THIS_MODULE,
+ .write = av7110_ir_proc_write,
+};
/* interrupt handler */
static void ir_handler(struct av7110 *av7110, u32 ircom)
@@ -368,11 +372,9 @@ int __devinit av7110_ir_init(struct av7110 *av7110)
input_dev->timer.data = (unsigned long) &av7110->ir;
if (av_cnt == 1) {
- e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
- if (e) {
- e->write_proc = av7110_ir_write_proc;
+ e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops);
+ if (e)
e->size = 4 + 256 * sizeof(u16);
- }
}
tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir);
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 9782e059373..49c2a817a06 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -254,7 +254,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
budget_ci->ir.timer_keyup.function = msp430_ir_keyup;
budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir;
budget_ci->ir.last_raw = 0xffff; /* An impossible value */
- error = ir_input_register(input_dev, ir_codes);
+ error = ir_input_register(input_dev, ir_codes, NULL);
if (error) {
printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
return error;
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
index e48380c4899..9fdf26cc699 100644
--- a/drivers/media/dvb/ttpci/budget.c
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -433,9 +433,8 @@ static struct stv090x_config tt1600_stv090x_config = {
.demod_mode = STV090x_SINGLE,
.clk_mode = STV090x_CLK_EXT,
- .xtal = 27000000,
+ .xtal = 13500000,
.address = 0x68,
- .ref_clk = 27000000,
.ts1_mode = STV090x_TSMODE_DVBCI,
.ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
@@ -457,6 +456,7 @@ static struct stv090x_config tt1600_stv090x_config = {
static struct stv6110x_config tt1600_stv6110x_config = {
.addr = 0x60,
.refclk = 27000000,
+ .clk_div = 2,
};
static struct isl6423_config tt1600_isl6423_config = {
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 3f40f375981..83567b898d0 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -417,6 +417,18 @@ 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_SAA7706H
+ tristate "SAA7706H Car Radio DSP"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to use the SAA7706H Car radio Digital
+ Signal Processor, 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 SAA7706H.
+
config RADIO_TEF6862
tristate "TEF6862 Car Radio Enhanced Selectivity Tuner"
depends on I2C && VIDEO_V4L2
@@ -429,4 +441,15 @@ config RADIO_TEF6862
To compile this driver as a module, choose M here: the
module will be called TEF6862.
+config RADIO_TIMBERDALE
+ tristate "Enable the Timberdale radio driver"
+ depends on MFD_TIMBERDALE && VIDEO_V4L2
+ depends on I2C # for RADIO_SAA7706H
+ select RADIO_TEF6862
+ select RADIO_SAA7706H
+ ---help---
+ This is a kind of umbrella driver for the Radio Tuner and DSP
+ found behind the Timberdale FPGA on the Russellville board.
+ Enabling this driver will automatically select the DSP and tuner.
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 01922ada691..f615583b483 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -23,6 +23,8 @@ 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_SAA7706H) += saa7706h.o
obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
+obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
new file mode 100644
index 00000000000..0de457f6e6e
--- /dev/null
+++ b/drivers/media/radio/radio-timb.c
@@ -0,0 +1,244 @@
+/*
+ * radio-timb.c Timberdale FPGA Radio driver
+ * 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/version.h>
+#include <linux/io.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <media/timb_radio.h>
+
+#define DRIVER_NAME "timb-radio"
+
+struct timbradio {
+ struct timb_radio_platform_data pdata;
+ struct v4l2_subdev *sd_tuner;
+ struct v4l2_subdev *sd_dsp;
+ struct video_device video_dev;
+ struct v4l2_device v4l2_dev;
+};
+
+
+static int timbradio_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver));
+ strlcpy(v->card, "Timberdale Radio", sizeof(v->card));
+ snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME);
+ v->version = KERNEL_VERSION(0, 0, 1);
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+}
+
+static int timbradio_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_tuner, tuner, g_tuner, v);
+}
+
+static int timbradio_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v);
+}
+
+static int timbradio_vidioc_g_input(struct file *filp, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int timbradio_vidioc_s_input(struct file *filp, void *priv,
+ unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+static int timbradio_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 timbradio_vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return a->index ? -EINVAL : 0;
+}
+
+static int timbradio_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_tuner, tuner, s_frequency, f);
+}
+
+static int timbradio_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_tuner, tuner, g_frequency, f);
+}
+
+static int timbradio_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_dsp, core, queryctrl, qc);
+}
+
+static int timbradio_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_dsp, core, g_ctrl, ctrl);
+}
+
+static int timbradio_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct timbradio *tr = video_drvdata(file);
+ return v4l2_subdev_call(tr->sd_dsp, core, s_ctrl, ctrl);
+}
+
+static const struct v4l2_ioctl_ops timbradio_ioctl_ops = {
+ .vidioc_querycap = timbradio_vidioc_querycap,
+ .vidioc_g_tuner = timbradio_vidioc_g_tuner,
+ .vidioc_s_tuner = timbradio_vidioc_s_tuner,
+ .vidioc_g_frequency = timbradio_vidioc_g_frequency,
+ .vidioc_s_frequency = timbradio_vidioc_s_frequency,
+ .vidioc_g_input = timbradio_vidioc_g_input,
+ .vidioc_s_input = timbradio_vidioc_s_input,
+ .vidioc_g_audio = timbradio_vidioc_g_audio,
+ .vidioc_s_audio = timbradio_vidioc_s_audio,
+ .vidioc_queryctrl = timbradio_vidioc_queryctrl,
+ .vidioc_g_ctrl = timbradio_vidioc_g_ctrl,
+ .vidioc_s_ctrl = timbradio_vidioc_s_ctrl
+};
+
+static const struct v4l2_file_operations timbradio_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = video_ioctl2,
+};
+
+static int __devinit timbradio_probe(struct platform_device *pdev)
+{
+ struct timb_radio_platform_data *pdata = pdev->dev.platform_data;
+ struct timbradio *tr;
+ int err;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data missing\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ tr = kzalloc(sizeof(*tr), GFP_KERNEL);
+ if (!tr) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ tr->pdata = *pdata;
+
+ strlcpy(tr->video_dev.name, "Timberdale Radio",
+ sizeof(tr->video_dev.name));
+ tr->video_dev.fops = &timbradio_fops;
+ tr->video_dev.ioctl_ops = &timbradio_ioctl_ops;
+ tr->video_dev.release = video_device_release_empty;
+ tr->video_dev.minor = -1;
+
+ strlcpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name));
+ err = v4l2_device_register(NULL, &tr->v4l2_dev);
+ if (err)
+ goto err_v4l2_dev;
+
+ tr->video_dev.v4l2_dev = &tr->v4l2_dev;
+
+ err = video_register_device(&tr->video_dev, VFL_TYPE_RADIO, -1);
+ if (err) {
+ dev_err(&pdev->dev, "Error reg video\n");
+ goto err_video_req;
+ }
+
+ video_set_drvdata(&tr->video_dev, tr);
+
+ platform_set_drvdata(pdev, tr);
+ return 0;
+
+err_video_req:
+ video_device_release_empty(&tr->video_dev);
+ v4l2_device_unregister(&tr->v4l2_dev);
+err_v4l2_dev:
+ kfree(tr);
+err:
+ dev_err(&pdev->dev, "Failed to register: %d\n", err);
+
+ return err;
+}
+
+static int __devexit timbradio_remove(struct platform_device *pdev)
+{
+ struct timbradio *tr = platform_get_drvdata(pdev);
+
+ video_unregister_device(&tr->video_dev);
+ video_device_release_empty(&tr->video_dev);
+
+ v4l2_device_unregister(&tr->v4l2_dev);
+
+ kfree(tr);
+
+ return 0;
+}
+
+static struct platform_driver timbradio_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = timbradio_probe,
+ .remove = timbradio_remove,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init timbradio_init(void)
+{
+ return platform_driver_register(&timbradio_platform_driver);
+}
+
+static void __exit timbradio_exit(void)
+{
+ platform_driver_unregister(&timbradio_platform_driver);
+}
+
+module_init(timbradio_init);
+module_exit(timbradio_exit);
+
+MODULE_DESCRIPTION("Timberdale Radio driver");
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
new file mode 100644
index 00000000000..5db5528a8b2
--- /dev/null
+++ b/drivers/media/radio/saa7706h.c
@@ -0,0 +1,451 @@
+/*
+ * saa7706.c Philips SAA7706H Car Radio DSP driver
+ * 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/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#define DRIVER_NAME "saa7706h"
+
+/* the I2C memory map looks like this
+
+ $1C00 - $FFFF Not Used
+ $2200 - $3FFF Reserved YRAM (DSP2) space
+ $2000 - $21FF YRAM (DSP2)
+ $1FF0 - $1FFF Hardware Registers
+ $1280 - $1FEF Reserved XRAM (DSP2) space
+ $1000 - $127F XRAM (DSP2)
+ $0FFF DSP CONTROL
+ $0A00 - $0FFE Reserved
+ $0980 - $09FF Reserved YRAM (DSP1) space
+ $0800 - $097F YRAM (DSP1)
+ $0200 - $07FF Not Used
+ $0180 - $01FF Reserved XRAM (DSP1) space
+ $0000 - $017F XRAM (DSP1)
+*/
+
+#define SAA7706H_REG_CTRL 0x0fff
+#define SAA7706H_CTRL_BYP_PLL 0x0001
+#define SAA7706H_CTRL_PLL_DIV_MASK 0x003e
+#define SAA7706H_CTRL_PLL3_62975MHZ 0x003e
+#define SAA7706H_CTRL_DSP_TURBO 0x0040
+#define SAA7706H_CTRL_PC_RESET_DSP1 0x0080
+#define SAA7706H_CTRL_PC_RESET_DSP2 0x0100
+#define SAA7706H_CTRL_DSP1_ROM_EN_MASK 0x0600
+#define SAA7706H_CTRL_DSP1_FUNC_PROM 0x0000
+#define SAA7706H_CTRL_DSP2_ROM_EN_MASK 0x1800
+#define SAA7706H_CTRL_DSP2_FUNC_PROM 0x0000
+#define SAA7706H_CTRL_DIG_SIL_INTERPOL 0x8000
+
+#define SAA7706H_REG_EVALUATION 0x1ff0
+#define SAA7706H_EVAL_DISABLE_CHARGE_PUMP 0x000001
+#define SAA7706H_EVAL_DCS_CLOCK 0x000002
+#define SAA7706H_EVAL_GNDRC1_ENABLE 0x000004
+#define SAA7706H_EVAL_GNDRC2_ENABLE 0x000008
+
+#define SAA7706H_REG_CL_GEN1 0x1ff3
+#define SAA7706H_CL_GEN1_MIN_LOOPGAIN_MASK 0x00000f
+#define SAA7706H_CL_GEN1_LOOPGAIN_MASK 0x0000f0
+#define SAA7706H_CL_GEN1_COARSE_RATION 0xffff00
+
+#define SAA7706H_REG_CL_GEN2 0x1ff4
+#define SAA7706H_CL_GEN2_WSEDGE_FALLING 0x000001
+#define SAA7706H_CL_GEN2_STOP_VCO 0x000002
+#define SAA7706H_CL_GEN2_FRERUN 0x000004
+#define SAA7706H_CL_GEN2_ADAPTIVE 0x000008
+#define SAA7706H_CL_GEN2_FINE_RATIO_MASK 0x0ffff0
+
+#define SAA7706H_REG_CL_GEN4 0x1ff6
+#define SAA7706H_CL_GEN4_BYPASS_PLL1 0x001000
+#define SAA7706H_CL_GEN4_PLL1_DIV_MASK 0x03e000
+#define SAA7706H_CL_GEN4_DSP1_TURBO 0x040000
+
+#define SAA7706H_REG_SEL 0x1ff7
+#define SAA7706H_SEL_DSP2_SRCA_MASK 0x000007
+#define SAA7706H_SEL_DSP2_FMTA_MASK 0x000031
+#define SAA7706H_SEL_DSP2_SRCB_MASK 0x0001c0
+#define SAA7706H_SEL_DSP2_FMTB_MASK 0x000e00
+#define SAA7706H_SEL_DSP1_SRC_MASK 0x003000
+#define SAA7706H_SEL_DSP1_FMT_MASK 0x01c003
+#define SAA7706H_SEL_SPDIF2 0x020000
+#define SAA7706H_SEL_HOST_IO_FMT_MASK 0x1c0000
+#define SAA7706H_SEL_EN_HOST_IO 0x200000
+
+#define SAA7706H_REG_IAC 0x1ff8
+#define SAA7706H_REG_CLK_SET 0x1ff9
+#define SAA7706H_REG_CLK_COEFF 0x1ffa
+#define SAA7706H_REG_INPUT_SENS 0x1ffb
+#define SAA7706H_INPUT_SENS_RDS_VOL_MASK 0x0003f
+#define SAA7706H_INPUT_SENS_FM_VOL_MASK 0x00fc0
+#define SAA7706H_INPUT_SENS_FM_MPX 0x01000
+#define SAA7706H_INPUT_SENS_OFF_FILTER_A_EN 0x02000
+#define SAA7706H_INPUT_SENS_OFF_FILTER_B_EN 0x04000
+#define SAA7706H_REG_PHONE_NAV_AUDIO 0x1ffc
+#define SAA7706H_REG_IO_CONF_DSP2 0x1ffd
+#define SAA7706H_REG_STATUS_DSP2 0x1ffe
+#define SAA7706H_REG_PC_DSP2 0x1fff
+
+#define SAA7706H_DSP1_MOD0 0x0800
+#define SAA7706H_DSP1_ROM_VER 0x097f
+#define SAA7706H_DSP2_MPTR0 0x1000
+
+#define SAA7706H_DSP1_MODPNTR 0x0000
+
+#define SAA7706H_DSP2_XMEM_CONTLLCW 0x113e
+#define SAA7706H_DSP2_XMEM_BUSAMP 0x114a
+#define SAA7706H_DSP2_XMEM_FDACPNTR 0x11f9
+#define SAA7706H_DSP2_XMEM_IIS1PNTR 0x11fb
+
+#define SAA7706H_DSP2_YMEM_PVGA 0x212a
+#define SAA7706H_DSP2_YMEM_PVAT1 0x212b
+#define SAA7706H_DSP2_YMEM_PVAT 0x212c
+#define SAA7706H_DSP2_YMEM_ROM_VER 0x21ff
+
+#define SUPPORTED_DSP1_ROM_VER 0x667
+
+struct saa7706h_state {
+ struct v4l2_subdev sd;
+ unsigned muted;
+};
+
+static inline struct saa7706h_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7706h_state, sd);
+}
+
+static int saa7706h_i2c_send(struct i2c_client *client, const u8 *data, int len)
+{
+ int err = i2c_master_send(client, data, len);
+ if (err == len)
+ return 0;
+ return err > 0 ? -EIO : err;
+}
+
+static int saa7706h_i2c_transfer(struct i2c_client *client,
+ struct i2c_msg *msgs, int num)
+{
+ int err = i2c_transfer(client->adapter, msgs, num);
+ if (err == num)
+ return 0;
+ return err > 0 ? -EIO : err;
+}
+
+static int saa7706h_set_reg24(struct v4l2_subdev *sd, u16 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[5];
+ int pos = 0;
+
+ buf[pos++] = reg >> 8;
+ buf[pos++] = reg;
+ buf[pos++] = val >> 16;
+ buf[pos++] = val >> 8;
+ buf[pos++] = val;
+
+ return saa7706h_i2c_send(client, buf, pos);
+}
+
+static int saa7706h_set_reg24_err(struct v4l2_subdev *sd, u16 reg, u32 val,
+ int *err)
+{
+ return *err ? *err : saa7706h_set_reg24(sd, reg, val);
+}
+
+static int saa7706h_set_reg16(struct v4l2_subdev *sd, u16 reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[4];
+ int pos = 0;
+
+ buf[pos++] = reg >> 8;
+ buf[pos++] = reg;
+ buf[pos++] = val >> 8;
+ buf[pos++] = val;
+
+ return saa7706h_i2c_send(client, buf, pos);
+}
+
+static int saa7706h_set_reg16_err(struct v4l2_subdev *sd, u16 reg, u16 val,
+ int *err)
+{
+ return *err ? *err : saa7706h_set_reg16(sd, reg, val);
+}
+
+static int saa7706h_get_reg16(struct v4l2_subdev *sd, u16 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[2];
+ int err;
+ u8 regaddr[] = {reg >> 8, reg};
+ struct i2c_msg msg[] = { {client->addr, 0, sizeof(regaddr), regaddr},
+ {client->addr, I2C_M_RD, sizeof(buf), buf} };
+
+ err = saa7706h_i2c_transfer(client, msg, ARRAY_SIZE(msg));
+ if (err)
+ return err;
+
+ return buf[0] << 8 | buf[1];
+}
+
+static int saa7706h_unmute(struct v4l2_subdev *sd)
+{
+ struct saa7706h_state *state = to_state(sd);
+ int err = 0;
+
+ err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL,
+ SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 |
+ SAA7706H_CTRL_PC_RESET_DSP2, &err);
+
+ /* newer versions of the chip requires a small sleep after reset */
+ msleep(1);
+
+ err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL,
+ SAA7706H_CTRL_PLL3_62975MHZ, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_EVALUATION, 0, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN1, 0x040022, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN2,
+ SAA7706H_CL_GEN2_WSEDGE_FALLING, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN4, 0x024080, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_SEL, 0x200080, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IAC, 0xf4caed, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_SET, 0x124334, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_COEFF, 0x004a1a,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_INPUT_SENS, 0x0071c7,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PHONE_NAV_AUDIO,
+ 0x0e22ff, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IO_CONF_DSP2, 0x001ff8,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_STATUS_DSP2, 0x080003,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PC_DSP2, 0x000004, &err);
+
+ err = saa7706h_set_reg16_err(sd, SAA7706H_DSP1_MOD0, 0x0c6c, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_MPTR0, 0x000b4b, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x000600, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x0000c0, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000819,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x00085a,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_BUSAMP, 0x7fffff,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_FDACPNTR, 0x2000cb,
+ &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_IIS1PNTR, 0x2000cb,
+ &err);
+
+ err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVGA, 0x0f80, &err);
+
+ err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT1, 0x0800,
+ &err);
+
+ err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT, 0x0800, &err);
+
+ err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000905,
+ &err);
+ if (!err)
+ state->muted = 0;
+ return err;
+}
+
+static int saa7706h_mute(struct v4l2_subdev *sd)
+{
+ struct saa7706h_state *state = to_state(sd);
+ int err;
+
+ err = saa7706h_set_reg16(sd, SAA7706H_REG_CTRL,
+ SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 |
+ SAA7706H_CTRL_PC_RESET_DSP2);
+ if (!err)
+ state->muted = 1;
+ return err;
+}
+
+static int saa7706h_queryctrl(struct v4l2_subdev *sd, 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 saa7706h_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct saa7706h_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = state->muted;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int saa7706h_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value)
+ return saa7706h_mute(sd);
+ return saa7706h_unmute(sd);
+ }
+ return -EINVAL;
+}
+
+static int saa7706h_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_SAA7706H, 0);
+}
+
+static const struct v4l2_subdev_core_ops saa7706h_core_ops = {
+ .g_chip_ident = saa7706h_g_chip_ident,
+ .queryctrl = saa7706h_queryctrl,
+ .g_ctrl = saa7706h_g_ctrl,
+ .s_ctrl = saa7706h_s_ctrl,
+};
+
+static const struct v4l2_subdev_ops saa7706h_ops = {
+ .core = &saa7706h_core_ops,
+};
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int __devinit saa7706h_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa7706h_state *state;
+ struct v4l2_subdev *sd;
+ int err;
+
+ /* 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 saa7706h_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7706h_ops);
+
+ /* check the rom versions */
+ err = saa7706h_get_reg16(sd, SAA7706H_DSP1_ROM_VER);
+ if (err < 0)
+ goto err;
+ if (err != SUPPORTED_DSP1_ROM_VER)
+ v4l2_warn(sd, "Unknown DSP1 ROM code version: 0x%x\n", err);
+
+ state->muted = 1;
+
+ /* startup in a muted state */
+ err = saa7706h_mute(sd);
+ if (err)
+ goto err;
+
+ return 0;
+
+err:
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+
+ printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", err);
+
+ return err;
+}
+
+static int __devexit saa7706h_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ saa7706h_mute(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id saa7706h_id[] = {
+ {DRIVER_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, saa7706h_id);
+
+static struct i2c_driver saa7706h_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+ .probe = saa7706h_probe,
+ .remove = saa7706h_remove,
+ .id_table = saa7706h_id,
+};
+
+static __init int saa7706h_init(void)
+{
+ return i2c_add_driver(&saa7706h_driver);
+}
+
+static __exit void saa7706h_exit(void)
+{
+ i2c_del_driver(&saa7706h_driver);
+}
+
+module_init(saa7706h_init);
+module_exit(saa7706h_exit);
+
+MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver");
+MODULE_AUTHOR("Mocean Laboratories");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 4da0f150c6e..47075fc71f1 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -724,7 +724,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
tuner->audmode = V4L2_TUNER_MODE_MONO;
/* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
- /* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */
+ /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
/* the ideal factor is 0xffff/75 = 873,8 */
tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index a96e1b9dd64..6f60841828d 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -590,8 +590,9 @@ int si470x_fops_release(struct file *file)
video_unregister_device(radio->videodev);
kfree(radio->int_in_buffer);
kfree(radio->buffer);
+ mutex_unlock(&radio->disconnect_lock);
kfree(radio);
- goto unlock;
+ goto done;
}
/* cancel read processes */
@@ -601,7 +602,6 @@ int si470x_fops_release(struct file *file)
retval = si470x_stop(radio);
usb_autopm_put_interface(radio->intf);
}
-unlock:
mutex_unlock(&radio->disconnect_lock);
done:
return retval;
@@ -842,9 +842,11 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
kfree(radio->int_in_buffer);
video_unregister_device(radio->videodev);
kfree(radio->buffer);
+ mutex_unlock(&radio->disconnect_lock);
kfree(radio);
+ } else {
+ mutex_unlock(&radio->disconnect_lock);
}
- mutex_unlock(&radio->disconnect_lock);
}
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 2f83be766d9..f8fc8654693 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -388,6 +388,15 @@ config VIDEO_TVP5150
To compile this driver as a module, choose M here: the
module will be called tvp5150.
+config VIDEO_TVP7002
+ tristate "Texas Instruments TVP7002 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TVP7002 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp7002.
+
config VIDEO_VPX3220
tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
depends on VIDEO_V4L2 && I2C
@@ -548,7 +557,6 @@ config VIDEO_VPSS_SYSTEM
depends on ARCH_DAVINCI
help
Support for vpss system module for video driver
- default y
config VIDEO_VPFE_CAPTURE
tristate "VPFE Video Capture Driver"
@@ -592,6 +600,19 @@ config VIDEO_DM355_CCDC
To compile this driver as a module, choose M here: the
module will be called vpfe.
+config VIDEO_ISIF
+ tristate "ISIF HW module"
+ depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables ISIF hw module. This is the hardware module for
+ configuring ISIF in VPFE to capture Raw Bayer RGB data from
+ a image sensor or YUV data from a YUV source.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
source "drivers/media/video/bt8xx/Kconfig"
config VIDEO_PMS
@@ -638,9 +659,14 @@ config VIDEO_W9966
information.
config VIDEO_CPIA
- tristate "CPiA Video For Linux"
+ tristate "CPiA Video For Linux (DEPRECATED)"
depends on VIDEO_V4L1
+ default n
---help---
+ This driver is DEPRECATED please use the gspca cpia1 module
+ instead. Note that you need atleast version 0.6.4 of libv4l for
+ the cpia1 gspca module.
+
This is the video4linux driver for cameras based on Vision's CPiA
(Colour Processor Interface ASIC), such as the Creative Labs Video
Blaster Webcam II. If you have one of these cameras, say Y here
@@ -944,6 +970,8 @@ source "drivers/media/video/hdpvr/Kconfig"
source "drivers/media/video/em28xx/Kconfig"
+source "drivers/media/video/tlg2300/Kconfig"
+
source "drivers/media/video/cx231xx/Kconfig"
source "drivers/media/video/usbvision/Kconfig"
@@ -955,6 +983,7 @@ source "drivers/media/video/et61x251/Kconfig"
config VIDEO_OVCAMCHIP
tristate "OmniVision Camera Chip support (DEPRECATED)"
depends on I2C && VIDEO_V4L1
+ default n
---help---
This driver is DEPRECATED please use the gspca ov519 module
instead. Note that for the ov511 / ov518 support of the gspca module
@@ -971,6 +1000,7 @@ config VIDEO_OVCAMCHIP
config USB_W9968CF
tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)"
depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP
+ default n
---help---
This driver is DEPRECATED please use the gspca ov519 module
instead. Note that for the w9968cf support of the gspca module
@@ -992,6 +1022,7 @@ config USB_W9968CF
config USB_OV511
tristate "USB OV511 Camera support (DEPRECATED)"
depends on VIDEO_V4L1
+ default n
---help---
This driver is DEPRECATED please use the gspca ov519 module
instead. Note that for the ov511 / ov518 support of the gspca module
@@ -1020,6 +1051,7 @@ source "drivers/media/video/sn9c102/Kconfig"
config USB_STV680
tristate "USB STV680 (Pencam) Camera support (DEPRECATED)"
depends on VIDEO_V4L1
+ default n
---help---
This driver is DEPRECATED please use the gspca stv0680 module
instead. Note that for the gspca stv0680 module you need
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 2af68ee8412..b88b6174a33 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_VIDEO_THS7303) += ths7303.o
obj-$(CONFIG_VIDEO_VINO) += indycam.o
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
+obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
@@ -99,6 +100,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index 5bb0f9e7158..547e1a93c42 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -254,7 +254,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
v4l2_err(sd, "no notify found!\n");
if (std & V4L2_STD_NTSC) {
- v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
bt819_setbit(decoder, 0x01, 0, 1);
bt819_setbit(decoder, 0x01, 1, 0);
bt819_setbit(decoder, 0x01, 5, 0);
@@ -263,7 +263,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
/* bt819_setbit(decoder, 0x1a, 5, 1); */
timing = &timing_data[1];
} else if (std & V4L2_STD_PAL) {
- v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
bt819_setbit(decoder, 0x01, 0, 1);
bt819_setbit(decoder, 0x01, 1, 1);
bt819_setbit(decoder, 0x01, 5, 1);
@@ -288,7 +288,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff);
bt819_write(decoder, 0x09, timing->hscale & 0xff);
decoder->norm = std;
- v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0);
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
return 0;
}
@@ -306,7 +306,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd,
v4l2_err(sd, "no notify found!\n");
if (decoder->input != input) {
- v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
decoder->input = input;
/* select mode */
if (decoder->input == 0) {
@@ -316,7 +316,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd,
bt819_setbit(decoder, 0x0b, 6, 1);
bt819_setbit(decoder, 0x1a, 1, 0);
}
- v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0);
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
}
return 0;
}
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 3182a406bdd..cb46e8fa8aa 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -81,6 +81,7 @@ static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
static int debug_latency;
+static int disable_ir;
static unsigned int fdsr;
@@ -107,6 +108,7 @@ module_param(bttv_gpio, int, 0644);
module_param(bttv_debug, int, 0644);
module_param(irq_debug, int, 0644);
module_param(debug_latency, int, 0644);
+module_param(disable_ir, int, 0444);
module_param(fdsr, int, 0444);
module_param(gbuffers, int, 0444);
@@ -139,6 +141,7 @@ MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
@@ -4461,7 +4464,10 @@ static int __devinit bttv_probe(struct pci_dev *dev,
request_modules(btv);
}
- bttv_input_init(btv);
+ if (!disable_ir) {
+ init_bttv_i2c_ir(btv);
+ bttv_input_init(btv);
+ }
/* everything is fine */
bttv_num++;
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 63aa31a041e..407fa61e4cd 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -388,7 +388,12 @@ int __devinit init_bttv_i2c(struct bttv *btv)
if (0 == btv->i2c_rc && i2c_scan)
do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client);
- /* Instantiate the IR receiver device, if present */
+ return btv->i2c_rc;
+}
+
+/* Instantiate the I2C IR receiver device, if present */
+void __devinit init_bttv_i2c_ir(struct bttv *btv)
+{
if (0 == btv->i2c_rc) {
struct i2c_board_info info;
/* The external IR receiver is at i2c address 0x34 (0x35 for
@@ -408,7 +413,6 @@ int __devinit init_bttv_i2c(struct bttv *btv)
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list);
}
- return btv->i2c_rc;
}
int __devexit fini_bttv_i2c(struct bttv *btv)
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index 277a092e121..b320dbd635a 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -247,7 +247,7 @@ int bttv_input_init(struct bttv *btv)
struct card_ir *ir;
struct ir_scancode_table *ir_codes = NULL;
struct input_dev *input_dev;
- int ir_type = IR_TYPE_OTHER;
+ u64 ir_type = IR_TYPE_OTHER;
int err = -ENOMEM;
if (!btv->has_remote)
@@ -389,7 +389,7 @@ int bttv_input_init(struct bttv *btv)
bttv_ir_start(btv, ir);
/* all done */
- err = ir_input_register(btv->remote->dev, ir_codes);
+ err = ir_input_register(btv->remote->dev, ir_codes, NULL);
if (err)
goto err_out_stop;
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index a1d0e9c9f28..6cccc2a17ee 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -279,6 +279,7 @@ extern unsigned int bttv_debug;
extern unsigned int bttv_gpio;
extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
extern int init_bttv_i2c(struct bttv *btv);
+extern void init_bttv_i2c_ir(struct bttv *btv);
extern int fini_bttv_i2c(struct bttv *btv);
#define bttv_printk if (bttv_verbose) printk
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 7bb9c1ec781..cbbf7e80d2c 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -1907,7 +1907,6 @@ static int cafe_pci_probe(struct pci_dev *pdev,
goto out_free;
mutex_init(&cam->s_mutex);
- mutex_lock(&cam->s_mutex);
spin_lock_init(&cam->dev_lock);
cam->state = S_NOTREADY;
cafe_set_config_needed(cam, 1);
@@ -1947,7 +1946,6 @@ static int cafe_pci_probe(struct pci_dev *pdev,
* because the sensor could attach in this call chain, leading to
* unsightly deadlocks.
*/
- mutex_unlock(&cam->s_mutex); /* attach can deadlock */
ret = cafe_smbus_setup(cam);
if (ret)
goto out_freeirq;
@@ -1973,7 +1971,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
cam->vdev.v4l2_dev = &cam->v4l2_dev;
ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
if (ret)
- goto out_smbus;
+ goto out_unlock;
video_set_drvdata(&cam->vdev, cam);
/*
@@ -1988,6 +1986,8 @@ static int cafe_pci_probe(struct pci_dev *pdev,
mutex_unlock(&cam->s_mutex);
return 0;
+out_unlock:
+ mutex_unlock(&cam->s_mutex);
out_smbus:
cafe_smbus_shutdown(cam);
out_freeirq:
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
index 551ddf216a4..933ae4c8cb9 100644
--- a/drivers/media/video/cpia.c
+++ b/drivers/media/video/cpia.c
@@ -3737,9 +3737,6 @@ static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
return -EINVAL;
- if (!cam || !cam->ops)
- return -ENODEV;
-
/* make this _really_ smp-safe */
if (mutex_lock_interruptible(&cam->busy_lock))
return -EINTR;
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
index e8a50a611eb..baf7e91ee0f 100644
--- a/drivers/media/video/cx18/Kconfig
+++ b/drivers/media/video/cx18/Kconfig
@@ -19,3 +19,14 @@ config VIDEO_CX18
To compile this driver as a module, choose M here: the
module will be called cx18.
+
+config VIDEO_CX18_ALSA
+ tristate "Conexant 23418 DMA audio support"
+ depends on VIDEO_CX18 && SND && EXPERIMENTAL
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio on
+ Conexant 23418 based TV cards using ALSA.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx18-alsa.
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
index f7bf0edf93f..2fadd9ded34 100644
--- a/drivers/media/video/cx18/Makefile
+++ b/drivers/media/video/cx18/Makefile
@@ -3,8 +3,10 @@ cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.
cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
cx18-dvb.o cx18-io.o
+cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
obj-$(CONFIG_VIDEO_CX18) += cx18.o
+obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c
new file mode 100644
index 00000000000..eb41d7ec65b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-main.c
@@ -0,0 +1,293 @@
+/*
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * Portions of this work were sponsored by ONELAN Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-alsa.h"
+#include "cx18-alsa-mixer.h"
+#include "cx18-alsa-pcm.h"
+
+int cx18_alsa_debug;
+
+#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \
+ do { \
+ if (cx18_alsa_debug & 2) \
+ printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \
+ } while (0);
+
+module_param_named(debug, cx18_alsa_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n");
+
+MODULE_AUTHOR("Andy Walls");
+MODULE_DESCRIPTION("CX23418 ALSA Interface");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+static inline
+struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)
+{
+ return to_cx18(v4l2_dev)->alsa;
+}
+
+static inline
+struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev)
+{
+ return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev);
+}
+
+static void snd_cx18_card_free(struct snd_cx18_card *cxsc)
+{
+ if (cxsc == NULL)
+ return;
+
+ if (cxsc->v4l2_dev != NULL)
+ to_cx18(cxsc->v4l2_dev)->alsa = NULL;
+
+ /* FIXME - take any other stopping actions needed */
+
+ kfree(cxsc);
+}
+
+static void snd_cx18_card_private_free(struct snd_card *sc)
+{
+ if (sc == NULL)
+ return;
+ snd_cx18_card_free(sc->private_data);
+ sc->private_data = NULL;
+ sc->private_free = NULL;
+}
+
+static int snd_cx18_card_create(struct v4l2_device *v4l2_dev,
+ struct snd_card *sc,
+ struct snd_cx18_card **cxsc)
+{
+ *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);
+ if (*cxsc == NULL)
+ return -ENOMEM;
+
+ (*cxsc)->v4l2_dev = v4l2_dev;
+ (*cxsc)->sc = sc;
+
+ sc->private_data = *cxsc;
+ sc->private_free = snd_cx18_card_private_free;
+
+ return 0;
+}
+
+static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ struct snd_card *sc = cxsc->sc;
+
+ /* sc->driver is used by alsa-lib's configurator: simple, unique */
+ strlcpy(sc->driver, "CX23418", sizeof(sc->driver));
+
+ /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */
+ snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d",
+ cx->instance);
+
+ /* sc->longname is read from /proc/asound/cards */
+ snprintf(sc->longname, sizeof(sc->longname),
+ "CX23418 #%d %s TV/FM Radio/Line-In Capture",
+ cx->instance, cx->card_name);
+
+ return 0;
+}
+
+static int snd_cx18_init(struct v4l2_device *v4l2_dev)
+{
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ struct snd_card *sc = NULL;
+ struct snd_cx18_card *cxsc;
+ int ret;
+
+ /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
+
+ /* (1) Check and increment the device index */
+ /* This is a no-op for us. We'll use the cx->instance */
+
+ /* (2) Create a card instance */
+ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
+ SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
+ THIS_MODULE, 0, &sc);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+ /* (3) Create a main component */
+ ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+
+ /* (4) Set the driver ID and name strings */
+ snd_cx18_card_set_names(cxsc);
+
+
+ ret = snd_cx18_pcm_create(cxsc);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+ /* FIXME - proc files */
+
+ /* (7) Set the driver data and return 0 */
+ /* We do this out of normal order for PCI drivers to avoid races */
+ cx->alsa = cxsc;
+
+ /* (6) Register the card instance */
+ ret = snd_card_register(sc);
+ if (ret) {
+ cx->alsa = NULL;
+ CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+
+ return 0;
+
+err_exit_free:
+ if (sc != NULL)
+ snd_card_free(sc);
+err_exit:
+ return ret;
+}
+
+int cx18_alsa_load(struct cx18 *cx)
+{
+ struct v4l2_device *v4l2_dev = &cx->v4l2_dev;
+ struct cx18_stream *s;
+
+ if (v4l2_dev == NULL) {
+ printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ cx = to_cx18(v4l2_dev);
+ if (cx == NULL) {
+ printk(KERN_ERR "cx18-alsa cx is NULL\n");
+ return 0;
+ }
+
+ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+ if (s->video_dev == NULL) {
+ CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
+ "skipping\n", __func__);
+ return 0;
+ }
+
+ if (cx->alsa != NULL) {
+ CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",
+ __func__);
+ return 0;
+ }
+
+ if (snd_cx18_init(v4l2_dev)) {
+ CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",
+ __func__);
+ } else {
+ CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance "
+ "\n", __func__);
+ }
+ return 0;
+}
+
+static int __init cx18_alsa_init(void)
+{
+ printk(KERN_INFO "cx18-alsa: module loading...\n");
+ cx18_ext_init = &cx18_alsa_load;
+ return 0;
+}
+
+static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+
+ /* FIXME - pointer checks & shutdown cxsc */
+
+ snd_card_free(cxsc->sc);
+ cx->alsa = NULL;
+}
+
+static int __exit cx18_alsa_exit_callback(struct device *dev, void *data)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct snd_cx18_card *cxsc;
+
+ if (v4l2_dev == NULL) {
+ printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ cxsc = to_snd_cx18_card(v4l2_dev);
+ if (cxsc == NULL) {
+ CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ snd_cx18_exit(cxsc);
+ return 0;
+}
+
+static void __exit cx18_alsa_exit(void)
+{
+ struct device_driver *drv;
+ int ret;
+
+ printk(KERN_INFO "cx18-alsa: module unloading...\n");
+
+ drv = driver_find("cx18", &pci_bus_type);
+ ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
+ put_driver(drv);
+
+ cx18_ext_init = NULL;
+ printk(KERN_INFO "cx18-alsa: module unload complete\n");
+}
+
+module_init(cx18_alsa_init);
+module_exit(cx18_alsa_exit);
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/video/cx18/cx18-alsa-mixer.c
new file mode 100644
index 00000000000..ef21114309f
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-mixer.c
@@ -0,0 +1,175 @@
+/*
+ * ALSA mixer controls for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "cx18-alsa.h"
+#include "cx18-driver.h"
+
+/*
+ * Note the cx18-av-core volume scale is funny, due to the alignment of the
+ * scale with another chip's range:
+ *
+ * v4l2_control value /512 indicated dB actual dB reg 0x8d4
+ * 0x0000 - 0x01ff 0 -119 -96 228
+ * 0x0200 - 0x02ff 1 -118 -96 228
+ * ...
+ * 0x2c00 - 0x2dff 22 -97 -96 228
+ * 0x2e00 - 0x2fff 23 -96 -96 228
+ * 0x3000 - 0x31ff 24 -95 -95 226
+ * ...
+ * 0xee00 - 0xefff 119 0 0 36
+ * ...
+ * 0xfe00 - 0xffff 127 +8 +8 20
+ */
+static inline int dB_to_cx18_av_vol(int dB)
+{
+ if (dB < -96)
+ dB = -96;
+ else if (dB > 8)
+ dB = 8;
+ return (dB + 119) << 9;
+}
+
+static inline int cx18_av_vol_to_dB(int v)
+{
+ if (v < (23 << 9))
+ v = (23 << 9);
+ else if (v > (127 << 9))
+ v = (127 << 9);
+ return (v >> 9) - 119;
+}
+
+static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ /* We're already translating values, just keep this control in dB */
+ uinfo->value.integer.min = -96;
+ uinfo->value.integer.max = 8;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ struct v4l2_control vctrl;
+ int ret;
+
+ vctrl.id = V4L2_CID_AUDIO_VOLUME;
+ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+
+ snd_cx18_lock(cxsc);
+ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+ snd_cx18_unlock(cxsc);
+
+ if (!ret)
+ uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value);
+ return ret;
+}
+
+static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ struct v4l2_control vctrl;
+ int ret;
+
+ vctrl.id = V4L2_CID_AUDIO_VOLUME;
+ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+
+ snd_cx18_lock(cxsc);
+
+ /* Fetch current state */
+ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+
+ if (ret ||
+ (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
+
+ /* Set, if needed */
+ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+ ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl);
+ if (!ret)
+ ret = 1; /* Indicate control was changed w/o error */
+ }
+ snd_cx18_unlock(cxsc);
+
+ return ret;
+}
+
+
+/* This is a bit of overkill, the slider is already in dB internally */
+static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
+
+static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog TV Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_cx18_mixer_tv_volume_info,
+ .get = snd_cx18_mixer_tv_volume_get,
+ .put = snd_cx18_mixer_tv_volume_put,
+ .tlv.p = snd_cx18_mixer_tv_vol_db_scale
+};
+
+/* FIXME - add mute switch and balance, bass, treble sliders:
+ V4L2_CID_AUDIO_MUTE
+
+ V4L2_CID_AUDIO_BALANCE
+
+ V4L2_CID_AUDIO_BASS
+ V4L2_CID_AUDIO_TREBLE
+*/
+
+/* FIXME - add stereo, lang1, lang2, mono menu */
+/* FIXME - add CS5345 I2S volume for HVR-1600 */
+
+int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc)
+{
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct snd_card *sc = cxsc->sc;
+ int ret;
+
+ strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername));
+
+ ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc));
+ if (ret) {
+ CX18_ALSA_WARN("%s: failed to add %s control, err %d\n",
+ __func__, snd_cx18_mixer_tv_vol.name, ret);
+ }
+ return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.h b/drivers/media/video/cx18/cx18-alsa-mixer.h
new file mode 100644
index 00000000000..2d418db000f
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-mixer.h
@@ -0,0 +1,23 @@
+/*
+ * ALSA mixer controls for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc);
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c
new file mode 100644
index 00000000000..2bd312daeb1
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-pcm.c
@@ -0,0 +1,354 @@
+/*
+ * ALSA PCM device for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * Portions of this work were sponsored by ONELAN Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "cx18-driver.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-fileops.h"
+#include "cx18-alsa.h"
+
+static unsigned int pcm_debug;
+module_param(pcm_debug, int, 0644);
+MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
+
+#define dprintk(fmt, arg...) do { \
+ if (pcm_debug) \
+ printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \
+ __func__, ##arg); \
+ } while (0)
+
+static struct snd_pcm_hardware snd_cx18_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data,
+ size_t num_bytes)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int oldptr;
+ unsigned int stride;
+ int period_elapsed = 0;
+ int length;
+
+ dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc,
+ pcm_data, num_bytes);
+
+ substream = cxsc->capture_pcm_substream;
+ if (substream == NULL) {
+ dprintk("substream was NULL\n");
+ return;
+ }
+
+ runtime = substream->runtime;
+ if (runtime == NULL) {
+ dprintk("runtime was NULL\n");
+ return;
+ }
+
+ stride = runtime->frame_bits >> 3;
+ if (stride == 0) {
+ dprintk("stride is zero\n");
+ return;
+ }
+
+ length = num_bytes / stride;
+ if (length == 0) {
+ dprintk("%s: length was zero\n", __func__);
+ return;
+ }
+
+ if (runtime->dma_area == NULL) {
+ dprintk("dma area was NULL - ignoring\n");
+ return;
+ }
+
+ oldptr = cxsc->hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+ cnt * stride);
+ memcpy(runtime->dma_area, pcm_data + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+ length * stride);
+ }
+ snd_pcm_stream_lock(substream);
+
+ cxsc->hwptr_done_capture += length;
+ if (cxsc->hwptr_done_capture >=
+ runtime->buffer_size)
+ cxsc->hwptr_done_capture -=
+ runtime->buffer_size;
+
+ cxsc->capture_transfer_done += length;
+ if (cxsc->capture_transfer_done >=
+ runtime->period_size) {
+ cxsc->capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+}
+
+static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ struct cx18_stream *s;
+ struct cx18_open_id item;
+ int ret;
+
+ /* Instruct the cx18 to start sending packets */
+ snd_cx18_lock(cxsc);
+ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+
+ item.cx = cx;
+ item.type = s->type;
+ item.open_id = cx->open_id++;
+
+ /* See if the stream is available */
+ if (cx18_claim_stream(&item, item.type)) {
+ /* No, it's already in use */
+ snd_cx18_unlock(cxsc);
+ return -EBUSY;
+ }
+
+ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+ test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ /* We're already streaming. No additional action required */
+ snd_cx18_unlock(cxsc);
+ return 0;
+ }
+
+
+ runtime->hw = snd_cx18_hw_capture;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ cxsc->capture_pcm_substream = substream;
+ runtime->private_data = cx;
+
+ cx->pcm_announce_callback = cx18_alsa_announce_pcm_data;
+
+ /* Not currently streaming, so start it up */
+ set_bit(CX18_F_S_STREAMING, &s->s_flags);
+ ret = cx18_start_v4l2_encode_stream(s);
+ snd_cx18_unlock(cxsc);
+
+ return 0;
+}
+
+static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ struct cx18_stream *s;
+ int ret;
+
+ /* Instruct the cx18 to stop sending packets */
+ snd_cx18_lock(cxsc);
+ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+ ret = cx18_stop_v4l2_encode_stream(s, 0);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+ cx18_release_stream(s);
+
+ cx->pcm_announce_callback = NULL;
+ snd_cx18_unlock(cxsc);
+
+ return 0;
+}
+
+static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+
+ dprintk("%s called\n", __func__);
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+ return 0;
+}
+
+static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ unsigned long flags;
+
+ spin_lock_irqsave(&cxsc->slock, flags);
+ if (substream->runtime->dma_area) {
+ dprintk("freeing pcm capture region\n");
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ }
+ spin_unlock_irqrestore(&cxsc->slock, flags);
+
+ return 0;
+}
+
+static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+
+ cxsc->hwptr_done_capture = 0;
+ cxsc->capture_transfer_done = 0;
+
+ return 0;
+}
+
+static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ return 0;
+}
+
+static
+snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ snd_pcm_uframes_t hwptr_done;
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+
+ spin_lock_irqsave(&cxsc->slock, flags);
+ hwptr_done = cxsc->hwptr_done_capture;
+ spin_unlock_irqrestore(&cxsc->slock, flags);
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_cx18_pcm_capture_ops = {
+ .open = snd_cx18_pcm_capture_open,
+ .close = snd_cx18_pcm_capture_close,
+ .ioctl = snd_cx18_pcm_ioctl,
+ .hw_params = snd_cx18_pcm_hw_params,
+ .hw_free = snd_cx18_pcm_hw_free,
+ .prepare = snd_cx18_pcm_prepare,
+ .trigger = snd_cx18_pcm_trigger,
+ .pointer = snd_cx18_pcm_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+int snd_cx18_pcm_create(struct snd_cx18_card *cxsc)
+{
+ struct snd_pcm *sp;
+ struct snd_card *sc = cxsc->sc;
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ int ret;
+
+ ret = snd_pcm_new(sc, "CX23418 PCM",
+ 0, /* PCM device 0, the only one for this card */
+ 0, /* 0 playback substreams */
+ 1, /* 1 capture substream */
+ &sp);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+ spin_lock_init(&cxsc->slock);
+
+ snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_cx18_pcm_capture_ops);
+ sp->info_flags = 0;
+ sp->private_data = cxsc;
+ strlcpy(sp->name, cx->card_name, sizeof(sp->name));
+
+ return 0;
+
+err_exit:
+ return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.h b/drivers/media/video/cx18/cx18-alsa-pcm.h
new file mode 100644
index 00000000000..325662c647a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-pcm.h
@@ -0,0 +1,27 @@
+/*
+ * ALSA PCM device for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc);
+
+/* Used by cx18-mailbox to announce the PCM data to the module */
+void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data,
+ size_t num_bytes);
diff --git a/drivers/media/video/cx18/cx18-alsa.h b/drivers/media/video/cx18/cx18-alsa.h
new file mode 100644
index 00000000000..88a1cde7540
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa.h
@@ -0,0 +1,75 @@
+/*
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+struct snd_card;
+
+struct snd_cx18_card {
+ struct v4l2_device *v4l2_dev;
+ struct snd_card *sc;
+ unsigned int capture_transfer_done;
+ unsigned int hwptr_done_capture;
+ struct snd_pcm_substream *capture_pcm_substream;
+ spinlock_t slock;
+};
+
+extern int cx18_alsa_debug;
+
+/*
+ * File operations that manipulate the encoder or video or audio subdevices
+ * need to be serialized. Use the same lock we use for v4l2 file ops.
+ */
+static inline void snd_cx18_lock(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ mutex_lock(&cx->serialize_lock);
+}
+
+static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ mutex_unlock(&cx->serialize_lock);
+}
+
+#define CX18_ALSA_DBGFLG_WARN (1 << 0)
+#define CX18_ALSA_DBGFLG_WARN (1 << 0)
+#define CX18_ALSA_DBGFLG_INFO (1 << 1)
+
+#define CX18_ALSA_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_alsa_debug) \
+ printk(KERN_INFO "%s-alsa: " type ": " fmt, \
+ v4l2_dev->name , ## args); \
+ } while (0)
+
+#define CX18_ALSA_DEBUG_WARN(fmt, args...) \
+ CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args)
+
+#define CX18_ALSA_DEBUG_INFO(fmt, args...) \
+ CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args)
+
+#define CX18_ALSA_ERR(fmt, args...) \
+ printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define CX18_ALSA_WARN(fmt, args...) \
+ printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define CX18_ALSA_INFO(fmt, args...) \
+ printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args)
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index f11e47a5828..f808fb6fc1c 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -393,7 +393,7 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = {
.gpio_init.direction = 0x7,
.gpio_audio_input = { .mask = 0x7,
.tuner = 0x6, .linein = 0x2, .radio = 0x2 },
- .xceive_pin = 15,
+ .xceive_pin = 1,
.pci_list = cx18_pci_leadtek_pvr2100,
.i2c = &cx18_i2c_std,
};
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 7f65a47f12e..c95a86ba33b 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -47,6 +47,10 @@
setting this to 1 you ensure that radio0 is now also radio1. */
int cx18_first_minor;
+/* Callback for registering extensions */
+int (*cx18_ext_init)(struct cx18 *);
+EXPORT_SYMBOL(cx18_ext_init);
+
/* add your revision and whatnot here */
static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
@@ -91,7 +95,7 @@ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
static int enc_ts_bufs = -1;
static int enc_mpg_bufs = -1;
-static int enc_idx_bufs = -1;
+static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
static int enc_yuv_bufs = -1;
static int enc_vbi_bufs = -1;
static int enc_pcm_bufs = -1;
@@ -196,14 +200,17 @@ MODULE_PARM_DESC(enc_mpg_bufs,
"Number of encoder MPG buffers\n"
"\t\t\tDefault is computed from other enc_mpg_* parameters");
MODULE_PARM_DESC(enc_idx_buffers,
- "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS));
+ "(Deprecated) Encoder IDX buffer memory (MB)\n"
+ "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
+ "\t\t\tDefault: 1 [Enabled]");
MODULE_PARM_DESC(enc_idx_bufsize,
"Size of an encoder IDX buffer (kB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE));
+ "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
+ "\t\t\t(multiples of size required for 64 index entries)\n"
+ "\t\t\tDefault: 2");
MODULE_PARM_DESC(enc_idx_bufs,
"Number of encoder IDX buffers\n"
- "\t\t\tDefault is computed from other enc_idx_* parameters");
+ "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
MODULE_PARM_DESC(enc_yuv_buffers,
"Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
@@ -231,7 +238,8 @@ MODULE_PARM_DESC(enc_pcm_bufs,
"Number of encoder PCM buffers\n"
"\t\t\tDefault is computed from other enc_pcm_* parameters");
-MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card");
+MODULE_PARM_DESC(cx18_first_minor,
+ "Set device node number assigned to first card");
MODULE_AUTHOR("Hans Verkuil");
MODULE_DESCRIPTION("CX23418 driver");
@@ -240,6 +248,28 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(CX18_VERSION);
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
+
+ /* Make sure cx18-alsa module is loaded */
+ request_module("cx18-alsa");
+
+ /* Initialize cx18-alsa for this instance of the cx18 device */
+ if (cx18_ext_init != NULL)
+ cx18_ext_init(dev);
+}
+
+static void request_modules(struct cx18 *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
/* Generic utility functions */
int cx18_msleep_timeout(unsigned int msecs, int intr)
{
@@ -501,7 +531,12 @@ static void cx18_process_options(struct cx18 *cx)
/*
* 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
+ * lines to maintain alignment in case of lost buffers).
+ *
+ * IDX is a special case where the stream_buf_size should be
+ * an integral multiple of 1.5 kB (storage for 64 index entries
+ * to maintain alignment in case of lost buffers).
+ *
*/
if (i == CX18_ENC_STREAM_TYPE_YUV) {
cx->stream_buf_size[i] *= 1024;
@@ -511,15 +546,24 @@ static void cx18_process_options(struct cx18 *cx)
if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
cx->stream_buf_size[i] =
CX18_UNIT_ENC_YUV_BUFSIZE;
+ } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
+ cx->stream_buf_size[i] *= 1024;
+ cx->stream_buf_size[i] -=
+ (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
+
+ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
+ cx->stream_buf_size[i] =
+ CX18_UNIT_ENC_IDX_BUFSIZE;
}
/*
- * YUV is a special case where the stream_buf_size is
+ * YUV and IDX are special cases 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 ||
- i == CX18_ENC_STREAM_TYPE_YUV) {
+ i == CX18_ENC_STREAM_TYPE_YUV ||
+ i == CX18_ENC_STREAM_TYPE_IDX) {
if (cx->stream_buffers[i] < 0) {
cx->stream_buffers[i] =
cx->options.megabytes[i] * 1024 * 1024
@@ -1032,6 +1076,10 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
}
CX18_INFO("Initialized card: %s\n", cx->card_name);
+
+ /* Load cx18 submodules (cx18-alsa) */
+ request_modules(cx);
+
return 0;
free_streams:
@@ -1220,6 +1268,7 @@ static void cx18_remove(struct pci_dev *pci_dev)
kfree(cx);
}
+
/* define a pci_driver for card detection */
static struct pci_driver cx18_pci_driver = {
.name = "cx18",
@@ -1230,7 +1279,8 @@ static struct pci_driver cx18_pci_driver = {
static int __init module_start(void)
{
- printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION);
+ printk(KERN_INFO "cx18: Start initialization, version %s\n",
+ CX18_VERSION);
/* Validate parameters */
if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index e3f7911a738..23ad6d548dc 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -126,10 +126,22 @@
#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)
+/* IDX buffer size should be a multiple of the index entry size from the chip */
+struct cx18_enc_idx_entry {
+ __le32 length;
+ __le32 offset_low;
+ __le32 offset_high;
+ __le32 flags;
+ __le32 pts_low;
+ __le32 pts_high;
+} __attribute__ ((packed));
+#define CX18_UNIT_ENC_IDX_BUFSIZE \
+ (sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES)
+
/* 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_IDX_BUFSIZE (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1)
#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1)
#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
@@ -234,16 +246,8 @@
#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args)
#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args)
-/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
-#define MPEG_FRAME_TYPE_IFRAME 1
-#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
-#define MPEG_FRAME_TYPE_ALL 7
-
-#define CX18_MAX_PGM_INDEX (400)
-
extern int cx18_debug;
-
struct cx18_options {
int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */
int cardtype; /* force card type on load */
@@ -276,6 +280,18 @@ struct cx18_options {
#define CX18_SLICED_TYPE_WSS_625 (5)
#define CX18_SLICED_TYPE_VPS (7)
+/**
+ * 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))
+
struct cx18_buffer {
struct list_head list;
dma_addr_t dma_handle;
@@ -558,6 +574,10 @@ struct cx18 {
int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
+ struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */
+ void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data,
+ size_t num_bytes);
+
unsigned long i_flags; /* global cx18 flags */
atomic_t ana_capturing; /* count number of active analog capture streams */
atomic_t tot_capturing; /* total count number of active capture streams */
@@ -575,12 +595,6 @@ struct cx18 {
struct vbi_info vbi;
- u32 pgm_info_offset;
- u32 pgm_info_num;
- u32 pgm_info_write_idx;
- u32 pgm_info_read_idx;
- struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX];
-
u64 mpg_data_received;
u64 vbi_data_inserted;
@@ -623,6 +637,9 @@ struct cx18 {
u32 active_input;
v4l2_std_id std;
v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
+
+ /* Used for cx18-alsa module loading */
+ struct work_struct request_module_wk;
};
static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
@@ -630,6 +647,9 @@ static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
return container_of(v4l2_dev, struct cx18, v4l2_dev);
}
+/* cx18 extensions to be loaded */
+extern int (*cx18_ext_init)(struct cx18 *);
+
/* Globals */
extern int cx18_first_minor;
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 71ad2d1b4c2..0ae2c2e1eab 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -213,10 +213,14 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
- struct cx18 *cx = stream->cx;
+ struct cx18 *cx;
int ret;
u32 v;
+ if (!stream)
+ return -EINVAL;
+
+ cx = stream->cx;
CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
feed->pid, feed->index);
@@ -253,12 +257,10 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
if (!demux->dmx.frontend)
return -EINVAL;
- if (!stream)
- return -EINVAL;
-
mutex_lock(&stream->dvb.feedlock);
if (stream->dvb.feeding++ == 0) {
CX18_DEBUG_INFO("Starting Transport DMA\n");
+ mutex_lock(&cx->serialize_lock);
set_bit(CX18_F_S_STREAMING, &stream->s_flags);
ret = cx18_start_v4l2_encode_stream(stream);
if (ret < 0) {
@@ -267,6 +269,7 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
if (stream->dvb.feeding == 0)
clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
}
+ mutex_unlock(&cx->serialize_lock);
} else
ret = 0;
mutex_unlock(&stream->dvb.feedlock);
@@ -279,17 +282,20 @@ static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
- struct cx18 *cx = stream->cx;
+ struct cx18 *cx;
int ret = -EINVAL;
- CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
- feed->pid, feed->index);
-
if (stream) {
+ cx = stream->cx;
+ CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+
mutex_lock(&stream->dvb.feedlock);
if (--stream->dvb.feeding == 0) {
CX18_DEBUG_INFO("Stopping Transport DMA\n");
+ mutex_lock(&cx->serialize_lock);
ret = cx18_stop_v4l2_encode_stream(stream, 0);
+ mutex_unlock(&cx->serialize_lock);
} else
ret = 0;
mutex_unlock(&stream->dvb.feedlock);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index c0885c69fd8..863ce775823 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -37,15 +37,21 @@
/* This function tries to claim the stream for a specific file descriptor.
If no one else is using this stream then the stream is claimed and
- associated VBI streams are also automatically claimed.
+ associated VBI and IDX streams are also automatically claimed.
Possible error returns: -EBUSY if someone else has claimed
the stream or 0 on success. */
-static int cx18_claim_stream(struct cx18_open_id *id, int type)
+int cx18_claim_stream(struct cx18_open_id *id, int type)
{
struct cx18 *cx = id->cx;
struct cx18_stream *s = &cx->streams[type];
- struct cx18_stream *s_vbi;
- int vbi_type;
+ struct cx18_stream *s_assoc;
+
+ /* Nothing should ever try to directly claim the IDX stream */
+ if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ CX18_WARN("MPEG Index stream cannot be claimed "
+ "directly, but something tried.\n");
+ return -EINVAL;
+ }
if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
/* someone already claimed this stream */
@@ -67,32 +73,47 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type)
}
s->id = id->open_id;
- /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
- (provided VBI insertion is on and sliced VBI is selected), for all
- other streams we're done */
- if (type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) {
- vbi_type = CX18_ENC_STREAM_TYPE_VBI;
- } else {
+ /*
+ * CX18_ENC_STREAM_TYPE_MPG needs to claim:
+ * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or
+ * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI
+ * (We don't yet fix up MPEG Index entries for our inserted packets).
+ *
+ * For all other streams we're done.
+ */
+ if (type != CX18_ENC_STREAM_TYPE_MPG)
return 0;
- }
- s_vbi = &cx->streams[vbi_type];
- set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx))
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ else if (!cx18_stream_enabled(s_assoc))
+ return 0;
+
+ set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
/* mark that it is used internally */
- set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags);
+ set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags);
return 0;
}
+EXPORT_SYMBOL(cx18_claim_stream);
/* This function releases a previously claimed stream. It will take into
account associated VBI streams. */
-static void cx18_release_stream(struct cx18_stream *s)
+void cx18_release_stream(struct cx18_stream *s)
{
struct cx18 *cx = s->cx;
- struct cx18_stream *s_vbi;
+ struct cx18_stream *s_assoc;
s->id = -1;
+ if (s->type == CX18_ENC_STREAM_TYPE_IDX) {
+ /*
+ * The IDX stream is only used internally, and can
+ * only be indirectly unclaimed by unclaiming the MPG stream.
+ */
+ return;
+ }
+
if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
/* this stream is still in use internally */
@@ -105,25 +126,36 @@ static void cx18_release_stream(struct cx18_stream *s)
cx18_flush_queues(s);
- /* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI,
- for all other streams we're done */
- if (s->type == CX18_ENC_STREAM_TYPE_MPG)
- s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
- else
+ /*
+ * CX18_ENC_STREAM_TYPE_MPG needs to release the
+ * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams.
+ *
+ * For all other streams we're done.
+ */
+ if (s->type != CX18_ENC_STREAM_TYPE_MPG)
return;
- /* clear internal use flag */
- if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
- /* was already cleared */
- return;
+ /* Unclaim the associated MPEG Index stream */
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
+ clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+ cx18_flush_queues(s_assoc);
}
- if (s_vbi->id != -1) {
- /* VBI stream still claimed by a file descriptor */
- return;
+
+ /* Unclaim the associated VBI stream */
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
+ if (s_assoc->id == -1) {
+ /*
+ * The VBI stream is not still claimed by a file
+ * descriptor, so completely unclaim it.
+ */
+ clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+ cx18_flush_queues(s_assoc);
+ }
}
- clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
- cx18_flush_queues(s_vbi);
}
+EXPORT_SYMBOL(cx18_release_stream);
static void cx18_dualwatch(struct cx18 *cx)
{
@@ -177,9 +209,7 @@ static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
*err = 0;
while (1) {
if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
- /* Process pending program info updates and pending
- VBI data */
-
+ /* Process pending program updates and VBI data */
if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
cx->dualwatch_jiffies = jiffies;
cx18_dualwatch(cx);
@@ -362,18 +392,6 @@ 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)
{
@@ -498,6 +516,7 @@ int cx18_start_capture(struct cx18_open_id *id)
struct cx18 *cx = id->cx;
struct cx18_stream *s = &cx->streams[id->type];
struct cx18_stream *s_vbi;
+ struct cx18_stream *s_idx;
if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
/* you cannot read from these stream types. */
@@ -516,25 +535,33 @@ int cx18_start_capture(struct cx18_open_id *id)
return 0;
}
- /* Start VBI capture if required */
+ /* Start associated VBI or IDX stream capture if required */
s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
- if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
- test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
- !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
- /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed
- automatically when the MPG stream is claimed.
- We only need to start the VBI capturing. */
- if (cx18_start_v4l2_encode_stream(s_vbi)) {
- CX18_DEBUG_WARN("VBI capture start failed\n");
-
- /* Failure, clean up and return an error */
- clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
- clear_bit(CX18_F_S_STREAMING, &s->s_flags);
- /* also releases the associated VBI stream */
- cx18_release_stream(s);
- return -EIO;
+ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /*
+ * The VBI and IDX streams should have been claimed
+ * automatically, if for internal use, when the MPG stream was
+ * claimed. We only need to start these streams capturing.
+ */
+ if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) &&
+ !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+ if (cx18_start_v4l2_encode_stream(s_idx)) {
+ CX18_DEBUG_WARN("IDX capture start failed\n");
+ clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
+ goto start_failed;
+ }
+ CX18_DEBUG_INFO("IDX capture started\n");
+ }
+ if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+ if (cx18_start_v4l2_encode_stream(s_vbi)) {
+ CX18_DEBUG_WARN("VBI capture start failed\n");
+ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ goto start_failed;
+ }
+ CX18_DEBUG_INFO("VBI insertion started\n");
}
- CX18_DEBUG_INFO("VBI insertion started\n");
}
/* Tell the card to start capturing */
@@ -547,19 +574,29 @@ int cx18_start_capture(struct cx18_open_id *id)
return 0;
}
- /* failure, clean up */
+start_failed:
CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
- /* Note: the CX18_ENC_STREAM_TYPE_VBI is released
- automatically when the MPG stream is released.
- We only need to stop the VBI capturing. */
- if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
- test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
- cx18_stop_v4l2_encode_stream(s_vbi, 0);
- clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ /*
+ * The associated VBI and IDX streams for internal use are released
+ * automatically when the MPG stream is released. We only need to stop
+ * the associated stream.
+ */
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /* Stop the IDX stream which is always for internal use */
+ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+ cx18_stop_v4l2_encode_stream(s_idx, 0);
+ clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
+ }
+ /* Stop the VBI stream, if only running for internal use */
+ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ }
}
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
- cx18_release_stream(s);
+ cx18_release_stream(s); /* Also releases associated streams */
return -EIO;
}
@@ -618,6 +655,8 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
{
struct cx18 *cx = id->cx;
struct cx18_stream *s = &cx->streams[id->type];
+ struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
CX18_DEBUG_IOCTL("close() of %s\n", s->name);
@@ -625,17 +664,19 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
/* Stop capturing */
if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
- struct cx18_stream *s_vbi =
- &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
-
CX18_DEBUG_INFO("close stopping capture\n");
- /* Special case: a running VBI capture for VBI insertion
- in the mpeg stream. Need to stop that too. */
- if (id->type == CX18_ENC_STREAM_TYPE_MPG &&
- test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
- !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
- CX18_DEBUG_INFO("close stopping embedded VBI capture\n");
- cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ if (id->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /* Stop internal use associated VBI and IDX streams */
+ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ CX18_DEBUG_INFO("close stopping embedded VBI "
+ "capture\n");
+ cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ }
+ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+ CX18_DEBUG_INFO("close stopping IDX capture\n");
+ cx18_stop_v4l2_encode_stream(s_idx, 0);
+ }
}
if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h
index 92e2d5dab93..5c8fcb884f0 100644
--- a/drivers/media/video/cx18/cx18-fileops.h
+++ b/drivers/media/video/cx18/cx18-fileops.h
@@ -34,3 +34,6 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
void cx18_mute(struct cx18 *cx);
void cx18_unmute(struct cx18 *cx);
+/* Shared with cx18-alsa module */
+int cx18_claim_stream(struct cx18_open_id *id, int type);
+void cx18_release_stream(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 3e4fc192fde..b81dd0ea8eb 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -775,10 +775,143 @@ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
return 0;
}
+static int _cx18_process_idx_data(struct cx18_buffer *buf,
+ struct v4l2_enc_idx *idx)
+{
+ int consumed, remaining;
+ struct v4l2_enc_idx_entry *e_idx;
+ struct cx18_enc_idx_entry *e_buf;
+
+ /* Frame type lookup: 1=I, 2=P, 4=B */
+ const int mapping[8] = {
+ -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
+ -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
+ };
+
+ /*
+ * Assumption here is that a buf holds an integral number of
+ * struct cx18_enc_idx_entry objects and is properly aligned.
+ * This is enforced by the module options on IDX buffer sizes.
+ */
+ remaining = buf->bytesused - buf->readpos;
+ consumed = 0;
+ e_idx = &idx->entry[idx->entries];
+ e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
+
+ while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
+ idx->entries < V4L2_ENC_IDX_ENTRIES) {
+
+ e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
+ | le32_to_cpu(e_buf->offset_low);
+
+ e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
+ | le32_to_cpu(e_buf->pts_low);
+
+ e_idx->length = le32_to_cpu(e_buf->length);
+
+ e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
+
+ e_idx->reserved[0] = 0;
+ e_idx->reserved[1] = 0;
+
+ idx->entries++;
+ e_idx = &idx->entry[idx->entries];
+ e_buf++;
+
+ remaining -= sizeof(struct cx18_enc_idx_entry);
+ consumed += sizeof(struct cx18_enc_idx_entry);
+ }
+
+ /* Swallow any partial entries at the end, if there are any */
+ if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
+ consumed += remaining;
+
+ buf->readpos += consumed;
+ return consumed;
+}
+
+static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct v4l2_enc_idx *idx)
+{
+ if (s->type != CX18_ENC_STREAM_TYPE_IDX)
+ return -EINVAL;
+
+ 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) {
+
+ /* Skip any empty buffers in the MDL */
+ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+ continue;
+
+ mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
+
+ /* exit when MDL drained or request satisfied */
+ if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
+ mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+ mdl->readpos >= mdl->bytesused)
+ break;
+ }
+ return 0;
+}
+
static int cx18_g_enc_index(struct file *file, void *fh,
struct v4l2_enc_idx *idx)
{
- return -EINVAL;
+ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ s32 tmp;
+ struct cx18_mdl *mdl;
+
+ if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
+ return -EINVAL;
+
+ /* Compute the best case number of entries we can buffer */
+ tmp = s->buffers -
+ s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
+ if (tmp <= 0)
+ tmp = 1;
+ tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
+
+ /* Fill out the header of the return structure */
+ idx->entries = 0;
+ idx->entries_cap = tmp;
+ memset(idx->reserved, 0, sizeof(idx->reserved));
+
+ /* Pull IDX MDLs and buffers from q_full and populate the entries */
+ do {
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl == NULL) /* No more IDX data right now */
+ break;
+
+ /* Extract the Index entry data from the MDL and buffers */
+ cx18_process_idx_data(s, mdl, idx);
+ if (mdl->readpos < mdl->bytesused) {
+ /* We finished with data remaining, push the MDL back */
+ cx18_push(s, mdl, &s->q_full);
+ break;
+ }
+
+ /* We drained this MDL, schedule it to go to the firmware */
+ cx18_enqueue(s, mdl, &s->q_free);
+
+ } while (idx->entries < V4L2_ENC_IDX_ENTRIES);
+
+ /* Tell the work handler to send free IDX MDLs to the firmware */
+ cx18_stream_load_fw_queue(s);
+ return 0;
}
static int cx18_encoder_cmd(struct file *file, void *fh,
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index f231dd09c72..6dcce297752 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -29,6 +29,7 @@
#include "cx18-mailbox.h"
#include "cx18-queue.h"
#include "cx18-streams.h"
+#include "cx18-alsa-pcm.h" /* FIXME make configurable */
static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
@@ -157,6 +158,34 @@ static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
}
}
+
+static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ if (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)
+ cx->pcm_announce_callback(cx->alsa, buf->buf,
+ buf->bytesused);
+ return;
+ }
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+ cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused);
+ }
+}
+
static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
{
u32 handle, mdl_ack_count, id;
@@ -223,11 +252,21 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
s->name, mdl->bytesused);
- if (s->type != CX18_ENC_STREAM_TYPE_TS)
- cx18_enqueue(s, mdl, &s->q_full);
- else {
+ if (s->type == CX18_ENC_STREAM_TYPE_TS) {
cx18_mdl_send_to_dvb(s, mdl);
cx18_enqueue(s, mdl, &s->q_free);
+ } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) {
+ /* Pass the data to cx18-alsa */
+ if (cx->pcm_announce_callback != NULL) {
+ cx18_mdl_send_to_alsa(cx, s, mdl);
+ cx18_enqueue(s, mdl, &s->q_free);
+ } else {
+ cx18_enqueue(s, mdl, &s->q_full);
+ }
+ } else {
+ cx18_enqueue(s, mdl, &s->q_full);
+ if (s->type == CX18_ENC_STREAM_TYPE_IDX)
+ cx18_stream_rotate_idx_mdls(cx);
}
}
/* Put as many MDLs as possible back into fw use */
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 63304823cef..aefc8c8cf3c 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -419,6 +419,9 @@ void cx18_stream_free(struct cx18_stream *s)
{
struct cx18_mdl *mdl;
struct cx18_buffer *buf;
+ struct cx18 *cx = s->cx;
+
+ CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name);
/* move all buffers to buf_pool and all MDLs to q_idle */
cx18_unload_queues(s);
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 987a9308d93..054450f65a6 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -319,11 +319,27 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
/* Teardown all streams */
for (type = 0; type < CX18_MAX_STREAMS; type++) {
- if (cx->streams[type].dvb.enabled) {
- cx18_dvb_unregister(&cx->streams[type]);
- cx->streams[type].dvb.enabled = false;
+
+ /* No struct video_device, but can have buffers allocated */
+ if (type == CX18_ENC_STREAM_TYPE_TS) {
+ if (cx->streams[type].dvb.enabled) {
+ cx18_dvb_unregister(&cx->streams[type]);
+ cx->streams[type].dvb.enabled = false;
+ cx18_stream_free(&cx->streams[type]);
+ }
+ continue;
+ }
+
+ /* No struct video_device, but can have buffers allocated */
+ if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ if (cx->stream_buffers[type] != 0) {
+ cx->stream_buffers[type] = 0;
+ cx18_stream_free(&cx->streams[type]);
+ }
+ continue;
}
+ /* If struct video_device exists, can have buffers allocated */
vdev = cx->streams[type].video_dev;
cx->streams[type].video_dev = NULL;
@@ -447,6 +463,32 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
}
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
+{
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ struct cx18_mdl *mdl;
+
+ if (!cx18_stream_enabled(s))
+ return;
+
+ /* Return if the firmware is not running low on MDLs */
+ if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
+ CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
+ return;
+
+ /* Return if there are no MDLs to rotate back to the firmware */
+ if (atomic_read(&s->q_full.depth) < 2)
+ return;
+
+ /*
+ * Take the oldest IDX MDL still holding data, and discard its index
+ * entries by scheduling the MDL to go back to the firmware
+ */
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl != NULL)
+ cx18_enqueue(s, mdl, &s->q_free);
+}
+
static
struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
struct cx18_mdl *mdl)
@@ -546,8 +588,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
struct cx18 *cx = s->cx;
int captype = 0;
struct cx18_api_func_private priv;
+ struct cx18_stream *s_idx;
- if (s->video_dev == NULL && s->dvb.enabled == 0)
+ if (!cx18_stream_enabled(s))
return -EINVAL;
CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
@@ -561,6 +604,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx->search_pack_header = 0;
break;
+ case CX18_ENC_STREAM_TYPE_IDX:
+ captype = CAPTURE_CHANNEL_TYPE_INDEX;
+ break;
case CX18_ENC_STREAM_TYPE_TS:
captype = CAPTURE_CHANNEL_TYPE_TS;
break;
@@ -635,11 +681,13 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vbi_setup(s);
/*
- * assign program index info.
- * Mask 7: select I/P/B, Num_req: 400 max
- * FIXME - currently we have this hardcoded as disabled
+ * Select to receive I, P, and B frame index entries, if the
+ * index stream is enabled. Otherwise disable index entry
+ * generation.
*/
- cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
+ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
+ s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
/* Call out to the common CX2341x API setup for user controls */
priv.cx = cx;
@@ -697,6 +745,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
atomic_inc(&cx->tot_capturing);
return 0;
}
+EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
void cx18_stop_all_captures(struct cx18 *cx)
{
@@ -705,7 +754,7 @@ void cx18_stop_all_captures(struct cx18 *cx)
for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
struct cx18_stream *s = &cx->streams[i];
- if (s->video_dev == NULL && s->dvb.enabled == 0)
+ if (!cx18_stream_enabled(s))
continue;
if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
cx18_stop_v4l2_encode_stream(s, 0);
@@ -717,7 +766,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
struct cx18 *cx = s->cx;
unsigned long then;
- if (s->video_dev == NULL && s->dvb.enabled == 0)
+ if (!cx18_stream_enabled(s))
return -EINVAL;
/* This function assumes that you are allowed to stop the capture
@@ -762,6 +811,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
return 0;
}
+EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
u32 cx18_find_handle(struct cx18 *cx)
{
@@ -789,7 +839,7 @@ struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
s = &cx->streams[i];
if (s->handle != handle)
continue;
- if (s->video_dev || s->dvb.enabled)
+ if (cx18_stream_enabled(s))
return s;
}
return NULL;
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index 4a01db5e5a3..0bff0fa2976 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -28,6 +28,16 @@ int cx18_streams_setup(struct cx18 *cx);
int cx18_streams_register(struct cx18 *cx);
void cx18_streams_cleanup(struct cx18 *cx, int unregister);
+#define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3)
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx);
+
+static inline bool cx18_stream_enabled(struct cx18_stream *s)
+{
+ return s->video_dev || s->dvb.enabled ||
+ (s->type == CX18_ENC_STREAM_TYPE_IDX &&
+ s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0);
+}
+
/* Related to submission of mdls to firmware */
static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
{
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 9c0b5bb1b01..3e1aec4bcfd 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 3
+#define CX18_DRIVER_VERSION_MINOR 4
#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 868806effdc..2c00980acfc 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -191,7 +191,8 @@
#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E)
/* Description: This command set the picture type mask for index file
- IN[0] - 0 = disable index file output
+ IN[0] - Task handle (ignored by firmware)
+ IN[1] - 0 = disable index file output
1 = output I picture
2 = P picture
4 = B picture
diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c
index c5082a4e8ce..64e025e2bdf 100644
--- a/drivers/media/video/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/video/cx231xx/cx231xx-dvb.c
@@ -464,9 +464,9 @@ static int dvb_init(struct cx231xx *dev)
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
- if (dvb_attach(xc5000_attach, dev->dvb->frontend,
+ if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[1].i2c_adap,
- &cnxt_rde250_tunerconfig) < 0) {
+ &cnxt_rde250_tunerconfig)) {
result = -EINVAL;
goto out_free;
}
@@ -486,9 +486,9 @@ static int dvb_init(struct cx231xx *dev)
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
- if (dvb_attach(xc5000_attach, dev->dvb->frontend,
+ if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[1].i2c_adap,
- &cnxt_rde250_tunerconfig) < 0) {
+ &cnxt_rde250_tunerconfig)) {
result = -EINVAL;
goto out_free;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
index 15826f98b68..c5771db3bfc 100644
--- a/drivers/media/video/cx231xx/cx231xx-input.c
+++ b/drivers/media/video/cx231xx/cx231xx-input.c
@@ -216,7 +216,7 @@ int cx231xx_ir_init(struct cx231xx *dev)
cx231xx_ir_start(ir);
/* all done */
- err = ir_input_register(ir->input, dev->board.ir_codes);
+ err = ir_input_register(ir->input, dev->board.ir_codes, NULL);
if (err)
goto err_out_stop;
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 88c0d248111..2ab97ad7b6f 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -681,7 +681,7 @@ static char *cmd_to_str(int cmd)
case CX2341X_ENC_SET_VIDEO_ID:
return "SET_VIDEO_ID";
case CX2341X_ENC_SET_PCR_ID:
- return "SET_PCR_PID";
+ return "SET_PCR_ID";
case CX2341X_ENC_SET_FRAME_RATE:
return "SET_FRAME_RATE";
case CX2341X_ENC_SET_FRAME_SIZE:
@@ -693,7 +693,7 @@ static char *cmd_to_str(int cmd)
case CX2341X_ENC_SET_ASPECT_RATIO:
return "SET_ASPECT_RATIO";
case CX2341X_ENC_SET_DNR_FILTER_MODE:
- return "SET_DNR_FILTER_PROPS";
+ return "SET_DNR_FILTER_MODE";
case CX2341X_ENC_SET_DNR_FILTER_PROPS:
return "SET_DNR_FILTER_PROPS";
case CX2341X_ENC_SET_CORING_LEVELS:
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 1ec48169277..d639186f645 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -274,6 +274,31 @@ struct cx23885_board cx23885_boards[] = {
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = {
+ .name = "LEADTEK WinFast PxTV1200",
+ .porta = CX23885_ANALOG_VIDEO,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_NONE0_CH3,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN8_CH3 |
+ CX25840_COMPONENT_ON,
+ } },
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -417,6 +442,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x14f1,
.subdevice = 0x8578,
.card = CX23885_BOARD_MYGICA_X8558PRO,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f22,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -617,6 +646,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
/* Tuner Reset Command */
bitmask = 0x04;
break;
@@ -769,6 +799,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
/* GPIO-2 xc3028 tuner reset */
/* The following GPIO's are on the internal AVCore (cx25840) */
@@ -1076,6 +1107,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_MYGICA_X8506:
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
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-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index e45d2df0813..939079d7bbb 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -542,6 +542,9 @@ static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
.osc_clk_freq = 30400, /* in kHz */
.if_freq = 0, /* zero IF */
.zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0xFF,
+ .agc_hold_loop = 0,
};
static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
@@ -558,6 +561,9 @@ static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
.osc_clk_freq = 30400, /* in kHz */
.if_freq = 0, /* zero IF */
.zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0xFF,
+ .agc_hold_loop = 0,
};
static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
@@ -994,15 +1000,8 @@ static int dvb_register(struct cx23885_tsport *port)
netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
memcpy(port->frontends.adapter.proposed_mac,
cinfo.port[port->nr - 1].mac, 6);
- printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC="
- "%02X:%02X:%02X:%02X:%02X:%02X\n",
- port->nr,
- port->frontends.adapter.proposed_mac[0],
- port->frontends.adapter.proposed_mac[1],
- port->frontends.adapter.proposed_mac[2],
- port->frontends.adapter.proposed_mac[3],
- port->frontends.adapter.proposed_mac[4],
- port->frontends.adapter.proposed_mac[5]);
+ printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
+ port->nr, port->frontends.adapter.proposed_mac);
netup_ci_init(port);
break;
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index 768eec92ccf..9c6620f86dc 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -397,7 +397,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
dev->ir_input = ir;
cx23885_input_ir_start(dev);
- ret = ir_input_register(ir->dev, ir_codes);
+ ret = ir_input_register(ir->dev, ir_codes, NULL);
if (ret)
goto err_out_stop;
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 8934d61cf66..2d3ac8b83dc 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -36,6 +36,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include "cx23885-ioctl.h"
+#include "tuner-xc2028.h"
MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -1505,6 +1506,18 @@ int cx23885_video_register(struct cx23885_dev *dev)
tun_setup.tuner_callback = cx23885_tuner_callback;
v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
+
+ if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) {
+ struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = dev->tuner_type,
+ .priv = &ctrl
+ };
+ v4l2_subdev_call(sd, tuner, s_config, &cfg);
+ }
}
}
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 08b3f6b136a..0e3a98d243c 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -81,6 +81,7 @@
#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25
#define CX23885_BOARD_HAUPPAUGE_HVR1290 26
#define CX23885_BOARD_MYGICA_X8558PRO 27
+#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 385ecd58f1c..f2461cd3de5 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -734,10 +734,8 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n",
vid_input);
reg = vid_input & 0xff;
- if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON)
- is_composite = 0;
- else if ((vid_input & CX25840_COMPONENT_ON) == 0)
- is_composite = 1;
+ is_composite = !is_component &&
+ ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON);
v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
reg, is_composite);
@@ -1347,30 +1345,59 @@ static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
}
#endif
+static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state))
+ return 0;
+
+ v4l_dbg(1, cx25840_debug, client, "%s audio output\n",
+ enable ? "enable" : "disable");
+
+ if (enable) {
+ v = cx25840_read(client, 0x115) | 0x80;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x03;
+ cx25840_write(client, 0x116, v);
+ } else {
+ v = cx25840_read(client, 0x115) & ~(0x80);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x03);
+ cx25840_write(client, 0x116, v);
+ }
+ return 0;
+}
+
static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 v;
- v4l_dbg(1, cx25840_debug, client, "%s output\n",
+ v4l_dbg(1, cx25840_debug, client, "%s video output\n",
enable ? "enable" : "disable");
if (enable) {
if (is_cx2388x(state) || is_cx231xx(state)) {
- u8 v = (cx25840_read(client, 0x421) | 0x0b);
+ v = cx25840_read(client, 0x421) | 0x0b;
cx25840_write(client, 0x421, v);
} else {
- cx25840_write(client, 0x115,
- is_cx2583x(state) ? 0x0c : 0x8c);
- cx25840_write(client, 0x116,
- is_cx2583x(state) ? 0x04 : 0x07);
+ v = cx25840_read(client, 0x115) | 0x0c;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x04;
+ cx25840_write(client, 0x116, v);
}
} else {
if (is_cx2388x(state) || is_cx231xx(state)) {
- u8 v = cx25840_read(client, 0x421) & ~(0x0b);
+ v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
} else {
- cx25840_write(client, 0x115, 0x00);
- cx25840_write(client, 0x116, 0x00);
+ v = cx25840_read(client, 0x115) & ~(0x0c);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x04);
+ cx25840_write(client, 0x116, v);
}
}
return 0;
@@ -1601,6 +1628,7 @@ static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
.s_clock_freq = cx25840_s_clock_freq,
.s_routing = cx25840_s_audio_routing,
+ .s_stream = cx25840_s_audio_stream,
};
static const struct v4l2_subdev_video_ops cx25840_video_ops = {
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 5a67445dd6e..64b350df78e 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -583,16 +583,18 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core=chip->core;
- int v, b;
+ int left, right, v, b;
int changed = 0;
u32 old;
- b = value->value.integer.value[1] - value->value.integer.value[0];
+ left = value->value.integer.value[0] & 0x3f;
+ right = value->value.integer.value[1] & 0x3f;
+ b = right - left;
if (b < 0) {
- v = 0x3f - value->value.integer.value[0];
+ v = 0x3f - left;
b = (-b) | 0x40;
} else {
- v = 0x3f - value->value.integer.value[1];
+ v = 0x3f - right;
}
/* Do we really know this will always be called with IRQs on? */
spin_lock_irq(&chip->reg_lock);
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index d844f2aaa01..eaf0ee7de83 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1466,6 +1466,18 @@ static const struct cx88_board cx88_boards[] = {
.audioroute = 8,
},
},
+ [CX88_BOARD_SAMSUNG_SMT_7020] = {
+ .name = "Samsung SMT 7020 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_ADSTECH_PTV_390] = {
.name = "ADS Tech Instant Video PCI",
.tuner_type = TUNER_ABSENT,
@@ -2355,6 +2367,14 @@ static const struct cx88_subid cx88_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x1404,
.card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdc00,
+ .card = CX88_BOARD_SAMSUNG_SMT_7020,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdccd,
+ .card = CX88_BOARD_SAMSUNG_SMT_7020,
},{
.subvendor = 0x1461,
.subdevice = 0xc111, /* AverMedia M150-D */
@@ -2633,6 +2653,9 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */
/* known */
break;
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ cx_set(MO_GP0_IO, 0x008989FF);
+ break;
default:
warn_printk(core, "warning: unknown hauppauge model #%d\n",
tv.model);
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index b1429692325..94ab862f021 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -674,6 +674,194 @@ static int cx8802_alloc_frontends(struct cx8802_dev *dev)
return 0;
}
+
+
+static u8 samsung_smt_7020_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x00,
+ 0x03, 0x00,
+ 0x04, 0x7D,
+ 0x05, 0x0F,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0x60,
+
+ 0x0A, 0xC2,
+ 0x0B, 0x00,
+ 0x0C, 0x01,
+ 0x0D, 0x81,
+ 0x0E, 0x44,
+ 0x0F, 0x09,
+ 0x10, 0x3C,
+ 0x11, 0x84,
+ 0x12, 0xDA,
+ 0x13, 0x99,
+ 0x14, 0x8D,
+ 0x15, 0xCE,
+ 0x16, 0xE8,
+ 0x17, 0x43,
+ 0x18, 0x1C,
+ 0x19, 0x1B,
+ 0x1A, 0x1D,
+
+ 0x1C, 0x12,
+ 0x1D, 0x00,
+ 0x1E, 0x00,
+ 0x1F, 0x00,
+ 0x20, 0x00,
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+
+ 0x28, 0x02,
+ 0x29, 0x28,
+ 0x2A, 0x14,
+ 0x2B, 0x0F,
+ 0x2C, 0x09,
+ 0x2D, 0x05,
+
+ 0x31, 0x1F,
+ 0x32, 0x19,
+ 0x33, 0xFC,
+ 0x34, 0x13,
+ 0xff, 0xff,
+};
+
+
+static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = {
+ .addr = 0x61,
+ .flags = 0,
+ .buf = buf,
+ .len = sizeof(buf) };
+
+ div = params->frequency / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x84; /* 0xC4 */
+ buf[3] = 0x00;
+
+ if (params->frequency < 1500000)
+ buf[3] |= 0x10;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int samsung_smt_7020_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t tone)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ cx_set(MO_GP0_IO, 0x0800);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ cx_set(MO_GP0_IO, 0x08);
+ break;
+ case SEC_TONE_OFF:
+ cx_clear(MO_GP0_IO, 0x08);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ u8 data;
+ struct i2c_msg msg = {
+ .addr = 8,
+ .flags = 0,
+ .buf = &data,
+ .len = sizeof(data) };
+
+ cx_set(MO_GP0_IO, 0x8000);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ break;
+ case SEC_VOLTAGE_13:
+ data = ISL6421_EN1 | ISL6421_LLC1;
+ cx_clear(MO_GP0_IO, 0x80);
+ break;
+ case SEC_VOLTAGE_18:
+ data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1;
+ cx_clear(MO_GP0_IO, 0x80);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe,
+ u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+
+ if (srate < 1500000) {
+ aclk = 0xb7;
+ bclk = 0x47;
+ } else if (srate < 3000000) {
+ aclk = 0xb7;
+ bclk = 0x4b;
+ } else if (srate < 7000000) {
+ aclk = 0xb7;
+ bclk = 0x4f;
+ } else if (srate < 14000000) {
+ aclk = 0xb7;
+ bclk = 0x53;
+ } else if (srate < 30000000) {
+ aclk = 0xb6;
+ bclk = 0x53;
+ } else if (srate < 45000000) {
+ aclk = 0xb4;
+ bclk = 0x51;
+ }
+
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, ratio & 0xf0);
+
+ return 0;
+}
+
+
+static struct stv0299_config samsung_stv0299_config = {
+ .demod_address = 0x68,
+ .inittab = samsung_smt_7020_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_LK,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate,
+};
+
static int dvb_register(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
@@ -1203,6 +1391,32 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
}
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ dev->ts_gen_cntrl = 0x08;
+
+ cx_set(MO_GP0_IO, 0x0101);
+
+ cx_clear(MO_GP0_IO, 0x01);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 0x01);
+ mdelay(200);
+
+ fe0->dvb.frontend = dvb_attach(stv0299_attach,
+ &samsung_stv0299_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.set_params =
+ samsung_smt_7020_tuner_set_params;
+ fe0->dvb.frontend->tuner_priv =
+ &dev->core->i2c_adap;
+ fe0->dvb.frontend->ops.set_voltage =
+ samsung_smt_7020_set_voltage;
+ fe0->dvb.frontend->ops.set_tone =
+ samsung_smt_7020_set_tone;
+ }
+
+ break;
+
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
core->name);
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index f9fda18b410..de180d4d5a2 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -192,7 +192,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
struct cx88_IR *ir;
struct input_dev *input_dev;
struct ir_scancode_table *ir_codes = NULL;
- int ir_type = IR_TYPE_OTHER;
+ u64 ir_type = IR_TYPE_OTHER;
int err = -ENOMEM;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
@@ -383,7 +383,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
cx88_ir_start(core, ir);
/* all done */
- err = ir_input_register(ir->input, ir_codes);
+ err = ir_input_register(ir->input, ir_codes, NULL);
if (err)
goto err_out_stop;
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index bb510489341..338af77f7f0 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -110,6 +110,9 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
case CX88_BOARD_PCHDTV_HD5500:
cx_write(TS_SOP_STAT, 1<<13);
break;
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ cx_write(TS_SOP_STAT, 0x00);
+ break;
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index b1499bf604e..48b6c04fb49 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -239,6 +239,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_WINFAST_DTV1800H 81
#define CX88_BOARD_WINFAST_DTV2000H_J 82
#define CX88_BOARD_PROF_7301 83
+#define CX88_BOARD_SAMSUNG_SMT_7020 84
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c
index ee43876adb0..9b413a35e04 100644
--- a/drivers/media/video/dabusb.c
+++ b/drivers/media/video/dabusb.c
@@ -913,6 +913,8 @@ static void __exit dabusb_cleanup (void)
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("dabusb/firmware.fw");
+MODULE_FIRMWARE("dabusb/bitstream.bin");
module_param(buffers, int, 0);
MODULE_PARM_DESC (buffers, "Number of buffers (default=256)");
diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile
index 1a8b8f3f182..a37955745aa 100644
--- a/drivers/media/video/davinci/Makefile
+++ b/drivers/media/video/davinci/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o
obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o
obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o
+obj-$(CONFIG_VIDEO_ISIF) += isif.o
diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c
index 31439001637..c29ac88ffd7 100644
--- a/drivers/media/video/davinci/dm355_ccdc.c
+++ b/drivers/media/video/davinci/dm355_ccdc.c
@@ -37,8 +37,12 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
#include <media/davinci/dm355_ccdc.h>
#include <media/davinci/vpss.h>
+
#include "dm355_ccdc_regs.h"
#include "ccdc_hw_device.h"
@@ -46,67 +50,75 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CCDC Driver for DM355");
MODULE_AUTHOR("Texas Instruments");
-static struct device *dev;
-
-/* Object for CCDC raw mode */
-static struct ccdc_params_raw ccdc_hw_params_raw = {
- .pix_fmt = CCDC_PIXFMT_RAW,
- .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
- .win = CCDC_WIN_VGA,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .gain = {
- .r_ye = 256,
- .gb_g = 256,
- .gr_cy = 256,
- .b_mg = 256
- },
- .config_params = {
- .datasft = 2,
- .data_sz = CCDC_DATA_10BITS,
- .mfilt1 = CCDC_NO_MEDIAN_FILTER1,
- .mfilt2 = CCDC_NO_MEDIAN_FILTER2,
- .alaw = {
- .gama_wd = 2,
+static struct ccdc_oper_config {
+ struct device *dev;
+ /* CCDC interface type */
+ enum vpfe_hw_if_type if_type;
+ /* Raw Bayer configuration */
+ struct ccdc_params_raw bayer;
+ /* YCbCr configuration */
+ struct ccdc_params_ycbcr ycbcr;
+ /* Master clock */
+ struct clk *mclk;
+ /* slave clock */
+ struct clk *sclk;
+ /* ccdc base address */
+ void __iomem *base_addr;
+} ccdc_cfg = {
+ /* Raw configurations */
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = CCDC_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .gain = {
+ .r_ye = 256,
+ .gb_g = 256,
+ .gr_cy = 256,
+ .b_mg = 256
},
- .blk_clamp = {
- .sample_pixel = 1,
- .dc_sub = 25
- },
- .col_pat_field0 = {
- .olop = CCDC_GREEN_BLUE,
- .olep = CCDC_BLUE,
- .elop = CCDC_RED,
- .elep = CCDC_GREEN_RED
- },
- .col_pat_field1 = {
- .olop = CCDC_GREEN_BLUE,
- .olep = CCDC_BLUE,
- .elop = CCDC_RED,
- .elep = CCDC_GREEN_RED
+ .config_params = {
+ .datasft = 2,
+ .mfilt1 = CCDC_NO_MEDIAN_FILTER1,
+ .mfilt2 = CCDC_NO_MEDIAN_FILTER2,
+ .alaw = {
+ .gama_wd = 2,
+ },
+ .blk_clamp = {
+ .sample_pixel = 1,
+ .dc_sub = 25
+ },
+ .col_pat_field0 = {
+ .olop = CCDC_GREEN_BLUE,
+ .olep = CCDC_BLUE,
+ .elop = CCDC_RED,
+ .elep = CCDC_GREEN_RED
+ },
+ .col_pat_field1 = {
+ .olop = CCDC_GREEN_BLUE,
+ .olep = CCDC_BLUE,
+ .elop = CCDC_RED,
+ .elep = CCDC_GREEN_RED
+ },
},
},
+ /* YCbCr configuration */
+ .ycbcr = {
+ .win = CCDC_WIN_PAL,
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .bt656_enable = 1,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
+ },
};
-/* Object for CCDC ycbcr mode */
-static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = {
- .win = CCDC_WIN_PAL,
- .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
- .frm_fmt = CCDC_FRMFMT_INTERLACED,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .bt656_enable = 1,
- .pix_order = CCDC_PIXORDER_CBYCRY,
- .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
-};
-
-static enum vpfe_hw_if_type ccdc_if_type;
-static void *__iomem ccdc_base_addr;
-static int ccdc_addr_size;
-
/* Raw Bayer formats */
static u32 ccdc_raw_bayer_pix_formats[] =
{V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
@@ -118,18 +130,12 @@ static u32 ccdc_raw_yuv_pix_formats[] =
/* register access routines */
static inline u32 regr(u32 offset)
{
- return __raw_readl(ccdc_base_addr + offset);
+ return __raw_readl(ccdc_cfg.base_addr + offset);
}
static inline void regw(u32 val, u32 offset)
{
- __raw_writel(val, ccdc_base_addr + offset);
-}
-
-static void ccdc_set_ccdc_base(void *addr, int size)
-{
- ccdc_base_addr = addr;
- ccdc_addr_size = size;
+ __raw_writel(val, ccdc_cfg.base_addr + offset);
}
static void ccdc_enable(int en)
@@ -153,12 +159,12 @@ static void ccdc_enable_output_to_sdram(int en)
static void ccdc_config_gain_offset(void)
{
/* configure gain */
- regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN);
- regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN);
- regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN);
- regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN);
+ regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN);
+ regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN);
+ regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN);
+ regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN);
/* configure offset */
- regw(ccdc_hw_params_raw.ccdc_offset, OFFSET);
+ regw(ccdc_cfg.bayer.ccdc_offset, OFFSET);
}
/*
@@ -169,7 +175,7 @@ static int ccdc_restore_defaults(void)
{
int i;
- dev_dbg(dev, "\nstarting ccdc_restore_defaults...");
+ dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults...");
/* set all registers to zero */
for (i = 0; i <= CCDC_REG_LAST; i += 4)
regw(0, i);
@@ -180,30 +186,29 @@ static int ccdc_restore_defaults(void)
regw(CULH_DEFAULT, CULH);
regw(CULV_DEFAULT, CULV);
/* Set default Gain and Offset */
- ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT;
- ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT;
- ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT;
- ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT;
ccdc_config_gain_offset();
regw(OUTCLIP_DEFAULT, OUTCLIP);
regw(LSCCFG2_DEFAULT, LSCCFG2);
/* select ccdc input */
if (vpss_select_ccdc_source(VPSS_CCDCIN)) {
- dev_dbg(dev, "\ncouldn't select ccdc input source");
+ dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source");
return -EFAULT;
}
/* select ccdc clock */
if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) {
- dev_dbg(dev, "\ncouldn't enable ccdc clock");
+ dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock");
return -EFAULT;
}
- dev_dbg(dev, "\nEnd of ccdc_restore_defaults...");
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults...");
return 0;
}
static int ccdc_open(struct device *device)
{
- dev = device;
return ccdc_restore_defaults();
}
@@ -226,7 +231,7 @@ static void ccdc_setwin(struct v4l2_rect *image_win,
int vert_start, vert_nr_lines;
int mid_img = 0;
- dev_dbg(dev, "\nStarting ccdc_setwin...");
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
/*
* ppc - per pixel count. indicates how many pixels per cell
@@ -260,45 +265,46 @@ static void ccdc_setwin(struct v4l2_rect *image_win,
regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0);
regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1);
regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV);
- dev_dbg(dev, "\nEnd of ccdc_setwin...");
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
}
static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
{
if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
- dev_dbg(dev, "Invalid value of data shift\n");
+ dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n");
return -EINVAL;
}
if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
- dev_dbg(dev, "Invalid value of median filter1\n");
+ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n");
return -EINVAL;
}
if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
- dev_dbg(dev, "Invalid value of median filter2\n");
+ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n");
return -EINVAL;
}
if ((ccdcparam->med_filt_thres < 0) ||
(ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
- dev_dbg(dev, "Invalid value of median filter threshold\n");
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of median filter thresold\n");
return -EINVAL;
}
if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
ccdcparam->data_sz > CCDC_DATA_8BITS) {
- dev_dbg(dev, "Invalid value of data size\n");
+ dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n");
return -EINVAL;
}
if (ccdcparam->alaw.enable) {
if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 ||
ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) {
- dev_dbg(dev, "Invalid value of ALAW\n");
+ dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
return -EINVAL;
}
}
@@ -306,12 +312,14 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
if (ccdcparam->blk_clamp.b_clamp_enable) {
if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
- dev_dbg(dev, "Invalid value of sample pixel\n");
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of sample pixel\n");
return -EINVAL;
}
if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
- dev_dbg(dev, "Invalid value of sample lines\n");
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of sample lines\n");
return -EINVAL;
}
}
@@ -325,18 +333,18 @@ static int ccdc_set_params(void __user *params)
int x;
/* only raw module parameters can be set through the IOCTL */
- if (ccdc_if_type != VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
return -EINVAL;
x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
if (x) {
- dev_dbg(dev, "ccdc_set_params: error in copying ccdc"
+ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc"
"params, %d\n", x);
return -EFAULT;
}
if (!validate_ccdc_param(&ccdc_raw_params)) {
- memcpy(&ccdc_hw_params_raw.config_params,
+ memcpy(&ccdc_cfg.bayer.config_params,
&ccdc_raw_params,
sizeof(ccdc_raw_params));
return 0;
@@ -347,11 +355,11 @@ static int ccdc_set_params(void __user *params)
/* This function will configure CCDC for YCbCr video capture */
static void ccdc_config_ycbcr(void)
{
- struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr;
+ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
u32 temp;
/* first set the CCDC power on defaults values in all registers */
- dev_dbg(dev, "\nStarting ccdc_config_ycbcr...");
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
ccdc_restore_defaults();
/* configure pixel format & video frame format */
@@ -403,7 +411,7 @@ static void ccdc_config_ycbcr(void)
regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST);
}
- dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n");
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
}
/*
@@ -483,7 +491,7 @@ int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
*/
if (count) {
- dev_err(dev, "defect table write timeout !!!\n");
+ dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n");
return -1;
}
return 0;
@@ -605,12 +613,12 @@ static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0,
/* This function will configure CCDC for Raw mode image capture */
static int ccdc_config_raw(void)
{
- struct ccdc_params_raw *params = &ccdc_hw_params_raw;
+ struct ccdc_params_raw *params = &ccdc_cfg.bayer;
struct ccdc_config_params_raw *config_params =
- &ccdc_hw_params_raw.config_params;
+ &ccdc_cfg.bayer.config_params;
unsigned int val;
- dev_dbg(dev, "\nStarting ccdc_config_raw...");
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
/* restore power on defaults to register */
ccdc_restore_defaults();
@@ -659,7 +667,7 @@ static int ccdc_config_raw(void)
val |= (config_params->datasft & CCDC_DATASFT_MASK) <<
CCDC_DATASFT_SHIFT;
regw(val , MODESET);
- dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val);
/* Configure the Median Filter threshold */
regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT);
@@ -681,7 +689,7 @@ static int ccdc_config_raw(void)
(config_params->mfilt2 << CCDC_MFILT2_SHIFT));
regw(val, GAMMAWD);
- dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val);
/* configure video window */
ccdc_setwin(&params->win, params->frm_fmt, 1);
@@ -706,7 +714,7 @@ static int ccdc_config_raw(void)
/* Configure the Gain & offset control */
ccdc_config_gain_offset();
- dev_dbg(dev, "\nWriting %x to COLPTN...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val);
/* Configure DATAOFST register */
val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) <<
@@ -726,7 +734,7 @@ static int ccdc_config_raw(void)
CCDC_HSIZE_VAL_MASK;
/* adjust to multiple of 32 */
- dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
(((params->win.width) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK);
} else {
@@ -734,7 +742,7 @@ static int ccdc_config_raw(void)
val |= (((params->win.width * 2) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK;
- dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
(((params->win.width * 2) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK);
}
@@ -745,34 +753,34 @@ static int ccdc_config_raw(void)
if (params->image_invert_enable) {
/* For interlace inverse mode */
regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST);
- dev_dbg(dev, "\nWriting %x to SDOFST...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_INTERLACE_INVERSE);
} else {
/* For interlace non inverse mode */
regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST);
- dev_dbg(dev, "\nWriting %x to SDOFST...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_INTERLACE_NORMAL);
}
} else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
if (params->image_invert_enable) {
/* For progessive inverse mode */
regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST);
- dev_dbg(dev, "\nWriting %x to SDOFST...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_PROGRESSIVE_INVERSE);
} else {
/* For progessive non inverse mode */
regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST);
- dev_dbg(dev, "\nWriting %x to SDOFST...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_PROGRESSIVE_NORMAL);
}
}
- dev_dbg(dev, "\nend of ccdc_config_raw...");
+ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
return 0;
}
static int ccdc_configure(void)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
return ccdc_config_raw();
else
ccdc_config_ycbcr();
@@ -781,23 +789,23 @@ static int ccdc_configure(void)
static int ccdc_set_buftype(enum ccdc_buftype buf_type)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- ccdc_hw_params_raw.buf_type = buf_type;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.buf_type = buf_type;
else
- ccdc_hw_params_ycbcr.buf_type = buf_type;
+ ccdc_cfg.ycbcr.buf_type = buf_type;
return 0;
}
static enum ccdc_buftype ccdc_get_buftype(void)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- return ccdc_hw_params_raw.buf_type;
- return ccdc_hw_params_ycbcr.buf_type;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.buf_type;
+ return ccdc_cfg.ycbcr.buf_type;
}
static int ccdc_enum_pix(u32 *pix, int i)
{
int ret = -EINVAL;
- if (ccdc_if_type == VPFE_RAW_BAYER) {
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
*pix = ccdc_raw_bayer_pix_formats[i];
ret = 0;
@@ -813,20 +821,19 @@ static int ccdc_enum_pix(u32 *pix, int i)
static int ccdc_set_pixel_format(u32 pixfmt)
{
- struct ccdc_a_law *alaw =
- &ccdc_hw_params_raw.config_params.alaw;
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
- if (ccdc_if_type == VPFE_RAW_BAYER) {
- ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
if (pixfmt == V4L2_PIX_FMT_SBGGR8)
alaw->enable = 1;
else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
return -EINVAL;
} else {
if (pixfmt == V4L2_PIX_FMT_YUYV)
- ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
else if (pixfmt == V4L2_PIX_FMT_UYVY)
- ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
else
return -EINVAL;
}
@@ -834,17 +841,16 @@ static int ccdc_set_pixel_format(u32 pixfmt)
}
static u32 ccdc_get_pixel_format(void)
{
- struct ccdc_a_law *alaw =
- &ccdc_hw_params_raw.config_params.alaw;
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
u32 pixfmt;
- if (ccdc_if_type == VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
if (alaw->enable)
pixfmt = V4L2_PIX_FMT_SBGGR8;
else
pixfmt = V4L2_PIX_FMT_SBGGR16;
else {
- if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
pixfmt = V4L2_PIX_FMT_YUYV;
else
pixfmt = V4L2_PIX_FMT_UYVY;
@@ -853,53 +859,53 @@ static u32 ccdc_get_pixel_format(void)
}
static int ccdc_set_image_window(struct v4l2_rect *win)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- ccdc_hw_params_raw.win = *win;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.win = *win;
else
- ccdc_hw_params_ycbcr.win = *win;
+ ccdc_cfg.ycbcr.win = *win;
return 0;
}
static void ccdc_get_image_window(struct v4l2_rect *win)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- *win = ccdc_hw_params_raw.win;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ *win = ccdc_cfg.bayer.win;
else
- *win = ccdc_hw_params_ycbcr.win;
+ *win = ccdc_cfg.ycbcr.win;
}
static unsigned int ccdc_get_line_length(void)
{
struct ccdc_config_params_raw *config_params =
- &ccdc_hw_params_raw.config_params;
+ &ccdc_cfg.bayer.config_params;
unsigned int len;
- if (ccdc_if_type == VPFE_RAW_BAYER) {
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
if ((config_params->alaw.enable) ||
(config_params->data_sz == CCDC_DATA_8BITS))
- len = ccdc_hw_params_raw.win.width;
+ len = ccdc_cfg.bayer.win.width;
else
- len = ccdc_hw_params_raw.win.width * 2;
+ len = ccdc_cfg.bayer.win.width * 2;
} else
- len = ccdc_hw_params_ycbcr.win.width * 2;
+ len = ccdc_cfg.ycbcr.win.width * 2;
return ALIGN(len, 32);
}
static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- ccdc_hw_params_raw.frm_fmt = frm_fmt;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.frm_fmt = frm_fmt;
else
- ccdc_hw_params_ycbcr.frm_fmt = frm_fmt;
+ ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
return 0;
}
static enum ccdc_frmfmt ccdc_get_frame_format(void)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- return ccdc_hw_params_raw.frm_fmt;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.frm_fmt;
else
- return ccdc_hw_params_ycbcr.frm_fmt;
+ return ccdc_cfg.ycbcr.frm_fmt;
}
static int ccdc_getfid(void)
@@ -916,14 +922,14 @@ static inline void ccdc_setfbaddr(unsigned long addr)
static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
{
- ccdc_if_type = params->if_type;
+ ccdc_cfg.if_type = params->if_type;
switch (params->if_type) {
case VPFE_BT656:
case VPFE_YCBCR_SYNC_16:
case VPFE_YCBCR_SYNC_8:
- ccdc_hw_params_ycbcr.vd_pol = params->vdpol;
- ccdc_hw_params_ycbcr.hd_pol = params->hdpol;
+ ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+ ccdc_cfg.ycbcr.hd_pol = params->hdpol;
break;
default:
/* TODO add support for raw bayer here */
@@ -938,7 +944,6 @@ static struct ccdc_hw_device ccdc_hw_dev = {
.hw_ops = {
.open = ccdc_open,
.close = ccdc_close,
- .set_ccdc_base = ccdc_set_ccdc_base,
.enable = ccdc_enable,
.enable_out_to_sdram = ccdc_enable_output_to_sdram,
.set_hw_if_params = ccdc_set_hw_if_params,
@@ -959,19 +964,118 @@ static struct ccdc_hw_device ccdc_hw_dev = {
},
};
-static int __init dm355_ccdc_init(void)
+static int __init dm355_ccdc_probe(struct platform_device *pdev)
{
- printk(KERN_NOTICE "dm355_ccdc_init\n");
- if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
- return -1;
- printk(KERN_NOTICE "%s is registered with vpfe.\n",
- ccdc_hw_dev.name);
+ void (*setup_pinmux)(void);
+ struct resource *res;
+ int status = 0;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&ccdc_hw_dev);
+ if (status < 0)
+ return status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nores;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nores;
+ }
+
+ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ if (!ccdc_cfg.base_addr) {
+ status = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ /* Get and enable Master clock */
+ ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(ccdc_cfg.mclk)) {
+ status = PTR_ERR(ccdc_cfg.mclk);
+ goto fail_nomap;
+ }
+ if (clk_enable(ccdc_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Get and enable Slave clock */
+ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
+ if (IS_ERR(ccdc_cfg.sclk)) {
+ status = PTR_ERR(ccdc_cfg.sclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(ccdc_cfg.sclk)) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+
+ /* Platform data holds setup_pinmux function ptr */
+ if (NULL == pdev->dev.platform_data) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+ setup_pinmux = pdev->dev.platform_data;
+ /*
+ * setup Mux configuration for ccdc which may be different for
+ * different SoCs using this CCDC
+ */
+ setup_pinmux();
+ ccdc_cfg.dev = &pdev->dev;
+ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
+fail_sclk:
+ clk_put(ccdc_cfg.sclk);
+fail_mclk:
+ clk_put(ccdc_cfg.mclk);
+fail_nomap:
+ iounmap(ccdc_cfg.base_addr);
+fail_nomem:
+ release_mem_region(res->start, resource_size(res));
+fail_nores:
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return status;
}
-static void __exit dm355_ccdc_exit(void)
+static int dm355_ccdc_remove(struct platform_device *pdev)
{
+ struct resource *res;
+
+ clk_put(ccdc_cfg.mclk);
+ clk_put(ccdc_cfg.sclk);
+ iounmap(ccdc_cfg.base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return 0;
+}
+
+static struct platform_driver dm355_ccdc_driver = {
+ .driver = {
+ .name = "dm355_ccdc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dm355_ccdc_remove),
+ .probe = dm355_ccdc_probe,
+};
+
+static int __init dm355_ccdc_init(void)
+{
+ return platform_driver_register(&dm355_ccdc_driver);
+}
+
+static void __exit dm355_ccdc_exit(void)
+{
+ platform_driver_unregister(&dm355_ccdc_driver);
}
module_init(dm355_ccdc_init);
diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c
index d5fa193f32d..0c394cade22 100644
--- a/drivers/media/video/davinci/dm644x_ccdc.c
+++ b/drivers/media/video/davinci/dm644x_ccdc.c
@@ -37,8 +37,12 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
#include <media/davinci/dm644x_ccdc.h>
#include <media/davinci/vpss.h>
+
#include "dm644x_ccdc_regs.h"
#include "ccdc_hw_device.h"
@@ -46,32 +50,44 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CCDC Driver for DM6446");
MODULE_AUTHOR("Texas Instruments");
-static struct device *dev;
-
-/* Object for CCDC raw mode */
-static struct ccdc_params_raw ccdc_hw_params_raw = {
- .pix_fmt = CCDC_PIXFMT_RAW,
- .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
- .win = CCDC_WIN_VGA,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .config_params = {
- .data_sz = CCDC_DATA_10BITS,
+static struct ccdc_oper_config {
+ struct device *dev;
+ /* CCDC interface type */
+ enum vpfe_hw_if_type if_type;
+ /* Raw Bayer configuration */
+ struct ccdc_params_raw bayer;
+ /* YCbCr configuration */
+ struct ccdc_params_ycbcr ycbcr;
+ /* Master clock */
+ struct clk *mclk;
+ /* slave clock */
+ struct clk *sclk;
+ /* ccdc base address */
+ void __iomem *base_addr;
+} ccdc_cfg = {
+ /* Raw configurations */
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = CCDC_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .config_params = {
+ .data_sz = CCDC_DATA_10BITS,
+ },
+ },
+ .ycbcr = {
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .win = CCDC_WIN_PAL,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .bt656_enable = 1,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
},
-};
-
-/* Object for CCDC ycbcr mode */
-static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = {
- .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
- .frm_fmt = CCDC_FRMFMT_INTERLACED,
- .win = CCDC_WIN_PAL,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .bt656_enable = 1,
- .pix_order = CCDC_PIXORDER_CBYCRY,
- .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
};
#define CCDC_MAX_RAW_YUV_FORMATS 2
@@ -84,25 +100,15 @@ static u32 ccdc_raw_bayer_pix_formats[] =
static u32 ccdc_raw_yuv_pix_formats[] =
{V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
-static void *__iomem ccdc_base_addr;
-static int ccdc_addr_size;
-static enum vpfe_hw_if_type ccdc_if_type;
-
/* register access routines */
static inline u32 regr(u32 offset)
{
- return __raw_readl(ccdc_base_addr + offset);
+ return __raw_readl(ccdc_cfg.base_addr + offset);
}
static inline void regw(u32 val, u32 offset)
{
- __raw_writel(val, ccdc_base_addr + offset);
-}
-
-static void ccdc_set_ccdc_base(void *addr, int size)
-{
- ccdc_base_addr = addr;
- ccdc_addr_size = size;
+ __raw_writel(val, ccdc_cfg.base_addr + offset);
}
static void ccdc_enable(int flag)
@@ -132,7 +138,7 @@ void ccdc_setwin(struct v4l2_rect *image_win,
int vert_start, vert_nr_lines;
int val = 0, mid_img = 0;
- dev_dbg(dev, "\nStarting ccdc_setwin...");
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
/*
* ppc - per pixel count. indicates how many pixels per cell
* output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
@@ -171,7 +177,7 @@ void ccdc_setwin(struct v4l2_rect *image_win,
regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start,
CCDC_VERT_START);
regw(vert_nr_lines, CCDC_VERT_LINES);
- dev_dbg(dev, "\nEnd of ccdc_setwin...");
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
}
static void ccdc_readregs(void)
@@ -179,39 +185,39 @@ static void ccdc_readregs(void)
unsigned int val = 0;
val = regr(CCDC_ALAW);
- dev_notice(dev, "\nReading 0x%x to ALAW...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val);
val = regr(CCDC_CLAMP);
- dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val);
val = regr(CCDC_DCSUB);
- dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val);
val = regr(CCDC_BLKCMP);
- dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val);
val = regr(CCDC_FPC_ADDR);
- dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val);
val = regr(CCDC_FPC);
- dev_notice(dev, "\nReading 0x%x to FPC...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val);
val = regr(CCDC_FMTCFG);
- dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val);
val = regr(CCDC_COLPTN);
- dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val);
val = regr(CCDC_FMT_HORZ);
- dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val);
val = regr(CCDC_FMT_VERT);
- dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val);
val = regr(CCDC_HSIZE_OFF);
- dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
val = regr(CCDC_SDOFST);
- dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val);
val = regr(CCDC_VP_OUT);
- dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val);
val = regr(CCDC_SYN_MODE);
- dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val);
val = regr(CCDC_HORZ_INFO);
- dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val);
val = regr(CCDC_VERT_START);
- dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val);
val = regr(CCDC_VERT_LINES);
- dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val);
}
static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
@@ -220,7 +226,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) ||
(ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) ||
(ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) {
- dev_dbg(dev, "\nInvalid data line select");
+ dev_dbg(ccdc_cfg.dev, "\nInvalid data line select");
return -1;
}
}
@@ -230,7 +236,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
{
struct ccdc_config_params_raw *config_params =
- &ccdc_hw_params_raw.config_params;
+ &ccdc_cfg.bayer.config_params;
unsigned int *fpc_virtaddr = NULL;
unsigned int *fpc_physaddr = NULL;
@@ -266,7 +272,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
FP_NUM_BYTES));
if (fpc_virtaddr == NULL) {
- dev_dbg(dev,
+ dev_dbg(ccdc_cfg.dev,
"\nUnable to allocate memory for FPC");
return -EFAULT;
}
@@ -279,7 +285,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
if (copy_from_user(fpc_virtaddr,
(void __user *)raw_params->fault_pxl.fpc_table_addr,
config_params->fault_pxl.fp_num * FP_NUM_BYTES)) {
- dev_dbg(dev, "\n copy_from_user failed");
+ dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
return -EFAULT;
}
config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
@@ -289,7 +295,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
static int ccdc_close(struct device *dev)
{
struct ccdc_config_params_raw *config_params =
- &ccdc_hw_params_raw.config_params;
+ &ccdc_cfg.bayer.config_params;
unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL;
fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
@@ -323,9 +329,8 @@ static void ccdc_restore_defaults(void)
static int ccdc_open(struct device *device)
{
- dev = device;
ccdc_restore_defaults();
- if (ccdc_if_type == VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_enable_vport(1);
return 0;
}
@@ -341,12 +346,12 @@ static int ccdc_set_params(void __user *params)
struct ccdc_config_params_raw ccdc_raw_params;
int x;
- if (ccdc_if_type != VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
return -EINVAL;
x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
if (x) {
- dev_dbg(dev, "ccdc_set_params: error in copying"
+ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying"
"ccdc params, %d\n", x);
return -EFAULT;
}
@@ -364,10 +369,10 @@ static int ccdc_set_params(void __user *params)
*/
void ccdc_config_ycbcr(void)
{
- struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr;
+ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
u32 syn_mode;
- dev_dbg(dev, "\nStarting ccdc_config_ycbcr...");
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
/*
* first restore the CCDC registers to default values
* This is important since we assume default values to be set in
@@ -428,7 +433,7 @@ void ccdc_config_ycbcr(void)
regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST);
ccdc_sbl_reset();
- dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n");
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
ccdc_readregs();
}
@@ -440,9 +445,9 @@ static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
/* configure DCSub */
val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK;
regw(val, CCDC_DCSUB);
- dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val);
regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP);
- dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n");
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n");
return;
}
/*
@@ -457,10 +462,10 @@ static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE);
regw(val, CCDC_CLAMP);
- dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val);
/* If Black clamping is enable then make dcsub 0 */
regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB);
- dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n");
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n");
}
static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
@@ -490,17 +495,17 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
/* Configure Fault pixel if needed */
regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
- dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n",
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n",
(fpc->fpc_table_addr));
/* Write the FPC params with FPC disable */
val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
regw(val, CCDC_FPC);
- dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
/* read the FPC register */
val = regr(CCDC_FPC) | CCDC_FPC_ENABLE;
regw(val, CCDC_FPC);
- dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
}
/*
@@ -509,13 +514,13 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
*/
void ccdc_config_raw(void)
{
- struct ccdc_params_raw *params = &ccdc_hw_params_raw;
+ struct ccdc_params_raw *params = &ccdc_cfg.bayer;
struct ccdc_config_params_raw *config_params =
- &ccdc_hw_params_raw.config_params;
+ &ccdc_cfg.bayer.config_params;
unsigned int syn_mode = 0;
unsigned int val;
- dev_dbg(dev, "\nStarting ccdc_config_raw...");
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
/* Reset CCDC */
ccdc_restore_defaults();
@@ -545,7 +550,7 @@ void ccdc_config_raw(void)
val = ((config_params->alaw.gama_wd &
CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE);
regw(val, CCDC_ALAW);
- dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val);
}
/* Configure video window */
@@ -582,11 +587,11 @@ void ccdc_config_raw(void)
/* Write value in FMTCFG */
regw(val, CCDC_FMTCFG);
- dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val);
/* Configure the color pattern according to mt9t001 sensor */
regw(CCDC_COLPTN_VAL, CCDC_COLPTN);
- dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
/*
* Configure Data formatter(Video port) pixel selection
* (FMT_HORZ, FMT_VERT)
@@ -596,7 +601,7 @@ void ccdc_config_raw(void)
(params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK);
regw(val, CCDC_FMT_HORZ);
- dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK)
<< CCDC_FMT_VERT_FMTSLV_SHIFT;
if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
@@ -604,13 +609,13 @@ void ccdc_config_raw(void)
else
val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK;
- dev_dbg(dev, "\nparams->win.height 0x%x ...\n",
+ dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n",
params->win.height);
regw(val, CCDC_FMT_VERT);
- dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val);
- dev_dbg(dev, "\nbelow regw(val, FMT_VERT)...");
+ dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)...");
/*
* Configure Horizontal offset register. If pack 8 is enabled then
@@ -631,17 +636,17 @@ void ccdc_config_raw(void)
if (params->image_invert_enable) {
/* For intelace inverse mode */
regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST);
- dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n");
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n");
}
else {
/* For intelace non inverse mode */
regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST);
- dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n");
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n");
}
} else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST);
- dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n");
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n");
}
/*
@@ -662,18 +667,18 @@ void ccdc_config_raw(void)
val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK;
regw(val, CCDC_VP_OUT);
- dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val);
regw(syn_mode, CCDC_SYN_MODE);
- dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
ccdc_sbl_reset();
- dev_dbg(dev, "\nend of ccdc_config_raw...");
+ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
ccdc_readregs();
}
static int ccdc_configure(void)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_config_raw();
else
ccdc_config_ycbcr();
@@ -682,24 +687,24 @@ static int ccdc_configure(void)
static int ccdc_set_buftype(enum ccdc_buftype buf_type)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- ccdc_hw_params_raw.buf_type = buf_type;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.buf_type = buf_type;
else
- ccdc_hw_params_ycbcr.buf_type = buf_type;
+ ccdc_cfg.ycbcr.buf_type = buf_type;
return 0;
}
static enum ccdc_buftype ccdc_get_buftype(void)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- return ccdc_hw_params_raw.buf_type;
- return ccdc_hw_params_ycbcr.buf_type;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.buf_type;
+ return ccdc_cfg.ycbcr.buf_type;
}
static int ccdc_enum_pix(u32 *pix, int i)
{
int ret = -EINVAL;
- if (ccdc_if_type == VPFE_RAW_BAYER) {
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
*pix = ccdc_raw_bayer_pix_formats[i];
ret = 0;
@@ -715,17 +720,17 @@ static int ccdc_enum_pix(u32 *pix, int i)
static int ccdc_set_pixel_format(u32 pixfmt)
{
- if (ccdc_if_type == VPFE_RAW_BAYER) {
- ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
if (pixfmt == V4L2_PIX_FMT_SBGGR8)
- ccdc_hw_params_raw.config_params.alaw.enable = 1;
+ ccdc_cfg.bayer.config_params.alaw.enable = 1;
else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
return -EINVAL;
} else {
if (pixfmt == V4L2_PIX_FMT_YUYV)
- ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
else if (pixfmt == V4L2_PIX_FMT_UYVY)
- ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
else
return -EINVAL;
}
@@ -734,17 +739,16 @@ static int ccdc_set_pixel_format(u32 pixfmt)
static u32 ccdc_get_pixel_format(void)
{
- struct ccdc_a_law *alaw =
- &ccdc_hw_params_raw.config_params.alaw;
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
u32 pixfmt;
- if (ccdc_if_type == VPFE_RAW_BAYER)
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
if (alaw->enable)
pixfmt = V4L2_PIX_FMT_SBGGR8;
else
pixfmt = V4L2_PIX_FMT_SBGGR16;
else {
- if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
pixfmt = V4L2_PIX_FMT_YUYV;
else
pixfmt = V4L2_PIX_FMT_UYVY;
@@ -754,53 +758,53 @@ static u32 ccdc_get_pixel_format(void)
static int ccdc_set_image_window(struct v4l2_rect *win)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- ccdc_hw_params_raw.win = *win;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.win = *win;
else
- ccdc_hw_params_ycbcr.win = *win;
+ ccdc_cfg.ycbcr.win = *win;
return 0;
}
static void ccdc_get_image_window(struct v4l2_rect *win)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- *win = ccdc_hw_params_raw.win;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ *win = ccdc_cfg.bayer.win;
else
- *win = ccdc_hw_params_ycbcr.win;
+ *win = ccdc_cfg.ycbcr.win;
}
static unsigned int ccdc_get_line_length(void)
{
struct ccdc_config_params_raw *config_params =
- &ccdc_hw_params_raw.config_params;
+ &ccdc_cfg.bayer.config_params;
unsigned int len;
- if (ccdc_if_type == VPFE_RAW_BAYER) {
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
if ((config_params->alaw.enable) ||
(config_params->data_sz == CCDC_DATA_8BITS))
- len = ccdc_hw_params_raw.win.width;
+ len = ccdc_cfg.bayer.win.width;
else
- len = ccdc_hw_params_raw.win.width * 2;
+ len = ccdc_cfg.bayer.win.width * 2;
} else
- len = ccdc_hw_params_ycbcr.win.width * 2;
+ len = ccdc_cfg.ycbcr.win.width * 2;
return ALIGN(len, 32);
}
static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- ccdc_hw_params_raw.frm_fmt = frm_fmt;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.frm_fmt = frm_fmt;
else
- ccdc_hw_params_ycbcr.frm_fmt = frm_fmt;
+ ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
return 0;
}
static enum ccdc_frmfmt ccdc_get_frame_format(void)
{
- if (ccdc_if_type == VPFE_RAW_BAYER)
- return ccdc_hw_params_raw.frm_fmt;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.frm_fmt;
else
- return ccdc_hw_params_ycbcr.frm_fmt;
+ return ccdc_cfg.ycbcr.frm_fmt;
}
static int ccdc_getfid(void)
@@ -816,14 +820,14 @@ static inline void ccdc_setfbaddr(unsigned long addr)
static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
{
- ccdc_if_type = params->if_type;
+ ccdc_cfg.if_type = params->if_type;
switch (params->if_type) {
case VPFE_BT656:
case VPFE_YCBCR_SYNC_16:
case VPFE_YCBCR_SYNC_8:
- ccdc_hw_params_ycbcr.vd_pol = params->vdpol;
- ccdc_hw_params_ycbcr.hd_pol = params->hdpol;
+ ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+ ccdc_cfg.ycbcr.hd_pol = params->hdpol;
break;
default:
/* TODO add support for raw bayer here */
@@ -838,7 +842,6 @@ static struct ccdc_hw_device ccdc_hw_dev = {
.hw_ops = {
.open = ccdc_open,
.close = ccdc_close,
- .set_ccdc_base = ccdc_set_ccdc_base,
.reset = ccdc_sbl_reset,
.enable = ccdc_enable,
.set_hw_if_params = ccdc_set_hw_if_params,
@@ -859,19 +862,105 @@ static struct ccdc_hw_device ccdc_hw_dev = {
},
};
-static int __init dm644x_ccdc_init(void)
+static int __init dm644x_ccdc_probe(struct platform_device *pdev)
{
- printk(KERN_NOTICE "dm644x_ccdc_init\n");
- if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
- return -1;
- printk(KERN_NOTICE "%s is registered with vpfe.\n",
- ccdc_hw_dev.name);
+ struct resource *res;
+ int status = 0;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&ccdc_hw_dev);
+ if (status < 0)
+ return status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nores;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nores;
+ }
+
+ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ if (!ccdc_cfg.base_addr) {
+ status = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ /* Get and enable Master clock */
+ ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(ccdc_cfg.mclk)) {
+ status = PTR_ERR(ccdc_cfg.mclk);
+ goto fail_nomap;
+ }
+ if (clk_enable(ccdc_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Get and enable Slave clock */
+ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
+ if (IS_ERR(ccdc_cfg.sclk)) {
+ status = PTR_ERR(ccdc_cfg.sclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(ccdc_cfg.sclk)) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+ ccdc_cfg.dev = &pdev->dev;
+ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
+fail_sclk:
+ clk_put(ccdc_cfg.sclk);
+fail_mclk:
+ clk_put(ccdc_cfg.mclk);
+fail_nomap:
+ iounmap(ccdc_cfg.base_addr);
+fail_nomem:
+ release_mem_region(res->start, resource_size(res));
+fail_nores:
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return status;
}
-static void __exit dm644x_ccdc_exit(void)
+static int dm644x_ccdc_remove(struct platform_device *pdev)
{
+ struct resource *res;
+
+ clk_put(ccdc_cfg.mclk);
+ clk_put(ccdc_cfg.sclk);
+ iounmap(ccdc_cfg.base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return 0;
+}
+
+static struct platform_driver dm644x_ccdc_driver = {
+ .driver = {
+ .name = "dm644x_ccdc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dm644x_ccdc_remove),
+ .probe = dm644x_ccdc_probe,
+};
+
+static int __init dm644x_ccdc_init(void)
+{
+ return platform_driver_register(&dm644x_ccdc_driver);
+}
+
+static void __exit dm644x_ccdc_exit(void)
+{
+ platform_driver_unregister(&dm644x_ccdc_driver);
}
module_init(dm644x_ccdc_init);
diff --git a/drivers/media/video/davinci/isif.c b/drivers/media/video/davinci/isif.c
new file mode 100644
index 00000000000..29c29c66859
--- /dev/null
+++ b/drivers/media/video/davinci/isif.c
@@ -0,0 +1,1172 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ * Image Sensor Interface (ISIF) driver
+ *
+ * This driver is for configuring the ISIF IP available on DM365 or any other
+ * TI SoCs. This is used for capturing yuv or bayer video or image data
+ * from a decoder or sensor. This IP is similar to the CCDC IP on DM355
+ * and DM6446, but with enhanced or additional ip blocks. The driver
+ * configures the ISIF upon commands from the vpfe bridge driver through
+ * ccdc_hw_device interface.
+ *
+ * TODO: 1) Raw bayer parameter settings and bayer capture
+ * 2) Add support for control ioctl
+ */
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <mach/mux.h>
+
+#include <media/davinci/isif.h>
+#include <media/davinci/vpss.h>
+
+#include "isif_regs.h"
+#include "ccdc_hw_device.h"
+
+/* Defaults for module configuration parameters */
+static struct isif_config_params_raw isif_config_defaults = {
+ .linearize = {
+ .en = 0,
+ .corr_shft = ISIF_NO_SHIFT,
+ .scale_fact = {1, 0},
+ },
+ .df_csc = {
+ .df_or_csc = 0,
+ .csc = {
+ .en = 0,
+ },
+ },
+ .dfc = {
+ .en = 0,
+ },
+ .bclamp = {
+ .en = 0,
+ },
+ .gain_offset = {
+ .gain = {
+ .r_ye = {1, 0},
+ .gr_cy = {1, 0},
+ .gb_g = {1, 0},
+ .b_mg = {1, 0},
+ },
+ },
+ .culling = {
+ .hcpat_odd = 0xff,
+ .hcpat_even = 0xff,
+ .vcpat = 0xff,
+ },
+ .compress = {
+ .alg = ISIF_ALAW,
+ },
+};
+
+/* ISIF operation configuration */
+static struct isif_oper_config {
+ struct device *dev;
+ enum vpfe_hw_if_type if_type;
+ struct isif_ycbcr_config ycbcr;
+ struct isif_params_raw bayer;
+ enum isif_data_pack data_pack;
+ /* Master clock */
+ struct clk *mclk;
+ /* ISIF base address */
+ void __iomem *base_addr;
+ /* ISIF Linear Table 0 */
+ void __iomem *linear_tbl0_addr;
+ /* ISIF Linear Table 1 */
+ void __iomem *linear_tbl1_addr;
+} isif_cfg = {
+ .ycbcr = {
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .win = ISIF_WIN_NTSC,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED,
+ },
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = ISIF_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .gain = {
+ .r_ye = {1, 0},
+ .gr_cy = {1, 0},
+ .gb_g = {1, 0},
+ .b_mg = {1, 0},
+ },
+ .cfa_pat = ISIF_CFA_PAT_MOSAIC,
+ .data_msb = ISIF_BIT_MSB_11,
+ .config_params = {
+ .data_shift = ISIF_NO_SHIFT,
+ .col_pat_field0 = {
+ .olop = ISIF_GREEN_BLUE,
+ .olep = ISIF_BLUE,
+ .elop = ISIF_RED,
+ .elep = ISIF_GREEN_RED,
+ },
+ .col_pat_field1 = {
+ .olop = ISIF_GREEN_BLUE,
+ .olep = ISIF_BLUE,
+ .elop = ISIF_RED,
+ .elep = ISIF_GREEN_RED,
+ },
+ .test_pat_gen = 0,
+ },
+ },
+ .data_pack = ISIF_DATA_PACK8,
+};
+
+/* Raw Bayer formats */
+static const u32 isif_raw_bayer_pix_formats[] = {
+ V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static const u32 isif_raw_yuv_pix_formats[] = {
+ V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(isif_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, isif_cfg.base_addr + offset);
+}
+
+/* reg_modify() - read, modify and write register */
+static inline u32 reg_modify(u32 mask, u32 val, u32 offset)
+{
+ u32 new_val = (regr(offset) & ~mask) | (val & mask);
+
+ regw(new_val, offset);
+ return new_val;
+}
+
+static inline void regw_lin_tbl(u32 val, u32 offset, int i)
+{
+ if (!i)
+ __raw_writel(val, isif_cfg.linear_tbl0_addr + offset);
+ else
+ __raw_writel(val, isif_cfg.linear_tbl1_addr + offset);
+}
+
+static void isif_disable_all_modules(void)
+{
+ /* disable BC */
+ regw(0, CLAMPCFG);
+ /* disable vdfc */
+ regw(0, DFCCTL);
+ /* disable CSC */
+ regw(0, CSCCTL);
+ /* disable linearization */
+ regw(0, LINCFG0);
+ /* disable other modules here as they are supported */
+}
+
+static void isif_enable(int en)
+{
+ if (!en) {
+ /* Before disable isif, disable all ISIF modules */
+ isif_disable_all_modules();
+ /*
+ * wait for next VD. Assume lowest scan rate is 12 Hz. So
+ * 100 msec delay is good enough
+ */
+ msleep(100);
+ }
+ reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN);
+}
+
+static void isif_enable_output_to_sdram(int en)
+{
+ reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
+}
+
+static void isif_config_culling(struct isif_cul *cul)
+{
+ u32 val;
+
+ /* Horizontal pattern */
+ val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd;
+ regw(val, CULH);
+
+ /* vertical pattern */
+ regw(cul->vcpat, CULV);
+
+ /* LPF */
+ reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT,
+ cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
+}
+
+static void isif_config_gain_offset(void)
+{
+ struct isif_gain_offsets_adj *gain_off_p =
+ &isif_cfg.bayer.config_params.gain_offset;
+ u32 val;
+
+ val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) |
+ (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) |
+ (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) |
+ (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) |
+ (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) |
+ (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT);
+
+ reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
+
+ val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.r_ye.decimal;
+ regw(val, CRGAIN);
+
+ val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.gr_cy.decimal;
+ regw(val, CGRGAIN);
+
+ val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.gb_g.decimal;
+ regw(val, CGBGAIN);
+
+ val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.b_mg.decimal;
+ regw(val, CBGAIN);
+
+ regw(gain_off_p->offset, COFSTA);
+}
+
+static void isif_restore_defaults(void)
+{
+ enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
+
+ dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults...");
+ isif_cfg.bayer.config_params = isif_config_defaults;
+ /* Enable clock to ISIF, IPIPEIF and BL */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
+ vpss_enable_clock(VPSS_BL_CLOCK, 1);
+ /* Set default offset and gain */
+ isif_config_gain_offset();
+ vpss_select_ccdc_source(source);
+ dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults...");
+}
+
+static int isif_open(struct device *device)
+{
+ isif_restore_defaults();
+ return 0;
+}
+
+/* This function will configure the window size to be capture in ISIF reg */
+static void isif_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt, int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int mid_img = 0;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_setwin...");
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ regw(horz_start & START_PX_HOR_MASK, SPH);
+ regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ } else {
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ regw(mid_img, VDINT1);
+ }
+
+ regw(0, VDINT0);
+ regw(vert_start & START_VER_ONE_MASK, SLV0);
+ regw(vert_start & START_VER_TWO_MASK, SLV1);
+ regw(vert_nr_lines & NUM_LINES_VER, LNV);
+}
+
+static void isif_config_bclamp(struct isif_black_clamp *bc)
+{
+ u32 val;
+
+ /*
+ * DC Offset is always added to image data irrespective of bc enable
+ * status
+ */
+ regw(bc->dc_offset, CLDCOFST);
+
+ if (bc->en) {
+ val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
+
+ /* Enable BC and horizontal clamp caculation paramaters */
+ val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
+
+ regw(val, CLAMPCFG);
+
+ if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) {
+ /*
+ * Window count for calculation
+ * Base window selection
+ * pixel limit
+ * Horizontal size of window
+ * vertical size of the window
+ * Horizontal start position of the window
+ * Vertical start position of the window
+ */
+ val = bc->horz.win_count_calc |
+ ((!!bc->horz.base_win_sel_calc) <<
+ ISIF_HORZ_BC_WIN_SEL_SHIFT) |
+ ((!!bc->horz.clamp_pix_limit) <<
+ ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
+ (bc->horz.win_h_sz_calc <<
+ ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
+ (bc->horz.win_v_sz_calc <<
+ ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
+ regw(val, CLHWIN0);
+
+ regw(bc->horz.win_start_h_calc, CLHWIN1);
+ regw(bc->horz.win_start_v_calc, CLHWIN2);
+ }
+
+ /* vertical clamp caculation paramaters */
+
+ /* Reset clamp value sel for previous line */
+ val |=
+ (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) |
+ (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT);
+ regw(val, CLVWIN0);
+
+ /* Optical Black horizontal start position */
+ regw(bc->vert.ob_start_h, CLVWIN1);
+ /* Optical Black vertical start position */
+ regw(bc->vert.ob_start_v, CLVWIN2);
+ /* Optical Black vertical size for calculation */
+ regw(bc->vert.ob_v_sz_calc, CLVWIN3);
+ /* Vertical start position for BC subtraction */
+ regw(bc->vert_start_sub, CLSV);
+ }
+}
+
+static void isif_config_linearization(struct isif_linearize *linearize)
+{
+ u32 val, i;
+
+ if (!linearize->en) {
+ regw(0, LINCFG0);
+ return;
+ }
+
+ /* shift value for correction & enable linearization (set lsb) */
+ val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1;
+ regw(val, LINCFG0);
+
+ /* Scale factor */
+ val = ((!!linearize->scale_fact.integer) <<
+ ISIF_LIN_SCALE_FACT_INTEG_SHIFT) |
+ linearize->scale_fact.decimal;
+ regw(val, LINCFG1);
+
+ for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) {
+ if (i % 2)
+ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1);
+ else
+ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0);
+ }
+}
+
+static int isif_config_dfc(struct isif_dfc *vdfc)
+{
+ /* initialize retries to loop for max ~ 250 usec */
+ u32 val, count, retries = loops_per_jiffy / (4000/HZ);
+ int i;
+
+ if (!vdfc->en)
+ return 0;
+
+ /* Correction mode */
+ val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT);
+
+ /* Correct whole line or partial */
+ if (vdfc->corr_whole_line)
+ val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
+
+ /* level shift value */
+ val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT;
+
+ regw(val, DFCCTL);
+
+ /* Defect saturation level */
+ regw(vdfc->def_sat_level, VDFSATLV);
+
+ regw(vdfc->table[0].pos_vert, DFCMEM0);
+ regw(vdfc->table[0].pos_horz, DFCMEM1);
+ if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ regw(vdfc->table[0].level_at_pos, DFCMEM2);
+ regw(vdfc->table[0].level_up_pixels, DFCMEM3);
+ regw(vdfc->table[0].level_low_pixels, DFCMEM4);
+ }
+
+ /* set DFCMARST and set DFCMWR */
+ val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1;
+ regw(val, DFCMEMCTL);
+
+ count = retries;
+ while (count && (regr(DFCMEMCTL) & 0x1))
+ count--;
+
+ if (!count) {
+ dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n");
+ return -1;
+ }
+
+ for (i = 1; i < vdfc->num_vdefects; i++) {
+ regw(vdfc->table[i].pos_vert, DFCMEM0);
+ regw(vdfc->table[i].pos_horz, DFCMEM1);
+ if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ regw(vdfc->table[i].level_at_pos, DFCMEM2);
+ regw(vdfc->table[i].level_up_pixels, DFCMEM3);
+ regw(vdfc->table[i].level_low_pixels, DFCMEM4);
+ }
+ val = regr(DFCMEMCTL);
+ /* clear DFCMARST and set DFCMWR */
+ val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT);
+ val |= 1;
+ regw(val, DFCMEMCTL);
+
+ count = retries;
+ while (count && (regr(DFCMEMCTL) & 0x1))
+ count--;
+
+ if (!count) {
+ dev_err(isif_cfg.dev,
+ "defect table write timeout !!!\n");
+ return -1;
+ }
+ }
+ if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) {
+ /* Extra cycle needed */
+ regw(0, DFCMEM0);
+ regw(0x1FFF, DFCMEM1);
+ regw(1, DFCMEMCTL);
+ }
+
+ /* enable VDFC */
+ reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT),
+ DFCCTL);
+ return 0;
+}
+
+static void isif_config_csc(struct isif_df_csc *df_csc)
+{
+ u32 val1 = 0, val2 = 0, i;
+
+ if (!df_csc->csc.en) {
+ regw(0, CSCCTL);
+ return;
+ }
+ for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = (df_csc->csc.coeff[i].integer <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ df_csc->csc.coeff[i].decimal;
+ } else {
+
+ /* CSCM - MSB */
+ val2 = (df_csc->csc.coeff[i].integer <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ df_csc->csc.coeff[i].decimal;
+ val2 <<= ISIF_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ regw(val2, (CSCM0 + ((i - 1) << 1)));
+ }
+ }
+
+ /* program the active area */
+ regw(df_csc->start_pix, FMTSPH);
+ /*
+ * one extra pixel as required for CSC. Actually number of
+ * pixel - 1 should be configured in this register. So we
+ * need to subtract 1 before writing to FMTSPH, but we will
+ * not do this since csc requires one extra pixel
+ */
+ regw(df_csc->num_pixels, FMTLNH);
+ regw(df_csc->start_line, FMTSLV);
+ /*
+ * one extra line as required for CSC. See reason documented for
+ * num_pixels
+ */
+ regw(df_csc->num_lines, FMTLNV);
+
+ /* Enable CSC */
+ regw(1, CSCCTL);
+}
+
+static int isif_config_raw(void)
+{
+ struct isif_params_raw *params = &isif_cfg.bayer;
+ struct isif_config_params_raw *module_params =
+ &isif_cfg.bayer.config_params;
+ struct vpss_pg_frame_size frame_size;
+ struct vpss_sync_pol sync;
+ u32 val;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n");
+
+ /*
+ * Configure CCDCFG register:-
+ * Set CCD Not to swap input since input is RAW data
+ * Set FID detection function to Latch at V-Sync
+ * Set WENLOG - isif valid area
+ * Set TRGSEL
+ * Set EXTRG
+ * Packed to 8 or 16 bits
+ */
+
+ val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
+ ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
+ ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack;
+
+ dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val);
+ regw(val, CCDCFG);
+
+ /*
+ * Configure the vertical sync polarity(MODESET.VDPOL)
+ * Configure the horizontal sync polarity (MODESET.HDPOL)
+ * Configure frame id polarity (MODESET.FLDPOL)
+ * Configure data polarity
+ * Configure External WEN Selection
+ * Configure frame format(progressive or interlace)
+ * Configure pixel format (Input mode)
+ * Configure the data shift
+ */
+
+ val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) |
+ (params->hd_pol << ISIF_HD_POL_SHIFT) |
+ (params->fid_pol << ISIF_FID_POL_SHIFT) |
+ (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) |
+ (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) |
+ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
+ (params->pix_fmt << ISIF_INPUT_SHIFT) |
+ (params->config_params.data_shift << ISIF_DATASFT_SHIFT);
+
+ regw(val, MODESET);
+ dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val);
+
+ /*
+ * Configure GAMMAWD register
+ * CFA pattern setting
+ */
+ val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT;
+
+ /* Gamma msb */
+ if (module_params->compress.alg == ISIF_ALAW)
+ val |= ISIF_ALAW_ENABLE;
+
+ val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT);
+ regw(val, CGAMMAWD);
+
+ /* Configure DPCM compression settings */
+ if (module_params->compress.alg == ISIF_DPCM) {
+ val = BIT(ISIF_DPCM_EN_SHIFT) |
+ (module_params->compress.pred <<
+ ISIF_DPCM_PREDICTOR_SHIFT);
+ }
+
+ regw(val, MISC);
+
+ /* Configure Gain & Offset */
+ isif_config_gain_offset();
+
+ /* Configure Color pattern */
+ val = (params->config_params.col_pat_field0.olop) |
+ (params->config_params.col_pat_field0.olep << 2) |
+ (params->config_params.col_pat_field0.elop << 4) |
+ (params->config_params.col_pat_field0.elep << 6) |
+ (params->config_params.col_pat_field1.olop << 8) |
+ (params->config_params.col_pat_field1.olep << 10) |
+ (params->config_params.col_pat_field1.elop << 12) |
+ (params->config_params.col_pat_field1.elep << 14);
+ regw(val, CCOLP);
+ dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val);
+
+ /* Configure HSIZE register */
+ val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT;
+
+ /* calculate line offset in 32 bytes based on pack value */
+ if (isif_cfg.data_pack == ISIF_PACK_8BIT)
+ val |= ((params->win.width + 31) >> 5);
+ else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
+ val |= (((params->win.width +
+ (params->win.width >> 2)) + 31) >> 5);
+ else
+ val |= (((params->win.width * 2) + 31) >> 5);
+ regw(val, HSIZE);
+
+ /* Configure SDOFST register */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_en) {
+ /* For interlace inverse mode */
+ regw(0x4B6D, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n");
+ } else {
+ /* For interlace non inverse mode */
+ regw(0x0B6D, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n");
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_en) {
+ /* For progressive inverse mode */
+ regw(0x4000, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n");
+ } else {
+ /* For progressive non inverse mode */
+ regw(0x0000, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n");
+ }
+ }
+
+ /* Configure video window */
+ isif_setwin(&params->win, params->frm_fmt, 1);
+
+ /* Configure Black Clamp */
+ isif_config_bclamp(&module_params->bclamp);
+
+ /* Configure Vertical Defection Pixel Correction */
+ if (isif_config_dfc(&module_params->dfc) < 0)
+ return -EFAULT;
+
+ if (!module_params->df_csc.df_or_csc)
+ /* Configure Color Space Conversion */
+ isif_config_csc(&module_params->df_csc);
+
+ isif_config_linearization(&module_params->linearize);
+
+ /* Configure Culling */
+ isif_config_culling(&module_params->culling);
+
+ /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */
+ regw(module_params->horz_offset, DATAHOFST);
+ regw(module_params->vert_offset, DATAVOFST);
+
+ /* Setup test pattern if enabled */
+ if (params->config_params.test_pat_gen) {
+ /* Use the HD/VD pol settings from user */
+ sync.ccdpg_hdpol = params->hd_pol;
+ sync.ccdpg_vdpol = params->vd_pol;
+ dm365_vpss_set_sync_pol(sync);
+ frame_size.hlpfr = isif_cfg.bayer.win.width;
+ frame_size.pplen = isif_cfg.bayer.win.height;
+ dm365_vpss_set_pg_frame_size(frame_size);
+ vpss_select_ccdc_source(VPSS_PGLPBK);
+ }
+
+ dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n");
+ return 0;
+}
+
+static int isif_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ isif_cfg.bayer.buf_type = buf_type;
+ else
+ isif_cfg.ycbcr.buf_type = buf_type;
+
+ return 0;
+
+}
+static enum ccdc_buftype isif_get_buftype(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_cfg.bayer.buf_type;
+
+ return isif_cfg.ycbcr.buf_type;
+}
+
+static int isif_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) {
+ *pix = isif_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) {
+ *pix = isif_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+static int isif_set_pixel_format(unsigned int pixfmt)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8) {
+ if ((isif_cfg.bayer.config_params.compress.alg !=
+ ISIF_ALAW) &&
+ (isif_cfg.bayer.config_params.compress.alg !=
+ ISIF_DPCM)) {
+ dev_dbg(isif_cfg.dev,
+ "Either configure A-Law or DPCM\n");
+ return -EINVAL;
+ }
+ isif_cfg.data_pack = ISIF_PACK_8BIT;
+ } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) {
+ isif_cfg.bayer.config_params.compress.alg =
+ ISIF_NO_COMPRESSION;
+ isif_cfg.data_pack = ISIF_PACK_16BIT;
+ } else
+ return -EINVAL;
+ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ isif_cfg.data_pack = ISIF_PACK_8BIT;
+ }
+ return 0;
+}
+
+static u32 isif_get_pixel_format(void)
+{
+ u32 pixfmt;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW ||
+ isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+
+static int isif_set_image_window(struct v4l2_rect *win)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ isif_cfg.bayer.win.top = win->top;
+ isif_cfg.bayer.win.left = win->left;
+ isif_cfg.bayer.win.width = win->width;
+ isif_cfg.bayer.win.height = win->height;
+ } else {
+ isif_cfg.ycbcr.win.top = win->top;
+ isif_cfg.ycbcr.win.left = win->left;
+ isif_cfg.ycbcr.win.width = win->width;
+ isif_cfg.ycbcr.win.height = win->height;
+ }
+ return 0;
+}
+
+static void isif_get_image_window(struct v4l2_rect *win)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ *win = isif_cfg.bayer.win;
+ else
+ *win = isif_cfg.ycbcr.win;
+}
+
+static unsigned int isif_get_line_length(void)
+{
+ unsigned int len;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (isif_cfg.data_pack == ISIF_PACK_8BIT)
+ len = ((isif_cfg.bayer.win.width));
+ else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
+ len = (((isif_cfg.bayer.win.width * 2) +
+ (isif_cfg.bayer.win.width >> 2)));
+ else
+ len = (((isif_cfg.bayer.win.width * 2)));
+ } else
+ len = (((isif_cfg.ycbcr.win.width * 2)));
+ return ALIGN(len, 32);
+}
+
+static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ isif_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ isif_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+static enum ccdc_frmfmt isif_get_frame_format(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_cfg.bayer.frm_fmt;
+ return isif_cfg.ycbcr.frm_fmt;
+}
+
+static int isif_getfid(void)
+{
+ return (regr(MODESET) >> 15) & 0x1;
+}
+
+/* misc operations */
+static void isif_setfbaddr(unsigned long addr)
+{
+ regw((addr >> 21) & 0x07ff, CADU);
+ regw((addr >> 5) & 0x0ffff, CADL);
+}
+
+static int isif_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ isif_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_BT656_10BIT:
+ case VPFE_YCBCR_SYNC_8:
+ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ break;
+ case VPFE_BT1120:
+ case VPFE_YCBCR_SYNC_16:
+ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT;
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ break;
+ case VPFE_RAW_BAYER:
+ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ break;
+ default:
+ dev_dbg(isif_cfg.dev, "Invalid interface type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* This function will configure ISIF for YCbCr parameters. */
+static int isif_config_ycbcr(void)
+{
+ struct isif_ycbcr_config *params = &isif_cfg.ycbcr;
+ struct vpss_pg_frame_size frame_size;
+ u32 modeset = 0, ccdcfg = 0;
+ struct vpss_sync_pol sync;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr...");
+
+ /* configure pixel format or input mode */
+ modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) |
+ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
+ (params->fid_pol << ISIF_FID_POL_SHIFT) |
+ (params->hd_pol << ISIF_HD_POL_SHIFT) |
+ (params->vd_pol << ISIF_VD_POL_SHIFT);
+
+ /* pack the data to 8-bit ISIFCFG */
+ switch (isif_cfg.if_type) {
+ case VPFE_BT656:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT);
+ regw(3, REC656IF);
+ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR;
+ break;
+ case VPFE_BT656_10BIT:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* setup BT.656, embedded sync */
+ regw(3, REC656IF);
+ /* enable 10 bit mode in ccdcfg */
+ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR |
+ ISIF_BW656_ENABLE;
+ break;
+ case VPFE_BT1120:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ regw(3, REC656IF);
+ break;
+
+ case VPFE_YCBCR_SYNC_8:
+ ccdcfg |= ISIF_DATA_PACK8;
+ ccdcfg |= ISIF_YCINSWP_YCBCR;
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+ case VPFE_YCBCR_SYNC_16:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ /* should never come here */
+ dev_dbg(isif_cfg.dev, "Invalid interface type\n");
+ return -EINVAL;
+ }
+
+ regw(modeset, MODESET);
+
+ /* Set up pix order */
+ ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT;
+
+ regw(ccdcfg, CCDCFG);
+
+ /* configure video window */
+ if ((isif_cfg.if_type == VPFE_BT1120) ||
+ (isif_cfg.if_type == VPFE_YCBCR_SYNC_16))
+ isif_setwin(&params->win, params->frm_fmt, 1);
+ else
+ isif_setwin(&params->win, params->frm_fmt, 2);
+
+ /*
+ * configure the horizontal line offset
+ * this is done by rounding up width to a multiple of 16 pixels
+ * and multiply by two to account for y:cb:cr 4:2:2 data
+ */
+ regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) &&
+ (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED))
+ /* two fields are interleaved in memory */
+ regw(0x00000249, SDOFST);
+
+ /* Setup test pattern if enabled */
+ if (isif_cfg.bayer.config_params.test_pat_gen) {
+ sync.ccdpg_hdpol = params->hd_pol;
+ sync.ccdpg_vdpol = params->vd_pol;
+ dm365_vpss_set_sync_pol(sync);
+ dm365_vpss_set_pg_frame_size(frame_size);
+ }
+ return 0;
+}
+
+static int isif_configure(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_config_raw();
+ return isif_config_ycbcr();
+}
+
+static int isif_close(struct device *device)
+{
+ /* copy defaults to module params */
+ isif_cfg.bayer.config_params = isif_config_defaults;
+ return 0;
+}
+
+static struct ccdc_hw_device isif_hw_dev = {
+ .name = "ISIF",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = isif_open,
+ .close = isif_close,
+ .enable = isif_enable,
+ .enable_out_to_sdram = isif_enable_output_to_sdram,
+ .set_hw_if_params = isif_set_hw_if_params,
+ .configure = isif_configure,
+ .set_buftype = isif_set_buftype,
+ .get_buftype = isif_get_buftype,
+ .enum_pix = isif_enum_pix,
+ .set_pixel_format = isif_set_pixel_format,
+ .get_pixel_format = isif_get_pixel_format,
+ .set_frame_format = isif_set_frame_format,
+ .get_frame_format = isif_get_frame_format,
+ .set_image_window = isif_set_image_window,
+ .get_image_window = isif_get_image_window,
+ .get_line_length = isif_get_line_length,
+ .setfbaddr = isif_setfbaddr,
+ .getfid = isif_getfid,
+ },
+};
+
+static int __init isif_probe(struct platform_device *pdev)
+{
+ void (*setup_pinmux)(void);
+ struct resource *res;
+ void *__iomem addr;
+ int status = 0, i;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&isif_hw_dev);
+ if (status < 0)
+ return status;
+
+ /* Get and enable Master clock */
+ isif_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(isif_cfg.mclk)) {
+ status = PTR_ERR(isif_cfg.mclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(isif_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Platform data holds setup_pinmux function ptr */
+ if (NULL == pdev->dev.platform_data) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+ setup_pinmux = pdev->dev.platform_data;
+ /*
+ * setup Mux configuration for ccdc which may be different for
+ * different SoCs using this CCDC
+ */
+ setup_pinmux();
+
+ i = 0;
+ /* Get the ISIF base address, linearization table0 and table1 addr. */
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nobase_res;
+ }
+ res = request_mem_region(res->start, resource_size(res),
+ res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nobase_res;
+ }
+ addr = ioremap_nocache(res->start, resource_size(res));
+ if (!addr) {
+ status = -ENOMEM;
+ goto fail_base_iomap;
+ }
+ switch (i) {
+ case 0:
+ /* ISIF base address */
+ isif_cfg.base_addr = addr;
+ break;
+ case 1:
+ /* ISIF linear tbl0 address */
+ isif_cfg.linear_tbl0_addr = addr;
+ break;
+ default:
+ /* ISIF linear tbl0 address */
+ isif_cfg.linear_tbl1_addr = addr;
+ break;
+ }
+ i++;
+ }
+ isif_cfg.dev = &pdev->dev;
+
+ printk(KERN_NOTICE "%s is registered with vpfe.\n",
+ isif_hw_dev.name);
+ return 0;
+fail_base_iomap:
+ release_mem_region(res->start, resource_size(res));
+ i--;
+fail_nobase_res:
+ if (isif_cfg.base_addr)
+ iounmap(isif_cfg.base_addr);
+ if (isif_cfg.linear_tbl0_addr)
+ iounmap(isif_cfg.linear_tbl0_addr);
+
+ while (i >= 0) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ release_mem_region(res->start, resource_size(res));
+ i--;
+ }
+fail_mclk:
+ clk_put(isif_cfg.mclk);
+ vpfe_unregister_ccdc_device(&isif_hw_dev);
+ return status;
+}
+
+static int isif_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ int i = 0;
+
+ iounmap(isif_cfg.base_addr);
+ iounmap(isif_cfg.linear_tbl0_addr);
+ iounmap(isif_cfg.linear_tbl1_addr);
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ i++;
+ }
+ vpfe_unregister_ccdc_device(&isif_hw_dev);
+ return 0;
+}
+
+static struct platform_driver isif_driver = {
+ .driver = {
+ .name = "isif",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(isif_remove),
+ .probe = isif_probe,
+};
+
+static int __init isif_init(void)
+{
+ return platform_driver_register(&isif_driver);
+}
+
+static void isif_exit(void)
+{
+ platform_driver_unregister(&isif_driver);
+}
+
+module_init(isif_init);
+module_exit(isif_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/video/davinci/isif_regs.h
new file mode 100644
index 00000000000..f7b8893a295
--- /dev/null
+++ b/drivers/media/video/davinci/isif_regs.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _ISIF_REGS_H
+#define _ISIF_REGS_H
+
+/* ISIF registers relative offsets */
+#define SYNCEN 0x00
+#define MODESET 0x04
+#define HDW 0x08
+#define VDW 0x0c
+#define PPLN 0x10
+#define LPFR 0x14
+#define SPH 0x18
+#define LNH 0x1c
+#define SLV0 0x20
+#define SLV1 0x24
+#define LNV 0x28
+#define CULH 0x2c
+#define CULV 0x30
+#define HSIZE 0x34
+#define SDOFST 0x38
+#define CADU 0x3c
+#define CADL 0x40
+#define LINCFG0 0x44
+#define LINCFG1 0x48
+#define CCOLP 0x4c
+#define CRGAIN 0x50
+#define CGRGAIN 0x54
+#define CGBGAIN 0x58
+#define CBGAIN 0x5c
+#define COFSTA 0x60
+#define FLSHCFG0 0x64
+#define FLSHCFG1 0x68
+#define FLSHCFG2 0x6c
+#define VDINT0 0x70
+#define VDINT1 0x74
+#define VDINT2 0x78
+#define MISC 0x7c
+#define CGAMMAWD 0x80
+#define REC656IF 0x84
+#define CCDCFG 0x88
+/*****************************************************
+* Defect Correction registers
+*****************************************************/
+#define DFCCTL 0x8c
+#define VDFSATLV 0x90
+#define DFCMEMCTL 0x94
+#define DFCMEM0 0x98
+#define DFCMEM1 0x9c
+#define DFCMEM2 0xa0
+#define DFCMEM3 0xa4
+#define DFCMEM4 0xa8
+/****************************************************
+* Black Clamp registers
+****************************************************/
+#define CLAMPCFG 0xac
+#define CLDCOFST 0xb0
+#define CLSV 0xb4
+#define CLHWIN0 0xb8
+#define CLHWIN1 0xbc
+#define CLHWIN2 0xc0
+#define CLVRV 0xc4
+#define CLVWIN0 0xc8
+#define CLVWIN1 0xcc
+#define CLVWIN2 0xd0
+#define CLVWIN3 0xd4
+/****************************************************
+* Lense Shading Correction
+****************************************************/
+#define DATAHOFST 0xd8
+#define DATAVOFST 0xdc
+#define LSCHVAL 0xe0
+#define LSCVVAL 0xe4
+#define TWODLSCCFG 0xe8
+#define TWODLSCOFST 0xec
+#define TWODLSCINI 0xf0
+#define TWODLSCGRBU 0xf4
+#define TWODLSCGRBL 0xf8
+#define TWODLSCGROF 0xfc
+#define TWODLSCORBU 0x100
+#define TWODLSCORBL 0x104
+#define TWODLSCOROF 0x108
+#define TWODLSCIRQEN 0x10c
+#define TWODLSCIRQST 0x110
+/****************************************************
+* Data formatter
+****************************************************/
+#define FMTCFG 0x114
+#define FMTPLEN 0x118
+#define FMTSPH 0x11c
+#define FMTLNH 0x120
+#define FMTSLV 0x124
+#define FMTLNV 0x128
+#define FMTRLEN 0x12c
+#define FMTHCNT 0x130
+#define FMTAPTR_BASE 0x134
+/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */
+#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4))
+#define FMTPGMVF0 0x174
+#define FMTPGMVF1 0x178
+#define FMTPGMAPU0 0x17c
+#define FMTPGMAPU1 0x180
+#define FMTPGMAPS0 0x184
+#define FMTPGMAPS1 0x188
+#define FMTPGMAPS2 0x18c
+#define FMTPGMAPS3 0x190
+#define FMTPGMAPS4 0x194
+#define FMTPGMAPS5 0x198
+#define FMTPGMAPS6 0x19c
+#define FMTPGMAPS7 0x1a0
+/************************************************
+* Color Space Converter
+************************************************/
+#define CSCCTL 0x1a4
+#define CSCM0 0x1a8
+#define CSCM1 0x1ac
+#define CSCM2 0x1b0
+#define CSCM3 0x1b4
+#define CSCM4 0x1b8
+#define CSCM5 0x1bc
+#define CSCM6 0x1c0
+#define CSCM7 0x1c4
+#define OBWIN0 0x1c8
+#define OBWIN1 0x1cc
+#define OBWIN2 0x1d0
+#define OBWIN3 0x1d4
+#define OBVAL0 0x1d8
+#define OBVAL1 0x1dc
+#define OBVAL2 0x1e0
+#define OBVAL3 0x1e4
+#define OBVAL4 0x1e8
+#define OBVAL5 0x1ec
+#define OBVAL6 0x1f0
+#define OBVAL7 0x1f4
+#define CLKCTL 0x1f8
+
+/* Masks & Shifts below */
+#define START_PX_HOR_MASK 0x7FFF
+#define NUM_PX_HOR_MASK 0x7FFF
+#define START_VER_ONE_MASK 0x7FFF
+#define START_VER_TWO_MASK 0x7FFF
+#define NUM_LINES_VER 0x7FFF
+
+/* gain - offset masks */
+#define GAIN_INTEGER_SHIFT 9
+#define OFFSET_MASK 0xFFF
+#define GAIN_SDRAM_EN_SHIFT 12
+#define GAIN_IPIPE_EN_SHIFT 13
+#define GAIN_H3A_EN_SHIFT 14
+#define OFST_SDRAM_EN_SHIFT 8
+#define OFST_IPIPE_EN_SHIFT 9
+#define OFST_H3A_EN_SHIFT 10
+#define GAIN_OFFSET_EN_MASK 0x7700
+
+/* Culling */
+#define CULL_PAT_EVEN_LINE_SHIFT 8
+
+/* CCDCFG register */
+#define ISIF_YCINSWP_RAW (0x00 << 4)
+#define ISIF_YCINSWP_YCBCR (0x01 << 4)
+#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6)
+#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8)
+#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9)
+#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10)
+#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15)
+#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15)
+#define ISIF_DATA_PACK_MASK 3
+#define ISIF_DATA_PACK16 0
+#define ISIF_DATA_PACK12 1
+#define ISIF_DATA_PACK8 2
+#define ISIF_PIX_ORDER_SHIFT 11
+#define ISIF_BW656_ENABLE (0x01 << 5)
+
+/* MODESET registers */
+#define ISIF_VDHDOUT_INPUT (0x00 << 0)
+#define ISIF_INPUT_SHIFT 12
+#define ISIF_RAW_INPUT_MODE 0
+#define ISIF_FID_POL_SHIFT 4
+#define ISIF_HD_POL_SHIFT 3
+#define ISIF_VD_POL_SHIFT 2
+#define ISIF_DATAPOL_NORMAL 0
+#define ISIF_DATAPOL_SHIFT 6
+#define ISIF_EXWEN_DISABLE 0
+#define ISIF_EXWEN_SHIFT 5
+#define ISIF_FRM_FMT_SHIFT 7
+#define ISIF_DATASFT_SHIFT 8
+#define ISIF_LPF_SHIFT 14
+#define ISIF_LPF_MASK 1
+
+/* GAMMAWD registers */
+#define ISIF_ALAW_GAMA_WD_MASK 0xF
+#define ISIF_ALAW_GAMA_WD_SHIFT 1
+#define ISIF_ALAW_ENABLE 1
+#define ISIF_GAMMAWD_CFA_SHIFT 5
+
+/* HSIZE registers */
+#define ISIF_HSIZE_FLIP_MASK 1
+#define ISIF_HSIZE_FLIP_SHIFT 12
+
+/* MISC registers */
+#define ISIF_DPCM_EN_SHIFT 12
+#define ISIF_DPCM_PREDICTOR_SHIFT 13
+
+/* Black clamp related */
+#define ISIF_BC_MODE_COLOR_SHIFT 4
+#define ISIF_HORZ_BC_MODE_SHIFT 1
+#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5
+#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6
+#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8
+#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12
+#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4
+#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8
+
+/* VDFC registers */
+#define ISIF_VDFC_EN_SHIFT 4
+#define ISIF_VDFC_CORR_MOD_SHIFT 5
+#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7
+#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8
+#define ISIF_VDFC_POS_MASK 0x1FFF
+#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2
+
+/* CSC registers */
+#define ISIF_CSC_COEF_INTEG_MASK 7
+#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f
+#define ISIF_CSC_COEF_INTEG_SHIFT 5
+#define ISIF_CSCM_MSB_SHIFT 8
+#define ISIF_DF_CSC_SPH_MASK 0x1FFF
+#define ISIF_DF_CSC_LNH_MASK 0x1FFF
+#define ISIF_DF_CSC_SLV_MASK 0x1FFF
+#define ISIF_DF_CSC_LNV_MASK 0x1FFF
+#define ISIF_DF_NUMLINES 0x7FFF
+#define ISIF_DF_NUMPIX 0x1FFF
+
+/* Offsets for LSC/DFC/Gain */
+#define ISIF_DATA_H_OFFSET_MASK 0x1FFF
+#define ISIF_DATA_V_OFFSET_MASK 0x1FFF
+
+/* Linearization */
+#define ISIF_LIN_CORRSFT_SHIFT 4
+#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10
+
+
+/* Pattern registers */
+#define ISIF_PG_EN (1 << 3)
+#define ISIF_SEL_PG_SRC (3 << 4)
+#define ISIF_PG_VD_POL_SHIFT 0
+#define ISIF_PG_HD_POL_SHIFT 1
+
+/*random other junk*/
+#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0)
+#define ISIF_SYNCEN_WEN_MASK (1 << 1)
+#define ISIF_SYNCEN_WEN_SHIFT 1
+
+#endif
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
index de22bc9faf2..885cd54499c 100644
--- a/drivers/media/video/davinci/vpfe_capture.c
+++ b/drivers/media/video/davinci/vpfe_capture.c
@@ -107,9 +107,6 @@ struct ccdc_config {
int vpfe_probed;
/* name of ccdc device */
char name[32];
- /* for storing mem maps for CCDC */
- int ccdc_addr_size;
- void *__iomem ccdc_addr;
};
/* data structures */
@@ -229,7 +226,6 @@ int vpfe_register_ccdc_device(struct ccdc_hw_device *dev)
BUG_ON(!dev->hw_ops.set_image_window);
BUG_ON(!dev->hw_ops.get_image_window);
BUG_ON(!dev->hw_ops.get_line_length);
- BUG_ON(!dev->hw_ops.setfbaddr);
BUG_ON(!dev->hw_ops.getfid);
mutex_lock(&ccdc_lock);
@@ -240,25 +236,23 @@ int vpfe_register_ccdc_device(struct ccdc_hw_device *dev)
* walk through it during vpfe probe
*/
printk(KERN_ERR "vpfe capture not initialized\n");
- ret = -1;
+ ret = -EFAULT;
goto unlock;
}
if (strcmp(dev->name, ccdc_cfg->name)) {
/* ignore this ccdc */
- ret = -1;
+ ret = -EINVAL;
goto unlock;
}
if (ccdc_dev) {
printk(KERN_ERR "ccdc already registered\n");
- ret = -1;
+ ret = -EINVAL;
goto unlock;
}
ccdc_dev = dev;
- dev->hw_ops.set_ccdc_base(ccdc_cfg->ccdc_addr,
- ccdc_cfg->ccdc_addr_size);
unlock:
mutex_unlock(&ccdc_lock);
return ret;
@@ -1786,61 +1780,6 @@ static struct vpfe_device *vpfe_initialize(void)
return vpfe_dev;
}
-static void vpfe_disable_clock(struct vpfe_device *vpfe_dev)
-{
- struct vpfe_config *vpfe_cfg = vpfe_dev->cfg;
-
- clk_disable(vpfe_cfg->vpssclk);
- clk_put(vpfe_cfg->vpssclk);
- clk_disable(vpfe_cfg->slaveclk);
- clk_put(vpfe_cfg->slaveclk);
- v4l2_info(vpfe_dev->pdev->driver,
- "vpfe vpss master & slave clocks disabled\n");
-}
-
-static int vpfe_enable_clock(struct vpfe_device *vpfe_dev)
-{
- struct vpfe_config *vpfe_cfg = vpfe_dev->cfg;
- int ret = -ENOENT;
-
- vpfe_cfg->vpssclk = clk_get(vpfe_dev->pdev, "vpss_master");
- if (NULL == vpfe_cfg->vpssclk) {
- v4l2_err(vpfe_dev->pdev->driver, "No clock defined for"
- "vpss_master\n");
- return ret;
- }
-
- if (clk_enable(vpfe_cfg->vpssclk)) {
- v4l2_err(vpfe_dev->pdev->driver,
- "vpfe vpss master clock not enabled\n");
- goto out;
- }
- v4l2_info(vpfe_dev->pdev->driver,
- "vpfe vpss master clock enabled\n");
-
- vpfe_cfg->slaveclk = clk_get(vpfe_dev->pdev, "vpss_slave");
- if (NULL == vpfe_cfg->slaveclk) {
- v4l2_err(vpfe_dev->pdev->driver,
- "No clock defined for vpss slave\n");
- goto out;
- }
-
- if (clk_enable(vpfe_cfg->slaveclk)) {
- v4l2_err(vpfe_dev->pdev->driver,
- "vpfe vpss slave clock not enabled\n");
- goto out;
- }
- v4l2_info(vpfe_dev->pdev->driver, "vpfe vpss slave clock enabled\n");
- return 0;
-out:
- if (vpfe_cfg->vpssclk)
- clk_put(vpfe_cfg->vpssclk);
- if (vpfe_cfg->slaveclk)
- clk_put(vpfe_cfg->slaveclk);
-
- return -1;
-}
-
/*
* vpfe_probe : This function creates device entries by register
* itself to the V4L2 driver and initializes fields of each
@@ -1870,7 +1809,7 @@ static __init int vpfe_probe(struct platform_device *pdev)
if (NULL == pdev->dev.platform_data) {
v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n");
- ret = -ENOENT;
+ ret = -ENODEV;
goto probe_free_dev_mem;
}
@@ -1884,18 +1823,13 @@ static __init int vpfe_probe(struct platform_device *pdev)
goto probe_free_dev_mem;
}
- /* enable vpss clocks */
- ret = vpfe_enable_clock(vpfe_dev);
- if (ret)
- goto probe_free_dev_mem;
-
mutex_lock(&ccdc_lock);
/* Allocate memory for ccdc configuration */
ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL);
if (NULL == ccdc_cfg) {
v4l2_err(pdev->dev.driver,
"Memory allocation failed for ccdc_cfg\n");
- goto probe_disable_clock;
+ goto probe_free_dev_mem;
}
strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32);
@@ -1904,61 +1838,34 @@ static __init int vpfe_probe(struct platform_device *pdev)
if (!res1) {
v4l2_err(pdev->dev.driver,
"Unable to get interrupt for VINT0\n");
- ret = -ENOENT;
- goto probe_disable_clock;
+ ret = -ENODEV;
+ goto probe_free_ccdc_cfg_mem;
}
vpfe_dev->ccdc_irq0 = res1->start;
/* Get VINT1 irq resource */
- res1 = platform_get_resource(pdev,
- IORESOURCE_IRQ, 1);
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!res1) {
v4l2_err(pdev->dev.driver,
"Unable to get interrupt for VINT1\n");
- ret = -ENOENT;
- goto probe_disable_clock;
+ ret = -ENODEV;
+ goto probe_free_ccdc_cfg_mem;
}
vpfe_dev->ccdc_irq1 = res1->start;
- /* Get address base of CCDC */
- res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res1) {
- v4l2_err(pdev->dev.driver,
- "Unable to get register address map\n");
- ret = -ENOENT;
- goto probe_disable_clock;
- }
-
- ccdc_cfg->ccdc_addr_size = res1->end - res1->start + 1;
- if (!request_mem_region(res1->start, ccdc_cfg->ccdc_addr_size,
- pdev->dev.driver->name)) {
- v4l2_err(pdev->dev.driver,
- "Failed request_mem_region for ccdc base\n");
- ret = -ENXIO;
- goto probe_disable_clock;
- }
- ccdc_cfg->ccdc_addr = ioremap_nocache(res1->start,
- ccdc_cfg->ccdc_addr_size);
- if (!ccdc_cfg->ccdc_addr) {
- v4l2_err(pdev->dev.driver, "Unable to ioremap ccdc addr\n");
- ret = -ENXIO;
- goto probe_out_release_mem1;
- }
-
ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
"vpfe_capture0", vpfe_dev);
if (0 != ret) {
v4l2_err(pdev->dev.driver, "Unable to request interrupt\n");
- goto probe_out_unmap1;
+ goto probe_free_ccdc_cfg_mem;
}
/* Allocate memory for video device */
vfd = video_device_alloc();
if (NULL == vfd) {
ret = -ENOMEM;
- v4l2_err(pdev->dev.driver,
- "Unable to alloc video device\n");
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
goto probe_out_release_irq;
}
@@ -2073,12 +1980,7 @@ probe_out_video_release:
video_device_release(vpfe_dev->video_dev);
probe_out_release_irq:
free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
-probe_out_unmap1:
- iounmap(ccdc_cfg->ccdc_addr);
-probe_out_release_mem1:
- release_mem_region(res1->start, res1->end - res1->start + 1);
-probe_disable_clock:
- vpfe_disable_clock(vpfe_dev);
+probe_free_ccdc_cfg_mem:
mutex_unlock(&ccdc_lock);
kfree(ccdc_cfg);
probe_free_dev_mem:
@@ -2092,7 +1994,6 @@ probe_free_dev_mem:
static int __devexit vpfe_remove(struct platform_device *pdev)
{
struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
- struct resource *res;
v4l2_info(pdev->dev.driver, "vpfe_remove\n");
@@ -2100,12 +2001,6 @@ static int __devexit vpfe_remove(struct platform_device *pdev)
kfree(vpfe_dev->sd);
v4l2_device_unregister(&vpfe_dev->v4l2_dev);
video_unregister_device(vpfe_dev->video_dev);
- mutex_lock(&ccdc_lock);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
- iounmap(ccdc_cfg->ccdc_addr);
- mutex_unlock(&ccdc_lock);
- vpfe_disable_clock(vpfe_dev);
kfree(vpfe_dev);
kfree(ccdc_cfg);
return 0;
diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c
index 7ee72ecd3d8..7918680917d 100644
--- a/drivers/media/video/davinci/vpss.c
+++ b/drivers/media/video/davinci/vpss.c
@@ -15,7 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * common vpss driver for all video drivers.
+ * common vpss system module platform driver for all video drivers.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -35,12 +35,52 @@ MODULE_AUTHOR("Texas Instruments");
/* DM644x defines */
#define DM644X_SBL_PCR_VPSS (4)
+#define DM355_VPSSBL_INTSEL 0x10
+#define DM355_VPSSBL_EVTSEL 0x14
/* vpss BL register offsets */
#define DM355_VPSSBL_CCDCMUX 0x1c
/* vpss CLK register offsets */
#define DM355_VPSSCLK_CLKCTRL 0x04
/* masks and shifts */
#define VPSS_HSSISEL_SHIFT 4
+/*
+ * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4,
+ * IPIPE_INT1_SDR - vpss_int5
+ */
+#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10
+/* VENCINT - vpss_int8 */
+#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
+
+#define DM365_ISP5_PCCR 0x04
+#define DM365_ISP5_INTSEL1 0x10
+#define DM365_ISP5_INTSEL2 0x14
+#define DM365_ISP5_INTSEL3 0x18
+#define DM365_ISP5_CCDCMUX 0x20
+#define DM365_ISP5_PG_FRAME_SIZE 0x28
+#define DM365_VPBE_CLK_CTRL 0x00
+/*
+ * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
+ * AF - vpss_int3
+ */
+#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100
+/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */
+#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f
+/* VENC - vpss_int8 */
+#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015
+
+/* masks and shifts for DM365*/
+#define DM365_CCDC_PG_VD_POL_SHIFT 0
+#define DM365_CCDC_PG_HD_POL_SHIFT 1
+
+#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4))
+#define CCD_SRC_SEL_SHIFT 4
+
+/* Different SoC platforms supported by this driver */
+enum vpss_platform_type {
+ DM644X,
+ DM355,
+ DM365,
+};
/*
* vpss operations. Depends on platform. Not all functions are available
@@ -59,13 +99,9 @@ struct vpss_hw_ops {
/* vpss configuration */
struct vpss_oper_config {
- __iomem void *vpss_bl_regs_base;
- __iomem void *vpss_regs_base;
- struct resource *r1;
- resource_size_t len1;
- struct resource *r2;
- resource_size_t len2;
- char vpss_name[32];
+ __iomem void *vpss_regs_base0;
+ __iomem void *vpss_regs_base1;
+ enum vpss_platform_type platform;
spinlock_t vpss_lock;
struct vpss_hw_ops hw_ops;
};
@@ -75,22 +111,46 @@ static struct vpss_oper_config oper_cfg;
/* register access routines */
static inline u32 bl_regr(u32 offset)
{
- return __raw_readl(oper_cfg.vpss_bl_regs_base + offset);
+ return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
}
static inline void bl_regw(u32 val, u32 offset)
{
- __raw_writel(val, oper_cfg.vpss_bl_regs_base + offset);
+ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
}
static inline u32 vpss_regr(u32 offset)
{
- return __raw_readl(oper_cfg.vpss_regs_base + offset);
+ return __raw_readl(oper_cfg.vpss_regs_base1 + offset);
}
static inline void vpss_regw(u32 val, u32 offset)
{
- __raw_writel(val, oper_cfg.vpss_regs_base + offset);
+ __raw_writel(val, oper_cfg.vpss_regs_base1 + offset);
+}
+
+/* For DM365 only */
+static inline u32 isp5_read(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
+}
+
+/* For DM365 only */
+static inline void isp5_write(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
+}
+
+static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK;
+
+ /* if we are using pattern generator, enable it */
+ if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG)
+ temp |= 0x08;
+
+ temp |= (src_sel << CCD_SRC_SEL_SHIFT);
+ isp5_write(temp, DM365_ISP5_CCDCMUX);
}
static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
@@ -101,9 +161,9 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
if (!oper_cfg.hw_ops.select_ccdc_source)
- return -1;
+ return -EINVAL;
- dm355_select_ccdc_source(src_sel);
+ oper_cfg.hw_ops.select_ccdc_source(src_sel);
return 0;
}
EXPORT_SYMBOL(vpss_select_ccdc_source);
@@ -114,7 +174,7 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
wbl_sel > VPSS_PCR_CCDC_WBL_O)
- return -1;
+ return -EINVAL;
/* writing a 0 clear the overflow */
mask = ~(mask << wbl_sel);
@@ -126,7 +186,7 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
{
if (!oper_cfg.hw_ops.clear_wbl_overflow)
- return -1;
+ return -EINVAL;
return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
}
@@ -166,7 +226,7 @@ static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
default:
printk(KERN_ERR "dm355_enable_clock:"
" Invalid selector: %d\n", clock_sel);
- return -1;
+ return -EINVAL;
}
spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
@@ -181,100 +241,221 @@ static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
return 0;
}
+static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ unsigned long flags;
+ u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR;
+ u32 (*read)(u32 offset) = isp5_read;
+ void(*write)(u32 val, u32 offset) = isp5_write;
+
+ switch (clock_sel) {
+ case VPSS_BL_CLOCK:
+ break;
+ case VPSS_CCDC_CLOCK:
+ shift = 1;
+ break;
+ case VPSS_H3A_CLOCK:
+ shift = 2;
+ break;
+ case VPSS_RSZ_CLOCK:
+ shift = 3;
+ break;
+ case VPSS_IPIPE_CLOCK:
+ shift = 4;
+ break;
+ case VPSS_IPIPEIF_CLOCK:
+ shift = 5;
+ break;
+ case VPSS_PCLK_INTERNAL:
+ shift = 6;
+ break;
+ case VPSS_PSYNC_CLOCK_SEL:
+ shift = 7;
+ break;
+ case VPSS_VPBE_CLOCK:
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_VENC_CLOCK_SEL:
+ shift = 2;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_LDC_CLOCK:
+ shift = 3;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_FDIF_CLOCK:
+ shift = 4;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_OSD_CLOCK_SEL:
+ shift = 6;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_LDC_CLOCK_SEL:
+ shift = 7;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ default:
+ printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n",
+ clock_sel);
+ return -1;
+ }
+
+ spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
+ utemp = read(offset);
+ if (!en) {
+ mask = ~mask;
+ utemp &= (mask << shift);
+ } else
+ utemp |= (mask << shift);
+
+ write(utemp, offset);
+ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
+
+ return 0;
+}
+
int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
{
if (!oper_cfg.hw_ops.enable_clock)
- return -1;
+ return -EINVAL;
return oper_cfg.hw_ops.enable_clock(clock_sel, en);
}
EXPORT_SYMBOL(vpss_enable_clock);
+void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
+{
+ int val = 0;
+ val = isp5_read(DM365_ISP5_CCDCMUX);
+
+ val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT);
+ val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT);
+
+ isp5_write(val, DM365_ISP5_CCDCMUX);
+}
+EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
+
+void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
+{
+ int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
+
+ current_reg |= (frame_size.pplen - 1);
+ isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE);
+}
+EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
+
static int __init vpss_probe(struct platform_device *pdev)
{
- int status, dm355 = 0;
+ struct resource *r1, *r2;
+ char *platform_name;
+ int status;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data\n");
return -ENOENT;
}
- strcpy(oper_cfg.vpss_name, pdev->dev.platform_data);
- if (!strcmp(oper_cfg.vpss_name, "dm355_vpss"))
- dm355 = 1;
- else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) {
+ platform_name = pdev->dev.platform_data;
+ if (!strcmp(platform_name, "dm355_vpss"))
+ oper_cfg.platform = DM355;
+ else if (!strcmp(platform_name, "dm365_vpss"))
+ oper_cfg.platform = DM365;
+ else if (!strcmp(platform_name, "dm644x_vpss"))
+ oper_cfg.platform = DM644X;
+ else {
dev_err(&pdev->dev, "vpss driver not supported on"
" this platform\n");
return -ENODEV;
}
- dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name);
- oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!oper_cfg.r1)
+ dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
+ r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r1)
return -ENOENT;
- oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1;
-
- oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1,
- oper_cfg.r1->name);
- if (!oper_cfg.r1)
+ r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
+ if (!r1)
return -EBUSY;
- oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1);
- if (!oper_cfg.vpss_bl_regs_base) {
+ oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
+ if (!oper_cfg.vpss_regs_base0) {
status = -EBUSY;
goto fail1;
}
- if (dm355) {
- oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!oper_cfg.r2) {
+ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
+ r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r2) {
status = -ENOENT;
goto fail2;
}
- oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1;
- oper_cfg.r2 = request_mem_region(oper_cfg.r2->start,
- oper_cfg.len2,
- oper_cfg.r2->name);
- if (!oper_cfg.r2) {
+ r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
+ if (!r2) {
status = -EBUSY;
goto fail2;
}
- oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start,
- oper_cfg.len2);
- if (!oper_cfg.vpss_regs_base) {
+ oper_cfg.vpss_regs_base1 = ioremap(r2->start,
+ resource_size(r2));
+ if (!oper_cfg.vpss_regs_base1) {
status = -EBUSY;
goto fail3;
}
}
- if (dm355) {
+ if (oper_cfg.platform == DM355) {
oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
+ /* Setup vpss interrupts */
+ bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
+ bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
+ } else if (oper_cfg.platform == DM365) {
+ oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
+ oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
+ /* Setup vpss interrupts */
+ isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
+ isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
+ isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
} else
oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
spin_lock_init(&oper_cfg.vpss_lock);
- dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name);
+ dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
return 0;
fail3:
- release_mem_region(oper_cfg.r2->start, oper_cfg.len2);
+ release_mem_region(r2->start, resource_size(r2));
fail2:
- iounmap(oper_cfg.vpss_bl_regs_base);
+ iounmap(oper_cfg.vpss_regs_base0);
fail1:
- release_mem_region(oper_cfg.r1->start, oper_cfg.len1);
+ release_mem_region(r1->start, resource_size(r1));
return status;
}
static int __devexit vpss_remove(struct platform_device *pdev)
{
- iounmap(oper_cfg.vpss_bl_regs_base);
- release_mem_region(oper_cfg.r1->start, oper_cfg.len1);
- if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) {
- iounmap(oper_cfg.vpss_regs_base);
- release_mem_region(oper_cfg.r2->start, oper_cfg.len2);
+ struct resource *res;
+
+ iounmap(oper_cfg.vpss_regs_base0);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
+ iounmap(oper_cfg.vpss_regs_base1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(res->start, resource_size(res));
}
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 25100001fff..ecbcefb0873 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -232,6 +232,12 @@ static struct em28xx_reg_seq vc211a_enable[] = {
{ -1, -1, -1, -1},
};
+static struct em28xx_reg_seq dikom_dk300_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
/*
* Board definitions
@@ -461,21 +467,30 @@ struct em28xx_board em28xx_boards[] = {
.name = "Leadtek Winfast USB II Deluxe",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .tda9887_conf = TDA9887_PRESENT,
+ .has_ir_i2c = 1,
+ .tvaudio_addr = 0x58,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT2_ACTIVE |
+ TDA9887_QSS,
.decoder = EM28XX_SAA711X,
+ .adecoder = EM28XX_TVAUDIO,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = EM28XX_AMUX_AUX,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
+ .vmux = SAA7115_COMPOSITE5,
.amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_COMPOSITE0,
+ .vmux = SAA7115_SVIDEO3,
.amux = EM28XX_AMUX_LINE_IN,
} },
+ .radio = {
+ .type = EM28XX_RADIO,
+ .amux = EM28XX_AMUX_AUX,
+ }
},
[EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
.name = "Videology 20K14XUSB USB2.0",
@@ -730,11 +745,12 @@ struct em28xx_board em28xx_boards[] = {
[EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
.name = "Terratec Hybrid XS Secam",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.has_msp34xx = 1,
.tuner_type = TUNER_XC2028,
.tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
@@ -1265,6 +1281,7 @@ struct em28xx_board em28xx_boards[] = {
.decoder = EM28XX_SAA711X,
.has_dvb = 1,
.dvb_gpio = em2882_kworld_315u_digital,
+ .ir_codes = &ir_codes_kworld_315u_table,
.xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE,
/* Analog mode - still not ready */
@@ -1431,6 +1448,21 @@ struct em28xx_board em28xx_boards[] = {
.gpio = hauppauge_wintv_hvr_900_analog,
} },
},
+ [EM2882_BOARD_DIKOM_DK300] = {
+ .name = "Dikom DK300",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = dikom_dk300_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ } },
+ },
[EM2883_BOARD_KWORLD_HYBRID_330U] = {
.name = "Kworld PlusTV HD Hybrid 330",
.tuner_type = TUNER_XC2028,
@@ -1751,6 +1783,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
{0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
{0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
+ {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
};
/* I2C devicelist hash table for devices with generic USB IDs */
@@ -2103,6 +2136,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
ctl->demod = XC3028_FE_DEFAULT;
break;
case EM2883_BOARD_KWORLD_HYBRID_330U:
+ case EM2882_BOARD_DIKOM_DK300:
ctl->demod = XC3028_FE_CHINA;
ctl->fname = XC2028_DEFAULT_FIRMWARE;
break;
@@ -2259,9 +2293,12 @@ static int em28xx_hint_board(struct em28xx *dev)
/* ----------------------------------------------------------------------- */
void em28xx_register_i2c_ir(struct em28xx *dev)
{
+ /* Leadtek winfast tv USBII deluxe can find a non working IR-device */
+ /* at address 0x18, so if that address is needed for another board in */
+ /* the future, please put it after 0x1f. */
struct i2c_board_info info;
const unsigned short addr_list[] = {
- 0x30, 0x47, I2C_CLIENT_END
+ 0x1f, 0x30, 0x47, I2C_CLIENT_END
};
if (disable_ir)
@@ -2288,6 +2325,10 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
dev->init_data.ir_codes = &ir_codes_rc5_hauppauge_new_table;
dev->init_data.get_key = em28xx_get_key_em_haup;
dev->init_data.name = "i2c IR (EM2840 Hauppauge)";
+ case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
+ dev->init_data.ir_codes = &ir_codes_winfast_usbii_deluxe_table;;
+ dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe;
+ dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)";
break;
}
@@ -2381,6 +2422,31 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
+
+/*
+ * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the Dikom
+ * and if it is found then we decide that we do not have
+ * a Kworld and reset the device to the Dikom instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
+
+ /* In cases where we had to use a board hint, the call to
+ em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ so make the call now so the analog GPIOs are set properly
+ before probing the i2c bus. */
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ break;
}
#if defined(CONFIG_MODULES) && defined(MODULE)
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index b311d4514bd..5a37eccbd7d 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -691,9 +691,15 @@ int em28xx_set_outfmt(struct em28xx *dev)
if (em28xx_vbi_supported(dev) == 1) {
vinctrl |= EM28XX_VINCTRL_VBI_RAW;
em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
- em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
- em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4);
- em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c);
+ em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4);
+ em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height);
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
+ }
}
return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
@@ -760,6 +766,13 @@ int em28xx_resolution_set(struct em28xx *dev)
width = norm_maxw(dev);
height = norm_maxh(dev);
+ /* Properly setup VBI */
+ dev->vbi_width = 720;
+ if (dev->norm & V4L2_STD_525_60)
+ dev->vbi_height = 12;
+ else
+ dev->vbi_height = 18;
+
if (!dev->progressive)
height >>= norm_maxh(dev);
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index cc0505eb900..1b96356b3ab 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -502,7 +502,9 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+ case EM2882_BOARD_DIKOM_DK300:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap);
@@ -606,6 +608,7 @@ static int dvb_fini(struct em28xx *dev)
if (dev->dvb) {
unregister_dvb(dev->dvb);
+ kfree(dev->dvb);
dev->dvb = NULL;
}
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index af0d935c29b..1fb754e2087 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -75,6 +75,10 @@ struct em28xx_IR {
unsigned int repeat_interval;
int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
+
+ /* IR device properties */
+
+ struct ir_dev_props props;
};
/**********************************************************
@@ -180,6 +184,36 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
return 1;
}
+int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char subaddr, keydetect, key;
+
+ struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1},
+
+ { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} };
+
+ subaddr = 0x10;
+ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+ if (keydetect == 0x00)
+ return 0;
+
+ subaddr = 0x00;
+ msg[1].buf = &key;
+ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+ if (key == 0x00)
+ return 0;
+
+ *ir_key = key;
+ *ir_raw = key;
+ return 1;
+}
+
/**********************************************************
Poll based get keycode functions
**********************************************************/
@@ -336,35 +370,28 @@ static void em28xx_ir_stop(struct em28xx_IR *ir)
cancel_delayed_work_sync(&ir->work);
}
-int em28xx_ir_init(struct em28xx *dev)
+int em28xx_ir_change_protocol(void *priv, u64 ir_type)
{
- struct em28xx_IR *ir;
- struct input_dev *input_dev;
- u8 ir_config;
- int err = -ENOMEM;
-
- if (dev->board.ir_codes == NULL) {
- /* No remote control support */
- return 0;
- }
-
- ir = kzalloc(sizeof(*ir), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ir || !input_dev)
- goto err_out_free;
-
- ir->input = input_dev;
- ir_config = EM2874_IR_RC5;
+ int rc = 0;
+ struct em28xx_IR *ir = priv;
+ struct em28xx *dev = ir->dev;
+ u8 ir_config = EM2874_IR_RC5;
/* Adjust xclk based o IR table for RC5/NEC tables */
- if (dev->board.ir_codes->ir_type == IR_TYPE_RC5) {
+
+ dev->board.ir_codes->ir_type = IR_TYPE_OTHER;
+ if (ir_type == IR_TYPE_RC5) {
dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
ir->full_code = 1;
- } else if (dev->board.ir_codes->ir_type == IR_TYPE_NEC) {
+ } else if (ir_type == IR_TYPE_NEC) {
dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
ir_config = EM2874_IR_NEC;
ir->full_code = 1;
- }
+ } else
+ rc = -EINVAL;
+
+ dev->board.ir_codes->ir_type = ir_type;
+
em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
EM28XX_XCLK_IR_RC5_MODE);
@@ -380,9 +407,42 @@ int em28xx_ir_init(struct em28xx *dev)
break;
default:
printk("Unrecognized em28xx chip id: IR not supported\n");
- goto err_out_free;
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+ struct em28xx_IR *ir;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ if (dev->board.ir_codes == NULL) {
+ /* No remote control support */
+ return 0;
}
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ir || !input_dev)
+ goto err_out_free;
+
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+
+ ir->input = input_dev;
+
+ /*
+ * em2874 supports more protocols. For now, let's just announce
+ * the two protocols that were already tested
+ */
+ ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC;
+ ir->props.priv = ir;
+ ir->props.change_protocol = em28xx_ir_change_protocol;
+
/* This is how often we ask the chip for IR information */
ir->polling = 100; /* ms */
@@ -393,6 +453,8 @@ int em28xx_ir_init(struct em28xx *dev)
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
+ /* Set IR protocol */
+ em28xx_ir_change_protocol(ir, dev->board.ir_codes->ir_type);
err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER);
if (err < 0)
goto err_out_free;
@@ -405,14 +467,13 @@ int em28xx_ir_init(struct em28xx *dev)
input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
input_dev->dev.parent = &dev->udev->dev;
- /* record handles to ourself */
- ir->dev = dev;
- dev->ir = ir;
+
em28xx_ir_start(ir);
/* all done */
- err = ir_input_register(ir->input, dev->board.ir_codes);
+ err = ir_input_register(ir->input, dev->board.ir_codes,
+ &ir->props);
if (err)
goto err_out_stop;
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index 058ac87639c..91e90559642 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -173,8 +173,8 @@
/* em2874 IR config register (0x50) */
#define EM2874_IR_NEC 0x00
#define EM2874_IR_RC5 0x04
-#define EM2874_IR_RC5_MODE_0 0x08
-#define EM2874_IR_RC5_MODE_6A 0x0b
+#define EM2874_IR_RC6_MODE_0 0x08
+#define EM2874_IR_RC6_MODE_6A 0x0b
/* em2874 Transport Stream Enable Register (0x5f) */
#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c
index 94943e5a152..c7dce39823d 100644
--- a/drivers/media/video/em28xx/em28xx-vbi.c
+++ b/drivers/media/video/em28xx/em28xx-vbi.c
@@ -71,7 +71,11 @@ free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
static int
vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
- *size = 720 * 12 * 2;
+ struct em28xx_fh *fh = q->priv_data;
+ struct em28xx *dev = fh->dev;
+
+ *size = dev->vbi_width * dev->vbi_height * 2;
+
if (0 == *count)
*count = vbibufs;
if (*count < 2)
@@ -85,19 +89,18 @@ static int
vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field)
{
+ struct em28xx_fh *fh = q->priv_data;
+ struct em28xx *dev = fh->dev;
struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
int rc = 0;
- unsigned int size;
-
- size = 720 * 12 * 2;
- buf->vb.size = size;
+ buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
- buf->vb.width = 720;
- buf->vb.height = 12;
+ buf->vb.width = dev->vbi_width;
+ buf->vb.height = dev->vbi_height;
buf->vb.field = field;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 849b18c9403..ac2bd935927 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -282,7 +282,7 @@ static void em28xx_copy_vbi(struct em28xx *dev,
{
void *startwrite, *startread;
int offset;
- int bytesperline = 720;
+ int bytesperline = dev->vbi_width;
if (dev == NULL) {
em28xx_isocdbg("dev is null\n");
@@ -323,8 +323,8 @@ static void em28xx_copy_vbi(struct em28xx *dev,
/* Make sure the bottom field populates the second half of the frame */
if (buf->top_field == 0) {
- startwrite += bytesperline * 0x0c;
- offset += bytesperline * 0x0c;
+ startwrite += bytesperline * dev->vbi_height;
+ offset += bytesperline * dev->vbi_height;
}
memcpy(startwrite, startread, len);
@@ -578,8 +578,7 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
dev->cur_field = p[2];
}
- /* FIXME: get rid of hard-coded value */
- vbi_size = 720 * 0x0c;
+ vbi_size = dev->vbi_width * dev->vbi_height;
if (dev->capture_type == 0) {
if (dev->vbi_read >= vbi_size) {
@@ -1850,18 +1849,27 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *format)
{
- format->fmt.vbi.samples_per_line = 720;
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
format->fmt.vbi.offset = 0;
format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
/* Varies by video standard (NTSC, PAL, etc.) */
- /* FIXME: hard-coded for NTSC support */
- format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
- format->fmt.vbi.count[0] = 12;
- format->fmt.vbi.count[1] = 12;
- format->fmt.vbi.start[0] = 10;
- format->fmt.vbi.start[1] = 273;
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ format->fmt.vbi.start[0] = 6;
+ format->fmt.vbi.start[1] = 318;
+ }
return 0;
}
@@ -1869,18 +1877,27 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *format)
{
- format->fmt.vbi.samples_per_line = 720;
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
format->fmt.vbi.offset = 0;
format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
/* Varies by video standard (NTSC, PAL, etc.) */
- /* FIXME: hard-coded for NTSC support */
- format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
- format->fmt.vbi.count[0] = 12;
- format->fmt.vbi.count[1] = 12;
- format->fmt.vbi.start[0] = 10;
- format->fmt.vbi.start[1] = 273;
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ format->fmt.vbi.start[0] = 6;
+ format->fmt.vbi.start[1] = 318;
+ }
return 0;
}
@@ -1922,7 +1939,8 @@ static int vidioc_querybuf(struct file *file, void *priv,
At a minimum, it causes a crash in zvbi since it does
a memcpy based on the source buffer length */
int result = videobuf_querybuf(&fh->vb_vbiq, b);
- b->length = 17280;
+ b->length = dev->vbi_width * dev->vbi_height * 2;
+
return result;
}
}
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 80d9b4fa1b9..ba6fe5daff8 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -111,6 +111,7 @@
#define EM2861_BOARD_GADMEI_UTV330PLUS 72
#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
#define EM2800_BOARD_VC211A 74
+#define EM2882_BOARD_DIKOM_DK300 75
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -552,7 +553,8 @@ struct em28xx {
int capture_type;
int vbi_read;
unsigned char cur_field;
-
+ unsigned int vbi_width;
+ unsigned int vbi_height; /* lines per field */
struct work_struct request_module_wk;
@@ -693,6 +695,8 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
u32 *ir_raw);
+int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw);
void em28xx_register_snapshot_button(struct em28xx *dev);
void em28xx_deregister_snapshot_button(struct em28xx *dev);
diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig
index dcc1a033544..87981b078fe 100644
--- a/drivers/media/video/et61x251/Kconfig
+++ b/drivers/media/video/et61x251/Kconfig
@@ -1,7 +1,11 @@
config USB_ET61X251
- tristate "USB ET61X[12]51 PC Camera Controller support"
+ tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)"
depends on VIDEO_V4L2
+ default n
---help---
+ This driver is DEPRECATED please use the gspca zc3xx module
+ instead.
+
Say Y here if you want support for cameras based on Etoms ET61X151
or ET61X251 PC Camera Controllers.
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 609d65b0b10..e0060c1f054 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -21,6 +21,15 @@ source "drivers/media/video/gspca/m5602/Kconfig"
source "drivers/media/video/gspca/stv06xx/Kconfig"
source "drivers/media/video/gspca/gl860/Kconfig"
+config USB_GSPCA_BENQ
+ tristate "Benq USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the Benq DC E300 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_benq.
+
config USB_GSPCA_CONEX
tristate "Conexant Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -30,6 +39,17 @@ config USB_GSPCA_CONEX
To compile this driver as a module, choose M here: the
module will be called gspca_conex.
+config USB_GSPCA_CPIA1
+ tristate "cpia CPiA (version 1) Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for USB cameras based on the cpia
+ CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
+ applications to understand the videoformat generated by this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_cpia1.
+
config USB_GSPCA_ETOMS
tristate "Etoms USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -86,15 +106,25 @@ config USB_GSPCA_OV519
module will be called gspca_ov519.
config USB_GSPCA_OV534
- tristate "OV534 USB Camera Driver"
+ tristate "OV534 OV772x USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
- Say Y here if you want support for cameras based on the OV534 chip.
- (e.g. Sony Playstation EYE)
+ Say Y here if you want support for cameras based on the OV534 chip
+ and sensor OV772x (e.g. Sony Playstation EYE)
To compile this driver as a module, choose M here: the
module will be called gspca_ov534.
+config USB_GSPCA_OV534_9
+ tristate "OV534 OV965x USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip
+ and sensor OV965x (e.g. Hercules Dualpix)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534_9.
+
config USB_GSPCA_PAC207
tristate "Pixart PAC207 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -122,6 +152,16 @@ config USB_GSPCA_PAC7311
To compile this driver as a module, choose M here: the
module will be called gspca_pac7311.
+config USB_GSPCA_SN9C2028
+ tristate "SONIX Dual-Mode USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want streaming support for Sonix SN9C2028 cameras.
+ These are supported as stillcams in libgphoto2/camlibs/sonix.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sn9c2028.
+
config USB_GSPCA_SN9C20X
tristate "SN9C20X USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index ff2c7279d82..6e4cf1ce01c 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -1,5 +1,7 @@
obj-$(CONFIG_USB_GSPCA) += gspca_main.o
+obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o
obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
+obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o
obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o
@@ -7,9 +9,11 @@ obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
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_OV534_9) += gspca_ov534_9.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_SN9C2028) += gspca_sn9c2028.o
obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o
obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
@@ -30,7 +34,9 @@ obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
gspca_main-objs := gspca.o
+gspca_benq-objs := benq.o
gspca_conex-objs := conex.o
+gspca_cpia1-objs := cpia1.o
gspca_etoms-objs := etoms.o
gspca_finepix-objs := finepix.o
gspca_jeilinj-objs := jeilinj.o
@@ -38,9 +44,11 @@ gspca_mars-objs := mars.o
gspca_mr97310a-objs := mr97310a.o
gspca_ov519-objs := ov519.o
gspca_ov534-objs := ov534.o
+gspca_ov534_9-objs := ov534_9.o
gspca_pac207-objs := pac207.o
gspca_pac7302-objs := pac7302.o
gspca_pac7311-objs := pac7311.o
+gspca_sn9c2028-objs := sn9c2028.o
gspca_sn9c20x-objs := sn9c20x.o
gspca_sonixb-objs := sonixb.o
gspca_sonixj-objs := sonixj.o
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
new file mode 100644
index 00000000000..43ac4af8d3e
--- /dev/null
+++ b/drivers/media/video/gspca/benq.c
@@ -0,0 +1,322 @@
+/*
+ * Benq DC E300 subdriver
+ *
+ * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.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
+ * 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
+ */
+
+#define MODULE_NAME "benq"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+};
+
+/* V4L2 controls supported by the driver */
+static const struct ctrl sd_ctrls[] = {
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static void sd_isoc_irq(struct urb *urb);
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 value, u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.no_urb_create = 1;
+ gspca_dev->cam.reverse_alts = 1;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface,
+ gspca_dev->nbalt - 1);
+ if (ret < 0) {
+ err("usb_set_interface failed");
+ return ret;
+ }
+/* reg_w(gspca_dev, 0x0003, 0x0002); */
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+ int i, n;
+
+ /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+#define SD_PKT_SZ 64
+#define SD_NPKT 32
+ for (n = 0; n < 4; n++) {
+ urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
+ if (!urb) {
+ err("usb_alloc_urb failed");
+ return -ENOMEM;
+ }
+ gspca_dev->urb[n] = urb;
+ urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev,
+ SD_PKT_SZ * SD_NPKT,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ if (urb->transfer_buffer == NULL) {
+ err("usb_buffer_alloc failed");
+ return -ENOMEM;
+ }
+ urb->dev = gspca_dev->dev;
+ urb->context = gspca_dev;
+ urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT;
+ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+ n & 1 ? 0x82 : 0x83);
+ urb->transfer_flags = URB_ISO_ASAP
+ | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1;
+ urb->complete = sd_isoc_irq;
+ urb->number_of_packets = SD_NPKT;
+ for (i = 0; i < SD_NPKT; i++) {
+ urb->iso_frame_desc[i].length = SD_PKT_SZ;
+ urb->iso_frame_desc[i].offset = SD_PKT_SZ * i;
+ }
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0x003c, 0x0003);
+ reg_w(gspca_dev, 0x003c, 0x0004);
+ reg_w(gspca_dev, 0x003c, 0x0005);
+ reg_w(gspca_dev, 0x003c, 0x0006);
+ reg_w(gspca_dev, 0x003c, 0x0007);
+ usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->nbalt - 1);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ /* unused */
+}
+
+/* reception of an URB */
+static void sd_isoc_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ struct urb *urb0;
+ u8 *data;
+ int i, st;
+
+ PDEBUG(D_PACK, "sd isoc irq");
+ if (!gspca_dev->streaming)
+ return;
+ if (urb->status != 0) {
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ return;
+ }
+
+ /* if this is a control URN (ep 0x83), wait */
+ if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2])
+ return;
+
+ /* scan both received URBs */
+ if (urb == gspca_dev->urb[1])
+ urb0 = gspca_dev->urb[0];
+ else
+ urb0 = gspca_dev->urb[2];
+ for (i = 0; i < urb->number_of_packets; i++) {
+
+ /* check the packet status and length */
+ if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ
+ || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) {
+ PDEBUG(D_ERR, "ISOC bad lengths %d / %d",
+ urb0->iso_frame_desc[i].actual_length,
+ urb->iso_frame_desc[i].actual_length);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+ st = urb0->iso_frame_desc[i].status;
+ if (st == 0)
+ st = urb->iso_frame_desc[i].status;
+ if (st) {
+ PDEBUG(D_ERR,
+ "ISOC data error: [%d] status=%d",
+ i, st);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+
+ /*
+ * The images are received in URBs of different endpoints
+ * (0x83 and 0x82).
+ * Image pieces in URBs of ep 0x83 are continuated in URBs of
+ * ep 0x82 of the same index.
+ * The packets in the URBs of endpoint 0x83 start with:
+ * - 80 ba/bb 00 00 = start of image followed by 'ff d8'
+ * - 04 ba/bb oo oo = image piece
+ * where 'oo oo' is the image offset
+ (not cheked)
+ * - (other -> bad frame)
+ * The images are JPEG encoded with full header and
+ * normal ff escape.
+ * The end of image ('ff d9') may occur in any URB.
+ * (not cheked)
+ */
+ data = (u8 *) urb0->transfer_buffer
+ + urb0->iso_frame_desc[i].offset;
+ if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) {
+
+ /* new image */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data + 4, SD_PKT_SZ - 4);
+ } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 4, SD_PKT_SZ - 4);
+ } else {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+ data = (u8 *) urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, SD_PKT_SZ);
+ }
+
+ /* resubmit the URBs */
+ st = usb_submit_urb(urb0, GFP_ATOMIC);
+ if (st < 0)
+ PDEBUG(D_ERR|D_PACK, "usb_submit_urb(0) ret %d", st);
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+}
+
+/* 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,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04a5, 0x3035)},
+ {}
+};
+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;
+ info("registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ info("deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/coarse_expo_autogain.h b/drivers/media/video/gspca/coarse_expo_autogain.h
new file mode 100644
index 00000000000..1cb9d941eaf
--- /dev/null
+++ b/drivers/media/video/gspca/coarse_expo_autogain.h
@@ -0,0 +1,116 @@
+/*
+ * Auto gain algorithm for camera's with a coarse exposure control
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.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
+ */
+
+/* Autogain + exposure algorithm for cameras with a coarse exposure control
+ (usually this means we can only control the clockdiv to change exposure)
+ As changing the clockdiv so that the fps drops from 30 to 15 fps for
+ example, will lead to a huge exposure change (it effectively doubles),
+ this algorithm normally tries to only adjust the gain (between 40 and
+ 80 %) and if that does not help, only then changes exposure. This leads
+ to a much more stable image then using the knee algorithm which at
+ certain points of the knee graph will only try to adjust exposure,
+ which leads to oscilating as one exposure step is huge.
+
+ Note this assumes that the sd struct for the cam in question has
+ exp_too_high_cnt and exp_too_high_cnt int members for use by this function.
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+static int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
+ int avg_lum, int desired_avg_lum, int deadzone)
+{
+ int i, steps, gain, orig_gain, exposure, orig_exposure;
+ int gain_low, gain_high;
+ const struct ctrl *gain_ctrl = NULL;
+ const struct ctrl *exposure_ctrl = NULL;
+ struct sd *sd = (struct sd *) gspca_dev;
+ int retval = 0;
+
+ for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
+ gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
+ exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ }
+ if (!gain_ctrl || !exposure_ctrl) {
+ PDEBUG(D_ERR, "Error: gspca_coarse_grained_expo_autogain "
+ "called on cam without gain or exposure");
+ return 0;
+ }
+
+ if (gain_ctrl->get(gspca_dev, &gain) ||
+ exposure_ctrl->get(gspca_dev, &exposure))
+ return 0;
+
+ orig_gain = gain;
+ orig_exposure = exposure;
+ gain_low =
+ (gain_ctrl->qctrl.maximum - gain_ctrl->qctrl.minimum) / 5 * 2;
+ gain_low += gain_ctrl->qctrl.minimum;
+ gain_high =
+ (gain_ctrl->qctrl.maximum - gain_ctrl->qctrl.minimum) / 5 * 4;
+ gain_high += gain_ctrl->qctrl.minimum;
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ desired lumination fast (with the risc of a slight overshoot) */
+ steps = (desired_avg_lum - avg_lum) / deadzone;
+
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
+ avg_lum, desired_avg_lum, steps);
+
+ if ((gain + steps) > gain_high &&
+ sd->exposure < exposure_ctrl->qctrl.maximum) {
+ gain = gain_high;
+ sd->exp_too_low_cnt++;
+ } else if ((gain + steps) < gain_low &&
+ sd->exposure > exposure_ctrl->qctrl.minimum) {
+ gain = gain_low;
+ sd->exp_too_high_cnt++;
+ } else {
+ gain += steps;
+ if (gain > gain_ctrl->qctrl.maximum)
+ gain = gain_ctrl->qctrl.maximum;
+ else if (gain < gain_ctrl->qctrl.minimum)
+ gain = gain_ctrl->qctrl.minimum;
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
+ }
+
+ if (sd->exp_too_high_cnt > 3) {
+ exposure--;
+ sd->exp_too_high_cnt = 0;
+ } else if (sd->exp_too_low_cnt > 3) {
+ exposure++;
+ sd->exp_too_low_cnt = 0;
+ }
+
+ if (gain != orig_gain) {
+ gain_ctrl->set(gspca_dev, gain);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ exposure_ctrl->set(gspca_dev, exposure);
+ retval = 1;
+ }
+
+ return retval;
+}
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index c98b5d69c43..19fe6b24c9a 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -52,7 +52,7 @@ 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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -1032,7 +1032,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
}
/* sub-driver description */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
new file mode 100644
index 00000000000..82945ed5cbe
--- /dev/null
+++ b/drivers/media/video/gspca/cpia1.c
@@ -0,0 +1,2022 @@
+/*
+ * cpia CPiA (1) gspca driver
+ *
+ * Copyright (C) 2010 Hans de Goede <hdgoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 cpia driver which is :
+ *
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
+ *
+ * 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 "cpia1"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_DESCRIPTION("Vision CPiA");
+MODULE_LICENSE("GPL");
+
+/* constant value's */
+#define MAGIC_0 0x19
+#define MAGIC_1 0x68
+#define DATA_IN 0xC0
+#define DATA_OUT 0x40
+#define VIDEOSIZE_QCIF 0 /* 176x144 */
+#define VIDEOSIZE_CIF 1 /* 352x288 */
+#define SUBSAMPLE_420 0
+#define SUBSAMPLE_422 1
+#define YUVORDER_YUYV 0
+#define YUVORDER_UYVY 1
+#define NOT_COMPRESSED 0
+#define COMPRESSED 1
+#define NO_DECIMATION 0
+#define DECIMATION_ENAB 1
+#define EOI 0xff /* End Of Image */
+#define EOL 0xfd /* End Of Line */
+#define FRAME_HEADER_SIZE 64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE 0
+#define CPIA_GRAB_CONTINEOUS 1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE 0
+#define CPIA_COMPRESSION_AUTO 1
+#define CPIA_COMPRESSION_MANUAL 2
+#define CPIA_COMPRESSION_TARGET_QUALITY 0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE 0
+#define GRABSTATE 1
+#define STREAMSTATE 2
+#define FATALERROR 3
+#define CMDERROR 4
+#define DEBUGFLAGS 5
+#define VPSTATUS 6
+#define ERRORCODE 7
+
+/* SystemState */
+#define UNINITIALISED_STATE 0
+#define PASS_THROUGH_STATE 1
+#define LO_POWER_STATE 2
+#define HI_POWER_STATE 3
+#define WARM_BOOT_STATE 4
+
+/* GrabState */
+#define GRAB_IDLE 0
+#define GRAB_ACTIVE 1
+#define GRAB_DONE 2
+
+/* StreamState */
+#define STREAM_NOT_READY 0
+#define STREAM_READY 1
+#define STREAM_OPEN 2
+#define STREAM_PAUSED 3
+#define STREAM_FINISHED 4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG 1
+#define SYSTEM_FLAG 2
+#define INT_CTRL_FLAG 4
+#define PROCESS_FLAG 8
+#define COM_FLAG 16
+#define VP_CTRL_FLAG 32
+#define CAPTURE_FLAG 64
+#define DEBUG_FLAG 128
+
+/* VPStatus */
+#define VP_STATE_OK 0x00
+
+#define VP_STATE_FAILED_VIDEOINIT 0x01
+#define VP_STATE_FAILED_AECACBINIT 0x02
+#define VP_STATE_AEC_MAX 0x04
+#define VP_STATE_ACB_BMAX 0x08
+
+#define VP_STATE_ACB_RMIN 0x10
+#define VP_STATE_ACB_GMIN 0x20
+#define VP_STATE_ACB_RMAX 0x40
+#define VP_STATE_ACB_GMAX 0x80
+
+/* default (minimum) compensation values */
+#define COMP_RED 220
+#define COMP_GREEN1 214
+#define COMP_GREEN2 COMP_GREEN1
+#define COMP_BLUE 230
+
+/* exposure status */
+#define EXPOSURE_VERY_LIGHT 0
+#define EXPOSURE_LIGHT 1
+#define EXPOSURE_NORMAL 2
+#define EXPOSURE_DARK 3
+#define EXPOSURE_VERY_DARK 4
+
+#define CPIA_MODULE_CPIA (0 << 5)
+#define CPIA_MODULE_SYSTEM (1 << 5)
+#define CPIA_MODULE_VP_CTRL (5 << 5)
+#define CPIA_MODULE_CAPTURE (6 << 5)
+#define CPIA_MODULE_DEBUG (7 << 5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
+#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
+
+#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
+#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
+
+#define ROUND_UP_EXP_FOR_FLICKER 15
+
+/* Constants for automatic frame rate adjustment */
+#define MAX_EXP 302
+#define MAX_EXP_102 255
+#define LOW_EXP 140
+#define VERY_LOW_EXP 70
+#define TC 94
+#define EXP_ACC_DARK 50
+#define EXP_ACC_LIGHT 90
+#define HIGH_COMP_102 160
+#define MAX_COMP 239
+#define DARK_TIME 3
+#define LIGHT_TIME 3
+
+#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
+ sd->params.version.firmwareRevision == (y))
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+struct cam_params {
+ struct {
+ u8 firmwareVersion;
+ u8 firmwareRevision;
+ u8 vcVersion;
+ u8 vcRevision;
+ } version;
+ struct {
+ u16 vendor;
+ u16 product;
+ u16 deviceRevision;
+ } pnpID;
+ struct {
+ u8 vpVersion;
+ u8 vpRevision;
+ u16 cameraHeadID;
+ } vpVersion;
+ struct {
+ u8 systemState;
+ u8 grabState;
+ u8 streamState;
+ u8 fatalError;
+ u8 cmdError;
+ u8 debugFlags;
+ u8 vpStatus;
+ u8 errorCode;
+ } status;
+ struct {
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ } colourParams;
+ struct {
+ u8 gainMode;
+ u8 expMode;
+ u8 compMode;
+ u8 centreWeight;
+ u8 gain;
+ u8 fineExp;
+ u8 coarseExpLo;
+ u8 coarseExpHi;
+ u8 redComp;
+ u8 green1Comp;
+ u8 green2Comp;
+ u8 blueComp;
+ } exposure;
+ struct {
+ u8 balanceMode;
+ u8 redGain;
+ u8 greenGain;
+ u8 blueGain;
+ } colourBalance;
+ struct {
+ u8 divisor;
+ u8 baserate;
+ } sensorFps;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } apcor;
+ struct {
+ u8 disabled;
+ u8 flickerMode;
+ u8 coarseJump;
+ u8 allowableOverExposure;
+ } flickerControl;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } vlOffset;
+ struct {
+ u8 mode;
+ u8 decimation;
+ } compression;
+ struct {
+ u8 frTargeting;
+ u8 targetFR;
+ u8 targetQ;
+ } compressionTarget;
+ struct {
+ u8 yThreshold;
+ u8 uvThreshold;
+ } yuvThreshold;
+ struct {
+ u8 hysteresis;
+ u8 threshMax;
+ u8 smallStep;
+ u8 largeStep;
+ u8 decimationHysteresis;
+ u8 frDiffStepThresh;
+ u8 qDiffStepThresh;
+ u8 decimationThreshMod;
+ } compressionParams;
+ struct {
+ u8 videoSize; /* CIF/QCIF */
+ u8 subSample;
+ u8 yuvOrder;
+ } format;
+ struct { /* Intel QX3 specific data */
+ u8 qx3_detected; /* a QX3 is present */
+ u8 toplight; /* top light lit , R/W */
+ u8 bottomlight; /* bottom light lit, R/W */
+ u8 button; /* snapshot button pressed (R/O) */
+ u8 cradled; /* microscope is in cradle (R/O) */
+ } qx3;
+ struct {
+ u8 colStart; /* skip first 8*colStart pixels */
+ u8 colEnd; /* finish at 8*colEnd pixels */
+ u8 rowStart; /* skip first 4*rowStart lines */
+ u8 rowEnd; /* finish at 4*rowEnd lines */
+ } roi;
+ u8 ecpTiming;
+ u8 streamStartLine;
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct cam_params params; /* camera settings */
+
+ atomic_t cam_exposure;
+ atomic_t fps;
+ int exposure_count;
+ u8 exposure_status;
+ u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
+ u8 first_frame;
+ u8 freq;
+};
+
+/* 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_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsaturation(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 int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+#define BRIGHTNESS_DEF 50
+ .default_value = BRIGHTNESS_DEF,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 96,
+ .step = 8,
+#define CONTRAST_DEF 48
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+#define SATURATION_DEF 50
+ .default_value = SATURATION_DEF,
+ },
+ .set = sd_setsaturation,
+ .get = sd_getsaturation,
+ },
+ {
+ {
+ .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 1
+ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq,
+ },
+ {
+ {
+#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
+ .id = V4L2_CID_COMP_TARGET,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Compression Target",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
+ .default_value = COMP_TARGET_DEF,
+ },
+ .set = sd_setcomptarget,
+ .get = sd_getcomptarget,
+ },
+};
+
+static const struct v4l2_pix_format mode[] = {
+ {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ /* The sizeimage is trial and error, as with low framerates
+ the camera will pad out usb frames, making the image
+ data larger then strictly necessary */
+ .bytesperline = 160,
+ .sizeimage = 65536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 172,
+ .sizeimage = 65536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 262144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 262144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+
+static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
+{
+ u8 requesttype;
+ unsigned int pipe;
+ int ret, databytes = command[6] | (command[7] << 8);
+ /* Sometimes we see spurious EPIPE errors */
+ int retries = 3;
+
+ if (command[0] == DATA_IN) {
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+ requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ } else if (command[0] == DATA_OUT) {
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+ requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ } else {
+ PDEBUG(D_ERR, "Unexpected first byte of command: %x",
+ command[0]);
+ return -EINVAL;
+ }
+
+retry:
+ ret = usb_control_msg(gspca_dev->dev, pipe,
+ command[1],
+ requesttype,
+ command[2] | (command[3] << 8),
+ command[4] | (command[5] << 8),
+ gspca_dev->usb_buf, databytes, 1000);
+
+ if (ret < 0)
+ PDEBUG(D_ERR, "usb_control_msg %02x, error %d", command[1],
+ ret);
+
+ if (ret == -EPIPE && retries > 0) {
+ retries--;
+ goto retry;
+ }
+
+ return (ret < 0) ? ret : 0;
+}
+
+/* send an arbitrary command to the camera */
+static int do_command(struct gspca_dev *gspca_dev, u16 command,
+ u8 a, u8 b, u8 c, u8 d)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, datasize;
+ u8 cmd[8];
+
+ switch (command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ case CPIA_COMMAND_GetPnPID:
+ case CPIA_COMMAND_GetCameraStatus:
+ case CPIA_COMMAND_GetVPVersion:
+ case CPIA_COMMAND_GetColourParams:
+ case CPIA_COMMAND_GetColourBalance:
+ case CPIA_COMMAND_GetExposure:
+ datasize = 8;
+ break;
+ case CPIA_COMMAND_ReadMCPorts:
+ case CPIA_COMMAND_ReadVCRegs:
+ datasize = 4;
+ break;
+ default:
+ datasize = 0;
+ break;
+ }
+
+ cmd[0] = command >> 8;
+ cmd[1] = command & 0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = datasize;
+ cmd[7] = 0;
+
+ ret = cpia_usb_transferCmd(gspca_dev, cmd);
+ if (ret)
+ return ret;
+
+ switch (command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
+ sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
+ sd->params.version.vcVersion = gspca_dev->usb_buf[2];
+ sd->params.version.vcRevision = gspca_dev->usb_buf[3];
+ break;
+ case CPIA_COMMAND_GetPnPID:
+ sd->params.pnpID.vendor =
+ gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+ sd->params.pnpID.product =
+ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+ sd->params.pnpID.deviceRevision =
+ gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
+ break;
+ case CPIA_COMMAND_GetCameraStatus:
+ sd->params.status.systemState = gspca_dev->usb_buf[0];
+ sd->params.status.grabState = gspca_dev->usb_buf[1];
+ sd->params.status.streamState = gspca_dev->usb_buf[2];
+ sd->params.status.fatalError = gspca_dev->usb_buf[3];
+ sd->params.status.cmdError = gspca_dev->usb_buf[4];
+ sd->params.status.debugFlags = gspca_dev->usb_buf[5];
+ sd->params.status.vpStatus = gspca_dev->usb_buf[6];
+ sd->params.status.errorCode = gspca_dev->usb_buf[7];
+ break;
+ case CPIA_COMMAND_GetVPVersion:
+ sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
+ sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
+ sd->params.vpVersion.cameraHeadID =
+ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
+ sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
+ sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
+ break;
+ case CPIA_COMMAND_GetColourBalance:
+ sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
+ sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
+ sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
+ break;
+ case CPIA_COMMAND_GetExposure:
+ sd->params.exposure.gain = gspca_dev->usb_buf[0];
+ sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
+ sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
+ sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
+ sd->params.exposure.redComp = gspca_dev->usb_buf[4];
+ sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
+ sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
+ sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
+ break;
+
+ case CPIA_COMMAND_ReadMCPorts:
+ if (!sd->params.qx3.qx3_detected)
+ break;
+ /* test button press */
+ sd->params.qx3.button = ((gspca_dev->usb_buf[1] & 0x02) == 0);
+ if (sd->params.qx3.button) {
+ /* button pressed - unlock the latch */
+ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ 3, 0xDF, 0xDF, 0);
+ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ 3, 0xFF, 0xFF, 0);
+ }
+
+ /* test whether microscope is cradled */
+ sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
+ u8 a, u8 b, u8 c, u8 d,
+ u8 e, u8 f, u8 g, u8 h,
+ u8 i, u8 j, u8 k, u8 l)
+{
+ u8 cmd[8];
+
+ cmd[0] = command >> 8;
+ cmd[1] = command & 0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ gspca_dev->usb_buf[0] = e;
+ gspca_dev->usb_buf[1] = f;
+ gspca_dev->usb_buf[2] = g;
+ gspca_dev->usb_buf[3] = h;
+ gspca_dev->usb_buf[4] = i;
+ gspca_dev->usb_buf[5] = j;
+ gspca_dev->usb_buf[6] = k;
+ gspca_dev->usb_buf[7] = l;
+
+ return cpia_usb_transferCmd(gspca_dev, cmd);
+}
+
+/* find_over_exposure
+ * Finds a suitable value of OverExposure for use with SetFlickerCtrl
+ * Some calculation is required because this value changes with the brightness
+ * set with SetColourParameters
+ *
+ * Parameters: Brightness - last brightness value set with SetColourParameters
+ *
+ * Returns: OverExposure value to use with SetFlickerCtrl
+ */
+#define FLICKER_MAX_EXPOSURE 250
+#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
+#define FLICKER_BRIGHTNESS_CONSTANT 59
+static int find_over_exposure(int brightness)
+{
+ int MaxAllowableOverExposure, OverExposure;
+
+ MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
+ FLICKER_BRIGHTNESS_CONSTANT;
+
+ if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
+ OverExposure = MaxAllowableOverExposure;
+ else
+ OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+
+ return OverExposure;
+}
+#undef FLICKER_MAX_EXPOSURE
+#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
+#undef FLICKER_BRIGHTNESS_CONSTANT
+
+/* initialise cam_data structure */
+static void reset_camera_params(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam_params *params = &sd->params;
+
+ /* The following parameter values are the defaults from
+ * "Software Developer's Guide for CPiA Cameras". Any changes
+ * to the defaults are noted in comments. */
+ params->colourParams.brightness = BRIGHTNESS_DEF;
+ params->colourParams.contrast = CONTRAST_DEF;
+ params->colourParams.saturation = SATURATION_DEF;
+ params->exposure.gainMode = 4;
+ params->exposure.expMode = 2; /* AEC */
+ params->exposure.compMode = 1;
+ params->exposure.centreWeight = 1;
+ params->exposure.gain = 0;
+ params->exposure.fineExp = 0;
+ params->exposure.coarseExpLo = 185;
+ params->exposure.coarseExpHi = 0;
+ params->exposure.redComp = COMP_RED;
+ params->exposure.green1Comp = COMP_GREEN1;
+ params->exposure.green2Comp = COMP_GREEN2;
+ params->exposure.blueComp = COMP_BLUE;
+ params->colourBalance.balanceMode = 2; /* ACB */
+ params->colourBalance.redGain = 32;
+ params->colourBalance.greenGain = 6;
+ params->colourBalance.blueGain = 92;
+ params->apcor.gain1 = 0x18;
+ params->apcor.gain2 = 0x16;
+ params->apcor.gain4 = 0x24;
+ params->apcor.gain8 = 0x34;
+ params->flickerControl.flickerMode = 0;
+ params->flickerControl.disabled = 1;
+
+ params->flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [params->sensorFps.baserate]
+ [params->sensorFps.divisor];
+ params->flickerControl.allowableOverExposure =
+ find_over_exposure(params->colourParams.brightness);
+ params->vlOffset.gain1 = 20;
+ params->vlOffset.gain2 = 24;
+ params->vlOffset.gain4 = 26;
+ params->vlOffset.gain8 = 26;
+ params->compressionParams.hysteresis = 3;
+ params->compressionParams.threshMax = 11;
+ params->compressionParams.smallStep = 1;
+ params->compressionParams.largeStep = 3;
+ params->compressionParams.decimationHysteresis = 2;
+ params->compressionParams.frDiffStepThresh = 5;
+ params->compressionParams.qDiffStepThresh = 3;
+ params->compressionParams.decimationThreshMod = 2;
+ /* End of default values from Software Developer's Guide */
+
+ /* Set Sensor FPS to 15fps. This seems better than 30fps
+ * for indoor lighting. */
+ params->sensorFps.divisor = 1;
+ params->sensorFps.baserate = 1;
+
+ params->yuvThreshold.yThreshold = 6; /* From windows driver */
+ params->yuvThreshold.uvThreshold = 6; /* From windows driver */
+
+ params->format.subSample = SUBSAMPLE_420;
+ params->format.yuvOrder = YUVORDER_YUYV;
+
+ params->compression.mode = CPIA_COMPRESSION_AUTO;
+ params->compression.decimation = NO_DECIMATION;
+
+ params->compressionTarget.frTargeting = COMP_TARGET_DEF;
+ params->compressionTarget.targetFR = 15; /* From windows driver */
+ params->compressionTarget.targetQ = 5; /* From windows driver */
+
+ params->qx3.qx3_detected = 0;
+ params->qx3.toplight = 0;
+ params->qx3.bottomlight = 0;
+ params->qx3.button = 0;
+ params->qx3.cradled = 0;
+}
+
+static void printstatus(struct cam_params *params)
+{
+ PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
+ params->status.systemState, params->status.grabState,
+ params->status.streamState, params->status.fatalError,
+ params->status.cmdError, params->status.debugFlags,
+ params->status.vpStatus, params->status.errorCode);
+}
+
+static int goto_low_power(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.systemState != LO_POWER_STATE) {
+ if (sd->params.status.systemState != WARM_BOOT_STATE) {
+ PDEBUG(D_ERR,
+ "unexpected state after lo power cmd: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ }
+ return -EIO;
+ }
+
+ PDEBUG(D_CONF, "camera now in LOW power state");
+ return 0;
+}
+
+static int goto_high_power(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ msleep_interruptible(40); /* windows driver does it too */
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.systemState != HI_POWER_STATE) {
+ PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ return -EIO;
+ }
+
+ PDEBUG(D_CONF, "camera now in HIGH power state");
+ return 0;
+}
+
+static int get_version_information(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ /* GetCPIAVersion */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ /* GetPnPID */
+ return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+static int save_camera_state(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+}
+
+int command_setformat(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
+ sd->params.format.videoSize,
+ sd->params.format.subSample,
+ sd->params.format.yuvOrder, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetROI,
+ sd->params.roi.colStart, sd->params.roi.colEnd,
+ sd->params.roi.rowStart, sd->params.roi.rowEnd);
+}
+
+int command_setcolourparams(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
+ sd->params.colourParams.brightness,
+ sd->params.colourParams.contrast,
+ sd->params.colourParams.saturation, 0);
+}
+
+int command_setapcor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
+ sd->params.apcor.gain1,
+ sd->params.apcor.gain2,
+ sd->params.apcor.gain4,
+ sd->params.apcor.gain8);
+}
+
+int command_setvloffset(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
+ sd->params.vlOffset.gain1,
+ sd->params.vlOffset.gain2,
+ sd->params.vlOffset.gain4,
+ sd->params.vlOffset.gain8);
+}
+
+int command_setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+ sd->params.exposure.gainMode,
+ 1,
+ sd->params.exposure.compMode,
+ sd->params.exposure.centreWeight,
+ sd->params.exposure.gain,
+ sd->params.exposure.fineExp,
+ sd->params.exposure.coarseExpLo,
+ sd->params.exposure.coarseExpHi,
+ sd->params.exposure.redComp,
+ sd->params.exposure.green1Comp,
+ sd->params.exposure.green2Comp,
+ sd->params.exposure.blueComp);
+ if (ret)
+ return ret;
+
+ if (sd->params.exposure.expMode != 1) {
+ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+ 0,
+ sd->params.exposure.expMode,
+ 0, 0,
+ sd->params.exposure.gain,
+ sd->params.exposure.fineExp,
+ sd->params.exposure.coarseExpLo,
+ sd->params.exposure.coarseExpHi,
+ 0, 0, 0, 0);
+ }
+
+ return ret;
+}
+
+int command_setcolourbalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->params.colourBalance.balanceMode == 1) {
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 1,
+ sd->params.colourBalance.redGain,
+ sd->params.colourBalance.greenGain,
+ sd->params.colourBalance.blueGain);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+ if (sd->params.colourBalance.balanceMode == 2) {
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 2, 0, 0, 0);
+ }
+ if (sd->params.colourBalance.balanceMode == 3) {
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+
+ return -EINVAL;
+}
+
+int command_setcompressiontarget(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
+ sd->params.compressionTarget.frTargeting,
+ sd->params.compressionTarget.targetFR,
+ sd->params.compressionTarget.targetQ, 0);
+}
+
+int command_setyuvtresh(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
+ sd->params.yuvThreshold.yThreshold,
+ sd->params.yuvThreshold.uvThreshold, 0, 0);
+}
+
+int command_setcompressionparams(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command_extended(gspca_dev,
+ CPIA_COMMAND_SetCompressionParams,
+ 0, 0, 0, 0,
+ sd->params.compressionParams.hysteresis,
+ sd->params.compressionParams.threshMax,
+ sd->params.compressionParams.smallStep,
+ sd->params.compressionParams.largeStep,
+ sd->params.compressionParams.decimationHysteresis,
+ sd->params.compressionParams.frDiffStepThresh,
+ sd->params.compressionParams.qDiffStepThresh,
+ sd->params.compressionParams.decimationThreshMod);
+}
+
+int command_setcompression(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+ sd->params.compression.mode,
+ sd->params.compression.decimation, 0, 0);
+}
+
+int command_setsensorfps(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
+ sd->params.sensorFps.divisor,
+ sd->params.sensorFps.baserate, 0, 0);
+}
+
+int command_setflickerctrl(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
+ sd->params.flickerControl.flickerMode,
+ sd->params.flickerControl.coarseJump,
+ sd->params.flickerControl.allowableOverExposure,
+ 0);
+}
+
+int command_setecptiming(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
+ sd->params.ecpTiming, 0, 0, 0);
+}
+
+int command_pause(struct gspca_dev *gspca_dev)
+{
+ return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+}
+
+int command_resume(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
+ 0, sd->params.streamStartLine, 0, 0);
+}
+
+int command_setlights(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, p1, p2;
+
+ if (!sd->params.qx3.qx3_detected)
+ return 0;
+
+ p1 = (sd->params.qx3.bottomlight == 0) << 1;
+ p2 = (sd->params.qx3.toplight == 0) << 3;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
+ 0x90, 0x8F, 0x50, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
+ p1 | p2 | 0xE0, 0);
+}
+
+static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
+{
+ /* Everything in here is from the Windows driver */
+/* define for compgain calculation */
+#if 0
+#define COMPGAIN(base, curexp, newexp) \
+ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
+ (float)(u8)(basecomp - 128))
+#else
+ /* equivalent functions without floating point math */
+#define COMPGAIN(base, curexp, newexp) \
+ (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
+#endif
+
+ struct sd *sd = (struct sd *) gspca_dev;
+ int currentexp = sd->params.exposure.coarseExpLo +
+ sd->params.exposure.coarseExpHi * 256;
+ int ret, startexp;
+
+ if (on) {
+ int cj = sd->params.flickerControl.coarseJump;
+ sd->params.flickerControl.flickerMode = 1;
+ sd->params.flickerControl.disabled = 0;
+ if (sd->params.exposure.expMode != 2) {
+ sd->params.exposure.expMode = 2;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ currentexp = currentexp << sd->params.exposure.gain;
+ sd->params.exposure.gain = 0;
+ /* round down current exposure to nearest value */
+ startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
+ if (startexp < 1)
+ startexp = 1;
+ startexp = (startexp * cj) - 1;
+ if (FIRMWARE_VERSION(1, 2))
+ while (startexp > MAX_EXP_102)
+ startexp -= cj;
+ else
+ while (startexp > MAX_EXP)
+ startexp -= cj;
+ sd->params.exposure.coarseExpLo = startexp & 0xff;
+ sd->params.exposure.coarseExpHi = startexp >> 8;
+ if (currentexp > startexp) {
+ if (currentexp > (2 * startexp))
+ currentexp = 2 * startexp;
+ sd->params.exposure.redComp =
+ COMPGAIN(COMP_RED, currentexp, startexp);
+ sd->params.exposure.green1Comp =
+ COMPGAIN(COMP_GREEN1, currentexp, startexp);
+ sd->params.exposure.green2Comp =
+ COMPGAIN(COMP_GREEN2, currentexp, startexp);
+ sd->params.exposure.blueComp =
+ COMPGAIN(COMP_BLUE, currentexp, startexp);
+ } else {
+ sd->params.exposure.redComp = COMP_RED;
+ sd->params.exposure.green1Comp = COMP_GREEN1;
+ sd->params.exposure.green2Comp = COMP_GREEN2;
+ sd->params.exposure.blueComp = COMP_BLUE;
+ }
+ if (FIRMWARE_VERSION(1, 2))
+ sd->params.exposure.compMode = 0;
+ else
+ sd->params.exposure.compMode = 1;
+
+ sd->params.apcor.gain1 = 0x18;
+ sd->params.apcor.gain2 = 0x18;
+ sd->params.apcor.gain4 = 0x16;
+ sd->params.apcor.gain8 = 0x14;
+ } else {
+ sd->params.flickerControl.flickerMode = 0;
+ sd->params.flickerControl.disabled = 1;
+ /* Average equivalent coarse for each comp channel */
+ startexp = EXP_FROM_COMP(COMP_RED,
+ sd->params.exposure.redComp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN1,
+ sd->params.exposure.green1Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN2,
+ sd->params.exposure.green2Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_BLUE,
+ sd->params.exposure.blueComp, currentexp);
+ startexp = startexp >> 2;
+ while (startexp > MAX_EXP && sd->params.exposure.gain <
+ sd->params.exposure.gainMode - 1) {
+ startexp = startexp >> 1;
+ ++sd->params.exposure.gain;
+ }
+ if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
+ startexp = MAX_EXP_102;
+ if (startexp > MAX_EXP)
+ startexp = MAX_EXP;
+ sd->params.exposure.coarseExpLo = startexp & 0xff;
+ sd->params.exposure.coarseExpHi = startexp >> 8;
+ sd->params.exposure.redComp = COMP_RED;
+ sd->params.exposure.green1Comp = COMP_GREEN1;
+ sd->params.exposure.green2Comp = COMP_GREEN2;
+ sd->params.exposure.blueComp = COMP_BLUE;
+ sd->params.exposure.compMode = 1;
+ sd->params.apcor.gain1 = 0x18;
+ sd->params.apcor.gain2 = 0x16;
+ sd->params.apcor.gain4 = 0x24;
+ sd->params.apcor.gain8 = 0x34;
+ }
+ sd->params.vlOffset.gain1 = 20;
+ sd->params.vlOffset.gain2 = 24;
+ sd->params.vlOffset.gain4 = 26;
+ sd->params.vlOffset.gain8 = 26;
+
+ if (apply) {
+ ret = command_setexposure(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setapcor(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setvloffset(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setflickerctrl(gspca_dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+#undef EXP_FROM_COMP
+#undef COMPGAIN
+}
+
+/* monitor the exposure and adjust the sensor frame rate if needed */
+static void monitor_exposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 exp_acc, bcomp, gain, coarseL, cmd[8];
+ int ret, light_exp, dark_exp, very_dark_exp;
+ int old_exposure, new_exposure, framerate;
+ int setfps = 0, setexp = 0, setflicker = 0;
+
+ /* get necessary stats and register settings from camera */
+ /* do_command can't handle this, so do it ourselves */
+ cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
+ cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
+ cmd[2] = 30;
+ cmd[3] = 4;
+ cmd[4] = 9;
+ cmd[5] = 8;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ ret = cpia_usb_transferCmd(gspca_dev, cmd);
+ if (ret) {
+ PDEBUG(D_ERR, "ReadVPRegs(30,4,9,8) - failed: %d", ret);
+ return;
+ }
+ exp_acc = gspca_dev->usb_buf[0];
+ bcomp = gspca_dev->usb_buf[1];
+ gain = gspca_dev->usb_buf[2];
+ coarseL = gspca_dev->usb_buf[3];
+
+ light_exp = sd->params.colourParams.brightness +
+ TC - 50 + EXP_ACC_LIGHT;
+ if (light_exp > 255)
+ light_exp = 255;
+ dark_exp = sd->params.colourParams.brightness +
+ TC - 50 - EXP_ACC_DARK;
+ if (dark_exp < 0)
+ dark_exp = 0;
+ very_dark_exp = dark_exp / 2;
+
+ old_exposure = sd->params.exposure.coarseExpHi * 256 +
+ sd->params.exposure.coarseExpLo;
+
+ if (!sd->params.flickerControl.disabled) {
+ /* Flicker control on */
+ int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
+ HIGH_COMP_102;
+ bcomp += 128; /* decode */
+ if (bcomp >= max_comp && exp_acc < dark_exp) {
+ /* dark */
+ if (exp_acc < very_dark_exp) {
+ /* very dark */
+ if (sd->exposure_status == EXPOSURE_VERY_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_DARK;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if (sd->exposure_status == EXPOSURE_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_DARK;
+ sd->exposure_count = 1;
+ }
+ }
+ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if (old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_LIGHT;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if (sd->exposure_status == EXPOSURE_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_LIGHT;
+ sd->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ } else {
+ /* Flicker control off */
+ if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
+ /* dark */
+ if (exp_acc < very_dark_exp) {
+ /* very dark */
+ if (sd->exposure_status == EXPOSURE_VERY_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_DARK;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if (sd->exposure_status == EXPOSURE_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_DARK;
+ sd->exposure_count = 1;
+ }
+ }
+ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if (old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_LIGHT;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if (sd->exposure_status == EXPOSURE_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_LIGHT;
+ sd->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ }
+
+ framerate = atomic_read(&sd->fps);
+ if (framerate > 30 || framerate < 1)
+ framerate = 1;
+
+ if (!sd->params.flickerControl.disabled) {
+ /* Flicker control on */
+ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+ sd->exposure_status == EXPOSURE_DARK) &&
+ sd->exposure_count >= DARK_TIME * framerate &&
+ sd->params.sensorFps.divisor < 3) {
+
+ /* dark for too long */
+ ++sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+ setflicker = 1;
+
+ new_exposure = sd->params.flickerControl.coarseJump-1;
+ while (new_exposure < old_exposure / 2)
+ new_exposure +=
+ sd->params.flickerControl.coarseJump;
+ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+ sd->params.exposure.coarseExpHi = new_exposure >> 8;
+ setexp = 1;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+ sd->exposure_status == EXPOSURE_LIGHT) &&
+ sd->exposure_count >= LIGHT_TIME * framerate &&
+ sd->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
+ MAX_EXP;
+ --sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+ setflicker = 1;
+
+ new_exposure = sd->params.flickerControl.coarseJump-1;
+ while (new_exposure < 2 * old_exposure &&
+ new_exposure +
+ sd->params.flickerControl.coarseJump < max_exp)
+ new_exposure +=
+ sd->params.flickerControl.coarseJump;
+ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+ sd->params.exposure.coarseExpHi = new_exposure >> 8;
+ setexp = 1;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+ }
+ } else {
+ /* Flicker control off */
+ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+ sd->exposure_status == EXPOSURE_DARK) &&
+ sd->exposure_count >= DARK_TIME * framerate &&
+ sd->params.sensorFps.divisor < 3) {
+
+ /* dark for too long */
+ ++sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ if (sd->params.exposure.gain > 0) {
+ --sd->params.exposure.gain;
+ setexp = 1;
+ }
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+ sd->exposure_status == EXPOSURE_LIGHT) &&
+ sd->exposure_count >= LIGHT_TIME * framerate &&
+ sd->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ --sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ if (sd->params.exposure.gain <
+ sd->params.exposure.gainMode - 1) {
+ ++sd->params.exposure.gain;
+ setexp = 1;
+ }
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+ }
+ }
+
+ if (setexp)
+ command_setexposure(gspca_dev);
+
+ if (setfps)
+ command_setsensorfps(gspca_dev);
+
+ if (setflicker)
+ command_setflickerctrl(gspca_dev);
+}
+
+/*-----------------------------------------------------------------*/
+/* if flicker is switched off, this function switches it back on.It checks,
+ however, that conditions are suitable before restarting it.
+ This should only be called for firmware version 1.2.
+
+ It also adjust the colour balance when an exposure step is detected - as
+ long as flicker is running
+*/
+static void restart_flicker(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int cam_exposure, old_exp;
+
+ if (!FIRMWARE_VERSION(1, 2))
+ return;
+
+ cam_exposure = atomic_read(&sd->cam_exposure);
+
+ if (sd->params.flickerControl.flickerMode == 0 ||
+ cam_exposure == 0)
+ return;
+
+ old_exp = sd->params.exposure.coarseExpLo +
+ sd->params.exposure.coarseExpHi*256;
+ /*
+ see how far away camera exposure is from a valid
+ flicker exposure value
+ */
+ cam_exposure %= sd->params.flickerControl.coarseJump;
+ if (!sd->params.flickerControl.disabled &&
+ cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
+ /* Flicker control auto-disabled */
+ sd->params.flickerControl.disabled = 1;
+ }
+
+ if (sd->params.flickerControl.disabled &&
+ old_exp > sd->params.flickerControl.coarseJump +
+ ROUND_UP_EXP_FOR_FLICKER) {
+ /* exposure is now high enough to switch
+ flicker control back on */
+ set_flicker(gspca_dev, 1, 1);
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+
+ reset_camera_params(gspca_dev);
+
+ PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
+ id->idVendor, id->idProduct);
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = mode;
+ cam->nmodes = ARRAY_SIZE(mode);
+
+ sd_setfreq(gspca_dev, FREQ_DEF);
+
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int priv, ret;
+
+ /* Start the camera in low power mode */
+ if (goto_low_power(gspca_dev)) {
+ if (sd->params.status.systemState != WARM_BOOT_STATE) {
+ PDEBUG(D_ERR, "unexpected systemstate: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ return -ENODEV;
+ }
+
+ /* FIXME: this is just dirty trial and error */
+ ret = goto_high_power(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
+ 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = goto_low_power(gspca_dev);
+ if (ret)
+ return ret;
+ }
+
+ /* procedure described in developer's guide p3-28 */
+
+ /* Check the firmware version. */
+ sd->params.version.firmwareVersion = 0;
+ get_version_information(gspca_dev);
+ if (sd->params.version.firmwareVersion != 1) {
+ PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
+ sd->params.version.firmwareVersion);
+ return -ENODEV;
+ }
+
+ /* A bug in firmware 1-02 limits gainMode to 2 */
+ if (sd->params.version.firmwareRevision <= 2 &&
+ sd->params.exposure.gainMode > 2) {
+ sd->params.exposure.gainMode = 2;
+ }
+
+ /* set QX3 detected flag */
+ sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
+ sd->params.pnpID.product == 0x0001);
+
+ /* The fatal error checking should be done after
+ * the camera powers up (developer's guide p 3-38) */
+
+ /* Set streamState before transition to high power to avoid bug
+ * in firmware 1-02 */
+ ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
+ STREAMSTATE, 0, STREAM_NOT_READY, 0);
+ if (ret)
+ return ret;
+
+ /* GotoHiPower */
+ ret = goto_high_power(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Check the camera status */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.fatalError) {
+ PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x",
+ sd->params.status.fatalError,
+ sd->params.status.vpStatus);
+ return -EIO;
+ }
+
+ /* VPVersion can't be retrieved before the camera is in HiPower,
+ * so get it here instead of in get_version_information. */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ /* Determine video mode settings */
+ sd->params.streamStartLine = 120;
+
+ priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (priv & 0x01) { /* crop */
+ sd->params.roi.colStart = 2;
+ sd->params.roi.rowStart = 6;
+ } else {
+ sd->params.roi.colStart = 0;
+ sd->params.roi.rowStart = 0;
+ }
+
+ if (priv & 0x02) { /* quarter */
+ sd->params.format.videoSize = VIDEOSIZE_QCIF;
+ sd->params.roi.colStart /= 2;
+ sd->params.roi.rowStart /= 2;
+ sd->params.streamStartLine /= 2;
+ } else
+ sd->params.format.videoSize = VIDEOSIZE_CIF;
+
+ sd->params.roi.colEnd = sd->params.roi.colStart +
+ (gspca_dev->width >> 3);
+ sd->params.roi.rowEnd = sd->params.roi.rowStart +
+ (gspca_dev->height >> 2);
+
+ /* And now set the camera to a known state */
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
+ CPIA_GRAB_CONTINEOUS, 0, 0, 0);
+ if (ret)
+ return ret;
+ /* We start with compression disabled, as we need one uncompressed
+ frame to handle later compressed frames */
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+ CPIA_COMPRESSION_NONE,
+ NO_DECIMATION, 0, 0);
+ if (ret)
+ return ret;
+ ret = command_setcompressiontarget(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcolourparams(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setformat(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setyuvtresh(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setecptiming(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcompressionparams(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setexposure(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcolourbalance(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setsensorfps(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setapcor(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setflickerctrl(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setvloffset(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Start stream */
+ ret = command_resume(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Wait 6 frames before turning compression on for the sensor to get
+ all settings and AEC/ACB to settle */
+ sd->first_frame = 6;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ sd->exposure_count = 0;
+ atomic_set(&sd->cam_exposure, 0);
+ atomic_set(&sd->fps, 0);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ command_pause(gspca_dev);
+
+ /* save camera state for later open (developers guide ch 3.5.3) */
+ save_camera_state(gspca_dev);
+
+ /* GotoLoPower */
+ goto_low_power(gspca_dev);
+
+ /* Update the camera status */
+ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ /* Start / Stop the camera to make sure we are talking to
+ a supported camera, and to get some information from it
+ to print. */
+ ret = sd_start(gspca_dev);
+ if (ret)
+ return ret;
+
+ sd_stopN(gspca_dev);
+
+ PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)",
+ sd->params.version.firmwareVersion,
+ sd->params.version.firmwareRevision,
+ sd->params.version.vcVersion,
+ sd->params.version.vcRevision);
+ PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x",
+ sd->params.pnpID.vendor, sd->params.pnpID.product,
+ sd->params.pnpID.deviceRevision);
+ PDEBUG(D_PROBE, "VP-Version: %d.%d %04x",
+ sd->params.vpVersion.vpVersion,
+ sd->params.vpVersion.vpRevision,
+ sd->params.vpVersion.cameraHeadID);
+
+ return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Check for SOF */
+ if (len >= 64 &&
+ data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
+ data[16] == sd->params.format.videoSize &&
+ data[17] == sd->params.format.subSample &&
+ data[18] == sd->params.format.yuvOrder &&
+ data[24] == sd->params.roi.colStart &&
+ data[25] == sd->params.roi.colEnd &&
+ data[26] == sd->params.roi.rowStart &&
+ data[27] == sd->params.roi.rowEnd) {
+ struct gspca_frame *frame = gspca_get_i_frame(gspca_dev);
+
+ atomic_set(&sd->cam_exposure, data[39] * 2);
+ atomic_set(&sd->fps, data[41]);
+
+ if (frame == NULL) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ /* Check for proper EOF for last frame */
+ if ((frame->data_end - frame->data) > 4 &&
+ frame->data_end[-4] == 0xff &&
+ frame->data_end[-3] == 0xff &&
+ frame->data_end[-2] == 0xff &&
+ frame->data_end[-1] == 0xff)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Set the normal compression settings once we have captured a
+ few uncompressed frames (and AEC has hopefully settled) */
+ if (sd->first_frame) {
+ sd->first_frame--;
+ if (sd->first_frame == 0)
+ command_setcompression(gspca_dev);
+ }
+
+ /* Switch flicker control back on if it got turned off */
+ restart_flicker(gspca_dev);
+
+ /* If AEC is enabled, monitor the exposure and
+ adjust the sensor frame rate if needed */
+ if (sd->params.exposure.expMode == 2)
+ monitor_exposure(gspca_dev);
+
+ /* Update our knowledge of the camera state */
+ do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+ if (sd->params.qx3.qx3_detected)
+ do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ sd->params.colourParams.brightness = val;
+ sd->params.flickerControl.allowableOverExposure =
+ find_over_exposure(sd->params.colourParams.brightness);
+ if (gspca_dev->streaming) {
+ ret = command_setcolourparams(gspca_dev);
+ if (ret)
+ return ret;
+ return command_setflickerctrl(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.colourParams.brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->params.colourParams.contrast = val;
+ if (gspca_dev->streaming)
+ return command_setcolourparams(gspca_dev);
+
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.colourParams.contrast;
+ return 0;
+}
+
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->params.colourParams.saturation = val;
+ if (gspca_dev->streaming)
+ return command_setcolourparams(gspca_dev);
+
+ return 0;
+}
+
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.colourParams.saturation;
+ return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int on;
+
+ switch (val) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ on = 0;
+ break;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ on = 1;
+ sd->mainsFreq = 0;
+ break;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ on = 1;
+ sd->mainsFreq = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sd->freq = val;
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+
+ return set_flicker(gspca_dev, on, gspca_dev->streaming);
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->freq;
+ return 0;
+}
+
+static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->params.compressionTarget.frTargeting = val;
+ if (gspca_dev->streaming)
+ return command_setcompressiontarget(gspca_dev);
+
+ return 0;
+}
+
+static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.compressionTarget.frTargeting;
+ 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;
+ case V4L2_CID_COMP_TARGET:
+ switch (menu->index) {
+ case CPIA_COMPRESSION_TARGET_QUALITY:
+ strcpy((char *) menu->name, "Quality");
+ return 0;
+ case CPIA_COMPRESSION_TARGET_FRAMERATE:
+ strcpy((char *) menu->name, "Framerate");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+/* 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,
+ .dq_callback = sd_dq_callback,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0553, 0x0002)},
+ {USB_DEVICE(0x0813, 0x0001)},
+ {}
+};
+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/etoms.c b/drivers/media/video/gspca/etoms.c
index fdf4c0ec5e7..ecd4d743d2b 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -52,7 +52,7 @@ 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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -851,7 +851,7 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
}
/* sub-driver description */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index 4878c8f6654..9e42476c0ea 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -161,7 +161,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev)
/*==================== sud-driver structure initialisation =================*/
-static struct sd_desc sd_desc_mi1320 = {
+static const struct sd_desc sd_desc_mi1320 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi1320,
.nctrls = GL860_NCTRLS,
@@ -174,7 +174,7 @@ static struct sd_desc sd_desc_mi1320 = {
.dq_callback = sd_callback,
};
-static struct sd_desc sd_desc_mi2020 = {
+static const struct sd_desc sd_desc_mi2020 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi2020,
.nctrls = GL860_NCTRLS,
@@ -187,7 +187,7 @@ static struct sd_desc sd_desc_mi2020 = {
.dq_callback = sd_callback,
};
-static struct sd_desc sd_desc_mi2020b = {
+static const struct sd_desc sd_desc_mi2020b = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi2020b,
.nctrls = GL860_NCTRLS,
@@ -200,7 +200,7 @@ static struct sd_desc sd_desc_mi2020b = {
.dq_callback = sd_callback,
};
-static struct sd_desc sd_desc_ov2640 = {
+static const struct sd_desc sd_desc_ov2640 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_ov2640,
.nctrls = GL860_NCTRLS,
@@ -213,7 +213,7 @@ static struct sd_desc sd_desc_ov2640 = {
.dq_callback = sd_callback,
};
-static struct sd_desc sd_desc_ov9655 = {
+static const struct sd_desc sd_desc_ov9655 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_ov9655,
.nctrls = GL860_NCTRLS,
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index bd6214d4ab3..222af479150 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -3,6 +3,9 @@
*
* Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr)
*
+ * Camera button input handling by Márton Németh
+ * Copyright (C) 2009-2010 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 (at your
@@ -37,6 +40,11 @@
#include "gspca.h"
+#ifdef CONFIG_INPUT
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#endif
+
/* global values */
#define DEF_NURBS 3 /* default number of URBs */
#if DEF_NURBS > MAX_NURBS
@@ -47,7 +55,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, 8, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 9, 0)
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
@@ -104,15 +112,185 @@ static const struct vm_operations_struct gspca_vm_ops = {
.close = gspca_vm_close,
};
+/*
+ * Input and interrupt endpoint handling functions
+ */
+#ifdef CONFIG_INPUT
+static void int_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ int ret;
+
+ ret = urb->status;
+ switch (ret) {
+ case 0:
+ if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
+ urb->transfer_buffer, urb->actual_length) < 0) {
+ PDEBUG(D_ERR, "Unknown packet received");
+ }
+ break;
+
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ /* Stop is requested either by software or hardware is gone,
+ * keep the ret value non-zero and don't resubmit later.
+ */
+ break;
+
+ default:
+ PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status);
+ urb->status = 0;
+ ret = 0;
+ }
+
+ if (ret == 0) {
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ PDEBUG(D_ERR, "Resubmit URB failed with error %i", ret);
+ }
+}
+
+static int gspca_input_connect(struct gspca_dev *dev)
+{
+ struct input_dev *input_dev;
+ int err = 0;
+
+ dev->input_dev = NULL;
+ if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) {
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ usb_make_path(dev->dev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = dev->sd_desc->name;
+ input_dev->phys = dev->phys;
+
+ usb_to_input_id(dev->dev, &input_dev->id);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+ input_dev->dev.parent = &dev->dev->dev;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ PDEBUG(D_ERR, "Input device registration failed "
+ "with error %i", err);
+ input_dev->dev.parent = NULL;
+ input_free_device(input_dev);
+ } else {
+ dev->input_dev = input_dev;
+ }
+ }
+
+ return err;
+}
+
+static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev,
+ struct usb_endpoint_descriptor *ep)
+{
+ unsigned int buffer_len;
+ int interval;
+ struct urb *urb;
+ struct usb_device *dev;
+ void *buffer = NULL;
+ int ret = -EINVAL;
+
+ buffer_len = ep->wMaxPacketSize;
+ interval = ep->bInterval;
+ PDEBUG(D_PROBE, "found int in endpoint: 0x%x, "
+ "buffer_len=%u, interval=%u",
+ ep->bEndpointAddress, buffer_len, interval);
+
+ dev = gspca_dev->dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ buffer = usb_buffer_alloc(dev, ep->wMaxPacketSize,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto error_buffer;
+ }
+ usb_fill_int_urb(urb, dev,
+ usb_rcvintpipe(dev, ep->bEndpointAddress),
+ buffer, buffer_len,
+ int_irq, (void *)gspca_dev, interval);
+ gspca_dev->int_urb = urb;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "submit URB failed with error %i", ret);
+ goto error_submit;
+ }
+ return ret;
+
+error_submit:
+ usb_buffer_free(dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+error_buffer:
+ usb_free_urb(urb);
+error:
+ return ret;
+}
+
+static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *intf_desc;
+ struct usb_endpoint_descriptor *ep;
+ int i;
+
+ if (gspca_dev->sd_desc->int_pkt_scan) {
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ intf_desc = intf->cur_altsetting;
+ for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+ ep = &intf_desc->endpoint[i].desc;
+ if (usb_endpoint_dir_in(ep) &&
+ usb_endpoint_xfer_int(ep)) {
+
+ alloc_and_submit_int_urb(gspca_dev, ep);
+ break;
+ }
+ }
+ }
+}
+
+static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+
+ urb = gspca_dev->int_urb;
+ if (urb) {
+ gspca_dev->int_urb = NULL;
+ usb_kill_urb(urb);
+ usb_buffer_free(gspca_dev->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+}
+#else
+#define gspca_input_connect(gspca_dev) 0
+#define gspca_input_create_urb(gspca_dev)
+#define gspca_input_destroy_urb(gspca_dev)
+#endif
+
/* get the current input frame buffer */
struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev)
{
struct gspca_frame *frame;
- int i;
- i = gspca_dev->fr_i;
- i = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[i];
+ frame = gspca_dev->cur_frame;
if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
!= V4L2_BUF_FLAG_QUEUED)
return NULL;
@@ -486,11 +664,13 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
i, ep->desc.bEndpointAddress);
gspca_dev->alt = i; /* memorize the current alt setting */
if (gspca_dev->nbalt > 1) {
+ gspca_input_destroy_urb(gspca_dev);
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
if (ret < 0) {
err("set alt %d err %d", i, ret);
- return NULL;
+ ep = NULL;
}
+ gspca_input_create_urb(gspca_dev);
}
return ep;
}
@@ -534,26 +714,22 @@ static int create_urbs(struct gspca_dev *gspca_dev,
nurbs = 1;
}
- gspca_dev->nurbs = nurbs;
for (n = 0; n < nurbs; n++) {
urb = usb_alloc_urb(npkt, GFP_KERNEL);
if (!urb) {
err("usb_alloc_urb failed");
- destroy_urbs(gspca_dev);
return -ENOMEM;
}
+ gspca_dev->urb[n] = urb;
urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev,
bsize,
GFP_KERNEL,
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
- usb_free_urb(urb);
- err("usb_buffer_urb failed");
- destroy_urbs(gspca_dev);
+ err("usb_buffer_alloc failed");
return -ENOMEM;
}
- gspca_dev->urb[n] = urb;
urb->dev = gspca_dev->dev;
urb->context = gspca_dev;
urb->transfer_buffer_length = bsize;
@@ -585,6 +761,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
static int gspca_init_transfer(struct gspca_dev *gspca_dev)
{
struct usb_host_endpoint *ep;
+ struct urb *urb;
int n, ret;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
@@ -595,6 +772,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
goto out;
}
+ gspca_dev->usb_err = 0;
+
/* set the higher alternate setting and
* loop until urb submit succeeds */
if (gspca_dev->cam.reverse_alts)
@@ -613,10 +792,15 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
goto out;
}
for (;;) {
- PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt);
- ret = create_urbs(gspca_dev, ep);
- if (ret < 0)
- goto out;
+ if (!gspca_dev->cam.no_urb_create) {
+ PDEBUG(D_STREAM, "init transfer alt %d",
+ gspca_dev->alt);
+ ret = create_urbs(gspca_dev, ep);
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
+ }
/* clear the bulk endpoint */
if (gspca_dev->cam.bulk)
@@ -636,8 +820,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
break;
/* submit the URBs */
- for (n = 0; n < gspca_dev->nurbs; n++) {
- ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL);
+ for (n = 0; n < MAX_NURBS; n++) {
+ urb = gspca_dev->urb[n];
+ if (urb == NULL)
+ break;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret < 0)
break;
}
@@ -694,7 +881,9 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev)
if (gspca_dev->sd_desc->stopN)
gspca_dev->sd_desc->stopN(gspca_dev);
destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
gspca_set_alt0(gspca_dev);
+ gspca_input_create_urb(gspca_dev);
}
/* always call stop0 to free the subdriver's resources */
@@ -2060,11 +2249,12 @@ int gspca_dev_probe(struct usb_interface *intf,
PDEBUG(D_ERR, "Too many config");
return -ENODEV;
}
+
+ /* the USB video interface must be the first one */
interface = &intf->cur_altsetting->desc;
- if (interface->bInterfaceNumber > 0) {
- PDEBUG(D_ERR, "intf != 0");
+ if (dev->config->desc.bNumInterfaces != 1 &&
+ interface->bInterfaceNumber != 0)
return -ENODEV;
- }
/* create the device */
if (dev_size < sizeof *gspca_dev)
@@ -2096,6 +2286,10 @@ int gspca_dev_probe(struct usb_interface *intf,
goto out;
gspca_set_default_mode(gspca_dev);
+ ret = gspca_input_connect(gspca_dev);
+ if (ret)
+ goto out;
+
mutex_init(&gspca_dev->usb_lock);
mutex_init(&gspca_dev->read_lock);
mutex_init(&gspca_dev->queue_lock);
@@ -2116,8 +2310,15 @@ int gspca_dev_probe(struct usb_interface *intf,
usb_set_intfdata(intf, gspca_dev);
PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
+
+ gspca_input_create_urb(gspca_dev);
+
return 0;
out:
+#ifdef CONFIG_INPUT
+ if (gspca_dev->input_dev)
+ input_unregister_device(gspca_dev->input_dev);
+#endif
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
return ret;
@@ -2133,6 +2334,9 @@ EXPORT_SYMBOL(gspca_dev_probe);
void gspca_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+#ifdef CONFIG_INPUT
+ struct input_dev *input_dev;
+#endif
PDEBUG(D_PROBE, "%s disconnect",
video_device_node_name(&gspca_dev->vdev));
@@ -2144,6 +2348,15 @@ void gspca_disconnect(struct usb_interface *intf)
wake_up_interruptible(&gspca_dev->wq);
}
+#ifdef CONFIG_INPUT
+ gspca_input_destroy_urb(gspca_dev);
+ input_dev = gspca_dev->input_dev;
+ if (input_dev) {
+ gspca_dev->input_dev = NULL;
+ input_unregister_device(input_dev);
+ }
+#endif
+
/* the device is freed at exit of this function */
gspca_dev->dev = NULL;
mutex_unlock(&gspca_dev->usb_lock);
@@ -2169,6 +2382,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message)
if (gspca_dev->sd_desc->stopN)
gspca_dev->sd_desc->stopN(gspca_dev);
destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
gspca_set_alt0(gspca_dev);
if (gspca_dev->sd_desc->stop0)
gspca_dev->sd_desc->stop0(gspca_dev);
@@ -2182,6 +2396,7 @@ int gspca_resume(struct usb_interface *intf)
gspca_dev->frozen = 0;
gspca_dev->sd_desc->init(gspca_dev);
+ gspca_input_create_urb(gspca_dev);
if (gspca_dev->streaming)
return gspca_init_transfer(gspca_dev);
return 0;
@@ -2205,6 +2420,8 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
int retval = 0;
for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 59c7941da99..02c696a22be 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -48,26 +48,27 @@ extern int gspca_debug;
/* used to list framerates supported by a camera mode (resolution) */
struct framerates {
- int *rates;
+ const u8 *rates;
int nrates;
};
/* device information - set at probe time */
struct cam {
- int bulk_size; /* buffer size when image transfer by bulk */
const struct v4l2_pix_format *cam_mode; /* size nmodes */
- char nmodes;
const struct framerates *mode_framerates; /* must have size nmode,
* just like cam_mode */
- __u8 bulk_nurbs; /* number of URBs in bulk mode
+ u32 bulk_size; /* buffer size when image transfer by bulk */
+ u32 input_flags; /* value for ENUM_INPUT status flags */
+ u8 nmodes; /* size of cam_mode */
+ u8 no_urb_create; /* don't create transfer URBs */
+ u8 bulk_nurbs; /* number of URBs in bulk mode
* - cannot be > MAX_NURBS
* - when 0 and bulk_size != 0 means
* 1 URB and submit done by subdriver */
u8 bulk; /* image transfer by 0:isoc / 1:bulk */
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 */
+ u8 reverse_alts; /* Alt settings are in high to low order */
};
struct gspca_dev;
@@ -90,6 +91,9 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *,
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
u8 *data,
int len);
+typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len);
struct ctrl {
struct v4l2_queryctrl qctrl;
@@ -125,6 +129,12 @@ struct sd_desc {
cam_reg_op get_register;
#endif
cam_ident_op get_chip_ident;
+#ifdef CONFIG_INPUT
+ cam_int_pkt_op int_pkt_scan;
+ /* other_input makes the gspca core create gspca_dev->input even when
+ int_pkt_scan is NULL, for cams with non interrupt driven buttons */
+ u8 other_input;
+#endif
};
/* packet types when moving from iso buf to frame buf */
@@ -147,6 +157,10 @@ struct gspca_dev {
struct module *module; /* subdriver handling the device */
struct usb_device *dev;
struct file *capt_file; /* file doing video capture */
+#ifdef CONFIG_INPUT
+ struct input_dev *input_dev;
+ char phys[64]; /* physical device path */
+#endif
struct cam cam; /* device information */
const struct sd_desc *sd_desc; /* subdriver description */
@@ -156,6 +170,9 @@ struct gspca_dev {
#define USB_BUF_SZ 64
__u8 *usb_buf; /* buffer for USB exchanges */
struct urb *urb[MAX_NURBS];
+#ifdef CONFIG_INPUT
+ struct urb *int_urb;
+#endif
__u8 *frbuf; /* buffer for nframes */
struct gspca_frame frame[GSPCA_MAX_FRAMES];
@@ -187,7 +204,6 @@ struct gspca_dev {
char users; /* number of opens */
char present; /* device connected */
char nbufread; /* number of buffers for read() */
- char nurbs; /* number of allocated URBs */
char memory; /* memory type (V4L2_MEMORY_xxx) */
__u8 iface; /* USB interface number */
__u8 alt; /* USB alternate setting */
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index 8d071dff694..c0722fa6460 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -48,7 +48,7 @@ static struct v4l2_pix_format mt9m111_modes[] = {
}
};
-const static struct ctrl mt9m111_ctrls[] = {
+static const struct ctrl mt9m111_ctrls[] = {
#define VFLIP_IDX 0
{
{
@@ -171,7 +171,7 @@ int mt9m111_probe(struct sd *sd)
return -ENODEV;
}
- info("Probing for a mt9m111 sensor");
+ PDEBUG(D_PROBE, "Probing for a mt9m111 sensor");
/* Do the preinit */
for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c
index 2a28b74cb3f..62c1cbf0666 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c
@@ -33,7 +33,7 @@ static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
-const static struct ctrl ov7660_ctrls[] = {
+static const struct ctrl ov7660_ctrls[] = {
#define GAIN_IDX 1
{
{
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h
index f5588ebe667..4d9dcf29da2 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.h
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.h
@@ -94,7 +94,7 @@ int ov7660_start(struct sd *sd);
int ov7660_stop(struct sd *sd);
void ov7660_disconnect(struct sd *sd);
-const static struct m5602_sensor ov7660 = {
+static const struct m5602_sensor ov7660 = {
.name = "ov7660",
.i2c_slave_id = 0x42,
.i2c_regW = 1,
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 923cdd5f7a6..069ba0044f8 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -307,7 +307,7 @@ int ov9650_probe(struct sd *sd)
return -ENODEV;
}
- info("Probing for an ov9650 sensor");
+ PDEBUG(D_PROBE, "Probing for an ov9650 sensor");
/* Run the pre-init before probing the sensor */
for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
index 8d74d8065b7..925b87d66f4 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -205,7 +205,7 @@ int po1030_probe(struct sd *sd)
return -ENODEV;
}
- info("Probing for a po1030 sensor");
+ PDEBUG(D_PROBE, "Probing for a po1030 sensor");
/* Run the pre-init to actually probe the unit */
for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index 1b536f7d30c..da0a38c7870 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -248,7 +248,7 @@ int s5k4aa_probe(struct sd *sd)
return -ENODEV;
}
- info("Probing for a s5k4aa sensor");
+ PDEBUG(D_PROBE, "Probing for a s5k4aa sensor");
/* Preinit the sensor */
for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index 6b89f33a4ce..fbd91545497 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -143,7 +143,7 @@ int s5k83a_probe(struct sd *sd)
return -ENODEV;
}
- info("Probing for a s5k83a sensor");
+ PDEBUG(D_PROBE, "Probing for a s5k83a sensor");
/* Preinit the sensor */
for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 9cf8d68c71b..3d9229e22b2 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -54,7 +54,7 @@ static int sd_getgamma(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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index 9154870e07d..33744e724ea 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -57,6 +57,14 @@
#define MR97310A_GAIN_MAX 31
#define MR97310A_GAIN_DEFAULT 25
+#define MR97310A_CONTRAST_MIN 0
+#define MR97310A_CONTRAST_MAX 31
+#define MR97310A_CONTRAST_DEFAULT 23
+
+#define MR97310A_CS_GAIN_MIN 0
+#define MR97310A_CS_GAIN_MAX 0x7ff
+#define MR97310A_CS_GAIN_DEFAULT 0x110
+
#define MR97310A_MIN_CLOCKDIV_MIN 3
#define MR97310A_MIN_CLOCKDIV_MAX 8
#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
@@ -82,7 +90,8 @@ struct sd {
int brightness;
u16 exposure;
- u8 gain;
+ u32 gain;
+ u8 contrast;
u8 min_clockdiv;
};
@@ -98,6 +107,8 @@ 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_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(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);
@@ -105,11 +116,13 @@ 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);
+static void setcontrast(struct gspca_dev *gspca_dev);
/* V4L2 controls supported by the driver */
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
/* Separate brightness control description for Argus QuickClix as it has
- different limits from the other mr97310a cameras */
+ * different limits from the other mr97310a cameras, and separate gain
+ * control for Sakar CyberPix camera. */
{
#define NORM_BRIGHTNESS_IDX 0
{
@@ -171,7 +184,37 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getgain,
},
{
-#define MIN_CLOCKDIV_IDX 4
+#define SAKAR_CS_GAIN_IDX 4
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = MR97310A_CS_GAIN_MIN,
+ .maximum = MR97310A_CS_GAIN_MAX,
+ .step = 1,
+ .default_value = MR97310A_CS_GAIN_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ {
+#define CONTRAST_IDX 5
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = MR97310A_CONTRAST_MIN,
+ .maximum = MR97310A_CONTRAST_MAX,
+ .step = 1,
+ .default_value = MR97310A_CONTRAST_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+#define MIN_CLOCKDIV_IDX 6
{
.id = V4L2_CID_PRIVATE_BASE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -327,7 +370,6 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
if (err_code < 0)
return err_code;
- err_code = mr_write(gspca_dev, 1);
data[0] = 0x19;
data[1] = 0x51;
err_code = mr_write(gspca_dev, 2);
@@ -437,6 +479,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
+ int gain_default = MR97310A_GAIN_DEFAULT;
int err_code;
cam = &gspca_dev->cam;
@@ -460,12 +503,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
if (err_code < 0)
return err_code;
+ /* Now, the query for sensor type. */
+ err_code = cam_get_response16(gspca_dev, 0x07, 1);
+ if (err_code < 0)
+ return err_code;
+
if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
sd->cam_type = CAM_TYPE_CIF;
cam->nmodes--;
- err_code = cam_get_response16(gspca_dev, 0x06, 1);
- if (err_code < 0)
- return err_code;
/*
* All but one of the known CIF cameras share the same USB ID,
* but two different init routines are in use, and the control
@@ -473,12 +518,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
* of the two known varieties is connected!
*
* A list of known CIF cameras follows. They all report either
- * 0002 for type 0 or 0003 for type 1.
+ * 0200 for type 0 or 0300 for type 1.
* If you have another to report, please do
*
* Name sd->sensor_type reported by
*
- * Sakar Spy-shot 0 T. Kilgore
+ * Sakar 56379 Spy-shot 0 T. Kilgore
* Innovage 0 T. Kilgore
* Vivitar Mini 0 H. De Goede
* Vivitar Mini 0 E. Rodriguez
@@ -487,7 +532,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
* Philips dig. keych. 1 T. Kilgore
* Trust Spyc@m 100 1 A. Jacobs
*/
- switch (gspca_dev->usb_buf[1]) {
+ switch (gspca_dev->usb_buf[0]) {
case 2:
sd->sensor_type = 0;
break;
@@ -504,20 +549,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
} else {
sd->cam_type = CAM_TYPE_VGA;
- 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.
+ * Here is a table of the responses to the query for sensor
+ * type, from the known MR97310A VGA cameras. Six different
+ * cameras of which five share the same USB ID.
*
* Name gspca_dev->usb_buf[] sd->sensor_type
* sd->do_lcd_stop
* Aiptek Pencam VGA+ 0300 0 1
- * ION digital 0350 0 1
+ * ION digital 0300 0 1
* Argus DC-1620 0450 1 0
* Argus QuickClix 0420 1 1
+ * Sakar 77379 Digital 0350 0 1
+ * Sakar 1638x CyberPix 0120 0 2
*
* Based upon these results, we assume default settings
* and then correct as necessary, as follows.
@@ -527,10 +571,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor_type = 1;
sd->do_lcd_stop = 0;
sd->adj_colors = 0;
- if ((gspca_dev->usb_buf[0] != 0x03) &&
+ if (gspca_dev->usb_buf[0] == 0x01) {
+ sd->sensor_type = 2;
+ } else 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]);
+ gspca_dev->usb_buf[0]);
PDEBUG(D_ERR, "Defaults assumed, may not work");
PDEBUG(D_ERR, "Please report this");
}
@@ -560,7 +606,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
sd->sensor_type);
}
- /* Stop streaming as we've started it to probe the sensor type. */
+ /* Stop streaming as we've started it only to probe the sensor type. */
sd_stopN(gspca_dev);
if (force_sensor_type != -1) {
@@ -574,9 +620,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* No brightness for sensor_type 0 */
if (sd->sensor_type == 0)
gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX);
+ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX);
else
gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX) |
(1 << MIN_CLOCKDIV_IDX);
} else {
/* All controls need to be disabled if VGA sensor_type is 0 */
@@ -585,17 +635,30 @@ static int sd_config(struct gspca_dev *gspca_dev,
(1 << ARGUS_QC_BRIGHTNESS_IDX) |
(1 << EXPOSURE_IDX) |
(1 << GAIN_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX) |
(1 << MIN_CLOCKDIV_IDX);
- else if (sd->do_lcd_stop)
+ else if (sd->sensor_type == 2) {
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << GAIN_IDX) |
+ (1 << MIN_CLOCKDIV_IDX);
+ gain_default = MR97310A_CS_GAIN_DEFAULT;
+ } else if (sd->do_lcd_stop)
/* Argus QuickClix has different brightness limits */
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX);
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX);
else
- gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX);
+ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX);
}
sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
sd->exposure = MR97310A_EXPOSURE_DEFAULT;
- sd->gain = MR97310A_GAIN_DEFAULT;
+ sd->gain = gain_default;
+ sd->contrast = MR97310A_CONTRAST_DEFAULT;
sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
return 0;
@@ -697,6 +760,12 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
{0x13, 0x00, {0x01}, 1},
{0, 0, {0}, 0}
};
+ /* Without this command the cam won't work with USB-UHCI */
+ gspca_dev->usb_buf[0] = 0x0a;
+ gspca_dev->usb_buf[1] = 0x00;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
ARRAY_SIZE(cif_sensor1_init_data));
}
@@ -717,6 +786,10 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
data[5] = 0x00;
data[10] = 0x91;
}
+ if (sd->sensor_type == 2) {
+ data[5] = 0x00;
+ data[10] = 0x18;
+ }
switch (gspca_dev->width) {
case 160:
@@ -731,6 +804,10 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
data[4] = 0x78; /* reg 3, V size/4 */
data[6] = 0x04; /* reg 5, H start */
data[8] = 0x03; /* reg 7, V start */
+ if (sd->sensor_type == 2) {
+ data[6] = 2;
+ data[8] = 1;
+ }
if (sd->do_lcd_stop)
data[8] = 0x04; /* Bayer tile shifted */
break;
@@ -753,7 +830,6 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
return err_code;
if (!sd->sensor_type) {
- /* The only known sensor_type 0 cam is the Argus DC-1620 */
const struct sensor_w_data vga_sensor0_init_data[] = {
{0x01, 0x00, {0x0c, 0x00, 0x04}, 3},
{0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4},
@@ -764,7 +840,7 @@ 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 */
+ } else if (sd->sensor_type == 1) {
const struct sensor_w_data color_adj[] = {
{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
/* adjusted blue, green, red gain correct
@@ -802,6 +878,48 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
ARRAY_SIZE(vga_sensor1_init_data));
+ } else { /* sensor type == 2 */
+ const struct sensor_w_data vga_sensor2_init_data[] = {
+
+ {0x01, 0x00, {0x48}, 1},
+ {0x02, 0x00, {0x22}, 1},
+ /* Reg 3 msb and 4 is lsb of the exposure setting*/
+ {0x05, 0x00, {0x10}, 1},
+ {0x06, 0x00, {0x00}, 1},
+ {0x07, 0x00, {0x00}, 1},
+ {0x08, 0x00, {0x00}, 1},
+ {0x09, 0x00, {0x00}, 1},
+ /* The following are used in the gain control
+ * which is BTW completely borked in the OEM driver
+ * The values for each color go from 0 to 0x7ff
+ *{0x0a, 0x00, {0x01}, 1}, green1 gain msb
+ *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb
+ *{0x0c, 0x00, {0x01}, 1}, red gain msb
+ *{0x0d, 0x00, {0x10}, 1}, red gain lsb
+ *{0x0e, 0x00, {0x01}, 1}, blue gain msb
+ *{0x0f, 0x00, {0x10}, 1}, blue gain lsb
+ *{0x10, 0x00, {0x01}, 1}, green2 gain msb
+ *{0x11, 0x00, {0x10}, 1}, green2 gain lsb
+ */
+ {0x12, 0x00, {0x00}, 1},
+ {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */
+ {0x14, 0x00, {0x00}, 1},
+ {0x15, 0x00, {0x06}, 1},
+ {0x16, 0x00, {0x01}, 1},
+ {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */
+ {0x18, 0x00, {0x02}, 1},
+ {0x19, 0x00, {0x82}, 1}, /* don't mess with */
+ {0x1a, 0x00, {0x00}, 1},
+ {0x1b, 0x00, {0x20}, 1},
+ /* {0x1c, 0x00, {0x17}, 1}, contrast control */
+ {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */
+ {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */
+ {0x1f, 0x00, {0x0c}, 1},
+ {0x20, 0x00, {0x00}, 1},
+ {0, 0, {0}, 0}
+ };
+ err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data,
+ ARRAY_SIZE(vga_sensor2_init_data));
}
return err_code;
}
@@ -834,6 +952,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
return err_code;
setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
setexposure(gspca_dev);
setgain(gspca_dev);
@@ -893,7 +1012,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int exposure;
+ int exposure = MR97310A_EXPOSURE_DEFAULT;
u8 buf[2];
if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
@@ -905,6 +1024,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
exposure = (sd->exposure * 9267) / 10000 + 300;
sensor_write1(gspca_dev, 3, exposure >> 4);
sensor_write1(gspca_dev, 4, exposure & 0x0f);
+ } else if (sd->sensor_type == 2) {
+ exposure = sd->exposure;
+ exposure >>= 3;
+ sensor_write1(gspca_dev, 3, exposure >> 8);
+ sensor_write1(gspca_dev, 4, exposure & 0xff);
} else {
/* We have both a clock divider and an exposure register.
We first calculate the clock divider, as that determines
@@ -943,17 +1067,34 @@ static void setexposure(struct gspca_dev *gspca_dev)
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 gainreg;
- if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
+ if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
+ (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
return;
- if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
+ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
sensor_write1(gspca_dev, 0x0e, sd->gain);
- } else {
+ else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
+ for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
+ sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
+ sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
+ }
+ else
sensor_write1(gspca_dev, 0x10, sd->gain);
- }
}
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
+ return;
+
+ sensor_write1(gspca_dev, 0x1c, sd->contrast);
+}
+
+
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1008,6 +1149,25 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
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)
+ setcontrast(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_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index b4f96573124..bc4ced6c013 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -38,6 +38,7 @@
*/
#define MODULE_NAME "ov519"
+#include <linux/input.h>
#include "gspca.h"
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
@@ -70,6 +71,9 @@ struct sd {
char invert_led;
#define BRIDGE_INVERT_LED 8
+ char snapshot_pressed;
+ char snapshot_needs_reset;
+
/* Determined by sensor type */
__u8 sif;
@@ -99,10 +103,12 @@ struct sd {
#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
+#define SEN_OV7620AE 8
+#define SEN_OV7640 9
+#define SEN_OV7648 10
+#define SEN_OV7670 11
+#define SEN_OV76BE 12
+#define SEN_OV8610 13
u8 sensor_addr;
int sensor_width;
@@ -139,6 +145,7 @@ static void setautobrightness(struct sd *sd);
static void setfreq(struct sd *sd);
static const struct ctrl sd_ctrls[] = {
+#define BRIGHTNESS_IDX 0
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -153,6 +160,7 @@ static const struct ctrl sd_ctrls[] = {
.set = sd_setbrightness,
.get = sd_getbrightness,
},
+#define CONTRAST_IDX 1
{
{
.id = V4L2_CID_CONTRAST,
@@ -167,6 +175,7 @@ static const struct ctrl sd_ctrls[] = {
.set = sd_setcontrast,
.get = sd_getcontrast,
},
+#define COLOR_IDX 2
{
{
.id = V4L2_CID_SATURATION,
@@ -2554,7 +2563,7 @@ static int ov7xx0_configure(struct sd *sd)
/* I don't know what's different about the 76BE yet. */
if (i2c_r(sd, 0x15) & 1) {
PDEBUG(D_PROBE, "Sensor is an OV7620AE");
- sd->sensor = SEN_OV7620;
+ sd->sensor = SEN_OV7620AE;
} else {
PDEBUG(D_PROBE, "Sensor is an OV76BE");
sd->sensor = SEN_OV76BE;
@@ -2588,7 +2597,7 @@ static int ov7xx0_configure(struct sd *sd)
break;
case 0x48:
PDEBUG(D_PROBE, "Sensor is an OV7648");
- sd->sensor = SEN_OV7640; /* FIXME */
+ sd->sensor = SEN_OV7648;
break;
default:
PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
@@ -2680,6 +2689,36 @@ static void ov51x_led_control(struct sd *sd, int on)
}
}
+static void sd_reset_snapshot(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->snapshot_needs_reset)
+ return;
+
+ /* Note it is important that we clear sd->snapshot_needs_reset,
+ before actually clearing the snapshot state in the bridge
+ otherwise we might race with the pkt_scan interrupt handler */
+ sd->snapshot_needs_reset = 0;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_SNAP, 0x02);
+ reg_w(sd, R51x_SYS_SNAP, 0x00);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */
+ reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, R51x_SYS_RESET, 0x40);
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ }
+}
+
static int ov51x_upload_quan_tables(struct sd *sd)
{
const unsigned char yQuanTable511[] = {
@@ -3115,7 +3154,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
(1 << OV7670_FREQ_IDX);
}
sd->quality = QUALITY_DEF;
- if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
+ if (sd->sensor == SEN_OV7640 ||
+ sd->sensor == SEN_OV7648)
+ gspca_dev->ctrl_dis |= (1 << AUTOBRIGHT_IDX) |
+ (1 << CONTRAST_IDX);
+ if (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)
@@ -3169,10 +3212,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
return -EIO;
break;
case SEN_OV7620:
+ case SEN_OV7620AE:
if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620)))
return -EIO;
break;
case SEN_OV7640:
+ case SEN_OV7648:
if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640)))
return -EIO;
break;
@@ -3246,7 +3291,9 @@ static int ov511_mode_init_regs(struct sd *sd)
/* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed
for more sensors we need to do this for them too */
case SEN_OV7620:
+ case SEN_OV7620AE:
case SEN_OV7640:
+ case SEN_OV7648:
case SEN_OV76BE:
if (sd->gspca_dev.width == 320)
interlaced = 1;
@@ -3377,7 +3424,7 @@ static int ov518_mode_init_regs(struct sd *sd)
if (sd->bridge == BRIDGE_OV518PLUS) {
switch (sd->sensor) {
- case SEN_OV7620:
+ case SEN_OV7620AE:
if (sd->gspca_dev.width == 320) {
reg_w(sd, 0x20, 0x00);
reg_w(sd, 0x21, 0x19);
@@ -3386,6 +3433,10 @@ static int ov518_mode_init_regs(struct sd *sd)
reg_w(sd, 0x21, 0x1f);
}
break;
+ case SEN_OV7620:
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
+ break;
default:
reg_w(sd, 0x21, 0x19);
}
@@ -3488,7 +3539,8 @@ static int ov519_mode_init_regs(struct sd *sd)
if (write_regvals(sd, mode_init_519,
ARRAY_SIZE(mode_init_519)))
return -EIO;
- if (sd->sensor == SEN_OV7640) {
+ if (sd->sensor == SEN_OV7640 ||
+ sd->sensor == SEN_OV7648) {
/* Select 8-bit input mode */
reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
}
@@ -3503,6 +3555,9 @@ static int ov519_mode_init_regs(struct sd *sd)
if (sd->sensor == SEN_OV7670 &&
sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
+ else if (sd->sensor == SEN_OV7648 &&
+ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x01);
else
reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
@@ -3520,6 +3575,7 @@ static int ov519_mode_init_regs(struct sd *sd)
sd->clockdiv = 0;
switch (sd->sensor) {
case SEN_OV7640:
+ case SEN_OV7648:
switch (sd->frame_rate) {
default:
/* case 30: */
@@ -3649,6 +3705,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
break;
case SEN_OV7620:
+ case SEN_OV7620AE:
case SEN_OV76BE:
i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
@@ -3663,13 +3720,16 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
break;
case SEN_OV7640:
+ case SEN_OV7648:
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); */
+ /* Setting this undocumented bit in qvga mode removes a very
+ annoying vertical shaking of the image */
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ /* Unknown */
+ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+ /* Allow higher automatic gain (to allow higher framerates) */
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
break;
case SEN_OV7670:
@@ -3795,11 +3855,13 @@ static int set_ov_sensor_window(struct sd *sd)
}
break;
case SEN_OV7620:
+ case SEN_OV7620AE:
hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
hwebase = 0x2f;
vwsbase = vwebase = 0x05;
break;
case SEN_OV7640:
+ case SEN_OV7648:
hwsbase = 0x1a;
hwebase = 0x1a;
vwsbase = vwebase = 0x03;
@@ -3893,6 +3955,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
setautobrightness(sd);
setfreq(sd);
+ /* Force clear snapshot state in case the snapshot button was
+ pressed while we weren't streaming */
+ sd->snapshot_needs_reset = 1;
+ sd_reset_snapshot(gspca_dev);
+ sd->snapshot_pressed = 0;
+
ret = ov51x_restart(sd);
if (ret < 0)
goto out;
@@ -3919,6 +3987,34 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
w9968cf_stop0(sd);
}
+static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->snapshot_pressed != state) {
+#ifdef CONFIG_INPUT
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+ input_sync(gspca_dev->input_dev);
+#endif
+ if (state)
+ sd->snapshot_needs_reset = 1;
+
+ sd->snapshot_pressed = state;
+ } else {
+ /* On the ov511 / ov519 we need to reset the button state
+ multiple times, as resetting does not work as long as the
+ button stays pressed */
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ case BRIDGE_OV519:
+ if (state)
+ sd->snapshot_needs_reset = 1;
+ break;
+ }
+ }
+}
+
static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
u8 *in, /* isoc packet */
int len) /* iso packet length */
@@ -3940,6 +4036,7 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
*/
if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) &&
(in[8] & 0x08)) {
+ ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1);
if (in[8] & 0x80) {
/* Frame end */
if ((in[9] + 1) * 8 != gspca_dev->width ||
@@ -3977,6 +4074,7 @@ 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]) {
+ ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1);
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
sd->packet_nr = 0;
@@ -4024,6 +4122,9 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) {
switch (data[3]) {
case 0x50: /* start of frame */
+ /* Don't check the button state here, as the state
+ usually (always ?) changes at EOF and checking it
+ here leads to unnecessary snapshot state resets. */
#define HDRSZ 16
data += HDRSZ;
len -= HDRSZ;
@@ -4035,6 +4136,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
gspca_dev->last_packet_type = DISCARD_PACKET;
return;
case 0x51: /* end of frame */
+ ov51x_handle_button(gspca_dev, data[11] & 1);
if (data[9] != 0)
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_frame_add(gspca_dev, LAST_PACKET,
@@ -4103,9 +4205,11 @@ static void setbrightness(struct gspca_dev *gspca_dev)
case SEN_OV6630:
case SEN_OV66308AF:
case SEN_OV7640:
+ case SEN_OV7648:
i2c_w(sd, OV7610_REG_BRT, val);
break;
case SEN_OV7620:
+ case SEN_OV7620AE:
/* 7620 doesn't like manual changes when in auto mode */
if (!sd->autobrightness)
i2c_w(sd, OV7610_REG_BRT, val);
@@ -4142,7 +4246,8 @@ static void setcontrast(struct gspca_dev *gspca_dev)
i2c_w(sd, 0x64, ctab[val >> 5]);
break;
}
- case SEN_OV7620: {
+ case SEN_OV7620:
+ case SEN_OV7620AE: {
static const __u8 ctab[] = {
0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
@@ -4152,10 +4257,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
i2c_w(sd, 0x64, ctab[val >> 4]);
break;
}
- case SEN_OV7640:
- /* Use gain control instead. */
- i2c_w(sd, OV7610_REG_GAIN, val >> 2);
- break;
case SEN_OV7670:
/* check that this isn't just the same as ov7610 */
i2c_w(sd, OV7670_REG_CONTRAS, val >> 1);
@@ -4179,6 +4280,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
i2c_w(sd, OV7610_REG_SAT, val);
break;
case SEN_OV7620:
+ case SEN_OV7620AE:
/* Use UV gamma control instead. Bits 0 & 7 are reserved. */
/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e);
if (rc < 0)
@@ -4186,6 +4288,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
i2c_w(sd, OV7610_REG_SAT, val);
break;
case SEN_OV7640:
+ case SEN_OV7648:
i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
break;
case SEN_OV7670:
@@ -4198,7 +4301,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_OV7648 ||
+ sd->sensor == SEN_OV7670 ||
sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
return;
@@ -4475,9 +4579,13 @@ static const struct sd_desc sd_desc = {
.stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_reset_snapshot,
.querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
+#ifdef CONFIG_INPUT
+ .other_input = 1,
+#endif
};
/* -- module initialisation -- */
@@ -4494,7 +4602,8 @@ static const __devinitdata struct usb_device_id device_table[] = {
.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
{USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x054c, 0x0155),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
{USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
{USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
{USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 },
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 0a6b8f07a69..957e05e2d08 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -1,5 +1,5 @@
/*
- * ov534 gspca driver
+ * ov534-ov772x gspca driver
*
* Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
* Copyright (C) 2008 Jim Paris <jim@jtan.com>
@@ -68,12 +68,7 @@ struct sd {
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 */
@@ -101,12 +96,8 @@ 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[] = {
+static const struct ctrl sd_ctrls[] = {
{ /* 0 */
{
.id = V4L2_CID_BRIGHTNESS,
@@ -115,8 +106,8 @@ static struct ctrl sd_ctrls_ov772x[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define BRIGHTNESS_77_DEF 20
- .default_value = BRIGHTNESS_77_DEF,
+#define BRIGHTNESS_DEF 20
+ .default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
@@ -129,8 +120,8 @@ static struct ctrl sd_ctrls_ov772x[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define CONTRAST_77_DEF 37
- .default_value = CONTRAST_77_DEF,
+#define CONTRAST_DEF 37
+ .default_value = CONTRAST_DEF,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
@@ -157,8 +148,8 @@ static struct ctrl sd_ctrls_ov772x[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define EXPO_77_DEF 120
- .default_value = EXPO_77_DEF,
+#define EXPO_DEF 120
+ .default_value = EXPO_DEF,
},
.set = sd_setexposure,
.get = sd_getexposure,
@@ -213,13 +204,13 @@ static struct ctrl sd_ctrls_ov772x[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AUTOGAIN_77_DEF 0
- .default_value = AUTOGAIN_77_DEF,
+#define AUTOGAIN_DEF 0
+ .default_value = AUTOGAIN_DEF,
},
.set = sd_setautogain,
.get = sd_getautogain,
},
-#define AWB_77_IDX 8
+#define AWB_IDX 8
{ /* 8 */
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
@@ -242,8 +233,8 @@ static struct ctrl sd_ctrls_ov772x[] = {
.minimum = 0,
.maximum = 63,
.step = 1,
-#define SHARPNESS_77_DEF 0
- .default_value = SHARPNESS_77_DEF,
+#define SHARPNESS_DEF 0
+ .default_value = SHARPNESS_DEF,
},
.set = sd_setsharpness,
.get = sd_getsharpness,
@@ -277,107 +268,6 @@ static struct ctrl sd_ctrls_ov772x[] = {
.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 ov772x_mode[] = {
{320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
@@ -392,35 +282,21 @@ static const struct v4l2_pix_format ov772x_mode[] = {
.priv = 0},
};
-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 = 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 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30};
+static const u8 vga_rates[] = {60, 50, 40, 30, 15};
+
+static const struct framerates ov772x_framerates[] = {
+ { /* 320x240 */
+ .rates = qvga_rates,
+ .nrates = ARRAY_SIZE(qvga_rates),
+ },
+ { /* 640x480 */
+ .rates = vga_rates,
+ .nrates = ARRAY_SIZE(vga_rates),
+ },
};
-static const u8 bridge_init_ov772x[][2] = {
+static const u8 bridge_init[][2] = {
{ 0xc2, 0x0c },
{ 0x88, 0xf8 },
{ 0xc3, 0x69 },
@@ -478,7 +354,7 @@ static const u8 bridge_init_ov772x[][2] = {
{ 0xc1, 0x3c },
{ 0xc2, 0x0c },
};
-static const u8 sensor_init_ov772x[][2] = {
+static const u8 sensor_init[][2] = {
{ 0x12, 0x80 },
{ 0x11, 0x01 },
/*fixme: better have a delay?*/
@@ -571,7 +447,7 @@ static const u8 sensor_init_ov772x[][2] = {
{ 0x8e, 0x00 }, /* De-noise threshold */
{ 0x0c, 0xd0 }
};
-static const u8 bridge_start_ov772x_vga[][2] = {
+static const u8 bridge_start_vga[][2] = {
{0x1c, 0x00},
{0x1d, 0x40},
{0x1d, 0x02},
@@ -582,7 +458,7 @@ static const u8 bridge_start_ov772x_vga[][2] = {
{0xc0, 0x50},
{0xc1, 0x3c},
};
-static const u8 sensor_start_ov772x_vga[][2] = {
+static const u8 sensor_start_vga[][2] = {
{0x12, 0x00},
{0x17, 0x26},
{0x18, 0xa0},
@@ -592,7 +468,7 @@ static const u8 sensor_start_ov772x_vga[][2] = {
{0x2c, 0xf0},
{0x65, 0x20},
};
-static const u8 bridge_start_ov772x_qvga[][2] = {
+static const u8 bridge_start_qvga[][2] = {
{0x1c, 0x00},
{0x1d, 0x40},
{0x1d, 0x02},
@@ -603,7 +479,7 @@ static const u8 bridge_start_ov772x_qvga[][2] = {
{0xc0, 0x28},
{0xc1, 0x1e},
};
-static const u8 sensor_start_ov772x_qvga[][2] = {
+static const u8 sensor_start_qvga[][2] = {
{0x12, 0x40},
{0x17, 0x3f},
{0x18, 0x50},
@@ -614,571 +490,6 @@ static const u8 sensor_start_ov772x_qvga[][2] = {
{0x65, 0x2f},
};
-static const u8 bridge_init_ov965x[][2] = {
- {0x88, 0xf8},
- {0x89, 0xff},
- {0x76, 0x03},
- {0x92, 0x03},
- {0x95, 0x10},
- {0xe2, 0x00},
- {0xe7, 0x3e},
- {0x8d, 0x1c},
- {0x8e, 0x00},
- {0x8f, 0x00},
- {0x1f, 0x00},
- {0xc3, 0xf9},
- {0x89, 0xff},
- {0x88, 0xf8},
- {0x76, 0x03},
- {0x92, 0x01},
- {0x93, 0x18},
- {0x1c, 0x0a},
- {0x1d, 0x48},
- {0xc0, 0x50},
- {0xc1, 0x3c},
- {0x34, 0x05},
- {0xc2, 0x0c},
- {0xc3, 0xf9},
- {0x34, 0x05},
- {0xe7, 0x2e},
- {0x31, 0xf9},
- {0x35, 0x02},
- {0xd9, 0x10},
- {0x25, 0x42},
- {0x94, 0x11},
-};
-
-static const u8 sensor_init_ov965x[][2] = {
- {0x12, 0x80}, /* com7 - SSCB reset */
- {0x00, 0x00}, /* gain */
- {0x01, 0x80}, /* blue */
- {0x02, 0x80}, /* red */
- {0x03, 0x1b}, /* vref */
- {0x04, 0x03}, /* com1 - exposure low bits */
- {0x0b, 0x57}, /* ver */
- {0x0e, 0x61}, /* com5 */
- {0x0f, 0x42}, /* com6 */
- {0x11, 0x00}, /* clkrc */
- {0x12, 0x02}, /* com7 - 15fps VGA YUYV */
- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
- {0x14, 0x28}, /* com9 */
- {0x16, 0x24}, /* reg16 */
- {0x17, 0x1d}, /* hstart*/
- {0x18, 0xbd}, /* hstop */
- {0x19, 0x01}, /* vstrt */
- {0x1a, 0x81}, /* vstop*/
- {0x1e, 0x04}, /* mvfp */
- {0x24, 0x3c}, /* aew */
- {0x25, 0x36}, /* aeb */
- {0x26, 0x71}, /* vpt */
- {0x27, 0x08}, /* bbias */
- {0x28, 0x08}, /* gbbias */
- {0x29, 0x15}, /* gr com */
- {0x2a, 0x00}, /* exhch */
- {0x2b, 0x00}, /* exhcl */
- {0x2c, 0x08}, /* rbias */
- {0x32, 0xff}, /* href */
- {0x33, 0x00}, /* chlf */
- {0x34, 0x3f}, /* aref1 */
- {0x35, 0x00}, /* aref2 */
- {0x36, 0xf8}, /* aref3 */
- {0x38, 0x72}, /* adc2 */
- {0x39, 0x57}, /* aref4 */
- {0x3a, 0x80}, /* tslb - yuyv */
- {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
- {0x3d, 0x99}, /* com13 */
- {0x3f, 0xc1}, /* edge */
- {0x40, 0xc0}, /* com15 */
- {0x41, 0x40}, /* com16 */
- {0x42, 0xc0}, /* com17 */
- {0x43, 0x0a}, /* rsvd */
- {0x44, 0xf0},
- {0x45, 0x46},
- {0x46, 0x62},
- {0x47, 0x2a},
- {0x48, 0x3c},
- {0x4a, 0xfc},
- {0x4b, 0xfc},
- {0x4c, 0x7f},
- {0x4d, 0x7f},
- {0x4e, 0x7f},
- {0x4f, 0x98}, /* matrix */
- {0x50, 0x98},
- {0x51, 0x00},
- {0x52, 0x28},
- {0x53, 0x70},
- {0x54, 0x98},
- {0x58, 0x1a}, /* matrix coef sign */
- {0x59, 0x85}, /* AWB control */
- {0x5a, 0xa9},
- {0x5b, 0x64},
- {0x5c, 0x84},
- {0x5d, 0x53},
- {0x5e, 0x0e},
- {0x5f, 0xf0}, /* AWB blue limit */
- {0x60, 0xf0}, /* AWB red limit */
- {0x61, 0xf0}, /* AWB green limit */
- {0x62, 0x00}, /* lcc1 */
- {0x63, 0x00}, /* lcc2 */
- {0x64, 0x02}, /* lcc3 */
- {0x65, 0x16}, /* lcc4 */
- {0x66, 0x01}, /* lcc5 */
- {0x69, 0x02}, /* hv */
- {0x6b, 0x5a}, /* dbvl */
- {0x6c, 0x04},
- {0x6d, 0x55},
- {0x6e, 0x00},
- {0x6f, 0x9d},
- {0x70, 0x21}, /* dnsth */
- {0x71, 0x78},
- {0x72, 0x00}, /* poidx */
- {0x73, 0x01}, /* pckdv */
- {0x74, 0x3a}, /* xindx */
- {0x75, 0x35}, /* yindx */
- {0x76, 0x01},
- {0x77, 0x02},
- {0x7a, 0x12}, /* gamma curve */
- {0x7b, 0x08},
- {0x7c, 0x16},
- {0x7d, 0x30},
- {0x7e, 0x5e},
- {0x7f, 0x72},
- {0x80, 0x82},
- {0x81, 0x8e},
- {0x82, 0x9a},
- {0x83, 0xa4},
- {0x84, 0xac},
- {0x85, 0xb8},
- {0x86, 0xc3},
- {0x87, 0xd6},
- {0x88, 0xe6},
- {0x89, 0xf2},
- {0x8a, 0x03},
- {0x8c, 0x89}, /* com19 */
- {0x14, 0x28}, /* com9 */
- {0x90, 0x7d},
- {0x91, 0x7b},
- {0x9d, 0x03}, /* lcc6 */
- {0x9e, 0x04}, /* lcc7 */
- {0x9f, 0x7a},
- {0xa0, 0x79},
- {0xa1, 0x40}, /* aechm */
- {0xa4, 0x50}, /* com21 */
- {0xa5, 0x68}, /* com26 */
- {0xa6, 0x4a}, /* AWB green */
- {0xa8, 0xc1}, /* refa8 */
- {0xa9, 0xef}, /* refa9 */
- {0xaa, 0x92},
- {0xab, 0x04},
- {0xac, 0x80}, /* black level control */
- {0xad, 0x80},
- {0xae, 0x80},
- {0xaf, 0x80},
- {0xb2, 0xf2},
- {0xb3, 0x20},
- {0xb4, 0x20}, /* ctrlb4 */
- {0xb5, 0x00},
- {0xb6, 0xaf},
- {0xbb, 0xae},
- {0xbc, 0x7f}, /* ADC channel offsets */
- {0xdb, 0x7f},
- {0xbe, 0x7f},
- {0xbf, 0x7f},
- {0xc0, 0xe2},
- {0xc1, 0xc0},
- {0xc2, 0x01},
- {0xc3, 0x4e},
- {0xc6, 0x85},
- {0xc7, 0x80}, /* com24 */
- {0xc9, 0xe0},
- {0xca, 0xe8},
- {0xcb, 0xf0},
- {0xcc, 0xd8},
- {0xcd, 0xf1},
- {0x4f, 0x98}, /* matrix */
- {0x50, 0x98},
- {0x51, 0x00},
- {0x52, 0x28},
- {0x53, 0x70},
- {0x54, 0x98},
- {0x58, 0x1a},
- {0xff, 0x41}, /* read 41, write ff 00 */
- {0x41, 0x40}, /* com16 */
-
- {0xc5, 0x03}, /* 60 Hz banding filter */
- {0x6a, 0x02}, /* 50 Hz banding filter */
-
- {0x12, 0x62}, /* com7 - 30fps VGA YUV */
- {0x36, 0xfa}, /* aref3 */
- {0x69, 0x0a}, /* hv */
- {0x8c, 0x89}, /* com22 */
- {0x14, 0x28}, /* com9 */
- {0x3e, 0x0c},
- {0x41, 0x40}, /* com16 */
- {0x72, 0x00},
- {0x73, 0x00},
- {0x74, 0x3a},
- {0x75, 0x35},
- {0x76, 0x01},
- {0xc7, 0x80},
- {0x03, 0x12}, /* vref */
- {0x17, 0x16}, /* hstart */
- {0x18, 0x02}, /* hstop */
- {0x19, 0x01}, /* vstrt */
- {0x1a, 0x3d}, /* vstop */
- {0x32, 0xff}, /* href */
- {0xc0, 0xaa},
-};
-
-static const u8 bridge_init_ov965x_2[][2] = {
- {0x94, 0xaa},
- {0xf1, 0x60},
- {0xe5, 0x04},
- {0xc0, 0x50},
- {0xc1, 0x3c},
- {0x8c, 0x00},
- {0x8d, 0x1c},
- {0x34, 0x05},
-
- {0xc2, 0x0c},
- {0xc3, 0xf9},
- {0xda, 0x01},
- {0x50, 0x00},
- {0x51, 0xa0},
- {0x52, 0x3c},
- {0x53, 0x00},
- {0x54, 0x00},
- {0x55, 0x00},
- {0x57, 0x00},
- {0x5c, 0x00},
- {0x5a, 0xa0},
- {0x5b, 0x78},
- {0x35, 0x02},
- {0xd9, 0x10},
- {0x94, 0x11},
-};
-
-static const u8 sensor_init_ov965x_2[][2] = {
- {0x3b, 0xc4},
- {0x1e, 0x04}, /* mvfp */
- {0x13, 0xe0}, /* com8 */
- {0x00, 0x00}, /* gain */
- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
- {0x11, 0x03}, /* clkrc */
- {0x6b, 0x5a}, /* dblv */
- {0x6a, 0x05},
- {0xc5, 0x07},
- {0xa2, 0x4b},
- {0xa3, 0x3e},
- {0x2d, 0x00},
- {0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc0}, /* com17 */
- {0x2d, 0x00},
- {0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc1}, /* com17 */
-/* sharpness */
- {0x3f, 0x01},
- {0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc1}, /* com17 */
-/* saturation */
- {0x4f, 0x98}, /* matrix */
- {0x50, 0x98},
- {0x51, 0x00},
- {0x52, 0x28},
- {0x53, 0x70},
- {0x54, 0x98},
- {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_1_vga[][2] = { /* same for qvga */
- {0x12, 0x62}, /* com7 - 30fps VGA YUV */
- {0x36, 0xfa}, /* aref3 */
- {0x69, 0x0a}, /* hv */
- {0x8c, 0x89}, /* com22 */
- {0x14, 0x28}, /* com9 */
- {0x3e, 0x0c}, /* com14 */
- {0x41, 0x40}, /* com16 */
- {0x72, 0x00},
- {0x73, 0x00},
- {0x74, 0x3a},
- {0x75, 0x35},
- {0x76, 0x01},
- {0xc7, 0x80}, /* com24 */
- {0x03, 0x12}, /* vref */
- {0x17, 0x16}, /* hstart */
- {0x18, 0x02}, /* hstop */
- {0x19, 0x01}, /* vstrt */
- {0x1a, 0x3d}, /* vstop */
- {0x32, 0xff}, /* href */
- {0xc0, 0xaa},
-};
-
-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},
- {0xc0, 0x50},
- {0xc1, 0x3c},
- {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},
- {0x50, 0x00},
- {0x51, 0xa0},
- {0x52, 0x3c},
- {0x53, 0x00},
- {0x54, 0x00},
- {0x55, 0x00},
- {0x57, 0x00},
- {0x5c, 0x00},
- {0x5a, 0xa0},
- {0x5b, 0x78},
- {0x35, 0x02},
- {0xd9, 0x10},
- {0x94, 0x11},
-};
-
-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},
- {0x50, 0x00},
- {0x51, 0x40},
- {0x52, 0x00},
- {0x53, 0x00},
- {0x54, 0x00},
- {0x55, 0x88},
- {0x57, 0x00},
- {0x5c, 0x00},
- {0x5a, 0xc8},
- {0x5b, 0x96},
- {0x35, 0x02},
- {0xd9, 0x10},
- {0xda, 0x00},
- {0x94, 0x11},
-};
-
-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 */
- {0x00, 0x00},
- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
- {0x11, 0x03}, /* clkrc */
- {0x6b, 0x5a}, /* dblv */
- {0x6a, 0x05}, /* 50 Hz banding filter */
- {0xc5, 0x07}, /* 60 Hz banding filter */
- {0xa2, 0x4b}, /* bd50 */
- {0xa3, 0x3e}, /* bd60 */
-
- {0x2d, 0x00}, /* advfl */
-};
-
-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, 0x0c}, /* 50 Hz banding filter */
- {0xc5, 0x0f}, /* 60 Hz banding filter */
- {0xa2, 0x4e}, /* bd50 */
- {0xa3, 0x41}, /* bd60 */
-};
-
-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;
@@ -1360,14 +671,14 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
}
-static void setbrightness_77(struct gspca_dev *gspca_dev)
+static void setbrightness(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)
+static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1401,7 +712,7 @@ static void setgain(struct gspca_dev *gspca_dev)
sccb_reg_write(gspca_dev, 0x00, val);
}
-static void setexposure_77(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
@@ -1432,7 +743,7 @@ static void sethue(struct gspca_dev *gspca_dev)
sccb_reg_write(gspca_dev, 0x01, sd->hue);
}
-static void setautogain_77(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1457,7 +768,7 @@ static void setawb(struct gspca_dev *gspca_dev)
sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */
}
-static void setsharpness_77(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
@@ -1491,132 +802,6 @@ static void setvflip(struct gspca_dev *gspca_dev)
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;
- s8 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);
-
- 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 */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
@@ -1624,77 +809,50 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
- sd->sensor = id->driver_info;
-
cam = &gspca_dev->cam;
- if (sd->sensor == SENSOR_OV772X) {
- cam->cam_mode = ov772x_mode;
- cam->nmodes = ARRAY_SIZE(ov772x_mode);
+ cam->cam_mode = ov772x_mode;
+ cam->nmodes = ARRAY_SIZE(ov772x_mode);
+ cam->mode_framerates = ov772x_framerates;
- cam->bulk = 1;
- cam->bulk_size = 16384;
- cam->bulk_nurbs = 2;
- } else { /* ov965x */
- cam->cam_mode = ov965x_mode;
- cam->nmodes = ARRAY_SIZE(ov965x_mode);
- }
+ cam->bulk = 1;
+ cam->bulk_size = 16384;
+ cam->bulk_nurbs = 2;
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;
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPO_DEF;
+ sd->redblc = RED_BALANCE_DEF;
+ sd->blueblc = BLUE_BALANCE_DEF;
+ sd->hue = HUE_DEF;
+#if AUTOGAIN_DEF != 0
+ sd->autogain = AUTOGAIN_DEF;
#else
- gspca_dev->ctrl_inac |= (1 << AWB_77_IDX);
+ gspca_dev->ctrl_inac |= (1 << AWB_IDX);
#endif
#if AWB_DEF != 0
- sd->awb = AWB_DEF
+ sd->awb = AWB_DEF
#endif
-#if SHARPNESS_77_DEF != 0
- sd->sharpness = SHARPNESS_77_DEF;
+#if SHARPNESS_DEF != 0
+ sd->sharpness = SHARPNESS_DEF;
#endif
#if HFLIP_DEF != 0
- sd->hflip = HFLIP_DEF;
+ 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);
+ sd->vflip = VFLIP_DEF;
#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;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
u16 sensor_id;
- static const u8 sensor_addr[2] = {
- 0x42, /* 0 SENSOR_OV772X */
- 0x60, /* 1 SENSOR_OV965X */
- };
/* reset bridge */
ov534_reg_write(gspca_dev, 0xe7, 0x3a);
@@ -1702,8 +860,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
msleep(100);
/* initialize the sensor address */
- ov534_reg_write(gspca_dev, OV534_REG_ADDRESS,
- sensor_addr[sd->sensor]);
+ ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42);
/* reset sensor */
sccb_reg_write(gspca_dev, 0x12, 0x80);
@@ -1717,64 +874,46 @@ static int sd_init(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
/* initialize */
- switch (sd->sensor) {
- case SENSOR_OV772X:
- 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_ov772x,
- ARRAY_SIZE(sensor_init_ov772x));
- ov534_reg_write(gspca_dev, 0xe0, 0x09);
- ov534_set_led(gspca_dev, 0);
- set_frame_rate(gspca_dev);
- break;
- default:
-/* case SENSOR_OV965X: */
- reg_w_array(gspca_dev, bridge_init_ov965x,
- ARRAY_SIZE(bridge_init_ov965x));
- sccb_w_array(gspca_dev, sensor_init_ov965x,
- ARRAY_SIZE(sensor_init_ov965x));
- reg_w_array(gspca_dev, bridge_init_ov965x_2,
- ARRAY_SIZE(bridge_init_ov965x_2));
- sccb_w_array(gspca_dev, sensor_init_ov965x_2,
- ARRAY_SIZE(sensor_init_ov965x_2));
- ov534_reg_write(gspca_dev, 0xe0, 0x00);
- ov534_reg_write(gspca_dev, 0xe0, 0x01);
- ov534_set_led(gspca_dev, 0);
- ov534_reg_write(gspca_dev, 0xe0, 0x00);
- }
+ reg_w_array(gspca_dev, bridge_init,
+ ARRAY_SIZE(bridge_init));
+ ov534_set_led(gspca_dev, 1);
+ sccb_w_array(gspca_dev, sensor_init,
+ ARRAY_SIZE(sensor_init));
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+ set_frame_rate(gspca_dev);
return 0;
}
-static int sd_start_ov772x(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int mode;
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));
+ reg_w_array(gspca_dev, bridge_start_qvga,
+ ARRAY_SIZE(bridge_start_qvga));
+ sccb_w_array(gspca_dev, sensor_start_qvga,
+ ARRAY_SIZE(sensor_start_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));
+ reg_w_array(gspca_dev, bridge_start_vga,
+ ARRAY_SIZE(bridge_start_vga));
+ sccb_w_array(gspca_dev, sensor_start_vga,
+ ARRAY_SIZE(sensor_start_vga));
}
set_frame_rate(gspca_dev);
- setautogain_77(gspca_dev);
+ setautogain(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);
+ setexposure(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setsharpness(gspca_dev);
setvflip(gspca_dev);
sethflip(gspca_dev);
@@ -1783,81 +922,12 @@ static int sd_start_ov772x(struct gspca_dev *gspca_dev)
return 0;
}
-static int sd_start_ov965x(struct gspca_dev *gspca_dev)
-{
- int mode;
-
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- switch (mode) {
- default:
-/* 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)
+static void sd_stopN(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) */
#define UVC_STREAM_EOH (1 << 7)
#define UVC_STREAM_ERR (1 << 6)
@@ -1875,11 +945,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
__u32 this_pts;
u16 this_fid;
int remaining_len = len;
- int payload_len;
- payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
do {
- len = min(remaining_len, payload_len);
+ len = min(remaining_len, 2048);
/* Payloads are prefixed with a UVC-style header. We
consider a frame to start when the FID toggles, or the PTS
@@ -1918,7 +986,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data + 12, len - 12);
/* If this packet is marked as EOF, end the frame */
} else if (data[1] & UVC_STREAM_EOF) {
+ struct gspca_frame *frame;
+
sd->last_pts = 0;
+ frame = gspca_get_i_frame(gspca_dev);
+ if (frame == NULL)
+ goto discard;
+ if (frame->data_end - frame->data + (len - 12) !=
+ gspca_dev->width * gspca_dev->height * 2) {
+ PDEBUG(D_PACK, "wrong sized frame");
+ goto discard;
+ }
gspca_frame_add(gspca_dev, LAST_PACKET,
data + 12, len - 12);
} else {
@@ -1965,12 +1043,8 @@ 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);
- }
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
return 0;
}
@@ -1987,12 +1061,8 @@ 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);
- }
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
return 0;
}
@@ -2009,12 +1079,8 @@ 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);
- }
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
return 0;
}
@@ -2026,41 +1092,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
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;
@@ -2122,22 +1153,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
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);
- }
+
+ /* the auto white balance control works only
+ * when auto gain is set */
+ if (val)
+ gspca_dev->ctrl_inac &= ~(1 << AWB_IDX);
+ else
+ gspca_dev->ctrl_inac |= (1 << AWB_IDX);
+ setautogain(gspca_dev);
}
return 0;
}
@@ -2173,12 +1196,8 @@ 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);
- }
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
return 0;
}
@@ -2257,7 +1276,7 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev,
/* Set requested framerate */
sd->frame_rate = tpf->denominator / tpf->numerator;
- if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X)
+ if (gspca_dev->streaming)
set_frame_rate(gspca_dev);
/* Return the actual framerate */
@@ -2267,57 +1286,23 @@ 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_ov772x = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls_ov772x,
- .nctrls = ARRAY_SIZE(sd_ctrls_ov772x),
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
- .start = sd_start_ov772x,
- .stopN = sd_stopN_ov772x,
+ .start = sd_start,
+ .stopN = sd_stopN,
.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},
- {USB_DEVICE(0x1415, 0x2000), .driver_info = SENSOR_OV772X},
+ {USB_DEVICE(0x1415, 0x2000)},
{}
};
@@ -2326,11 +1311,7 @@ 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,
- id->driver_info == SENSOR_OV772X
- ? &sd_desc_ov772x
- : &sd_desc_ov965x,
- sizeof(struct sd),
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
THIS_MODULE);
}
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
new file mode 100644
index 00000000000..bbe5a030e3b
--- /dev/null
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -0,0 +1,1477 @@
+/*
+ * ov534-ov965x gspca driver
+ *
+ * Copyright (C) 2009-2010 Jean-Francois Moine http://moinejf.free.fr
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * 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
+ */
+
+#define MODULE_NAME "ov534_9"
+
+#include "gspca.h"
+
+#define OV534_REG_ADDRESS 0xf1 /* sensor address */
+#define OV534_REG_SUBADDR 0xf2
+#define OV534_REG_WRITE 0xf3
+#define OV534_REG_READ 0xf4
+#define OV534_REG_OPERATION 0xf5
+#define OV534_REG_STATUS 0xf6
+
+#define OV534_OP_WRITE_3 0x37
+#define OV534_OP_WRITE_2 0x33
+#define OV534_OP_READ_2 0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ __u32 last_pts;
+ u8 last_fid;
+
+ u8 brightness;
+ u8 contrast;
+ u8 autogain;
+ u8 exposure;
+ s8 sharpness;
+ u8 satur;
+ u8 freq;
+};
+
+/* 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_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(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_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(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 const struct ctrl sd_ctrls[] = {
+ { /* 0 */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define BRIGHTNESS_DEF 7
+ .default_value = BRIGHTNESS_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_DEF 3
+ .default_value = CONTRAST_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_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define EXPO_IDX 3
+ { /* 3 */
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define EXPO_DEF 0
+ .default_value = EXPO_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_DEF -1
+ .default_value = SHARPNESS_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 ov965x_mode[] = {
+#define QVGA_MODE 0
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define VGA_MODE 1
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define SVGA_MODE 2
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define XGA_MODE 3
+ {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define SXGA_MODE 4
+ {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static const u8 bridge_init[][2] = {
+ {0x88, 0xf8},
+ {0x89, 0xff},
+ {0x76, 0x03},
+ {0x92, 0x03},
+ {0x95, 0x10},
+ {0xe2, 0x00},
+ {0xe7, 0x3e},
+ {0x8d, 0x1c},
+ {0x8e, 0x00},
+ {0x8f, 0x00},
+ {0x1f, 0x00},
+ {0xc3, 0xf9},
+ {0x89, 0xff},
+ {0x88, 0xf8},
+ {0x76, 0x03},
+ {0x92, 0x01},
+ {0x93, 0x18},
+ {0x1c, 0x0a},
+ {0x1d, 0x48},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x34, 0x05},
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0x34, 0x05},
+ {0xe7, 0x2e},
+ {0x31, 0xf9},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x25, 0x42},
+ {0x94, 0x11},
+};
+
+static const u8 sensor_init[][2] = {
+ {0x12, 0x80}, /* com7 - SSCB reset */
+ {0x00, 0x00}, /* gain */
+ {0x01, 0x80}, /* blue */
+ {0x02, 0x80}, /* red */
+ {0x03, 0x1b}, /* vref */
+ {0x04, 0x03}, /* com1 - exposure low bits */
+ {0x0b, 0x57}, /* ver */
+ {0x0e, 0x61}, /* com5 */
+ {0x0f, 0x42}, /* com6 */
+ {0x11, 0x00}, /* clkrc */
+ {0x12, 0x02}, /* com7 - 15fps VGA YUYV */
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x14, 0x28}, /* com9 */
+ {0x16, 0x24}, /* reg16 */
+ {0x17, 0x1d}, /* hstart*/
+ {0x18, 0xbd}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop*/
+ {0x1e, 0x04}, /* mvfp */
+ {0x24, 0x3c}, /* aew */
+ {0x25, 0x36}, /* aeb */
+ {0x26, 0x71}, /* vpt */
+ {0x27, 0x08}, /* bbias */
+ {0x28, 0x08}, /* gbbias */
+ {0x29, 0x15}, /* gr com */
+ {0x2a, 0x00}, /* exhch */
+ {0x2b, 0x00}, /* exhcl */
+ {0x2c, 0x08}, /* rbias */
+ {0x32, 0xff}, /* href */
+ {0x33, 0x00}, /* chlf */
+ {0x34, 0x3f}, /* aref1 */
+ {0x35, 0x00}, /* aref2 */
+ {0x36, 0xf8}, /* aref3 */
+ {0x38, 0x72}, /* adc2 */
+ {0x39, 0x57}, /* aref4 */
+ {0x3a, 0x80}, /* tslb - yuyv */
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+ {0x3d, 0x99}, /* com13 */
+ {0x3f, 0xc1}, /* edge */
+ {0x40, 0xc0}, /* com15 */
+ {0x41, 0x40}, /* com16 */
+ {0x42, 0xc0}, /* com17 */
+ {0x43, 0x0a}, /* rsvd */
+ {0x44, 0xf0},
+ {0x45, 0x46},
+ {0x46, 0x62},
+ {0x47, 0x2a},
+ {0x48, 0x3c},
+ {0x4a, 0xfc},
+ {0x4b, 0xfc},
+ {0x4c, 0x7f},
+ {0x4d, 0x7f},
+ {0x4e, 0x7f},
+ {0x4f, 0x98}, /* matrix */
+ {0x50, 0x98},
+ {0x51, 0x00},
+ {0x52, 0x28},
+ {0x53, 0x70},
+ {0x54, 0x98},
+ {0x58, 0x1a}, /* matrix coef sign */
+ {0x59, 0x85}, /* AWB control */
+ {0x5a, 0xa9},
+ {0x5b, 0x64},
+ {0x5c, 0x84},
+ {0x5d, 0x53},
+ {0x5e, 0x0e},
+ {0x5f, 0xf0}, /* AWB blue limit */
+ {0x60, 0xf0}, /* AWB red limit */
+ {0x61, 0xf0}, /* AWB green limit */
+ {0x62, 0x00}, /* lcc1 */
+ {0x63, 0x00}, /* lcc2 */
+ {0x64, 0x02}, /* lcc3 */
+ {0x65, 0x16}, /* lcc4 */
+ {0x66, 0x01}, /* lcc5 */
+ {0x69, 0x02}, /* hv */
+ {0x6b, 0x5a}, /* dbvl */
+ {0x6c, 0x04},
+ {0x6d, 0x55},
+ {0x6e, 0x00},
+ {0x6f, 0x9d},
+ {0x70, 0x21}, /* dnsth */
+ {0x71, 0x78},
+ {0x72, 0x00}, /* poidx */
+ {0x73, 0x01}, /* pckdv */
+ {0x74, 0x3a}, /* xindx */
+ {0x75, 0x35}, /* yindx */
+ {0x76, 0x01},
+ {0x77, 0x02},
+ {0x7a, 0x12}, /* gamma curve */
+ {0x7b, 0x08},
+ {0x7c, 0x16},
+ {0x7d, 0x30},
+ {0x7e, 0x5e},
+ {0x7f, 0x72},
+ {0x80, 0x82},
+ {0x81, 0x8e},
+ {0x82, 0x9a},
+ {0x83, 0xa4},
+ {0x84, 0xac},
+ {0x85, 0xb8},
+ {0x86, 0xc3},
+ {0x87, 0xd6},
+ {0x88, 0xe6},
+ {0x89, 0xf2},
+ {0x8a, 0x03},
+ {0x8c, 0x89}, /* com19 */
+ {0x14, 0x28}, /* com9 */
+ {0x90, 0x7d},
+ {0x91, 0x7b},
+ {0x9d, 0x03}, /* lcc6 */
+ {0x9e, 0x04}, /* lcc7 */
+ {0x9f, 0x7a},
+ {0xa0, 0x79},
+ {0xa1, 0x40}, /* aechm */
+ {0xa4, 0x50}, /* com21 */
+ {0xa5, 0x68}, /* com26 */
+ {0xa6, 0x4a}, /* AWB green */
+ {0xa8, 0xc1}, /* refa8 */
+ {0xa9, 0xef}, /* refa9 */
+ {0xaa, 0x92},
+ {0xab, 0x04},
+ {0xac, 0x80}, /* black level control */
+ {0xad, 0x80},
+ {0xae, 0x80},
+ {0xaf, 0x80},
+ {0xb2, 0xf2},
+ {0xb3, 0x20},
+ {0xb4, 0x20}, /* ctrlb4 */
+ {0xb5, 0x00},
+ {0xb6, 0xaf},
+ {0xbb, 0xae},
+ {0xbc, 0x7f}, /* ADC channel offsets */
+ {0xdb, 0x7f},
+ {0xbe, 0x7f},
+ {0xbf, 0x7f},
+ {0xc0, 0xe2},
+ {0xc1, 0xc0},
+ {0xc2, 0x01},
+ {0xc3, 0x4e},
+ {0xc6, 0x85},
+ {0xc7, 0x80}, /* com24 */
+ {0xc9, 0xe0},
+ {0xca, 0xe8},
+ {0xcb, 0xf0},
+ {0xcc, 0xd8},
+ {0xcd, 0xf1},
+ {0x4f, 0x98}, /* matrix */
+ {0x50, 0x98},
+ {0x51, 0x00},
+ {0x52, 0x28},
+ {0x53, 0x70},
+ {0x54, 0x98},
+ {0x58, 0x1a},
+ {0xff, 0x41}, /* read 41, write ff 00 */
+ {0x41, 0x40}, /* com16 */
+
+ {0xc5, 0x03}, /* 60 Hz banding filter */
+ {0x6a, 0x02}, /* 50 Hz banding filter */
+
+ {0x12, 0x62}, /* com7 - 30fps VGA YUV */
+ {0x36, 0xfa}, /* aref3 */
+ {0x69, 0x0a}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c},
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80},
+ {0x03, 0x12}, /* vref */
+ {0x17, 0x16}, /* hstart */
+ {0x18, 0x02}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x3d}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xaa},
+};
+
+static const u8 bridge_init_2[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0xda, 0x01},
+ {0x50, 0x00},
+ {0x51, 0xa0},
+ {0x52, 0x3c},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0xa0},
+ {0x5b, 0x78},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 sensor_init_2[][2] = {
+ {0x3b, 0xc4},
+ {0x1e, 0x04}, /* mvfp */
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00}, /* gain */
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x11, 0x03}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x05},
+ {0xc5, 0x07},
+ {0xa2, 0x4b},
+ {0xa3, 0x3e},
+ {0x2d, 0x00},
+ {0xff, 0x42}, /* read 42, write ff 00 */
+ {0x42, 0xc0}, /* com17 */
+ {0x2d, 0x00},
+ {0xff, 0x42}, /* read 42, write ff 00 */
+ {0x42, 0xc1}, /* com17 */
+/* sharpness */
+ {0x3f, 0x01},
+ {0xff, 0x42}, /* read 42, write ff 00 */
+ {0x42, 0xc1}, /* com17 */
+/* saturation */
+ {0x4f, 0x98}, /* matrix */
+ {0x50, 0x98},
+ {0x51, 0x00},
+ {0x52, 0x28},
+ {0x53, 0x70},
+ {0x54, 0x98},
+ {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_1_vga[][2] = { /* same for qvga */
+ {0x12, 0x62}, /* com7 - 30fps VGA YUV */
+ {0x36, 0xfa}, /* aref3 */
+ {0x69, 0x0a}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x12}, /* vref */
+ {0x17, 0x16}, /* hstart */
+ {0x18, 0x02}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x3d}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xaa},
+};
+
+static const u8 sensor_start_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_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_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_qvga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {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_vga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0xda, 0x01},
+ {0x50, 0x00},
+ {0x51, 0xa0},
+ {0x52, 0x3c},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0xa0},
+ {0x5b, 0x78},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_svga[][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, 0x00},
+ {0x5a, 0xc8},
+ {0x5b, 0x96},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0xda, 0x00},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_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_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_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_2_vga[][2] = {
+ {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, 0x03}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x05}, /* 50 Hz banding filter */
+ {0xc5, 0x07}, /* 60 Hz banding filter */
+ {0xa2, 0x4b}, /* bd50 */
+ {0xa3, 0x3e}, /* bd60 */
+
+ {0x2d, 0x00}, /* advfl */
+};
+
+static const u8 sensor_start_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, 0x0c}, /* 50 Hz banding filter */
+ {0xc5, 0x0f}, /* 60 Hz banding filter */
+ {0xa2, 0x4e}, /* bd50 */
+ {0xa3, 0x41}, /* bd60 */
+};
+
+static const u8 sensor_start_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 reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = val;
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w failed %d", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val);
+ reg_w_i(gspca_dev, reg, val);
+}
+
+static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_r err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+ u8 data;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ data = reg_r(gspca_dev, OV534_REG_STATUS);
+
+ switch (data) {
+ case 0x00:
+ return 1;
+ case 0x04:
+ return 0;
+ case 0x03:
+ break;
+ default:
+ PDEBUG(D_USBI|D_USBO,
+ "sccb status 0x%02x, attempt %d/5",
+ data, i + 1);
+ }
+ }
+ return 0;
+}
+
+static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val);
+ reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg);
+ reg_w_i(gspca_dev, OV534_REG_WRITE, val);
+ reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_write failed");
+}
+
+static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
+ reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_read failed 1");
+
+ reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_read failed 2");
+
+ return reg_r(gspca_dev, OV534_REG_READ);
+}
+
+/* output a bridge sequence (reg - val) */
+static void reg_w_array(struct gspca_dev *gspca_dev,
+ const u8 (*data)[2], int len)
+{
+ while (--len >= 0) {
+ reg_w(gspca_dev, (*data)[0], (*data)[1]);
+ data++;
+ }
+}
+
+/* output a sensor sequence (reg - val) */
+static void sccb_w_array(struct gspca_dev *gspca_dev,
+ const u8 (*data)[2], int len)
+{
+ while (--len >= 0) {
+ if ((*data)[0] != 0xff) {
+ sccb_write(gspca_dev, (*data)[0], (*data)[1]);
+ } else {
+ sccb_read(gspca_dev, (*data)[1]);
+ sccb_write(gspca_dev, 0xff, 0x00);
+ }
+ data++;
+ }
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void set_led(struct gspca_dev *gspca_dev, int status)
+{
+ u8 data;
+
+ PDEBUG(D_CONF, "led status: %d", status);
+
+ data = reg_r(gspca_dev, 0x21);
+ data |= 0x80;
+ reg_w(gspca_dev, 0x21, data);
+
+ data = reg_r(gspca_dev, 0x23);
+ if (status)
+ data |= 0x80;
+ else
+ data &= ~0x80;
+
+ reg_w(gspca_dev, 0x23, data);
+
+ if (!status) {
+ data = reg_r(gspca_dev, 0x21);
+ data &= ~0x80;
+ reg_w(gspca_dev, 0x21, data);
+ }
+}
+
+static void setbrightness(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_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
+ 0x0f | (val << 4));
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
+ sd->contrast << 4);
+}
+
+static void setautogain(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_read(gspca_dev, 0x13); /* com8 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ if (sd->autogain)
+ val |= 0x05; /* agc & aec */
+ else
+ val &= 0xfa;
+ sccb_write(gspca_dev, 0x13, val);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+
+ sccb_write(gspca_dev, 0x10, /* aec[9:2] */
+ expo[sd->exposure]);
+
+ val = sccb_read(gspca_dev, 0x13); /* com8 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x13, val);
+
+ val = sccb_read(gspca_dev, 0xa1); /* aech */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s8 val;
+
+ val = sd->sharpness;
+ if (val < 0) { /* auto */
+ val = sccb_read(gspca_dev, 0x42); /* com17 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x42, val | 0x40);
+ /* Edge enhancement strength auto adjust */
+ return;
+ }
+ if (val != 0)
+ val = 1 << (val - 1);
+ sccb_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */
+ val);
+ val = sccb_read(gspca_dev, 0x42); /* com17 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x42, val & 0xbf);
+}
+
+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_write(gspca_dev, 0x4f, val3); /* matrix coeff */
+ sccb_write(gspca_dev, 0x50, val3);
+ sccb_write(gspca_dev, 0x51, 0x00);
+ sccb_write(gspca_dev, 0x52, val1);
+ sccb_write(gspca_dev, 0x53, val2);
+ sccb_write(gspca_dev, 0x54, val3);
+ sccb_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */
+
+ val1 = sccb_read(gspca_dev, 0x41); /* com16 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x41, val1);
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sccb_read(gspca_dev, 0x13); /* com8 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ if (sd->freq == 0) {
+ sccb_write(gspca_dev, 0x13, val & 0xdf);
+ return;
+ }
+ sccb_write(gspca_dev, 0x13, val | 0x20);
+
+ val = sccb_read(gspca_dev, 0x42); /* com17 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ if (sd->freq == 1)
+ val |= 0x01;
+ else
+ val &= 0xfe;
+ sccb_write(gspca_dev, 0x42, val);
+}
+
+/* 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;
+
+ cam = &gspca_dev->cam;
+
+ cam->cam_mode = ov965x_mode;
+ cam->nmodes = ARRAY_SIZE(ov965x_mode);
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+#if AUTOGAIN_DEF != 0
+ sd->autogain = AUTOGAIN_DEF;
+ gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
+#endif
+#if EXPO_DEF != 0
+ sd->exposure = EXPO_DEF;
+#endif
+#if SHARPNESS_DEF != 0
+ sd->sharpness = SHARPNESS_DEF;
+#endif
+ sd->satur = SATUR_DEF;
+ sd->freq = FREQ_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ u16 sensor_id;
+
+ /* reset bridge */
+ reg_w(gspca_dev, 0xe7, 0x3a);
+ reg_w(gspca_dev, 0xe0, 0x08);
+ msleep(100);
+
+ /* initialize the sensor address */
+ reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60);
+
+ /* reset sensor */
+ sccb_write(gspca_dev, 0x12, 0x80);
+ msleep(10);
+
+ /* probe the sensor */
+ sccb_read(gspca_dev, 0x0a);
+ sensor_id = sccb_read(gspca_dev, 0x0a) << 8;
+ sccb_read(gspca_dev, 0x0b);
+ sensor_id |= sccb_read(gspca_dev, 0x0b);
+ PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+
+ /* initialize */
+ reg_w_array(gspca_dev, bridge_init,
+ ARRAY_SIZE(bridge_init));
+ sccb_w_array(gspca_dev, sensor_init,
+ ARRAY_SIZE(sensor_init));
+ reg_w_array(gspca_dev, bridge_init_2,
+ ARRAY_SIZE(bridge_init_2));
+ sccb_w_array(gspca_dev, sensor_init_2,
+ ARRAY_SIZE(sensor_init_2));
+ reg_w(gspca_dev, 0xe0, 0x00);
+ reg_w(gspca_dev, 0xe0, 0x01);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, 0xe0, 0x00);
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ switch (gspca_dev->curr_mode) {
+ case QVGA_MODE: /* 320x240 */
+ sccb_w_array(gspca_dev, sensor_start_1_vga,
+ ARRAY_SIZE(sensor_start_1_vga));
+ reg_w_array(gspca_dev, bridge_start_qvga,
+ ARRAY_SIZE(bridge_start_qvga));
+ sccb_w_array(gspca_dev, sensor_start_2_qvga,
+ ARRAY_SIZE(sensor_start_2_qvga));
+ break;
+ case VGA_MODE: /* 640x480 */
+ sccb_w_array(gspca_dev, sensor_start_1_vga,
+ ARRAY_SIZE(sensor_start_1_vga));
+ reg_w_array(gspca_dev, bridge_start_vga,
+ ARRAY_SIZE(bridge_start_vga));
+ sccb_w_array(gspca_dev, sensor_start_2_vga,
+ ARRAY_SIZE(sensor_start_2_vga));
+ break;
+ case SVGA_MODE: /* 800x600 */
+ sccb_w_array(gspca_dev, sensor_start_1_svga,
+ ARRAY_SIZE(sensor_start_1_svga));
+ reg_w_array(gspca_dev, bridge_start_svga,
+ ARRAY_SIZE(bridge_start_svga));
+ sccb_w_array(gspca_dev, sensor_start_2_svga,
+ ARRAY_SIZE(sensor_start_2_svga));
+ break;
+ case XGA_MODE: /* 1024x768 */
+ sccb_w_array(gspca_dev, sensor_start_1_xga,
+ ARRAY_SIZE(sensor_start_1_xga));
+ reg_w_array(gspca_dev, bridge_start_xga,
+ ARRAY_SIZE(bridge_start_xga));
+ sccb_w_array(gspca_dev, sensor_start_2_svga,
+ ARRAY_SIZE(sensor_start_2_svga));
+ break;
+ default:
+/* case SXGA_MODE: * 1280x1024 */
+ sccb_w_array(gspca_dev, sensor_start_1_sxga,
+ ARRAY_SIZE(sensor_start_1_sxga));
+ reg_w_array(gspca_dev, bridge_start_sxga,
+ ARRAY_SIZE(bridge_start_sxga));
+ sccb_w_array(gspca_dev, sensor_start_2_sxga,
+ ARRAY_SIZE(sensor_start_2_sxga));
+ break;
+ }
+ setfreq(gspca_dev);
+ setautogain(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setexposure(gspca_dev);
+ setsharpness(gspca_dev);
+ setsatur(gspca_dev);
+
+ reg_w(gspca_dev, 0xe0, 0x00);
+ reg_w(gspca_dev, 0xe0, 0x00);
+ set_led(gspca_dev, 1);
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0xe0, 0x01);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, 0xe0, 0x00);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ u8 this_fid;
+ int remaining_len = len;
+
+ do {
+ len = min(remaining_len, 2040);
+
+ /* Payloads are prefixed with a UVC-style header. We
+ consider a frame to start when the FID toggles, or the PTS
+ changes. A frame ends when EOF is set, and we've received
+ the correct number of bytes. */
+
+ /* Verify UVC header. Header length is always 12 */
+ if (data[0] != 12 || len < 12) {
+ PDEBUG(D_PACK, "bad header");
+ goto discard;
+ }
+
+ /* Check errors */
+ if (data[1] & UVC_STREAM_ERR) {
+ PDEBUG(D_PACK, "payload error");
+ goto discard;
+ }
+
+ /* Extract PTS and FID */
+ if (!(data[1] & UVC_STREAM_PTS)) {
+ PDEBUG(D_PACK, "PTS not present");
+ goto discard;
+ }
+ this_pts = (data[5] << 24) | (data[4] << 16)
+ | (data[3] << 8) | data[2];
+ this_fid = data[1] & UVC_STREAM_FID;
+
+ /* 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)
+ 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,
+ 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;
+ 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,
+ data + 12, len - 12);
+ }
+
+ /* Done this payload */
+ goto scan_next;
+
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+
+scan_next:
+ remaining_len -= len;
+ data += len;
+ } while (remaining_len > 0);
+}
+
+/* controls */
+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)
+ setbrightness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+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)
+ setcontrast(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+
+ if (gspca_dev->streaming) {
+ if (val)
+ gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
+ else
+ gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX);
+ setautogain(gspca_dev);
+ }
+ return gspca_dev->usb_err;
+}
+
+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_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 gspca_dev->usb_err;
+}
+
+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_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+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_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 gspca_dev->usb_err;
+}
+
+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->freq = val;
+ if (gspca_dev->streaming)
+ setfreq(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->freq;
+ 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 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06f8, 0x3003)},
+ {}
+};
+
+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/pac207.c b/drivers/media/video/gspca/pac207.c
index 4706a823add..0c87c3490b1 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -25,6 +25,7 @@
#define MODULE_NAME "pac207"
+#include <linux/input.h>
#include "gspca.h"
MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
@@ -77,7 +78,7 @@ static int sd_getautogain(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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
#define SD_BRIGHTNESS 0
{
{
@@ -495,6 +496,25 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -506,6 +526,9 @@ static const struct sd_desc sd_desc = {
.stopN = sd_stopN,
.dq_callback = pac207_do_auto_gain,
.pkt_scan = sd_pkt_scan,
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
/* -- module initialisation -- */
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index de0b66c4b56..2a68220d1ad 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -4,7 +4,9 @@
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*
- * Separated from Pixart PAC7311 library by Márton Németh <nm127@freemail.hu>
+ * Separated from Pixart PAC7311 library by Márton Németh
+ * Camera button input handling by Márton Németh <nm127@freemail.hu>
+ * Copyright (C) 2009-2010 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
@@ -22,33 +24,26 @@
*/
/* 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:
+ Register page 3:
Address Description
- 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+ 0x02 Clock divider 3-63, fps = 90 / 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)
+ 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
+ 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
+ 63 -> ~27 fps, the 2 msb's must always be 1 !!
+ 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
+ 1 -> ~30 fps, 2 -> ~20 fps
+ 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
+ 0x0f Exposure bit 8, 0-448, 448 = no exposure at all
+ 0x10 Master gain 0-31
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:
@@ -68,6 +63,7 @@
#define MODULE_NAME "pac7302"
+#include <linux/input.h>
#include <media/v4l2-chip-ident.h>
#include "gspca.h"
@@ -86,8 +82,8 @@ struct sd {
unsigned char red_balance;
unsigned char blue_balance;
unsigned char gain;
- unsigned char exposure;
unsigned char autogain;
+ unsigned short exposure;
__u8 hflip;
__u8 vflip;
u8 flags;
@@ -124,8 +120,7 @@ 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 */
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -141,7 +136,6 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setbrightness,
.get = sd_getbrightness,
},
-/* This control is for both the 7302 and the 7311 */
{
{
.id = V4L2_CID_CONTRAST,
@@ -157,7 +151,6 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setcontrast,
.get = sd_getcontrast,
},
-/* This control is pac7302 only */
{
{
.id = V4L2_CID_SATURATION,
@@ -215,7 +208,6 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setbluebalance,
.get = sd_getbluebalance,
},
-/* All controls below are for both the 7302 and the 7311 */
{
{
.id = V4L2_CID_GAIN,
@@ -238,11 +230,10 @@ static struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = 0,
-#define EXPOSURE_MAX 255
- .maximum = EXPOSURE_MAX,
+ .maximum = 1023,
.step = 1,
-#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */
-#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
+#define EXPOSURE_DEF 66 /* 33 ms / 30 fps */
+#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
.default_value = EXPOSURE_DEF,
},
.set = sd_setexposure,
@@ -301,7 +292,6 @@ static const struct v4l2_pix_format vga_mode[] = {
};
#define LOAD_PAGE3 255
-#define LOAD_PAGE4 254
#define END_OF_SEQUENCE 0
/* pac 7302 */
@@ -379,7 +369,7 @@ static const __u8 start_7302[] = {
#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,
+ 0x90, 0x40, 0x03, 0x00, 0xc0, 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,
@@ -388,7 +378,7 @@ static const __u8 page3_7302[] = {
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,
+ SKIP, 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,
@@ -401,12 +391,14 @@ static const __u8 page3_7302[] = {
0x00
};
-static int reg_w_buf(struct gspca_dev *gspca_dev,
+static void reg_w_buf(struct gspca_dev *gspca_dev,
__u8 index,
const char *buffer, int len)
{
int ret;
+ if (gspca_dev->usb_err < 0)
+ return;
memcpy(gspca_dev->usb_buf, buffer, len);
ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
@@ -415,20 +407,23 @@ static int reg_w_buf(struct gspca_dev *gspca_dev,
0, /* value */
index, gspca_dev->usb_buf, len,
500);
- if (ret < 0)
+ if (ret < 0) {
PDEBUG(D_ERR, "reg_w_buf(): "
"Failed to write registers to index 0x%x, error %i",
index, ret);
- return ret;
+ gspca_dev->usb_err = ret;
+ }
}
-static int reg_w(struct gspca_dev *gspca_dev,
+static void reg_w(struct gspca_dev *gspca_dev,
__u8 index,
__u8 value)
{
int ret;
+ if (gspca_dev->usb_err < 0)
+ return;
gspca_dev->usb_buf[0] = value;
ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
@@ -436,32 +431,32 @@ static int reg_w(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, gspca_dev->usb_buf, 1,
500);
- if (ret < 0)
+ 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;
+ gspca_dev->usb_err = ret;
+ }
}
-static int reg_w_seq(struct gspca_dev *gspca_dev,
+static void 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]);
+ 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,
+static void reg_w_page(struct gspca_dev *gspca_dev,
const __u8 *page, int len)
{
int index;
int ret = 0;
+ if (gspca_dev->usb_err < 0)
+ return;
for (index = 0; index < len; index++) {
if (page[index] == SKIP) /* skip this index */
continue;
@@ -477,56 +472,47 @@ static int reg_w_page(struct gspca_dev *gspca_dev,
"Failed to write register to index 0x%x, "
"value 0x%x, error %i",
index, page[index], ret);
+ gspca_dev->usb_err = ret;
break;
}
}
- return ret;
}
/* output a variable sequence */
-static int reg_w_var(struct gspca_dev *gspca_dev,
+static void 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)
+ const __u8 *page3, unsigned int page3_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;
+ return;
case LOAD_PAGE3:
- ret = reg_w_page(gspca_dev, page3, page3_len);
+ 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;
+ return;
}
while (len > 0) {
if (len < 8) {
- ret = reg_w_buf(gspca_dev,
+ 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);
+ reg_w_buf(gspca_dev, index, seq, 8);
seq += 8;
index += 8;
len -= 8;
}
}
- if (ret < 0)
- return ret;
}
/* not reached */
}
@@ -560,11 +546,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
/* This function is used by pac7302 only */
-static int setbrightcont(struct gspca_dev *gspca_dev)
+static void 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};
@@ -572,7 +557,7 @@ static int setbrightcont(struct gspca_dev *gspca_dev)
{0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
0x11, 0x0b};
- ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
for (i = 0; i < 10; i++) {
v = max[i];
v += (sd->brightness - BRIGHTNESS_MAX)
@@ -582,136 +567,121 @@ static int setbrightcont(struct gspca_dev *gspca_dev)
v = 0;
else if (v > 0xff)
v = 0xff;
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xa2 + i, v);
+ reg_w(gspca_dev, 0xa2 + i, v);
}
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xdc, 0x01);
- return ret;
+ reg_w(gspca_dev, 0xdc, 0x01);
}
/* This function is used by pac7302 only */
-static int setcolors(struct gspca_dev *gspca_dev)
+static void 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 */
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x11, 0x01);
+ 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);
+ reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+ reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
}
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xdc, 0x01);
+ 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)
+static void 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);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc6, sd->white_balance);
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xdc, 0x01);
+ 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)
+static void 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);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc5, sd->red_balance);
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xdc, 0x01);
+ 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)
+static void 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);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc7, sd->blue_balance);
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xdc, 0x01);
+ 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)
+static void 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);
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ 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;
+ reg_w(gspca_dev, 0x11, 0x01);
}
-static int setexposure(struct gspca_dev *gspca_dev)
+static void 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);
+ __u8 clockdiv;
+ __u16 exposure;
+
+ /* register 2 of frame 3 contains the clock divider configuring the
+ no fps according to the formula: 90 / reg. sd->exposure is the
+ desired exposure time in 0.5 ms. */
+ clockdiv = (90 * sd->exposure + 1999) / 2000;
+
+ /* Note clockdiv = 3 also works, but when running at 30 fps, depending
+ on the scene being recorded, the camera switches to another
+ quantization table for certain JPEG blocks, and we don't know how
+ to decompress these blocks. So we cap the framerate at 15 fps */
+ if (clockdiv < 6)
+ clockdiv = 6;
+ else if (clockdiv > 63)
+ clockdiv = 63;
+
+ /* reg2 MUST be a multiple of 3, except when between 6 and 12?
+ Always round up, otherwise we cannot get the desired frametime
+ using the partial frame time exposure control */
+ if (clockdiv < 6 || clockdiv > 12)
+ clockdiv = ((clockdiv + 2) / 3) * 3;
+
+ /* frame exposure time in ms = 1000 * clockdiv / 90 ->
+ exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */
+ exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv);
+ /* 0 = use full frametime, 448 = no exposure, reverse it */
+ exposure = 448 - exposure;
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x02, clockdiv);
+ reg_w(gspca_dev, 0x0e, exposure & 0xff);
+ reg_w(gspca_dev, 0x0f, exposure >> 8);
/* load registers to sensor (Bit 0, auto clear) */
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x11, 0x01);
- return ret;
+ reg_w(gspca_dev, 0x11, 0x01);
}
-static int sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int ret;
u8 data, hflip, vflip;
hflip = sd->hflip;
@@ -721,48 +691,37 @@ static int sethvflip(struct gspca_dev *gspca_dev)
if (sd->flags & FL_VFLIP)
vflip = !vflip;
- ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x21, data);
+ 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;
+ reg_w(gspca_dev, 0x11, 0x01);
}
/* 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);
+ reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
+ return gspca_dev->usb_err;
}
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);
+ reg_w_var(gspca_dev, start_7302,
+ page3_7302, sizeof(page3_7302));
+ setbrightcont(gspca_dev);
+ setcolors(gspca_dev);
+ setwhitebalance(gspca_dev);
+ setredbalance(gspca_dev);
+ setbluebalance(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ sethvflip(gspca_dev);
/* only resolution 640x480 is supported for pac7302 */
@@ -771,34 +730,27 @@ static int sd_start(struct gspca_dev *gspca_dev)
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);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x01);
- return ret;
+ return gspca_dev->usb_err;
}
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);
+ reg_w(gspca_dev, 0xff, 0x01);
+ 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);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x40);
}
/* Include pac common sof detection functions */
@@ -808,22 +760,13 @@ 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;
+ int desired_lum;
+ const int deadzone = 30;
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;
+ desired_lum = 270 + sd->brightness;
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
@@ -947,7 +890,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
sd->brightness = val;
if (gspca_dev->streaming)
setbrightcont(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -966,7 +909,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
if (gspca_dev->streaming) {
setbrightcont(gspca_dev);
}
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
@@ -984,7 +927,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
sd->colors = val;
if (gspca_dev->streaming)
setcolors(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
@@ -998,14 +941,11 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
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;
+ setwhitebalance(gspca_dev);
+ return gspca_dev->usb_err;
}
static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1019,14 +959,11 @@ static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
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;
+ setredbalance(gspca_dev);
+ return gspca_dev->usb_err;
}
static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1040,14 +977,11 @@ static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
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;
+ setbluebalance(gspca_dev);
+ return gspca_dev->usb_err;
}
static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1065,7 +999,7 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
sd->gain = val;
if (gspca_dev->streaming)
setgain(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1083,7 +1017,7 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
sd->exposure = val;
if (gspca_dev->streaming)
setexposure(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1114,7 +1048,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
}
}
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1132,7 +1066,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
sd->hflip = val;
if (gspca_dev->streaming)
sethvflip(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1150,7 +1084,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
sd->vflip = val;
if (gspca_dev->streaming)
sethvflip(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1165,7 +1099,6 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
struct v4l2_dbg_register *reg)
{
- int ret = -EINVAL;
__u8 index;
__u8 value;
@@ -1185,14 +1118,12 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
/* 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);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, index, value);
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xdc, 0x01);
+ reg_w(gspca_dev, 0xdc, 0x01);
}
- return ret;
+ return gspca_dev->usb_err;
}
static int sd_chip_ident(struct gspca_dev *gspca_dev,
@@ -1210,8 +1141,39 @@ static int sd_chip_ident(struct gspca_dev *gspca_dev,
}
#endif
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ int ret = -EINVAL;
+ u8 data0, data1;
+
+ if (len == 2) {
+ data0 = data[0];
+ data1 = data[1];
+ if ((data0 == 0x00 && data1 == 0x11) ||
+ (data0 == 0x22 && data1 == 0x33) ||
+ (data0 == 0x44 && data1 == 0x55) ||
+ (data0 == 0x66 && data1 == 0x77) ||
+ (data0 == 0x88 && data1 == 0x99) ||
+ (data0 == 0xaa && data1 == 0xbb) ||
+ (data0 == 0xcc && data1 == 0xdd) ||
+ (data0 == 0xee && data1 == 0xff)) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+#endif
+
/* sub-driver description for pac7302 */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
@@ -1226,6 +1188,9 @@ static struct sd_desc sd_desc = {
.set_register = sd_dbg_s_register,
.get_chip_ident = sd_chip_ident,
#endif
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
/* -- module initialisation -- */
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index 42cfcdfd8f4..44fed968672 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -51,6 +51,7 @@
#define MODULE_NAME "pac7311"
+#include <linux/input.h>
#include "gspca.h"
MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
@@ -88,7 +89,7 @@ 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[] = {
+static const struct ctrl sd_ctrls[] = {
/* This control is for both the 7302 and the 7311 */
{
{
@@ -200,7 +201,6 @@ static const struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-#define LOAD_PAGE3 255
#define LOAD_PAGE4 254
#define END_OF_SEQUENCE 0
@@ -259,12 +259,14 @@ static const __u8 page4_7311[] = {
0x23, 0x28, 0x04, 0x11, 0x00, 0x00
};
-static int reg_w_buf(struct gspca_dev *gspca_dev,
+static void reg_w_buf(struct gspca_dev *gspca_dev,
__u8 index,
const char *buffer, int len)
{
int ret;
+ if (gspca_dev->usb_err < 0)
+ return;
memcpy(gspca_dev->usb_buf, buffer, len);
ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
@@ -273,20 +275,23 @@ static int reg_w_buf(struct gspca_dev *gspca_dev,
0, /* value */
index, gspca_dev->usb_buf, len,
500);
- if (ret < 0)
+ if (ret < 0) {
PDEBUG(D_ERR, "reg_w_buf(): "
"Failed to write registers to index 0x%x, error %i",
index, ret);
- return ret;
+ gspca_dev->usb_err = ret;
+ }
}
-static int reg_w(struct gspca_dev *gspca_dev,
+static void reg_w(struct gspca_dev *gspca_dev,
__u8 index,
__u8 value)
{
int ret;
+ if (gspca_dev->usb_err < 0)
+ return;
gspca_dev->usb_buf[0] = value;
ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
@@ -294,32 +299,32 @@ static int reg_w(struct gspca_dev *gspca_dev,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, gspca_dev->usb_buf, 1,
500);
- if (ret < 0)
+ 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;
+ gspca_dev->usb_err = ret;
+ }
}
-static int reg_w_seq(struct gspca_dev *gspca_dev,
+static void 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]);
+ 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,
+static void reg_w_page(struct gspca_dev *gspca_dev,
const __u8 *page, int len)
{
int index;
int ret = 0;
+ if (gspca_dev->usb_err < 0)
+ return;
for (index = 0; index < len; index++) {
if (page[index] == SKIP) /* skip this index */
continue;
@@ -335,56 +340,47 @@ static int reg_w_page(struct gspca_dev *gspca_dev,
"Failed to write register to index 0x%x, "
"value 0x%x, error %i",
index, page[index], ret);
+ gspca_dev->usb_err = ret;
break;
}
}
- return ret;
}
/* output a variable sequence */
-static int reg_w_var(struct gspca_dev *gspca_dev,
+static void 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;
+ return;
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);
+ reg_w_page(gspca_dev, page4, page4_len);
break;
default:
if (len > USB_BUF_SZ) {
PDEBUG(D_ERR|D_STREAM,
"Incorrect variable sequence");
- return -EINVAL;
+ return;
}
while (len > 0) {
if (len < 8) {
- ret = reg_w_buf(gspca_dev,
+ 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);
+ reg_w_buf(gspca_dev, index, seq, 8);
seq += 8;
index += 8;
len -= 8;
}
}
- if (ret < 0)
- return ret;
}
/* not reached */
}
@@ -412,46 +408,36 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
/* This function is used by pac7311 only */
-static int setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int ret;
- ret = reg_w(gspca_dev, 0xff, 0x04);
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4);
+ reg_w(gspca_dev, 0xff, 0x04);
+ reg_w(gspca_dev, 0x10, sd->contrast >> 4);
/* load registers to sensor (Bit 0, auto clear) */
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x11, 0x01);
- return ret;
+ reg_w(gspca_dev, 0x11, 0x01);
}
-static int setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int gain = GAIN_MAX - sd->gain;
- int ret;
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);
+ 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) */
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x11, 0x01);
- return ret;
+ reg_w(gspca_dev, 0x11, 0x01);
}
-static int setexposure(struct gspca_dev *gspca_dev)
+static void 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
@@ -463,94 +449,72 @@ static int setexposure(struct gspca_dev *gspca_dev)
else if (reg > 63)
reg = 63;
- ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x02, reg);
+ 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 */
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0xff, 0x01);
+ 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);
+ reg_w(gspca_dev, 0x08, 0x09);
} else {
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x08, 0x08);
+ reg_w(gspca_dev, 0x08, 0x08);
}
/* load registers to sensor (Bit 0, auto clear) */
- if (0 <= ret)
- ret = reg_w(gspca_dev, 0x11, 0x01);
- return ret;
+ reg_w(gspca_dev, 0x11, 0x01);
}
-static int sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int ret;
__u8 data;
- ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ 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);
+ 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;
+ reg_w(gspca_dev, 0x11, 0x01);
}
/* this function is called at probe and resume time for pac7311 */
static int sd_init(struct gspca_dev *gspca_dev)
{
- return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
+ reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
+ return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int ret;
sd->sof_read = 0;
- ret = reg_w_var(gspca_dev, start_7311,
- NULL, 0,
+ reg_w_var(gspca_dev, start_7311,
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);
+ setcontrast(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ sethvflip(gspca_dev);
/* set correct resolution */
switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
case 2: /* 160x120 pac7311 */
- 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);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x20);
+ reg_w(gspca_dev, 0x87, 0x10);
break;
case 1: /* 320x240 pac7311 */
- 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);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x30);
+ reg_w(gspca_dev, 0x87, 0x11);
break;
case 0: /* 640x480 */
- 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);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x00);
+ reg_w(gspca_dev, 0x87, 0x12);
break;
}
@@ -559,37 +523,24 @@ static int sd_start(struct gspca_dev *gspca_dev)
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, 0x05);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x05);
- return ret;
+ return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- int ret;
-
- 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 */
+ 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 */
}
/* called on streamoff with alt 0 and on disconnect for 7311 */
@@ -734,7 +685,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
if (gspca_dev->streaming) {
setcontrast(gspca_dev);
}
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
@@ -752,7 +703,7 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
sd->gain = val;
if (gspca_dev->streaming)
setgain(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -770,7 +721,7 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
sd->exposure = val;
if (gspca_dev->streaming)
setexposure(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -801,7 +752,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
}
}
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -819,7 +770,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
sd->hflip = val;
if (gspca_dev->streaming)
sethvflip(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -837,7 +788,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
sd->vflip = val;
if (gspca_dev->streaming)
sethvflip(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -848,8 +799,39 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+ u8 data0, data1;
+
+ if (len == 2) {
+ data0 = data[0];
+ data1 = data[1];
+ if ((data0 == 0x00 && data1 == 0x11) ||
+ (data0 == 0x22 && data1 == 0x33) ||
+ (data0 == 0x44 && data1 == 0x55) ||
+ (data0 == 0x66 && data1 == 0x77) ||
+ (data0 == 0x88 && data1 == 0x99) ||
+ (data0 == 0xaa && data1 == 0xbb) ||
+ (data0 == 0xcc && data1 == 0xdd) ||
+ (data0 == 0xee && data1 == 0xff)) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+#endif
+
/* sub-driver description for pac7311 */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
@@ -860,6 +842,9 @@ static struct sd_desc sd_desc = {
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = do_autogain,
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
/* -- module initialisation -- */
diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h
index 20f67d9b8c0..8462a7c1a33 100644
--- a/drivers/media/video/gspca/pac_common.h
+++ b/drivers/media/video/gspca/pac_common.h
@@ -24,11 +24,10 @@
*/
/* We calculate the autogain at the end of the transfer of a frame, at this
- moment a frame with the old settings is being transmitted, and a frame is
- being captured with the old settings. So if we adjust the autogain we must
- ignore atleast the 2 next frames for the new settings to come into effect
- before doing any other adjustments */
-#define PAC_AUTOGAIN_IGNORE_FRAMES 3
+ moment a frame with the old settings is being captured and transmitted. So
+ if we adjust the gain or exposure we must ignore atleast the next frame for
+ the new settings to come into effect before doing any other adjustments. */
+#define PAC_AUTOGAIN_IGNORE_FRAMES 2
static const unsigned char pac_sof_marker[5] =
{ 0xff, 0xff, 0x00, 0xff, 0x96 };
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
new file mode 100644
index 00000000000..dda5fd4aa69
--- /dev/null
+++ b/drivers/media/video/gspca/sn9c2028.c
@@ -0,0 +1,757 @@
+/*
+ * SN9C2028 library
+ *
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * 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 "sn9c2028"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Theodore Kilgore");
+MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ u8 sof_read;
+ u16 model;
+};
+
+struct init_command {
+ unsigned char instruction[6];
+ unsigned char to_read; /* length to read. 0 means no reply requested */
+};
+
+/* V4L2 controls supported by the driver */
+static struct ctrl sd_ctrls[] = {
+};
+
+/* How to change the resolution of any of the VGA cams is unknown */
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* No way to change the resolution of the CIF cams is known */
+static const struct v4l2_pix_format cif_mode[] = {
+ {352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+ int rc;
+
+ PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0],
+ command[1], command[2], command[3], command[4], command[5]);
+
+ memcpy(gspca_dev->usb_buf, command, 6);
+ rc = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_CONFIGURATION,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 2, 0, gspca_dev->usb_buf, 6, 500);
+ if (rc < 0) {
+ PDEBUG(D_ERR, "command write [%02x] error %d",
+ gspca_dev->usb_buf[0], rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int sn9c2028_read1(struct gspca_dev *gspca_dev)
+{
+ int rc;
+
+ rc = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 1, 0, gspca_dev->usb_buf, 1, 500);
+ if (rc != 1) {
+ PDEBUG(D_ERR, "read1 error %d", rc);
+ return (rc < 0) ? rc : -EIO;
+ }
+ PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
+ return gspca_dev->usb_buf[0];
+}
+
+static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
+{
+ int rc;
+ rc = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 4, 0, gspca_dev->usb_buf, 4, 500);
+ if (rc != 4) {
+ PDEBUG(D_ERR, "read4 error %d", rc);
+ return (rc < 0) ? rc : -EIO;
+ }
+ memcpy(reading, gspca_dev->usb_buf, 4);
+ PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0],
+ reading[1], reading[2], reading[3]);
+ return rc;
+}
+
+static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+ int i, status;
+ __u8 reading[4];
+
+ status = sn9c2028_command(gspca_dev, command);
+ if (status < 0)
+ return status;
+
+ status = -1;
+ for (i = 0; i < 256 && status < 2; i++)
+ status = sn9c2028_read1(gspca_dev);
+ if (status != 2) {
+ PDEBUG(D_ERR, "long command status read error %d", status);
+ return (status < 0) ? status : -EIO;
+ }
+
+ memset(reading, 0, 4);
+ status = sn9c2028_read4(gspca_dev, reading);
+ if (status < 0)
+ return status;
+
+ /* in general, the first byte of the response is the first byte of
+ * the command, or'ed with 8 */
+ status = sn9c2028_read1(gspca_dev);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+ int err_code;
+
+ err_code = sn9c2028_command(gspca_dev, command);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = sn9c2028_read1(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ return 0;
+}
+
+/* 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 = &gspca_dev->cam;
+
+ PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)",
+ id->idVendor, id->idProduct);
+
+ sd->model = id->idProduct;
+
+ switch (sd->model) {
+ case 0x7005:
+ PDEBUG(D_PROBE, "Genius Smart 300 camera");
+ break;
+ case 0x8000:
+ PDEBUG(D_PROBE, "DC31VC");
+ break;
+ case 0x8001:
+ PDEBUG(D_PROBE, "Spy camera");
+ break;
+ case 0x8003:
+ PDEBUG(D_PROBE, "CIF camera");
+ break;
+ case 0x8008:
+ PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera");
+ break;
+ case 0x800a:
+ PDEBUG(D_PROBE, "Vivitar 3350b type camera");
+ cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+ break;
+ }
+
+ switch (sd->model) {
+ case 0x8000:
+ case 0x8001:
+ case 0x8003:
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ break;
+ default:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ }
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ int status = -1;
+
+ sn9c2028_read1(gspca_dev);
+ sn9c2028_read1(gspca_dev);
+ status = sn9c2028_read1(gspca_dev);
+
+ return (status < 0) ? status : 0;
+}
+
+static int run_start_commands(struct gspca_dev *gspca_dev,
+ struct init_command *cam_commands, int n)
+{
+ int i, err_code = -1;
+
+ for (i = 0; i < n; i++) {
+ switch (cam_commands[i].to_read) {
+ case 4:
+ err_code = sn9c2028_long_command(gspca_dev,
+ cam_commands[i].instruction);
+ break;
+ case 1:
+ err_code = sn9c2028_short_command(gspca_dev,
+ cam_commands[i].instruction);
+ break;
+ case 0:
+ err_code = sn9c2028_command(gspca_dev,
+ cam_commands[i].instruction);
+ break;
+ }
+ if (err_code < 0)
+ return err_code;
+ }
+ return 0;
+}
+
+static int start_spy_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command spy_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width 352 */
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */
+ /* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */
+ {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/
+ /* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */
+ {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
+ /* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ /* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+ /* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */
+ {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/
+ /*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
+ {{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
+ /*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */
+ {{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
+ {{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */
+ {{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4},
+ /*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
+ {{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4},
+ /* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */
+ /* brightness or gain. 0 is default. 4 is good
+ * indoors at night with incandescent lighting */
+ {{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/
+ {{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4},
+ /* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */
+ {{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */
+ /* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */
+ {{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, spy_start_commands,
+ ARRAY_SIZE(spy_start_commands));
+}
+
+static int start_cif_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command cif_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ /* The entire sequence below seems redundant */
+ /* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width?
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height?
+ {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
+ {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/
+ {{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */
+ /* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
+ * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing
+ * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */
+ /* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+ * causes subsampling
+ * but not a change in the resolution setting! */
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, cif_start_commands,
+ ARRAY_SIZE(cif_start_commands));
+}
+
+static int start_ms350_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command ms350_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width */
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */
+ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */
+ {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0},
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, ms350_start_commands,
+ ARRAY_SIZE(ms350_start_commands));
+}
+
+static int start_genius_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command genius_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+ /* "preliminary" width and height settings */
+ {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */
+ {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
+ {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+ {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0}
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, genius_start_commands,
+ ARRAY_SIZE(genius_start_commands));
+}
+
+static int start_vivitar_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command vivitar_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4},
+ /*
+ * Above is changed from OEM 0x0b. Fixes Bayer tiling.
+ * Presumably gives a vertical shift of one row.
+ */
+ {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
+ /* Above seems to do horizontal shift. */
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ /* Above three commands seem to relate to brightness. */
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1},
+ /* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */
+ {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1},
+ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1},
+ /* Above is brightness; OEM driver setting is 0x10 */
+ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}
+ };
+
+ return run_start_commands(gspca_dev, vivitar_start_commands,
+ ARRAY_SIZE(vivitar_start_commands));
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err_code;
+
+ sd->sof_read = 0;
+
+ switch (sd->model) {
+ case 0x7005:
+ err_code = start_genius_cam(gspca_dev);
+ break;
+ case 0x8001:
+ err_code = start_spy_cam(gspca_dev);
+ break;
+ case 0x8003:
+ err_code = start_cif_cam(gspca_dev);
+ break;
+ case 0x8008:
+ err_code = start_ms350_cam(gspca_dev);
+ break;
+ case 0x800a:
+ err_code = start_vivitar_cam(gspca_dev);
+ break;
+ default:
+ PDEBUG(D_ERR, "Starting unknown camera, please report this");
+ return -ENXIO;
+ }
+
+ return err_code;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ int result;
+ __u8 data[6];
+
+ result = sn9c2028_read1(gspca_dev);
+ if (result < 0)
+ PDEBUG(D_ERR, "Camera Stop read failed");
+
+ memset(data, 0, 6);
+ data[0] = 0x14;
+ result = sn9c2028_command(gspca_dev, data);
+ if (result < 0)
+ PDEBUG(D_ERR, "Camera Stop command failed");
+}
+
+/* Include sn9c2028 sof detection functions */
+#include "sn9c2028.h"
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ unsigned char *sof;
+
+ sof = sn9c2028_find_sof(gspca_dev, data, len);
+ if (sof) {
+ int n;
+
+ /* finish decoding current frame */
+ n = sof - data;
+ if (n > sizeof sn9c2028_sof_marker)
+ n -= sizeof sn9c2028_sof_marker;
+ else
+ n = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, n);
+ /* Start next frame. */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sn9c2028_sof_marker, sizeof sn9c2028_sof_marker);
+ len -= sof - data;
+ data = sof;
+ }
+ gspca_frame_add(gspca_dev, INTER_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,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */
+ /* The Genius Smart is untested. I can't find an owner ! */
+ /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */
+ {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */
+ {USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */
+ /* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */
+ {USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */
+ {USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */
+ {}
+};
+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/sn9c2028.h b/drivers/media/video/gspca/sn9c2028.h
new file mode 100644
index 00000000000..8fd1d3e0566
--- /dev/null
+++ b/drivers/media/video/gspca/sn9c2028.h
@@ -0,0 +1,51 @@
+/*
+ * SN9C2028 common functions
+ *
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn,edu>
+ *
+ * Based closely upon the file gspca/pac_common.h
+ *
+ * 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
+ *
+ */
+
+static const unsigned char sn9c2028_sof_marker[5] =
+ { 0xff, 0xff, 0x00, 0xc4, 0xc4 };
+
+static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev,
+ 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] == sn9c2028_sof_marker[sd->sof_read]) {
+ sd->sof_read++;
+ if (sd->sof_read == sizeof(sn9c2028_sof_marker)) {
+ PDEBUG(D_FRAM,
+ "SOF found, bytes to analyze: %u."
+ " Frame starts at byte #%u",
+ len, i + 1);
+ sd->sof_read = 0;
+ return m + i + 1;
+ }
+ } else {
+ sd->sof_read = 0;
+ }
+ }
+
+ return NULL;
+}
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index 0ca1c06652b..4a1bc08f82b 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -129,7 +129,7 @@ static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val);
static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val);
static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val);
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
#define BRIGHTNESS_IDX 0
{
@@ -1506,36 +1506,36 @@ static int set_cmatrix(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
s32 hue_coord, hue_index = 180 + sd->hue;
u8 cmatrix[21];
- memset(cmatrix, 0, 21);
+ memset(cmatrix, 0, sizeof cmatrix);
cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26;
cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
cmatrix[18] = sd->brightness - 0x80;
hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8;
- cmatrix[6] = (unsigned char)(hue_coord & 0xff);
- cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f);
+ cmatrix[6] = hue_coord;
+ cmatrix[7] = (hue_coord >> 8) & 0x0f;
hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8;
- cmatrix[8] = (unsigned char)(hue_coord & 0xff);
- cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f);
+ cmatrix[8] = hue_coord;
+ cmatrix[9] = (hue_coord >> 8) & 0x0f;
hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8;
- cmatrix[10] = (unsigned char)(hue_coord & 0xff);
- cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f);
+ cmatrix[10] = hue_coord;
+ cmatrix[11] = (hue_coord >> 8) & 0x0f;
hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8;
- cmatrix[12] = (unsigned char)(hue_coord & 0xff);
- cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f);
+ cmatrix[12] = hue_coord;
+ cmatrix[13] = (hue_coord >> 8) & 0x0f;
hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8;
- cmatrix[14] = (unsigned char)(hue_coord & 0xff);
- cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f);
+ cmatrix[14] = hue_coord;
+ cmatrix[15] = (hue_coord >> 8) & 0x0f;
hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8;
- cmatrix[16] = (unsigned char)(hue_coord & 0xff);
- cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f);
+ cmatrix[16] = hue_coord;
+ cmatrix[17] = (hue_coord >> 8) & 0x0f;
return reg_w(gspca_dev, 0x10e1, cmatrix, 21);
}
@@ -2015,6 +2015,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
default:
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
}
sd->old_step = 0;
@@ -2319,7 +2320,7 @@ static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
}
}
if (avg_lum > MAX_AVG_LUM) {
- if (sd->gain >= 1) {
+ if (sd->gain > 0) {
sd->gain--;
set_gain(gspca_dev);
}
@@ -2347,7 +2348,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum;
- static unsigned char frame_header[] =
+ static u8 frame_header[] =
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
if (len == 64 && memcmp(data, frame_header, 6) == 0) {
avg_lum = ((data[35] >> 2) & 3) |
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index ddff2b5ee5c..785eeb4c201 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -42,6 +42,7 @@ Reg Use
#define MODULE_NAME "sonixb"
+#include <linux/input.h>
#include "gspca.h"
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
@@ -53,9 +54,11 @@ struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
atomic_t avg_lum;
int prev_avg_lum;
+ int exp_too_low_cnt;
+ int exp_too_high_cnt;
+ unsigned short exposure;
unsigned char gain;
- unsigned char exposure;
unsigned char brightness;
unsigned char autogain;
unsigned char autogain_ignore_frames;
@@ -73,8 +76,9 @@ struct sd {
#define SENSOR_OV7630 2
#define SENSOR_PAS106 3
#define SENSOR_PAS202 4
-#define SENSOR_TAS5110 5
-#define SENSOR_TAS5130CXX 6
+#define SENSOR_TAS5110C 5
+#define SENSOR_TAS5110D 6
+#define SENSOR_TAS5130CXX 7
__u8 reg11;
};
@@ -95,13 +99,15 @@ struct sensor_data {
/* sensor_data flags */
#define F_GAIN 0x01 /* has gain */
#define F_SIF 0x02 /* sif or vga */
+#define F_COARSE_EXPO 0x04 /* exposure control is coarse */
/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
#define MODE_RAW 0x10 /* raw bayer mode */
#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */
/* ctrl_dis helper macros */
-#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX))
+#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << COARSE_EXPOSURE_IDX) | \
+ (1 << AUTOGAIN_IDX))
#define NO_FREQ (1 << FREQ_IDX)
#define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX)
@@ -127,11 +133,10 @@ struct sensor_data {
}
/* We calculate the autogain at the end of the transfer of a frame, at this
- moment a frame with the old settings is being transmitted, and a frame is
- being captured with the old settings. So if we adjust the autogain we must
- ignore atleast the 2 next frames for the new settings to come into effect
- before doing any other adjustments */
-#define AUTOGAIN_IGNORE_FRAMES 3
+ moment a frame with the old settings is being captured and transmitted. So
+ if we adjust the gain or exposure we must ignore atleast the next frame for
+ the new settings to come into effect before doing any other adjustments. */
+#define AUTOGAIN_IGNORE_FRAMES 1
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
@@ -145,7 +150,7 @@ static int sd_getautogain(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[] = {
+static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_IDX 0
{
{
@@ -171,7 +176,7 @@ static struct ctrl sd_ctrls[] = {
.maximum = 255,
.step = 1,
#define GAIN_DEF 127
-#define GAIN_KNEE 200
+#define GAIN_KNEE 230
.default_value = GAIN_DEF,
},
.set = sd_setgain,
@@ -183,10 +188,10 @@ static struct ctrl sd_ctrls[] = {
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
-#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */
-#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
+#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PASXXX) */
+#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */
.minimum = 0,
- .maximum = 255,
+ .maximum = 1023,
.step = 1,
.default_value = EXPOSURE_DEF,
.flags = 0,
@@ -194,7 +199,23 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setexposure,
.get = sd_getexposure,
},
-#define AUTOGAIN_IDX 3
+#define COARSE_EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+#define COARSE_EXPOSURE_DEF 2 /* 30 fps */
+ .minimum = 2,
+ .maximum = 15,
+ .step = 1,
+ .default_value = COARSE_EXPOSURE_DEF,
+ .flags = 0,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+#define AUTOGAIN_IDX 4
{
{
.id = V4L2_CID_AUTOGAIN,
@@ -210,7 +231,7 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setautogain,
.get = sd_getautogain,
},
-#define FREQ_IDX 4
+#define FREQ_IDX 5
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -219,7 +240,7 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
.step = 1,
-#define FREQ_DEF 1
+#define FREQ_DEF 0
.default_value = FREQ_DEF,
},
.set = sd_setfreq,
@@ -345,7 +366,7 @@ static const __u8 initOv7630[] = {
};
static const __u8 initOv7630_3[] = {
0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */
- 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */
0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */
0x28, 0x1e, /* H & V sizes r15 .. r16 */
0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */
@@ -387,6 +408,30 @@ static const __u8 initPas106[] = {
0x18, 0x10, 0x02, 0x02, 0x09, 0x07
};
/* compression 0x86 mckinit1 0x2b */
+
+/* "Known" PAS106B registers:
+ 0x02 clock divider
+ 0x03 Variable framerate bits 4-11
+ 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !!
+ The variable framerate control must never be set lower then 300,
+ which sets the framerate at 90 / reg02, otherwise vsync is lost.
+ 0x05 Shutter Time Line Offset, this can be used as an exposure control:
+ 0 = use full frame time, 255 = no exposure at all
+ Note this may never be larger then "var-framerate control" / 2 - 2.
+ When var-framerate control is < 514, no exposure is reached at the max
+ allowed value for the framerate control value, rather then at 255.
+ 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but
+ only a very little bit, leave at 0xcd
+ 0x07 offset sign bit (bit0 1 > negative offset)
+ 0x08 offset
+ 0x09 Blue Gain
+ 0x0a Green1 Gain
+ 0x0b Green2 Gain
+ 0x0c Red Gain
+ 0x0e Global gain
+ 0x13 Write 1 to commit settings to sensor
+*/
+
static const __u8 pas106_sensor_init[][8] = {
/* Pixel Clock Divider 6 */
{ 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 },
@@ -433,37 +478,55 @@ static const __u8 initPas202[] = {
0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x03, 0x0a,
- 0x28, 0x1e, 0x28, 0x89, 0x20,
+ 0x28, 0x1e, 0x20, 0x89, 0x20,
0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c
};
+
+/* "Known" PAS202BCB registers:
+ 0x02 clock divider
+ 0x04 Variable framerate bits 6-11 (*)
+ 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !!
+ 0x07 Blue Gain
+ 0x08 Green Gain
+ 0x09 Red Gain
+ 0x0b offset sign bit (bit0 1 > negative offset)
+ 0x0c offset
+ 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too,
+ leave at 1 otherwise we get a jump in our exposure control
+ 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all
+ 0x10 Master gain 0 - 31
+ 0x11 write 1 to apply changes
+ (*) The variable framerate control must never be set lower then 500
+ which sets the framerate at 30 / reg02, otherwise vsync is lost.
+*/
static const __u8 pas202_sensor_init[][8] = {
- {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10},
+ /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like
+ to set it lower, but for some reason the bridge starts missing
+ vsync's then */
+ {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10},
{0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10},
{0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10},
- {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10},
+ {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x01, 0x32, 0x10},
{0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10},
{0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10},
{0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10},
{0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
{0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10},
{0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
- {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10},
- {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10},
-
- {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16},
- {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15},
- {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16},
- {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16},
- {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16},
- {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
- {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15},
- {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
};
-static const __u8 initTas5110[] = {
+static const __u8 initTas5110c[] = {
0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
0x00, 0x00,
- 0x00, 0x01, 0x00, 0x45, 0x09, 0x0a,
+ 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a,
+ 0x16, 0x12, 0x60, 0x86, 0x2b,
+ 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07
+};
+/* Same as above, except a different hstart */
+static const __u8 initTas5110d[] = {
+ 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a,
0x16, 0x12, 0x60, 0x86, 0x2b,
0x14, 0x0a, 0x02, 0x02, 0x09, 0x07
};
@@ -476,7 +539,7 @@ static const __u8 tas5110_sensor_init[][8] = {
static const __u8 initTas5130[] = {
0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
0x00, 0x00,
- 0x00, 0x01, 0x00, 0x68, 0x0c, 0x0a,
+ 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a,
0x28, 0x1e, 0x60, COMP, MCK_INIT,
0x18, 0x10, 0x04, 0x03, 0x11, 0x0c
};
@@ -493,12 +556,14 @@ SENS(initHv7131, NULL, hv7131_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0),
SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60),
SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3,
F_GAIN, 0, 0x21),
-SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ,
+SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_GAIN|F_SIF, NO_FREQ,
0),
-SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0,
- NO_EXPO|NO_FREQ, 0),
-SENS(initTas5110, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF,
- NO_BRIGHTNESS|NO_FREQ, 0),
+SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN,
+ NO_FREQ, 0),
+SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL,
+ F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0),
+SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL,
+ F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0),
SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ,
0),
};
@@ -587,42 +652,28 @@ static void setbrightness(struct gspca_dev *gspca_dev)
goto err;
break;
}
- case SENSOR_PAS106: {
- __u8 i2c1[] =
- {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14};
-
- i2c1[3] = sd->brightness >> 3;
- i2c1[2] = 0x0e;
- if (i2c_w(gspca_dev, i2c1) < 0)
- goto err;
- i2c1[3] = 0x01;
- i2c1[2] = 0x13;
- if (i2c_w(gspca_dev, i2c1) < 0)
- goto err;
- break;
- }
+ case SENSOR_PAS106:
case SENSOR_PAS202: {
- /* __u8 i2cpexpo1[] =
- {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */
- __u8 i2cpexpo[] =
- {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16};
- __u8 i2cp202[] =
- {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15};
- static __u8 i2cpdoit[] =
- {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16};
-
- /* change reg 0x10 */
- i2cpexpo[4] = 0xff - sd->brightness;
-/* if(i2c_w(gspca_dev,i2cpexpo1) < 0)
- goto err; */
-/* if(i2c_w(gspca_dev,i2cpdoit) < 0)
- goto err; */
- if (i2c_w(gspca_dev, i2cpexpo) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpdoit) < 0)
- goto err;
- i2cp202[3] = sd->brightness >> 3;
- if (i2c_w(gspca_dev, i2cp202) < 0)
+ __u8 i2cpbright[] =
+ {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16};
+ __u8 i2cpdoit[] =
+ {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+ /* PAS106 uses reg 7 and 8 instead of b and c */
+ if (sd->sensor == SENSOR_PAS106) {
+ i2cpbright[2] = 7;
+ i2cpdoit[2] = 0x13;
+ }
+
+ if (sd->brightness < 127) {
+ /* change reg 0x0b, signreg */
+ i2cpbright[3] = 0x01;
+ /* set reg 0x0c, offset */
+ i2cpbright[4] = 127 - sd->brightness;
+ } else
+ i2cpbright[4] = sd->brightness - 127;
+
+ if (i2c_w(gspca_dev, i2cpbright) < 0)
goto err;
if (i2c_w(gspca_dev, i2cpdoit) < 0)
goto err;
@@ -652,7 +703,8 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
- case SENSOR_TAS5110: {
+ case SENSOR_TAS5110C:
+ case SENSOR_TAS5110D: {
__u8 i2c[] =
{0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
@@ -674,6 +726,37 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
goto err;
break;
}
+ case SENSOR_PAS106:
+ case SENSOR_PAS202: {
+ __u8 i2cpgain[] =
+ {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15};
+ __u8 i2cpcolorgain[] =
+ {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15};
+ __u8 i2cpdoit[] =
+ {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+ /* PAS106 uses different regs (and has split green gains) */
+ if (sd->sensor == SENSOR_PAS106) {
+ i2cpgain[2] = 0x0e;
+ i2cpcolorgain[0] = 0xd0;
+ i2cpcolorgain[2] = 0x09;
+ i2cpdoit[2] = 0x13;
+ }
+
+ i2cpgain[3] = sd->gain >> 3;
+ i2cpcolorgain[3] = sd->gain >> 4;
+ i2cpcolorgain[4] = sd->gain >> 4;
+ i2cpcolorgain[5] = sd->gain >> 4;
+ i2cpcolorgain[6] = sd->gain >> 4;
+
+ if (i2c_w(gspca_dev, i2cpgain) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpcolorgain) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
+ break;
+ }
}
return;
err:
@@ -684,19 +767,21 @@ static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 gain;
- __u8 rgb_value;
+ __u8 buf[2] = { 0, 0 };
+
+ if (sensor_data[sd->sensor].flags & F_GAIN) {
+ /* Use the sensor gain to do the actual gain */
+ setsensorgain(gspca_dev);
+ return;
+ }
gain = sd->gain >> 4;
/* red and blue gain */
- rgb_value = gain << 4 | gain;
- reg_w(gspca_dev, 0x10, &rgb_value, 1);
+ buf[0] = gain << 4 | gain;
/* green gain */
- rgb_value = gain;
- reg_w(gspca_dev, 0x11, &rgb_value, 1);
-
- if (sensor_data[sd->sensor].flags & F_GAIN)
- setsensorgain(gspca_dev);
+ buf[1] = gain;
+ reg_w(gspca_dev, 0x10, buf, 2);
}
static void setexposure(struct gspca_dev *gspca_dev)
@@ -704,17 +789,12 @@ static void setexposure(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
switch (sd->sensor) {
- case SENSOR_TAS5110: {
- __u8 reg;
-
+ case SENSOR_TAS5110C:
+ case SENSOR_TAS5110D: {
/* register 19's high nibble contains the sn9c10x clock divider
The high nibble configures the no fps according to the
formula: 60 / high_nibble. With a maximum of 30 fps */
- reg = 120 * sd->exposure / 1000;
- if (reg < 2)
- reg = 2;
- else if (reg > 15)
- reg = 15;
+ __u8 reg = sd->exposure;
reg = (reg << 4) | 0x0b;
reg_w(gspca_dev, 0x19, &reg, 1);
break;
@@ -750,20 +830,21 @@ static void setexposure(struct gspca_dev *gspca_dev)
} else
reg10_max = 0x41;
- reg11 = (60 * sd->exposure + 999) / 1000;
+ reg11 = (15 * sd->exposure + 999) / 1000;
if (reg11 < 1)
reg11 = 1;
else if (reg11 > 16)
reg11 = 16;
- /* In 640x480, if the reg11 has less than 3, the image is
- unstable (not enough bandwidth). */
- if (gspca_dev->width == 640 && reg11 < 3)
- reg11 = 3;
+ /* In 640x480, if the reg11 has less than 4, the image is
+ unstable (the bridge goes into a higher compression mode
+ which we have not reverse engineered yet). */
+ if (gspca_dev->width == 640 && reg11 < 4)
+ reg11 = 4;
/* frame exposure time in ms = 1000 * reg11 / 30 ->
- reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */
- reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11);
+ reg10 = (sd->exposure / 2) * reg10_max / (1000 * reg11 / 30) */
+ reg10 = (sd->exposure * 15 * reg10_max) / (1000 * reg11);
/* Don't allow this to get below 10 when using autogain, the
steps become very large (relatively) when below 10 causing
@@ -786,10 +867,85 @@ static void setexposure(struct gspca_dev *gspca_dev)
if (i2c_w(gspca_dev, i2c) == 0)
sd->reg11 = reg11;
else
- PDEBUG(D_ERR, "i2c error exposure");
+ goto err;
+ break;
+ }
+ case SENSOR_PAS202: {
+ __u8 i2cpframerate[] =
+ {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
+ __u8 i2cpexpo[] =
+ {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16};
+ const __u8 i2cpdoit[] =
+ {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+ int framerate_ctrl;
+
+ /* The exposure knee for the autogain algorithm is 200
+ (100 ms / 10 fps on other sensors), for values below this
+ use the control for setting the partial frame expose time,
+ above that use variable framerate. This way we run at max
+ framerate (640x480@7.5 fps, 320x240@10fps) until the knee
+ is reached. Using the variable framerate control above 200
+ is better then playing around with both clockdiv + partial
+ frame exposure times (like we are doing with the ov chips),
+ as that sometimes leads to jumps in the exposure control,
+ which are bad for auto exposure. */
+ if (sd->exposure < 200) {
+ i2cpexpo[3] = 255 - (sd->exposure * 255) / 200;
+ framerate_ctrl = 500;
+ } else {
+ /* The PAS202's exposure control goes from 0 - 4095,
+ but anything below 500 causes vsync issues, so scale
+ our 200-1023 to 500-4095 */
+ framerate_ctrl = (sd->exposure - 200) * 1000 / 229 +
+ 500;
+ }
+
+ i2cpframerate[3] = framerate_ctrl >> 6;
+ i2cpframerate[4] = framerate_ctrl & 0x3f;
+ if (i2c_w(gspca_dev, i2cpframerate) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpexpo) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
+ break;
+ }
+ case SENSOR_PAS106: {
+ __u8 i2cpframerate[] =
+ {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
+ __u8 i2cpexpo[] =
+ {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14};
+ const __u8 i2cpdoit[] =
+ {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14};
+ int framerate_ctrl;
+
+ /* For values below 150 use partial frame exposure, above
+ that use framerate ctrl */
+ if (sd->exposure < 150) {
+ i2cpexpo[3] = 150 - sd->exposure;
+ framerate_ctrl = 300;
+ } else {
+ /* The PAS106's exposure control goes from 0 - 4095,
+ but anything below 300 causes vsync issues, so scale
+ our 150-1023 to 300-4095 */
+ framerate_ctrl = (sd->exposure - 150) * 1000 / 230 +
+ 300;
+ }
+
+ i2cpframerate[3] = framerate_ctrl >> 4;
+ i2cpframerate[4] = framerate_ctrl & 0x0f;
+ if (i2c_w(gspca_dev, i2cpframerate) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpexpo) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
break;
}
}
+ return;
+err:
+ PDEBUG(D_ERR, "i2c error exposure");
}
static void setfreq(struct gspca_dev *gspca_dev)
@@ -823,30 +979,43 @@ static void setfreq(struct gspca_dev *gspca_dev)
}
}
+#include "coarse_expo_autogain.h"
+
static void do_autogain(struct gspca_dev *gspca_dev)
{
- int deadzone, desired_avg_lum;
+ int deadzone, desired_avg_lum, result;
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum = atomic_read(&sd->avg_lum);
- if (avg_lum == -1)
+ if (avg_lum == -1 || !sd->autogain)
return;
+ if (sd->autogain_ignore_frames > 0) {
+ sd->autogain_ignore_frames--;
+ return;
+ }
+
/* SIF / VGA sensors have a different autoexposure area and thus
different avg_lum values for the same picture brightness */
if (sensor_data[sd->sensor].flags & F_SIF) {
- deadzone = 1000;
- desired_avg_lum = 7000;
+ deadzone = 500;
+ /* SIF sensors tend to overexpose, so keep this small */
+ desired_avg_lum = 5000;
} else {
- deadzone = 3000;
- desired_avg_lum = 23000;
+ deadzone = 1500;
+ desired_avg_lum = 18000;
}
- if (sd->autogain_ignore_frames > 0)
- sd->autogain_ignore_frames--;
- else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
- sd->brightness * desired_avg_lum / 127,
- deadzone, GAIN_KNEE, EXPOSURE_KNEE)) {
+ if (sensor_data[sd->sensor].flags & F_COARSE_EXPO)
+ result = gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+ sd->brightness * desired_avg_lum / 127,
+ deadzone);
+ else
+ result = gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
+ sd->brightness * desired_avg_lum / 127,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE);
+
+ if (result) {
PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
(int)sd->gain, (int)sd->exposure);
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
@@ -881,7 +1050,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->brightness = BRIGHTNESS_DEF;
sd->gain = GAIN_DEF;
- sd->exposure = EXPOSURE_DEF;
+ if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) {
+ sd->exposure = COARSE_EXPOSURE_DEF;
+ gspca_dev->ctrl_dis |= (1 << EXPOSURE_IDX);
+ } else {
+ sd->exposure = EXPOSURE_DEF;
+ gspca_dev->ctrl_dis |= (1 << COARSE_EXPOSURE_IDX);
+ }
if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
sd->autogain = 0; /* Disable do_autogain callback */
else
@@ -917,9 +1092,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4);
/* Special cases where reg 17 and or 19 value depends on mode */
switch (sd->sensor) {
- case SENSOR_PAS202:
- reg12_19[5] = mode ? 0x24 : 0x20;
- break;
case SENSOR_TAS5130CXX:
/* probably not mode specific at all most likely the upper
nibble of 0x19 is exposure (clock divider) just as with
@@ -955,6 +1127,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
sensor_data[sd->sensor].sensor_bridge_init_size[
sd->bridge]);
+ /* Mode specific sensor setup */
+ switch (sd->sensor) {
+ case SENSOR_PAS202: {
+ const __u8 i2cpclockdiv[] =
+ {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10};
+ /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */
+ if (mode)
+ i2c_w(gspca_dev, i2cpclockdiv);
+ }
+ }
/* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */
reg_w(gspca_dev, 0x15, &reg12_19[3], 2);
/* compression register */
@@ -985,6 +1167,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
sd->frames_to_drop = 0;
sd->autogain_ignore_frames = 0;
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
atomic_set(&sd->avg_lum, -1);
return 0;
}
@@ -1143,11 +1327,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->autogain = val;
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
+
/* 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) {
+ if (sd->autogain && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) {
sd->exposure = EXPOSURE_DEF;
sd->gain = GAIN_DEF;
if (gspca_dev->streaming) {
@@ -1207,6 +1394,25 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
return -EINVAL;
}
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -1219,6 +1425,9 @@ static const struct sd_desc sd_desc = {
.pkt_scan = sd_pkt_scan,
.querymenu = sd_querymenu,
.dq_callback = do_autogain,
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
/* -- module initialisation -- */
@@ -1227,21 +1436,21 @@ static const struct sd_desc sd_desc = {
static const struct usb_device_id device_table[] __devinitconst = {
- {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */
- {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */
+ {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */
+ {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
- {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110, 101)}, /* TAS5110D */
+ {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */
+#endif
{USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)},
{USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)},
-#endif
{USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)},
{USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)},
{USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
+#endif
{USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
{USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
-#endif
{USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
{USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 0bd36a00dd2..83d5773d462 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -21,6 +21,7 @@
#define MODULE_NAME "sonixj"
+#include <linux/input.h>
#include "gspca.h"
#include "jpeg.h"
@@ -45,6 +46,7 @@ struct sd {
u8 red;
u8 gamma;
u8 vflip; /* ov7630/ov7648 only */
+ u8 sharpness;
u8 infrared; /* mt9v111 only */
u8 freq; /* ov76xx only */
u8 quality; /* image quality */
@@ -64,16 +66,17 @@ struct sd {
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
u8 sensor; /* Type of image sensor chip */
-#define SENSOR_HV7131R 0
-#define SENSOR_MI0360 1
-#define SENSOR_MO4000 2
-#define SENSOR_MT9V111 3
-#define SENSOR_OM6802 4
-#define SENSOR_OV7630 5
-#define SENSOR_OV7648 6
-#define SENSOR_OV7660 7
-#define SENSOR_PO1030 8
-#define SENSOR_SP80708 9
+#define SENSOR_ADCM1700 0
+#define SENSOR_HV7131R 1
+#define SENSOR_MI0360 2
+#define SENSOR_MO4000 3
+#define SENSOR_MT9V111 4
+#define SENSOR_OM6802 5
+#define SENSOR_OV7630 6
+#define SENSOR_OV7648 7
+#define SENSOR_OV7660 8
+#define SENSOR_PO1030 9
+#define SENSOR_SP80708 10
u8 i2c_addr;
u8 *jpeg_hdr;
@@ -96,12 +99,14 @@ 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_setvflip(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getvflip(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_setinfrared(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getinfrared(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[] = {
+static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_IDX 0
{
{
@@ -225,8 +230,23 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setvflip,
.get = sd_getvflip,
},
+#define SHARPNESS_IDX 8
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define SHARPNESS_DEF 90
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
/* mt9v111 only */
-#define INFRARED_IDX 8
+#define INFRARED_IDX 9
{
{
.id = V4L2_CID_INFRARED,
@@ -242,7 +262,7 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getinfrared,
},
/* ov7630/ov7648/ov7660 only */
-#define FREQ_IDX 9
+#define FREQ_IDX 10
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -261,28 +281,37 @@ static struct ctrl sd_ctrls[] = {
/* table of the disabled controls */
static __u32 ctrl_dis[] = {
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX) |
+ (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */
+ (1 << INFRARED_IDX) | (1 << FREQ_IDX),
+ /* SENSOR_HV7131R 1 */
(1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
- /* SENSOR_HV7131R 0 */
+ /* SENSOR_MI0360 2 */
(1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
- /* SENSOR_MI0360 1 */
- (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
- /* SENSOR_MO4000 2 */
+ /* SENSOR_MO4000 3 */
(1 << VFLIP_IDX) | (1 << FREQ_IDX),
- /* SENSOR_MT9V111 3 */
+ /* SENSOR_MT9V111 4 */
(1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
- /* SENSOR_OM6802 4 */
+ /* SENSOR_OM6802 5 */
(1 << INFRARED_IDX),
- /* SENSOR_OV7630 5 */
+ /* SENSOR_OV7630 6 */
(1 << INFRARED_IDX),
- /* SENSOR_OV7648 6 */
+ /* SENSOR_OV7648 7 */
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
- /* SENSOR_OV7660 7 */
+ /* SENSOR_OV7660 8 */
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
- (1 << FREQ_IDX), /* SENSOR_PO1030 8 */
+ (1 << FREQ_IDX), /* SENSOR_PO1030 9 */
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
- (1 << FREQ_IDX), /* SENSOR_SP80708 9 */
+ (1 << FREQ_IDX), /* SENSOR_SP80708 10 */
};
+static const struct v4l2_pix_format cif_mode[] = {
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -302,6 +331,17 @@ static const struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
+static const u8 sn_adcm1700[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
/*Data from sn9c102p+hv7131r */
static const u8 sn_hv7131[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
@@ -415,6 +455,7 @@ static const u8 sn_sp80708[0x1c] = {
/* sequence specific to the sensors - !! index = SENSOR_xxx */
static const u8 *sn_tb[] = {
+ sn_adcm1700,
sn_hv7131,
sn_mi0360,
sn_mo4000,
@@ -432,6 +473,11 @@ static const u8 gamma_def[17] = {
0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
};
+/* gamma for sensor ADCM1700 */
+static const u8 gamma_spec_0[17] = {
+ 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4,
+ 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5
+};
/* gamma for sensors HV7131R and MT9V111 */
static const u8 gamma_spec_1[17] = {
0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
@@ -450,6 +496,42 @@ static const u8 reg84[] = {
0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
0x00, 0x00, 0x00 /* YUV offsets */
};
+static const u8 adcm1700_sensor_init[][8] = {
+ {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 adcm1700_sensor_param1[][8] = {
+ {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */
+ {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10},
+
+ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10},
+ {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */
+
+ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10},
+ {}
+};
static const u8 hv7131r_sensor_init[][8] = {
{0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
{0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10},
@@ -986,17 +1068,18 @@ static const u8 sp80708_sensor_param1[][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 */
+static const u8 (*sensor_init[11])[8] = {
+ adcm1700_sensor_init, /* ADCM1700 0 */
+ hv7131r_sensor_init, /* HV7131R 1 */
+ mi0360_sensor_init, /* MI0360 2 */
+ mo4000_sensor_init, /* MO4000 3 */
+ mt9v111_sensor_init, /* MT9V111 4 */
+ om6802_sensor_init, /* OM6802 5 */
+ ov7630_sensor_init, /* OV7630 6 */
+ ov7648_sensor_init, /* OV7648 7 */
+ ov7660_sensor_init, /* OV7660 8 */
+ po1030_sensor_init, /* PO1030 9 */
+ sp80708_sensor_init, /* SP80708 10 */
};
/* read <len> bytes to gspca_dev->usb_buf */
@@ -1064,6 +1147,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val);
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */
gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
break;
@@ -1110,6 +1194,7 @@ static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
u8 mode[8];
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */
mode[0] = 0x80 | 0x10;
break;
@@ -1260,7 +1345,8 @@ static void bridge_init(struct gspca_dev *gspca_dev,
{0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
static const u8 regd4[] = {0x60, 0x00, 0x00};
- reg_w1(gspca_dev, 0xf1, 0x00);
+ /* sensor clock already enabled in sd_init */
+ /* reg_w1(gspca_dev, 0xf1, 0x00); */
reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
/* configure gpio */
@@ -1284,6 +1370,12 @@ static void bridge_init(struct gspca_dev *gspca_dev,
reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ reg_w1(gspca_dev, 0x01, 0x43);
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ break;
case SENSOR_MT9V111:
reg_w1(gspca_dev, 0x01, 0x61);
reg_w1(gspca_dev, 0x17, 0x61);
@@ -1357,14 +1449,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
- cam = &gspca_dev->cam;
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- cam->npkt = 24; /* 24 packets per ISOC message */
-
sd->bridge = id->driver_info >> 16;
sd->sensor = id->driver_info;
+ cam = &gspca_dev->cam;
+ if (sd->sensor == SENSOR_ADCM1700) {
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ } else {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ }
+ cam->npkt = 24; /* 24 packets per ISOC message */
+
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
@@ -1374,6 +1471,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->autogain = AUTOGAIN_DEF;
sd->ag_cnt = -1;
sd->vflip = VFLIP_DEF;
+ switch (sd->sensor) {
+ case SENSOR_OM6802:
+ sd->sharpness = 0x10;
+ break;
+ default:
+ sd->sharpness = SHARPNESS_DEF;
+ break;
+ }
sd->infrared = INFRARED_DEF;
sd->freq = FREQ_DEF;
sd->quality = QUALITY_DEF;
@@ -1433,7 +1538,9 @@ static int sd_init(struct gspca_dev *gspca_dev)
break;
}
- reg_w1(gspca_dev, 0xf1, 0x01);
+ /* Note we do not disable the sensor clock here (power saving mode),
+ as that also disables the button on the cam. */
+ reg_w1(gspca_dev, 0xf1, 0x00);
/* set the i2c address */
sn9c1xx = sn_tb[sd->sensor];
@@ -1543,6 +1650,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
k2 = ((int) sd->brightness - 0x8000) >> 10;
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ if (k2 > 0x1f)
+ k2 = 0; /* only positive Y offset */
+ break;
case SENSOR_HV7131R:
expo = sd->brightness << 4;
if (expo > 0x002dc6c0)
@@ -1625,6 +1736,9 @@ static void setgamma(struct gspca_dev *gspca_dev)
};
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ gamma_base = gamma_spec_0;
+ break;
case SENSOR_HV7131R:
case SENSOR_MT9V111:
gamma_base = gamma_spec_1;
@@ -1670,23 +1784,39 @@ static void setautogain(struct gspca_dev *gspca_dev)
sd->ag_cnt = -1;
}
-/* ov7630/ov7648 only */
+/* hv7131r/ov7630/ov7648 only */
static void setvflip(struct sd *sd)
{
u8 comn;
if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX))
return;
- if (sd->sensor == SENSOR_OV7630) {
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ comn = 0x18; /* clkdiv = 1, ablcen = 1 */
+ if (sd->vflip)
+ comn |= 0x01;
+ i2c_w1(&sd->gspca_dev, 0x01, comn); /* sctra */
+ break;
+ case SENSOR_OV7630:
comn = 0x02;
if (!sd->vflip)
comn |= 0x80;
- } else {
+ i2c_w1(&sd->gspca_dev, 0x75, comn);
+ break;
+ default:
+/* case SENSOR_OV7648: */
comn = 0x06;
if (sd->vflip)
comn |= 0x80;
+ i2c_w1(&sd->gspca_dev, 0x75, comn);
+ break;
}
- i2c_w1(&sd->gspca_dev, 0x75, comn);
+}
+
+static void setsharpness(struct sd *sd)
+{
+ reg_w1(&sd->gspca_dev, 0x99, sd->sharpness);
}
static void setinfrared(struct sd *sd)
@@ -1804,6 +1934,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
int mode;
static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+ static const u8 CA_adcm1700[] =
+ { 0x14, 0xec, 0x0a, 0xf6 };
static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
static const u8 CE_ov76xx[] =
{ 0x32, 0xdd, 0x32, 0xdd };
@@ -1824,6 +1956,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ reg2 = 0x60;
+ break;
case SENSOR_OM6802:
reg2 = 0x71;
break;
@@ -1842,17 +1977,28 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]);
reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]);
reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
- reg_w1(gspca_dev, 0xd2, 0x6a); /* DC29 */
- reg_w1(gspca_dev, 0xd3, 0x50);
+ if (sd->sensor == SENSOR_ADCM1700) {
+ reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */
+ reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */
+ } else {
+ reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */
+ reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */
+ }
reg_w1(gspca_dev, 0xc6, 0x00);
reg_w1(gspca_dev, 0xc7, 0x00);
- reg_w1(gspca_dev, 0xc8, 0x50);
- reg_w1(gspca_dev, 0xc9, 0x3c);
+ if (sd->sensor == SENSOR_ADCM1700) {
+ reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */
+ reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */
+ } else {
+ reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */
+ reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */
+ }
reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
switch (sd->sensor) {
case SENSOR_MT9V111:
reg17 = 0xe0;
break;
+ case SENSOR_ADCM1700:
case SENSOR_OV7630:
reg17 = 0xe2;
break;
@@ -1870,44 +2016,39 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
reg_w1(gspca_dev, 0x17, reg17);
-/* set reg1 was here */
- reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */
- reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */
- reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */
+
+ reg_w1(gspca_dev, 0x05, 0x00); /* red */
+ reg_w1(gspca_dev, 0x07, 0x00); /* green */
+ reg_w1(gspca_dev, 0x06, 0x00); /* blue */
reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
setgamma(gspca_dev);
+/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */
for (i = 0; i < 8; i++)
reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OV7660:
+ case SENSOR_SP80708:
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ break;
case SENSOR_MT9V111:
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);
- break;
- case SENSOR_OV7660:
- case SENSOR_SP80708:
- reg_w1(gspca_dev, 0x9a, 0x05);
- reg_w1(gspca_dev, 0x99, 0x59);
break;
default:
reg_w1(gspca_dev, 0x9a, 0x08);
- reg_w1(gspca_dev, 0x99, 0x59);
break;
}
+ setsharpness(sd);
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 */
+ reg_w1(gspca_dev, 0x05, 0x20); /* red */
+ reg_w1(gspca_dev, 0x07, 0x20); /* green */
+ reg_w1(gspca_dev, 0x06, 0x20); /* blue */
init = NULL;
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
@@ -1917,6 +2058,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */
reg17 = 0x61; /* 0x:20: enable sensor clock */
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ init = adcm1700_sensor_param1;
+ reg1 = 0x46;
+ reg17 = 0xe2;
+ break;
case SENSOR_MO4000:
if (mode) {
/* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */
@@ -1940,7 +2086,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg17 = 0x64; /* 640 MCKSIZE */
break;
case SENSOR_OV7630:
- setvflip(sd);
reg17 = 0xe2;
reg1 = 0x44;
break;
@@ -1986,8 +2131,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
reg_w(gspca_dev, 0xc0, C0, 6);
- reg_w(gspca_dev, 0xca, CA, 4);
+ if (sd->sensor == SENSOR_ADCM1700)
+ reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
+ else
+ reg_w(gspca_dev, 0xca, CA, 4);
switch (sd->sensor) {
+ case SENSOR_ADCM1700:
case SENSOR_OV7630:
case SENSOR_OV7648:
case SENSOR_OV7660:
@@ -2008,11 +2157,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);
- break;
- }
+ setvflip(sd);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
setautogain(gspca_dev);
@@ -2056,7 +2201,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]);
reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
reg_w1(gspca_dev, 0x01, data);
- reg_w1(gspca_dev, 0xf1, 0x00);
+ /* Don't disable sensor clock as that disables the button on the cam */
+ /* reg_w1(gspca_dev, 0xf1, 0x01); */
}
static void sd_stop0(struct gspca_dev *gspca_dev)
@@ -2288,6 +2434,24 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
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)
+ setsharpness(sd);
+ 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_setvflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2391,6 +2555,25 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
return -EINVAL;
}
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -2406,6 +2589,9 @@ static const struct sd_desc sd_desc = {
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
.querymenu = sd_querymenu,
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
/* -- module initialisation -- */
@@ -2472,6 +2658,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/
{USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/
{USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index fe46868a87f..b866c73c97d 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -68,7 +68,7 @@ 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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -1047,7 +1047,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
}
/* sub-driver description */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index 6761a3048a9..c99333933e3 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -59,7 +59,7 @@ static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
#define MY_BRIGHTNESS 0
{
{
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index 0f9232ff128..c576eed73ab 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -42,7 +42,7 @@ struct sd {
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index 39257e4e074..89fec4c500a 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -51,7 +51,7 @@ static int sd_getcolors(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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
#define SD_BRIGHTNESS 0
{
{
@@ -673,7 +673,7 @@ static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
}
/* sub-driver description */
-static struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 4d8e6cf75d5..15b2eef8a3f 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -45,7 +45,7 @@ struct sd {
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index 58c2f0039af..dc7f2b0fbc7 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -922,7 +922,7 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
}
/* control tables */
-static struct ctrl sd_ctrls_12a[] = {
+static const struct ctrl sd_ctrls_12a[] = {
{
{
.id = V4L2_CID_HUE,
@@ -964,7 +964,7 @@ static struct ctrl sd_ctrls_12a[] = {
},
};
-static struct ctrl sd_ctrls_72a[] = {
+static const struct ctrl sd_ctrls_72a[] = {
{
{
.id = V4L2_CID_HUE,
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index d70b156872d..e6466205299 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -47,6 +47,7 @@ MODULE_LICENSE("GPL");
/* Commands. These go in the "value" slot. */
#define SQ905C_CLEAR 0xa0 /* clear everything */
+#define SQ905C_GET_ID 0x14f4 /* Read version number */
#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */
#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */
#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */
@@ -101,6 +102,26 @@ static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
return 0;
}
+static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
+ int size)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ command, index, gspca_dev->usb_buf, size,
+ SQ905C_CMD_TIMEOUT);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
/* This function is called as a workqueue function and runs whenever the camera
* is streaming data. Because it is a workqueue function it is allowed to sleep
* so we can use synchronous USB calls. To avoid possible collisions with other
@@ -183,13 +204,34 @@ static int sd_config(struct gspca_dev *gspca_dev,
{
struct cam *cam = &gspca_dev->cam;
struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
PDEBUG(D_PROBE,
"SQ9050 camera detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+ ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Get version command failed");
+ return ret;
+ }
+
+ ret = sq905c_read(gspca_dev, 0xf5, 0, 20);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Reading version command failed");
+ return ret;
+ }
+ /* Note we leave out the usb id and the manufacturing date */
+ PDEBUG(D_PROBE,
+ "SQ9050 ID string: %02x - %02x %02x %02x %02x %02x %02x",
+ gspca_dev->usb_buf[3],
+ gspca_dev->usb_buf[14], gspca_dev->usb_buf[15],
+ gspca_dev->usb_buf[16], gspca_dev->usb_buf[17],
+ gspca_dev->usb_buf[18], gspca_dev->usb_buf[19]);
+
cam->cam_mode = sq905c_mode;
cam->nmodes = 2;
- if (id->idProduct == 0x9050)
+ if (gspca_dev->usb_buf[15] == 0)
cam->nmodes = 1;
/* We don't use the buffer gspca allocates so make it small. */
cam->bulk_size = 32;
@@ -258,6 +300,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x2770, 0x905c)},
{USB_DEVICE(0x2770, 0x9050)},
+ {USB_DEVICE(0x2770, 0x9052)},
{USB_DEVICE(0x2770, 0x913d)},
{}
};
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 2e2935532d9..0fb534210a2 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -53,7 +53,7 @@ static int sd_getcolors(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[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
index 2a69d7ccb50..e50dd7693f7 100644
--- a/drivers/media/video/gspca/stv0680.c
+++ b/drivers/media/video/gspca/stv0680.c
@@ -45,7 +45,7 @@ struct sd {
};
/* V4L2 controls supported by the driver */
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
};
static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
@@ -53,24 +53,28 @@ static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
{
int ret = -1;
u8 req_type = 0;
+ unsigned int pipe = 0;
switch (set) {
case 0: /* 0xc1 */
req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
break;
case 1: /* 0x41 */
req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
break;
case 2: /* 0x80 */
req_type = USB_DIR_IN | USB_RECIP_DEVICE;
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
break;
case 3: /* 0x40 */
req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
break;
}
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
+ ret = usb_control_msg(gspca_dev->dev, pipe,
req, req_type,
val, 0, gspca_dev->usb_buf, size, 500);
@@ -138,6 +142,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam = &gspca_dev->cam;
+ /* Give the camera some time to settle, otherwise initalization will
+ fail on hotplug, and yes it really needs a full second. */
+ msleep(1000);
+
/* 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) {
@@ -169,6 +177,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
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] & 0x04)
+ PDEBUG(D_PROBE, "Camera supports QCIF mode");
if (gspca_dev->usb_buf[7] & 0x08)
PDEBUG(D_PROBE, "Camera supports QVGA mode");
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
index 5d0241bb161..af73da34c83 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -27,6 +27,7 @@
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
+#include <linux/input.h>
#include "stv06xx_sensor.h"
MODULE_AUTHOR("Erik Andrén");
@@ -219,6 +220,7 @@ static void stv06xx_dump_bridge(struct sd *sd)
info("Read 0x%x from address 0x%x", data, i);
}
+ info("Testing stv06xx bridge registers for writability");
for (i = 0x1400; i < 0x160f; i++) {
stv06xx_read_bridge(sd, i, &data);
buf = data;
@@ -229,7 +231,7 @@ static void stv06xx_dump_bridge(struct sd *sd)
info("Register 0x%x is read/write", i);
else if (data != buf)
info("Register 0x%x is read/write,"
- "but only partially", i);
+ " but only partially", i);
else
info("Register 0x%x is read-only", i);
@@ -426,6 +428,29 @@ frame_data:
}
}
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 0x80) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ if (len == 1 && data[0] == 0x88) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
static int stv06xx_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id);
@@ -436,7 +461,10 @@ static const struct sd_desc sd_desc = {
.init = stv06xx_init,
.start = stv06xx_start,
.stopN = stv06xx_stopN,
- .pkt_scan = stv06xx_pkt_scan
+ .pkt_scan = stv06xx_pkt_scan,
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
/* This function is called at probe time */
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index 306b7d75b4a..0c786e00ebc 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -67,7 +67,7 @@ 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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -267,142 +267,6 @@ static const struct cmd spca504A_clicksmart420_open_data[] = {
{0x06, 0x0000, 0x0000},
{0x00, 0x0004, 0x2880},
{0x00, 0x0001, 0x2881},
-/* look like setting a qTable */
- {0x00, 0x0006, 0x2800},
- {0x00, 0x0004, 0x2801},
- {0x00, 0x0004, 0x2802},
- {0x00, 0x0006, 0x2803},
- {0x00, 0x000a, 0x2804},
- {0x00, 0x0010, 0x2805},
- {0x00, 0x0014, 0x2806},
- {0x00, 0x0018, 0x2807},
- {0x00, 0x0005, 0x2808},
- {0x00, 0x0005, 0x2809},
- {0x00, 0x0006, 0x280a},
- {0x00, 0x0008, 0x280b},
- {0x00, 0x000a, 0x280c},
- {0x00, 0x0017, 0x280d},
- {0x00, 0x0018, 0x280e},
- {0x00, 0x0016, 0x280f},
-
- {0x00, 0x0006, 0x2810},
- {0x00, 0x0005, 0x2811},
- {0x00, 0x0006, 0x2812},
- {0x00, 0x000a, 0x2813},
- {0x00, 0x0010, 0x2814},
- {0x00, 0x0017, 0x2815},
- {0x00, 0x001c, 0x2816},
- {0x00, 0x0016, 0x2817},
- {0x00, 0x0006, 0x2818},
- {0x00, 0x0007, 0x2819},
- {0x00, 0x0009, 0x281a},
- {0x00, 0x000c, 0x281b},
- {0x00, 0x0014, 0x281c},
- {0x00, 0x0023, 0x281d},
- {0x00, 0x0020, 0x281e},
- {0x00, 0x0019, 0x281f},
-
- {0x00, 0x0007, 0x2820},
- {0x00, 0x0009, 0x2821},
- {0x00, 0x000f, 0x2822},
- {0x00, 0x0016, 0x2823},
- {0x00, 0x001b, 0x2824},
- {0x00, 0x002c, 0x2825},
- {0x00, 0x0029, 0x2826},
- {0x00, 0x001f, 0x2827},
- {0x00, 0x000a, 0x2828},
- {0x00, 0x000e, 0x2829},
- {0x00, 0x0016, 0x282a},
- {0x00, 0x001a, 0x282b},
- {0x00, 0x0020, 0x282c},
- {0x00, 0x002a, 0x282d},
- {0x00, 0x002d, 0x282e},
- {0x00, 0x0025, 0x282f},
-
- {0x00, 0x0014, 0x2830},
- {0x00, 0x001a, 0x2831},
- {0x00, 0x001f, 0x2832},
- {0x00, 0x0023, 0x2833},
- {0x00, 0x0029, 0x2834},
- {0x00, 0x0030, 0x2835},
- {0x00, 0x0030, 0x2836},
- {0x00, 0x0028, 0x2837},
- {0x00, 0x001d, 0x2838},
- {0x00, 0x0025, 0x2839},
- {0x00, 0x0026, 0x283a},
- {0x00, 0x0027, 0x283b},
- {0x00, 0x002d, 0x283c},
- {0x00, 0x0028, 0x283d},
- {0x00, 0x0029, 0x283e},
- {0x00, 0x0028, 0x283f},
-
- {0x00, 0x0007, 0x2840},
- {0x00, 0x0007, 0x2841},
- {0x00, 0x000a, 0x2842},
- {0x00, 0x0013, 0x2843},
- {0x00, 0x0028, 0x2844},
- {0x00, 0x0028, 0x2845},
- {0x00, 0x0028, 0x2846},
- {0x00, 0x0028, 0x2847},
- {0x00, 0x0007, 0x2848},
- {0x00, 0x0008, 0x2849},
- {0x00, 0x000a, 0x284a},
- {0x00, 0x001a, 0x284b},
- {0x00, 0x0028, 0x284c},
- {0x00, 0x0028, 0x284d},
- {0x00, 0x0028, 0x284e},
- {0x00, 0x0028, 0x284f},
-
- {0x00, 0x000a, 0x2850},
- {0x00, 0x000a, 0x2851},
- {0x00, 0x0016, 0x2852},
- {0x00, 0x0028, 0x2853},
- {0x00, 0x0028, 0x2854},
- {0x00, 0x0028, 0x2855},
- {0x00, 0x0028, 0x2856},
- {0x00, 0x0028, 0x2857},
- {0x00, 0x0013, 0x2858},
- {0x00, 0x001a, 0x2859},
- {0x00, 0x0028, 0x285a},
- {0x00, 0x0028, 0x285b},
- {0x00, 0x0028, 0x285c},
- {0x00, 0x0028, 0x285d},
- {0x00, 0x0028, 0x285e},
- {0x00, 0x0028, 0x285f},
-
- {0x00, 0x0028, 0x2860},
- {0x00, 0x0028, 0x2861},
- {0x00, 0x0028, 0x2862},
- {0x00, 0x0028, 0x2863},
- {0x00, 0x0028, 0x2864},
- {0x00, 0x0028, 0x2865},
- {0x00, 0x0028, 0x2866},
- {0x00, 0x0028, 0x2867},
- {0x00, 0x0028, 0x2868},
- {0x00, 0x0028, 0x2869},
- {0x00, 0x0028, 0x286a},
- {0x00, 0x0028, 0x286b},
- {0x00, 0x0028, 0x286c},
- {0x00, 0x0028, 0x286d},
- {0x00, 0x0028, 0x286e},
- {0x00, 0x0028, 0x286f},
-
- {0x00, 0x0028, 0x2870},
- {0x00, 0x0028, 0x2871},
- {0x00, 0x0028, 0x2872},
- {0x00, 0x0028, 0x2873},
- {0x00, 0x0028, 0x2874},
- {0x00, 0x0028, 0x2875},
- {0x00, 0x0028, 0x2876},
- {0x00, 0x0028, 0x2877},
- {0x00, 0x0028, 0x2878},
- {0x00, 0x0028, 0x2879},
- {0x00, 0x0028, 0x287a},
- {0x00, 0x0028, 0x287b},
- {0x00, 0x0028, 0x287c},
- {0x00, 0x0028, 0x287d},
- {0x00, 0x0028, 0x287e},
- {0x00, 0x0028, 0x287f},
{0xa0, 0x0000, 0x0503},
};
@@ -622,6 +486,20 @@ static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
PDEBUG(D_FRAM, "after wait 0x%04x", notdone);
}
+static void spca504_read_info(struct gspca_dev *gspca_dev)
+{
+ int i;
+ u8 info[6];
+
+ for (i = 0; i < 6; i++)
+ info[i] = reg_r_1(gspca_dev, i);
+ PDEBUG(D_STREAM,
+ "Read info: %d %d %d %d %d %d."
+ " Should be 1,0,2,2,0,0",
+ info[0], info[1], info[2],
+ info[3], info[4], info[5]);
+}
+
static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
u8 req,
u16 idx, u16 val, u16 endcode, u8 count)
@@ -881,8 +759,6 @@ 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;
- int i;
- u8 info[6];
switch (sd->bridge) {
case BRIDGE_SPCA504B:
@@ -924,15 +800,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* case BRIDGE_SPCA504: */
PDEBUG(D_STREAM, "Opening SPCA504");
if (sd->subtype == AiptekMiniPenCam13) {
- /*****************************/
- for (i = 0; i < 6; i++)
- info[i] = reg_r_1(gspca_dev, i);
- PDEBUG(D_STREAM,
- "Read info: %d %d %d %d %d %d."
- " Should be 1,0,2,2,0,0",
- info[0], info[1], info[2],
- info[3], info[4], info[5]);
- /* spca504a aiptek */
+ spca504_read_info(gspca_dev);
+
/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
spca504A_acknowledged_command(gspca_dev, 0x24,
8, 3, 0x9e, 1);
@@ -971,8 +840,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int enable;
- int i;
- u8 info[6];
/* create the JPEG header */
sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
@@ -1008,14 +875,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
case BRIDGE_SPCA504:
if (sd->subtype == AiptekMiniPenCam13) {
- for (i = 0; i < 6; i++)
- info[i] = reg_r_1(gspca_dev, i);
- PDEBUG(D_STREAM,
- "Read info: %d %d %d %d %d %d."
- " Should be 1,0,2,2,0,0",
- info[0], info[1], info[2],
- info[3], info[4], info[5]);
- /* spca504a aiptek */
+ spca504_read_info(gspca_dev);
+
/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
spca504A_acknowledged_command(gspca_dev, 0x24,
8, 3, 0x9e, 1);
@@ -1026,13 +887,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
0, 0, 0x9d, 1);
} else {
spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
- for (i = 0; i < 6; i++)
- info[i] = reg_r_1(gspca_dev, i);
- PDEBUG(D_STREAM,
- "Read info: %d %d %d %d %d %d."
- " Should be 1,0,2,2,0,0",
- info[0], info[1], info[2],
- info[3], info[4], info[5]);
+ spca504_read_info(gspca_dev);
spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
}
@@ -1336,6 +1191,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},
{USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
{USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},
{USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
{USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
{USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 55ef6a74442..668a7536af9 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -52,6 +52,7 @@ struct sd {
#define SENSOR_OM6802 0
#define SENSOR_OTHER 1
#define SENSOR_TAS5130A 2
+#define SENSOR_LT168G 3 /* must verify if this is the actual model */
};
/* V4L2 controls supported by the driver */
@@ -78,7 +79,7 @@ static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu);
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -306,6 +307,17 @@ static const u8 n4_tas5130a[] = {
0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8,
0xc6, 0xda
};
+static const u8 n4_lt168g[] = {
+ 0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28,
+ 0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70,
+ 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3,
+ 0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20,
+ 0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68,
+ 0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40,
+ 0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0,
+ 0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c,
+ 0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80
+};
static const struct additional_sensor_data sensor_data[] = {
{ /* 0: OM6802 */
@@ -380,6 +392,23 @@ static const struct additional_sensor_data sensor_data[] = {
.stream =
{0x0b, 0x04, 0x0a, 0x40},
},
+ { /* 3: LT168G */
+ .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00},
+ .n4 = n4_lt168g,
+ .n4sz = sizeof n4_lt168g,
+ .reg80 = 0x7c,
+ .reg8e = 0xb3,
+ .nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00},
+ .data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40,
+ 0xb0, 0xf4},
+ .data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
+ 0xff},
+ .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
+ 0xff},
+ .data4 = {0x66, 0x41, 0xa8, 0xf0},
+ .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b},
+ .stream = {0x0b, 0x04, 0x0a, 0x28},
+ },
};
#define MAX_EFFECTS 7
@@ -716,6 +745,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "sensor tas5130a");
sd->sensor = SENSOR_TAS5130A;
break;
+ case 0x0802:
+ PDEBUG(D_PROBE, "sensor lt168g");
+ sd->sensor = SENSOR_LT168G;
+ break;
case 0x0803:
PDEBUG(D_PROBE, "sensor 'other'");
sd->sensor = SENSOR_OTHER;
@@ -758,6 +791,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3);
reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz);
+ if (sd->sensor == SENSOR_LT168G) {
+ test_byte = reg_r(gspca_dev, 0x80);
+ PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80,
+ test_byte);
+ reg_w(gspca_dev, 0x6c80);
+ }
+
reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
@@ -782,6 +822,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8);
reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+ if (sd->sensor == SENSOR_LT168G) {
+ test_byte = reg_r(gspca_dev, 0x80);
+ PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80,
+ test_byte);
+ reg_w(gspca_dev, 0x6c80);
+ }
+
reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
@@ -888,6 +935,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_OM6802:
om6802_sensor_init(gspca_dev);
break;
+ case SENSOR_LT168G:
+ break;
case SENSOR_OTHER:
break;
default:
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index b74a3b6489c..c7b6eb1e04d 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -39,7 +39,7 @@ struct sd {
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
{
{
.id = V4L2_CID_BRIGHTNESS,
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 71921c87842..4989f9afb46 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -32,10 +32,13 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+ u8 brightness;
+ u8 contrast;
+ u8 colors;
u8 hflip;
u8 vflip;
u8 lightfreq;
- u8 sharpness;
+ s8 sharpness;
u8 image_offset;
@@ -52,6 +55,7 @@ struct sd {
#define SENSOR_OV7670 6
#define SENSOR_PO1200 7
#define SENSOR_PO3130NC 8
+#define SENSOR_POxxxx 9
u8 flags;
#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */
#define FL_HFLIP 0x02 /* mirrored by default */
@@ -59,6 +63,12 @@ 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_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);
@@ -68,9 +78,54 @@ static int sd_getfreq(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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
+#define BRIGHTNESS_IDX 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 128
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define CONTRAST_IDX 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define CONTRAST_DEF 127
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define COLORS_IDX 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 1,
+ .maximum = 127,
+ .step = 1,
+#define COLOR_DEF 63
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
/* next 2 controls work with some sensors only */
-#define HFLIP_IDX 0
+#define HFLIP_IDX 3
{
{
.id = V4L2_CID_HFLIP,
@@ -85,7 +140,7 @@ static struct ctrl sd_ctrls[] = {
.set = sd_sethflip,
.get = sd_gethflip,
},
-#define VFLIP_IDX 1
+#define VFLIP_IDX 4
{
{
.id = V4L2_CID_VFLIP,
@@ -100,7 +155,7 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setvflip,
.get = sd_getvflip,
},
-#define LIGHTFREQ_IDX 2
+#define LIGHTFREQ_IDX 5
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -115,17 +170,16 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setfreq,
.get = sd_getfreq,
},
-/* po1200 only */
-#define SHARPNESS_IDX 3
+#define SHARPNESS_IDX 6
{
{
.id = V4L2_CID_SHARPNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Sharpness",
- .minimum = 0,
+ .minimum = -1,
.maximum = 2,
.step = 1,
-#define SHARPNESS_DEF 1
+#define SHARPNESS_DEF -1
.default_value = SHARPNESS_DEF,
},
.set = sd_setsharpness,
@@ -133,6 +187,42 @@ static struct ctrl sd_ctrls[] = {
},
};
+/* table of the disabled controls */
+static u32 ctrl_dis[] = {
+/* SENSOR_HV7131R 0 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
+ | (1 << SHARPNESS_IDX),
+/* SENSOR_MI0360 1 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
+ | (1 << SHARPNESS_IDX),
+/* SENSOR_MI1310_SOC 2 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+/* SENSOR_MI1320 3 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+/* SENSOR_MI1320_SOC 4 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+/* SENSOR_OV7660 5 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+/* SENSOR_OV7670 6 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << SHARPNESS_IDX),
+/* SENSOR_PO1200 7 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX),
+/* SENSOR_PO3130NC 8 */
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
+ | (1 << SHARPNESS_IDX),
+/* SENSOR_POxxxx 9 */
+ (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
+};
+
static const struct v4l2_pix_format vc0321_mode[] = {
{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -215,7 +305,7 @@ static const u8 mi0360_initVGA_JPG[][4] = {
{0xb3, 0x15, 0x00, 0xcc},
{0xb3, 0x16, 0x02, 0xcc},
{0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
{0xb3, 0x34, 0x02, 0xcc},
{0xb3, 0x00, 0x25, 0xcc},
{0xbc, 0x00, 0x71, 0xcc},
@@ -435,7 +525,7 @@ static const u8 mi1310_socinitVGA_JPG[][4] = {
{0xb3, 0x08, 0x01, 0xcc},
{0xb3, 0x09, 0x0c, 0xcc},
{0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
{0xb3, 0x02, 0x00, 0xcc},
{0xb3, 0x03, 0x0a, 0xcc},
{0xb3, 0x04, 0x05, 0xcc},
@@ -860,7 +950,8 @@ static const u8 mi1320_initVGA_data[][4] = {
{0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
{0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
{0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
+ {0xb3, 0x02, 0x00, 0xcc},
{0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
{0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
{0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc},
@@ -901,7 +992,8 @@ static const u8 mi1320_initVGA_data[][4] = {
{0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb},
{0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb},
{0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb},
- {0x08, 0x00, 0x27, 0xbb}, {0x20, 0x01, 0x03, 0xbb},
+ {0x08, 0x00, 0x27, 0xbb},
+ {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */
{0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb},
{0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
{0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb},
@@ -1012,7 +1104,7 @@ static const u8 mi1320_soc_InitVGA[][4] = {
{0xb3, 0x08, 0x01, 0xcc},
{0xb3, 0x09, 0x0c, 0xcc},
{0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
{0xb3, 0x02, 0x00, 0xcc},
{0xb3, 0x03, 0x0a, 0xcc},
{0xb3, 0x04, 0x05, 0xcc},
@@ -1359,7 +1451,8 @@ static const u8 po3130_initVGA_data[][4] = {
{0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc},
{0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
{0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
{0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc},
{0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
{0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
@@ -1561,7 +1654,7 @@ static const u8 hv7131r_initVGA_data[][4] = {
{0xb3, 0x16, 0x02, 0xcc},
{0xb3, 0x17, 0x7f, 0xcc},
{0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0x91, 0xcc},
+ {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */
{0xb3, 0x00, 0x27, 0xcc},
{0xbc, 0x00, 0x73, 0xcc},
{0xb8, 0x00, 0x23, 0xcc},
@@ -1747,7 +1840,8 @@ static const u8 ov7660_initVGA_data[][4] = {
{0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
{0xb3, 0x1f, 0x02, 0xcc},
{0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x00, 0x26, 0xcc},
{0xb8, 0x00, 0x33, 0xcc}, /* 13 */
{0xb8, 0x01, 0x7d, 0xcc},
{0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
@@ -1883,7 +1977,8 @@ static const u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0x00, 0x10, 0xdd},
{0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd},
{0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc},
- {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x34, 0x01, 0xcc},
{0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc},
{0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc},
{0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc},
@@ -2181,7 +2276,7 @@ static const u8 po1200_initVGA_data[][4] = {
{0xb0, 0x54, 0x13, 0xcc},
{0xb3, 0x00, 0x67, 0xcc},
{0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xdc, 0xcc},
+ {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */
{0x00, 0x03, 0x00, 0xaa},
{0x00, 0x12, 0x05, 0xaa},
{0x00, 0x13, 0x02, 0xaa},
@@ -2408,6 +2503,251 @@ static const u8 po1200_initVGA_data[][4] = {
{0x00, 0xb6, 0x39, 0xaa},
{0x00, 0xb7, 0x24, 0xaa},
/*write 89 0400 1415*/
+ {}
+};
+
+static const u8 poxxxx_init_common[][4] = {
+ {0xb3, 0x00, 0x04, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x03, 0x09, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x04, 0xcc},
+ {0xb3, 0x23, 0x00, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x2c, 0x03, 0xcc},
+ {0xb3, 0x2d, 0x56, 0xcc},
+ {0xb3, 0x2e, 0x02, 0xcc},
+ {0xb3, 0x2f, 0x0a, 0xcc},
+ {0xb3, 0x40, 0x00, 0xcc},
+ {0xb3, 0x41, 0x34, 0xcc},
+ {0xb3, 0x42, 0x01, 0xcc},
+ {0xb3, 0x43, 0xe0, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb3, 0x4d, 0x00, 0xcc},
+ {0x00, 0x0b, 0x2a, 0xaa},
+ {0x00, 0x0e, 0x03, 0xaa},
+ {0x00, 0x0f, 0xea, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x31, 0x1f, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa},
+ {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa},
+ {0x00, 0x4d, 0x2e, 0xaa},
+ {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa},
+ {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x56, 0x0a, 0xaa},
+ {0x00, 0x57, 0x07, 0xaa},
+ {0x00, 0x58, 0x07, 0xaa},
+ {0x00, 0x59, 0x04, 0xaa},
+ {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x71, 0x04, 0xaa},
+ {0x00, 0x72, 0x10, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa},
+ {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa},
+ {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa},
+ {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x8b, 0x25, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x8d, 0x86, 0xaa},
+ {0x00, 0x8e, 0x82, 0xaa},
+ {0x00, 0x8f, 0x2d, 0xaa},
+ {0x00, 0x90, 0x8b, 0xaa},
+ {0x00, 0x91, 0x81, 0xaa},
+ {0x00, 0x92, 0x81, 0xaa},
+ {0x00, 0x93, 0x23, 0xaa},
+ {0x00, 0xa3, 0x2a, 0xaa},
+ {0x00, 0xa4, 0x03, 0xaa},
+ {0x00, 0xa5, 0xea, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa},
+ {0x00, 0xbe, 0x3b, 0xaa},
+ {0x00, 0x4e, 0x40, 0xaa},
+ {0x00, 0x06, 0x04, 0xaa},
+ {0x00, 0x07, 0x03, 0xaa},
+ {0x00, 0xcd, 0x18, 0xaa},
+ {0x00, 0x28, 0x03, 0xaa},
+ {0x00, 0x29, 0xef, 0xaa},
+/* reinit on alt 2 (qvga) or alt7 (vga) */
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb8, 0x00, 0x01, 0xcc},
+
+ {0x00, 0x1d, 0x85, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa},
+ {0x00, 0x00, 0x40, 0xdd},
+ {0x00, 0x1d, 0x05, 0xaa},
+
+ {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xaa, 0xff, 0xaa}, /* back light comp */
+ {0x00, 0xc4, 0x03, 0xaa},
+ {0x00, 0xc5, 0x19, 0xaa},
+ {0x00, 0xc6, 0x03, 0xaa},
+ {0x00, 0xc7, 0x91, 0xaa},
+ {0x00, 0xc8, 0x01, 0xaa},
+ {0x00, 0xc9, 0xdd, 0xaa},
+ {0x00, 0xca, 0x02, 0xaa},
+ {0x00, 0xcb, 0x37, 0xaa},
+
+/* read d1 */
+ {0x00, 0xd1, 0x3c, 0xaa},
+ {0x00, 0xb8, 0x28, 0xaa},
+ {0x00, 0xb9, 0x1e, 0xaa},
+ {0x00, 0xb6, 0x14, 0xaa},
+ {0x00, 0xb7, 0x0f, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa},
+ {0x00, 0x5d, 0x18, 0xaa},
+ {0x00, 0x5e, 0x24, 0xaa},
+ {0x00, 0x5f, 0x24, 0xaa},
+ {0x00, 0x86, 0x1a, 0xaa},
+ {0x00, 0x60, 0x00, 0xaa},
+ {0x00, 0x61, 0x1b, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x40, 0xaa},
+ {0x00, 0x87, 0x1a, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x08, 0xaa},
+ {0x00, 0x66, 0x10, 0xaa},
+ {0x00, 0x67, 0x20, 0xaa},
+ {0x00, 0x88, 0x10, 0xaa},
+ {0x00, 0x68, 0x00, 0xaa},
+ {0x00, 0x69, 0x08, 0xaa},
+ {0x00, 0x6a, 0x0f, 0xaa},
+ {0x00, 0x6b, 0x0f, 0xaa},
+ {0x00, 0x89, 0x07, 0xaa},
+ {0x00, 0xd5, 0x4c, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x2a, 0xaa},
+ {0x00, 0x0e, 0x03, 0xaa},
+ {0x00, 0x0f, 0xea, 0xaa},
+ {0x00, 0xa2, 0x00, 0xaa},
+ {0x00, 0xa3, 0x2a, 0xaa},
+ {0x00, 0xa4, 0x03, 0xaa},
+ {0x00, 0xa5, 0xea, 0xaa},
+ {}
+};
+static const u8 poxxxx_initVGA[][4] = {
+ {0x00, 0x20, 0x11, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0xbb, 0x0d, 0xaa},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0x00, 0x04, 0x06, 0xaa},
+ {0x00, 0x05, 0x3f, 0xaa},
+ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
+ {}
+};
+static const u8 poxxxx_initQVGA[][4] = {
+ {0x00, 0x20, 0x33, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0xbb, 0x0d, 0xaa},
+ {0xb3, 0x22, 0x00, 0xcc},
+ {0xb3, 0x23, 0xf0, 0xcc},
+ {0xb3, 0x16, 0x01, 0xcc},
+ {0xb3, 0x17, 0x3f, 0xcc},
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x00, 0xcc},
+ {0x00, 0x04, 0x06, 0xaa},
+ {0x00, 0x05, 0x3f, 0xaa},
+ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
+ {}
+};
+static const u8 poxxxx_init_end_1[][4] = {
+ {0x00, 0x47, 0x25, 0xaa},
+ {0x00, 0x48, 0x80, 0xaa},
+ {0x00, 0x49, 0x1f, 0xaa},
+ {0x00, 0x4a, 0x40, 0xaa},
+ {0x00, 0x44, 0x40, 0xaa},
+ {0x00, 0xab, 0x4a, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb2, 0x04, 0xaa},
+ {0x00, 0xb3, 0x08, 0xaa},
+ {0x00, 0xb4, 0x0b, 0xaa},
+ {0x00, 0xb5, 0x0d, 0xaa},
+ {0x00, 0x59, 0x7e, 0xaa}, /* sharpness */
+ {0x00, 0x16, 0x00, 0xaa}, /* white balance */
+ {0x00, 0x18, 0x00, 0xaa},
+ {}
+};
+static const u8 poxxxx_init_end_2[][4] = {
+ {0x00, 0x1d, 0x85, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x1d, 0x05, 0xaa},
+ {}
};
struct sensor_info {
@@ -2420,33 +2760,89 @@ struct sensor_info {
u8 op;
};
-static const struct sensor_info sensor_info_data[] = {
-/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* probe values */
+static const struct sensor_info vc0321_probe_data[] = {
+/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* 0 OV9640 */
{-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
{-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
-/* (tested in vc032x_probe_sensor) */
-/* {-1, 0x80 | 0x20, 0x83, 0x0000, 0x24, 0x25, 0x01}, */
- {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
{SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
-/* (tested in vc032x_probe_sensor) */
+/* 12 OV9650 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 PO3030K */
+ {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 17 PO2030 */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+ {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
+};
+static const struct sensor_info vc0323_probe_data[] = {
+/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* 0 OV9640 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
{SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
{-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
{-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
{SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
-/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, */
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
{-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, */
-/* {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, */
+/* 11 MI1310_SOC */
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
{-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
{SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 ?? */
{-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
+/* 17 PO2030 */
{-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
{-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
{SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
-/*fixme: previously detected?*/
- {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
-/*fixme: not in the ms-win probe - may be found before?*/
+/*fixme: not in the ms-win probe - may be found before? */
{SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
};
@@ -2520,7 +2916,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- int i;
+ int i, n;
u16 value;
const struct sensor_info *ptsensor_info;
@@ -2531,9 +2927,16 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
}
reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
- PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]);
- for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) {
- ptsensor_info = &sensor_info_data[i];
+ PDEBUG(D_PROBE, "vc032%d check sensor header %02x",
+ sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]);
+ if (sd->bridge == BRIDGE_VC0321) {
+ ptsensor_info = vc0321_probe_data;
+ n = ARRAY_SIZE(vc0321_probe_data);
+ } else {
+ ptsensor_info = vc0323_probe_data;
+ n = ARRAY_SIZE(vc0323_probe_data);
+ }
+ for (i = 0; i < n; i++) {
reg_w(dev, 0xa0, 0x02, 0xb334);
reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300);
reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300);
@@ -2551,13 +2954,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
return ptsensor_info->sensorId;
switch (value) {
+ case 0x3130:
+ return SENSOR_PO3130NC;
case 0x7673:
return SENSOR_OV7670;
case 0x8243:
return SENSOR_MI0360;
}
-/*fixme: should return here*/
}
+ ptsensor_info++;
}
return -1;
}
@@ -2619,7 +3024,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
i2c_write(gspca_dev, data[i][0], &data[i][1], 2);
break;
case 0xdd:
- msleep(data[i][2] + 10);
+ msleep(data[i][1] * 256 + data[i][2] + 10);
break;
}
i++;
@@ -2646,12 +3051,17 @@ static int sd_config(struct gspca_dev *gspca_dev,
64, /* OV7670 6 */
128, /* PO1200 7 */
128, /* PO3130NC 8 */
+ 128, /* POxxxx 9 */
};
cam = &gspca_dev->cam;
sd->bridge = id->driver_info >> 8;
sd->flags = id->driver_info & 0xff;
- sensor = vc032x_probe_sensor(gspca_dev);
+ if (id->idVendor == 0x046d &&
+ (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+ sensor = SENSOR_POxxxx;
+ else
+ sensor = vc032x_probe_sensor(gspca_dev);
switch (sensor) {
case -1:
PDEBUG(D_PROBE, "Unknown sensor...");
@@ -2684,6 +3094,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
case SENSOR_PO3130NC:
PDEBUG(D_PROBE, "Find Sensor PO3130NC");
break;
+ case SENSOR_POxxxx:
+ PDEBUG(D_PROBE, "Sensor POxxxx");
+ break;
}
sd->sensor = sensor;
@@ -2712,28 +3125,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
cam->npkt = npkt[sd->sensor];
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
sd->hflip = HFLIP_DEF;
sd->vflip = VFLIP_DEF;
- if (sd->sensor == SENSOR_OV7670)
- sd->flags |= FL_HFLIP | FL_VFLIP;
sd->lightfreq = FREQ_DEF;
- if (sd->sensor != SENSOR_OV7670)
- gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
- switch (sd->sensor) {
- case SENSOR_MI1310_SOC:
- case SENSOR_MI1320_SOC:
- case SENSOR_OV7660:
- case SENSOR_OV7670:
- case SENSOR_PO1200:
- break;
- default:
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX)
- | (1 << VFLIP_IDX);
- break;
- }
-
sd->sharpness = SHARPNESS_DEF;
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+
+ if (sd->sensor == SENSOR_OV7670)
+ sd->flags |= FL_HFLIP | FL_VFLIP;
+
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
reg_w(dev, 0x87, 0x00, 0x0f0f);
@@ -2747,10 +3151,55 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_POxxxx) {
+ reg_r(gspca_dev, 0xa1, 0xb300, 1);
+ if (gspca_dev->usb_buf[0] != 0) {
+ reg_w(gspca_dev->dev, 0xa0, 0x26, 0xb300);
+ reg_w(gspca_dev->dev, 0xa0, 0x04, 0xb300);
+ reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb300);
+ }
+ }
return 0;
}
-/* some sensors only */
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
+ return;
+ data = sd->brightness;
+ if (data >= 0x80)
+ data &= 0x7f;
+ else
+ data = 0xff ^ data;
+ i2c_write(gspca_dev, 0x98, &data, 1);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
+ return;
+ i2c_write(gspca_dev, 0x99, &sd->contrast, 1);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << COLORS_IDX))
+ return;
+ data = sd->colors - (sd->colors >> 3) - 1;
+ i2c_write(gspca_dev, 0x94, &data, 1);
+ i2c_write(gspca_dev, 0x95, &sd->colors, 1);
+}
+
static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2764,6 +3213,7 @@ static void sethvflip(struct gspca_dev *gspca_dev)
vflip = !vflip;
switch (sd->sensor) {
case SENSOR_MI1310_SOC:
+ case SENSOR_MI1320:
case SENSOR_MI1320_SOC:
data[0] = data[1] = 0; /* select page 0 */
i2c_write(gspca_dev, 0xf0, data, 2);
@@ -2801,18 +3251,29 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
}
-/* po1200 only */
static void setsharpness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 data;
- if (sd->sensor != SENSOR_PO1200)
- return;
- data = 0;
- i2c_write(gspca_dev, 0x03, &data, 1);
- data = 0xb5 + sd->sharpness * 3;
- i2c_write(gspca_dev, 0x61, &data, 1);
+ switch (sd->sensor) {
+ case SENSOR_PO1200:
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ if (sd->sharpness < 0)
+ data = 0x6a;
+ else
+ data = 0xb5 + sd->sharpness * 3;
+ i2c_write(gspca_dev, 0x61, &data, 1);
+ break;
+ case SENSOR_POxxxx:
+ if (sd->sharpness < 0)
+ data = 0x7e; /* def = max */
+ else
+ data = 0x60 + sd->sharpness * 0x0f;
+ i2c_write(gspca_dev, 0x59, &data, 1);
+ break;
+ }
}
static int sd_start(struct gspca_dev *gspca_dev)
@@ -2922,12 +3383,27 @@ static int sd_start(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, init);
init = po3130_rundata;
break;
- default:
-/* case SENSOR_PO1200: */
+ case SENSOR_PO1200:
GammaT = po1200_gamma;
MatrixT = po1200_matrix;
init = po1200_initVGA_data;
break;
+ default:
+/* case SENSOR_POxxxx: */
+ usb_exchange(gspca_dev, poxxxx_init_common);
+ if (mode)
+ init = poxxxx_initQVGA;
+ else
+ init = poxxxx_initVGA;
+ usb_exchange(gspca_dev, init);
+ reg_r(gspca_dev, 0x8c, 0x0000, 3);
+ reg_w(gspca_dev->dev, 0xa0,
+ gspca_dev->usb_buf[2] & 1 ? 0 : 1,
+ 0xb35c);
+ msleep(300);
+/*fixme: i2c read 04 and 05*/
+ init = poxxxx_init_end_1;
+ break;
}
usb_exchange(gspca_dev, init);
if (GammaT && MatrixT) {
@@ -2936,7 +3412,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
- /* set the led on 0x0892 0x0896 */
switch (sd->sensor) {
case SENSOR_PO1200:
case SENSOR_HV7131R:
@@ -2945,16 +3420,22 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_MI1310_SOC:
reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000);
break;
- default:
- if (!(sd->flags & FL_SAMSUNG))
- reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
- break;
}
msleep(100);
setsharpness(gspca_dev);
sethvflip(gspca_dev);
setlightfreq(gspca_dev);
}
+ if (sd->sensor == SENSOR_POxxxx) {
+ setcolors(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+
+ /* led on */
+ msleep(80);
+ reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+ usb_exchange(gspca_dev, poxxxx_init_end_2);
+ }
return 0;
}
@@ -2963,10 +3444,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
struct usb_device *dev = gspca_dev->dev;
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->sensor == SENSOR_MI1310_SOC)
+ switch (sd->sensor) {
+ case SENSOR_MI1310_SOC:
reg_w(dev, 0x89, 0x058c, 0x00ff);
- else if (!(sd->flags & FL_SAMSUNG))
- reg_w(dev, 0x89, 0xffff, 0xffff);
+ break;
+ case SENSOR_POxxxx:
+ return;
+ default:
+ if (!(sd->flags & FL_SAMSUNG))
+ reg_w(dev, 0x89, 0xffff, 0xffff);
+ break;
+ }
reg_w(dev, 0xa0, 0x01, 0xb301);
reg_w(dev, 0xa0, 0x09, 0xb003);
}
@@ -2984,6 +3472,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
reg_w(dev, 0x89, 0x058c, 0x00ff);
else if (!(sd->flags & FL_SAMSUNG))
reg_w(dev, 0x89, 0xffff, 0xffff);
+
+ if (sd->sensor == SENSOR_POxxxx) {
+ reg_w(dev, 0xa0, 0x26, 0xb300);
+ reg_w(dev, 0xa0, 0x04, 0xb300);
+ reg_w(dev, 0xa0, 0x00, 0xb300);
+ }
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -3020,6 +3514,60 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
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)
+ setbrightness(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)
+ setcontrast(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_sethflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 1a800fc1c00..50986da3d91 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -1,9 +1,8 @@
/*
- * Z-Star/Vimicro zc301/zc302p/vc30x library
- * Copyright (C) 2004 2005 2006 Michel Xhaard
- * mxhaard@magic.fr
+ * Z-Star/Vimicro zc301/zc302p/vc30x library
*
- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2009-2010 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2004 2005 2006 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
@@ -22,10 +21,11 @@
#define MODULE_NAME "zc3xx"
+#include <linux/input.h>
#include "gspca.h"
#include "jpeg.h"
-MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, "
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
"Serge A. Suchkov <Serge.A.S@tochka.ru>");
MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
MODULE_LICENSE("GPL");
@@ -39,18 +39,18 @@ static int force_sensor = -1;
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u8 brightness;
- __u8 contrast;
- __u8 gamma;
- __u8 autogain;
- __u8 lightfreq;
- __u8 sharpness;
+ u8 brightness;
+ u8 contrast;
+ u8 gamma;
+ u8 autogain;
+ u8 lightfreq;
+ u8 sharpness;
u8 quality; /* image quality */
#define QUALITY_MIN 40
#define QUALITY_MAX 60
#define QUALITY_DEF 50
- signed char sensor; /* Type of image sensor chip */
+ u8 sensor; /* Type of image sensor chip */
/* !! values used in different tables */
#define SENSOR_ADCM2700 0
#define SENSOR_CS2102 1
@@ -92,9 +92,8 @@ static int sd_getfreq(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 struct ctrl sd_ctrls[] = {
+static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_IDX 0
-#define SD_BRIGHTNESS 0
{
{
.id = V4L2_CID_BRIGHTNESS,
@@ -103,26 +102,26 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
- .default_value = 128,
+#define BRIGHTNESS_DEF 128
+ .default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
},
-#define SD_CONTRAST 1
{
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
- .maximum = 256,
+ .maximum = 255,
.step = 1,
- .default_value = 128,
+#define CONTRAST_DEF 128
+ .default_value = CONTRAST_DEF,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
},
-#define SD_GAMMA 2
{
{
.id = V4L2_CID_GAMMA,
@@ -136,7 +135,6 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setgamma,
.get = sd_getgamma,
},
-#define SD_AUTOGAIN 3
{
{
.id = V4L2_CID_AUTOGAIN,
@@ -145,13 +143,13 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
- .default_value = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
},
.set = sd_setautogain,
.get = sd_getautogain,
},
#define LIGHTFREQ_IDX 4
-#define SD_FREQ 4
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -160,12 +158,12 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
.step = 1,
- .default_value = 1,
+#define FREQ_DEF 0
+ .default_value = FREQ_DEF,
},
.set = sd_setfreq,
.get = sd_getfreq,
},
-#define SD_SHARPNESS 5
{
{
.id = V4L2_CID_SHARPNESS,
@@ -174,7 +172,8 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 3,
.step = 1,
- .default_value = 2,
+#define SHARPNESS_DEF 2
+ .default_value = SHARPNESS_DEF,
},
.set = sd_setsharpness,
.get = sd_getsharpness,
@@ -194,6 +193,19 @@ static const struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
+static const struct v4l2_pix_format broken_vga_mode[] = {
+ {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 232 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 472 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
@@ -209,9 +221,9 @@ static const struct v4l2_pix_format sif_mode[] = {
/* usb exchanges */
struct usb_action {
- __u8 req;
- __u8 val;
- __u16 idx;
+ u8 req;
+ u8 val;
+ u16 idx;
};
static const struct usb_action adcm2700_Initial[] = {
@@ -421,7 +433,7 @@ static const struct usb_action adcm2700_NoFliker[] = {
{0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
{}
};
-static const struct usb_action cs2102_Initial[] = { /* 320x240 */
+static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -473,7 +485,7 @@ static const struct usb_action cs2102_Initial[] = { /* 320x240 */
{}
};
-static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */
+static const struct usb_action cs2102_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -524,7 +536,7 @@ static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */
{0xa0, 0x00, 0x01ad},
{}
};
-static const struct usb_action cs2102_50HZ[] = {
+static const struct usb_action cs2102_50HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0001},
{0xaa, 0x24, 0x005f},
@@ -546,7 +558,7 @@ static const struct usb_action cs2102_50HZ[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_50HZScale[] = {
+static const struct usb_action cs2102_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0000},
{0xaa, 0x24, 0x00af},
@@ -568,7 +580,7 @@ static const struct usb_action cs2102_50HZScale[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_60HZ[] = {
+static const struct usb_action cs2102_60HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0001},
{0xaa, 0x24, 0x0055},
@@ -590,7 +602,7 @@ static const struct usb_action cs2102_60HZ[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_60HZScale[] = {
+static const struct usb_action cs2102_60HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0000},
{0xaa, 0x24, 0x00aa},
@@ -612,7 +624,7 @@ static const struct usb_action cs2102_60HZScale[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_NoFliker[] = {
+static const struct usb_action cs2102_NoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0001},
{0xaa, 0x24, 0x005f},
@@ -634,7 +646,7 @@ static const struct usb_action cs2102_NoFliker[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
-static const struct usb_action cs2102_NoFlikerScale[] = {
+static const struct usb_action cs2102_NoFliker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0000},
{0xaa, 0x24, 0x00af},
@@ -658,7 +670,7 @@ static const struct usb_action cs2102_NoFlikerScale[] = {
};
/* CS2102_KOCOM */
-static const struct usb_action cs2102K_Initial[] = {
+static const struct usb_action cs2102K_InitialScale[] = {
{0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
@@ -917,7 +929,7 @@ static const struct usb_action cs2102K_Initial[] = {
{}
};
-static const struct usb_action cs2102K_InitialScale[] = {
+static const struct usb_action cs2102K_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -1495,7 +1507,7 @@ static const struct usb_action gc0305_NoFliker[] = {
{}
};
-static const struct usb_action hdcs2020xb_Initial[] = {
+static const struct usb_action hdcs2020b_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */
@@ -1627,7 +1639,7 @@ static const struct usb_action hdcs2020xb_Initial[] = {
{0xa0, 0x40, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action hdcs2020xb_InitialScale[] = {
+static const struct usb_action hdcs2020b_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -1819,7 +1831,7 @@ static const struct usb_action hdcs2020b_NoFliker[] = {
{}
};
-static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */
+static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -1866,7 +1878,7 @@ static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */
{}
};
-static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/
+static const struct usb_action hv7131b_Initial[] = { /* 640x480*/
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -2063,7 +2075,7 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
{}
};
-static const struct usb_action hv7131cxx_Initial[] = {
+static const struct usb_action hv7131r_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
@@ -2157,7 +2169,7 @@ static const struct usb_action hv7131cxx_Initial[] = {
{}
};
-static const struct usb_action hv7131cxx_InitialScale[] = {
+static const struct usb_action hv7131r_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */
@@ -2259,7 +2271,7 @@ static const struct usb_action hv7131cxx_InitialScale[] = {
{}
};
-static const struct usb_action icm105axx_Initial[] = {
+static const struct usb_action icm105a_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -2436,7 +2448,7 @@ static const struct usb_action icm105axx_Initial[] = {
{}
};
-static const struct usb_action icm105axx_InitialScale[] = {
+static const struct usb_action icm105a_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -2615,7 +2627,7 @@ static const struct usb_action icm105axx_InitialScale[] = {
{0xa0, 0x40, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action icm105a_50HZ[] = {
+static const struct usb_action icm105a_50HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */
@@ -2646,7 +2658,7 @@ static const struct usb_action icm105a_50HZ[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{}
};
-static const struct usb_action icm105a_50HZScale[] = {
+static const struct usb_action icm105a_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */
@@ -2679,7 +2691,7 @@ static const struct usb_action icm105a_50HZScale[] = {
{0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
{}
};
-static const struct usb_action icm105a_60HZ[] = {
+static const struct usb_action icm105a_60HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
@@ -2710,7 +2722,7 @@ static const struct usb_action icm105a_60HZ[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{}
};
-static const struct usb_action icm105a_60HZScale[] = {
+static const struct usb_action icm105a_60HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
@@ -2743,7 +2755,7 @@ static const struct usb_action icm105a_60HZScale[] = {
{0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
{}
};
-static const struct usb_action icm105a_NoFliker[] = {
+static const struct usb_action icm105a_NoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
@@ -2774,7 +2786,7 @@ static const struct usb_action icm105a_NoFliker[] = {
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{}
};
-static const struct usb_action icm105a_NoFlikerScale[] = {
+static const struct usb_action icm105a_NoFliker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
{0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
@@ -2808,7 +2820,7 @@ static const struct usb_action icm105a_NoFlikerScale[] = {
{}
};
-static const struct usb_action MC501CB_InitialScale[] = {
+static const struct usb_action mc501cb_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
@@ -2928,7 +2940,7 @@ static const struct usb_action MC501CB_InitialScale[] = {
{}
};
-static const struct usb_action MC501CB_Initial[] = { /* 320x240 */
+static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
@@ -3047,7 +3059,7 @@ static const struct usb_action MC501CB_Initial[] = { /* 320x240 */
{}
};
-static const struct usb_action MC501CB_50HZ[] = {
+static const struct usb_action mc501cb_50HZScale[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
@@ -3064,7 +3076,7 @@ static const struct usb_action MC501CB_50HZ[] = {
{}
};
-static const struct usb_action MC501CB_50HZScale[] = {
+static const struct usb_action mc501cb_50HZ[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
@@ -3081,7 +3093,7 @@ static const struct usb_action MC501CB_50HZScale[] = {
{}
};
-static const struct usb_action MC501CB_60HZ[] = {
+static const struct usb_action mc501cb_60HZScale[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
@@ -3098,7 +3110,7 @@ static const struct usb_action MC501CB_60HZ[] = {
{}
};
-static const struct usb_action MC501CB_60HZScale[] = {
+static const struct usb_action mc501cb_60HZ[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
@@ -3115,7 +3127,7 @@ static const struct usb_action MC501CB_60HZScale[] = {
{}
};
-static const struct usb_action MC501CB_NoFliker[] = {
+static const struct usb_action mc501cb_NoFlikerScale[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
@@ -3132,7 +3144,7 @@ static const struct usb_action MC501CB_NoFliker[] = {
{}
};
-static const struct usb_action MC501CB_NoFlikerScale[] = {
+static const struct usb_action mc501cb_NoFliker[] = {
{0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
{0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
{0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
@@ -3144,8 +3156,8 @@ static const struct usb_action MC501CB_NoFlikerScale[] = {
{}
};
-/* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */
-static const struct usb_action OV7620_mode0[] = {
+/* from zs211.inf */
+static const struct usb_action ov7620_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */
{0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
@@ -3214,9 +3226,7 @@ static const struct usb_action OV7620_mode0[] = {
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
{}
};
-
-/* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */
-static const struct usb_action OV7620_mode1[] = {
+static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
{0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
@@ -3287,9 +3297,7 @@ static const struct usb_action OV7620_mode1[] = {
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
{}
};
-
-/* from zs211.inf - HKR,%OV7620%\AE,50HZ */
-static const struct usb_action OV7620_50HZ[] = {
+static const struct usb_action ov7620_50HZ[] = {
{0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
{0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
@@ -3307,9 +3315,7 @@ static const struct usb_action OV7620_50HZ[] = {
if mode0 (640x480) */
{}
};
-
-/* from zs211.inf - HKR,%OV7620%\AE,60HZ */
-static const struct usb_action OV7620_60HZ[] = {
+static const struct usb_action ov7620_60HZ[] = {
{0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
/* (bug in zs211.inf) */
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
@@ -3331,9 +3337,7 @@ static const struct usb_action OV7620_60HZ[] = {
{0xa1, 0x01, 0x0037}, */
{}
};
-
-/* from zs211.inf - HKR,%OV7620%\AE,NoFliker */
-static const struct usb_action OV7620_NoFliker[] = {
+static const struct usb_action ov7620_NoFliker[] = {
{0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
/* (bug in zs211.inf) */
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
@@ -3354,7 +3358,7 @@ static const struct usb_action OV7620_NoFliker[] = {
{}
};
-static const struct usb_action ov7630c_Initial[] = {
+static const struct usb_action ov7630c_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
@@ -3511,7 +3515,7 @@ static const struct usb_action ov7630c_Initial[] = {
{}
};
-static const struct usb_action ov7630c_InitialScale[] = {
+static const struct usb_action ov7630c_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -3682,7 +3686,7 @@ static const struct usb_action pas106b_Initial_com[] = {
{}
};
-static const struct usb_action pas106b_Initial[] = { /* 176x144 */
+static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
/* JPEG control */
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
/* Sream and Sensor specific */
@@ -3800,7 +3804,7 @@ static const struct usb_action pas106b_Initial[] = { /* 176x144 */
{}
};
-static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */
+static const struct usb_action pas106b_Initial[] = { /* 352x288 */
/* JPEG control */
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
/* Sream and Sensor specific */
@@ -3972,10 +3976,10 @@ static const struct usb_action pas106b_NoFliker[] = {
{}
};
-/* from usbvm31b.inf */
+/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */
static const struct usb_action pas202b_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
@@ -4000,7 +4004,7 @@ static const struct usb_action pas202b_Initial[] = { /* 640x480 */
{0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
{0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
{0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
- {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0c, 0x0006},
{0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
{0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
{0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
@@ -4019,13 +4023,13 @@ static const struct usb_action pas202b_Initial[] = { /* 640x480 */
};
static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
{0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
{0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
@@ -4035,7 +4039,7 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
{0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
{0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
{0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
{0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
{0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
{0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
@@ -4044,7 +4048,7 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
{0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
{0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
{0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
- {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0c, 0x0006},
{0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
{0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
{0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
@@ -4059,6 +4063,8 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
{0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW},
{}
};
static const struct usb_action pas202b_50HZ[] = {
@@ -4066,22 +4072,22 @@ static const struct usb_action pas202b_50HZ[] = {
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
{0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */
+ {0xaa, 0x21, 0x001b},
{0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
- {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
- {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */
+ {0xaa, 0x04, 0x0008},
+ {0xaa, 0x05, 0x001b},
{0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
{0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
{0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
{0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
@@ -4094,23 +4100,23 @@ static const struct usb_action pas202b_50HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x003d},
{0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
- {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
- {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x003d},
{0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
{0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
- {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
{0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
{0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
@@ -4130,16 +4136,16 @@ static const struct usb_action pas202b_60HZ[] = {
{0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
{0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
{0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
{0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
{0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
@@ -4152,23 +4158,23 @@ static const struct usb_action pas202b_60HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x0008},
{0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
- {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
- {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x0008},
{0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
{0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
{0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
- {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
{0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
{0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
@@ -4182,22 +4188,22 @@ static const struct usb_action pas202b_NoFliker[] = {
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
{0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */
+ {0xaa, 0x21, 0x0006},
{0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
{0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
- {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */
+ {0xaa, 0x05, 0x0006},
{0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
{0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
{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, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
{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, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
{0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
{0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
@@ -4210,23 +4216,23 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
{0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x000c},
{0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
- {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
- {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x000c},
{0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
{0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,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, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
{0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
{0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
@@ -4713,8 +4719,8 @@ static const struct usb_action pb0330_NoFlikerScale[] = {
{}
};
-/* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */
-static const struct usb_action PO2030_mode0[] = {
+/* from oem9.inf */
+static const struct usb_action po2030_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
{0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
@@ -4790,8 +4796,8 @@ static const struct usb_action PO2030_mode0[] = {
{}
};
-/* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */
-static const struct usb_action PO2030_mode1[] = {
+/* from oem9.inf */
+static const struct usb_action po2030_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
@@ -4867,7 +4873,7 @@ static const struct usb_action PO2030_mode1[] = {
{}
};
-static const struct usb_action PO2030_50HZ[] = {
+static const struct usb_action po2030_50HZ[] = {
{0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
{0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
{0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
@@ -4889,7 +4895,7 @@ static const struct usb_action PO2030_50HZ[] = {
{}
};
-static const struct usb_action PO2030_60HZ[] = {
+static const struct usb_action po2030_60HZ[] = {
{0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
{0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
{0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
@@ -4912,7 +4918,7 @@ static const struct usb_action PO2030_60HZ[] = {
{}
};
-static const struct usb_action PO2030_NoFliker[] = {
+static const struct usb_action po2030_NoFliker[] = {
{0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
{0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
{0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
@@ -4924,7 +4930,7 @@ static const struct usb_action PO2030_NoFliker[] = {
};
/* TEST */
-static const struct usb_action tas5130CK_Initial[] = {
+static const struct usb_action tas5130cK_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x01, 0x003b},
{0xa0, 0x0e, 0x003a},
@@ -5127,7 +5133,7 @@ static const struct usb_action tas5130CK_Initial[] = {
{}
};
-static const struct usb_action tas5130CK_InitialScale[] = {
+static const struct usb_action tas5130cK_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x01, 0x003b},
{0xa0, 0x0e, 0x003a},
@@ -5560,7 +5566,7 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
{}
};
-static const struct usb_action tas5130c_vf0250_Initial[] = {
+static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
{0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
@@ -5627,7 +5633,7 @@ static const struct usb_action tas5130c_vf0250_Initial[] = {
{}
};
-static const struct usb_action tas5130c_vf0250_InitialScale[] = {
+static const struct usb_action tas5130c_vf0250_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
{0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
@@ -5692,8 +5698,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
{}
};
-/* "50HZ" light frequency banding filter */
-static const struct usb_action tas5130c_vf0250_50HZ[] = {
+static const struct usb_action tas5130c_vf0250_50HZScale[] = {
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
{0xaa, 0x84, 0x00aa}, /* 00,84,aa,aa */
@@ -5717,8 +5722,7 @@ static const struct usb_action tas5130c_vf0250_50HZ[] = {
{}
};
-/* "50HZScale" light frequency banding filter */
-static const struct usb_action tas5130c_vf0250_50HZScale[] = {
+static const struct usb_action tas5130c_vf0250_50HZ[] = {
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0003}, /* 00,83,03,aa */
{0xaa, 0x84, 0x0054}, /* 00,84,54,aa */
@@ -5742,8 +5746,7 @@ static const struct usb_action tas5130c_vf0250_50HZScale[] = {
{}
};
-/* "60HZ" light frequency banding filter */
-static const struct usb_action tas5130c_vf0250_60HZ[] = {
+static const struct usb_action tas5130c_vf0250_60HZScale[] = {
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
{0xaa, 0x84, 0x0062}, /* 00,84,62,aa */
@@ -5767,8 +5770,7 @@ static const struct usb_action tas5130c_vf0250_60HZ[] = {
{}
};
-/* "60HZScale" light frequency banding ilter */
-static const struct usb_action tas5130c_vf0250_60HZScale[] = {
+static const struct usb_action tas5130c_vf0250_60HZ[] = {
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0002}, /* 00,83,02,aa */
{0xaa, 0x84, 0x00c4}, /* 00,84,c4,aa */
@@ -5792,8 +5794,7 @@ static const struct usb_action tas5130c_vf0250_60HZScale[] = {
{}
};
-/* "NoFliker" light frequency banding flter */
-static const struct usb_action tas5130c_vf0250_NoFliker[] = {
+static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
{0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
@@ -5815,8 +5816,7 @@ static const struct usb_action tas5130c_vf0250_NoFliker[] = {
{}
};
-/* "NoFlikerScale" light frequency banding filter */
-static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
+static const struct usb_action tas5130c_vf0250_NoFliker[] = {
{0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
{0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
{0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
@@ -5839,7 +5839,7 @@ static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
};
static u8 reg_r_i(struct gspca_dev *gspca_dev,
- __u16 index)
+ u16 index)
{
usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
@@ -5852,7 +5852,7 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev,
}
static u8 reg_r(struct gspca_dev *gspca_dev,
- __u16 index)
+ u16 index)
{
u8 ret;
@@ -5862,8 +5862,8 @@ static u8 reg_r(struct gspca_dev *gspca_dev,
}
static void reg_w_i(struct usb_device *dev,
- __u8 value,
- __u16 index)
+ u8 value,
+ u16 index)
{
usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
@@ -5874,18 +5874,18 @@ static void reg_w_i(struct usb_device *dev,
}
static void reg_w(struct usb_device *dev,
- __u8 value,
- __u16 index)
+ u8 value,
+ u16 index)
{
PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
reg_w_i(dev, value, index);
}
-static __u16 i2c_read(struct gspca_dev *gspca_dev,
- __u8 reg)
+static u16 i2c_read(struct gspca_dev *gspca_dev,
+ u8 reg)
{
- __u8 retbyte;
- __u16 retval;
+ u8 retbyte;
+ u16 retval;
reg_w_i(gspca_dev->dev, reg, 0x0092);
reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */
@@ -5900,12 +5900,12 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev,
return retval;
}
-static __u8 i2c_write(struct gspca_dev *gspca_dev,
- __u8 reg,
- __u8 valL,
- __u8 valH)
+static u8 i2c_write(struct gspca_dev *gspca_dev,
+ u8 reg,
+ u8 valL,
+ u8 valH)
{
- __u8 retbyte;
+ u8 retbyte;
reg_w_i(gspca_dev->dev, reg, 0x92);
reg_w_i(gspca_dev->dev, valL, 0x93);
@@ -5957,24 +5957,24 @@ static void setmatrix(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
- const __u8 *matrix;
+ const u8 *matrix;
static const u8 adcm2700_matrix[9] =
/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
/*ms-win*/
{0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
- static const __u8 gc0305_matrix[9] =
+ static const u8 gc0305_matrix[9] =
{0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
- static const __u8 ov7620_matrix[9] =
+ static const u8 ov7620_matrix[9] =
{0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
- static const __u8 pas202b_matrix[9] =
+ static const u8 pas202b_matrix[9] =
{0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
- static const __u8 po2030_matrix[9] =
+ 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] =
+ static const u8 vf0250_matrix[9] =
{0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
- static const __u8 *matrix_tb[SENSOR_MAX] = {
+ static const u8 *matrix_tb[SENSOR_MAX] = {
adcm2700_matrix, /* SENSOR_ADCM2700 0 */
ov7620_matrix, /* SENSOR_CS2102 1 */
NULL, /* SENSOR_CS2102K 2 */
@@ -6006,11 +6006,12 @@ static void setmatrix(struct gspca_dev *gspca_dev)
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 brightness;
+ u8 brightness;
switch (sd->sensor) {
case SENSOR_GC0305:
case SENSOR_OV7620:
+ case SENSOR_PAS202B:
case SENSOR_PO2030:
return;
}
@@ -6034,7 +6035,7 @@ static void setsharpness(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
int sharpness;
- static const __u8 sharpness_tb[][2] = {
+ static const u8 sharpness_tb[][2] = {
{0x02, 0x03},
{0x04, 0x07},
{0x08, 0x0f},
@@ -6053,118 +6054,69 @@ static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- const __u8 *Tgamma, *Tgradient;
- int g, i, k;
- static const __u8 kgamma_tb[16] = /* delta for contrast */
+ const u8 *Tgamma;
+ int g, i, k, adj, gp;
+ u8 gr[16];
+ static const u8 delta_tb[16] = /* delta for contrast */
{0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
- static const __u8 kgrad_tb[16] =
- {0x1b, 0x06, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04};
- static const __u8 Tgamma_1[16] =
+ static const u8 gamma_tb[6][16] = {
{0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
- 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff};
- static const __u8 Tgradient_1[16] =
- {0x00, 0x01, 0x05, 0x0b, 0x10, 0x15, 0x18, 0x1a,
- 0x1a, 0x18, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x06};
- static const __u8 Tgamma_2[16] =
+ 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff},
{0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
- 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff};
- static const __u8 Tgradient_2[16] =
- {0x05, 0x0f, 0x16, 0x1a, 0x19, 0x19, 0x17, 0x15,
- 0x12, 0x10, 0x0e, 0x0b, 0x09, 0x08, 0x06, 0x03};
- static const __u8 Tgamma_3[16] =
+ 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff},
{0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
- 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff};
- static const __u8 Tgradient_3[16] =
- {0x0c, 0x16, 0x1b, 0x1c, 0x19, 0x18, 0x15, 0x12,
- 0x10, 0x0d, 0x0b, 0x09, 0x08, 0x06, 0x05, 0x03};
- static const __u8 Tgamma_4[16] =
+ 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff},
{0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff};
- static const __u8 Tgradient_4[16] =
- {0x26, 0x22, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0d,
- 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02};
- static const __u8 Tgamma_5[16] =
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff},
{0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
- 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff};
- static const __u8 Tgradient_5[16] =
- {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b,
- 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02};
- static const __u8 Tgamma_6[16] = /* ?? was gamma 5 */
+ 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff},
{0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
- 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff};
- static const __u8 Tgradient_6[16] =
- {0x18, 0x20, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0e,
- 0x0b, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01};
- static const __u8 *gamma_tb[] = {
- NULL, Tgamma_1, Tgamma_2,
- Tgamma_3, Tgamma_4, Tgamma_5, Tgamma_6
+ 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
};
- static const __u8 *gradient_tb[] = {
- NULL, Tgradient_1, Tgradient_2,
- Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6
- };
-#ifdef GSPCA_DEBUG
- __u8 v[16];
-#endif
- Tgamma = gamma_tb[sd->gamma];
- Tgradient = gradient_tb[sd->gamma];
+ Tgamma = gamma_tb[sd->gamma - 1];
- k = (sd->contrast - 128) /* -128 / 128 */
- * Tgamma[0];
- PDEBUG(D_CONF, "gamma:%d contrast:%d gamma coeff: %d/128",
- sd->gamma, sd->contrast, k);
+ k = ((int) sd->contrast - 128); /* -128 / 128 */
+ adj = 0;
+ gp = 0;
for (i = 0; i < 16; i++) {
- g = Tgamma[i] + kgamma_tb[i] * k / 128;
+ g = Tgamma[i] - delta_tb[i] * k / 128 - adj / 2;
if (g > 0xff)
g = 0xff;
else if (g <= 0)
g = 1;
reg_w(dev, g, 0x0120 + i); /* gamma */
-#ifdef GSPCA_DEBUG
- if (gspca_debug & D_CONF)
- v[i] = g;
-#endif
- }
- PDEBUG(D_CONF, "tb: %02x %02x %02x %02x %02x %02x %02x %02x",
- v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
- PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x",
- v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
- for (i = 0; i < 16; i++) {
- g = Tgradient[i] - kgrad_tb[i] * k / 128;
- if (g > 0xff)
- g = 0xff;
- else if (g <= 0) {
- if (i != 15)
- g = 0;
+ if (k > 0)
+ adj--;
+ else
+ adj++;
+
+ if (i != 0) {
+ if (gp == 0)
+ gr[i - 1] = 0;
else
- g = 1;
+ gr[i - 1] = g - gp;
}
- reg_w(dev, g, 0x0130 + i); /* gradient */
-#ifdef GSPCA_DEBUG
- if (gspca_debug & D_CONF)
- v[i] = g;
-#endif
+ gp = g;
}
- PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x",
- v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
- PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x",
- v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
+ gr[15] = gr[14] / 2;
+ for (i = 0; i < 16; i++)
+ reg_w(dev, gr[i], 0x0130 + i); /* gradient */
}
static void setquality(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- __u8 frxt;
+ u8 frxt;
switch (sd->sensor) {
case SENSOR_ADCM2700:
case SENSOR_GC0305:
case SENSOR_HV7131B:
case SENSOR_OV7620:
+ case SENSOR_PAS202B:
case SENSOR_PO2030:
return;
}
@@ -6218,9 +6170,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
hdcs2020b_50HZ, hdcs2020b_50HZ,
hdcs2020b_60HZ, hdcs2020b_60HZ},
/* SENSOR_HV7131B 5 */
- {hv7131b_NoFlikerScale, hv7131b_NoFliker,
- hv7131b_50HZScale, hv7131b_50HZ,
- hv7131b_60HZScale, hv7131b_60HZ},
+ {hv7131b_NoFliker, hv7131b_NoFlikerScale,
+ hv7131b_50HZ, hv7131b_50HZScale,
+ hv7131b_60HZ, hv7131b_60HZScale},
/* SENSOR_HV7131C 6 */
{NULL, NULL,
NULL, NULL,
@@ -6230,17 +6182,17 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
/* SENSOR_MC501CB 8 */
- {MC501CB_NoFliker, MC501CB_NoFlikerScale,
- MC501CB_50HZ, MC501CB_50HZScale,
- MC501CB_60HZ, MC501CB_60HZScale},
+ {mc501cb_NoFliker, mc501cb_NoFlikerScale,
+ mc501cb_50HZ, mc501cb_50HZScale,
+ mc501cb_60HZ, mc501cb_60HZScale},
/* SENSOR_MI0360SOC 9 */
- {mi360soc_AENoFlikerScale, mi360soc_AENoFliker,
- mi360soc_AE50HZScale, mi360soc_AE50HZ,
- mi360soc_AE60HZScale, mi360soc_AE60HZ},
+ {mi360soc_AENoFliker, mi360soc_AENoFlikerScale,
+ mi360soc_AE50HZ, mi360soc_AE50HZScale,
+ mi360soc_AE60HZ, mi360soc_AE60HZScale},
/* SENSOR_OV7620 10 */
- {OV7620_NoFliker, OV7620_NoFliker,
- OV7620_50HZ, OV7620_50HZ,
- OV7620_60HZ, OV7620_60HZ},
+ {ov7620_NoFliker, ov7620_NoFliker,
+ ov7620_50HZ, ov7620_50HZ,
+ ov7620_60HZ, ov7620_60HZ},
/* SENSOR_OV7630C 11 */
{NULL, NULL,
NULL, NULL,
@@ -6258,17 +6210,17 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
pb0330_50HZScale, pb0330_50HZ,
pb0330_60HZScale, pb0330_60HZ},
/* SENSOR_PO2030 15 */
- {PO2030_NoFliker, PO2030_NoFliker,
- PO2030_50HZ, PO2030_50HZ,
- PO2030_60HZ, PO2030_60HZ},
+ {po2030_NoFliker, po2030_NoFliker,
+ po2030_50HZ, po2030_50HZ,
+ po2030_60HZ, po2030_60HZ},
/* SENSOR_TAS5130CK 16 */
- {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker,
- tas5130cxx_50HZScale, tas5130cxx_50HZ,
- tas5130cxx_60HZScale, tas5130cxx_60HZ},
+ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+ tas5130cxx_50HZ, tas5130cxx_50HZScale,
+ tas5130cxx_60HZ, tas5130cxx_60HZScale},
/* SENSOR_TAS5130CXX 17 */
- {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker,
- tas5130cxx_50HZScale, tas5130cxx_50HZ,
- tas5130cxx_60HZScale, tas5130cxx_60HZ},
+ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+ tas5130cxx_50HZ, tas5130cxx_50HZScale,
+ tas5130cxx_60HZ, tas5130cxx_60HZScale},
/* SENSOR_TAS5130C_VF0250 18 */
{tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
@@ -6277,9 +6229,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
i = sd->lightfreq * 2;
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
- if (!mode)
- i++; /* 640x480 */
- zc3_freq = freq_tb[(int) sd->sensor][i];
+ if (mode)
+ i++; /* 320x240 */
+ zc3_freq = freq_tb[sd->sensor][i];
if (zc3_freq != NULL) {
usb_exchange(gspca_dev, zc3_freq);
switch (sd->sensor) {
@@ -6297,6 +6249,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
reg_w(gspca_dev->dev, 0x44, 0x0002);
}
break;
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev->dev, 0x00, 0x01a7);
+ break;
}
}
return 0;
@@ -6305,7 +6260,7 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 autoval;
+ u8 autoval;
if (sd->autogain)
autoval = 0x42;
@@ -6333,6 +6288,12 @@ static void send_unknown(struct usb_device *dev, int sensor)
reg_w(dev, 0x02, 0x003b);
reg_w(dev, 0x00, 0x0038);
break;
+ case SENSOR_PAS202B:
+ reg_w(dev, 0x03, 0x003b);
+ reg_w(dev, 0x0c, 0x003a);
+ reg_w(dev, 0x0b, 0x0039);
+ reg_w(dev, 0x0b, 0x0038);
+ break;
}
}
@@ -6349,7 +6310,7 @@ static void start_2wr_probe(struct usb_device *dev, int sensor)
static int sif_probe(struct gspca_dev *gspca_dev)
{
- __u16 checkword;
+ u16 checkword;
start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */
reg_w(gspca_dev->dev, 0x08, 0x008d);
@@ -6392,6 +6353,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
}
start_2wr_probe(dev, 0x08); /* HDCS2020 */
+ i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x15);
if (retword != 0)
@@ -6420,8 +6382,10 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
msleep(50);
retword = i2c_read(gspca_dev, 0x03);
- if (retword != 0)
+ if (retword != 0) {
+ send_unknown(dev, SENSOR_PAS202B);
return 0x0e; /* PAS202BCB */
+ }
start_2wr_probe(dev, 0x02); /* TAS5130C */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
@@ -6457,8 +6421,8 @@ ov_check:
}
struct sensor_by_chipset_revision {
- __u16 revision;
- __u8 internal_sensor_id;
+ u16 revision;
+ u8 internal_sensor_id;
};
static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
{0xc000, 0x12}, /* TAS5130C */
@@ -6467,6 +6431,7 @@ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
{0x8001, 0x13},
{0x8000, 0x14}, /* CS2102K */
{0x8400, 0x15}, /* TAS5130K */
+ {0xe400, 0x15},
};
static int vga_3wr_probe(struct gspca_dev *gspca_dev)
@@ -6474,7 +6439,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
int i;
- __u8 retbyte;
+ u8 retbyte;
u16 retword;
/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
@@ -6622,8 +6587,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
int sensor;
- int vga = 1; /* 1: vga, 0: sif */
- static const __u8 gamma[SENSOR_MAX] = {
+ static const u8 gamma[SENSOR_MAX] = {
4, /* SENSOR_ADCM2700 0 */
4, /* SENSOR_CS2102 1 */
5, /* SENSOR_CS2102K 2 */
@@ -6644,9 +6608,30 @@ static int sd_config(struct gspca_dev *gspca_dev,
3, /* SENSOR_TAS5130CXX 17 */
3, /* SENSOR_TAS5130C_VF0250 18 */
};
+ static const u8 mode_tb[SENSOR_MAX] = {
+ 2, /* SENSOR_ADCM2700 0 */
+ 1, /* SENSOR_CS2102 1 */
+ 1, /* SENSOR_CS2102K 2 */
+ 1, /* SENSOR_GC0305 3 */
+ 1, /* SENSOR_HDCS2020b 4 */
+ 1, /* SENSOR_HV7131B 5 */
+ 1, /* SENSOR_HV7131C 6 */
+ 1, /* SENSOR_ICM105A 7 */
+ 2, /* SENSOR_MC501CB 8 */
+ 1, /* SENSOR_MI0360SOC 9 */
+ 2, /* SENSOR_OV7620 10 */
+ 1, /* SENSOR_OV7630C 11 */
+ 0, /* SENSOR_PAS106 12 */
+ 1, /* SENSOR_PAS202B 13 */
+ 1, /* SENSOR_PB0330 14 */
+ 1, /* SENSOR_PO2030 15 */
+ 1, /* SENSOR_TAS5130CK 16 */
+ 1, /* SENSOR_TAS5130CXX 17 */
+ 1, /* SENSOR_TAS5130C_VF0250 18 */
+ };
/* define some sensors from the vendor/product */
- sd->sharpness = 2;
+ sd->sharpness = SHARPNESS_DEF;
sd->sensor = id->driver_info;
sensor = zcxx_probeSensor(gspca_dev);
if (sensor >= 0)
@@ -6671,8 +6656,21 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
break;
case 0:
- PDEBUG(D_PROBE, "Find Sensor HV7131B");
- sd->sensor = SENSOR_HV7131B;
+ /* check the sensor type */
+ sensor = i2c_read(gspca_dev, 0x00);
+ PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor);
+ switch (sensor) {
+ case 0: /* hv7131b */
+ case 1: /* hv7131e */
+ PDEBUG(D_PROBE, "Find Sensor HV7131B");
+ sd->sensor = SENSOR_HV7131B;
+ break;
+ default:
+/* case 2: * hv7131r */
+ PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
+ sd->sensor = SENSOR_HV7131C;
+ break;
+ }
break;
case 0x02:
PDEBUG(D_PROBE, "Sensor TAS5130C");
@@ -6699,12 +6697,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
case 0x0e:
PDEBUG(D_PROBE, "Find Sensor PAS202B");
sd->sensor = SENSOR_PAS202B;
- sd->sharpness = 1;
+/* sd->sharpness = 1; */
break;
case 0x0f:
PDEBUG(D_PROBE, "Find Sensor PAS106");
sd->sensor = SENSOR_PAS106;
- vga = 0; /* SIF */
break;
case 0x10:
case 0x12:
@@ -6770,31 +6767,38 @@ static int sd_config(struct gspca_dev *gspca_dev,
if (sensor < 0x20) {
if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
reg_w(gspca_dev->dev, 0x02, 0x0010);
- else
- reg_w(gspca_dev->dev, sensor & 0x0f, 0x0010);
reg_r(gspca_dev, 0x0010);
}
cam = &gspca_dev->cam;
/*fixme:test*/
gspca_dev->nbalt--;
- if (vga) {
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- } else {
+ switch (mode_tb[sd->sensor]) {
+ case 0:
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(sif_mode);
+ break;
+ case 1:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ default:
+/* case 2: */
+ cam->cam_mode = broken_vga_mode;
+ cam->nmodes = ARRAY_SIZE(broken_vga_mode);
+ break;
}
- sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
- sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
- sd->gamma = gamma[(int) sd->sensor];
- sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
- sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value;
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->gamma = gamma[sd->sensor];
+ sd->autogain = AUTOGAIN_DEF;
+ sd->lightfreq = FREQ_DEF;
sd->quality = QUALITY_DEF;
switch (sd->sensor) {
case SENSOR_GC0305:
case SENSOR_OV7620:
+ case SENSOR_PAS202B:
case SENSOR_PO2030:
gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
break;
@@ -6805,14 +6809,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
}
- /* switch the led off */
- reg_w(gspca_dev->dev, 0x01, 0x0000);
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
+ /* switch off the led */
reg_w(gspca_dev->dev, 0x01, 0x0000);
return 0;
}
@@ -6821,28 +6824,27 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- const struct usb_action *zc3_init;
int mode;
static const struct usb_action *init_tb[SENSOR_MAX][2] = {
{adcm2700_Initial, adcm2700_InitialScale}, /* 0 */
- {cs2102_InitialScale, cs2102_Initial}, /* 1 */
- {cs2102K_InitialScale, cs2102K_Initial}, /* 2 */
+ {cs2102_Initial, cs2102_InitialScale}, /* 1 */
+ {cs2102K_Initial, cs2102K_InitialScale}, /* 2 */
{gc0305_Initial, gc0305_InitialScale}, /* 3 */
- {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */
- {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */
- {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */
- {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */
- {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */
+ {hdcs2020b_Initial, hdcs2020b_InitialScale}, /* 4 */
+ {hv7131b_Initial, hv7131b_InitialScale}, /* 5 */
+ {hv7131r_Initial, hv7131r_InitialScale}, /* 6 */
+ {icm105a_Initial, icm105a_InitialScale}, /* 7 */
+ {mc501cb_Initial, mc501cb_InitialScale}, /* 8 */
{mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */
- {OV7620_mode0, OV7620_mode1}, /* 10 */
- {ov7630c_InitialScale, ov7630c_Initial}, /* 11 */
- {pas106b_InitialScale, pas106b_Initial}, /* 12 */
+ {ov7620_Initial, ov7620_InitialScale}, /* 10 */
+ {ov7630c_Initial, ov7630c_InitialScale}, /* 11 */
+ {pas106b_Initial, pas106b_InitialScale}, /* 12 */
{pas202b_Initial, pas202b_InitialScale}, /* 13 */
{pb0330_Initial, pb0330_InitialScale}, /* 14 */
- {PO2030_mode0, PO2030_mode1}, /* 15 */
- {tas5130CK_InitialScale, tas5130CK_Initial}, /* 16 */
+ {po2030_Initial, po2030_InitialScale}, /* 15 */
+ {tas5130cK_Initial, tas5130cK_InitialScale}, /* 16 */
{tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */
- {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial},
+ {tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale},
/* 18 */
};
@@ -6854,8 +6856,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
0x21); /* JPEG 422 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
- zc3_init = init_tb[(int) sd->sensor][mode];
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
switch (sd->sensor) {
case SENSOR_HV7131C:
zcxx_probeSensor(gspca_dev);
@@ -6864,7 +6865,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, pas106b_Initial_com);
break;
}
- usb_exchange(gspca_dev, zc3_init);
+ usb_exchange(gspca_dev, init_tb[sd->sensor][mode]);
switch (sd->sensor) {
case SENSOR_ADCM2700:
@@ -6883,6 +6884,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(dev, 0x02, 0x003b);
reg_w(dev, 0x00, 0x0038);
break;
+ case SENSOR_PAS202B:
+ reg_w(dev, 0x03, 0x003b);
+ reg_w(dev, 0x0c, 0x003a);
+ reg_w(dev, 0x0b, 0x0039);
+ break;
}
setmatrix(gspca_dev);
@@ -6961,13 +6967,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_PO2030:
msleep(50);
- reg_r(gspca_dev, 0x0008);
- reg_r(gspca_dev, 0x0007);
- /*fall thru*/
- case SENSOR_PAS202B:
reg_w(dev, 0x00, 0x0007); /* (from win traces) */
reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
break;
+ case SENSOR_PAS202B:
+ reg_w(dev, 0x32, 0x0007); /* (from win traces) */
+ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+ break;
}
return 0;
}
@@ -7165,6 +7171,22 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
return 0;
}
+#ifdef CONFIG_INPUT
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ if (len == 8 && data[4] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ }
+
+ return 0;
+}
+#endif
+
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
@@ -7177,6 +7199,9 @@ static const struct sd_desc sd_desc = {
.querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
+#ifdef CONFIG_INPUT
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
};
static const __devinitdata struct usb_device_id device_table[] = {
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index 51f393d03a4..2fc9865fd48 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -39,12 +39,12 @@ int hdpvr_debug;
module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(hdpvr_debug, "enable debugging output");
-uint default_video_input = HDPVR_VIDEO_INPUTS;
+static uint default_video_input = HDPVR_VIDEO_INPUTS;
module_param(default_video_input, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / "
"1=S-Video / 2=Composite");
-uint default_audio_input = HDPVR_AUDIO_INPUTS;
+static uint default_audio_input = HDPVR_AUDIO_INPUTS;
module_param(default_audio_input, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / "
"1=RCA front / 2=S/PDIF");
@@ -59,6 +59,7 @@ static struct usb_device_id hdpvr_table[] = {
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) },
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) },
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, hdpvr_table);
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index fdd782039e9..196f82de48f 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -302,7 +302,8 @@ static int hdpvr_start_streaming(struct hdpvr_device *dev)
/* function expects dev->io_mutex to be hold by caller */
static int hdpvr_stop_streaming(struct hdpvr_device *dev)
{
- uint actual_length, c = 0;
+ int actual_length;
+ uint c = 0;
u8 *buf;
if (dev->status == STATUS_IDLE)
@@ -572,7 +573,7 @@ static int vidioc_querycap(struct file *file, void *priv,
struct hdpvr_device *dev = video_drvdata(file);
strcpy(cap->driver, "hdpvr");
- strcpy(cap->card, "Haupauge HD PVR");
+ strcpy(cap->card, "Hauppauge HD PVR");
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
cap->version = HDPVR_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
index 1edd8759121..49ae25d83d1 100644
--- a/drivers/media/video/hdpvr/hdpvr.h
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -30,6 +30,7 @@
#define HD_PVR_PRODUCT_ID 0x4900
#define HD_PVR_PRODUCT_ID1 0x4901
#define HD_PVR_PRODUCT_ID2 0x4902
+#define HD_PVR_PRODUCT_ID3 0x4982
#define UNSET (-1U)
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index 60d992ee258..e620a3a92f2 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -352,9 +352,13 @@ static struct saa7146_ext_vv vv_data;
static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ int ret;
DEB_EE((".\n"));
+ ret = saa7146_vv_devinit(dev);
+ if (ret)
+ return ret;
hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
@@ -400,9 +404,10 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
vv_data.ops.vidioc_enum_input = vidioc_enum_input;
vv_data.ops.vidioc_g_input = vidioc_g_input;
vv_data.ops.vidioc_s_input = vidioc_s_input;
- if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) {
+ ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
+ if (ret < 0) {
printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
- return -1;
+ return ret;
}
printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 938a1f8f880..fe596a1c12a 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -216,6 +216,10 @@ static int hexium_probe(struct saa7146_dev *dev)
return -EFAULT;
}
+ err = saa7146_vv_devinit(dev);
+ if (err)
+ return err;
+
hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
if (NULL == hexium) {
printk("hexium_orion: hexium_probe: not enough kernel memory.\n");
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index b86e35386ce..da18d698e7f 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -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 = 0;
+ u64 ir_type = 0;
struct IR_i2c *ir;
struct input_dev *input_dev;
struct i2c_adapter *adap = client->adapter;
@@ -331,6 +331,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir_codes = &ir_codes_pv951_table;
break;
case 0x18:
+ case 0x1f:
case 0x1a:
name = "Hauppauge";
ir->get_key = get_key_haup;
@@ -446,7 +447,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
input_dev->name = ir->name;
input_dev->phys = ir->phys;
- err = ir_input_register(ir->input, ir->ir_codes);
+ err = ir_input_register(ir->input, ir->ir_codes, NULL);
if (err)
goto err_out_free;
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index 79d0fe4990d..ca1fd3227a9 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -1210,6 +1210,53 @@ static const struct ivtv_card ivtv_card_buffalo = {
.i2c = &ivtv_i2c_std,
};
+/* ------------------------------------------------------------------------- */
+/* Sony Kikyou */
+
+static const struct ivtv_card_pci_info ivtv_pci_kikyou[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_SONY, 0x813d },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_kikyou = {
+ .type = IVTV_CARD_KIKYOU,
+ .name = "Sony VAIO Giga Pocket (ENX Kikyou)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ { IVTV_CARD_INPUT_LINE_IN2, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0x03e1, .initial_value = 0x0320 },
+ .gpio_audio_input = { .mask = 0x0060,
+ .tuner = 0x0020,
+ .linein = 0x0000,
+ .radio = 0x0060 },
+ .gpio_audio_mute = { .mask = 0x0000,
+ .mute = 0x0000 }, /* 0x200? Disable for now. */
+ .gpio_audio_mode = { .mask = 0x0080,
+ .mono = 0x0000,
+ .stereo = 0x0000, /* SAP */
+ .lang1 = 0x0080,
+ .lang2 = 0x0000,
+ .both = 0x0080 },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_SONY_BTF_PXN01Z },
+ },
+ .pci_list = ivtv_pci_kikyou,
+ .i2c = &ivtv_i2c_std,
+};
+
+
static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_pvr250,
&ivtv_card_pvr350,
@@ -1238,6 +1285,7 @@ static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_aver_m104,
&ivtv_card_buffalo,
&ivtv_card_aver_ultra1500mce,
+ &ivtv_card_kikyou,
/* Variations of standard cards but with the same PCI IDs.
These cards must come last in this list. */
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index 6148827ec88..78eca992e1f 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -51,7 +51,8 @@
#define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */
#define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */
#define IVTV_CARD_AVER_ULTRA1500MCE 26 /* AVerMedia UltraTV 1500 MCE */
-#define IVTV_CARD_LAST 26
+#define IVTV_CARD_KIKYOU 27 /* Sony VAIO Giga Pocket (ENX Kikyou) */
+#define IVTV_CARD_LAST 27
/* Variants of existing cards but with the same PCI IDs. The driver
detects these based on other device information.
@@ -86,6 +87,7 @@
#define IVTV_PCI_ID_MELCO 0x1154
#define IVTV_PCI_ID_GOTVIEW1 0xffac
#define IVTV_PCI_ID_GOTVIEW2 0xffad
+#define IVTV_PCI_ID_SONY 0x104d
/* hardware flags, no gaps allowed */
#define IVTV_HW_CX25840 (1 << 0)
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 347c3344f56..9a250548be4 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -193,6 +193,7 @@ MODULE_PARM_DESC(cardtype,
"\t\t\t25 = AverMedia M104 (not yet working)\n"
"\t\t\t26 = Buffalo PC-MV5L/PCI\n"
"\t\t\t27 = AVerMedia UltraTV 1500 MCE\n"
+ "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
index c1b7ec475c2..a71e8ba306b 100644
--- a/drivers/media/video/ivtv/ivtv-firmware.c
+++ b/drivers/media/video/ivtv/ivtv-firmware.c
@@ -258,7 +258,7 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
return;
}
- ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
mem_offset = itv->dec_mem + data[1];
if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index cd9db0bf33b..12d36ca91d5 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -562,7 +562,7 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s;
- ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);
del_timer(&itv->dma_timer);
@@ -638,7 +638,7 @@ static void ivtv_irq_dma_err(struct ivtv *itv)
u32 data[CX2341X_MBOX_MAX_DATA];
del_timer(&itv->dma_timer);
- ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
@@ -669,7 +669,7 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv)
struct ivtv_stream *s;
/* Get DMA destination and size arguments from card */
- ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data);
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data);
IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
@@ -713,9 +713,9 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
struct ivtv_stream *s;
/* YUV or MPG */
- ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
itv->dma_data_req_size =
1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
itv->dma_data_req_offset = data[1];
@@ -724,6 +724,7 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
}
else {
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data);
itv->dma_data_req_size = min_t(u32, data[2], 0x10000);
itv->dma_data_req_offset = data[1];
s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
@@ -940,9 +941,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
ivtv_dma_enc_start(s);
break;
}
- if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) {
+
+ if (i == IVTV_MAX_STREAMS &&
+ test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
ivtv_udma_start(itv);
- }
}
if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c
index 1b5c0ac09a8..84577f6f41a 100644
--- a/drivers/media/video/ivtv/ivtv-mailbox.c
+++ b/drivers/media/video/ivtv/ivtv-mailbox.c
@@ -369,10 +369,11 @@ int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
}
/* This one is for stuff that can't sleep.. irq handlers, etc.. */
-void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[])
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
+ int argc, u32 data[])
{
+ volatile u32 __iomem *p = mbdata->mbox[mb].data;
int i;
-
- for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
- data[i] = readl(&mbdata->mbox[mb].data[i]);
+ for (i = 0; i < argc; i++, p++)
+ data[i] = readl(p);
}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h
index 6ef12091e3f..8247662c928 100644
--- a/drivers/media/video/ivtv/ivtv-mailbox.h
+++ b/drivers/media/video/ivtv/ivtv-mailbox.h
@@ -24,7 +24,8 @@
#define IVTV_MBOX_DMA_END 8
#define IVTV_MBOX_DMA 9
-void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]);
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
+ int argc, u32 data[]);
int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index e12c6022373..1f9387f6ca2 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -577,10 +577,14 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
+ /* Avoid tinny audio problem - ensure audio clocks are going */
+ v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
+ /* Avoid unpredictable PCI bus hang - disable video clocks */
v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
- ivtv_msleep_timeout(300, 1);
+ ivtv_msleep_timeout(150, 1);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
+ ivtv_msleep_timeout(150, 1);
}
/* begin_capture */
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
index d07ad6c3902..1daf1dd65bf 100644
--- a/drivers/media/video/ivtv/ivtv-udma.c
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -213,6 +213,7 @@ void ivtv_udma_start(struct ivtv *itv)
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
set_bit(IVTV_F_I_DMA, &itv->i_flags);
set_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
}
void ivtv_udma_prepare(struct ivtv *itv)
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index fc4dd604572..7438f8d775b 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -514,7 +514,7 @@ static int mt9t112_init_pll(const struct i2c_client *client)
/* poll to verify out of standby. Must Poll this bit */
for (i = 0; i < 100; i++) {
mt9t112_reg_read(data, client, 0x0018);
- if (0x4000 & data)
+ if (!(0x4000 & data))
break;
mdelay(10);
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 91df7ec91fb..1a34d2993e9 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -257,19 +257,18 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
{
struct soc_camera_link *icl = to_soc_camera_link(icd);
- unsigned int width_flag;
+ unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE |
+ SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
+ SOCAM_DATA_ACTIVE_HIGH;
if (icl->query_bus_param)
- width_flag = icl->query_bus_param(icl) &
- SOCAM_DATAWIDTH_MASK;
+ flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
else
- width_flag = SOCAM_DATAWIDTH_10;
+ flags |= SOCAM_DATAWIDTH_10;
- return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
- SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
- SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_SLAVE |
- width_flag;
+ return soc_camera_apply_sensor_flags(icl, flags);
}
static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index c1fc6dc776f..9f01f14e4aa 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -169,7 +169,11 @@ static struct saa7146_extension extension;
static int mxb_probe(struct saa7146_dev *dev)
{
struct mxb *mxb = NULL;
+ int err;
+ err = saa7146_vv_devinit(dev);
+ if (err)
+ return err;
mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
if (mxb == NULL) {
DEB_D(("not enough kernel memory.\n"));
@@ -294,7 +298,7 @@ static int mxb_init_done(struct saa7146_dev* dev)
/* select tuner-output on saa7111a */
i = 0;
saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0,
- SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS, 0);
+ SAA7111_FMT_CCIR, 0);
/* select a tuner type */
tun_setup.mode_mask = T_ANALOG_TV;
@@ -518,8 +522,8 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
return err;
/* switch video in saa7111a */
- if (saa7111a_call(mxb, video, s_routing, i, 0, 0))
- printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n");
+ if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
+ printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a.\n");
/* switch the audio-source only if necessary */
if (0 == mxb->cur_mute)
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 3a45e945a52..7f8ece30c77 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -547,7 +547,6 @@ static const struct v4l2_queryctrl ov772x_controls[] = {
},
};
-
/*
* general function
*/
@@ -634,7 +633,12 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
+ SOCAM_DATA_ACTIVE_HIGH;
+
+ if (priv->info->flags & OV772X_FLAG_8BIT)
+ flags |= SOCAM_DATAWIDTH_8;
+ else
+ flags |= SOCAM_DATAWIDTH_10;
return soc_camera_apply_sensor_flags(icl, flags);
}
@@ -1040,15 +1044,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
return -ENODEV;
/*
- * ov772x only use 8 or 10 bit bus width
- */
- if (SOCAM_DATAWIDTH_10 != priv->info->buswidth &&
- SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
- dev_err(&client->dev, "bus width error\n");
- return -ENODEV;
- }
-
- /*
* check and show product ID and manufacturer ID
*/
pid = i2c_smbus_read_byte_data(client, PID);
@@ -1130,7 +1125,6 @@ static int ov772x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov772x_priv *priv;
- struct ov772x_camera_info *info;
struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct soc_camera_link *icl;
@@ -1145,8 +1139,6 @@ static int ov772x_probe(struct i2c_client *client,
if (!icl || !icl->priv)
return -EINVAL;
- info = icl->priv;
-
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&adapter->dev,
"I2C-Adapter doesn't support "
@@ -1158,7 +1150,7 @@ static int ov772x_probe(struct i2c_client *client,
if (!priv)
return -ENOMEM;
- priv->info = info;
+ priv->info = icl->priv;
v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index de5485f506b..cb4057bb07a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -233,8 +233,9 @@ struct pvr2_hdw {
int state_encoder_waitok; /* Encoder pre-wait done */
int state_encoder_runok; /* Encoder has run for >= .25 sec */
int state_decoder_run; /* Decoder is running */
+ int state_decoder_ready; /* Decoder is stabilized & streamable */
int state_usbstream_run; /* FX2 is streaming */
- int state_decoder_quiescent; /* Decoder idle for > 50msec */
+ int state_decoder_quiescent; /* Decoder idle for minimal interval */
int state_pipeline_config; /* Pipeline is configured */
int state_pipeline_req; /* Somebody wants to stream */
int state_pipeline_pause; /* Pipeline must be paused */
@@ -255,9 +256,16 @@ struct pvr2_hdw {
void (*state_func)(void *);
void *state_data;
- /* Timer for measuring decoder settling time */
+ /* Timer for measuring required decoder settling time before we're
+ allowed to fire it up again. */
struct timer_list quiescent_timer;
+ /* Timer for measuring decoder stabilization time, which is the
+ amount of time we need to let the decoder run before we can
+ trust its output (otherwise the encoder might see garbage and
+ then fail to start correctly). */
+ struct timer_list decoder_stabilization_timer;
+
/* Timer for measuring encoder pre-wait time */
struct timer_list encoder_wait_timer;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 1bbdab08fe0..712b300f723 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -48,11 +48,13 @@
before we are allowed to start it running. */
#define TIME_MSEC_DECODER_WAIT 50
+/* This defines a minimum interval that the decoder must be allowed to run
+ before we can safely begin using its streaming output. */
+#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300
+
/* This defines a minimum interval that the encoder must remain quiet
- before we are allowed to configure it. I had this originally set to
- 50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that
- things work better when it's set to 100msec. */
-#define TIME_MSEC_ENCODER_WAIT 100
+ before we are allowed to configure it. */
+#define TIME_MSEC_ENCODER_WAIT 50
/* This defines the minimum interval that the encoder must successfully run
before we consider that the encoder has run at least once since its
@@ -334,6 +336,7 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
static void pvr2_hdw_quiescent_timeout(unsigned long);
+static void pvr2_hdw_decoder_stabilization_timeout(unsigned long);
static void pvr2_hdw_encoder_wait_timeout(unsigned long);
static void pvr2_hdw_encoder_run_timeout(unsigned long);
static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32);
@@ -1705,6 +1708,7 @@ static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
(enablefl ? "on" : "off"));
v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl);
if (hdw->decoder_client_id) {
/* We get here if the encoder has been noticed. Otherwise
we'll issue a warning to the user (which should
@@ -2461,6 +2465,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->quiescent_timer.data = (unsigned long)hdw;
hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout;
+ init_timer(&hdw->decoder_stabilization_timer);
+ hdw->decoder_stabilization_timer.data = (unsigned long)hdw;
+ hdw->decoder_stabilization_timer.function =
+ pvr2_hdw_decoder_stabilization_timeout;
+
init_timer(&hdw->encoder_wait_timer);
hdw->encoder_wait_timer.data = (unsigned long)hdw;
hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
@@ -2674,6 +2683,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
fail:
if (hdw) {
del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->decoder_stabilization_timer);
del_timer_sync(&hdw->encoder_run_timer);
del_timer_sync(&hdw->encoder_wait_timer);
if (hdw->workqueue) {
@@ -2741,6 +2751,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
hdw->workqueue = NULL;
}
del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->decoder_stabilization_timer);
del_timer_sync(&hdw->encoder_run_timer);
del_timer_sync(&hdw->encoder_wait_timer);
if (hdw->fw_buffer) {
@@ -4452,7 +4463,7 @@ static int state_check_enable_encoder_run(struct pvr2_hdw *hdw)
switch (hdw->pathway_state) {
case PVR2_PATHWAY_ANALOG:
- if (hdw->state_decoder_run) {
+ if (hdw->state_decoder_run && hdw->state_decoder_ready) {
/* In analog mode, if the decoder is running, then
run the encoder. */
return !0;
@@ -4519,6 +4530,17 @@ static void pvr2_hdw_quiescent_timeout(unsigned long data)
}
+/* Timeout function for decoder stabilization timer. */
+static void pvr2_hdw_decoder_stabilization_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_decoder_ready = !0;
+ trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue, &hdw->workpoll);
+}
+
+
/* Timeout function for encoder wait timer. */
static void pvr2_hdw_encoder_wait_timeout(unsigned long data)
{
@@ -4557,8 +4579,13 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw)
}
hdw->state_decoder_quiescent = 0;
hdw->state_decoder_run = 0;
- /* paranoia - solve race if timer just completed */
+ /* paranoia - solve race if timer(s) just completed */
del_timer_sync(&hdw->quiescent_timer);
+ /* Kill the stabilization timer, in case we're killing the
+ encoder before the previous stabilization interval has
+ been properly timed. */
+ del_timer_sync(&hdw->decoder_stabilization_timer);
+ hdw->state_decoder_ready = 0;
} else {
if (!hdw->state_decoder_quiescent) {
if (!timer_pending(&hdw->quiescent_timer)) {
@@ -4596,10 +4623,21 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw)
if (hdw->flag_decoder_missed) return 0;
if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_ready = 0;
hdw->state_decoder_run = !0;
+ if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
+ hdw->decoder_stabilization_timer.expires =
+ jiffies +
+ (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT /
+ 1000);
+ add_timer(&hdw->decoder_stabilization_timer);
+ } else {
+ hdw->state_decoder_ready = !0;
+ }
}
trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
trace_stbit("state_decoder_run",hdw->state_decoder_run);
+ trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
return !0;
}
@@ -4797,7 +4835,8 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
buf,acnt,
"worker:%s%s%s%s%s%s%s",
(hdw->state_decoder_run ?
- " <decode:run>" :
+ (hdw->state_decoder_ready ?
+ "<decode:run>" : " <decode:start>") :
(hdw->state_decoder_quiescent ?
"" : " <decode:stop>")),
(hdw->state_decoder_quiescent ?
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 56e70eae20c..51d3009ab57 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -306,6 +306,7 @@ struct pvr2_hdw_debug_info {
int state_encoder_ok;
int state_encoder_run;
int state_decoder_run;
+ int state_decoder_ready;
int state_usbstream_run;
int state_decoder_quiescent;
int state_pipeline_config;
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index 50b415e07ed..f7f7e04cf48 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -753,7 +753,7 @@ int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value)
buf[0] = 0xff; /* fixed */
ret = send_control_msg(pdev,
- SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, sizeof(buf));
+ SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, 1);
if (!mode && ret >= 0) {
if (value < 0)
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 294f860ce2b..322ac4eecf0 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -898,18 +898,8 @@ static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
{
- struct pxacamera_platform_data *pdata = pcdev->pdata;
- struct device *dev = pcdev->soc_host.v4l2_dev.dev;
u32 cicr4 = 0;
- dev_dbg(dev, "Registered platform device at %p data %p\n",
- pcdev, pdata);
-
- if (pdata && pdata->init) {
- dev_dbg(dev, "%s: Init gpios\n", __func__);
- pdata->init(dev);
- }
-
/* disable all interrupts */
__raw_writel(0x3ff, pcdev->base + CICR0);
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 805226e0d9c..9277194cd82 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -165,7 +165,7 @@ struct rj54n1_reg_val {
u8 val;
};
-const static struct rj54n1_reg_val bank_4[] = {
+static const struct rj54n1_reg_val bank_4[] = {
{0x417, 0},
{0x42c, 0},
{0x42d, 0xf0},
@@ -186,7 +186,7 @@ const static struct rj54n1_reg_val bank_4[] = {
{0x4fe, 2},
};
-const static struct rj54n1_reg_val bank_5[] = {
+static const struct rj54n1_reg_val bank_5[] = {
{0x514, 0},
{0x516, 0},
{0x518, 0},
@@ -207,7 +207,7 @@ const static struct rj54n1_reg_val bank_5[] = {
{0x5fe, 2},
};
-const static struct rj54n1_reg_val bank_7[] = {
+static const struct rj54n1_reg_val bank_7[] = {
{0x70a, 0},
{0x714, 0xff},
{0x715, 0xff},
@@ -215,7 +215,7 @@ const static struct rj54n1_reg_val bank_7[] = {
{0x7FE, 2},
};
-const static struct rj54n1_reg_val bank_8[] = {
+static const struct rj54n1_reg_val bank_8[] = {
{0x800, 0x00},
{0x801, 0x01},
{0x802, 0x61},
@@ -403,12 +403,12 @@ const static struct rj54n1_reg_val bank_8[] = {
{0x8FE, 2},
};
-const static struct rj54n1_reg_val bank_10[] = {
+static const 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 = {
+static const struct rj54n1_clock_div clk_div = {
.ratio_tg = 3 /* default: 5 */,
.ratio_t = 4 /* default: 1 */,
.ratio_r = 4 /* default: 0 */,
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 44873a016c2..c0a7f8a369f 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -104,6 +104,10 @@ static int saa711x_has_reg(const int id, const u8 reg)
if (id == V4L2_IDENT_SAA7111)
return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
(reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
+ if (id == V4L2_IDENT_SAA7111A)
+ return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+ reg != 0x14 && reg != 0x18 && reg != 0x19 &&
+ reg != 0x1d && reg != 0x1e;
/* common for saa7113/4/5/8 */
if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
@@ -954,8 +958,7 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
011 NTSC N (3.58MHz) PAL M (3.58MHz)
100 reserved NTSC-Japan (3.58MHz)
*/
- if (state->ident == V4L2_IDENT_SAA7111 ||
- state->ident == V4L2_IDENT_SAA7113) {
+ if (state->ident <= V4L2_IDENT_SAA7113) {
u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
@@ -1232,22 +1235,19 @@ static int saa711x_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct saa711x_state *state = to_state(sd);
- u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
+ u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
input, output);
/* saa7111/3 does not have these inputs */
- if ((state->ident == V4L2_IDENT_SAA7113 ||
- state->ident == V4L2_IDENT_SAA7111) &&
+ if (state->ident <= V4L2_IDENT_SAA7113 &&
(input == SAA7115_COMPOSITE4 ||
input == SAA7115_COMPOSITE5)) {
return -EINVAL;
}
if (input > SAA7115_SVIDEO3)
return -EINVAL;
- if (output > SAA7115_IPORT_ON)
- return -EINVAL;
if (state->input == input && state->output == output)
return 0;
v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
@@ -1256,7 +1256,7 @@ static int saa711x_s_routing(struct v4l2_subdev *sd,
state->input = input;
/* saa7111 has slightly different input numbering */
- if (state->ident == V4L2_IDENT_SAA7111) {
+ if (state->ident <= V4L2_IDENT_SAA7111A) {
if (input >= SAA7115_COMPOSITE4)
input -= 2;
/* saa7111 specific */
@@ -1292,7 +1292,7 @@ static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
{
struct saa711x_state *state = to_state(sd);
- if (state->ident != V4L2_IDENT_SAA7111)
+ if (state->ident > V4L2_IDENT_SAA7111A)
return -EINVAL;
saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
(val ? 0x80 : 0));
@@ -1596,6 +1596,10 @@ static int saa711x_probe(struct i2c_client *client,
switch (chip_id) {
case '1':
state->ident = V4L2_IDENT_SAA7111;
+ if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
+ v4l_info(client, "saa7111a variant found\n");
+ state->ident = V4L2_IDENT_SAA7111A;
+ }
break;
case '3':
state->ident = V4L2_IDENT_SAA7113;
@@ -1612,7 +1616,7 @@ static int saa711x_probe(struct i2c_client *client,
default:
state->ident = V4L2_IDENT_SAA7111;
v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
-
+ break;
}
state->audclk_freq = 48000;
@@ -1623,6 +1627,7 @@ static int saa711x_probe(struct i2c_client *client,
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
switch (state->ident) {
case V4L2_IDENT_SAA7111:
+ case V4L2_IDENT_SAA7111A:
saa711x_writeregs(sd, saa7111_init);
break;
case V4L2_IDENT_SAA7113:
@@ -1632,7 +1637,7 @@ static int saa711x_probe(struct i2c_client *client,
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
saa711x_writeregs(sd, saa7115_init_auto_input);
}
- if (state->ident != V4L2_IDENT_SAA7111)
+ if (state->ident > V4L2_IDENT_SAA7111A)
saa711x_writeregs(sd, saa7115_init_misc);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index 2fe7a701b95..250ef84cf5c 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -181,7 +181,7 @@ static const struct i2c_reg_value saa7127_init_config_common[] = {
#define SAA7127_60HZ_DAC_CONTROL 0x15
static const struct i2c_reg_value saa7127_init_config_60hz[] = {
{ SAA7127_REG_BURST_START, 0x19 },
- /* BURST_END is also used as a chip ID in saa7127_detect_client */
+ /* BURST_END is also used as a chip ID in saa7127_probe */
{ SAA7127_REG_BURST_END, 0x1d },
{ SAA7127_REG_CHROMA_PHASE, 0xa3 },
{ SAA7127_REG_GAINU, 0x98 },
@@ -200,10 +200,10 @@ static const struct i2c_reg_value saa7127_init_config_60hz[] = {
{ 0, 0 }
};
-#define SAA7127_50HZ_DAC_CONTROL 0x02
-static struct i2c_reg_value saa7127_init_config_50hz[] = {
+#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02
+static struct i2c_reg_value saa7127_init_config_50hz_pal[] = {
{ SAA7127_REG_BURST_START, 0x21 },
- /* BURST_END is also used as a chip ID in saa7127_detect_client */
+ /* BURST_END is also used as a chip ID in saa7127_probe */
{ SAA7127_REG_BURST_END, 0x1d },
{ SAA7127_REG_CHROMA_PHASE, 0x3f },
{ SAA7127_REG_GAINU, 0x7d },
@@ -222,6 +222,28 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = {
{ 0, 0 }
};
+#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08
+static struct i2c_reg_value saa7127_init_config_50hz_secam[] = {
+ { SAA7127_REG_BURST_START, 0x21 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0x3f },
+ { SAA7127_REG_GAINU, 0x6a },
+ { SAA7127_REG_GAINV, 0x81 },
+ { SAA7127_REG_BLACK_LEVEL, 0x33 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x35 },
+ { SAA7127_REG_VBI_BLANKING, 0x35 },
+ { SAA7127_REG_DAC_CONTROL, 0x08 },
+ { SAA7127_REG_BURST_AMP, 0x2f },
+ { SAA7127_REG_SUBC3, 0xb2 },
+ { SAA7127_REG_SUBC2, 0x3b },
+ { SAA7127_REG_SUBC1, 0xa3 },
+ { SAA7127_REG_SUBC0, 0x28 },
+ { SAA7127_REG_MULTI, 0x90 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x00 },
+ { 0, 0 }
+};
+
/*
**********************************************************************
*
@@ -463,10 +485,21 @@ static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
inittab = saa7127_init_config_60hz;
state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
+
+ } else if (state->ident == V4L2_IDENT_SAA7129 &&
+ (std & V4L2_STD_SECAM) &&
+ !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) {
+
+ /* If and only if SECAM, with a SAA712[89] */
+ v4l2_dbg(1, debug, sd,
+ "Selecting 50 Hz SECAM video Standard\n");
+ inittab = saa7127_init_config_50hz_secam;
+ state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL;
+
} else {
- v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n");
- inittab = saa7127_init_config_50hz;
- state->reg_61 = SAA7127_50HZ_DAC_CONTROL;
+ v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n");
+ inittab = saa7127_init_config_50hz_pal;
+ state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL;
}
/* Write Table */
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 03f572708b8..297833fb3b4 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -4160,7 +4160,7 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE2,
},
},
- [SAA7134_BOARD_BEHOLD_505RDS] = {
+ [SAA7134_BOARD_BEHOLD_505RDS_MK5] = {
/* Beholder Intl. Ltd. 2008 */
/*Dmitry Belimov <d.belimov@gmail.com> */
.name = "Beholder BeholdTV 505 RDS",
@@ -5320,6 +5320,41 @@ struct saa7134_board saa7134_boards[] = {
.vmux = 8,
} },
},
+ [SAA7134_BOARD_BEHOLD_505RDS_MK3] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 505 RDS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
};
@@ -6235,7 +6270,13 @@ struct pci_device_id saa7134_pci_tbl[] = {
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = 0x0000,
.subdevice = 0x505B,
- .driver_data = SAA7134_BOARD_BEHOLD_505RDS,
+ .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x5051,
+ .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
@@ -6792,7 +6833,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_BEHOLD_407FM:
case SAA7134_BOARD_BEHOLD_409:
case SAA7134_BOARD_BEHOLD_505FM:
- case SAA7134_BOARD_BEHOLD_505RDS:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK3:
case SAA7134_BOARD_BEHOLD_507_9FM:
case SAA7134_BOARD_BEHOLD_507RDS_MK3:
case SAA7134_BOARD_BEHOLD_507RDS_MK5:
@@ -6953,8 +6995,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_VIDEOMATE_S350:
dev->has_remote = SAA7134_REMOTE_GPIO;
- saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000);
- saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0000C000, 0x0000C000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0000C000, 0x0000C000);
break;
}
return 0;
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index f8e985989ca..9499000f66b 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -460,7 +460,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
int polling = 0;
int rc5_gpio = 0;
int nec_gpio = 0;
- int ir_type = IR_TYPE_OTHER;
+ u64 ir_type = IR_TYPE_OTHER;
int err;
if (dev->has_remote != SAA7134_REMOTE_GPIO)
@@ -568,7 +568,8 @@ int saa7134_input_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_BEHOLD_407FM:
case SAA7134_BOARD_BEHOLD_409:
case SAA7134_BOARD_BEHOLD_505FM:
- case SAA7134_BOARD_BEHOLD_505RDS:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK3:
case SAA7134_BOARD_BEHOLD_507_9FM:
case SAA7134_BOARD_BEHOLD_507RDS_MK3:
case SAA7134_BOARD_BEHOLD_507RDS_MK5:
@@ -728,7 +729,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
dev->remote = ir;
saa7134_ir_start(dev, ir);
- err = ir_input_register(ir->dev, ir_codes);
+ err = ir_input_register(ir->dev, ir_codes, NULL);
if (err)
goto err_out_stop;
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index cb732640ac4..31138d3e51b 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -205,7 +205,7 @@ static struct saa7134_format formats[] = {
#define NORM_525_60 \
.h_start = 0, \
- .h_stop = 703, \
+ .h_stop = 719, \
.video_v_start = 23, \
.video_v_stop = 262, \
.vbi_v_start_0 = 10, \
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 53b7e0b8a2f..bf130967ed1 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -282,7 +282,7 @@ struct saa7134_format {
#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156
#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
-#define SAA7134_BOARD_BEHOLD_505RDS 159
+#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159
#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160
#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161
#define SAA7134_BOARD_BEHOLD_607FM_MK5 162
@@ -299,6 +299,7 @@ struct saa7134_format {
#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173
#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174
#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
+#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c
index 6f094a96ac8..1d487c15034 100644
--- a/drivers/media/video/saa7164/saa7164-api.c
+++ b/drivers/media/video/saa7164/saa7164-api.c
@@ -523,7 +523,7 @@ int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
}
reglen = saa7164_i2caddr_to_reglen(bus, addr);
- if (unitid < 0) {
+ if (reglen < 0) {
printk(KERN_ERR
"%s() error, cannot translate regaddr to reglen\n",
__func__);
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index f09c7140d6b..fb88c63188f 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -1748,6 +1748,22 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
icd);
}
+static int sh_mobile_ceu_get_parm(struct soc_camera_device *icd,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, video, g_parm, parm);
+}
+
+static int sh_mobile_ceu_set_parm(struct soc_camera_device *icd,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, video, s_parm, parm);
+}
+
static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
struct v4l2_control *ctrl)
{
@@ -1808,6 +1824,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.try_fmt = sh_mobile_ceu_try_fmt,
.set_ctrl = sh_mobile_ceu_set_ctrl,
.get_ctrl = sh_mobile_ceu_get_ctrl,
+ .set_parm = sh_mobile_ceu_set_parm,
+ .get_parm = sh_mobile_ceu_get_parm,
.reqbufs = sh_mobile_ceu_reqbufs,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig
index f71f272776d..6ebaf2940d0 100644
--- a/drivers/media/video/sn9c102/Kconfig
+++ b/drivers/media/video/sn9c102/Kconfig
@@ -1,7 +1,10 @@
config USB_SN9C102
- tristate "USB SN9C1xx PC Camera Controller support"
+ tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)"
depends on VIDEO_V4L2
---help---
+ This driver is DEPRECATED please use the gspca sonixb and
+ sonixj modules instead.
+
Say Y here if you want support for cameras based on SONiX SN9C101,
SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers.
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index 36ee43a9ee9..cc40d6ba9f2 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -45,20 +45,24 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), },
#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), },
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), },
/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), },
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), },
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), },
#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), },
-#endif
/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), },
/* SN9C103 */
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 6b3fbcca774..80f6bfa2632 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -781,6 +781,32 @@ static int soc_camera_s_crop(struct file *file, void *fh,
return ret;
}
+static int soc_camera_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+
+ if (ici->ops->get_parm)
+ return ici->ops->get_parm(icd, a);
+
+ return -ENOIOCTLCMD;
+}
+
+static int soc_camera_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+
+ if (ici->ops->set_parm)
+ return ici->ops->set_parm(icd, a);
+
+ return -ENOIOCTLCMD;
+}
+
static int soc_camera_g_chip_ident(struct file *file, void *fh,
struct v4l2_dbg_chip_ident *id)
{
@@ -846,10 +872,8 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
struct v4l2_subdev *subdev;
- int ret;
if (!adap) {
- ret = -ENODEV;
dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n",
icl->i2c_adapter_id);
goto ei2cga;
@@ -859,10 +883,8 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
icl->module_name, icl->board_info, NULL);
- if (!subdev) {
- ret = -ENOMEM;
+ if (!subdev)
goto ei2cnd;
- }
client = subdev->priv;
@@ -873,7 +895,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
ei2cnd:
i2c_put_adapter(adap);
ei2cga:
- return ret;
+ return -ENODEV;
}
static void soc_camera_free_i2c(struct soc_camera_device *icd)
@@ -1260,6 +1282,8 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_cropcap = soc_camera_cropcap,
.vidioc_g_crop = soc_camera_g_crop,
.vidioc_s_crop = soc_camera_s_crop,
+ .vidioc_g_parm = soc_camera_g_parm,
+ .vidioc_s_parm = soc_camera_s_parm,
.vidioc_g_chip_ident = soc_camera_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = soc_camera_g_register,
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index f8d5c87dc2a..8b63b6545e7 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -24,91 +24,106 @@ static const struct soc_mbus_pixelfmt mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(YVYU8_2X8_LE)] = {
+ },
+ [MBUS_IDX(YVYU8_2X8_LE)] = {
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YVYU",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(YUYV8_2X8_BE)] = {
+ },
+ [MBUS_IDX(YUYV8_2X8_BE)] = {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(YVYU8_2X8_BE)] = {
+ },
+ [MBUS_IDX(YVYU8_2X8_BE)] = {
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "VYUY",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(RGB555_2X8_PADHI_LE)] = {
+ },
+ [MBUS_IDX(RGB555_2X8_PADHI_LE)] = {
.fourcc = V4L2_PIX_FMT_RGB555,
.name = "RGB555",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(RGB555_2X8_PADHI_BE)] = {
+ },
+ [MBUS_IDX(RGB555_2X8_PADHI_BE)] = {
.fourcc = V4L2_PIX_FMT_RGB555X,
.name = "RGB555X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(RGB565_2X8_LE)] = {
+ },
+ [MBUS_IDX(RGB565_2X8_LE)] = {
.fourcc = V4L2_PIX_FMT_RGB565,
.name = "RGB565",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(RGB565_2X8_BE)] = {
+ },
+ [MBUS_IDX(RGB565_2X8_BE)] = {
.fourcc = V4L2_PIX_FMT_RGB565X,
.name = "RGB565X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(SBGGR8_1X8)] = {
+ },
+ [MBUS_IDX(SBGGR8_1X8)] = {
.fourcc = V4L2_PIX_FMT_SBGGR8,
.name = "Bayer 8 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(SBGGR10_1X10)] = {
+ },
+ [MBUS_IDX(SBGGR10_1X10)] = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(GREY8_1X8)] = {
+ },
+ [MBUS_IDX(GREY8_1X8)] = {
.fourcc = V4L2_PIX_FMT_GREY,
.name = "Grey",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(Y10_1X10)] = {
+ },
+ [MBUS_IDX(Y10_1X10)] = {
.fourcc = V4L2_PIX_FMT_Y10,
.name = "Grey 10bit",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(SBGGR10_2X8_PADHI_LE)] = {
+ },
+ [MBUS_IDX(SBGGR10_2X8_PADHI_LE)] = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(SBGGR10_2X8_PADLO_LE)] = {
+ },
+ [MBUS_IDX(SBGGR10_2X8_PADLO_LE)] = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADLO,
.order = SOC_MBUS_ORDER_LE,
- }, [MBUS_IDX(SBGGR10_2X8_PADHI_BE)] = {
+ },
+ [MBUS_IDX(SBGGR10_2X8_PADHI_BE)] = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
- }, [MBUS_IDX(SBGGR10_2X8_PADLO_BE)] = {
+ },
+ [MBUS_IDX(SBGGR10_2X8_PADLO_BE)] = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
@@ -134,7 +149,8 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line);
const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
enum v4l2_mbus_pixelcode code)
{
- if ((unsigned int)(code - V4L2_MBUS_FMT_FIXED) > ARRAY_SIZE(mbus_fmt))
+ if (code - V4L2_MBUS_FMT_FIXED > ARRAY_SIZE(mbus_fmt) ||
+ code <= V4L2_MBUS_FMT_FIXED)
return NULL;
return mbus_fmt + code - V4L2_MBUS_FMT_FIXED - 1;
}
diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig
new file mode 100644
index 00000000000..2c29ec659b4
--- /dev/null
+++ b/drivers/media/video/tlg2300/Kconfig
@@ -0,0 +1,16 @@
+config VIDEO_TLG2300
+ tristate "Telegent TLG2300 USB video capture support"
+ depends on VIDEO_DEV && I2C && INPUT && SND && DVB_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_IR
+ select VIDEOBUF_VMALLOC
+ select SND_PCM
+ select VIDEOBUF_DVB
+
+ ---help---
+ This is a video4linux driver for Telegent tlg2300 based TV cards.
+ The driver supports V4L2, DVB-T and radio.
+
+ To compile this driver as a module, choose M here: the
+ module will be called poseidon
diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile
new file mode 100644
index 00000000000..81bb7fdd1e3
--- /dev/null
+++ b/drivers/media/video/tlg2300/Makefile
@@ -0,0 +1,9 @@
+poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
+
+obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/video/tlg2300/pd-alsa.c b/drivers/media/video/tlg2300/pd-alsa.c
new file mode 100644
index 00000000000..6f42621ad47
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-alsa.c
@@ -0,0 +1,332 @@
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static void complete_handler_audio(struct urb *urb);
+#define AUDIO_EP (0x83)
+#define AUDIO_BUF_SIZE (512)
+#define PERIOD_SIZE (1024 * 8)
+#define PERIOD_MIN (4)
+#define PERIOD_MAX PERIOD_MIN
+
+static struct snd_pcm_hardware snd_pd_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
+ .period_bytes_min = PERIOD_SIZE,
+ .period_bytes_max = PERIOD_SIZE,
+ .periods_min = PERIOD_MIN,
+ .periods_max = PERIOD_MAX,
+ /*
+ .buffer_bytes_max = 62720 * 8,
+ .period_bytes_min = 64,
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98
+ */
+};
+
+static int snd_pd_capture_open(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (!p)
+ return -ENODEV;
+ pa->users++;
+ pa->card_close = 0;
+ pa->capture_pcm_substream = substream;
+ runtime->private_data = p;
+
+ runtime->hw = snd_pd_hw_capture;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ usb_autopm_get_interface(p->interface);
+ kref_get(&p->kref);
+ return 0;
+}
+
+static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+
+ pa->users--;
+ pa->card_close = 1;
+ usb_autopm_put_interface(p->interface);
+ kref_put(&p->kref, poseidon_delete);
+ return 0;
+}
+
+static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int size;
+
+ size = params_buffer_bytes(hw_params);
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+ else
+ runtime->dma_bytes = size;
+ return 0;
+}
+
+static int audio_buf_free(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+ int i;
+
+ for (i = 0; i < AUDIO_BUFS; i++)
+ if (pa->urb_array[i])
+ usb_kill_urb(pa->urb_array[i]);
+ free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
+ logpm();
+ return 0;
+}
+
+static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+
+ logpm();
+ audio_buf_free(p);
+ return 0;
+}
+
+static int snd_pd_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+#define AUDIO_TRAILER_SIZE (16)
+static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
+{
+ struct poseidon_audio *pa = urb->context;
+ struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
+
+ int stride = runtime->frame_bits >> 3;
+ int len = urb->actual_length / stride;
+ unsigned char *cp = urb->transfer_buffer;
+ unsigned int oldptr = pa->rcv_position;
+
+ if (urb->actual_length == AUDIO_BUF_SIZE - 4)
+ len -= (AUDIO_TRAILER_SIZE / stride);
+
+ /* do the copy */
+ if (oldptr + len >= runtime->buffer_size) {
+ unsigned int cnt = runtime->buffer_size - oldptr;
+
+ memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
+ memcpy(runtime->dma_area, (cp + cnt * stride),
+ (len * stride - cnt * stride));
+ } else
+ memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+
+ /* update the statas */
+ snd_pcm_stream_lock(pa->capture_pcm_substream);
+ pa->rcv_position += len;
+ if (pa->rcv_position >= runtime->buffer_size)
+ pa->rcv_position -= runtime->buffer_size;
+
+ pa->copied_position += (len);
+ if (pa->copied_position >= runtime->period_size) {
+ pa->copied_position -= runtime->period_size;
+ *period_elapsed = 1;
+ }
+ snd_pcm_stream_unlock(pa->capture_pcm_substream);
+}
+
+static void complete_handler_audio(struct urb *urb)
+{
+ struct poseidon_audio *pa = urb->context;
+ struct snd_pcm_substream *substream = pa->capture_pcm_substream;
+ int period_elapsed = 0;
+ int ret;
+
+ if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
+ return;
+
+ if (urb->status != 0) {
+ /*if (urb->status == -ESHUTDOWN)*/
+ return;
+ }
+
+ if (substream) {
+ if (urb->actual_length) {
+ handle_audio_data(urb, &period_elapsed);
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ log("audio urb failed (errcod = %i)", ret);
+ return;
+}
+
+static int fire_audio_urb(struct poseidon *p)
+{
+ int i, ret = 0;
+ struct poseidon_audio *pa = &p->audio;
+
+ alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
+ p->udev, AUDIO_EP,
+ AUDIO_BUF_SIZE, GFP_ATOMIC,
+ complete_handler_audio, pa);
+
+ for (i = 0; i < AUDIO_BUFS; i++) {
+ ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
+ if (ret)
+ log("urb err : %d", ret);
+ }
+ log();
+ return ret;
+}
+
+static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+
+ if (debug_mode)
+ log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ if (pa->capture_stream == STREAM_ON)
+ return 0;
+
+ pa->rcv_position = pa->copied_position = 0;
+ pa->capture_stream = STREAM_ON;
+
+ if (in_hibernation(p))
+ return 0;
+ fire_audio_urb(p);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pa->capture_stream = STREAM_SUSPEND;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pa->capture_stream = STREAM_OFF;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t
+snd_pd_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+ return pa->rcv_position;
+}
+
+static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = snd_pd_capture_open,
+ .close = snd_pd_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_pd_hw_capture_params,
+ .hw_free = snd_pd_hw_capture_free,
+ .prepare = snd_pd_prepare,
+ .trigger = snd_pd_capture_trigger,
+ .pointer = snd_pd_capture_pointer,
+ .page = snd_pcm_pd_get_page,
+};
+
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *p)
+{
+ logpm(p);
+ audio_buf_free(p);
+ return 0;
+}
+
+int pm_alsa_resume(struct poseidon *p)
+{
+ logpm(p);
+ fire_audio_urb(p);
+ return 0;
+}
+#endif
+
+int poseidon_audio_init(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int ret;
+
+ ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
+ if (ret != 0)
+ return ret;
+
+ ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+ pcm->info_flags = 0;
+ pcm->private_data = p;
+ strcpy(pcm->name, "poseidon audio capture");
+
+ strcpy(card->driver, "ALSA driver");
+ strcpy(card->shortname, "poseidon Audio");
+ strcpy(card->longname, "poseidon ALSA Audio");
+
+ if (snd_card_register(card)) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+ pa->card = card;
+ return 0;
+}
+
+int poseidon_audio_free(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+
+ if (pa->card)
+ snd_card_free(pa->card);
+ return 0;
+}
diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h
new file mode 100644
index 00000000000..46066bdc73f
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-common.h
@@ -0,0 +1,282 @@
+#ifndef PD_COMMON_H
+#define PD_COMMON_H
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/videodev2.h>
+#include <linux/semaphore.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+#define SBUF_NUM 8
+#define MAX_BUFFER_NUM 6
+#define PK_PER_URB 32
+#define ISO_PKT_SIZE 3072
+
+#define POSEIDON_STATE_NONE (0x0000)
+#define POSEIDON_STATE_ANALOG (0x0001)
+#define POSEIDON_STATE_FM (0x0002)
+#define POSEIDON_STATE_DVBT (0x0004)
+#define POSEIDON_STATE_VBI (0x0008)
+#define POSEIDON_STATE_DISCONNECT (0x0080)
+
+#define PM_SUSPEND_DELAY 3
+
+#define V4L_PAL_VBI_LINES 18
+#define V4L_NTSC_VBI_LINES 12
+#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2)
+#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2)
+
+#define TUNER_FREQ_MIN (45000000)
+#define TUNER_FREQ_MAX (862000000)
+
+struct vbi_data {
+ struct video_device *v_dev;
+ struct video_data *video;
+ struct front_face *front;
+
+ unsigned int copied;
+ unsigned int vbi_size; /* the whole size of two fields */
+ int users;
+};
+
+/*
+ * This is the running context of the video, it is useful for
+ * resume()
+ */
+struct running_context {
+ u32 freq; /* VIDIOC_S_FREQUENCY */
+ int audio_idx; /* VIDIOC_S_TUNER */
+ v4l2_std_id tvnormid; /* VIDIOC_S_STD */
+ int sig_index; /* VIDIOC_S_INPUT */
+ struct v4l2_pix_format pix; /* VIDIOC_S_FMT */
+};
+
+struct video_data {
+ /* v4l2 video device */
+ struct video_device *v_dev;
+
+ /* the working context */
+ struct running_context context;
+
+ /* for data copy */
+ int field_count;
+
+ char *dst;
+ int lines_copied;
+ int prev_left;
+
+ int lines_per_field;
+ int lines_size;
+
+ /* for communication */
+ u8 endpoint_addr;
+ struct urb *urb_array[SBUF_NUM];
+ struct vbi_data *vbi;
+ struct poseidon *pd;
+ struct front_face *front;
+
+ int is_streaming;
+ int users;
+
+ /* for bubble handler */
+ struct work_struct bubble_work;
+};
+
+enum pcm_stream_state {
+ STREAM_OFF,
+ STREAM_ON,
+ STREAM_SUSPEND,
+};
+
+#define AUDIO_BUFS (3)
+#define CAPTURE_STREAM_EN 1
+struct poseidon_audio {
+ struct urb *urb_array[AUDIO_BUFS];
+ unsigned int copied_position;
+ struct snd_pcm_substream *capture_pcm_substream;
+
+ unsigned int rcv_position;
+ struct snd_card *card;
+ int card_close;
+
+ int users;
+ int pm_state;
+ enum pcm_stream_state capture_stream;
+};
+
+struct radio_data {
+ __u32 fm_freq;
+ int users;
+ unsigned int is_radio_streaming;
+ int pre_emphasis;
+ struct video_device *fm_dev;
+};
+
+#define DVB_SBUF_NUM 4
+#define DVB_URB_BUF_SIZE 0x2000
+struct pd_dvb_adapter {
+ struct dvb_adapter dvb_adap;
+ struct dvb_frontend dvb_fe;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+
+ atomic_t users;
+ atomic_t active_feed;
+
+ /* data transfer */
+ s32 is_streaming;
+ struct urb *urb_array[DVB_SBUF_NUM];
+ struct poseidon *pd_device;
+ u8 ep_addr;
+ u8 reserved[3];
+
+ /* data for power resume*/
+ struct dvb_frontend_parameters fe_param;
+
+ /* for channel scanning */
+ int prev_freq;
+ int bandwidth;
+ unsigned long last_jiffies;
+};
+
+struct front_face {
+ /* use this field to distinguish VIDEO and VBI */
+ enum v4l2_buf_type type;
+
+ /* for host */
+ struct videobuf_queue q;
+
+ /* the bridge for host and device */
+ struct videobuf_buffer *curr_frame;
+
+ /* for device */
+ spinlock_t queue_lock;
+ struct list_head active;
+ struct poseidon *pd;
+};
+
+struct poseidon {
+ struct list_head device_list;
+
+ struct mutex lock;
+ struct kref kref;
+
+ /* for V4L2 */
+ struct v4l2_device v4l2_dev;
+
+ /* hardware info */
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ int cur_transfer_mode;
+
+ struct video_data video_data; /* video */
+ struct vbi_data vbi_data; /* vbi */
+ struct poseidon_audio audio; /* audio (alsa) */
+ struct radio_data radio_data; /* FM */
+ struct pd_dvb_adapter dvb_data; /* DVB */
+
+ u32 state;
+ struct file *file_for_stream; /* the active stream*/
+
+#ifdef CONFIG_PM
+ int (*pm_suspend)(struct poseidon *);
+ int (*pm_resume)(struct poseidon *);
+ pm_message_t msg;
+
+ struct work_struct pm_work;
+ u8 portnum;
+#endif
+};
+
+struct poseidon_format {
+ char *name;
+ int fourcc; /* video4linux 2 */
+ int depth; /* bit/pixel */
+ int flags;
+};
+
+struct poseidon_tvnorm {
+ v4l2_std_id v4l2_id;
+ char name[12];
+ u32 tlg_tvnorm;
+};
+
+/* video */
+int pd_video_init(struct poseidon *);
+void pd_video_exit(struct poseidon *);
+int stop_all_video_stream(struct poseidon *);
+
+/* alsa audio */
+int poseidon_audio_init(struct poseidon *);
+int poseidon_audio_free(struct poseidon *);
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *);
+int pm_alsa_resume(struct poseidon *);
+#endif
+
+/* dvb */
+int pd_dvb_usb_device_init(struct poseidon *);
+void pd_dvb_usb_device_exit(struct poseidon *);
+void pd_dvb_usb_device_cleanup(struct poseidon *);
+int pd_dvb_get_adapter_num(struct pd_dvb_adapter *);
+void dvb_stop_streaming(struct pd_dvb_adapter *);
+
+/* FM */
+int poseidon_fm_init(struct poseidon *);
+int poseidon_fm_exit(struct poseidon *);
+struct video_device *vdev_init(struct poseidon *, struct video_device *);
+
+/* vendor command ops */
+int send_set_req(struct poseidon*, u8, s32, s32*);
+int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32);
+s32 set_tuner_mode(struct poseidon*, unsigned char);
+
+/* bulk urb alloc/free */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+ struct usb_device *udev, u8 ep_addr,
+ int buf_size, gfp_t gfp_flags,
+ usb_complete_t complete_fn, void *context);
+void free_all_urb_generic(struct urb **urb_array, int num);
+
+/* misc */
+void poseidon_delete(struct kref *kref);
+void destroy_video_device(struct video_device **v_dev);
+extern int debug_mode;
+void set_debug_mode(struct video_device *vfd, int debug_mode);
+
+#ifdef CONFIG_PM
+#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
+#else
+#define in_hibernation(pd) (0)
+#endif
+#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt))
+
+#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \
+ __func__, __LINE__, ## __VA_ARGS__)
+
+/* for power management */
+#define logpm(pd) do {\
+ if (debug_mode & 0x10)\
+ log();\
+ } while (0)
+
+#define logs(f) do { \
+ if ((debug_mode & 0x4) && \
+ (f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \
+ log("type : VBI");\
+ \
+ if ((debug_mode & 0x8) && \
+ (f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \
+ log("type : VIDEO");\
+ } while (0)
+#endif
diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c
new file mode 100644
index 00000000000..4133aee568b
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-dvb.c
@@ -0,0 +1,593 @@
+#include "pd-common.h"
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/dvb/dmx.h>
+#include <linux/delay.h>
+
+#include "vendorcmds.h"
+#include <linux/sched.h>
+#include <asm/atomic.h>
+
+static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
+
+static int dvb_bandwidth[][2] = {
+ { TLG_BW_8, BANDWIDTH_8_MHZ },
+ { TLG_BW_7, BANDWIDTH_7_MHZ },
+ { TLG_BW_6, BANDWIDTH_6_MHZ }
+};
+static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth);
+
+static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb);
+static int poseidon_check_mode_dvbt(struct poseidon *pd)
+{
+ s32 ret = 0, cmd_status = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+
+ ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE);
+ if (ret != 0)
+ return ret;
+
+ ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T);
+ if (ret)
+ return ret;
+
+ /* signal source */
+ ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status);
+ if (ret|cmd_status)
+ return ret;
+
+ return 0;
+}
+
+/* acquire :
+ * 1 == open
+ * 0 == release
+ */
+static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb;
+ int ret = 0;
+
+ if (!pd)
+ return -ENODEV;
+
+ pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe);
+ if (acquire) {
+ mutex_lock(&pd->lock);
+ if (pd->state & POSEIDON_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ goto open_out;
+ }
+
+ if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) {
+ ret = -EBUSY;
+ goto open_out;
+ }
+
+ usb_autopm_get_interface(pd->interface);
+ if (0 == pd->state) {
+ ret = poseidon_check_mode_dvbt(pd);
+ if (ret < 0) {
+ usb_autopm_put_interface(pd->interface);
+ goto open_out;
+ }
+ pd->state |= POSEIDON_STATE_DVBT;
+ pd_dvb->bandwidth = 0;
+ pd_dvb->prev_freq = 0;
+ }
+ atomic_inc(&pd_dvb->users);
+ kref_get(&pd->kref);
+open_out:
+ mutex_unlock(&pd->lock);
+ } else {
+ dvb_stop_streaming(pd_dvb);
+
+ if (atomic_dec_and_test(&pd_dvb->users)) {
+ mutex_lock(&pd->lock);
+ pd->state &= ~POSEIDON_STATE_DVBT;
+ mutex_unlock(&pd->lock);
+ }
+ kref_put(&pd->kref, poseidon_delete);
+ usb_autopm_put_interface(pd->interface);
+ }
+ return ret;
+}
+
+static void poseidon_fe_release(struct dvb_frontend *fe)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+
+#ifdef CONFIG_PM
+ pd->pm_suspend = NULL;
+ pd->pm_resume = NULL;
+#endif
+}
+
+static s32 poseidon_fe_sleep(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+/*
+ * return true if we can satisfy the conditions, else return false.
+ */
+static bool check_scan_ok(__u32 freq, int bandwidth,
+ struct pd_dvb_adapter *adapter)
+{
+ if (bandwidth < 0)
+ return false;
+
+ if (adapter->prev_freq == freq
+ && adapter->bandwidth == bandwidth) {
+ long nl = jiffies - adapter->last_jiffies;
+ unsigned int msec ;
+
+ msec = jiffies_to_msecs(abs(nl));
+ return msec > 15000 ? true : false;
+ }
+ return true;
+}
+
+/*
+ * Check if the firmware delays too long for an invalid frequency.
+ */
+static int fw_delay_overflow(struct pd_dvb_adapter *adapter)
+{
+ long nl = jiffies - adapter->last_jiffies;
+ unsigned int msec ;
+
+ msec = jiffies_to_msecs(abs(nl));
+ return msec > 800 ? true : false;
+}
+
+static int poseidon_set_fe(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ s32 ret = 0, cmd_status = 0;
+ s32 i, bandwidth = -1;
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ if (in_hibernation(pd))
+ return -EBUSY;
+
+ mutex_lock(&pd->lock);
+ for (i = 0; i < dvb_bandwidth_length; i++)
+ if (fep->u.ofdm.bandwidth == dvb_bandwidth[i][1])
+ bandwidth = dvb_bandwidth[i][0];
+
+ if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) {
+ ret = send_set_req(pd, TUNE_FREQ_SELECT,
+ fep->frequency / 1000, &cmd_status);
+ if (ret | cmd_status) {
+ log("error line");
+ goto front_out;
+ }
+
+ ret = send_set_req(pd, DVBT_BANDW_SEL,
+ bandwidth, &cmd_status);
+ if (ret | cmd_status) {
+ log("error line");
+ goto front_out;
+ }
+
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ if (ret | cmd_status) {
+ log("error line");
+ goto front_out;
+ }
+
+ /* save the context for future */
+ memcpy(&pd_dvb->fe_param, fep, sizeof(*fep));
+ pd_dvb->bandwidth = bandwidth;
+ pd_dvb->prev_freq = fep->frequency;
+ pd_dvb->last_jiffies = jiffies;
+ }
+front_out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_dvb_suspend(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+ dvb_stop_streaming(pd_dvb);
+ dvb_urb_cleanup(pd_dvb);
+ msleep(500);
+ return 0;
+}
+
+static int pm_dvb_resume(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ poseidon_check_mode_dvbt(pd);
+ msleep(300);
+ poseidon_set_fe(&pd_dvb->dvb_fe, &pd_dvb->fe_param);
+
+ dvb_start_streaming(pd_dvb);
+ return 0;
+}
+#endif
+
+static s32 poseidon_fe_init(struct dvb_frontend *fe)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+#ifdef CONFIG_PM
+ pd->pm_suspend = pm_dvb_suspend;
+ pd->pm_resume = pm_dvb_resume;
+#endif
+ memset(&pd_dvb->fe_param, 0,
+ sizeof(struct dvb_frontend_parameters));
+ return 0;
+}
+
+static int poseidon_get_fe(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ memcpy(fep, &pd_dvb->fe_param, sizeof(*fep));
+ return 0;
+}
+
+static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ s32 ret = -1, cmd_status;
+ struct tuner_dtv_sig_stat_s status = {};
+
+ if (in_hibernation(pd))
+ return -EBUSY;
+ mutex_lock(&pd->lock);
+
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
+ &status, &cmd_status, sizeof(status));
+ if (ret | cmd_status) {
+ log("get tuner status error");
+ goto out;
+ }
+
+ if (debug_mode)
+ log("P : %d, L %d, LB :%d", status.sig_present,
+ status.sig_locked, status.sig_lock_busy);
+
+ if (status.sig_lock_busy) {
+ goto out;
+ } else if (status.sig_present || status.sig_locked) {
+ *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER
+ | FE_HAS_SYNC | FE_HAS_VITERBI;
+ } else {
+ if (fw_delay_overflow(&pd->dvb_data))
+ *stat |= FE_TIMEDOUT;
+ }
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct tuner_ber_rate_s tlg_ber = {};
+ s32 ret = -1, cmd_status;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_BER_RATE, 0,
+ &tlg_ber, &cmd_status, sizeof(tlg_ber));
+ if (ret | cmd_status)
+ goto out;
+ *ber = tlg_ber.ber_rate;
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct tuner_dtv_sig_stat_s status = {};
+ s32 ret = 0, cmd_status;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
+ &status, &cmd_status, sizeof(status));
+ if (ret | cmd_status)
+ goto out;
+ if ((status.sig_present || status.sig_locked) && !status.sig_strength)
+ *strength = 0xFFFF;
+ else
+ *strength = status.sig_strength;
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ return 0;
+}
+
+static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ *unc = 0;
+ return 0;
+}
+
+static struct dvb_frontend_ops poseidon_frontend_ops = {
+ .info = {
+ .name = "Poseidon DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 62500,/* FIXME */
+ .caps = FE_CAN_INVERSION_AUTO |
+ 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_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = poseidon_fe_release,
+
+ .init = poseidon_fe_init,
+ .sleep = poseidon_fe_sleep,
+
+ .set_frontend = poseidon_set_fe,
+ .get_frontend = poseidon_get_fe,
+ .get_tune_settings = poseidon_fe_get_tune_settings,
+
+ .read_status = poseidon_read_status,
+ .read_ber = poseidon_read_ber,
+ .read_signal_strength = poseidon_read_signal_strength,
+ .read_snr = poseidon_read_snr,
+ .read_ucblocks = poseidon_read_unc_blocks,
+
+ .ts_bus_ctrl = poseidon_ts_bus_ctrl,
+};
+
+static void dvb_urb_irq(struct urb *urb)
+{
+ struct pd_dvb_adapter *pd_dvb = urb->context;
+ int len = urb->transfer_buffer_length;
+ struct dvb_demux *demux = &pd_dvb->demux;
+ s32 ret;
+
+ if (!pd_dvb->is_streaming || urb->status) {
+ if (urb->status == -EPROTO)
+ goto resend;
+ return;
+ }
+
+ if (urb->actual_length == len)
+ dvb_dmx_swfilter(demux, urb->transfer_buffer, len);
+ else if (urb->actual_length == len - 4) {
+ int offset;
+ u8 *buf = urb->transfer_buffer;
+
+ /*
+ * The packet size is 512,
+ * last packet contains 456 bytes tsp data
+ */
+ for (offset = 456; offset < len; offset += 512) {
+ if (!strncmp(buf + offset, "DVHS", 4)) {
+ dvb_dmx_swfilter(demux, buf, offset);
+ if (len > offset + 52 + 4) {
+ /*16 bytes trailer + 36 bytes padding */
+ buf += offset + 52;
+ len -= offset + 52 + 4;
+ dvb_dmx_swfilter(demux, buf, len);
+ }
+ break;
+ }
+ }
+ }
+
+resend:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log(" usb_submit_urb failed: error %d", ret);
+}
+
+static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb)
+{
+ if (pd_dvb->urb_array[0])
+ return 0;
+
+ alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM,
+ pd_dvb->pd_device->udev, pd_dvb->ep_addr,
+ DVB_URB_BUF_SIZE, GFP_KERNEL,
+ dvb_urb_irq, pd_dvb);
+ return 0;
+}
+
+static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb)
+{
+ free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM);
+}
+
+static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb)
+{
+ struct poseidon *pd = pd_dvb->pd_device;
+ int ret = 0;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ mutex_lock(&pd->lock);
+ if (!pd_dvb->is_streaming) {
+ s32 i, cmd_status = 0;
+ /*
+ * Once upon a time, there was a difficult bug lying here.
+ * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ */
+
+ ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status);
+ if (ret | cmd_status)
+ goto out;
+
+ ret = dvb_urb_init(pd_dvb);
+ if (ret < 0)
+ goto out;
+
+ pd_dvb->is_streaming = 1;
+ for (i = 0; i < DVB_SBUF_NUM; i++) {
+ ret = usb_submit_urb(pd_dvb->urb_array[i],
+ GFP_KERNEL);
+ if (ret) {
+ log(" submit urb error %d", ret);
+ goto out;
+ }
+ }
+ }
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb)
+{
+ struct poseidon *pd = pd_dvb->pd_device;
+
+ mutex_lock(&pd->lock);
+ if (pd_dvb->is_streaming) {
+ s32 i, ret, cmd_status = 0;
+
+ pd_dvb->is_streaming = 0;
+
+ for (i = 0; i < DVB_SBUF_NUM; i++)
+ if (pd_dvb->urb_array[i])
+ usb_kill_urb(pd_dvb->urb_array[i]);
+
+ ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+ if (ret | cmd_status)
+ log("error");
+ }
+ mutex_unlock(&pd->lock);
+}
+
+static int pd_start_feed(struct dvb_demux_feed *feed)
+{
+ struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
+ int ret = 0;
+
+ if (!pd_dvb)
+ return -1;
+ if (atomic_inc_return(&pd_dvb->active_feed) == 1)
+ ret = dvb_start_streaming(pd_dvb);
+ return ret;
+}
+
+static int pd_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
+
+ if (!pd_dvb)
+ return -1;
+ if (atomic_dec_and_test(&pd_dvb->active_feed))
+ dvb_stop_streaming(pd_dvb);
+ return 0;
+}
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+int pd_dvb_usb_device_init(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+ struct dvb_demux *dvbdemux;
+ int ret = 0;
+
+ pd_dvb->ep_addr = 0x82;
+ atomic_set(&pd_dvb->users, 0);
+ atomic_set(&pd_dvb->active_feed, 0);
+ pd_dvb->pd_device = pd;
+
+ ret = dvb_register_adapter(&pd_dvb->dvb_adap,
+ "Poseidon dvbt adapter",
+ THIS_MODULE,
+ NULL /* for hibernation correctly*/,
+ adapter_nr);
+ if (ret < 0)
+ goto error1;
+
+ /* register frontend */
+ pd_dvb->dvb_fe.demodulator_priv = pd;
+ memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops,
+ sizeof(struct dvb_frontend_ops));
+ ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe);
+ if (ret < 0)
+ goto error2;
+
+ /* register demux device */
+ dvbdemux = &pd_dvb->demux;
+ dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+ dvbdemux->priv = pd_dvb;
+ dvbdemux->feednum = dvbdemux->filternum = 64;
+ dvbdemux->start_feed = pd_start_feed;
+ dvbdemux->stop_feed = pd_stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto error3;
+
+ pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum;
+ pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx;
+ pd_dvb->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap);
+ if (ret < 0)
+ goto error3;
+ return 0;
+
+error3:
+ dvb_unregister_frontend(&pd_dvb->dvb_fe);
+error2:
+ dvb_unregister_adapter(&pd_dvb->dvb_adap);
+error1:
+ return ret;
+}
+
+void pd_dvb_usb_device_exit(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ while (atomic_read(&pd_dvb->users) != 0
+ || atomic_read(&pd_dvb->active_feed) != 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ }
+ dvb_dmxdev_release(&pd_dvb->dmxdev);
+ dvb_unregister_frontend(&pd_dvb->dvb_fe);
+ dvb_unregister_adapter(&pd_dvb->dvb_adap);
+ pd_dvb_usb_device_cleanup(pd);
+}
+
+void pd_dvb_usb_device_cleanup(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ dvb_urb_cleanup(pd_dvb);
+}
+
+int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb)
+{
+ return pd_dvb->dvb_adap.num;
+}
diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c
new file mode 100644
index 00000000000..2cf0ebf9f28
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-main.c
@@ -0,0 +1,539 @@
+/*
+ * device driver for Telegent tlg2300 based TV cards
+ *
+ * Author :
+ * Kang Yong <kangyong@telegent.com>
+ * Zhang Xiaobing <xbzhang@telegent.com>
+ * Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com>
+ *
+ * (c) 2009 Telegent Systems
+ * (c) 2010 Telegent Systems
+ *
+ * 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/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/suspend.h>
+#include <linux/usb/quirks.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <linux/smp_lock.h>
+
+#include "vendorcmds.h"
+#include "pd-common.h"
+
+#define VENDOR_ID 0x1B24
+#define PRODUCT_ID 0x4001
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+int debug_mode;
+module_param(debug_mode, int, 0644);
+MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
+
+const char *firmware_name = "tlg2300_firmware.bin";
+struct usb_driver poseidon_driver;
+static LIST_HEAD(pd_device_list);
+
+/*
+ * send set request to USB firmware.
+ */
+s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
+{
+ s32 ret;
+ s8 data[32] = {};
+ u16 lower_16, upper_16;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ mdelay(30);
+
+ if (param == 0) {
+ upper_16 = lower_16 = 0;
+ } else {
+ /* send 32 bit param as two 16 bit param,little endian */
+ lower_16 = (unsigned short)(param & 0xffff);
+ upper_16 = (unsigned short)((param >> 16) & 0xffff);
+ }
+ ret = usb_control_msg(pd->udev,
+ usb_rcvctrlpipe(pd->udev, 0),
+ REQ_SET_CMD | cmdid,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ lower_16,
+ upper_16,
+ &data,
+ sizeof(*cmd_status),
+ USB_CTRL_GET_TIMEOUT);
+
+ if (!ret) {
+ return -ENXIO;
+ } else {
+ /* 1st 4 bytes into cmd_status */
+ memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
+ }
+ return 0;
+}
+
+/*
+ * send get request to Poseidon firmware.
+ */
+s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
+ void *buf, s32 *cmd_status, s32 datalen)
+{
+ s32 ret;
+ s8 data[128] = {};
+ u16 lower_16, upper_16;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ mdelay(30);
+ if (param == 0) {
+ upper_16 = lower_16 = 0;
+ } else {
+ /*send 32 bit param as two 16 bit param, little endian */
+ lower_16 = (unsigned short)(param & 0xffff);
+ upper_16 = (unsigned short)((param >> 16) & 0xffff);
+ }
+ ret = usb_control_msg(pd->udev,
+ usb_rcvctrlpipe(pd->udev, 0),
+ REQ_GET_CMD | cmdid,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ lower_16,
+ upper_16,
+ &data,
+ (datalen + sizeof(*cmd_status)),
+ USB_CTRL_GET_TIMEOUT);
+
+ if (ret < 0) {
+ return -ENXIO;
+ } else {
+ /* 1st 4 bytes into cmd_status, remaining data into cmd_data */
+ memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
+ memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
+ }
+ return 0;
+}
+
+static int pm_notifier_block(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+ struct poseidon *pd = NULL;
+ struct list_head *node, *next;
+
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ list_for_each_safe(node, next, &pd_device_list) {
+ struct usb_device *udev;
+ struct usb_interface *iface;
+ int rc = 0;
+
+ pd = container_of(node, struct poseidon, device_list);
+ udev = pd->udev;
+ iface = pd->interface;
+
+ /* It will cause the system to reload the firmware */
+ rc = usb_lock_device_for_reset(udev, iface);
+ if (rc >= 0) {
+ usb_reset_device(udev);
+ usb_unlock_device(udev);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ log("event :%ld\n", event);
+ return 0;
+}
+
+static struct notifier_block pm_notifer = {
+ .notifier_call = pm_notifier_block,
+};
+
+int set_tuner_mode(struct poseidon *pd, unsigned char mode)
+{
+ s32 ret, cmd_status;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
+ if (ret || cmd_status)
+ return -ENXIO;
+ return 0;
+}
+
+void poseidon_delete(struct kref *kref)
+{
+ struct poseidon *pd = container_of(kref, struct poseidon, kref);
+
+ if (!pd)
+ return;
+ list_del_init(&pd->device_list);
+
+ pd_dvb_usb_device_cleanup(pd);
+ /* clean_audio_data(&pd->audio_data);*/
+
+ if (pd->udev) {
+ usb_put_dev(pd->udev);
+ pd->udev = NULL;
+ }
+ if (pd->interface) {
+ usb_put_intf(pd->interface);
+ pd->interface = NULL;
+ }
+ kfree(pd);
+ log();
+}
+
+static int firmware_download(struct usb_device *udev)
+{
+ int ret = 0, actual_length;
+ const struct firmware *fw = NULL;
+ void *fwbuf = NULL;
+ size_t fwlength = 0, offset;
+ size_t max_packet_size;
+
+ ret = request_firmware(&fw, firmware_name, &udev->dev);
+ if (ret) {
+ log("download err : %d", ret);
+ return ret;
+ }
+
+ fwlength = fw->size;
+
+ fwbuf = kzalloc(fwlength, GFP_KERNEL);
+ if (!fwbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memcpy(fwbuf, fw->data, fwlength);
+
+ max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize;
+ log("\t\t download size : %d", (int)max_packet_size);
+
+ for (offset = 0; offset < fwlength; offset += max_packet_size) {
+ actual_length = 0;
+ ret = usb_bulk_msg(udev,
+ usb_sndbulkpipe(udev, 0x01), /* ep 1 */
+ fwbuf + offset,
+ min(max_packet_size, fwlength - offset),
+ &actual_length,
+ HZ * 10);
+ if (ret)
+ break;
+ }
+ kfree(fwbuf);
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static inline struct poseidon *get_pd(struct usb_interface *intf)
+{
+ return usb_get_intfdata(intf);
+}
+
+#ifdef CONFIG_PM
+/* one-to-one map : poseidon{} <----> usb_device{}'s port */
+static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
+{
+ pd->portnum = udev->portnum;
+}
+
+static inline int get_autopm_ref(struct poseidon *pd)
+{
+ return pd->video_data.users + pd->vbi_data.users + pd->audio.users
+ + atomic_read(&pd->dvb_data.users) + pd->radio_data.users;
+}
+
+/* fixup something for poseidon */
+static inline struct poseidon *fixup(struct poseidon *pd)
+{
+ int count;
+
+ /* old udev and interface have gone, so put back reference . */
+ count = get_autopm_ref(pd);
+ log("count : %d, ref count : %d", count, get_pm_count(pd));
+ while (count--)
+ usb_autopm_put_interface(pd->interface);
+ /*usb_autopm_set_interface(pd->interface); */
+
+ usb_put_dev(pd->udev);
+ usb_put_intf(pd->interface);
+ log("event : %d\n", pd->msg.event);
+ return pd;
+}
+
+static struct poseidon *find_old_poseidon(struct usb_device *udev)
+{
+ struct poseidon *pd;
+
+ list_for_each_entry(pd, &pd_device_list, device_list) {
+ if (pd->portnum == udev->portnum && in_hibernation(pd))
+ return fixup(pd);
+ }
+ return NULL;
+}
+
+/* Is the card working now ? */
+static inline int is_working(struct poseidon *pd)
+{
+ return get_pm_count(pd) > 0;
+}
+
+static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct poseidon *pd = get_pd(intf);
+
+ if (!pd)
+ return 0;
+ if (!is_working(pd)) {
+ if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
+ pd->msg.event = PM_EVENT_AUTO_SUSPEND;
+ pd->pm_resume = NULL; /* a good guard */
+ printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n");
+ }
+ return 0;
+ }
+ pd->msg = msg; /* save it here */
+ logpm(pd);
+ return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
+}
+
+static int poseidon_resume(struct usb_interface *intf)
+{
+ struct poseidon *pd = get_pd(intf);
+
+ if (!pd)
+ return 0;
+ printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n");
+
+ if (!is_working(pd)) {
+ if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
+ pd->msg = PMSG_ON;
+ return 0;
+ }
+ if (in_hibernation(pd)) {
+ logpm(pd);
+ return 0;
+ }
+ logpm(pd);
+ return pd->pm_resume ? pd->pm_resume(pd) : 0;
+}
+
+static void hibernation_resume(struct work_struct *w)
+{
+ struct poseidon *pd = container_of(w, struct poseidon, pm_work);
+ int count;
+
+ pd->msg.event = 0; /* clear it here */
+ pd->state &= ~POSEIDON_STATE_DISCONNECT;
+
+ /* set the new interface's reference */
+ count = get_autopm_ref(pd);
+ while (count--)
+ usb_autopm_get_interface(pd->interface);
+
+ /* resume the context */
+ logpm(pd);
+ if (pd->pm_resume)
+ pd->pm_resume(pd);
+}
+#else /* CONFIG_PM is not enabled: */
+static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
+{
+ return NULL;
+}
+
+static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
+{
+}
+#endif
+
+static bool check_firmware(struct usb_device *udev, int *down_firmware)
+{
+ void *buf;
+ int ret;
+ struct cmd_firmware_vers_s *cmd_firm;
+
+ buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ REQ_GET_CMD | GET_FW_ID,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ 0,
+ buf,
+ sizeof(*cmd_firm) + sizeof(u32),
+ USB_CTRL_GET_TIMEOUT);
+ kfree(buf);
+
+ if (ret < 0) {
+ *down_firmware = 1;
+ return firmware_download(udev);
+ }
+ return ret;
+}
+
+static int poseidon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct poseidon *pd = NULL;
+ int ret = 0;
+ int new_one = 0;
+
+ /* download firmware */
+ check_firmware(udev, &ret);
+ if (ret)
+ return 0;
+
+ /* Do I recovery from the hibernate ? */
+ pd = find_old_poseidon(udev);
+ if (!pd) {
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+ kref_init(&pd->kref);
+ set_map_flags(pd, udev);
+ new_one = 1;
+ }
+
+ pd->udev = usb_get_dev(udev);
+ pd->interface = usb_get_intf(interface);
+ usb_set_intfdata(interface, pd);
+
+ if (new_one) {
+ struct device *dev = &interface->dev;
+
+ logpm(pd);
+ mutex_init(&pd->lock);
+
+ /* register v4l2 device */
+ snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s",
+ dev->driver->name, dev_name(dev));
+ ret = v4l2_device_register(NULL, &pd->v4l2_dev);
+
+ /* register devices in directory /dev */
+ ret = pd_video_init(pd);
+ poseidon_audio_init(pd);
+ poseidon_fm_init(pd);
+ pd_dvb_usb_device_init(pd);
+
+ INIT_LIST_HEAD(&pd->device_list);
+ list_add_tail(&pd->device_list, &pd_device_list);
+ }
+
+ device_init_wakeup(&udev->dev, 1);
+#ifdef CONFIG_PM
+ pd->udev->autosuspend_disabled = 0;
+ pd->udev->autosuspend_delay = HZ * PM_SUSPEND_DELAY;
+
+ if (in_hibernation(pd)) {
+ INIT_WORK(&pd->pm_work, hibernation_resume);
+ schedule_work(&pd->pm_work);
+ }
+#endif
+ return 0;
+}
+
+static void poseidon_disconnect(struct usb_interface *interface)
+{
+ struct poseidon *pd = get_pd(interface);
+
+ if (!pd)
+ return;
+ logpm(pd);
+ if (in_hibernation(pd))
+ return;
+
+ mutex_lock(&pd->lock);
+ pd->state |= POSEIDON_STATE_DISCONNECT;
+ mutex_unlock(&pd->lock);
+
+ /* stop urb transferring */
+ stop_all_video_stream(pd);
+ dvb_stop_streaming(&pd->dvb_data);
+
+ /*unregister v4l2 device */
+ v4l2_device_unregister(&pd->v4l2_dev);
+
+ lock_kernel();
+ {
+ pd_dvb_usb_device_exit(pd);
+ poseidon_fm_exit(pd);
+
+ poseidon_audio_free(pd);
+ pd_video_exit(pd);
+ }
+ unlock_kernel();
+
+ usb_set_intfdata(interface, NULL);
+ kref_put(&pd->kref, poseidon_delete);
+}
+
+struct usb_driver poseidon_driver = {
+ .name = "poseidon",
+ .probe = poseidon_probe,
+ .disconnect = poseidon_disconnect,
+ .id_table = id_table,
+#ifdef CONFIG_PM
+ .suspend = poseidon_suspend,
+ .resume = poseidon_resume,
+#endif
+ .supports_autosuspend = 1,
+};
+
+static int __init poseidon_init(void)
+{
+ int ret;
+
+ ret = usb_register(&poseidon_driver);
+ if (ret)
+ return ret;
+ register_pm_notifier(&pm_notifer);
+ return ret;
+}
+
+static void __exit poseidon_exit(void)
+{
+ log();
+ unregister_pm_notifier(&pm_notifer);
+ usb_deregister(&poseidon_driver);
+}
+
+module_init(poseidon_init);
+module_exit(poseidon_exit);
+
+MODULE_AUTHOR("Telegent Systems");
+MODULE_DESCRIPTION("For tlg2300-based USB device ");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c
new file mode 100644
index 00000000000..755766b1515
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-radio.c
@@ -0,0 +1,420 @@
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <media/v4l2-dev.h>
+#include <linux/version.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/sched.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static int set_frequency(struct poseidon *p, __u32 frequency);
+static int poseidon_fm_close(struct file *filp);
+static int poseidon_fm_open(struct file *filp);
+
+#define TUNER_FREQ_MIN_FM 76000000
+#define TUNER_FREQ_MAX_FM 108000000
+
+#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
+static int preemphasis[MAX_PREEMPHASIS] = {
+ TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */
+ TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */
+ TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */
+};
+
+static int poseidon_check_mode_radio(struct poseidon *p)
+{
+ int ret;
+ u32 status;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+ ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
+ if (ret < 0)
+ goto out;
+
+ ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
+ if (ret != 0)
+ goto out;
+
+ ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
+ ret = send_set_req(p, TUNER_AUD_ANA_STD,
+ p->radio_data.pre_emphasis, &status);
+ ret |= send_set_req(p, TUNER_AUD_MODE,
+ TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
+ ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
+ ATV_AUDIO_RATE_48K, &status);
+ ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
+out:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_fm_suspend(struct poseidon *p)
+{
+ logpm(p);
+ pm_alsa_suspend(p);
+ usb_set_interface(p->udev, 0, 0);
+ msleep(300);
+ return 0;
+}
+
+static int pm_fm_resume(struct poseidon *p)
+{
+ logpm(p);
+ poseidon_check_mode_radio(p);
+ set_frequency(p, p->radio_data.fm_freq);
+ pm_alsa_resume(p);
+ return 0;
+}
+#endif
+
+static int poseidon_fm_open(struct file *filp)
+{
+ struct video_device *vfd = video_devdata(filp);
+ struct poseidon *p = video_get_drvdata(vfd);
+ int ret = 0;
+
+ if (!p)
+ return -1;
+
+ mutex_lock(&p->lock);
+ if (p->state & POSEIDON_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (p->state && !(p->state & POSEIDON_STATE_FM)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ usb_autopm_get_interface(p->interface);
+ if (0 == p->state) {
+ /* default pre-emphasis */
+ if (p->radio_data.pre_emphasis == 0)
+ p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
+ set_debug_mode(vfd, debug_mode);
+
+ ret = poseidon_check_mode_radio(p);
+ if (ret < 0) {
+ usb_autopm_put_interface(p->interface);
+ goto out;
+ }
+ p->state |= POSEIDON_STATE_FM;
+ }
+ p->radio_data.users++;
+ kref_get(&p->kref);
+ filp->private_data = p;
+out:
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+static int poseidon_fm_close(struct file *filp)
+{
+ struct poseidon *p = filp->private_data;
+ struct radio_data *fm = &p->radio_data;
+ uint32_t status;
+
+ mutex_lock(&p->lock);
+ fm->users--;
+ if (0 == fm->users)
+ p->state &= ~POSEIDON_STATE_FM;
+
+ if (fm->is_radio_streaming && filp == p->file_for_stream) {
+ fm->is_radio_streaming = 0;
+ send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
+ }
+ usb_autopm_put_interface(p->interface);
+ mutex_unlock(&p->lock);
+
+ kref_put(&p->kref, poseidon_delete);
+ filp->private_data = NULL;
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct poseidon *p = file->private_data;
+
+ strlcpy(v->driver, "tele-radio", sizeof(v->driver));
+ strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
+ usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
+ v->version = KERNEL_VERSION(0, 0, 1);
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+}
+
+static const struct v4l2_file_operations poseidon_fm_fops = {
+ .owner = THIS_MODULE,
+ .open = poseidon_fm_open,
+ .release = poseidon_fm_close,
+ .ioctl = video_ioctl2,
+};
+
+int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ struct tuner_fm_sig_stat_s fm_stat = {};
+ int ret, status, count = 5;
+ struct poseidon *p = file->private_data;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ vt->type = V4L2_TUNER_RADIO;
+ vt->capability = V4L2_TUNER_CAP_STEREO;
+ vt->rangelow = TUNER_FREQ_MIN_FM / 62500;
+ vt->rangehigh = TUNER_FREQ_MAX_FM / 62500;
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ vt->signal = 0;
+ vt->afc = 0;
+
+ mutex_lock(&p->lock);
+ ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+ &fm_stat, &status, sizeof(fm_stat));
+
+ while (fm_stat.sig_lock_busy && count-- && !ret) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+ &fm_stat, &status, sizeof(fm_stat));
+ }
+ mutex_unlock(&p->lock);
+
+ if (ret || status) {
+ vt->signal = 0;
+ } else if ((fm_stat.sig_present || fm_stat.sig_locked)
+ && fm_stat.sig_strength == 0) {
+ vt->signal = 0xffff;
+ } else
+ vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
+
+ return 0;
+}
+
+int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
+{
+ struct poseidon *p = file->private_data;
+
+ argp->frequency = p->radio_data.fm_freq;
+ return 0;
+}
+
+static int set_frequency(struct poseidon *p, __u32 frequency)
+{
+ __u32 freq ;
+ int ret, status;
+
+ mutex_lock(&p->lock);
+
+ ret = send_set_req(p, TUNER_AUD_ANA_STD,
+ p->radio_data.pre_emphasis, &status);
+
+ freq = (frequency * 125) * 500 / 1000;/* kHZ */
+ if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
+ if (ret < 0)
+ goto error ;
+ ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ if (!p->radio_data.is_radio_streaming) {
+ ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+ ret = send_set_req(p, PLAY_SERVICE,
+ TLG_TUNE_PLAY_SVC_START, &status);
+ p->radio_data.is_radio_streaming = 1;
+ }
+ p->radio_data.fm_freq = frequency;
+error:
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp)
+{
+ struct poseidon *p = file->private_data;
+
+ p->file_for_stream = file;
+#ifdef CONFIG_PM
+ p->pm_suspend = pm_fm_suspend;
+ p->pm_resume = pm_fm_resume;
+#endif
+ return set_frequency(p, argp->frequency);
+}
+
+int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *arg)
+{
+ return 0;
+}
+
+int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct poseidon *p = file->private_data;
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
+ continue;
+
+ if (i < MAX_PREEMPHASIS)
+ ctrl->value = p->radio_data.pre_emphasis;
+ }
+ return 0;
+}
+
+int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
+ continue;
+
+ if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) {
+ struct poseidon *p = file->private_data;
+ int pre_emphasis = preemphasis[ctrl->value];
+ u32 status;
+
+ send_set_req(p, TUNER_AUD_ANA_STD,
+ pre_emphasis, &status);
+ p->radio_data.pre_emphasis = pre_emphasis;
+ }
+ }
+ return 0;
+}
+
+int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ return 0;
+}
+
+int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+{
+ if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL))
+ return -EINVAL;
+
+ ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) {
+ /* return the next supported control */
+ ctrl->id = V4L2_CID_TUNE_PREEMPHASIS;
+ v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED,
+ V4L2_PREEMPHASIS_75_uS, 1,
+ V4L2_PREEMPHASIS_50_uS);
+ ctrl->flags = V4L2_CTRL_FLAG_UPDATE;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int tlg_fm_vidioc_querymenu(struct file *file, void *fh,
+ struct v4l2_querymenu *qmenu)
+{
+ return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ return vt->index > 0 ? -EINVAL : 0;
+}
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va)
+{
+ return (va->index != 0) ? -EINVAL : 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ a->index = 0;
+ a->mode = 0;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, u32 i)
+{
+ return (i != 0) ? -EINVAL : 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
+{
+ return (*i != 0) ? -EINVAL : 0;
+}
+
+static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = tlg_fm_vidioc_queryctrl,
+ .vidioc_querymenu = tlg_fm_vidioc_querymenu,
+ .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl,
+ .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl,
+ .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl,
+ .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_tuner = tlg_fm_vidioc_g_tuner,
+ .vidioc_g_frequency = fm_get_freq,
+ .vidioc_s_frequency = fm_set_freq,
+};
+
+static struct video_device poseidon_fm_template = {
+ .name = "Telegent-Radio",
+ .fops = &poseidon_fm_fops,
+ .minor = -1,
+ .release = video_device_release,
+ .ioctl_ops = &poseidon_fm_ioctl_ops,
+};
+
+int poseidon_fm_init(struct poseidon *p)
+{
+ struct video_device *fm_dev;
+
+ fm_dev = vdev_init(p, &poseidon_fm_template);
+ if (fm_dev == NULL)
+ return -1;
+
+ if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
+ video_device_release(fm_dev);
+ return -1;
+ }
+ p->radio_data.fm_dev = fm_dev;
+ return 0;
+}
+
+int poseidon_fm_exit(struct poseidon *p)
+{
+ destroy_video_device(&p->radio_data.fm_dev);
+ return 0;
+}
diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c
new file mode 100644
index 00000000000..becfba6a304
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-video.c
@@ -0,0 +1,1667 @@
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static int pm_video_suspend(struct poseidon *pd);
+static int pm_video_resume(struct poseidon *pd);
+static void iso_bubble_handler(struct work_struct *w);
+
+int usb_transfer_mode;
+module_param(usb_transfer_mode, int, 0644);
+MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
+
+static const struct poseidon_format poseidon_formats[] = {
+ { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
+ { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
+};
+
+static const struct poseidon_tvnorm poseidon_tvnorms[] = {
+ { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D },
+ { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B },
+ { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G },
+ { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H },
+ { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I },
+ { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M },
+ { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO },
+ { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
+ { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
+ { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
+ { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
+ { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
+ { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
+ { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
+ { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
+ { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
+ { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
+ { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
+};
+static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
+
+struct pd_audio_mode {
+ u32 tlg_audio_mode;
+ u32 v4l2_audio_sub;
+ u32 v4l2_audio_mode;
+};
+
+static const struct pd_audio_mode pd_audio_modes[] = {
+ { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
+ V4L2_TUNER_MODE_MONO },
+ { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
+ V4L2_TUNER_MODE_STEREO },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
+ V4L2_TUNER_MODE_LANG1 },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
+ V4L2_TUNER_MODE_LANG2 },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
+ V4L2_TUNER_MODE_LANG1_LANG2 }
+};
+static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
+
+struct pd_input {
+ char *name;
+ uint32_t tlg_src;
+};
+
+static const struct pd_input pd_inputs[] = {
+ { "TV Antenna", TLG_SIG_SRC_ANTENNA },
+ { "TV Cable", TLG_SIG_SRC_CABLE },
+ { "TV SVideo", TLG_SIG_SRC_SVIDEO },
+ { "TV Composite", TLG_SIG_SRC_COMPOSITE }
+};
+static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
+
+struct poseidon_control {
+ struct v4l2_queryctrl v4l2_ctrl;
+ enum cmd_custom_param_id vc_id;
+};
+
+static struct poseidon_control controls[] = {
+ {
+ { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
+ "brightness", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_BRIGHTNESS_CTRL
+ }, {
+ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
+ "contrast", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_CONTRAST_CTRL,
+ }, {
+ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
+ "hue", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_HUE_CTRL,
+ }, {
+ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
+ "saturation", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_SATURATION_CTRL,
+ },
+};
+
+struct video_std_to_audio_std {
+ v4l2_std_id video_std;
+ int audio_std;
+};
+
+static const struct video_std_to_audio_std video_to_audio_map[] = {
+ /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
+ 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
+ { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
+ V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
+
+ /* country : { 1, 52, 54, 55, 886 } */
+ {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
+
+ /* country : { 81 } */
+ { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
+
+ /* other country : TLG_TUNE_ASTD_A2 */
+};
+static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
+
+static int get_audio_std(v4l2_std_id v4l2_std)
+{
+ int i = 0;
+
+ for (; i < map_size; i++) {
+ if (v4l2_std & video_to_audio_map[i].video_std)
+ return video_to_audio_map[i].audio_std;
+ }
+ return TLG_TUNE_ASTD_A2;
+}
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct front_face *front = fh;
+ struct poseidon *p = front->pd;
+
+ logs(front);
+
+ strcpy(cap->driver, "tele-video");
+ strcpy(cap->card, "Telegent Poseidon");
+ usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->version = KERNEL_VERSION(0, 0, 1);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
+ return 0;
+}
+
+/*====================================================================*/
+static void init_copy(struct video_data *video, bool index)
+{
+ struct front_face *front = video->front;
+
+ video->field_count = index;
+ video->lines_copied = 0;
+ video->prev_left = 0 ;
+ video->dst = (char *)videobuf_to_vmalloc(front->curr_frame)
+ + index * video->lines_size;
+ video->vbi->copied = 0; /* set it here */
+}
+
+static bool get_frame(struct front_face *front, int *need_init)
+{
+ struct videobuf_buffer *vb = front->curr_frame;
+
+ if (vb)
+ return true;
+
+ spin_lock(&front->queue_lock);
+ if (!list_empty(&front->active)) {
+ vb = list_entry(front->active.next,
+ struct videobuf_buffer, queue);
+ if (need_init)
+ *need_init = 1;
+ front->curr_frame = vb;
+ list_del_init(&vb->queue);
+ }
+ spin_unlock(&front->queue_lock);
+
+ return !!vb;
+}
+
+/* check if the video's buffer is ready */
+static bool get_video_frame(struct front_face *front, struct video_data *video)
+{
+ int need_init = 0;
+ bool ret = true;
+
+ ret = get_frame(front, &need_init);
+ if (ret && need_init)
+ init_copy(video, 0);
+ return ret;
+}
+
+static void submit_frame(struct front_face *front)
+{
+ struct videobuf_buffer *vb = front->curr_frame;
+
+ if (vb == NULL)
+ return;
+
+ front->curr_frame = NULL;
+ vb->state = VIDEOBUF_DONE;
+ vb->field_count++;
+ do_gettimeofday(&vb->ts);
+
+ wake_up(&vb->done);
+}
+
+/*
+ * A frame is composed of two fields. If we receive all the two fields,
+ * call the submit_frame() to submit the whole frame to applications.
+ */
+static void end_field(struct video_data *video)
+{
+ /* logs(video->front); */
+ if (1 == video->field_count)
+ submit_frame(video->front);
+ else
+ init_copy(video, 1);
+}
+
+static void copy_video_data(struct video_data *video, char *src,
+ unsigned int count)
+{
+#define copy_data(len) \
+ do { \
+ if (++video->lines_copied > video->lines_per_field) \
+ goto overflow; \
+ memcpy(video->dst, src, len);\
+ video->dst += len + video->lines_size; \
+ src += len; \
+ count -= len; \
+ } while (0)
+
+ while (count && count >= video->lines_size) {
+ if (video->prev_left) {
+ copy_data(video->prev_left);
+ video->prev_left = 0;
+ continue;
+ }
+ copy_data(video->lines_size);
+ }
+ if (count && count < video->lines_size) {
+ memcpy(video->dst, src, count);
+
+ video->prev_left = video->lines_size - count;
+ video->dst += count;
+ }
+ return;
+
+overflow:
+ end_field(video);
+}
+
+static void check_trailer(struct video_data *video, char *src, int count)
+{
+ struct vbi_data *vbi = video->vbi;
+ int offset; /* trailer's offset */
+ char *buf;
+
+ offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
+ - (vbi->copied + video->lines_size * video->lines_copied);
+ if (video->prev_left)
+ offset -= (video->lines_size - video->prev_left);
+
+ if (offset > count || offset <= 0)
+ goto short_package;
+
+ buf = src + offset;
+
+ /* trailer : (VFHS) + U32 + U32 + field_num */
+ if (!strncmp(buf, "VFHS", 4)) {
+ int field_num = *((u32 *)(buf + 12));
+
+ if ((field_num & 1) ^ video->field_count) {
+ init_copy(video, video->field_count);
+ return;
+ }
+ copy_video_data(video, src, offset);
+ }
+short_package:
+ end_field(video);
+}
+
+/* ========== Check this more carefully! =========== */
+static inline void copy_vbi_data(struct vbi_data *vbi,
+ char *src, unsigned int count)
+{
+ struct front_face *front = vbi->front;
+
+ if (front && get_frame(front, NULL)) {
+ char *buf = videobuf_to_vmalloc(front->curr_frame);
+
+ if (vbi->video->field_count)
+ buf += (vbi->vbi_size / 2);
+ memcpy(buf + vbi->copied, src, count);
+ }
+ vbi->copied += count;
+}
+
+/*
+ * Copy the normal data (VBI or VIDEO) without the trailer.
+ * VBI is not interlaced, while VIDEO is interlaced.
+ */
+static inline void copy_vbi_video_data(struct video_data *video,
+ char *src, unsigned int count)
+{
+ struct vbi_data *vbi = video->vbi;
+ unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
+
+ if (vbi_delta >= count) {
+ copy_vbi_data(vbi, src, count);
+ } else {
+ if (vbi_delta) {
+ copy_vbi_data(vbi, src, vbi_delta);
+
+ /* we receive the two fields of the VBI*/
+ if (vbi->front && video->field_count)
+ submit_frame(vbi->front);
+ }
+ copy_video_data(video, src + vbi_delta, count - vbi_delta);
+ }
+}
+
+static void urb_complete_bulk(struct urb *urb)
+{
+ struct front_face *front = urb->context;
+ struct video_data *video = &front->pd->video_data;
+ char *src = (char *)urb->transfer_buffer;
+ int count = urb->actual_length;
+ int ret = 0;
+
+ if (!video->is_streaming || urb->status) {
+ if (urb->status == -EPROTO)
+ goto resend_it;
+ return;
+ }
+ if (!get_video_frame(front, video))
+ goto resend_it;
+
+ if (count == urb->transfer_buffer_length)
+ copy_vbi_video_data(video, src, count);
+ else
+ check_trailer(video, src, count);
+
+resend_it:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log(" submit failed: error %d", ret);
+}
+
+/************************* for ISO *********************/
+#define GET_SUCCESS (0)
+#define GET_TRAILER (1)
+#define GET_TOO_MUCH_BUBBLE (2)
+#define GET_NONE (3)
+static int get_chunk(int start, struct urb *urb,
+ int *head, int *tail, int *bubble_err)
+{
+ struct usb_iso_packet_descriptor *pkt = NULL;
+ int ret = GET_SUCCESS;
+
+ for (*head = *tail = -1; start < urb->number_of_packets; start++) {
+ pkt = &urb->iso_frame_desc[start];
+
+ /* handle the bubble of the Hub */
+ if (-EOVERFLOW == pkt->status) {
+ if (++*bubble_err > urb->number_of_packets / 3)
+ return GET_TOO_MUCH_BUBBLE;
+ continue;
+ }
+
+ /* This is the gap */
+ if (pkt->status || pkt->actual_length <= 0
+ || pkt->actual_length > ISO_PKT_SIZE) {
+ if (*head != -1)
+ break;
+ continue;
+ }
+
+ /* a good isochronous packet */
+ if (pkt->actual_length == ISO_PKT_SIZE) {
+ if (*head == -1)
+ *head = start;
+ *tail = start;
+ continue;
+ }
+
+ /* trailer is here */
+ if (pkt->actual_length < ISO_PKT_SIZE) {
+ if (*head == -1) {
+ *head = start;
+ *tail = start;
+ return GET_TRAILER;
+ }
+ break;
+ }
+ }
+
+ if (*head == -1 && *tail == -1)
+ ret = GET_NONE;
+ return ret;
+}
+
+/*
+ * |__|------|___|-----|_______|
+ * ^ ^
+ * | |
+ * gap gap
+ */
+static void urb_complete_iso(struct urb *urb)
+{
+ struct front_face *front = urb->context;
+ struct video_data *video = &front->pd->video_data;
+ int bubble_err = 0, head = 0, tail = 0;
+ char *src = (char *)urb->transfer_buffer;
+ int ret = 0;
+
+ if (!video->is_streaming)
+ return;
+
+ do {
+ if (!get_video_frame(front, video))
+ goto out;
+
+ switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
+ case GET_SUCCESS:
+ copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
+ (tail - head + 1) * ISO_PKT_SIZE);
+ break;
+ case GET_TRAILER:
+ check_trailer(video, src + (head * ISO_PKT_SIZE),
+ ISO_PKT_SIZE);
+ break;
+ case GET_NONE:
+ goto out;
+ case GET_TOO_MUCH_BUBBLE:
+ log("\t We got too much bubble");
+ schedule_work(&video->bubble_work);
+ return;
+ }
+ } while (head = tail + 1, head < urb->number_of_packets);
+
+out:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log("usb_submit_urb err : %d", ret);
+}
+/*============================= [ end ] =====================*/
+
+static int prepare_iso_urb(struct video_data *video)
+{
+ struct usb_device *udev = video->pd->udev;
+ int i;
+
+ if (video->urb_array[0])
+ return 0;
+
+ for (i = 0; i < SBUF_NUM; i++) {
+ struct urb *urb;
+ void *mem;
+ int j;
+
+ urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
+ if (urb == NULL)
+ goto out;
+
+ video->urb_array[i] = urb;
+ mem = usb_buffer_alloc(udev,
+ ISO_PKT_SIZE * PK_PER_URB,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ urb->complete = urb_complete_iso; /* handler */
+ urb->dev = udev;
+ urb->context = video->front;
+ urb->pipe = usb_rcvisocpipe(udev,
+ video->endpoint_addr);
+ urb->interval = 1;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->number_of_packets = PK_PER_URB;
+ urb->transfer_buffer = mem;
+ urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
+
+ for (j = 0; j < PK_PER_URB; j++) {
+ urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
+ urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
+ }
+ }
+ return 0;
+out:
+ for (; i > 0; i--)
+ ;
+ return -ENOMEM;
+}
+
+/* return the succeeded number of the allocation */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+ struct usb_device *udev, u8 ep_addr,
+ int buf_size, gfp_t gfp_flags,
+ usb_complete_t complete_fn, void *context)
+{
+ struct urb *urb;
+ void *mem;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ urb = usb_alloc_urb(0, gfp_flags);
+ if (urb == NULL)
+ return i;
+
+ mem = usb_buffer_alloc(udev, buf_size, gfp_flags,
+ &urb->transfer_dma);
+ if (mem == NULL)
+ return i;
+
+ usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
+ mem, buf_size, complete_fn, context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ urb_array[i] = urb;
+ }
+ return i;
+}
+
+void free_all_urb_generic(struct urb **urb_array, int num)
+{
+ int i;
+ struct urb *urb;
+
+ for (i = 0; i < num; i++) {
+ urb = urb_array[i];
+ if (urb) {
+ usb_buffer_free(urb->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ urb_array[i] = NULL;
+ }
+ }
+}
+
+static int prepare_bulk_urb(struct video_data *video)
+{
+ if (video->urb_array[0])
+ return 0;
+
+ alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
+ video->pd->udev, video->endpoint_addr,
+ 0x2000, GFP_KERNEL,
+ urb_complete_bulk, video->front);
+ return 0;
+}
+
+/* free the URBs */
+static void free_all_urb(struct video_data *video)
+{
+ free_all_urb_generic(video->urb_array, SBUF_NUM);
+}
+
+static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ videobuf_vmalloc_free(vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct front_face *front = q->priv_data;
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &front->active);
+}
+
+static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct front_face *front = q->priv_data;
+ int rc;
+
+ switch (front->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ struct v4l2_pix_format *pix;
+
+ pix = &front->pd->video_data.context.pix;
+ vb->size = pix->sizeimage; /* real frame size */
+ vb->width = pix->width;
+ vb->height = pix->height;
+ rc = videobuf_iolock(q, vb, NULL);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->size = front->pd->vbi_data.vbi_size;
+ rc = videobuf_iolock(q, vb, NULL);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ vb->field = field;
+ vb->state = VIDEOBUF_PREPARED;
+ return 0;
+}
+
+int fire_all_urb(struct video_data *video)
+{
+ int i, ret;
+
+ video->is_streaming = 1;
+
+ for (i = 0; i < SBUF_NUM; i++) {
+ ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
+ if (ret)
+ log("(%d) failed: error %d", i, ret);
+ }
+ return ret;
+}
+
+static int start_video_stream(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ s32 cmd_status;
+
+ send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
+
+ if (pd->cur_transfer_mode) {
+ prepare_iso_urb(video);
+ INIT_WORK(&video->bubble_work, iso_bubble_handler);
+ } else {
+ /* The bulk mode does not need a bubble handler */
+ prepare_bulk_urb(video);
+ }
+ fire_all_urb(video);
+ return 0;
+}
+
+static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct front_face *front = q->priv_data;
+ struct poseidon *pd = front->pd;
+
+ switch (front->type) {
+ default:
+ return -EINVAL;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct video_data *video = &pd->video_data;
+ struct v4l2_pix_format *pix = &video->context.pix;
+
+ *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
+ if (*count < 4)
+ *count = 4;
+ if (1) {
+ /* same in different altersetting */
+ video->endpoint_addr = 0x82;
+ video->vbi = &pd->vbi_data;
+ video->vbi->video = video;
+ video->pd = pd;
+ video->lines_per_field = pix->height / 2;
+ video->lines_size = pix->width * 2;
+ video->front = front;
+ }
+ return start_video_stream(pd);
+ }
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ *size = PAGE_ALIGN(vbi->vbi_size);
+ log("size : %d", *size);
+ if (*count == 0)
+ *count = 4;
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct videobuf_queue_ops pd_video_qops = {
+ .buf_setup = pd_buf_setup,
+ .buf_prepare = pd_buf_prepare,
+ .buf_queue = pd_buf_queue,
+ .buf_release = pd_buf_release,
+};
+
+static int vidioc_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (ARRAY_SIZE(poseidon_formats) <= f->index)
+ return -EINVAL;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = 0;
+ f->pixelformat = poseidon_formats[f->index].fourcc;
+ strcpy(f->description, poseidon_formats[f->index].name);
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+ f->fmt.pix = pd->video_data.context.pix;
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+/*
+ * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
+ * Mplayer calls them in the reverse order.
+ */
+static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
+{
+ struct video_data *video = &pd->video_data;
+ struct running_context *context = &video->context;
+ struct v4l2_pix_format *pix_def = &context->pix;
+ s32 ret = 0, cmd_status = 0, vid_resol;
+
+ /* set the pixel format to firmware */
+ if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
+ vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
+ } else {
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ vid_resol = TLG_TUNER_VID_FORMAT_YUV;
+ }
+ ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+ vid_resol, &cmd_status);
+
+ /* set the resolution to firmware */
+ vid_resol = TLG_TUNE_VID_RES_720;
+ switch (pix->width) {
+ case 704:
+ vid_resol = TLG_TUNE_VID_RES_704;
+ break;
+ default:
+ pix->width = 720;
+ case 720:
+ break;
+ }
+ ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+ vid_resol, &cmd_status);
+ if (ret || cmd_status) {
+ mutex_unlock(&pd->lock);
+ return -EBUSY;
+ }
+
+ pix_def->pixelformat = pix->pixelformat; /* save it */
+ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576;
+
+ /* Compare with the default setting */
+ if ((pix_def->width != pix->width)
+ || (pix_def->height != pix->height)) {
+ pix_def->width = pix->width;
+ pix_def->height = pix->height;
+ pix_def->bytesperline = pix->width * 2;
+ pix_def->sizeimage = pix->width * pix->height * 2;
+ }
+ *pix = *pix_def;
+
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+ /* stop VBI here */
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ if (pd->file_for_stream == NULL)
+ pd->file_for_stream = file;
+ else if (file != pd->file_for_stream) {
+ mutex_unlock(&pd->lock);
+ return -EINVAL;
+ }
+
+ pd_vidioc_s_fmt(pd, &f->fmt.pix);
+ mutex_unlock(&pd->lock);
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi(struct file *file, void *fh,
+ struct v4l2_format *v4l2_f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
+
+ vbi_fmt->samples_per_line = 720 * 2;
+ vbi_fmt->sampling_rate = 6750000 * 4;
+ vbi_fmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbi_fmt->offset = 64 * 4; /*FIXME: why offset */
+ if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
+ vbi_fmt->start[0] = 10;
+ vbi_fmt->start[1] = 264;
+ vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
+ vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
+ } else {
+ vbi_fmt->start[0] = 6;
+ vbi_fmt->start[1] = 314;
+ vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
+ vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
+ }
+ vbi_fmt->flags = V4L2_VBI_UNSYNC;
+ logs(front);
+ return 0;
+}
+
+static int set_std(struct poseidon *pd, v4l2_std_id *norm)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+ struct running_context *context;
+ struct v4l2_pix_format *pix;
+ s32 i, ret = 0, cmd_status, param;
+ int height;
+
+ for (i = 0; i < POSEIDON_TVNORMS; i++) {
+ if (*norm & poseidon_tvnorms[i].v4l2_id) {
+ param = poseidon_tvnorms[i].tlg_tvnorm;
+ log("name : %s", poseidon_tvnorms[i].name);
+ goto found;
+ }
+ }
+ return -EINVAL;
+found:
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
+ if (ret || cmd_status)
+ goto out;
+
+ /* Set vbi size and check the height of the frame */
+ context = &video->context;
+ context->tvnormid = poseidon_tvnorms[i].v4l2_id;
+ if (context->tvnormid & V4L2_STD_525_60) {
+ vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
+ height = 480;
+ } else {
+ vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
+ height = 576;
+ }
+
+ pix = &context->pix;
+ if (pix->height != height) {
+ pix->height = height;
+ pix->sizeimage = pix->width * pix->height * 2;
+ }
+
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct front_face *front = fh;
+ logs(front);
+ return set_std(front->pd, norm);
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
+{
+ struct front_face *front = fh;
+
+ if (in->index < 0 || in->index >= POSEIDON_INPUTS)
+ return -EINVAL;
+ strcpy(in->name, pd_inputs[in->index].name);
+ in->type = V4L2_INPUT_TYPE_TUNER;
+
+ /*
+ * the audio input index mixed with this video input,
+ * Poseidon only have one audio/video, set to "0"
+ */
+ in->audioset = 0;
+ in->tuner = 0;
+ in->std = V4L2_STD_ALL;
+ in->status = 0;
+ logs(front);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct running_context *context = &pd->video_data.context;
+
+ logs(front);
+ *i = context->sig_index;
+ return 0;
+}
+
+/* We can support several inputs */
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ s32 ret, cmd_status;
+
+ if (i < 0 || i >= POSEIDON_INPUTS)
+ return -EINVAL;
+ ret = send_set_req(pd, SGNL_SRC_SEL,
+ pd_inputs[i].tlg_src, &cmd_status);
+ if (ret)
+ return ret;
+
+ pd->video_data.context.sig_index = i;
+ return 0;
+}
+
+static struct poseidon_control *check_control_id(__u32 id)
+{
+ struct poseidon_control *control = &controls[0];
+ int array_size = ARRAY_SIZE(controls);
+
+ for (; control < &controls[array_size]; control++)
+ if (control->v4l2_ctrl.id == id)
+ return control;
+ return NULL;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct poseidon_control *control = NULL;
+
+ control = check_control_id(a->id);
+ if (!control)
+ return -EINVAL;
+
+ *a = control->v4l2_ctrl;
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct poseidon_control *control = NULL;
+ struct tuner_custom_parameter_s tuner_param;
+ s32 ret = 0, cmd_status;
+
+ control = check_control_id(ctrl->id);
+ if (!control)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id,
+ &tuner_param, &cmd_status, sizeof(tuner_param));
+ mutex_unlock(&pd->lock);
+
+ if (ret || cmd_status)
+ return -1;
+
+ ctrl->value = tuner_param.param_value;
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct tuner_custom_parameter_s param = {0};
+ struct poseidon_control *control = NULL;
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ s32 ret = 0, cmd_status, params;
+
+ control = check_control_id(a->id);
+ if (!control)
+ return -EINVAL;
+
+ param.param_value = a->value;
+ param.param_id = control->vc_id;
+ params = *(s32 *)&param; /* temp code */
+
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ mutex_unlock(&pd->lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ return ret;
+}
+
+/* Audio ioctls */
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ if (0 != a->index)
+ return -EINVAL;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "USB audio in");
+ /*Poseidon have no AVL function.*/
+ a->mode = 0;
+ return 0;
+}
+
+int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ a->index = 0;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "USB audio in");
+ a->mode = 0;
+ return 0;
+}
+
+int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ return (0 == a->index) ? 0 : -EINVAL;
+}
+
+/* Tuner ioctls */
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct tuner_atv_sig_stat_s atv_stat;
+ s32 count = 5, ret, cmd_status;
+ int index;
+
+ if (0 != tuner->index)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+ &atv_stat, &cmd_status, sizeof(atv_stat));
+
+ while (atv_stat.sig_lock_busy && count-- && !ret) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+ &atv_stat, &cmd_status, sizeof(atv_stat));
+ }
+ mutex_unlock(&pd->lock);
+
+ if (debug_mode)
+ log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
+
+ if (ret || cmd_status)
+ tuner->signal = 0;
+ else if (atv_stat.sig_present && !atv_stat.sig_strength)
+ tuner->signal = 0xFFFF;
+ else
+ tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
+
+ strcpy(tuner->name, "Telegent Systems");
+ tuner->type = V4L2_TUNER_ANALOG_TV;
+ tuner->rangelow = TUNER_FREQ_MIN / 62500;
+ tuner->rangehigh = TUNER_FREQ_MAX / 62500;
+ tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ index = pd->video_data.context.audio_idx;
+ tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
+ tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
+ tuner->afc = 0;
+ logs(front);
+ return 0;
+}
+
+static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
+{
+ s32 ret = 0, cmd_status, param, audiomode;
+
+ mutex_lock(&pd->lock);
+ param = pd_audio_modes[index].tlg_audio_mode;
+ ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
+ audiomode = get_audio_std(pd->video_data.context.tvnormid);
+ ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
+ &cmd_status);
+ if (!ret)
+ pd->video_data.context.audio_idx = index;
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ int index;
+
+ if (0 != a->index)
+ return -EINVAL;
+ logs(front);
+ for (index = 0; index < POSEIDON_AUDIOMODS; index++)
+ if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
+ return pd_vidioc_s_tuner(pd, index);
+ return -EINVAL;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *freq)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct running_context *context = &pd->video_data.context;
+
+ if (0 != freq->tuner)
+ return -EINVAL;
+ freq->frequency = context->freq;
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ return 0;
+}
+
+static int set_frequency(struct poseidon *pd, __u32 frequency)
+{
+ s32 ret = 0, param, cmd_status;
+ struct running_context *context = &pd->video_data.context;
+
+ param = frequency * 62500 / 1000;
+ if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+
+ msleep(250); /* wait for a while until the hardware is ready. */
+ context->freq = frequency;
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *freq)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+#ifdef CONFIG_PM
+ pd->pm_suspend = pm_video_suspend;
+ pd->pm_resume = pm_video_resume;
+#endif
+ return set_frequency(pd, freq->frequency);
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct front_face *front = file->private_data;
+ logs(front);
+ return videobuf_reqbufs(&front->q, b);
+}
+
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ logs(front);
+ return videobuf_querybuf(&front->q, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_qbuf(&front->q, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
+}
+
+/* Just stop the URBs, do not free the URBs */
+int usb_transfer_stop(struct video_data *video)
+{
+ if (video->is_streaming) {
+ int i;
+ s32 cmd_status;
+ struct poseidon *pd = video->pd;
+
+ video->is_streaming = 0;
+ for (i = 0; i < SBUF_NUM; ++i) {
+ if (video->urb_array[i])
+ usb_kill_urb(video->urb_array[i]);
+ }
+
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+ }
+ return 0;
+}
+
+int stop_all_video_stream(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ mutex_lock(&pd->lock);
+ if (video->is_streaming) {
+ struct front_face *front = video->front;
+
+ /* stop the URBs */
+ usb_transfer_stop(video);
+ free_all_urb(video);
+
+ /* stop the host side of VIDEO */
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+
+ /* stop the host side of VBI */
+ front = vbi->front;
+ if (front) {
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+ }
+ }
+ mutex_unlock(&pd->lock);
+ return 0;
+}
+
+/*
+ * The bubbles can seriously damage the video's quality,
+ * though it occurs in very rare situation.
+ */
+static void iso_bubble_handler(struct work_struct *w)
+{
+ struct video_data *video;
+ struct poseidon *pd;
+
+ video = container_of(w, struct video_data, bubble_work);
+ pd = video->pd;
+
+ mutex_lock(&pd->lock);
+ usb_transfer_stop(video);
+ msleep(500);
+ start_video_stream(pd);
+ mutex_unlock(&pd->lock);
+}
+
+
+static int vidioc_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct front_face *front = fh;
+
+ logs(front);
+ if (unlikely(type != front->type))
+ return -EINVAL;
+ return videobuf_streamon(&front->q);
+}
+
+static int vidioc_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct front_face *front = file->private_data;
+
+ logs(front);
+ if (unlikely(type != front->type))
+ return -EINVAL;
+ return videobuf_streamoff(&front->q);
+}
+
+/* Set the firmware's default values : need altersetting */
+static int pd_video_checkmode(struct poseidon *pd)
+{
+ s32 ret = 0, cmd_status, audiomode;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+
+ /* choose the altersetting */
+ ret = usb_set_interface(pd->udev, 0,
+ (pd->cur_transfer_mode ?
+ ISO_3K_BULK_ALTERNATE_IFACE :
+ BULK_ALTERNATE_IFACE));
+ if (ret < 0)
+ goto error;
+
+ /* set default parameters for PAL-D , with the VBI enabled*/
+ ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
+ ret |= send_set_req(pd, SGNL_SRC_SEL,
+ TLG_SIG_SRC_ANTENNA, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_STD_SEL,
+ TLG_TUNE_VSTD_PAL_D, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+ TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+ TLG_TUNE_VID_RES_720, &cmd_status);
+ ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
+ ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
+
+ /* set the audio */
+ audiomode = get_audio_std(pd->video_data.context.tvnormid);
+ ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
+ ret |= send_set_req(pd, TUNER_AUD_MODE,
+ TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
+ ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
+ ATV_AUDIO_RATE_48K, &cmd_status);
+error:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd)
+{
+ /* stop audio */
+ pm_alsa_suspend(pd);
+
+ /* stop and free all the URBs */
+ usb_transfer_stop(&pd->video_data);
+ free_all_urb(&pd->video_data);
+
+ /* reset the interface */
+ usb_set_interface(pd->udev, 0, 0);
+ msleep(300);
+ return 0;
+}
+
+static int restore_v4l2_context(struct poseidon *pd,
+ struct running_context *context)
+{
+ struct front_face *front = pd->video_data.front;
+
+ pd_video_checkmode(pd);
+
+ set_std(pd, &context->tvnormid);
+ vidioc_s_input(NULL, front, context->sig_index);
+ pd_vidioc_s_tuner(pd, context->audio_idx);
+ pd_vidioc_s_fmt(pd, &context->pix);
+ set_frequency(pd, context->freq);
+ return 0;
+}
+
+static int pm_video_resume(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+
+ /* resume the video */
+ /* [1] restore the origin V4L2 parameters */
+ restore_v4l2_context(pd, &video->context);
+
+ /* [2] initiate video copy variables */
+ if (video->front->curr_frame)
+ init_copy(video, 0);
+
+ /* [3] fire urbs */
+ start_video_stream(pd);
+
+ /* resume the audio */
+ pm_alsa_resume(pd);
+ return 0;
+}
+#endif
+
+void set_debug_mode(struct video_device *vfd, int debug_mode)
+{
+ vfd->debug = 0;
+ if (debug_mode & 0x1)
+ vfd->debug = V4L2_DEBUG_IOCTL;
+ if (debug_mode & 0x2)
+ vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+}
+
+static void init_video_context(struct running_context *context)
+{
+ context->sig_index = 0;
+ context->audio_idx = 1; /* stereo */
+ context->tvnormid = V4L2_STD_PAL_D;
+ context->pix = (struct v4l2_pix_format) {
+ .width = 720,
+ .height = 576,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 576 * 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .priv = 0
+ };
+}
+
+static int pd_video_open(struct file *file)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct poseidon *pd = video_get_drvdata(vfd);
+ struct front_face *front = NULL;
+ int ret = -ENOMEM;
+
+ mutex_lock(&pd->lock);
+ usb_autopm_get_interface(pd->interface);
+
+ if (vfd->vfl_type == VFL_TYPE_GRABBER
+ && !(pd->state & POSEIDON_STATE_ANALOG)) {
+ front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+ if (!front)
+ goto out;
+
+ pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */
+ init_video_context(&pd->video_data.context);
+
+ ret = pd_video_checkmode(pd);
+ if (ret < 0) {
+ kfree(front);
+ ret = -1;
+ goto out;
+ }
+
+ pd->state |= POSEIDON_STATE_ANALOG;
+ front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pd->video_data.users++;
+ set_debug_mode(vfd, debug_mode);
+
+ videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+ NULL, &front->queue_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,/* video is interlacd */
+ sizeof(struct videobuf_buffer),/*it's enough*/
+ front);
+ } else if (vfd->vfl_type == VFL_TYPE_VBI
+ && !(pd->state & POSEIDON_STATE_VBI)) {
+ front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+ if (!front)
+ goto out;
+
+ pd->state |= POSEIDON_STATE_VBI;
+ front->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ pd->vbi_data.front = front;
+ pd->vbi_data.users++;
+
+ videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+ NULL, &front->queue_lock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_NONE, /* vbi is NONE mode */
+ sizeof(struct videobuf_buffer),
+ front);
+ } else {
+ /* maybe add FM support here */
+ log("other ");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ front->pd = pd;
+ front->curr_frame = NULL;
+ INIT_LIST_HEAD(&front->active);
+ spin_lock_init(&front->queue_lock);
+
+ file->private_data = front;
+ kref_get(&pd->kref);
+
+ mutex_unlock(&pd->lock);
+ return 0;
+out:
+ usb_autopm_put_interface(pd->interface);
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int pd_video_release(struct file *file)
+{
+ struct front_face *front = file->private_data;
+ struct poseidon *pd = front->pd;
+ s32 cmd_status = 0;
+
+ logs(front);
+ mutex_lock(&pd->lock);
+
+ if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pd->state &= ~POSEIDON_STATE_ANALOG;
+
+ /* stop the device, and free the URBs */
+ usb_transfer_stop(&pd->video_data);
+ free_all_urb(&pd->video_data);
+
+ /* stop the firmware */
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+
+ pd->file_for_stream = NULL;
+ pd->video_data.users--;
+ } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ pd->state &= ~POSEIDON_STATE_VBI;
+ pd->vbi_data.front = NULL;
+ pd->vbi_data.users--;
+ }
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+
+ usb_autopm_put_interface(pd->interface);
+ mutex_unlock(&pd->lock);
+
+ kfree(front);
+ file->private_data = NULL;
+ kref_put(&pd->kref, poseidon_delete);
+ return 0;
+}
+
+static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_mmap_mapper(&front->q, vma);
+}
+
+unsigned int pd_video_poll(struct file *file, poll_table *table)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_poll_stream(file, &front->q, table);
+}
+
+ssize_t pd_video_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_read_stream(&front->q, buffer, count, ppos,
+ 0, file->f_flags & O_NONBLOCK);
+}
+
+/* This struct works for both VIDEO and VBI */
+static const struct v4l2_file_operations pd_video_fops = {
+ .owner = THIS_MODULE,
+ .open = pd_video_open,
+ .release = pd_video_release,
+ .read = pd_video_read,
+ .poll = pd_video_poll,
+ .mmap = pd_video_mmap,
+ .ioctl = video_ioctl2, /* maybe changed in future */
+};
+
+static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ /* Video format */
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+
+ /* Input */
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_enum_input = vidioc_enum_input,
+
+ /* Audio ioctls */
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+
+ /* Tuner ioctls */
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+
+ /* Buffer handlers */
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ /* Stream on/off */
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+
+ /* Control handling */
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+};
+
+static struct video_device pd_video_template = {
+ .name = "Telegent-Video",
+ .fops = &pd_video_fops,
+ .minor = -1,
+ .release = video_device_release,
+ .tvnorms = V4L2_STD_ALL,
+ .ioctl_ops = &pd_video_ioctl_ops,
+};
+
+struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (vfd == NULL)
+ return NULL;
+ *vfd = *tmp;
+ vfd->minor = -1;
+ vfd->v4l2_dev = &pd->v4l2_dev;
+ /*vfd->parent = &(pd->udev->dev); */
+ vfd->release = video_device_release;
+ video_set_drvdata(vfd, pd);
+ return vfd;
+}
+
+void destroy_video_device(struct video_device **v_dev)
+{
+ struct video_device *dev = *v_dev;
+
+ if (dev == NULL)
+ return;
+
+ if (video_is_registered(dev))
+ video_unregister_device(dev);
+ else
+ video_device_release(dev);
+ *v_dev = NULL;
+}
+
+void pd_video_exit(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ destroy_video_device(&video->v_dev);
+ destroy_video_device(&vbi->v_dev);
+ log();
+}
+
+int pd_video_init(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+ int ret = -ENOMEM;
+
+ video->v_dev = vdev_init(pd, &pd_video_template);
+ if (video->v_dev == NULL)
+ goto out;
+
+ ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1);
+ if (ret != 0)
+ goto out;
+
+ /* VBI uses the same template as video */
+ vbi->v_dev = vdev_init(pd, &pd_video_template);
+ if (vbi->v_dev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1);
+ if (ret != 0)
+ goto out;
+ log("register VIDEO/VBI devices");
+ return 0;
+out:
+ log("VIDEO/VBI devices register failed, : %d", ret);
+ pd_video_exit(pd);
+ return ret;
+}
+
diff --git a/drivers/media/video/tlg2300/vendorcmds.h b/drivers/media/video/tlg2300/vendorcmds.h
new file mode 100644
index 00000000000..ba6f4ae3b2c
--- /dev/null
+++ b/drivers/media/video/tlg2300/vendorcmds.h
@@ -0,0 +1,243 @@
+#ifndef VENDOR_CMD_H_
+#define VENDOR_CMD_H_
+
+#define BULK_ALTERNATE_IFACE (2)
+#define ISO_3K_BULK_ALTERNATE_IFACE (1)
+#define REQ_SET_CMD (0X00)
+#define REQ_GET_CMD (0X80)
+
+enum tlg__analog_audio_standard {
+ TLG_TUNE_ASTD_NONE = 0x00000000,
+ TLG_TUNE_ASTD_A2 = 0x00000001,
+ TLG_TUNE_ASTD_NICAM = 0x00000002,
+ TLG_TUNE_ASTD_EIAJ = 0x00000004,
+ TLG_TUNE_ASTD_BTSC = 0x00000008,
+ TLG_TUNE_ASTD_FM_US = 0x00000010,
+ TLG_TUNE_ASTD_FM_EUR = 0x00000020,
+ TLG_TUNE_ASTD_ALL = 0x0000003f
+};
+
+/*
+ * identifiers for Custom Parameter messages.
+ * @typedef cmd_custom_param_id_t
+ */
+enum cmd_custom_param_id {
+ CUST_PARM_ID_NONE = 0x00,
+ CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01,
+ CUST_PARM_ID_CONTRAST_CTRL = 0x02,
+ CUST_PARM_ID_HUE_CTRL = 0x03,
+ CUST_PARM_ID_SATURATION_CTRL = 0x04,
+ CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10,
+ CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11,
+ CUST_PARM_ID_MAX
+};
+
+struct tuner_custom_parameter_s {
+ uint16_t param_id; /* Parameter identifier */
+ uint16_t param_value; /* Parameter value */
+};
+
+struct tuner_ber_rate_s {
+ uint32_t ber_rate; /* BER sample rate in seconds */
+};
+
+struct tuner_atv_sig_stat_s {
+ uint32_t sig_present;
+ uint32_t sig_locked;
+ uint32_t sig_lock_busy;
+ uint32_t sig_strength; /* milliDb */
+ uint32_t tv_audio_chan; /* mono/stereo/sap*/
+ uint32_t mvision_stat; /* macrovision status */
+};
+
+struct tuner_dtv_sig_stat_s {
+ uint32_t sig_present; /* Boolean*/
+ uint32_t sig_locked; /* Boolean */
+ uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */
+ uint32_t sig_strength; /* milliDb*/
+};
+
+struct tuner_fm_sig_stat_s {
+ uint32_t sig_present; /* Boolean*/
+ uint32_t sig_locked; /* Boolean */
+ uint32_t sig_lock_busy; /* Boolean */
+ uint32_t sig_stereo_mono;/* TBD*/
+ uint32_t sig_strength; /* milliDb*/
+};
+
+enum _tag_tlg_tune_srv_cmd {
+ TLG_TUNE_PLAY_SVC_START = 1,
+ TLG_TUNE_PLAY_SVC_STOP
+};
+
+enum _tag_tune_atv_audio_mode_caps {
+ TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001,
+ TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002,
+ TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/
+ TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/
+ TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040
+};
+
+
+enum _tag_tuner_atv_audio_rates {
+ ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/
+ ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/
+ ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/
+ ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */
+};
+
+enum _tag_tune_atv_vid_res_caps {
+ TLG_TUNE_VID_RES_NONE = 0x00000000,
+ TLG_TUNE_VID_RES_720 = 0x00000001,
+ TLG_TUNE_VID_RES_704 = 0x00000002,
+ TLG_TUNE_VID_RES_360 = 0x00000004
+};
+
+enum _tag_tuner_analog_video_format {
+ TLG_TUNER_VID_FORMAT_YUV = 0x00000001,
+ TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002,
+ TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004,
+};
+
+enum tlg_ext_audio_support {
+ TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */
+ TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/
+};
+
+enum {
+ TLG_MODE_NONE = 0x00, /* No Mode specified*/
+ TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/
+ TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/
+ TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/
+ TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/
+ TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/
+};
+
+enum tlg_signal_sources_t {
+ TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */
+ TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */
+ TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/
+ TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */
+ TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */
+};
+
+enum tuner_analog_video_standard {
+ TLG_TUNE_VSTD_NONE = 0x00000000,
+ TLG_TUNE_VSTD_NTSC_M = 0x00000001,
+ TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */
+ TLG_TUNE_VSTD_PAL_B = 0x00000010,
+ TLG_TUNE_VSTD_PAL_D = 0x00000020,
+ TLG_TUNE_VSTD_PAL_G = 0x00000040,
+ TLG_TUNE_VSTD_PAL_H = 0x00000080,
+ TLG_TUNE_VSTD_PAL_I = 0x00000100,
+ TLG_TUNE_VSTD_PAL_M = 0x00000200,
+ TLG_TUNE_VSTD_PAL_N = 0x00000400,
+ TLG_TUNE_VSTD_SECAM_B = 0x00001000,
+ TLG_TUNE_VSTD_SECAM_D = 0x00002000,
+ TLG_TUNE_VSTD_SECAM_G = 0x00004000,
+ TLG_TUNE_VSTD_SECAM_H = 0x00008000,
+ TLG_TUNE_VSTD_SECAM_K = 0x00010000,
+ TLG_TUNE_VSTD_SECAM_K1 = 0x00020000,
+ TLG_TUNE_VSTD_SECAM_L = 0x00040000,
+ TLG_TUNE_VSTD_SECAM_L1 = 0x00080000,
+ TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000
+};
+
+enum tlg_mode_caps {
+ TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */
+ TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */
+ TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/
+ TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */
+ TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */
+};
+
+enum poseidon_vendor_cmds {
+ LAST_CMD_STAT = 0x00,
+ GET_CHIP_ID = 0x01,
+ GET_FW_ID = 0x02,
+ PRODUCT_CAPS = 0x03,
+
+ TUNE_MODE_CAP_ATV = 0x10,
+ TUNE_MODE_CAP_ATVCOMP = 0X10,
+ TUNE_MODE_CAP_DVBT = 0x10,
+ TUNE_MODE_CAP_FM = 0x10,
+ TUNE_MODE_SELECT = 0x11,
+ TUNE_FREQ_SELECT = 0x12,
+ SGNL_SRC_SEL = 0x13,
+
+ VIDEO_STD_SEL = 0x14,
+ VIDEO_STREAM_FMT_SEL = 0x15,
+ VIDEO_ROSOLU_AVAIL = 0x16,
+ VIDEO_ROSOLU_SEL = 0x17,
+ VIDEO_CONT_PROTECT = 0x20,
+
+ VCR_TIMING_MODSEL = 0x21,
+ EXT_AUDIO_CAP = 0x22,
+ EXT_AUDIO_SEL = 0x23,
+ TEST_PATTERN_SEL = 0x24,
+ VBI_DATA_SEL = 0x25,
+ AUDIO_SAMPLE_RATE_CAP = 0x28,
+ AUDIO_SAMPLE_RATE_SEL = 0x29,
+ TUNER_AUD_MODE = 0x2a,
+ TUNER_AUD_MODE_AVAIL = 0x2b,
+ TUNER_AUD_ANA_STD = 0x2c,
+ TUNER_CUSTOM_PARAMETER = 0x2f,
+
+ DVBT_TUNE_MODE_SEL = 0x30,
+ DVBT_BANDW_CAP = 0x31,
+ DVBT_BANDW_SEL = 0x32,
+ DVBT_GUARD_INTERV_CAP = 0x33,
+ DVBT_GUARD_INTERV_SEL = 0x34,
+ DVBT_MODULATION_CAP = 0x35,
+ DVBT_MODULATION_SEL = 0x36,
+ DVBT_INNER_FEC_RATE_CAP = 0x37,
+ DVBT_INNER_FEC_RATE_SEL = 0x38,
+ DVBT_TRANS_MODE_CAP = 0x39,
+ DVBT_TRANS_MODE_SEL = 0x3a,
+ DVBT_SEARCH_RANG = 0x3c,
+
+ TUNER_SETUP_ANALOG = 0x40,
+ TUNER_SETUP_DIGITAL = 0x41,
+ TUNER_SETUP_FM_RADIO = 0x42,
+ TAKE_REQUEST = 0x43, /* Take effect of the command */
+ PLAY_SERVICE = 0x44, /* Play start or Play stop */
+ TUNER_STATUS = 0x45,
+ TUNE_PROP_DVBT = 0x46,
+ ERR_RATE_STATS = 0x47,
+ TUNER_BER_RATE = 0x48,
+
+ SCAN_CAPS = 0x50,
+ SCAN_SETUP = 0x51,
+ SCAN_SERVICE = 0x52,
+ SCAN_STATS = 0x53,
+
+ PID_SET = 0x58,
+ PID_UNSET = 0x59,
+ PID_LIST = 0x5a,
+
+ IRD_CAP = 0x60,
+ IRD_MODE_SEL = 0x61,
+ IRD_SETUP = 0x62,
+
+ PTM_MODE_CAP = 0x70,
+ PTM_MODE_SEL = 0x71,
+ PTM_SERVICE = 0x72,
+ TUNER_REG_SCRIPT = 0x73,
+ CMD_CHIP_RST = 0x74,
+};
+
+enum tlg_bw {
+ TLG_BW_5 = 5,
+ TLG_BW_6 = 6,
+ TLG_BW_7 = 7,
+ TLG_BW_8 = 8,
+ TLG_BW_12 = 12,
+ TLG_BW_15 = 15
+};
+
+struct cmd_firmware_vers_s {
+ uint8_t fw_rev_major;
+ uint8_t fw_rev_minor;
+ uint16_t fw_patch;
+};
+#endif /* VENDOR_CMD_H_ */
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 5b3eaa16afd..c4dab6cfd94 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -1078,6 +1078,7 @@ static int tuner_probe(struct i2c_client *client,
goto register_client;
}
+ kfree(t);
return -ENODEV;
case 0x42:
case 0x43:
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index d533ea57e7b..0a877497b93 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -680,10 +680,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n",
tvee->model, tvee->rev_str, tvee->serial_number);
if (tvee->has_MAC_address == 1)
- tveeprom_info("MAC address is %02X-%02X-%02X-%02X-%02X-%02X\n",
- tvee->MAC_address[0], tvee->MAC_address[1],
- tvee->MAC_address[2], tvee->MAC_address[3],
- tvee->MAC_address[4], tvee->MAC_address[5]);
+ tveeprom_info("MAC address is %pM\n", tvee->MAC_address);
tveeprom_info("tuner model is %s (idx %d, type %d)\n",
t_name1, tuner1, tvee->tuner_type);
tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
new file mode 100644
index 00000000000..5a878bca02d
--- /dev/null
+++ b/drivers/media/video/tvp7002.c
@@ -0,0 +1,1187 @@
+/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
+ * Digitizer with Horizontal PLL registers
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
+ *
+ * This code is partially based upon the TVP5150 driver
+ * written by Mauro Carvalho Chehab (mchehab@infradead.org),
+ * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
+ * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by
+ * Muralidharan Karicheri and Snehaprabha Narnakaje (TI).
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/tvp7002.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include "tvp7002_reg.h"
+
+MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
+MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
+MODULE_LICENSE("GPL");
+
+/* Module Name */
+#define TVP7002_MODULE_NAME "tvp7002"
+
+/* I2C retry attempts */
+#define I2C_RETRY_COUNT (5)
+
+/* End of registers */
+#define TVP7002_EOR 0x5c
+
+/* Read write definition for registers */
+#define TVP7002_READ 0
+#define TVP7002_WRITE 1
+#define TVP7002_RESERVED 2
+
+/* Interlaced vs progressive mask and shift */
+#define TVP7002_IP_SHIFT 5
+#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT)
+
+/* Shift for CPL and LPF registers */
+#define TVP7002_CL_SHIFT 8
+#define TVP7002_CL_MASK 0x0f
+
+/* Debug functions */
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* Structure for register values */
+struct i2c_reg_value {
+ u8 reg;
+ u8 value;
+ u8 type;
+};
+
+/*
+ * Register default values (according to tvp7002 datasheet)
+ * In the case of read-only registers, the value (0xff) is
+ * never written. R/W functionality is controlled by the
+ * writable bit in the register struct definition.
+ */
+static const struct i2c_reg_value tvp7002_init_default[] = {
+ { TVP7002_CHIP_REV, 0xff, TVP7002_READ },
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE },
+ { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE },
+ { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE },
+ { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE },
+ { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE },
+ { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE },
+ { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ },
+ { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE },
+ { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE },
+ { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE },
+ { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE },
+ { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE },
+ { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE },
+ { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE },
+ { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE },
+ { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE },
+ { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE },
+ { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ },
+ { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE },
+ { 0x29, 0x08, TVP7002_RESERVED },
+ { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE },
+ /* PWR_CTL is controlled only by the probe and reset functions */
+ { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED },
+ { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE },
+ { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
+ { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE },
+ { TVP7002_RGB_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
+ { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { 0x32, 0x18, TVP7002_RESERVED },
+ { 0x33, 0x60, TVP7002_RESERVED },
+ { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED },
+ { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE },
+ { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE },
+ { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ },
+ { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ },
+ { TVP7002_HSYNC_W, 0xff, TVP7002_READ },
+ { TVP7002_VSYNC_W, 0xff, TVP7002_READ },
+ { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE },
+ { 0x3e, 0x60, TVP7002_RESERVED },
+ { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE },
+ { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE },
+ { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE },
+ { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE },
+ { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE },
+ { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE },
+ { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE },
+ { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE },
+ { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE },
+ { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE },
+ { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE },
+ { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE },
+ { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE },
+ { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE },
+ { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE },
+ /* This signals end of register values */
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 480P */
+static const struct i2c_reg_value tvp7002_parms_480P[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0a, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 576P */
+static const struct i2c_reg_value tvp7002_parms_576P[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 1080I60 */
+static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 1080P60 */
+static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 1080I50 */
+static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 720P60 */
+static const struct i2c_reg_value tvp7002_parms_720P60[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x02, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 720P50 */
+static const struct i2c_reg_value tvp7002_parms_720P50[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0c, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Struct list for available formats */
+static const struct v4l2_fmtdesc tvp7002_fmt_list[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit UYVY 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(tvp7002_fmt_list)
+
+/* Preset definition for handling device operation */
+struct tvp7002_preset_definition {
+ u32 preset;
+ const struct i2c_reg_value *p_settings;
+ enum v4l2_colorspace color_space;
+ enum v4l2_field scanmode;
+ u16 progressive;
+ u16 lines_per_frame;
+ u16 cpl_min;
+ u16 cpl_max;
+};
+
+/* Struct list for digital video presets */
+static const struct tvp7002_preset_definition tvp7002_presets[] = {
+ {
+ V4L2_DV_720P60,
+ tvp7002_parms_720P60,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x2EE,
+ 135,
+ 153
+ },
+ {
+ V4L2_DV_1080I60,
+ tvp7002_parms_1080I60,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_INTERLACED,
+ 0,
+ 0x465,
+ 181,
+ 205
+ },
+ {
+ V4L2_DV_1080I50,
+ tvp7002_parms_1080I50,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_INTERLACED,
+ 0,
+ 0x465,
+ 217,
+ 245
+ },
+ {
+ V4L2_DV_720P50,
+ tvp7002_parms_720P50,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x2EE,
+ 163,
+ 183
+ },
+ {
+ V4L2_DV_1080P60,
+ tvp7002_parms_1080P60,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x465,
+ 90,
+ 102
+ },
+ {
+ V4L2_DV_480P59_94,
+ tvp7002_parms_480P,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x20D,
+ 0xffff,
+ 0xffff
+ },
+ {
+ V4L2_DV_576P50,
+ tvp7002_parms_576P,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x271,
+ 0xffff,
+ 0xffff
+ }
+};
+
+#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets)
+
+/* Device definition */
+struct tvp7002 {
+ struct v4l2_subdev sd;
+ const struct tvp7002_config *pdata;
+
+ int ver;
+ int streaming;
+
+ struct v4l2_pix_format pix;
+ const struct tvp7002_preset_definition *current_preset;
+ u8 gain;
+};
+
+/*
+ * to_tvp7002 - Obtain device handler TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Returns device handler tvp7002.
+ */
+static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tvp7002, sd);
+}
+
+/*
+ * tvp7002_read - Read a value from a register in an TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP7002 register address
+ * @dst: pointer to 8-bit destination
+ *
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ int retry;
+ int error;
+
+ for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
+ error = i2c_smbus_read_byte_data(c, addr);
+
+ if (error >= 0) {
+ *dst = (u8)error;
+ return 0;
+ }
+
+ msleep_interruptible(10);
+ }
+ v4l2_err(sd, "TVP7002 read error %d\n", error);
+ return error;
+}
+
+/*
+ * tvp7002_read_err() - Read a register value with error code
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @reg: destination register
+ * @val: value to be read
+ * @error: pointer to error value
+ *
+ * Read a value in a register and save error value in pointer.
+ * Also update the register table if successful
+ */
+static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg,
+ u8 *dst, int *err)
+{
+ if (!*err)
+ *err = tvp7002_read(sd, reg, dst);
+}
+
+/*
+ * tvp7002_write() - Write a value to a register in TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ * @addr: TVP7002 register address
+ * @value: value to be written to the register
+ *
+ * Write a value to a register in an TVP7002 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
+{
+ struct i2c_client *c;
+ int retry;
+ int error;
+
+ c = v4l2_get_subdevdata(sd);
+
+ for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
+ error = i2c_smbus_write_byte_data(c, addr, value);
+
+ if (error >= 0)
+ return 0;
+
+ v4l2_warn(sd, "Write: retry ... %d\n", retry);
+ msleep_interruptible(10);
+ }
+ v4l2_err(sd, "TVP7002 write error %d\n", error);
+ return error;
+}
+
+/*
+ * tvp7002_write_err() - Write a register value with error code
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @reg: destination register
+ * @val: value to be written
+ * @error: pointer to error value
+ *
+ * Write a value in a register and save error value in pointer.
+ * Also update the register table if successful
+ */
+static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg,
+ u8 val, int *err)
+{
+ if (!*err)
+ *err = tvp7002_write(sd, reg, val);
+}
+
+/*
+ * tvp7002_g_chip_ident() - Get chip identification number
+ * @sd: ptr to v4l2_subdev struct
+ * @chip: ptr to v4l2_dbg_chip_ident struct
+ *
+ * Obtains the chip's identification number.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ u8 rev;
+ int error;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev);
+
+ if (error < 0)
+ return error;
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev);
+}
+
+/*
+ * tvp7002_write_inittab() - Write initialization values
+ * @sd: ptr to v4l2_subdev struct
+ * @regs: ptr to i2c_reg_value struct
+ *
+ * Write initialization values.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_write_inittab(struct v4l2_subdev *sd,
+ const struct i2c_reg_value *regs)
+{
+ int error = 0;
+
+ /* Initialize the first (defined) registers */
+ while (TVP7002_EOR != regs->reg) {
+ if (TVP7002_WRITE == regs->type)
+ tvp7002_write_err(sd, regs->reg, regs->value, &error);
+ regs++;
+ }
+
+ return error;
+}
+
+/*
+ * tvp7002_s_dv_preset() - Set digital video preset
+ * @sd: ptr to v4l2_subdev struct
+ * @std: ptr to v4l2_dv_preset struct
+ *
+ * Set the digital video preset for a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ u32 preset;
+ int i;
+
+ for (i = 0; i < NUM_PRESETS; i++) {
+ preset = tvp7002_presets[i].preset;
+ if (preset == dv_preset->preset) {
+ device->current_preset = &tvp7002_presets[i];
+ return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings);
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * tvp7002_g_ctrl() - Get a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Get a control for a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = device->gain;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * tvp7002_s_ctrl() - Set a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Set a control in TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ int error = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ tvp7002_write_err(sd, TVP7002_R_FINE_GAIN,
+ ctrl->value & 0xff, &error);
+ tvp7002_write_err(sd, TVP7002_G_FINE_GAIN,
+ ctrl->value & 0xff, &error);
+ tvp7002_write_err(sd, TVP7002_B_FINE_GAIN,
+ ctrl->value & 0xff, &error);
+
+ if (error < 0)
+ return error;
+
+ /* Set only after knowing there is no error */
+ device->gain = ctrl->value & 0xff;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * tvp7002_queryctrl() - Query a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_queryctrl struct
+ *
+ * Query a control of a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register read fails.
+ */
+static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_GAIN:
+ /*
+ * Gain is supported [0-255, default=0, step=1]
+ */
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ struct v4l2_dv_enum_preset e_preset;
+ struct v4l2_pix_format *pix;
+ int error = 0;
+
+ pix = &f->fmt.pix;
+
+ /* Calculate height and width based on current standard */
+ error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset);
+ if (error)
+ return -EINVAL;
+
+ pix->width = e_preset.width;
+ pix->height = e_preset.height;
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ pix->field = device->current_preset->scanmode;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = device->current_preset->color_space;
+ pix->priv = 0;
+
+ v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d"
+ "Width - %d, Height - %d", "8-bit UYVY 4:2:2 Format",
+ pix->bytesperline, pix->width, pix->height);
+ return error;
+}
+
+/*
+ * tvp7002_s_fmt() - V4L2 decoder interface handler for s_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int tvp7002_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct tvp7002 *decoder = to_tvp7002(sd);
+ int rval;
+
+ rval = tvp7002_try_fmt_cap(sd, f);
+ if (!rval)
+ decoder->pix = f->fmt.pix;
+ return rval;
+}
+
+/*
+ * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the decoder's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct tvp7002 *decoder = to_tvp7002(sd);
+
+ f->fmt.pix = decoder->pix;
+
+ v4l2_dbg(1, debug, sd, "Current FMT: bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->pix.bytesperline,
+ decoder->pix.width, decoder->pix.height);
+ return 0;
+}
+
+/*
+ * tvp7002_query_dv_preset() - query DV preset
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 v4l2_dv_preset
+ *
+ * Returns the current DV preset by TVP7002. If no active input is
+ * detected, returns -EINVAL
+ */
+static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *qpreset)
+{
+ const struct tvp7002_preset_definition *presets = tvp7002_presets;
+ struct v4l2_dv_enum_preset e_preset;
+ struct tvp7002 *device;
+ u8 progressive;
+ u32 lpfr;
+ u32 cpln;
+ int error = 0;
+ u8 lpf_lsb;
+ u8 lpf_msb;
+ u8 cpl_lsb;
+ u8 cpl_msb;
+ int index;
+
+ device = to_tvp7002(sd);
+
+ /* Read standards from device registers */
+ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error);
+ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error);
+
+ if (error < 0)
+ return error;
+
+ tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error);
+ tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error);
+
+ if (error < 0)
+ return error;
+
+ /* Get lines per frame, clocks per line and interlaced/progresive */
+ lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT);
+ cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT);
+ progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT;
+
+ /* Do checking of video modes */
+ for (index = 0; index < NUM_PRESETS; index++, presets++)
+ if (lpfr == presets->lines_per_frame &&
+ progressive == presets->progressive) {
+ if (presets->cpl_min == 0xffff)
+ break;
+ if (cpln >= presets->cpl_min && cpln <= presets->cpl_max)
+ break;
+ }
+
+ if (index == NUM_PRESETS) {
+ v4l2_err(sd, "querystd error, lpf = %x, cpl = %x\n",
+ lpfr, cpln);
+ return -EINVAL;
+ }
+
+ if (v4l_fill_dv_preset_info(presets->preset, &e_preset))
+ return -EINVAL;
+
+ /* Set values in found preset */
+ qpreset->preset = presets->preset;
+
+ /* Update lines per frame and clocks per line info */
+ v4l2_dbg(1, debug, sd, "Current preset: %d %d",
+ e_preset.width, e_preset.height);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * tvp7002_g_register() - Get the value of a register
+ * @sd: ptr to v4l2_subdev struct
+ * @vreg: ptr to v4l2_dbg_register struct
+ *
+ * Get the value of a TVP7002 decoder device register.
+ * Returns zero when successful, -EINVAL if register read fails or
+ * access to I2C client fails, -EPERM if the call is not allowed
+ * by diabled CAP_SYS_ADMIN.
+ */
+static int tvp7002_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 val;
+ int ret;
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ret = tvp7002_read(sd, reg->reg & 0xff, &val);
+ reg->val = val;
+ return ret;
+}
+
+/*
+ * tvp7002_s_register() - set a control
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to v4l2_control struct
+ *
+ * Get the value of a TVP7002 decoder device register.
+ * Returns zero when successful, -EINVAL if register read fails or
+ * -EPERM if call not allowed.
+ */
+static int tvp7002_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
+}
+#endif
+
+/*
+ * tvp7002_enum_fmt() - Enum supported formats
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: pointer to format struct
+ *
+ * Enumerate supported formats.
+ */
+
+static int tvp7002_enum_fmt(struct v4l2_subdev *sd,
+ struct v4l2_fmtdesc *fmtdesc)
+{
+ /* Check requested format index is within range */
+ if (fmtdesc->index < 0 || fmtdesc->index >= NUM_FORMATS)
+ return -EINVAL;
+ *fmtdesc = tvp7002_fmt_list[fmtdesc->index];
+
+ return 0;
+}
+
+/*
+ * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: streaming enable or disable
+ *
+ * Sets streaming to enable or disable, if possible.
+ */
+static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ int error = 0;
+
+ if (device->streaming == enable)
+ return 0;
+
+ if (enable) {
+ /* Set output state on (low impedance means stream on) */
+ error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
+ device->streaming = enable;
+ } else {
+ /* Set output state off (high impedance means stream off) */
+ error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03);
+ if (error)
+ v4l2_dbg(1, debug, sd, "Unable to stop streaming\n");
+
+ device->streaming = enable;
+ }
+
+ return error;
+}
+
+/*
+ * tvp7002_log_status() - Print information about register settings
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Log register values of a TVP7002 decoder device.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_log_status(struct v4l2_subdev *sd)
+{
+ const struct tvp7002_preset_definition *presets = tvp7002_presets;
+ struct tvp7002 *device = to_tvp7002(sd);
+ struct v4l2_dv_enum_preset e_preset;
+ struct v4l2_dv_preset detected;
+ int i;
+
+ detected.preset = V4L2_DV_INVALID;
+ /* Find my current standard*/
+ tvp7002_query_dv_preset(sd, &detected);
+
+ /* Print standard related code values */
+ for (i = 0; i < NUM_PRESETS; i++, presets++)
+ if (presets->preset == detected.preset)
+ break;
+
+ if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset))
+ return -EINVAL;
+
+ v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name);
+ v4l2_info(sd, " Pixels per line: %u\n", e_preset.width);
+ v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height);
+ if (i == NUM_PRESETS) {
+ v4l2_info(sd, "Detected DV Preset: None\n");
+ } else {
+ if (v4l_fill_dv_preset_info(presets->preset, &e_preset))
+ return -EINVAL;
+ v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name);
+ v4l2_info(sd, " Pixels per line: %u\n", e_preset.width);
+ v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height);
+ }
+ v4l2_info(sd, "Streaming enabled: %s\n",
+ device->streaming ? "yes" : "no");
+
+ /* Print the current value of the gain control */
+ v4l2_info(sd, "Gain: %u\n", device->gain);
+
+ return 0;
+}
+
+/* V4L2 core operation handlers */
+static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
+ .g_chip_ident = tvp7002_g_chip_ident,
+ .log_status = tvp7002_log_status,
+ .g_ctrl = tvp7002_g_ctrl,
+ .s_ctrl = tvp7002_s_ctrl,
+ .queryctrl = tvp7002_queryctrl,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp7002_g_register,
+ .s_register = tvp7002_s_register,
+#endif
+};
+
+/* Specific video subsystem operation handlers */
+static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
+ .s_dv_preset = tvp7002_s_dv_preset,
+ .query_dv_preset = tvp7002_query_dv_preset,
+ .s_stream = tvp7002_s_stream,
+ .g_fmt = tvp7002_g_fmt,
+ .s_fmt = tvp7002_s_fmt,
+ .enum_fmt = tvp7002_enum_fmt,
+};
+
+/* V4L2 top level operation handlers */
+static const struct v4l2_subdev_ops tvp7002_ops = {
+ .core = &tvp7002_core_ops,
+ .video = &tvp7002_video_ops,
+};
+
+static struct tvp7002 tvp7002_dev = {
+ .streaming = 0,
+
+ .pix = {
+ .width = 1280,
+ .height = 720,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_NONE,
+ .bytesperline = 1280 * 2,
+ .sizeimage = 1280 * 2 * 720,
+ .colorspace = V4L2_COLORSPACE_REC709,
+ },
+
+ .current_preset = tvp7002_presets,
+ .gain = 0,
+};
+
+/*
+ * tvp7002_probe - Probe a TVP7002 device
+ * @sd: ptr to v4l2_subdev struct
+ * @ctrl: ptr to i2c_device_id struct
+ *
+ * Initialize the TVP7002 device
+ * Returns zero when successful, -EINVAL if register read fails or
+ * -EIO if i2c access is not available.
+ */
+static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ struct tvp7002 *device;
+ struct v4l2_dv_preset preset;
+ int polarity_a;
+ int polarity_b;
+ u8 revision;
+
+ int error;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(c->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ if (!c->dev.platform_data) {
+ v4l_err(c, "No platform data!!\n");
+ return -ENODEV;
+ }
+
+ device = kmalloc(sizeof(struct tvp7002), GFP_KERNEL);
+
+ if (!device)
+ return -ENOMEM;
+
+ *device = tvp7002_dev;
+ sd = &device->sd;
+ device->pdata = c->dev.platform_data;
+
+ /* Tell v4l2 the device is ready */
+ v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
+ v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
+ c->addr, c->adapter->name);
+
+ error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision);
+ if (error < 0)
+ goto found_error;
+
+ /* Get revision number */
+ v4l2_info(sd, "Rev. %02x detected.\n", revision);
+ if (revision != 0x02)
+ v4l2_info(sd, "Unknown revision detected.\n");
+
+ /* Initializes TVP7002 to its default values */
+ error = tvp7002_write_inittab(sd, tvp7002_init_default);
+
+ if (error < 0)
+ goto found_error;
+
+ /* Set polarity information after registers have been set */
+ polarity_a = 0x20 | device->pdata->hs_polarity << 5
+ | device->pdata->vs_polarity << 2;
+ error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a);
+ if (error < 0)
+ goto found_error;
+
+ polarity_b = 0x01 | device->pdata->fid_polarity << 2
+ | device->pdata->sog_polarity << 1
+ | device->pdata->clk_polarity;
+ error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b);
+ if (error < 0)
+ goto found_error;
+
+ /* Set registers according to default video mode */
+ preset.preset = device->current_preset->preset;
+ error = tvp7002_s_dv_preset(sd, &preset);
+
+found_error:
+ if (error < 0)
+ kfree(device);
+
+ return error;
+}
+
+/*
+ * tvp7002_remove - Remove TVP7002 device support
+ * @c: ptr to i2c_client struct
+ *
+ * Reset the TVP7002 device
+ * Returns zero.
+ */
+static int tvp7002_remove(struct i2c_client *c)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
+ struct tvp7002 *device = to_tvp7002(sd);
+
+ v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter"
+ "on address 0x%x\n", c->addr);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(device);
+ return 0;
+}
+
+/* I2C Device ID table */
+static const struct i2c_device_id tvp7002_id[] = {
+ { "tvp7002", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp7002_id);
+
+/* I2C driver data */
+static struct i2c_driver tvp7002_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TVP7002_MODULE_NAME,
+ },
+ .probe = tvp7002_probe,
+ .remove = tvp7002_remove,
+ .id_table = tvp7002_id,
+};
+
+/*
+ * tvp7002_init - Initialize driver via I2C interface
+ *
+ * Register the TVP7002 driver.
+ * Return 0 on success or error code on failure.
+ */
+static int __init tvp7002_init(void)
+{
+ return i2c_add_driver(&tvp7002_driver);
+}
+
+/*
+ * tvp7002_exit - Remove driver via I2C interface
+ *
+ * Unregister the TVP7002 driver.
+ * Returns nothing.
+ */
+static void __exit tvp7002_exit(void)
+{
+ i2c_del_driver(&tvp7002_driver);
+}
+
+module_init(tvp7002_init);
+module_exit(tvp7002_exit);
diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/video/tvp7002_reg.h
new file mode 100644
index 00000000000..0e34ca9bccf
--- /dev/null
+++ b/drivers/media/video/tvp7002_reg.h
@@ -0,0 +1,150 @@
+/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
+ * Digitizer with Horizontal PLL registers
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
+ *
+ * This code is partially based upon the TVP5150 driver
+ * written by Mauro Carvalho Chehab (mchehab@infradead.org),
+ * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
+ * and the TVP7002 driver in the TI LSP 2.10.00.14
+ *
+ * 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.
+ */
+
+/* Naming conventions
+ * ------------------
+ *
+ * FDBK: Feedback
+ * DIV: Divider
+ * CTL: Control
+ * SEL: Select
+ * IN: Input
+ * OUT: Output
+ * R: Red
+ * G: Green
+ * B: Blue
+ * OFF: Offset
+ * THRS: Threshold
+ * DGTL: Digital
+ * LVL: Level
+ * PWR: Power
+ * MVIS: Macrovision
+ * W: Width
+ * H: Height
+ * ALGN: Alignment
+ * CLK: Clocks
+ * TOL: Tolerance
+ * BWTH: Bandwidth
+ * COEF: Coefficient
+ * STAT: Status
+ * AUTO: Automatic
+ * FLD: Field
+ * L: Line
+ */
+
+#define TVP7002_CHIP_REV 0x00
+#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01
+#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02
+#define TVP7002_HPLL_CRTL 0x03
+#define TVP7002_HPLL_PHASE_SEL 0x04
+#define TVP7002_CLAMP_START 0x05
+#define TVP7002_CLAMP_W 0x06
+#define TVP7002_HSYNC_OUT_W 0x07
+#define TVP7002_B_FINE_GAIN 0x08
+#define TVP7002_G_FINE_GAIN 0x09
+#define TVP7002_R_FINE_GAIN 0x0a
+#define TVP7002_B_FINE_OFF_MSBS 0x0b
+#define TVP7002_G_FINE_OFF_MSBS 0x0c
+#define TVP7002_R_FINE_OFF_MSBS 0x0d
+#define TVP7002_SYNC_CTL_1 0x0e
+#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f
+#define TVP7002_SYNC_ON_G_THRS 0x10
+#define TVP7002_SYNC_SEPARATOR_THRS 0x11
+#define TVP7002_HPLL_PRE_COAST 0x12
+#define TVP7002_HPLL_POST_COAST 0x13
+#define TVP7002_SYNC_DETECT_STAT 0x14
+#define TVP7002_OUT_FORMATTER 0x15
+#define TVP7002_MISC_CTL_1 0x16
+#define TVP7002_MISC_CTL_2 0x17
+#define TVP7002_MISC_CTL_3 0x18
+#define TVP7002_IN_MUX_SEL_1 0x19
+#define TVP7002_IN_MUX_SEL_2 0x1a
+#define TVP7002_B_AND_G_COARSE_GAIN 0x1b
+#define TVP7002_R_COARSE_GAIN 0x1c
+#define TVP7002_FINE_OFF_LSBS 0x1d
+#define TVP7002_B_COARSE_OFF 0x1e
+#define TVP7002_G_COARSE_OFF 0x1f
+#define TVP7002_R_COARSE_OFF 0x20
+#define TVP7002_HSOUT_OUT_START 0x21
+#define TVP7002_MISC_CTL_4 0x22
+#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23
+#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24
+#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25
+#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26
+#define TVP7002_DGTL_ALC_OUT_MSBS 0x27
+#define TVP7002_AUTO_LVL_CTL_FILTER 0x28
+/* Reserved 0x29*/
+#define TVP7002_FINE_CLAMP_CTL 0x2a
+#define TVP7002_PWR_CTL 0x2b
+#define TVP7002_ADC_SETUP 0x2c
+#define TVP7002_COARSE_CLAMP_CTL 0x2d
+#define TVP7002_SOG_CLAMP 0x2e
+#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f
+#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30
+#define TVP7002_ALC_PLACEMENT 0x31
+/* Reserved 0x32 */
+/* Reserved 0x33 */
+#define TVP7002_MVIS_STRIPPER_W 0x34
+#define TVP7002_VSYNC_ALGN 0x35
+#define TVP7002_SYNC_BYPASS 0x36
+#define TVP7002_L_FRAME_STAT_LSBS 0x37
+#define TVP7002_L_FRAME_STAT_MSBS 0x38
+#define TVP7002_CLK_L_STAT_LSBS 0x39
+#define TVP7002_CLK_L_STAT_MSBS 0x3a
+#define TVP7002_HSYNC_W 0x3b
+#define TVP7002_VSYNC_W 0x3c
+#define TVP7002_L_LENGTH_TOL 0x3d
+/* Reserved 0x3e */
+#define TVP7002_VIDEO_BWTH_CTL 0x3f
+#define TVP7002_AVID_START_PIXEL_LSBS 0x40
+#define TVP7002_AVID_START_PIXEL_MSBS 0x41
+#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42
+#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43
+#define TVP7002_VBLK_F_0_START_L_OFF 0x44
+#define TVP7002_VBLK_F_1_START_L_OFF 0x45
+#define TVP7002_VBLK_F_0_DURATION 0x46
+#define TVP7002_VBLK_F_1_DURATION 0x47
+#define TVP7002_FBIT_F_0_START_L_OFF 0x48
+#define TVP7002_FBIT_F_1_START_L_OFF 0x49
+#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a
+#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b
+#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c
+#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d
+#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e
+#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f
+#define TVP7002_YUV_U_G_COEF_LSBS 0x50
+#define TVP7002_YUV_U_G_COEF_MSBS 0x51
+#define TVP7002_YUV_U_B_COEF_LSBS 0x52
+#define TVP7002_YUV_U_B_COEF_MSBS 0x53
+#define TVP7002_YUV_U_R_COEF_LSBS 0x54
+#define TVP7002_YUV_U_R_COEF_MSBS 0x55
+#define TVP7002_YUV_V_G_COEF_LSBS 0x56
+#define TVP7002_YUV_V_G_COEF_MSBS 0x57
+#define TVP7002_YUV_V_B_COEF_LSBS 0x58
+#define TVP7002_YUV_V_B_COEF_MSBS 0x59
+#define TVP7002_YUV_V_R_COEF_LSBS 0x5a
+#define TVP7002_YUV_V_R_COEF_MSBS 0x5b
+
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 5b801a6e1ee..76be733eabf 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -233,10 +233,10 @@ struct tw9910_hsync_ctrl {
};
struct tw9910_priv {
- struct v4l2_subdev subdev;
- struct tw9910_video_info *info;
- const struct tw9910_scale_ctrl *scale;
- u32 revision;
+ struct v4l2_subdev subdev;
+ struct tw9910_video_info *info;
+ const struct tw9910_scale_ctrl *scale;
+ u32 revision;
};
static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index 1054546db90..7c17ec63c5d 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -1487,7 +1487,7 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
usbvision->vbi = usbvision_vdev_init(usbvision,
&usbvision_vbi_template,
"USBVision VBI");
- if (usbvision->vdev == NULL) {
+ if (usbvision->vbi == NULL) {
goto err_exit;
}
if (video_register_device(usbvision->vbi,
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index ec8ef8c5560..3b2e7800d56 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -23,9 +23,13 @@
#include "uvcvideo.h"
-#define UVC_CTRL_NDATA 2
#define UVC_CTRL_DATA_CURRENT 0
#define UVC_CTRL_DATA_BACKUP 1
+#define UVC_CTRL_DATA_MIN 2
+#define UVC_CTRL_DATA_MAX 3
+#define UVC_CTRL_DATA_RES 4
+#define UVC_CTRL_DATA_DEF 5
+#define UVC_CTRL_DATA_LAST 6
/* ------------------------------------------------------------------------
* Controls
@@ -755,6 +759,49 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
return ctrl;
}
+static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl)
+{
+ int ret;
+
+ if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+ ctrl->info->size);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
+ ctrl->info->size);
+ if (ret < 0)
+ return ret;
+ }
+ if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
+ ctrl->info->size);
+ if (ret < 0)
+ return ret;
+ }
+ if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
+ ctrl->info->size);
+ if (ret < 0)
+ return ret;
+ }
+
+ ctrl->cached = 1;
+ return 0;
+}
+
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl)
{
@@ -762,17 +809,12 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct uvc_control_mapping *mapping;
struct uvc_menu_info *menu;
unsigned int i;
- __u8 *data;
int ret;
ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL)
return -EINVAL;
- data = kmalloc(ctrl->info->size, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);
v4l2_ctrl->id = mapping->id;
v4l2_ctrl->type = mapping->v4l2_type;
@@ -782,14 +824,15 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR))
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
- ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
- chain->dev->intfnum, ctrl->info->selector,
- data, ctrl->info->size);
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
- goto out;
- v4l2_ctrl->default_value =
- mapping->get(mapping, UVC_GET_DEF, data);
+ return ret;
+ }
+
+ if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
+ v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
}
switch (mapping->v4l2_type) {
@@ -806,56 +849,37 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
}
}
- ret = 0;
- goto out;
+ return 0;
case V4L2_CTRL_TYPE_BOOLEAN:
v4l2_ctrl->minimum = 0;
v4l2_ctrl->maximum = 1;
v4l2_ctrl->step = 1;
- ret = 0;
- goto out;
+ return 0;
case V4L2_CTRL_TYPE_BUTTON:
v4l2_ctrl->minimum = 0;
v4l2_ctrl->maximum = 0;
v4l2_ctrl->step = 0;
- ret = 0;
- goto out;
+ return 0;
default:
break;
}
- if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
- ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
- chain->dev->intfnum, ctrl->info->selector,
- data, ctrl->info->size);
- if (ret < 0)
- goto out;
- v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data);
- }
- if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
- ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
- chain->dev->intfnum, ctrl->info->selector,
- data, ctrl->info->size);
- if (ret < 0)
- goto out;
- v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data);
- }
- if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
- ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
- chain->dev->intfnum, ctrl->info->selector,
- data, ctrl->info->size);
- if (ret < 0)
- goto out;
- v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data);
- }
+ if (ctrl->info->flags & UVC_CONTROL_GET_MIN)
+ v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
- ret = 0;
-out:
- kfree(data);
- return ret;
+ if (ctrl->info->flags & UVC_CONTROL_GET_MAX)
+ v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+
+ if (ctrl->info->flags & UVC_CONTROL_GET_RES)
+ v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+
+ return 0;
}
@@ -997,19 +1021,57 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
{
struct uvc_control *ctrl;
struct uvc_control_mapping *mapping;
- s32 value = xctrl->value;
+ s32 value;
+ u32 step;
+ s32 min;
+ s32 max;
int ret;
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
return -EINVAL;
- if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
- if (value < 0 || value >= mapping->menu_count)
- return -EINVAL;
- value = mapping->menu_info[value].value;
+ /* Clamp out of range values. */
+ switch (mapping->v4l2_type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ return ret;
+ }
+
+ min = mapping->get(mapping, UVC_GET_MIN,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
+ max = mapping->get(mapping, UVC_GET_MAX,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+ step = mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+
+ xctrl->value = min + (xctrl->value - min + step/2) / step * step;
+ xctrl->value = clamp(xctrl->value, min, max);
+ value = xctrl->value;
+ break;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ xctrl->value = clamp(xctrl->value, 0, 1);
+ value = xctrl->value;
+ break;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (xctrl->value < 0 || xctrl->value >= mapping->menu_count)
+ return -ERANGE;
+ value = mapping->menu_info[xctrl->value].value;
+ break;
+
+ default:
+ value = xctrl->value;
+ break;
}
+ /* If the mapping doesn't span the whole UVC control, the current value
+ * needs to be loaded from the device to perform the read-modify-write
+ * operation.
+ */
if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) {
if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) {
memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
@@ -1027,6 +1089,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
ctrl->loaded = 1;
}
+ /* Backup the current value in case we need to rollback later. */
if (!ctrl->dirty) {
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
@@ -1080,10 +1143,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
}
if (!found) {
- uvc_trace(UVC_TRACE_CONTROL,
- "Control " UVC_GUID_FORMAT "/%u not found.\n",
- UVC_GUID_ARGS(entity->extension.guidExtensionCode),
- xctrl->selector);
+ uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
+ entity->extension.guidExtensionCode, xctrl->selector);
return -EINVAL;
}
@@ -1159,9 +1220,9 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
(ctrl->info->flags & UVC_CONTROL_RESTORE) == 0)
continue;
- printk(KERN_INFO "restoring control " UVC_GUID_FORMAT
- "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity),
- ctrl->info->index, ctrl->info->selector);
+ printk(KERN_INFO "restoring control %pUl/%u/%u\n",
+ ctrl->info->entity, ctrl->info->index,
+ ctrl->info->selector);
ctrl->dirty = 1;
}
@@ -1215,47 +1276,43 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id,
dev->intfnum, info->selector, (__u8 *)&size, 2);
if (ret < 0) {
- uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on "
- "control " UVC_GUID_FORMAT "/%u (%d).\n",
- UVC_GUID_ARGS(info->entity), info->selector,
- ret);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "GET_LEN failed on control %pUl/%u (%d).\n",
+ info->entity, info->selector, ret);
return;
}
if (info->size != le16_to_cpu(size)) {
- uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT
- "/%u size doesn't match user supplied "
- "value.\n", UVC_GUID_ARGS(info->entity),
- info->selector);
+ uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size "
+ "doesn't match user supplied value.\n",
+ info->entity, info->selector);
return;
}
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
dev->intfnum, info->selector, &inf, 1);
if (ret < 0) {
- uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on "
- "control " UVC_GUID_FORMAT "/%u (%d).\n",
- UVC_GUID_ARGS(info->entity), info->selector,
- ret);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "GET_INFO failed on control %pUl/%u (%d).\n",
+ info->entity, info->selector, ret);
return;
}
flags = info->flags;
if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) ||
((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) {
- uvc_trace(UVC_TRACE_CONTROL, "Control "
- UVC_GUID_FORMAT "/%u flags don't match "
- "supported operations.\n",
- UVC_GUID_ARGS(info->entity), info->selector);
+ uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags "
+ "don't match supported operations.\n",
+ info->entity, info->selector);
return;
}
}
ctrl->info = info;
- ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL);
- uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u "
- "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity),
- ctrl->info->selector, dev->udev->devpath, entity->id);
+ ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL);
+ uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
+ "entity %u\n", ctrl->info->entity, ctrl->info->selector,
+ dev->udev->devpath, entity->id);
}
/*
@@ -1281,17 +1338,16 @@ int uvc_ctrl_add_info(struct uvc_control_info *info)
continue;
if (ctrl->selector == info->selector) {
- uvc_trace(UVC_TRACE_CONTROL, "Control "
- UVC_GUID_FORMAT "/%u is already defined.\n",
- UVC_GUID_ARGS(info->entity), info->selector);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Control %pUl/%u is already defined.\n",
+ info->entity, info->selector);
ret = -EEXIST;
goto end;
}
if (ctrl->index == info->index) {
- uvc_trace(UVC_TRACE_CONTROL, "Control "
- UVC_GUID_FORMAT "/%u would overwrite index "
- "%d.\n", UVC_GUID_ARGS(info->entity),
- info->selector, info->index);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Control %pUl/%u would overwrite index %d.\n",
+ info->entity, info->selector, info->index);
ret = -EEXIST;
goto end;
}
@@ -1332,10 +1388,9 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
continue;
if (info->size * 8 < mapping->size + mapping->offset) {
- uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would "
- "overflow control " UVC_GUID_FORMAT "/%u\n",
- mapping->name, UVC_GUID_ARGS(info->entity),
- info->selector);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Mapping '%s' would overflow control %pUl/%u\n",
+ mapping->name, info->entity, info->selector);
ret = -EOVERFLOW;
goto end;
}
@@ -1354,9 +1409,9 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
mapping->ctrl = info;
list_add_tail(&mapping->list, &info->mappings);
- uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control "
- UVC_GUID_FORMAT "/%u.\n", mapping->name,
- UVC_GUID_ARGS(info->entity), info->selector);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Adding mapping %s to control %pUl/%u.\n",
+ mapping->name, info->entity, info->selector);
ret = 0;
break;
@@ -1378,6 +1433,7 @@ uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity)
struct usb_device_id id;
u8 index;
} blacklist[] = {
+ { { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */
{ { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */
{ { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */
};
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 391cccca7ff..a814820a3f6 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -43,8 +43,9 @@
#define DRIVER_VERSION "v0.1.0"
#endif
+unsigned int uvc_clock_param = CLOCK_MONOTONIC;
unsigned int uvc_no_drop_param;
-static unsigned int uvc_quirks_param;
+static unsigned int uvc_quirks_param = -1;
unsigned int uvc_trace_param;
unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
@@ -59,6 +60,11 @@ static struct uvc_format_desc uvc_fmts[] = {
.fcc = V4L2_PIX_FMT_YUYV,
},
{
+ .name = "YUV 4:2:2 (YUYV)",
+ .guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
.name = "YUV 4:2:0 (NV12)",
.guid = UVC_GUID_FORMAT_NV12,
.fcc = V4L2_PIX_FMT_NV12,
@@ -309,11 +315,10 @@ static int uvc_parse_format(struct uvc_device *dev,
sizeof format->name);
format->fcc = fmtdesc->fcc;
} else {
- uvc_printk(KERN_INFO, "Unknown video format "
- UVC_GUID_FORMAT "\n",
- UVC_GUID_ARGS(&buffer[5]));
- snprintf(format->name, sizeof format->name,
- UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5]));
+ uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
+ &buffer[5]);
+ snprintf(format->name, sizeof(format->name), "%pUl\n",
+ &buffer[5]);
format->fcc = 0;
}
@@ -1750,7 +1755,8 @@ static int uvc_probe(struct usb_interface *intf,
dev->udev = usb_get_dev(udev);
dev->intf = usb_get_intf(intf);
dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
- dev->quirks = id->driver_info | uvc_quirks_param;
+ dev->quirks = (uvc_quirks_param == -1)
+ ? id->driver_info : uvc_quirks_param;
if (udev->product != NULL)
strlcpy(dev->name, udev->product, sizeof dev->name);
@@ -1773,9 +1779,9 @@ static int uvc_probe(struct usb_interface *intf,
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
- if (uvc_quirks_param != 0) {
- uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module "
- "parameter for testing purpose.\n", uvc_quirks_param);
+ if (dev->quirks != id->driver_info) {
+ uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
+ "parameter for testing purpose.\n", dev->quirks);
uvc_printk(KERN_INFO, "Please report required quirks to the "
"linux-uvc-devel mailing list.\n");
}
@@ -1892,6 +1898,45 @@ static int uvc_reset_resume(struct usb_interface *intf)
}
/* ------------------------------------------------------------------------
+ * Module parameters
+ */
+
+static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
+{
+ if (uvc_clock_param == CLOCK_MONOTONIC)
+ return sprintf(buffer, "CLOCK_MONOTONIC");
+ else
+ return sprintf(buffer, "CLOCK_REALTIME");
+}
+
+static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
+{
+ if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
+ val += strlen("clock_");
+
+ if (strcasecmp(val, "monotonic") == 0)
+ uvc_clock_param = CLOCK_MONOTONIC;
+ else if (strcasecmp(val, "realtime") == 0)
+ uvc_clock_param = CLOCK_REALTIME;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
+ &uvc_clock_param, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
+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");
+
+/* ------------------------------------------------------------------------
* Driver initialization and cleanup
*/
@@ -2197,15 +2242,6 @@ static void __exit uvc_cleanup(void)
module_init(uvc_init);
module_exit(uvc_cleanup);
-module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
-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);
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index ea11839cba4..4a925a31b0e 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -502,7 +502,6 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
spin_unlock_irqrestore(&queue->irqlock, flags);
buf->buf.sequence = queue->sequence++;
- do_gettimeofday(&buf->buf.timestamp);
wake_up(&buf->wait);
return nextbuf;
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 23239a4adef..43152aa5222 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -539,7 +539,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
xctrl.id = ctrl->id;
xctrl.value = ctrl->value;
- uvc_ctrl_begin(chain);
+ ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
@@ -549,6 +549,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return ret;
}
ret = uvc_ctrl_commit(chain);
+ if (ret == 0)
+ ctrl->value = xctrl.value;
break;
}
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 7dcf534a0cf..6b0666be370 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -410,6 +410,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
* when the EOF bit is set to force synchronisation on the next packet.
*/
if (buf->state != UVC_BUF_STATE_ACTIVE) {
+ struct timespec ts;
+
if (fid == stream->last_fid) {
uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
"sync).\n");
@@ -419,6 +421,14 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
return -ENODATA;
}
+ if (uvc_clock_param == CLOCK_MONOTONIC)
+ ktime_get_ts(&ts);
+ else
+ ktime_get_real_ts(&ts);
+
+ buf->buf.timestamp.tv_sec = ts.tv_sec;
+ buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
/* TODO: Handle PTS and SCR. */
buf->state = UVC_BUF_STATE_ACTIVE;
}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 2337585001e..2bba059259e 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -113,6 +113,9 @@ struct uvc_xu_control {
#define UVC_GUID_FORMAT_YUY2 \
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2_ISIGHT \
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_NV12 \
{ 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
@@ -149,7 +152,7 @@ struct uvc_xu_control {
#define UVC_MAX_STATUS_SIZE 16
#define UVC_CTRL_CONTROL_TIMEOUT 300
-#define UVC_CTRL_STREAMING_TIMEOUT 3000
+#define UVC_CTRL_STREAMING_TIMEOUT 5000
/* Devices quirks */
#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
@@ -242,7 +245,8 @@ struct uvc_control {
uvc_control_info. */
__u8 dirty : 1,
loaded : 1,
- modified : 1;
+ modified : 1,
+ cached : 1;
__u8 *data;
};
@@ -533,6 +537,7 @@ struct uvc_driver {
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
+extern unsigned int uvc_clock_param;
extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_trace_param;
extern unsigned int uvc_timeout_param;
@@ -552,16 +557,6 @@ extern unsigned int uvc_timeout_param;
#define uvc_printk(level, msg...) \
printk(level "uvcvideo: " msg)
-#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \
- "%02x%02x%02x%02x%02x%02x"
-#define UVC_GUID_ARGS(guid) \
- (guid)[3], (guid)[2], (guid)[1], (guid)[0], \
- (guid)[5], (guid)[4], \
- (guid)[7], (guid)[6], \
- (guid)[8], (guid)[9], \
- (guid)[10], (guid)[11], (guid)[12], \
- (guid)[13], (guid)[14], (guid)[15]
-
/* --------------------------------------------------------------------------
* Internal functions.
*/
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index c4150bd2633..f77f84bfe71 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -288,7 +288,7 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
{
- if (copy_to_user(&up->w, &kp->w, sizeof(up->w)) ||
+ if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
put_user(kp->field, &up->field) ||
put_user(kp->chromakey, &up->chromakey) ||
put_user(kp->clipcount, &up->clipcount))
@@ -475,6 +475,9 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
return -EFAULT;
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
+ if (get_user(kp->length, &up->length) ||
+ get_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
{
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index fa78555b118..fcd045e7a1c 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -418,6 +418,8 @@ static void *__videobuf_alloc(size_t size)
struct videobuf_buffer *vb;
vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);
+ if (!vb)
+ return vb;
mem = vb->priv = ((char *)vb)+size;
mem->magic=MAGIC_SG_MEM;
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
index d6e6a28fb6b..136e09383c0 100644
--- a/drivers/media/video/videobuf-vmalloc.c
+++ b/drivers/media/video/videobuf-vmalloc.c
@@ -138,6 +138,8 @@ static void *__videobuf_alloc(size_t size)
struct videobuf_buffer *vb;
vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);
+ if (!vb)
+ return vb;
mem = vb->priv = ((char *)vb)+size;
mem->magic=MAGIC_VMAL_MEM;
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 37632a06496..cdbe70385c1 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -1371,7 +1371,7 @@ static int __init vivi_create_instance(int inst)
/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
- if (video_nr >= 0)
+ if (video_nr != -1)
video_nr++;
dev->vfd = vfd;
diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig
index edb00293cd5..a7e610e0be9 100644
--- a/drivers/media/video/zc0301/Kconfig
+++ b/drivers/media/video/zc0301/Kconfig
@@ -1,7 +1,11 @@
config USB_ZC0301
- tristate "USB ZC0301[P] Image Processor and Control Chip support"
+ tristate "USB ZC0301[P] webcam support (DEPRECATED)"
depends on VIDEO_V4L2
+ default n
---help---
+ This driver is DEPRECATED please use the gspca zc3xx module
+ instead.
+
Say Y here if you want support for cameras based on the ZC0301 or
ZC0301P Image Processors and Control Chips.
diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c
index f6c2fb4fc3b..e6ad4b20561 100644
--- a/drivers/media/video/zoran/zoran_device.c
+++ b/drivers/media/video/zoran/zoran_device.c
@@ -1196,7 +1196,8 @@ zoran_reap_stat_com (struct zoran *zr)
static void zoran_restart(struct zoran *zr)
{
/* Now the stat_comm buffer is ready for restart */
- int status = 0, mode;
+ unsigned int status = 0;
+ int mode;
if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
decoder_call(zr, video, g_input_status, &status);
@@ -1228,7 +1229,7 @@ error_handler (struct zoran *zr,
u32 astat,
u32 stat)
{
- int i, j;
+ int i;
/* This is JPEG error handling part */
if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS &&
@@ -1279,6 +1280,7 @@ error_handler (struct zoran *zr,
/* Report error */
if (zr36067_debug > 1 && zr->num_errors <= 8) {
long frame;
+ int j;
frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
printk(KERN_ERR
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 2ddffed019e..ec41303544e 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -324,7 +324,7 @@ static int jpg_fbuffer_alloc(struct zoran_fh *fh)
/* Allocate fragment table for this buffer */
mem = (void *)get_zeroed_page(GFP_KERNEL);
- if (mem == 0) {
+ if (!mem) {
dprintk(1,
KERN_ERR
"%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
@@ -1444,7 +1444,7 @@ zoran_set_norm (struct zoran *zr,
}
if (norm == V4L2_STD_ALL) {
- int status = 0;
+ unsigned int status = 0;
v4l2_std_id std = 0;
decoder_call(zr, video, querystd, &std);
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index f0eae83e3d8..3d4bac25290 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -78,6 +78,7 @@
#define METHOD0 0
#define METHOD1 1
#define METHOD2 2
+#define METHOD3 3
/* Module parameters */
@@ -114,7 +115,7 @@ static struct usb_device_id device_table[] = {
{USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 },
{USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 },
{USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 },
- {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 },
+ {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 },
{USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 },
{} /* Terminating entry */
};
@@ -302,7 +303,7 @@ static message m2[] = {
};
/* init table */
-static message *init[3] = { m0, m1, m2 };
+static message *init[4] = { m0, m1, m2, m2 };
/* JPEG static data in header (Huffman table, etc) */
@@ -967,6 +968,22 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
m0d1[0] = mode;
m1[2].value = 0xf000 + mode;
m2[1].value = 0xf000 + mode;
+
+ /* special case for METHOD3, the modes are different */
+ if (cam->method == METHOD3) {
+ switch (mode) {
+ case 1:
+ m2[1].value = 0xf000 + 4;
+ break;
+ case 2:
+ m2[1].value = 0xf000 + 0;
+ break;
+ default:
+ m2[1].value = 0xf000 + 1;
+ break;
+ }
+ }
+
header2[437] = cam->height / 256;
header2[438] = cam->height % 256;
header2[439] = cam->width / 256;
@@ -1582,6 +1599,22 @@ static int zr364xx_probe(struct usb_interface *intf,
m0d1[0] = mode;
m1[2].value = 0xf000 + mode;
m2[1].value = 0xf000 + mode;
+
+ /* special case for METHOD3, the modes are different */
+ if (cam->method == METHOD3) {
+ switch (mode) {
+ case 1:
+ m2[1].value = 0xf000 + 4;
+ break;
+ case 2:
+ m2[1].value = 0xf000 + 0;
+ break;
+ default:
+ m2[1].value = 0xf000 + 1;
+ break;
+ }
+ }
+
header2[437] = cam->height / 256;
header2[438] = cam->height % 256;
header2[439] = cam->width / 256;
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index bd83fa0a497..972b87069d5 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -1226,9 +1226,8 @@ static int mspro_block_init_disk(struct memstick_dev *card)
blk_queue_prep_rq(msb->queue, mspro_block_prepare_req);
blk_queue_bounce_limit(msb->queue, limit);
- blk_queue_max_sectors(msb->queue, MSPRO_BLOCK_MAX_PAGES);
- blk_queue_max_phys_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
- blk_queue_max_hw_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
+ blk_queue_max_hw_sectors(msb->queue, MSPRO_BLOCK_MAX_PAGES);
+ blk_queue_max_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
blk_queue_max_segment_size(msb->queue,
MSPRO_BLOCK_MAX_PAGES * msb->page_size);
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 44d2037e9e5..5382b5a44af 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -126,8 +126,6 @@ static int mfcounter = 0;
* Public data...
*/
-static struct proc_dir_entry *mpt_proc_root_dir;
-
#define WHOINIT_UNKNOWN 0xAA
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -146,6 +144,9 @@ static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS];
static MPT_RESETHANDLER MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS];
static struct mpt_pci_driver *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS];
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *mpt_proc_root_dir;
+#endif
/*
* Driver Callback Index's
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index b4948671eb9..9718c8f2e95 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.13"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.13"
+#define MPT_LINUX_VERSION_COMMON "3.04.14"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.14"
#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 352acd05c46..caa8f568a41 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -360,8 +360,8 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function)
u16 iocstatus;
/* bus reset is only good for SCSI IO, RAID PASSTHRU */
- if (!(function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) ||
- (function == MPI_FUNCTION_SCSI_IO_REQUEST)) {
+ if (!(function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ function == MPI_FUNCTION_SCSI_IO_REQUEST)) {
dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
"TaskMgmt, not SCSI_IO!!\n", ioc->name));
return -EPERM;
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index ebf6ae024da..612ab3c51a6 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -195,29 +195,34 @@ mptfc_block_error_handler(struct scsi_cmnd *SCpnt,
unsigned long flags;
int ready;
MPT_ADAPTER *ioc;
+ int loops = 40; /* seconds */
hd = shost_priv(SCpnt->device->host);
ioc = hd->ioc;
spin_lock_irqsave(shost->host_lock, flags);
- while ((ready = fc_remote_port_chkready(rport) >> 16) == DID_IMM_RETRY) {
+ while ((ready = fc_remote_port_chkready(rport) >> 16) == DID_IMM_RETRY
+ || (loops > 0 && ioc->active == 0)) {
spin_unlock_irqrestore(shost->host_lock, flags);
dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT
"mptfc_block_error_handler.%d: %d:%d, port status is "
- "DID_IMM_RETRY, deferring %s recovery.\n",
+ "%x, active flag %d, deferring %s recovery.\n",
ioc->name, ioc->sh->host_no,
- SCpnt->device->id, SCpnt->device->lun, caller));
+ SCpnt->device->id, SCpnt->device->lun,
+ ready, ioc->active, caller));
msleep(1000);
spin_lock_irqsave(shost->host_lock, flags);
+ loops --;
}
spin_unlock_irqrestore(shost->host_lock, flags);
- if (ready == DID_NO_CONNECT || !SCpnt->device->hostdata) {
+ if (ready == DID_NO_CONNECT || !SCpnt->device->hostdata
+ || ioc->active == 0) {
dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT
"%s.%d: %d:%d, failing recovery, "
- "port state %d, vdevice %p.\n", caller,
+ "port state %x, active %d, vdevice %p.\n", caller,
ioc->name, ioc->sh->host_no,
SCpnt->device->id, SCpnt->device->lun, ready,
- SCpnt->device->hostdata));
+ ioc->active, SCpnt->device->hostdata));
return FAILED;
}
dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 83873e3d0ce..c20bbe45da8 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -1075,6 +1075,19 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
return 0;
}
+static void
+mptsas_block_io_sdev(struct scsi_device *sdev, void *data)
+{
+ scsi_device_set_state(sdev, SDEV_BLOCK);
+}
+
+static void
+mptsas_block_io_starget(struct scsi_target *starget)
+{
+ if (starget)
+ starget_for_each_device(starget, NULL, mptsas_block_io_sdev);
+}
+
/**
* mptsas_target_reset_queue
*
@@ -1098,10 +1111,11 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc,
id = sas_event_data->TargetID;
channel = sas_event_data->Bus;
- if (!(vtarget = mptsas_find_vtarget(ioc, channel, id)))
- return;
-
- vtarget->deleted = 1; /* block IO */
+ vtarget = mptsas_find_vtarget(ioc, channel, id);
+ if (vtarget) {
+ mptsas_block_io_starget(vtarget->starget);
+ vtarget->deleted = 1; /* block IO */
+ }
target_reset_list = kzalloc(sizeof(struct mptsas_target_reset_event),
GFP_ATOMIC);
@@ -1868,7 +1882,8 @@ mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
if (ioc->sas_discovery_quiesce_io)
return SCSI_MLQUEUE_HOST_BUSY;
-// scsi_print_command(SCpnt);
+ if (ioc->debug_level & MPT_DEBUG_SCSI)
+ scsi_print_command(SCpnt);
return mptscsih_qcmd(SCpnt,done);
}
@@ -2686,6 +2701,187 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
return error;
}
+struct rep_manu_request{
+ u8 smp_frame_type;
+ u8 function;
+ u8 reserved;
+ u8 request_length;
+};
+
+struct rep_manu_reply{
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x01 */
+ u8 function_result;
+ u8 response_length;
+ u16 expander_change_count;
+ u8 reserved0[2];
+ u8 sas_format:1;
+ u8 reserved1:7;
+ u8 reserved2[3];
+ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
+ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
+ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
+ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
+ u16 component_id;
+ u8 component_revision_id;
+ u8 reserved3;
+ u8 vendor_specific[8];
+};
+
+/**
+ * mptsas_exp_repmanufacture_info -
+ * @ioc: per adapter object
+ * @sas_address: expander sas address
+ * @edev: the sas_expander_device object
+ *
+ * Fills in the sas_expander_device object when SMP port is created.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+mptsas_exp_repmanufacture_info(MPT_ADAPTER *ioc,
+ u64 sas_address, struct sas_expander_device *edev)
+{
+ MPT_FRAME_HDR *mf;
+ SmpPassthroughRequest_t *smpreq;
+ SmpPassthroughReply_t *smprep;
+ struct rep_manu_reply *manufacture_reply;
+ struct rep_manu_request *manufacture_request;
+ int ret;
+ int flagsLength;
+ unsigned long timeleft;
+ char *psge;
+ unsigned long flags;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma = 0;
+ u32 sz;
+
+ spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+ if (ioc->ioc_reset_in_progress) {
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ printk(MYIOC_s_INFO_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
+ ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex);
+ if (ret)
+ goto out;
+
+ mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
+ if (!mf) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ smpreq = (SmpPassthroughRequest_t *)mf;
+ memset(smpreq, 0, sizeof(*smpreq));
+
+ sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply);
+
+ data_out = pci_alloc_consistent(ioc->pcidev, sz, &data_out_dma);
+ if (!data_out) {
+ printk(KERN_ERR "Memory allocation failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ ret = -ENOMEM;
+ goto put_mf;
+ }
+
+ manufacture_request = data_out;
+ manufacture_request->smp_frame_type = 0x40;
+ manufacture_request->function = 1;
+ manufacture_request->reserved = 0;
+ manufacture_request->request_length = 0;
+
+ smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
+ smpreq->PhysicalPort = 0xFF;
+ *((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address);
+ smpreq->RequestDataLength = sizeof(struct rep_manu_request);
+
+ psge = (char *)
+ (((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4));
+
+ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI_SGE_FLAGS_SYSTEM_ADDRESS |
+ MPI_SGE_FLAGS_HOST_TO_IOC |
+ MPI_SGE_FLAGS_END_OF_BUFFER;
+ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
+ flagsLength |= sizeof(struct rep_manu_request);
+
+ ioc->add_sge(psge, flagsLength, data_out_dma);
+ psge += ioc->SGE_size;
+
+ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI_SGE_FLAGS_SYSTEM_ADDRESS |
+ MPI_SGE_FLAGS_IOC_TO_HOST |
+ MPI_SGE_FLAGS_END_OF_BUFFER;
+ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
+ flagsLength |= sizeof(struct rep_manu_reply);
+ ioc->add_sge(psge, flagsLength, data_out_dma +
+ sizeof(struct rep_manu_request));
+
+ INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
+ mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
+
+ timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
+ if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+ ret = -ETIME;
+ mpt_free_msg_frame(ioc, mf);
+ mf = NULL;
+ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
+ goto out_free;
+ if (!timeleft)
+ mpt_HardResetHandler(ioc, CAN_SLEEP);
+ goto out_free;
+ }
+
+ mf = NULL;
+
+ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
+ u8 *tmp;
+
+ smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
+ if (le16_to_cpu(smprep->ResponseDataLength) !=
+ sizeof(struct rep_manu_reply))
+ goto out_free;
+
+ manufacture_reply = data_out + sizeof(struct rep_manu_request);
+ strncpy(edev->vendor_id, manufacture_reply->vendor_id,
+ SAS_EXPANDER_VENDOR_ID_LEN);
+ strncpy(edev->product_id, manufacture_reply->product_id,
+ SAS_EXPANDER_PRODUCT_ID_LEN);
+ strncpy(edev->product_rev, manufacture_reply->product_rev,
+ SAS_EXPANDER_PRODUCT_REV_LEN);
+ edev->level = manufacture_reply->sas_format;
+ if (manufacture_reply->sas_format) {
+ strncpy(edev->component_vendor_id,
+ manufacture_reply->component_vendor_id,
+ SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
+ tmp = (u8 *)&manufacture_reply->component_id;
+ edev->component_id = tmp[0] << 8 | tmp[1];
+ edev->component_revision_id =
+ manufacture_reply->component_revision_id;
+ }
+ } else {
+ printk(MYIOC_s_ERR_FMT
+ "%s: smp passthru reply failed to be returned\n",
+ ioc->name, __func__);
+ ret = -ENXIO;
+ }
+out_free:
+ if (data_out_dma)
+ pci_free_consistent(ioc->pcidev, sz, data_out, data_out_dma);
+put_mf:
+ if (mf)
+ mpt_free_msg_frame(ioc, mf);
+out_unlock:
+ CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
+ mutex_unlock(&ioc->sas_mgmt.mutex);
+out:
+ return ret;
+ }
+
static void
mptsas_parse_device_info(struct sas_identify *identify,
struct mptsas_devinfo *device_info)
@@ -2967,6 +3163,11 @@ static int mptsas_probe_one_phy(struct device *dev,
goto out;
}
mptsas_set_rphy(ioc, phy_info, rphy);
+ if (identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
+ identify.device_type == SAS_FANOUT_EXPANDER_DEVICE)
+ mptsas_exp_repmanufacture_info(ioc,
+ identify.sas_address,
+ rphy_to_expander_device(rphy));
}
out:
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index 81279b3d694..4a7d1afcb66 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -1438,9 +1438,14 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
&& (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
&& (SCpnt->device->tagged_supported)) {
scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
- } else {
+ if (SCpnt->request && SCpnt->request->ioprio) {
+ if (((SCpnt->request->ioprio & 0x7) == 1) ||
+ !(SCpnt->request->ioprio & 0x7))
+ scsictl |= MPI_SCSIIO_CONTROL_HEADOFQ;
+ }
+ } else
scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
- }
+
/* Use the above information to set up the message frame
*/
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index e39986a7827..2658b1484a2 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -1065,9 +1065,8 @@ static int i2o_block_probe(struct device *dev)
queue = gd->queue;
queue->queuedata = i2o_blk_dev;
- blk_queue_max_phys_segments(queue, I2O_MAX_PHYS_SEGMENTS);
- blk_queue_max_sectors(queue, max_sectors);
- blk_queue_max_hw_segments(queue, i2o_sg_tablesize(c, body_size));
+ blk_queue_max_hw_sectors(queue, max_sectors);
+ blk_queue_max_segments(queue, i2o_sg_tablesize(c, body_size));
osm_debug("max sectors = %d\n", queue->max_sectors);
osm_debug("phys segments = %d\n", queue->max_phys_segments);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 1380e1c44e4..b670d10d5c9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -348,6 +348,16 @@ config AB4500_CORE
read/write functions for the devices to get access to this chip.
This chip embeds various other multimedia funtionalities as well.
+config MFD_TIMBERDALE
+ tristate "Support for the Timberdale FPGA"
+ select MFD_CORE
+ depends on PCI && GPIOLIB
+ ---help---
+ This is the core driver for the timberdale FPGA. This device is a
+ multifunction device which exposes numerous platform devices.
+
+ The timberdale FPGA can be found on the Intel Atom development board
+ for in-vehicle infontainment, called Russellville.
endmenu
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e09eb4870db..78295d6a75f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -54,5 +54,6 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
+obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
-obj-$(CONFIG_PMIC_ADP5520) += adp5520.o \ No newline at end of file
+obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
new file mode 100644
index 00000000000..1ed44d28380
--- /dev/null
+++ b/drivers/mfd/timberdale.c
@@ -0,0 +1,727 @@
+/*
+ * timberdale.c timberdale FPGA MFD driver
+ * 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.
+ */
+
+/* Supports:
+ * Timberdale FPGA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/mfd/core.h>
+
+#include <linux/timb_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-ocores.h>
+#include <linux/i2c/tsc2007.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/xilinx_spi.h>
+#include <linux/spi/max7301.h>
+#include <linux/spi/mc33880.h>
+
+#include <media/timb_radio.h>
+
+#include "timberdale.h"
+
+#define DRIVER_NAME "timberdale"
+
+struct timberdale_device {
+ resource_size_t ctl_mapbase;
+ unsigned char __iomem *ctl_membase;
+ struct {
+ u32 major;
+ u32 minor;
+ u32 config;
+ } fw;
+};
+
+/*--------------------------------------------------------------------------*/
+
+static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
+ .model = 2003,
+ .x_plate_ohms = 100
+};
+
+static struct i2c_board_info timberdale_i2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("tsc2007", 0x48),
+ .platform_data = &timberdale_tsc2007_platform_data,
+ .irq = IRQ_TIMBERDALE_TSC_INT
+ },
+};
+
+static __devinitdata struct ocores_i2c_platform_data
+timberdale_ocores_platform_data = {
+ .regstep = 4,
+ .clock_khz = 62500,
+ .devices = timberdale_i2c_board_info,
+ .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
+};
+
+const static __devinitconst struct resource timberdale_ocores_resources[] = {
+ {
+ .start = OCORESOFFSET,
+ .end = OCORESEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_I2C,
+ .end = IRQ_TIMBERDALE_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+const struct max7301_platform_data timberdale_max7301_platform_data = {
+ .base = 200
+};
+
+const struct mc33880_platform_data timberdale_mc33880_platform_data = {
+ .base = 100
+};
+
+static struct spi_board_info timberdale_spi_16bit_board_info[] = {
+ {
+ .modalias = "max7301",
+ .max_speed_hz = 26000,
+ .chip_select = 2,
+ .mode = SPI_MODE_0,
+ .platform_data = &timberdale_max7301_platform_data
+ },
+};
+
+static struct spi_board_info timberdale_spi_8bit_board_info[] = {
+ {
+ .modalias = "mc33880",
+ .max_speed_hz = 4000,
+ .chip_select = 1,
+ .mode = SPI_MODE_1,
+ .platform_data = &timberdale_mc33880_platform_data
+ },
+};
+
+static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
+ .num_chipselect = 3,
+ .little_endian = true,
+ /* bits per word and devices will be filled in runtime depending
+ * on the HW config
+ */
+};
+
+const static __devinitconst struct resource timberdale_spi_resources[] = {
+ {
+ .start = SPIOFFSET,
+ .end = SPIEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_SPI,
+ .end = IRQ_TIMBERDALE_SPI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+const static __devinitconst struct resource timberdale_eth_resources[] = {
+ {
+ .start = ETHOFFSET,
+ .end = ETHEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_ETHSW_IF,
+ .end = IRQ_TIMBERDALE_ETHSW_IF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static __devinitdata struct timbgpio_platform_data
+ timberdale_gpio_platform_data = {
+ .gpio_base = 0,
+ .nr_pins = GPIO_NR_PINS,
+ .irq_base = 200,
+};
+
+const static __devinitconst struct resource timberdale_gpio_resources[] = {
+ {
+ .start = GPIOOFFSET,
+ .end = GPIOEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_GPIO,
+ .end = IRQ_TIMBERDALE_GPIO,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+const static __devinitconst struct resource timberdale_mlogicore_resources[] = {
+ {
+ .start = MLCOREOFFSET,
+ .end = MLCOREEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_MLCORE,
+ .end = IRQ_TIMBERDALE_MLCORE,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_TIMBERDALE_MLCORE_BUF,
+ .end = IRQ_TIMBERDALE_MLCORE_BUF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+const static __devinitconst struct resource timberdale_uart_resources[] = {
+ {
+ .start = UARTOFFSET,
+ .end = UARTEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_UART,
+ .end = IRQ_TIMBERDALE_UART,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+const static __devinitconst struct resource timberdale_uartlite_resources[] = {
+ {
+ .start = UARTLITEOFFSET,
+ .end = UARTLITEEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_UARTLITE,
+ .end = IRQ_TIMBERDALE_UARTLITE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+const static __devinitconst struct resource timberdale_radio_resources[] = {
+ {
+ .start = RDSOFFSET,
+ .end = RDSEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_RDS,
+ .end = IRQ_TIMBERDALE_RDS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
+ I2C_BOARD_INFO("tef6862", 0x60)
+};
+
+static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
+ I2C_BOARD_INFO("saa7706h", 0x1C)
+};
+
+static __devinitdata struct timb_radio_platform_data
+ timberdale_radio_platform_data = {
+ .i2c_adapter = 0,
+ .tuner = {
+ .module_name = "tef6862",
+ .info = &timberdale_tef6868_i2c_board_info
+ },
+ .dsp = {
+ .module_name = "saa7706h",
+ .info = &timberdale_saa7706_i2c_board_info
+ }
+};
+
+const static __devinitconst struct resource timberdale_dma_resources[] = {
+ {
+ .start = DMAOFFSET,
+ .end = DMAEND,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_DMA,
+ .end = IRQ_TIMBERDALE_DMA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
+ {
+ .name = "timb-uart",
+ .num_resources = ARRAY_SIZE(timberdale_uart_resources),
+ .resources = timberdale_uart_resources,
+ },
+ {
+ .name = "timb-gpio",
+ .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+ .resources = timberdale_gpio_resources,
+ .platform_data = &timberdale_gpio_platform_data,
+ .data_size = sizeof(timberdale_gpio_platform_data),
+ },
+ {
+ .name = "timb-radio",
+ .num_resources = ARRAY_SIZE(timberdale_radio_resources),
+ .resources = timberdale_radio_resources,
+ .platform_data = &timberdale_radio_platform_data,
+ .data_size = sizeof(timberdale_radio_platform_data),
+ },
+ {
+ .name = "xilinx_spi",
+ .num_resources = ARRAY_SIZE(timberdale_spi_resources),
+ .resources = timberdale_spi_resources,
+ .platform_data = &timberdale_xspi_platform_data,
+ .data_size = sizeof(timberdale_xspi_platform_data),
+ },
+ {
+ .name = "ks8842",
+ .num_resources = ARRAY_SIZE(timberdale_eth_resources),
+ .resources = timberdale_eth_resources,
+ },
+ {
+ .name = "timb-dma",
+ .num_resources = ARRAY_SIZE(timberdale_dma_resources),
+ .resources = timberdale_dma_resources,
+ },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
+ {
+ .name = "timb-uart",
+ .num_resources = ARRAY_SIZE(timberdale_uart_resources),
+ .resources = timberdale_uart_resources,
+ },
+ {
+ .name = "uartlite",
+ .num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
+ .resources = timberdale_uartlite_resources,
+ },
+ {
+ .name = "timb-gpio",
+ .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+ .resources = timberdale_gpio_resources,
+ .platform_data = &timberdale_gpio_platform_data,
+ .data_size = sizeof(timberdale_gpio_platform_data),
+ },
+ {
+ .name = "timb-mlogicore",
+ .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
+ .resources = timberdale_mlogicore_resources,
+ },
+ {
+ .name = "timb-radio",
+ .num_resources = ARRAY_SIZE(timberdale_radio_resources),
+ .resources = timberdale_radio_resources,
+ .platform_data = &timberdale_radio_platform_data,
+ .data_size = sizeof(timberdale_radio_platform_data),
+ },
+ {
+ .name = "xilinx_spi",
+ .num_resources = ARRAY_SIZE(timberdale_spi_resources),
+ .resources = timberdale_spi_resources,
+ .platform_data = &timberdale_xspi_platform_data,
+ .data_size = sizeof(timberdale_xspi_platform_data),
+ },
+ {
+ .name = "ks8842",
+ .num_resources = ARRAY_SIZE(timberdale_eth_resources),
+ .resources = timberdale_eth_resources,
+ },
+ {
+ .name = "timb-dma",
+ .num_resources = ARRAY_SIZE(timberdale_dma_resources),
+ .resources = timberdale_dma_resources,
+ },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
+ {
+ .name = "timb-uart",
+ .num_resources = ARRAY_SIZE(timberdale_uart_resources),
+ .resources = timberdale_uart_resources,
+ },
+ {
+ .name = "timb-gpio",
+ .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+ .resources = timberdale_gpio_resources,
+ .platform_data = &timberdale_gpio_platform_data,
+ .data_size = sizeof(timberdale_gpio_platform_data),
+ },
+ {
+ .name = "timb-radio",
+ .num_resources = ARRAY_SIZE(timberdale_radio_resources),
+ .resources = timberdale_radio_resources,
+ .platform_data = &timberdale_radio_platform_data,
+ .data_size = sizeof(timberdale_radio_platform_data),
+ },
+ {
+ .name = "xilinx_spi",
+ .num_resources = ARRAY_SIZE(timberdale_spi_resources),
+ .resources = timberdale_spi_resources,
+ .platform_data = &timberdale_xspi_platform_data,
+ .data_size = sizeof(timberdale_xspi_platform_data),
+ },
+ {
+ .name = "timb-dma",
+ .num_resources = ARRAY_SIZE(timberdale_dma_resources),
+ .resources = timberdale_dma_resources,
+ },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
+ {
+ .name = "timb-uart",
+ .num_resources = ARRAY_SIZE(timberdale_uart_resources),
+ .resources = timberdale_uart_resources,
+ },
+ {
+ .name = "ocores-i2c",
+ .num_resources = ARRAY_SIZE(timberdale_ocores_resources),
+ .resources = timberdale_ocores_resources,
+ .platform_data = &timberdale_ocores_platform_data,
+ .data_size = sizeof(timberdale_ocores_platform_data),
+ },
+ {
+ .name = "timb-gpio",
+ .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+ .resources = timberdale_gpio_resources,
+ .platform_data = &timberdale_gpio_platform_data,
+ .data_size = sizeof(timberdale_gpio_platform_data),
+ },
+ {
+ .name = "timb-radio",
+ .num_resources = ARRAY_SIZE(timberdale_radio_resources),
+ .resources = timberdale_radio_resources,
+ .platform_data = &timberdale_radio_platform_data,
+ .data_size = sizeof(timberdale_radio_platform_data),
+ },
+ {
+ .name = "xilinx_spi",
+ .num_resources = ARRAY_SIZE(timberdale_spi_resources),
+ .resources = timberdale_spi_resources,
+ .platform_data = &timberdale_xspi_platform_data,
+ .data_size = sizeof(timberdale_xspi_platform_data),
+ },
+ {
+ .name = "ks8842",
+ .num_resources = ARRAY_SIZE(timberdale_eth_resources),
+ .resources = timberdale_eth_resources,
+ },
+ {
+ .name = "timb-dma",
+ .num_resources = ARRAY_SIZE(timberdale_dma_resources),
+ .resources = timberdale_dma_resources,
+ },
+};
+
+static const __devinitconst struct resource timberdale_sdhc_resources[] = {
+ /* located in bar 1 and bar 2 */
+ {
+ .start = SDHC0OFFSET,
+ .end = SDHC0END,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_TIMBERDALE_SDHC,
+ .end = IRQ_TIMBERDALE_SDHC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
+ {
+ .name = "sdhci",
+ .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
+ .resources = timberdale_sdhc_resources,
+ },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
+ {
+ .name = "sdhci",
+ .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
+ .resources = timberdale_sdhc_resources,
+ },
+};
+
+static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct timberdale_device *priv = pci_get_drvdata(pdev);
+
+ return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
+ priv->fw.config);
+}
+
+static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
+
+/*--------------------------------------------------------------------------*/
+
+static int __devinit timb_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct timberdale_device *priv;
+ int err, i;
+ resource_size_t mapbase;
+ struct msix_entry *msix_entries = NULL;
+ u8 ip_setup;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ pci_set_drvdata(dev, priv);
+
+ err = pci_enable_device(dev);
+ if (err)
+ goto err_enable;
+
+ mapbase = pci_resource_start(dev, 0);
+ if (!mapbase) {
+ dev_err(&dev->dev, "No resource\n");
+ goto err_start;
+ }
+
+ /* create a resource for the PCI master register */
+ priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
+ if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
+ dev_err(&dev->dev, "Failed to request ctl mem\n");
+ goto err_request;
+ }
+
+ priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
+ if (!priv->ctl_membase) {
+ dev_err(&dev->dev, "ioremap failed for ctl mem\n");
+ goto err_ioremap;
+ }
+
+ /* read the HW config */
+ priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
+ priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
+ priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
+
+ if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
+ dev_err(&dev->dev, "The driver supports an older "
+ "version of the FPGA, please update the driver to "
+ "support %d.%d\n", priv->fw.major, priv->fw.minor);
+ goto err_ioremap;
+ }
+ if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
+ priv->fw.minor < TIMB_REQUIRED_MINOR) {
+ dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
+ "please upgrade the FPGA to at least: %d.%d\n",
+ priv->fw.major, priv->fw.minor,
+ TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
+ goto err_ioremap;
+ }
+
+ msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
+ GFP_KERNEL);
+ if (!msix_entries)
+ goto err_ioremap;
+
+ for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
+ msix_entries[i].entry = i;
+
+ err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
+ if (err) {
+ dev_err(&dev->dev,
+ "MSI-X init failed: %d, expected entries: %d\n",
+ err, TIMBERDALE_NR_IRQS);
+ goto err_msix;
+ }
+
+ err = device_create_file(&dev->dev, &dev_attr_fw_ver);
+ if (err)
+ goto err_create_file;
+
+ /* Reset all FPGA PLB peripherals */
+ iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
+
+ /* update IRQ offsets in I2C board info */
+ for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
+ timberdale_i2c_board_info[i].irq =
+ msix_entries[timberdale_i2c_board_info[i].irq].vector;
+
+ /* Update the SPI configuration depending on the HW (8 or 16 bit) */
+ if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
+ timberdale_xspi_platform_data.bits_per_word = 8;
+ timberdale_xspi_platform_data.devices =
+ timberdale_spi_8bit_board_info;
+ timberdale_xspi_platform_data.num_devices =
+ ARRAY_SIZE(timberdale_spi_8bit_board_info);
+ } else {
+ timberdale_xspi_platform_data.bits_per_word = 16;
+ timberdale_xspi_platform_data.devices =
+ timberdale_spi_16bit_board_info;
+ timberdale_xspi_platform_data.num_devices =
+ ARRAY_SIZE(timberdale_spi_16bit_board_info);
+ }
+
+ ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
+ switch (ip_setup) {
+ case TIMB_HW_VER0:
+ err = mfd_add_devices(&dev->dev, -1,
+ timberdale_cells_bar0_cfg0,
+ ARRAY_SIZE(timberdale_cells_bar0_cfg0),
+ &dev->resource[0], msix_entries[0].vector);
+ break;
+ case TIMB_HW_VER1:
+ err = mfd_add_devices(&dev->dev, -1,
+ timberdale_cells_bar0_cfg1,
+ ARRAY_SIZE(timberdale_cells_bar0_cfg1),
+ &dev->resource[0], msix_entries[0].vector);
+ break;
+ case TIMB_HW_VER2:
+ err = mfd_add_devices(&dev->dev, -1,
+ timberdale_cells_bar0_cfg2,
+ ARRAY_SIZE(timberdale_cells_bar0_cfg2),
+ &dev->resource[0], msix_entries[0].vector);
+ break;
+ case TIMB_HW_VER3:
+ err = mfd_add_devices(&dev->dev, -1,
+ timberdale_cells_bar0_cfg3,
+ ARRAY_SIZE(timberdale_cells_bar0_cfg3),
+ &dev->resource[0], msix_entries[0].vector);
+ break;
+ default:
+ dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
+ priv->fw.major, priv->fw.minor, ip_setup);
+ err = -ENODEV;
+ goto err_mfd;
+ break;
+ }
+
+ if (err) {
+ dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
+ goto err_mfd;
+ }
+
+ err = mfd_add_devices(&dev->dev, 0,
+ timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
+ &dev->resource[1], msix_entries[0].vector);
+ if (err) {
+ dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
+ goto err_mfd2;
+ }
+
+ /* only version 0 and 3 have the iNand routed to SDHCI */
+ if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
+ ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
+ err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
+ ARRAY_SIZE(timberdale_cells_bar2),
+ &dev->resource[2], msix_entries[0].vector);
+ if (err) {
+ dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
+ goto err_mfd2;
+ }
+ }
+
+ kfree(msix_entries);
+
+ dev_info(&dev->dev,
+ "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
+ priv->fw.major, priv->fw.minor, priv->fw.config);
+
+ return 0;
+
+err_mfd2:
+ mfd_remove_devices(&dev->dev);
+err_mfd:
+ device_remove_file(&dev->dev, &dev_attr_fw_ver);
+err_create_file:
+ pci_disable_msix(dev);
+err_msix:
+ iounmap(priv->ctl_membase);
+err_ioremap:
+ release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
+err_request:
+ pci_set_drvdata(dev, NULL);
+err_start:
+ pci_disable_device(dev);
+err_enable:
+ kfree(msix_entries);
+ kfree(priv);
+ pci_set_drvdata(dev, NULL);
+ return -ENODEV;
+}
+
+static void __devexit timb_remove(struct pci_dev *dev)
+{
+ struct timberdale_device *priv = pci_get_drvdata(dev);
+
+ mfd_remove_devices(&dev->dev);
+
+ device_remove_file(&dev->dev, &dev_attr_fw_ver);
+
+ iounmap(priv->ctl_membase);
+ release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
+
+ pci_disable_msix(dev);
+ pci_disable_device(dev);
+ pci_set_drvdata(dev, NULL);
+ kfree(priv);
+}
+
+static struct pci_device_id timberdale_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
+
+static struct pci_driver timberdale_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = timberdale_pci_tbl,
+ .probe = timb_probe,
+ .remove = __devexit_p(timb_remove),
+};
+
+static int __init timberdale_init(void)
+{
+ int err;
+
+ err = pci_register_driver(&timberdale_pci_driver);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Failed to register PCI driver for %s device.\n",
+ timberdale_pci_driver.name);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Driver for %s has been successfully registered.\n",
+ timberdale_pci_driver.name);
+
+ return 0;
+}
+
+static void __exit timberdale_exit(void)
+{
+ pci_unregister_driver(&timberdale_pci_driver);
+
+ printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
+ timberdale_pci_driver.name);
+}
+
+module_init(timberdale_init);
+module_exit(timberdale_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/timberdale.h b/drivers/mfd/timberdale.h
new file mode 100644
index 00000000000..8d27ffabc25
--- /dev/null
+++ b/drivers/mfd/timberdale.h
@@ -0,0 +1,130 @@
+/*
+ * timberdale.h timberdale FPGA MFD driver defines
+ * 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.
+ */
+
+/* Supports:
+ * Timberdale FPGA
+ */
+
+#ifndef MFD_TIMBERDALE_H
+#define MFD_TIMBERDALE_H
+
+#define DRV_VERSION "0.1"
+
+/* This driver only support versions >= 3.8 and < 4.0 */
+#define TIMB_SUPPORTED_MAJOR 3
+
+/* This driver only support minor >= 8 */
+#define TIMB_REQUIRED_MINOR 8
+
+/* Registers of the control area */
+#define TIMB_REV_MAJOR 0x00
+#define TIMB_REV_MINOR 0x04
+#define TIMB_HW_CONFIG 0x08
+#define TIMB_SW_RST 0x40
+
+/* bits in the TIMB_HW_CONFIG register */
+#define TIMB_HW_CONFIG_SPI_8BIT 0x80
+
+#define TIMB_HW_VER_MASK 0x0f
+#define TIMB_HW_VER0 0x00
+#define TIMB_HW_VER1 0x01
+#define TIMB_HW_VER2 0x02
+#define TIMB_HW_VER3 0x03
+
+#define OCORESOFFSET 0x0
+#define OCORESEND 0x1f
+
+#define SPIOFFSET 0x80
+#define SPIEND 0xff
+
+#define UARTLITEOFFSET 0x100
+#define UARTLITEEND 0x10f
+
+#define RDSOFFSET 0x180
+#define RDSEND 0x183
+
+#define ETHOFFSET 0x300
+#define ETHEND 0x3ff
+
+#define GPIOOFFSET 0x400
+#define GPIOEND 0x7ff
+
+#define CHIPCTLOFFSET 0x800
+#define CHIPCTLEND 0x8ff
+#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET)
+
+#define INTCOFFSET 0xc00
+#define INTCEND 0xfff
+#define INTCSIZE (INTCEND - INTCOFFSET)
+
+#define MOSTOFFSET 0x1000
+#define MOSTEND 0x13ff
+
+#define UARTOFFSET 0x1400
+#define UARTEND 0x17ff
+
+#define XIICOFFSET 0x1800
+#define XIICEND 0x19ff
+
+#define I2SOFFSET 0x1C00
+#define I2SEND 0x1fff
+
+#define LOGIWOFFSET 0x30000
+#define LOGIWEND 0x37fff
+
+#define MLCOREOFFSET 0x40000
+#define MLCOREEND 0x43fff
+
+#define DMAOFFSET 0x01000000
+#define DMAEND 0x013fffff
+
+/* SDHC0 is placed in PCI bar 1 */
+#define SDHC0OFFSET 0x00
+#define SDHC0END 0xff
+
+/* SDHC1 is placed in PCI bar 2 */
+#define SDHC1OFFSET 0x00
+#define SDHC1END 0xff
+
+#define PCI_VENDOR_ID_TIMB 0x10ee
+#define PCI_DEVICE_ID_TIMB 0xa123
+
+#define IRQ_TIMBERDALE_INIC 0
+#define IRQ_TIMBERDALE_MLB 1
+#define IRQ_TIMBERDALE_GPIO 2
+#define IRQ_TIMBERDALE_I2C 3
+#define IRQ_TIMBERDALE_UART 4
+#define IRQ_TIMBERDALE_DMA 5
+#define IRQ_TIMBERDALE_I2S 6
+#define IRQ_TIMBERDALE_TSC_INT 7
+#define IRQ_TIMBERDALE_SDHC 8
+#define IRQ_TIMBERDALE_ADV7180 9
+#define IRQ_TIMBERDALE_ETHSW_IF 10
+#define IRQ_TIMBERDALE_SPI 11
+#define IRQ_TIMBERDALE_UARTLITE 12
+#define IRQ_TIMBERDALE_MLCORE 13
+#define IRQ_TIMBERDALE_MLCORE_BUF 14
+#define IRQ_TIMBERDALE_RDS 15
+#define TIMBERDALE_NR_IRQS 16
+
+#define GPIO_PIN_ASCB 8
+#define GPIO_PIN_INIC_RST 14
+#define GPIO_PIN_BT_RST 15
+#define GPIO_NR_PINS 16
+
+#endif
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 2a760653419..19a930d0624 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -115,7 +115,8 @@
#define twl_has_watchdog() false
#endif
-#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
+ defined(CONFIG_SND_SOC_TWL6030) || defined(CONFIG_SND_SOC_TWL6030_MODULE)
#define twl_has_codec() true
#else
#define twl_has_codec() false
@@ -711,8 +712,19 @@ 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",
+ if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
+ sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
+ child = add_child(sub_chip_id, "twl4030_codec",
+ pdata->codec, sizeof(*pdata->codec),
+ false, 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+ /* Phoenix*/
+ if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
+ sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
+ child = add_child(sub_chip_id, "twl6030_codec",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index c5a7a855f4b..381fe032caa 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -154,9 +154,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
if (mq->bounce_buf) {
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
- blk_queue_max_sectors(mq->queue, bouncesz / 512);
- blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
- blk_queue_max_hw_segments(mq->queue, bouncesz / 512);
+ blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
+ blk_queue_max_segments(mq->queue, bouncesz / 512);
blk_queue_max_segment_size(mq->queue, bouncesz);
mq->sg = kmalloc(sizeof(struct scatterlist),
@@ -180,10 +179,9 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
if (!mq->bounce_buf) {
blk_queue_bounce_limit(mq->queue, limit);
- blk_queue_max_sectors(mq->queue,
+ blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
- blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
- blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
+ blk_queue_max_segments(mq->queue, host->max_hw_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
mq->sg = kmalloc(sizeof(struct scatterlist) *
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index d3f55615c09..57b21198828 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -650,11 +650,11 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host,
flags = DDMA_FLAGS_IE;
if (host->flags & HOST_F_XMIT) {
- ret = au1xxx_dbdma_put_source_flags(channel,
- (void *)sg_virt(sg), len, flags);
+ ret = au1xxx_dbdma_put_source(channel,
+ sg_phys(sg), len, flags);
} else {
- ret = au1xxx_dbdma_put_dest_flags(channel,
- (void *)sg_virt(sg), len, flags);
+ ret = au1xxx_dbdma_put_dest(channel,
+ sg_phys(sg), len, flags);
}
if (!ret)
@@ -1017,6 +1017,10 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
} else
mmc->caps |= MMC_CAP_NEEDS_POLL;
+ /* platform may not be able to use all advertised caps */
+ if (host->platdata)
+ mmc->caps &= ~(host->platdata->mask_host_caps);
+
tasklet_init(&host->data_task, au1xmmc_tasklet_data,
(unsigned long)host);
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 90d168ad03b..84c103a7ee1 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -2,6 +2,7 @@
* linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver
*
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ * Copyright (C) 2010 ST-Ericsson AB.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -34,9 +35,6 @@
#define DRIVER_NAME "mmci-pl18x"
-#define DBG(host,fmt,args...) \
- pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args)
-
static unsigned int fmax = 515633;
/*
@@ -105,8 +103,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
void __iomem *base;
int blksz_bits;
- DBG(host, "blksz %04x blks %04x flags %08x\n",
- data->blksz, data->blocks, data->flags);
+ dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
+ data->blksz, data->blocks, data->flags);
host->data = data;
host->size = data->blksz;
@@ -155,7 +153,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
{
void __iomem *base = host->base;
- DBG(host, "op %02x arg %08x flags %08x\n",
+ dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
cmd->opcode, cmd->arg, cmd->flags);
if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
@@ -184,8 +182,20 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
{
if (status & MCI_DATABLOCKEND) {
host->data_xfered += data->blksz;
+#ifdef CONFIG_ARCH_U300
+ /*
+ * On the U300 some signal or other is
+ * badly routed so that a data write does
+ * not properly terminate with a MCI_DATAEND
+ * status flag. This quirk will make writes
+ * work again.
+ */
+ if (data->flags & MMC_DATA_WRITE)
+ status |= MCI_DATAEND;
+#endif
}
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+ dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status);
if (status & MCI_DATACRCFAIL)
data->error = -EILSEQ;
else if (status & MCI_DATATIMEOUT)
@@ -307,7 +317,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
status = readl(base + MMCISTATUS);
- DBG(host, "irq1 %08x\n", status);
+ dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
do {
unsigned long flags;
@@ -401,7 +411,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
status &= readl(host->base + MMCIMASK0);
writel(status, host->base + MMCICLEAR);
- DBG(host, "irq0 %08x\n", status);
+ dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
data = host->data;
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
@@ -428,8 +438,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(host->mrq != NULL);
if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
- printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
- mmc_hostname(mmc), mrq->data->blksz);
+ dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n",
+ mrq->data->blksz);
mrq->cmd->error = -EINVAL;
mmc_request_done(mmc, mrq);
return;
@@ -582,8 +592,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev);
- DBG(host, "designer ID = 0x%02x\n", host->hw_designer);
- DBG(host, "revision = 0x%01x\n", host->hw_revision);
+ dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer);
+ dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision);
host->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(host->clk)) {
@@ -608,7 +618,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
if (ret < 0)
goto clk_disable;
host->mclk = clk_get_rate(host->clk);
- DBG(host, "eventual mclk rate: %u Hz\n", host->mclk);
+ dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",
+ host->mclk);
}
host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
@@ -619,6 +630,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
mmc->ops = &mmci_ops;
mmc->f_min = (host->mclk + 511) / 512;
mmc->f_max = min(host->mclk, fmax);
+ dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
+
#ifdef CONFIG_REGULATOR
/* If we're using the regulator framework, try to fetch a regulator */
host->vcc = regulator_get(&dev->dev, "vmmc");
@@ -712,7 +725,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
mmc_add_host(mmc);
- printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
+ dev_info(&dev->dev, "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
mmc_hostname(mmc), amba_rev(dev), amba_config(dev),
(unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]);
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 9605cb87c74..aa2807d0ce7 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -251,12 +251,6 @@ config MTD_NETtel
help
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
-config MTD_ALCHEMY
- tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support"
- depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI
- help
- Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
-
config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index faa9fef1958..bb035cd54c7 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -40,7 +40,6 @@ obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
-obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o
obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
obj-$(CONFIG_MTD_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_IMPA7) += impa7.o
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c
deleted file mode 100644
index 845ad4f2a54..00000000000
--- a/drivers/mtd/maps/alchemy-flash.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Flash memory access on AMD Alchemy evaluation boards
- *
- * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-
-#ifdef CONFIG_MIPS_PB1000
-#define BOARD_MAP_NAME "Pb1000 Flash"
-#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_PB1500
-#define BOARD_MAP_NAME "Pb1500 Flash"
-#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_PB1100
-#define BOARD_MAP_NAME "Pb1100 Flash"
-#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_PB1550
-#define BOARD_MAP_NAME "Pb1550 Flash"
-#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_PB1200
-#define BOARD_MAP_NAME "Pb1200 Flash"
-#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
-#define BOARD_FLASH_WIDTH 2 /* 16-bits */
-#endif
-
-#ifdef CONFIG_MIPS_DB1000
-#define BOARD_MAP_NAME "Db1000 Flash"
-#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_DB1500
-#define BOARD_MAP_NAME "Db1500 Flash"
-#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_DB1100
-#define BOARD_MAP_NAME "Db1100 Flash"
-#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_DB1550
-#define BOARD_MAP_NAME "Db1550 Flash"
-#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#endif
-
-#ifdef CONFIG_MIPS_DB1200
-#define BOARD_MAP_NAME "Db1200 Flash"
-#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
-#define BOARD_FLASH_WIDTH 2 /* 16-bits */
-#endif
-
-#ifdef CONFIG_MIPS_BOSPORUS
-#define BOARD_MAP_NAME "Bosporus Flash"
-#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
-#define BOARD_FLASH_WIDTH 2 /* 16-bits */
-#endif
-
-#ifdef CONFIG_MIPS_MIRAGE
-#define BOARD_MAP_NAME "Mirage Flash"
-#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#define USE_LOCAL_ACCESSORS /* why? */
-#endif
-
-static struct map_info alchemy_map = {
- .name = BOARD_MAP_NAME,
-};
-
-static struct mtd_partition alchemy_partitions[] = {
- {
- .name = "User FS",
- .size = BOARD_FLASH_SIZE - 0x00400000,
- .offset = 0x0000000
- },{
- .name = "YAMON",
- .size = 0x0100000,
- .offset = MTDPART_OFS_APPEND,
- .mask_flags = MTD_WRITEABLE
- },{
- .name = "raw kernel",
- .size = (0x300000 - 0x40000), /* last 256KB is yamon env */
- .offset = MTDPART_OFS_APPEND,
- }
-};
-
-static struct mtd_info *mymtd;
-
-static int __init alchemy_mtd_init(void)
-{
- struct mtd_partition *parts;
- int nb_parts = 0;
- unsigned long window_addr;
- unsigned long window_size;
-
- /* Default flash buswidth */
- alchemy_map.bankwidth = BOARD_FLASH_WIDTH;
-
- window_addr = 0x20000000 - BOARD_FLASH_SIZE;
- window_size = BOARD_FLASH_SIZE;
-
- /*
- * Static partition definition selection
- */
- parts = alchemy_partitions;
- nb_parts = ARRAY_SIZE(alchemy_partitions);
- alchemy_map.size = window_size;
-
- /*
- * Now let's probe for the actual flash. Do it here since
- * specific machine settings might have been set above.
- */
- printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n",
- alchemy_map.bankwidth*8);
- alchemy_map.virt = ioremap(window_addr, window_size);
- mymtd = do_map_probe("cfi_probe", &alchemy_map);
- if (!mymtd) {
- iounmap(alchemy_map.virt);
- return -ENXIO;
- }
- mymtd->owner = THIS_MODULE;
-
- add_mtd_partitions(mymtd, parts, nb_parts);
- return 0;
-}
-
-static void __exit alchemy_mtd_cleanup(void)
-{
- if (mymtd) {
- del_mtd_partitions(mymtd);
- map_destroy(mymtd);
- iounmap(alchemy_map.virt);
- }
-}
-
-module_init(alchemy_mtd_init);
-module_exit(alchemy_mtd_cleanup);
-
-MODULE_AUTHOR("Embedded Alley Solutions, Inc");
-MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 677cd53f18c..bb646560423 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -457,10 +457,10 @@ config MTD_NAND_NOMADIK
config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
- depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723
+ depends on MTD_NAND && SUPERH
help
Several Renesas SuperH CPU has FLCTL. This option enables support
- for NAND Flash using FLCTL. This driver support SH7723.
+ for NAND Flash using FLCTL.
config MTD_NAND_DAVINCI
tristate "Support NAND on DaVinci SoC"
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 92c334ff450..43d46e42404 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -19,6 +19,7 @@
#include <asm/io.h>
#include <asm/mach-au1x00/au1xxx.h>
+#include <asm/mach-db1x00/bcsr.h>
/*
* MTD structure for NAND controller
@@ -475,7 +476,8 @@ static int __init au1xxx_nand_init(void)
/* set gpio206 high */
au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR);
- boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1);
+ boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr_read(BCSR_STATUS) >> 6) & 0x1);
+
switch (boot_swapboot) {
case 0:
case 2:
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 02bef21f2e4..1842df8bdd9 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -1,10 +1,10 @@
/*
* SuperH FLCTL nand controller
*
- * Copyright © 2008 Renesas Solutions Corp.
- * Copyright © 2008 Atom Create Engineering Co., Ltd.
+ * Copyright (c) 2008 Renesas Solutions Corp.
+ * Copyright (c) 2008 Atom Create Engineering Co., Ltd.
*
- * Based on fsl_elbc_nand.c, Copyright © 2006-2007 Freescale Semiconductor
+ * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -75,6 +75,11 @@ static void start_translation(struct sh_flctl *flctl)
writeb(TRSTRT, FLTRCR(flctl));
}
+static void timeout_error(struct sh_flctl *flctl, const char *str)
+{
+ dev_err(&flctl->pdev->dev, "Timeout occured in %s\n", str);
+}
+
static void wait_completion(struct sh_flctl *flctl)
{
uint32_t timeout = LOOP_TIMEOUT_MAX;
@@ -87,7 +92,7 @@ static void wait_completion(struct sh_flctl *flctl)
udelay(1);
}
- printk(KERN_ERR "wait_completion(): Timeout occured \n");
+ timeout_error(flctl, __func__);
writeb(0x0, FLTRCR(flctl));
}
@@ -100,6 +105,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr)
addr = page_addr; /* ERASE1 */
} else if (page_addr != -1) {
/* SEQIN, READ0, etc.. */
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ column >>= 1;
if (flctl->page_size) {
addr = column & 0x0FFF;
addr |= (page_addr & 0xff) << 16;
@@ -132,7 +139,7 @@ static void wait_rfifo_ready(struct sh_flctl *flctl)
return;
udelay(1);
}
- printk(KERN_ERR "wait_rfifo_ready(): Timeout occured \n");
+ timeout_error(flctl, __func__);
}
static void wait_wfifo_ready(struct sh_flctl *flctl)
@@ -146,7 +153,7 @@ static void wait_wfifo_ready(struct sh_flctl *flctl)
return;
udelay(1);
}
- printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n");
+ timeout_error(flctl, __func__);
}
static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
@@ -198,7 +205,7 @@ static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
writel(0, FL4ECCCR(flctl));
}
- printk(KERN_ERR "wait_recfifo_ready(): Timeout occured \n");
+ timeout_error(flctl, __func__);
return 1; /* timeout */
}
@@ -214,7 +221,7 @@ static void wait_wecfifo_ready(struct sh_flctl *flctl)
return;
udelay(1);
}
- printk(KERN_ERR "wait_wecfifo_ready(): Timeout occured \n");
+ timeout_error(flctl, __func__);
}
static void read_datareg(struct sh_flctl *flctl, int offset)
@@ -275,7 +282,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
{
struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint32_t flcmncr_val = readl(FLCMNCR(flctl));
+ uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT;
uint32_t flcmdcr_val, addr_len_bytes = 0;
/* Set SNAND bit if page size is 2048byte */
@@ -297,6 +304,8 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
case NAND_CMD_READOOB:
addr_len_bytes = flctl->rw_ADRCNT;
flcmdcr_val |= CDSRC_E;
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ flcmncr_val |= SEL_16BIT;
break;
case NAND_CMD_SEQIN:
/* This case is that cmd is READ0 or READ1 or READ00 */
@@ -305,6 +314,8 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
case NAND_CMD_PAGEPROG:
addr_len_bytes = flctl->rw_ADRCNT;
flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW;
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ flcmncr_val |= SEL_16BIT;
break;
case NAND_CMD_READID:
flcmncr_val &= ~SNAND_E;
@@ -523,6 +534,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
set_addr(mtd, 0, page_addr);
flctl->read_bytes = mtd->writesize + mtd->oobsize;
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ column >>= 1;
flctl->index += column;
goto read_normal_exit;
@@ -686,6 +699,18 @@ static uint8_t flctl_read_byte(struct mtd_info *mtd)
return data;
}
+static uint16_t flctl_read_word(struct mtd_info *mtd)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ int index = flctl->index;
+ uint16_t data;
+ uint16_t *buf = (uint16_t *)&flctl->done_buff[index];
+
+ data = *buf;
+ flctl->index += 2;
+ return data;
+}
+
static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
@@ -769,38 +794,36 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
return 0;
}
-static int __init flctl_probe(struct platform_device *pdev)
+static int __devinit flctl_probe(struct platform_device *pdev)
{
struct resource *res;
struct sh_flctl *flctl;
struct mtd_info *flctl_mtd;
struct nand_chip *nand;
struct sh_flctl_platform_data *pdata;
- int ret;
+ int ret = -ENXIO;
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
- printk(KERN_ERR "sh_flctl platform_data not found.\n");
- return -ENODEV;
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
}
flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL);
if (!flctl) {
- printk(KERN_ERR "Unable to allocate NAND MTD dev structure.\n");
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
- printk(KERN_ERR "%s: resource not found.\n", __func__);
- ret = -ENODEV;
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
goto err;
}
- flctl->reg = ioremap(res->start, res->end - res->start + 1);
+ flctl->reg = ioremap(res->start, resource_size(res));
if (flctl->reg == NULL) {
- printk(KERN_ERR "%s: ioremap error.\n", __func__);
- ret = -ENOMEM;
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
goto err;
}
@@ -808,6 +831,7 @@ static int __init flctl_probe(struct platform_device *pdev)
flctl_mtd = &flctl->mtd;
nand = &flctl->chip;
flctl_mtd->priv = nand;
+ flctl->pdev = pdev;
flctl->hwecc = pdata->has_hwecc;
flctl_register_init(flctl, pdata->flcmncr_val);
@@ -825,6 +849,11 @@ static int __init flctl_probe(struct platform_device *pdev)
nand->select_chip = flctl_select_chip;
nand->cmdfunc = flctl_cmdfunc;
+ if (pdata->flcmncr_val & SEL_16BIT) {
+ nand->options |= NAND_BUSWIDTH_16;
+ nand->read_word = flctl_read_word;
+ }
+
ret = nand_scan_ident(flctl_mtd, 1);
if (ret)
goto err;
@@ -846,7 +875,7 @@ err:
return ret;
}
-static int __exit flctl_remove(struct platform_device *pdev)
+static int __devexit flctl_remove(struct platform_device *pdev)
{
struct sh_flctl *flctl = platform_get_drvdata(pdev);
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index 6bac04603a8..6e5a68ecde0 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -55,6 +55,7 @@
#include <linux/delay.h>
#include <linux/crc32.h>
#include <linux/phy.h>
+#include <linux/platform_device.h>
#include <asm/cpu.h>
#include <asm/mipsregs.h>
@@ -63,6 +64,7 @@
#include <asm/processor.h>
#include <au1000.h>
+#include <au1xxx_eth.h>
#include <prom.h>
#include "au1000_eth.h"
@@ -112,15 +114,15 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
*
* PHY detection algorithm
*
- * If AU1XXX_PHY_STATIC_CONFIG is undefined, the PHY setup is
+ * If phy_static_config is undefined, the PHY setup is
* autodetected:
*
* mii_probe() first searches the current MAC's MII bus for a PHY,
- * selecting the first (or last, if AU1XXX_PHY_SEARCH_HIGHEST_ADDR is
+ * selecting the first (or last, if phy_search_highest_addr is
* defined) PHY address not already claimed by another netdev.
*
* If nothing was found that way when searching for the 2nd ethernet
- * controller's PHY and AU1XXX_PHY1_SEARCH_ON_MAC0 is defined, then
+ * controller's PHY and phy1_search_mac0 is defined, then
* the first MII bus is searched as well for an unclaimed PHY; this is
* needed in case of a dual-PHY accessible only through the MAC0's MII
* bus.
@@ -129,9 +131,7 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
* controller is not registered to the network subsystem.
*/
-/* autodetection defaults */
-#undef AU1XXX_PHY_SEARCH_HIGHEST_ADDR
-#define AU1XXX_PHY1_SEARCH_ON_MAC0
+/* autodetection defaults: phy1_search_mac0 */
/* static PHY setup
*
@@ -148,29 +148,6 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
* specific irq-map
*/
-#if defined(CONFIG_MIPS_BOSPORUS)
-/*
- * Micrel/Kendin 5 port switch attached to MAC0,
- * MAC0 is associated with PHY address 5 (== WAN port)
- * MAC1 is not associated with any PHY, since it's connected directly
- * to the switch.
- * no interrupts are used
- */
-# define AU1XXX_PHY_STATIC_CONFIG
-
-# define AU1XXX_PHY0_ADDR 5
-# define AU1XXX_PHY0_BUSID 0
-# undef AU1XXX_PHY0_IRQ
-
-# undef AU1XXX_PHY1_ADDR
-# undef AU1XXX_PHY1_BUSID
-# undef AU1XXX_PHY1_IRQ
-#endif
-
-#if defined(AU1XXX_PHY0_BUSID) && (AU1XXX_PHY0_BUSID > 0)
-# error MAC0-associated PHY attached 2nd MACs MII bus not supported yet
-#endif
-
static void enable_mac(struct net_device *dev, int force_reset)
{
unsigned long flags;
@@ -390,67 +367,55 @@ static int mii_probe (struct net_device *dev)
struct au1000_private *const aup = netdev_priv(dev);
struct phy_device *phydev = NULL;
-#if defined(AU1XXX_PHY_STATIC_CONFIG)
- BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
+ if (aup->phy_static_config) {
+ BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
- if(aup->mac_id == 0) { /* get PHY0 */
-# if defined(AU1XXX_PHY0_ADDR)
- phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus->phy_map[AU1XXX_PHY0_ADDR];
-# else
- printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
- dev->name);
- return 0;
-# endif /* defined(AU1XXX_PHY0_ADDR) */
- } else if (aup->mac_id == 1) { /* get PHY1 */
-# if defined(AU1XXX_PHY1_ADDR)
- phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus->phy_map[AU1XXX_PHY1_ADDR];
-# else
- printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
- dev->name);
+ if (aup->phy_addr)
+ phydev = aup->mii_bus->phy_map[aup->phy_addr];
+ else
+ printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
+ dev->name);
return 0;
-# endif /* defined(AU1XXX_PHY1_ADDR) */
- }
-
-#else /* defined(AU1XXX_PHY_STATIC_CONFIG) */
- int phy_addr;
-
- /* find the first (lowest address) PHY on the current MAC's MII bus */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
- if (aup->mii_bus->phy_map[phy_addr]) {
- phydev = aup->mii_bus->phy_map[phy_addr];
-# if !defined(AU1XXX_PHY_SEARCH_HIGHEST_ADDR)
- break; /* break out with first one found */
-# endif
- }
+ } else {
+ int phy_addr;
+
+ /* find the first (lowest address) PHY on the current MAC's MII bus */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
+ if (aup->mii_bus->phy_map[phy_addr]) {
+ phydev = aup->mii_bus->phy_map[phy_addr];
+ if (!aup->phy_search_highest_addr)
+ break; /* break out with first one found */
+ }
-# if defined(AU1XXX_PHY1_SEARCH_ON_MAC0)
- /* try harder to find a PHY */
- if (!phydev && (aup->mac_id == 1)) {
- /* no PHY found, maybe we have a dual PHY? */
- printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, "
- "let's see if it's attached to MAC0...\n");
+ if (aup->phy1_search_mac0) {
+ /* try harder to find a PHY */
+ if (!phydev && (aup->mac_id == 1)) {
+ /* no PHY found, maybe we have a dual PHY? */
+ printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, "
+ "let's see if it's attached to MAC0...\n");
- BUG_ON(!au_macs[0]);
+ /* find the first (lowest address) non-attached PHY on
+ * the MAC0 MII bus */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ struct phy_device *const tmp_phydev =
+ aup->mii_bus->phy_map[phy_addr];
- /* find the first (lowest address) non-attached PHY on
- * the MAC0 MII bus */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- struct phy_device *const tmp_phydev =
- au_macs[0]->mii_bus->phy_map[phy_addr];
+ if (aup->mac_id == 1)
+ break;
- if (!tmp_phydev)
- continue; /* no PHY here... */
+ if (!tmp_phydev)
+ continue; /* no PHY here... */
- if (tmp_phydev->attached_dev)
- continue; /* already claimed by MAC0 */
+ if (tmp_phydev->attached_dev)
+ continue; /* already claimed by MAC0 */
- phydev = tmp_phydev;
- break; /* found it */
+ phydev = tmp_phydev;
+ break; /* found it */
+ }
+ }
}
}
-# endif /* defined(AU1XXX_PHY1_SEARCH_OTHER_BUS) */
-#endif /* defined(AU1XXX_PHY_STATIC_CONFIG) */
if (!phydev) {
printk (KERN_ERR DRV_NAME ":%s: no PHY found\n", dev->name);
return -1;
@@ -578,31 +543,6 @@ setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
}
}
-static struct {
- u32 base_addr;
- u32 macen_addr;
- int irq;
- struct net_device *dev;
-} iflist[2] = {
-#ifdef CONFIG_SOC_AU1000
- {AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT},
- {AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT}
-#endif
-#ifdef CONFIG_SOC_AU1100
- {AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT}
-#endif
-#ifdef CONFIG_SOC_AU1500
- {AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT},
- {AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT}
-#endif
-#ifdef CONFIG_SOC_AU1550
- {AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT},
- {AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT}
-#endif
-};
-
-static int num_ifs;
-
/*
* ethtool operations
*/
@@ -711,7 +651,6 @@ static int au1000_init(struct net_device *dev)
static inline void update_rx_stats(struct net_device *dev, u32 status)
{
- struct au1000_private *aup = netdev_priv(dev);
struct net_device_stats *ps = &dev->stats;
ps->rx_packets++;
@@ -969,7 +908,7 @@ static netdev_tx_t au1000_tx(struct sk_buff *skb, struct net_device *dev)
}
pDB = aup->tx_db_inuse[aup->tx_head];
- skb_copy_from_linear_data(skb, pDB->vaddr, skb->len);
+ skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len);
if (skb->len < ETH_ZLEN) {
for (i=skb->len; i<ETH_ZLEN; i++) {
((char *)pDB->vaddr)[i] = 0;
@@ -1058,53 +997,59 @@ static const struct net_device_ops au1000_netdev_ops = {
.ndo_change_mtu = eth_change_mtu,
};
-static struct net_device * au1000_probe(int port_num)
+static int __devinit au1000_probe(struct platform_device *pdev)
{
static unsigned version_printed = 0;
struct au1000_private *aup = NULL;
+ struct au1000_eth_platform_data *pd;
struct net_device *dev = NULL;
db_dest_t *pDB, *pDBfree;
+ int irq, i, err = 0;
+ struct resource *base, *macen;
char ethaddr[6];
- int irq, i, err;
- u32 base, macen;
- if (port_num >= NUM_ETH_INTERFACES)
- return NULL;
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!base) {
+ printk(KERN_ERR DRV_NAME ": failed to retrieve base register\n");
+ err = -ENODEV;
+ goto out;
+ }
- base = CPHYSADDR(iflist[port_num].base_addr );
- macen = CPHYSADDR(iflist[port_num].macen_addr);
- irq = iflist[port_num].irq;
+ macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!macen) {
+ printk(KERN_ERR DRV_NAME ": failed to retrieve MAC Enable register\n");
+ err = -ENODEV;
+ goto out;
+ }
- if (!request_mem_region( base, MAC_IOSIZE, "Au1x00 ENET") ||
- !request_mem_region(macen, 4, "Au1x00 ENET"))
- return NULL;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ printk(KERN_ERR DRV_NAME ": failed to retrieve IRQ\n");
+ err = -ENODEV;
+ goto out;
+ }
- if (version_printed++ == 0)
- printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
+ if (!request_mem_region(base->start, resource_size(base), pdev->name)) {
+ printk(KERN_ERR DRV_NAME ": failed to request memory region for base registers\n");
+ err = -ENXIO;
+ goto out;
+ }
+
+ if (!request_mem_region(macen->start, resource_size(macen), pdev->name)) {
+ printk(KERN_ERR DRV_NAME ": failed to request memory region for MAC enable register\n");
+ err = -ENXIO;
+ goto err_request;
+ }
dev = alloc_etherdev(sizeof(struct au1000_private));
if (!dev) {
printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME);
- return NULL;
+ err = -ENOMEM;
+ goto err_alloc;
}
- dev->base_addr = base;
- dev->irq = irq;
- dev->netdev_ops = &au1000_netdev_ops;
- SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
- dev->watchdog_timeo = ETH_TX_TIMEOUT;
-
- err = register_netdev(dev);
- if (err != 0) {
- printk(KERN_ERR "%s: Cannot register net device, error %d\n",
- DRV_NAME, err);
- free_netdev(dev);
- return NULL;
- }
-
- printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n",
- dev->name, base, irq);
-
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
aup = netdev_priv(dev);
spin_lock_init(&aup->lock);
@@ -1115,21 +1060,29 @@ static struct net_device * au1000_probe(int port_num)
(NUM_TX_BUFFS + NUM_RX_BUFFS),
&aup->dma_addr, 0);
if (!aup->vaddr) {
- free_netdev(dev);
- release_mem_region( base, MAC_IOSIZE);
- release_mem_region(macen, 4);
- return NULL;
+ printk(KERN_ERR DRV_NAME ": failed to allocate data buffers\n");
+ err = -ENOMEM;
+ goto err_vaddr;
}
/* aup->mac is the base address of the MAC's registers */
- aup->mac = (volatile mac_reg_t *)iflist[port_num].base_addr;
+ aup->mac = (volatile mac_reg_t *)ioremap_nocache(base->start, resource_size(base));
+ if (!aup->mac) {
+ printk(KERN_ERR DRV_NAME ": failed to ioremap MAC registers\n");
+ err = -ENXIO;
+ goto err_remap1;
+ }
- /* Setup some variables for quick register address access */
- aup->enable = (volatile u32 *)iflist[port_num].macen_addr;
- aup->mac_id = port_num;
- au_macs[port_num] = aup;
+ /* Setup some variables for quick register address access */
+ aup->enable = (volatile u32 *)ioremap_nocache(macen->start, resource_size(macen));
+ if (!aup->enable) {
+ printk(KERN_ERR DRV_NAME ": failed to ioremap MAC enable register\n");
+ err = -ENXIO;
+ goto err_remap2;
+ }
+ aup->mac_id = pdev->id;
- if (port_num == 0) {
+ if (pdev->id == 0) {
if (prom_get_ethernet_addr(ethaddr) == 0)
memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr));
else {
@@ -1139,7 +1092,7 @@ static struct net_device * au1000_probe(int port_num)
}
setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
- } else if (port_num == 1)
+ } else if (pdev->id == 1)
setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
/*
@@ -1147,14 +1100,37 @@ static struct net_device * au1000_probe(int port_num)
* to match those that are printed on their stickers
*/
memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
- dev->dev_addr[5] += port_num;
+ dev->dev_addr[5] += pdev->id;
*aup->enable = 0;
aup->mac_enabled = 0;
+ pd = pdev->dev.platform_data;
+ if (!pd) {
+ printk(KERN_INFO DRV_NAME ": no platform_data passed, PHY search on MAC0\n");
+ aup->phy1_search_mac0 = 1;
+ } else {
+ aup->phy_static_config = pd->phy_static_config;
+ aup->phy_search_highest_addr = pd->phy_search_highest_addr;
+ aup->phy1_search_mac0 = pd->phy1_search_mac0;
+ aup->phy_addr = pd->phy_addr;
+ aup->phy_busid = pd->phy_busid;
+ aup->phy_irq = pd->phy_irq;
+ }
+
+ if (aup->phy_busid && aup->phy_busid > 0) {
+ printk(KERN_ERR DRV_NAME ": MAC0-associated PHY attached 2nd MACs MII"
+ "bus not supported yet\n");
+ err = -ENODEV;
+ goto err_mdiobus_alloc;
+ }
+
aup->mii_bus = mdiobus_alloc();
- if (aup->mii_bus == NULL)
- goto err_out;
+ if (aup->mii_bus == NULL) {
+ printk(KERN_ERR DRV_NAME ": failed to allocate mdiobus structure\n");
+ err = -ENOMEM;
+ goto err_mdiobus_alloc;
+ }
aup->mii_bus->priv = dev;
aup->mii_bus->read = au1000_mdiobus_read;
@@ -1168,23 +1144,19 @@ static struct net_device * au1000_probe(int port_num)
for(i = 0; i < PHY_MAX_ADDR; ++i)
aup->mii_bus->irq[i] = PHY_POLL;
-
/* if known, set corresponding PHY IRQs */
-#if defined(AU1XXX_PHY_STATIC_CONFIG)
-# if defined(AU1XXX_PHY0_IRQ)
- if (AU1XXX_PHY0_BUSID == aup->mac_id)
- aup->mii_bus->irq[AU1XXX_PHY0_ADDR] = AU1XXX_PHY0_IRQ;
-# endif
-# if defined(AU1XXX_PHY1_IRQ)
- if (AU1XXX_PHY1_BUSID == aup->mac_id)
- aup->mii_bus->irq[AU1XXX_PHY1_ADDR] = AU1XXX_PHY1_IRQ;
-# endif
-#endif
- mdiobus_register(aup->mii_bus);
+ if (aup->phy_static_config)
+ if (aup->phy_irq && aup->phy_busid == aup->mac_id)
+ aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq;
+
+ err = mdiobus_register(aup->mii_bus);
+ if (err) {
+ printk(KERN_ERR DRV_NAME " failed to register MDIO bus\n");
+ goto err_mdiobus_reg;
+ }
- if (mii_probe(dev) != 0) {
+ if (mii_probe(dev) != 0)
goto err_out;
- }
pDBfree = NULL;
/* setup the data buffer descriptors and attach a buffer to each one */
@@ -1216,19 +1188,35 @@ static struct net_device * au1000_probe(int port_num)
aup->tx_db_inuse[i] = pDB;
}
+ dev->base_addr = base->start;
+ dev->irq = irq;
+ dev->netdev_ops = &au1000_netdev_ops;
+ SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
+ dev->watchdog_timeo = ETH_TX_TIMEOUT;
+
/*
* The boot code uses the ethernet controller, so reset it to start
* fresh. au1000_init() expects that the device is in reset state.
*/
reset_mac(dev);
- return dev;
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_ERR DRV_NAME "%s: Cannot register net device, aborting.\n",
+ dev->name);
+ goto err_out;
+ }
+
+ printk("%s: Au1xx0 Ethernet found at 0x%lx, irq %d\n",
+ dev->name, (unsigned long)base->start, irq);
+ if (version_printed++ == 0)
+ printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
+
+ return 0;
err_out:
- if (aup->mii_bus != NULL) {
+ if (aup->mii_bus != NULL)
mdiobus_unregister(aup->mii_bus);
- mdiobus_free(aup->mii_bus);
- }
/* here we should have a valid dev plus aup-> register addresses
* so we can reset the mac properly.*/
@@ -1242,67 +1230,84 @@ err_out:
if (aup->tx_db_inuse[i])
ReleaseDB(aup, aup->tx_db_inuse[i]);
}
+err_mdiobus_reg:
+ mdiobus_free(aup->mii_bus);
+err_mdiobus_alloc:
+ iounmap(aup->enable);
+err_remap2:
+ iounmap(aup->mac);
+err_remap1:
dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
(void *)aup->vaddr, aup->dma_addr);
- unregister_netdev(dev);
+err_vaddr:
free_netdev(dev);
- release_mem_region( base, MAC_IOSIZE);
- release_mem_region(macen, 4);
- return NULL;
+err_alloc:
+ release_mem_region(macen->start, resource_size(macen));
+err_request:
+ release_mem_region(base->start, resource_size(base));
+out:
+ return err;
}
-/*
- * Setup the base address and interrupt of the Au1xxx ethernet macs
- * based on cpu type and whether the interface is enabled in sys_pinfunc
- * register. The last interface is enabled if SYS_PF_NI2 (bit 4) is 0.
- */
-static int __init au1000_init_module(void)
+static int __devexit au1000_remove(struct platform_device *pdev)
{
- int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
- struct net_device *dev;
- int i, found_one = 0;
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct au1000_private *aup = netdev_priv(dev);
+ int i;
+ struct resource *base, *macen;
- num_ifs = NUM_ETH_INTERFACES - ni;
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(dev);
+ mdiobus_unregister(aup->mii_bus);
+ mdiobus_free(aup->mii_bus);
+
+ for (i = 0; i < NUM_RX_DMA; i++)
+ if (aup->rx_db_inuse[i])
+ ReleaseDB(aup, aup->rx_db_inuse[i]);
+
+ for (i = 0; i < NUM_TX_DMA; i++)
+ if (aup->tx_db_inuse[i])
+ ReleaseDB(aup, aup->tx_db_inuse[i]);
+
+ dma_free_noncoherent(NULL, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ (void *)aup->vaddr, aup->dma_addr);
+
+ iounmap(aup->mac);
+ iounmap(aup->enable);
+
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(base->start, resource_size(base));
+
+ macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(macen->start, resource_size(macen));
+
+ free_netdev(dev);
- for(i = 0; i < num_ifs; i++) {
- dev = au1000_probe(i);
- iflist[i].dev = dev;
- if (dev)
- found_one++;
- }
- if (!found_one)
- return -ENODEV;
return 0;
}
-static void __exit au1000_cleanup_module(void)
+static struct platform_driver au1000_eth_driver = {
+ .probe = au1000_probe,
+ .remove = __devexit_p(au1000_remove),
+ .driver = {
+ .name = "au1000-eth",
+ .owner = THIS_MODULE,
+ },
+};
+MODULE_ALIAS("platform:au1000-eth");
+
+
+static int __init au1000_init_module(void)
+{
+ return platform_driver_register(&au1000_eth_driver);
+}
+
+static void __exit au1000_exit_module(void)
{
- int i, j;
- struct net_device *dev;
- struct au1000_private *aup;
-
- for (i = 0; i < num_ifs; i++) {
- dev = iflist[i].dev;
- if (dev) {
- aup = netdev_priv(dev);
- unregister_netdev(dev);
- mdiobus_unregister(aup->mii_bus);
- mdiobus_free(aup->mii_bus);
- for (j = 0; j < NUM_RX_DMA; j++)
- if (aup->rx_db_inuse[j])
- ReleaseDB(aup, aup->rx_db_inuse[j]);
- for (j = 0; j < NUM_TX_DMA; j++)
- if (aup->tx_db_inuse[j])
- ReleaseDB(aup, aup->tx_db_inuse[j]);
- dma_free_noncoherent(NULL, MAX_BUF_SIZE *
- (NUM_TX_BUFFS + NUM_RX_BUFFS),
- (void *)aup->vaddr, aup->dma_addr);
- release_mem_region(dev->base_addr, MAC_IOSIZE);
- release_mem_region(CPHYSADDR(iflist[i].macen_addr), 4);
- free_netdev(dev);
- }
- }
+ platform_driver_unregister(&au1000_eth_driver);
}
module_init(au1000_init_module);
-module_exit(au1000_cleanup_module);
+module_exit(au1000_exit_module);
diff --git a/drivers/net/au1000_eth.h b/drivers/net/au1000_eth.h
index 824ecd5ff3a..f9d29a29b8f 100644
--- a/drivers/net/au1000_eth.h
+++ b/drivers/net/au1000_eth.h
@@ -108,6 +108,15 @@ struct au1000_private {
struct phy_device *phy_dev;
struct mii_bus *mii_bus;
+ /* PHY configuration */
+ int phy_static_config;
+ int phy_search_highest_addr;
+ int phy1_search_mac0;
+
+ int phy_addr;
+ int phy_busid;
+ int phy_irq;
+
/* These variables are just for quick access to certain regs addresses. */
volatile mac_reg_t *mac; /* mac registers */
volatile u32 *enable; /* address of MAC Enable Register */
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c
index fee6eee7ae5..006cb2efcd2 100644
--- a/drivers/net/benet/be_cmds.c
+++ b/drivers/net/benet/be_cmds.c
@@ -296,6 +296,7 @@ static void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
req_hdr->opcode = opcode;
req_hdr->subsystem = subsystem;
req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
+ req_hdr->version = 0;
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index 8d0be26f94e..bf2072e5420 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -36,6 +36,7 @@
#include <linux/phy_fixed.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/clk.h>
#include <asm/gpio.h>
#include <asm/atomic.h>
@@ -294,9 +295,16 @@ static int cpmac_mdio_write(struct mii_bus *bus, int phy_id,
static int cpmac_mdio_reset(struct mii_bus *bus)
{
+ struct clk *cpmac_clk;
+
+ cpmac_clk = clk_get(&bus->dev, "cpmac");
+ if (IS_ERR(cpmac_clk)) {
+ printk(KERN_ERR "unable to get cpmac clock\n");
+ return -1;
+ }
ar7_device_reset(AR7_RESET_BIT_MDIO);
cpmac_write(bus->priv, CPMAC_MDIO_CONTROL, MDIOC_ENABLE |
- MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1));
+ MDIOC_CLKDIV(clk_get_rate(cpmac_clk) / 2200000 - 1));
return 0;
}
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index d29bb532ecc..765543663a4 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -4006,11 +4006,21 @@ check_page:
}
}
- if (!buffer_info->dma)
+ if (!buffer_info->dma) {
buffer_info->dma = pci_map_page(pdev,
buffer_info->page, 0,
buffer_info->length,
PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+ put_page(buffer_info->page);
+ dev_kfree_skb(skb);
+ buffer_info->page = NULL;
+ buffer_info->skb = NULL;
+ buffer_info->dma = 0;
+ adapter->alloc_rx_buff_failed++;
+ break; /* while !buffer_info->skb */
+ }
+ }
rx_desc = E1000_RX_DESC(*rx_ring, i);
rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
@@ -4101,6 +4111,13 @@ map_skb:
skb->data,
buffer_info->length,
PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+ dev_kfree_skb(skb);
+ buffer_info->skb = NULL;
+ buffer_info->dma = 0;
+ adapter->alloc_rx_buff_failed++;
+ break; /* while !buffer_info->skb */
+ }
/*
* XXX if it was allocated cleanly it will never map to a
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
index 9b2eebdbb25..b5cbd39d068 100644
--- a/drivers/net/irda/au1k_ir.c
+++ b/drivers/net/irda/au1k_ir.c
@@ -36,6 +36,7 @@
#include <asm/pb1000.h>
#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
#include <asm/db1x00.h>
+#include <asm/mach-db1x00/bcsr.h>
#else
#error au1k_ir: unsupported board
#endif
@@ -66,10 +67,6 @@ static char version[] __devinitdata =
#define RUN_AT(x) (jiffies + (x))
-#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
-static BCSR * const bcsr = (BCSR *)0xAE000000;
-#endif
-
static DEFINE_SPINLOCK(ir_lock);
/*
@@ -282,9 +279,8 @@ static int au1k_irda_net_init(struct net_device *dev)
#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
/* power on */
- bcsr->resets &= ~BCSR_RESETS_IRDA_MODE_MASK;
- bcsr->resets |= BCSR_RESETS_IRDA_MODE_FULL;
- au_sync();
+ bcsr_mod(BCSR_RESETS, BCSR_RESETS_IRDA_MODE_MASK,
+ BCSR_RESETS_IRDA_MODE_FULL);
#endif
return 0;
@@ -720,14 +716,14 @@ au1k_irda_set_speed(struct net_device *dev, int speed)
if (speed == 4000000) {
#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
- bcsr->resets |= BCSR_RESETS_FIR_SEL;
+ bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_FIR_SEL);
#else /* Pb1000 and Pb1100 */
writel(1<<13, CPLD_AUX1);
#endif
}
else {
#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
- bcsr->resets &= ~BCSR_RESETS_FIR_SEL;
+ bcsr_mod(BCSR_RESETS, BCSR_RESETS_FIR_SEL, 0);
#else /* Pb1000 and Pb1100 */
writel(readl(CPLD_AUX1) & ~(1<<13), CPLD_AUX1);
#endif
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
index 3103f416531..35a06b47587 100644
--- a/drivers/net/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -357,12 +357,34 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
u32 fctrl_reg;
u32 rmcs_reg;
u32 reg;
+ u32 link_speed = 0;
+ bool link_up;
#ifdef CONFIG_DCB
if (hw->fc.requested_mode == ixgbe_fc_pfc)
goto out;
#endif /* CONFIG_DCB */
+ /*
+ * On 82598 having Rx FC on causes resets while doing 1G
+ * so if it's on turn it off once we know link_speed. For
+ * more details see 82598 Specification update.
+ */
+ hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
+ if (link_up && link_speed == IXGBE_LINK_SPEED_1GB_FULL) {
+ switch (hw->fc.requested_mode) {
+ case ixgbe_fc_full:
+ hw->fc.requested_mode = ixgbe_fc_tx_pause;
+ break;
+ case ixgbe_fc_rx_pause:
+ hw->fc.requested_mode = ixgbe_fc_none;
+ break;
+ default:
+ /* no change */
+ break;
+ }
+ }
+
/* Negotiate the fc mode to use */
ret_val = ixgbe_fc_autoneg(hw);
if (ret_val)
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 7b7c8486c0b..951b73cf5ca 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -5763,6 +5763,10 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
if (err)
goto err_sw_init;
+ /* Make it possible the adapter to be woken up via WOL */
+ if (adapter->hw.mac.type == ixgbe_mac_82599EB)
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_WUS, ~0);
+
/*
* If there is a fan on this device and it has failed log the
* failure.
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
index d9fbad38638..43aea91e336 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/mace.c
@@ -206,7 +206,7 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i
mp->port_aaui = port_aaui;
else {
/* Apple Network Server uses the AAUI port */
- if (machine_is_compatible("AAPL,ShinerESB"))
+ if (of_machine_is_compatible("AAPL,ShinerESB"))
mp->port_aaui = 1;
else {
#ifdef CONFIG_MACE_AAUI_PORT
diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c
index 44f3c2896f2..79408c37787 100644
--- a/drivers/net/macmace.c
+++ b/drivers/net/macmace.c
@@ -39,7 +39,6 @@
#include "mace.h"
static char mac_mace_string[] = "macmace";
-static struct platform_device *mac_mace_device;
#define N_TX_BUFF_ORDER 0
#define N_TX_RING (1 << N_TX_BUFF_ORDER)
@@ -752,6 +751,7 @@ static irqreturn_t mace_dma_intr(int irq, void *dev_id)
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Macintosh MACE ethernet driver");
+MODULE_ALIAS("platform:macmace");
static int __devexit mac_mace_device_remove (struct platform_device *pdev)
{
@@ -777,47 +777,22 @@ static struct platform_driver mac_mace_driver = {
.probe = mace_probe,
.remove = __devexit_p(mac_mace_device_remove),
.driver = {
- .name = mac_mace_string,
+ .name = mac_mace_string,
+ .owner = THIS_MODULE,
},
};
static int __init mac_mace_init_module(void)
{
- int err;
-
if (!MACH_IS_MAC)
return -ENODEV;
- if ((err = platform_driver_register(&mac_mace_driver))) {
- printk(KERN_ERR "Driver registration failed\n");
- return err;
- }
-
- mac_mace_device = platform_device_alloc(mac_mace_string, 0);
- if (!mac_mace_device)
- goto out_unregister;
-
- if (platform_device_add(mac_mace_device)) {
- platform_device_put(mac_mace_device);
- mac_mace_device = NULL;
- }
-
- return 0;
-
-out_unregister:
- platform_driver_unregister(&mac_mace_driver);
-
- return -ENOMEM;
+ return platform_driver_register(&mac_mace_driver);
}
static void __exit mac_mace_cleanup_module(void)
{
platform_driver_unregister(&mac_mace_driver);
-
- if (mac_mace_device) {
- platform_device_unregister(mac_mace_device);
- mac_mace_device = NULL;
- }
}
module_init(mac_mace_init_module);
diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c
index 875d361fb79..24109c28810 100644
--- a/drivers/net/macsonic.c
+++ b/drivers/net/macsonic.c
@@ -62,7 +62,6 @@
#include <asm/mac_via.h>
static char mac_sonic_string[] = "macsonic";
-static struct platform_device *mac_sonic_device;
#include "sonic.h"
@@ -607,6 +606,7 @@ out:
MODULE_DESCRIPTION("Macintosh SONIC ethernet driver");
module_param(sonic_debug, int, 0);
MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)");
+MODULE_ALIAS("platform:macsonic");
#include "sonic.c"
@@ -627,44 +627,19 @@ static struct platform_driver mac_sonic_driver = {
.probe = mac_sonic_probe,
.remove = __devexit_p(mac_sonic_device_remove),
.driver = {
- .name = mac_sonic_string,
+ .name = mac_sonic_string,
+ .owner = THIS_MODULE,
},
};
static int __init mac_sonic_init_module(void)
{
- int err;
-
- if ((err = platform_driver_register(&mac_sonic_driver))) {
- printk(KERN_ERR "Driver registration failed\n");
- return err;
- }
-
- mac_sonic_device = platform_device_alloc(mac_sonic_string, 0);
- if (!mac_sonic_device)
- goto out_unregister;
-
- if (platform_device_add(mac_sonic_device)) {
- platform_device_put(mac_sonic_device);
- mac_sonic_device = NULL;
- }
-
- return 0;
-
-out_unregister:
- platform_driver_unregister(&mac_sonic_driver);
-
- return -ENOMEM;
+ return platform_driver_register(&mac_sonic_driver);
}
static void __exit mac_sonic_cleanup_module(void)
{
platform_driver_unregister(&mac_sonic_driver);
-
- if (mac_sonic_device) {
- platform_device_unregister(mac_sonic_device);
- mac_sonic_device = NULL;
- }
}
module_init(mac_sonic_init_module);
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index 6dd486d2977..aa57cfd1e3f 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -453,8 +453,7 @@ static int mhz_mfc_config(struct pcmcia_device *link)
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
- link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->io.IOAddrLines = 16;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
@@ -652,8 +651,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;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->io.NumPorts1 = 64;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 103e8b0e2a0..46997e177ee 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -2284,6 +2284,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
fail2:
efx_fini_struct(efx);
fail1:
+ WARN_ON(rc > 0);
EFX_LOG(efx, "initialisation failed. rc=%d\n", rc);
free_netdev(net_dev);
return rc;
diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c
index bf0b96af533..5712fddd72f 100644
--- a/drivers/net/sfc/falcon_boards.c
+++ b/drivers/net/sfc/falcon_boards.c
@@ -29,6 +29,15 @@
#define FALCON_BOARD_SFN4111T 0x51
#define FALCON_BOARD_SFN4112F 0x52
+/* Board temperature is about 15°C above ambient when air flow is
+ * limited. */
+#define FALCON_BOARD_TEMP_BIAS 15
+
+/* SFC4000 datasheet says: 'The maximum permitted junction temperature
+ * is 125°C; the thermal design of the environment for the SFC4000
+ * should aim to keep this well below 100°C.' */
+#define FALCON_JUNC_TEMP_MAX 90
+
/*****************************************************************************
* Support for LM87 sensor chip used on several boards
*/
@@ -548,16 +557,16 @@ fail_hwmon:
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 */
+ LM87_IN_LIMITS(0, 0x7c, 0x99), /* 2.5V: 1.8V +/- 10% */
+ LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */
+ LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */
+ LM87_IN_LIMITS(3, 0xac, 0xd4), /* 5V: 5.0V +/- 10% */
+ LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */
+ LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */
+ LM87_AIN_LIMITS(0, 0x98, 0xbb), /* AIN1: 1.66V +/- 10% */
+ LM87_AIN_LIMITS(1, 0x8a, 0xa9), /* AIN2: 1.5V +/- 10% */
+ LM87_TEMP_INT_LIMITS(0, 80 + FALCON_BOARD_TEMP_BIAS),
+ LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX),
0
};
@@ -619,14 +628,14 @@ static int sfe4002_init(struct efx_nic *efx)
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 */
+ LM87_IN_LIMITS(0, 0x7c, 0x99), /* 2.5V: 1.8V +/- 10% */
+ LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */
+ LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */
+ LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */
+ LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */
+ LM87_AIN_LIMITS(1, 0x8a, 0xa9), /* AIN2: 1.5V +/- 10% */
+ LM87_TEMP_INT_LIMITS(0, 60 + FALCON_BOARD_TEMP_BIAS),
+ LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX),
0
};
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c
index 9f035b9f035..f66b3da6ddf 100644
--- a/drivers/net/sfc/mcdi.c
+++ b/drivers/net/sfc/mcdi.c
@@ -127,7 +127,7 @@ static int efx_mcdi_poll(struct efx_nic *efx)
efx_dword_t reg;
/* Check for a reboot atomically with respect to efx_mcdi_copyout() */
- rc = efx_mcdi_poll_reboot(efx);
+ rc = -efx_mcdi_poll_reboot(efx);
if (rc)
goto out;
diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c
index e0d13a45101..67eec7a6e48 100644
--- a/drivers/net/sfc/qt202x_phy.c
+++ b/drivers/net/sfc/qt202x_phy.c
@@ -320,7 +320,7 @@ static int qt202x_reset_phy(struct efx_nic *efx)
falcon_board(efx)->type->init_phy(efx);
- return rc;
+ return 0;
fail:
EFX_ERR(efx, "PHY reset timed out\n");
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index 75a669d48e5..d71c1976072 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -1437,7 +1437,6 @@ static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit)
/* Transmit complete. */
lp->lstats.tx_ints++;
tc35815_txdone(dev);
- netif_wake_queue(dev);
if (ret < 0)
ret = 0;
}
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 4f27f022fbf..5f3b9eaeb04 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -584,6 +584,11 @@ static const struct usb_device_id products [] = {
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long) &mbm_info,
}, {
+ /* Ericsson C3607w ver 2 */
+ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x190b, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+ .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),
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index c93f58f5c6f..317aa34b21c 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -1877,13 +1877,12 @@ static void velocity_error(struct velocity_info *vptr, int status)
/**
* tx_srv - transmit interrupt service
* @vptr; Velocity
- * @status:
*
* Scan the queues looking for transmitted packets that
* we can complete and clean up. Update any statistics as
* necessary/
*/
-static int velocity_tx_srv(struct velocity_info *vptr, u32 status)
+static int velocity_tx_srv(struct velocity_info *vptr)
{
struct tx_desc *td;
int qnum;
@@ -2090,14 +2089,12 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
/**
* velocity_rx_srv - service RX interrupt
* @vptr: velocity
- * @status: adapter status (unused)
*
* Walk the receive ring of the velocity adapter and remove
* 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,
- int budget_left)
+static int velocity_rx_srv(struct velocity_info *vptr, int budget_left)
{
struct net_device_stats *stats = &vptr->dev->stats;
int rd_curr = vptr->rx.curr;
@@ -2151,32 +2148,24 @@ 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);
+ unsigned long flags;
+ spin_lock_irqsave(&vptr->lock, flags);
/*
* 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);
+ rx_done = velocity_rx_srv(vptr, budget / 2);
+ velocity_tx_srv(vptr);
+ rx_done += velocity_rx_srv(vptr, budget - rx_done);
+ velocity_tx_srv(vptr);
/* If budget not fully consumed, exit the polling mode */
if (rx_done < budget) {
napi_complete(napi);
mac_enable_int(vptr->mac_regs);
}
+ spin_unlock_irqrestore(&vptr->lock, flags);
return rx_done;
}
@@ -2206,10 +2195,17 @@ static irqreturn_t velocity_intr(int irq, void *dev_instance)
return IRQ_NONE;
}
+ /* Ack the interrupt */
+ mac_write_isr(vptr->mac_regs, isr_status);
+
if (likely(napi_schedule_prep(&vptr->napi))) {
mac_disable_int(vptr->mac_regs);
__napi_schedule(&vptr->napi);
}
+
+ if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI)))
+ velocity_error(vptr, isr_status);
+
spin_unlock(&vptr->lock);
return IRQ_HANDLED;
@@ -3100,7 +3096,7 @@ static int velocity_resume(struct pci_dev *pdev)
velocity_init_registers(vptr, VELOCITY_INIT_WOL);
mac_disable_int(vptr->mac_regs);
- velocity_tx_srv(vptr, 0);
+ velocity_tx_srv(vptr);
for (i = 0; i < vptr->tx.numq; i++) {
if (vptr->tx.used[i])
@@ -3344,6 +3340,7 @@ static int velocity_set_coalesce(struct net_device *dev,
{
struct velocity_info *vptr = netdev_priv(dev);
int max_us = 0x3f * 64;
+ unsigned long flags;
/* 6 bits of */
if (ecmd->tx_coalesce_usecs > max_us)
@@ -3365,6 +3362,7 @@ static int velocity_set_coalesce(struct net_device *dev,
ecmd->tx_coalesce_usecs);
/* Setup the interrupt suppression and queue timers */
+ spin_lock_irqsave(&vptr->lock, flags);
mac_disable_int(vptr->mac_regs);
setup_adaptive_interrupts(vptr);
setup_queue_timers(vptr);
@@ -3372,6 +3370,7 @@ static int velocity_set_coalesce(struct net_device *dev,
mac_write_int_mask(vptr->int_mask, vptr->mac_regs);
mac_clear_isr(vptr->mac_regs);
mac_enable_int(vptr->mac_regs);
+ spin_unlock_irqrestore(&vptr->lock, flags);
return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index fa12b9060b0..29bf33692f7 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1615,7 +1615,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
bf->bf_frmlen -= padsize;
}
- if (conf_is_ht(&hw->conf) && !is_pae(skb))
+ if (conf_is_ht(&hw->conf))
bf->bf_state.bf_type |= BUF_HT;
bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq);
@@ -1701,7 +1701,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
goto tx_done;
}
- if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && !is_pae(skb)) {
/*
* Try aggregation if it's a unicast data frame
* and the destination is HT capable.
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index fe3bf949199..c484cc25389 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -115,6 +115,7 @@
#define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */
#define B43_MMIO_RNG 0x65A
+#define B43_MMIO_IFSSLOT 0x684 /* Interframe slot time */
#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */
#define B43_MMIO_IFSCTL_USE_EDCF 0x0004
#define B43_MMIO_POWERUP_DELAY 0x6A8
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 4c41cfe44f2..490fb45d1d0 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -628,10 +628,17 @@ static void b43_upload_card_macaddress(struct b43_wldev *dev)
static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time)
{
/* slot_time is in usec. */
- if (dev->phy.type != B43_PHYTYPE_G)
+ /* This test used to exit for all but a G PHY. */
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
return;
- b43_write16(dev, 0x684, 510 + slot_time);
- b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
+ b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time);
+ /* Shared memory location 0x0010 is the slot time and should be
+ * set to slot_time; however, this register is initially 0 and changing
+ * the value adversely affects the transmit rate for BCM4311
+ * devices. Until this behavior is unterstood, delete this step
+ *
+ * b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
+ */
}
static void b43_short_slot_timing_enable(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 9b4b8b5c757..31462813bac 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2008,7 +2008,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
IWL_DEBUG_TX_REPLY(priv, "Retry scheduler reclaim scd_ssn "
"%d index %d\n", scd_ssn , index);
freed = iwl_tx_queue_reclaim(priv, txq_id, index);
- priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
if (priv->mac80211_registered &&
(iwl_queue_space(&txq->q) > txq->q.low_mark) &&
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index de45f308b74..cffaae772d5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -1125,7 +1125,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
scd_ssn , index, txq_id, txq->swq_id);
freed = iwl_tx_queue_reclaim(priv, txq_id, index);
- priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
if (priv->mac80211_registered &&
(iwl_queue_space(&txq->q) > txq->q.low_mark) &&
@@ -1153,16 +1153,14 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
tx_resp->failure_frame);
freed = iwl_tx_queue_reclaim(priv, txq_id, index);
- if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
- priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
if (priv->mac80211_registered &&
(iwl_queue_space(&txq->q) > txq->q.low_mark))
iwl_wake_queue(priv, txq_id);
}
- if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
- iwl_txq_check_empty(priv, sta_id, tid, txq_id);
+ iwl_txq_check_empty(priv, sta_id, tid, txq_id);
if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
IWL_ERR(priv, "TODO: Implement Tx ABORT REQUIRED!!!\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 5461f105bd2..f36f804804f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -2745,6 +2745,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
priv->staging_rxon.flags = 0;
iwl_set_rxon_channel(priv, conf->channel);
+ iwl_set_rxon_ht(priv, ht_conf);
iwl_set_flags_for_band(priv, conf->channel->band);
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 27ca859e745..b69e972671b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -446,6 +446,8 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv);
int iwl_hw_tx_queue_init(struct iwl_priv *priv,
struct iwl_tx_queue *txq);
int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
+void iwl_free_tfds_in_queue(struct iwl_priv *priv,
+ int sta_id, int tid, int freed);
int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
int slots_num, u32 txq_id);
void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id);
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 6f36b6e79f5..2dbce85404a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -928,7 +928,10 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
if (ieee80211_is_mgmt(fc) ||
ieee80211_has_protected(fc) ||
ieee80211_has_morefrags(fc) ||
- le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
+ le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG ||
+ (ieee80211_is_data_qos(fc) &&
+ *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CONTROL_A_MSDU_PRESENT))
ret = skb_linearize(skb);
else
ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 87ce2bd292c..8f407156285 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -120,6 +120,20 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
EXPORT_SYMBOL(iwl_txq_update_write_ptr);
+void iwl_free_tfds_in_queue(struct iwl_priv *priv,
+ int sta_id, int tid, int freed)
+{
+ if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
+ priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ else {
+ IWL_ERR(priv, "free more than tfds_in_queue (%u:%d)\n",
+ priv->stations[sta_id].tid[tid].tfds_in_queue,
+ freed);
+ priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
+ }
+}
+EXPORT_SYMBOL(iwl_free_tfds_in_queue);
+
/**
* iwl_tx_queue_free - Deallocate DMA queue.
* @txq: Transmit queue to deallocate.
@@ -1131,6 +1145,7 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
struct iwl_queue *q = &txq->q;
struct iwl_tx_info *tx_info;
int nfreed = 0;
+ struct ieee80211_hdr *hdr;
if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) {
IWL_ERR(priv, "Read index for DMA queue txq id (%d), index %d, "
@@ -1145,13 +1160,16 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
tx_info = &txq->txb[txq->q.read_ptr];
iwl_tx_status(priv, tx_info->skb[0]);
+
+ hdr = (struct ieee80211_hdr *)tx_info->skb[0]->data;
+ if (hdr && ieee80211_is_data_qos(hdr->frame_control))
+ nfreed++;
tx_info->skb[0] = NULL;
if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)
priv->cfg->ops->lib->txq_inval_byte_cnt_tbl(priv, txq);
priv->cfg->ops->lib->txq_free_tfd(priv, txq);
- nfreed++;
}
return nfreed;
}
@@ -1559,7 +1577,7 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
/* calculate mac80211 ampdu sw queue to wake */
int freed = iwl_tx_queue_reclaim(priv, scd_flow, index);
- priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
priv->mac80211_registered &&
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 6d6ed748517..f727b4a8319 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -794,7 +794,7 @@ static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
}
bss->bss = kzalloc(bss_len, GFP_KERNEL);
- if (!bss) {
+ if (!bss->bss) {
kfree(bss);
IWM_ERR(iwm, "Couldn't allocate bss\n");
return -ENOMEM;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index bc5726dd5fe..7ba3052b070 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -65,6 +65,7 @@ static struct usb_device_id rtl8187_table[] __devinitdata = {
/* Sitecom */
{USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187},
{USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B},
+ {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B},
/* Sphairon Access Systems GmbH */
{USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187},
/* Dick Smith Electronics */
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index d2fa27c5c1b..7cecc8fea9b 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -1,3 +1,11 @@
+config OF_FLATTREE
+ bool
+ depends on OF
+
+config OF_DYNAMIC
+ def_bool y
+ depends on OF && PPC_OF
+
config OF_DEVICE
def_bool y
depends on OF && (SPARC || PPC_OF || MICROBLAZE)
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index bdfb5f5d4b0..f232cc98ce0 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,5 @@
obj-y = base.o
+obj-$(CONFIG_OF_FLATTREE) += fdt.o
obj-$(CONFIG_OF_DEVICE) += device.o platform.o
obj-$(CONFIG_OF_GPIO) += gpio.o
obj-$(CONFIG_OF_I2C) += of_i2c.o
diff --git a/drivers/of/base.c b/drivers/of/base.c
index e6627b2320f..cb96888d142 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -20,8 +20,10 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
struct device_node *allnodes;
+struct device_node *of_chosen;
/* use when traversing tree through the allnext, child, sibling,
* or parent members of struct device_node.
@@ -37,7 +39,7 @@ int of_n_addr_cells(struct device_node *np)
np = np->parent;
ip = of_get_property(np, "#address-cells", NULL);
if (ip)
- return *ip;
+ return be32_to_cpup(ip);
} while (np->parent);
/* No #address-cells property for the root node */
return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
@@ -53,13 +55,88 @@ int of_n_size_cells(struct device_node *np)
np = np->parent;
ip = of_get_property(np, "#size-cells", NULL);
if (ip)
- return *ip;
+ return be32_to_cpup(ip);
} while (np->parent);
/* No #size-cells property for the root node */
return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
}
EXPORT_SYMBOL(of_n_size_cells);
+#if !defined(CONFIG_SPARC) /* SPARC doesn't do ref counting (yet) */
+/**
+ * of_node_get - Increment refcount of a node
+ * @node: Node to inc refcount, NULL is supported to
+ * simplify writing of callers
+ *
+ * Returns node.
+ */
+struct device_node *of_node_get(struct device_node *node)
+{
+ if (node)
+ kref_get(&node->kref);
+ return node;
+}
+EXPORT_SYMBOL(of_node_get);
+
+static inline struct device_node *kref_to_device_node(struct kref *kref)
+{
+ return container_of(kref, struct device_node, kref);
+}
+
+/**
+ * of_node_release - release a dynamically allocated node
+ * @kref: kref element of the node to be released
+ *
+ * In of_node_put() this function is passed to kref_put()
+ * as the destructor.
+ */
+static void of_node_release(struct kref *kref)
+{
+ struct device_node *node = kref_to_device_node(kref);
+ struct property *prop = node->properties;
+
+ /* We should never be releasing nodes that haven't been detached. */
+ if (!of_node_check_flag(node, OF_DETACHED)) {
+ pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
+ dump_stack();
+ kref_init(&node->kref);
+ return;
+ }
+
+ if (!of_node_check_flag(node, OF_DYNAMIC))
+ return;
+
+ while (prop) {
+ struct property *next = prop->next;
+ kfree(prop->name);
+ kfree(prop->value);
+ kfree(prop);
+ prop = next;
+
+ if (!prop) {
+ prop = node->deadprops;
+ node->deadprops = NULL;
+ }
+ }
+ kfree(node->full_name);
+ kfree(node->data);
+ kfree(node);
+}
+
+/**
+ * of_node_put - Decrement refcount of a node
+ * @node: Node to dec refcount, NULL is supported to
+ * simplify writing of callers
+ *
+ */
+void of_node_put(struct device_node *node)
+{
+ if (node)
+ kref_put(&node->kref, of_node_release);
+}
+EXPORT_SYMBOL(of_node_put);
+#endif /* !CONFIG_SPARC */
+
struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
@@ -144,6 +221,27 @@ int of_device_is_compatible(const struct device_node *device,
EXPORT_SYMBOL(of_device_is_compatible);
/**
+ * of_machine_is_compatible - Test root of device tree for a given compatible value
+ * @compat: compatible string to look for in root node's compatible property.
+ *
+ * Returns true if the root node has the given value in its
+ * compatible property.
+ */
+int of_machine_is_compatible(const char *compat)
+{
+ struct device_node *root;
+ int rc = 0;
+
+ root = of_find_node_by_path("/");
+ if (root) {
+ rc = of_device_is_compatible(root, compat);
+ of_node_put(root);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(of_machine_is_compatible);
+
+/**
* of_device_is_available - check if a device is available for use
*
* @device: Node to check for availability
@@ -519,6 +617,27 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
EXPORT_SYMBOL_GPL(of_modalias_node);
/**
+ * of_find_node_by_phandle - Find a node given a phandle
+ * @handle: phandle of the node to find
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_node_by_phandle(phandle handle)
+{
+ struct device_node *np;
+
+ read_lock(&devtree_lock);
+ for (np = allnodes; np; np = np->allnext)
+ if (np->phandle == handle)
+ break;
+ of_node_get(np);
+ read_unlock(&devtree_lock);
+ return np;
+}
+EXPORT_SYMBOL(of_find_node_by_phandle);
+
+/**
* of_parse_phandle - Resolve a phandle property to a device_node pointer
* @np: Pointer to device node holding phandle property
* @phandle_name: Name of property holding a phandle value
@@ -578,8 +697,8 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
const void **out_args)
{
int ret = -EINVAL;
- const u32 *list;
- const u32 *list_end;
+ const __be32 *list;
+ const __be32 *list_end;
int size;
int cur_index = 0;
struct device_node *node = NULL;
@@ -593,7 +712,7 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
list_end = list + size / sizeof(*list);
while (list < list_end) {
- const u32 *cells;
+ const __be32 *cells;
const phandle *phandle;
phandle = list++;
@@ -617,7 +736,7 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
goto err1;
}
- list += *cells;
+ list += be32_to_cpup(cells);
if (list > list_end) {
pr_debug("%s: insufficient arguments length\n",
np->full_name);
@@ -658,3 +777,190 @@ err0:
return ret;
}
EXPORT_SYMBOL(of_parse_phandles_with_args);
+
+/**
+ * prom_add_property - Add a property to a node
+ */
+int prom_add_property(struct device_node *np, struct property *prop)
+{
+ struct property **next;
+ unsigned long flags;
+
+ prop->next = NULL;
+ write_lock_irqsave(&devtree_lock, flags);
+ next = &np->properties;
+ while (*next) {
+ if (strcmp(prop->name, (*next)->name) == 0) {
+ /* duplicate ! don't insert it */
+ write_unlock_irqrestore(&devtree_lock, flags);
+ return -1;
+ }
+ next = &(*next)->next;
+ }
+ *next = prop;
+ write_unlock_irqrestore(&devtree_lock, flags);
+
+#ifdef CONFIG_PROC_DEVICETREE
+ /* try to add to proc as well if it was initialized */
+ if (np->pde)
+ proc_device_tree_add_prop(np->pde, prop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+ return 0;
+}
+
+/**
+ * prom_remove_property - Remove a property from a node.
+ *
+ * Note that we don't actually remove it, since we have given out
+ * who-knows-how-many pointers to the data using get-property.
+ * Instead we just move the property to the "dead properties"
+ * list, so it won't be found any more.
+ */
+int prom_remove_property(struct device_node *np, struct property *prop)
+{
+ struct property **next;
+ unsigned long flags;
+ int found = 0;
+
+ write_lock_irqsave(&devtree_lock, flags);
+ next = &np->properties;
+ while (*next) {
+ if (*next == prop) {
+ /* found the node */
+ *next = prop->next;
+ prop->next = np->deadprops;
+ np->deadprops = prop;
+ found = 1;
+ break;
+ }
+ next = &(*next)->next;
+ }
+ write_unlock_irqrestore(&devtree_lock, flags);
+
+ if (!found)
+ return -ENODEV;
+
+#ifdef CONFIG_PROC_DEVICETREE
+ /* try to remove the proc node as well */
+ if (np->pde)
+ proc_device_tree_remove_prop(np->pde, prop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+ return 0;
+}
+
+/*
+ * prom_update_property - Update a property in a node.
+ *
+ * Note that we don't actually remove it, since we have given out
+ * who-knows-how-many pointers to the data using get-property.
+ * Instead we just move the property to the "dead properties" list,
+ * and add the new property to the property list
+ */
+int prom_update_property(struct device_node *np,
+ struct property *newprop,
+ struct property *oldprop)
+{
+ struct property **next;
+ unsigned long flags;
+ int found = 0;
+
+ write_lock_irqsave(&devtree_lock, flags);
+ next = &np->properties;
+ while (*next) {
+ if (*next == oldprop) {
+ /* found the node */
+ newprop->next = oldprop->next;
+ *next = newprop;
+ oldprop->next = np->deadprops;
+ np->deadprops = oldprop;
+ found = 1;
+ break;
+ }
+ next = &(*next)->next;
+ }
+ write_unlock_irqrestore(&devtree_lock, flags);
+
+ if (!found)
+ return -ENODEV;
+
+#ifdef CONFIG_PROC_DEVICETREE
+ /* try to add to proc as well if it was initialized */
+ if (np->pde)
+ proc_device_tree_update_prop(np->pde, newprop, oldprop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+ return 0;
+}
+
+#if defined(CONFIG_OF_DYNAMIC)
+/*
+ * Support for dynamic device trees.
+ *
+ * On some platforms, the device tree can be manipulated at runtime.
+ * The routines in this section support adding, removing and changing
+ * device tree nodes.
+ */
+
+/**
+ * of_attach_node - Plug a device node into the tree and global list.
+ */
+void of_attach_node(struct device_node *np)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&devtree_lock, flags);
+ np->sibling = np->parent->child;
+ np->allnext = allnodes;
+ np->parent->child = np;
+ allnodes = np;
+ write_unlock_irqrestore(&devtree_lock, flags);
+}
+
+/**
+ * of_detach_node - "Unplug" a node from the device tree.
+ *
+ * The caller must hold a reference to the node. The memory associated with
+ * the node is not freed until its refcount goes to zero.
+ */
+void of_detach_node(struct device_node *np)
+{
+ struct device_node *parent;
+ unsigned long flags;
+
+ write_lock_irqsave(&devtree_lock, flags);
+
+ parent = np->parent;
+ if (!parent)
+ goto out_unlock;
+
+ if (allnodes == np)
+ allnodes = np->allnext;
+ else {
+ struct device_node *prev;
+ for (prev = allnodes;
+ prev->allnext != np;
+ prev = prev->allnext)
+ ;
+ prev->allnext = np->allnext;
+ }
+
+ if (parent->child == np)
+ parent->child = np->sibling;
+ else {
+ struct device_node *prevsib;
+ for (prevsib = np->parent->child;
+ prevsib->sibling != np;
+ prevsib = prevsib->sibling)
+ ;
+ prevsib->sibling = np->sibling;
+ }
+
+ of_node_set_flag(np, OF_DETACHED);
+
+out_unlock:
+ write_unlock_irqrestore(&devtree_lock, flags);
+}
+#endif /* defined(CONFIG_OF_DYNAMIC) */
+
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
new file mode 100644
index 00000000000..406757a9d7e
--- /dev/null
+++ b/drivers/of/fdt.c
@@ -0,0 +1,590 @@
+/*
+ * Functions for working with the Flattened Device Tree data format
+ *
+ * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
+ * benh@kernel.crashing.org
+ *
+ * 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/initrd.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#ifdef CONFIG_PPC
+#include <asm/machdep.h>
+#endif /* CONFIG_PPC */
+
+#include <asm/page.h>
+
+int __initdata dt_root_addr_cells;
+int __initdata dt_root_size_cells;
+
+struct boot_param_header *initial_boot_params;
+
+char *find_flat_dt_string(u32 offset)
+{
+ return ((char *)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_strings) + offset;
+}
+
+/**
+ * of_scan_flat_dt - scan flattened tree blob and call callback on each.
+ * @it: callback function
+ * @data: context data pointer
+ *
+ * This function is used to scan the flattened device-tree, it is
+ * used to extract the memory information at boot before we can
+ * unflatten the tree
+ */
+int __init of_scan_flat_dt(int (*it)(unsigned long node,
+ const char *uname, int depth,
+ void *data),
+ void *data)
+{
+ unsigned long p = ((unsigned long)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_struct);
+ int rc = 0;
+ int depth = -1;
+
+ do {
+ u32 tag = be32_to_cpup((__be32 *)p);
+ char *pathp;
+
+ p += 4;
+ if (tag == OF_DT_END_NODE) {
+ depth--;
+ continue;
+ }
+ if (tag == OF_DT_NOP)
+ continue;
+ if (tag == OF_DT_END)
+ break;
+ if (tag == OF_DT_PROP) {
+ u32 sz = be32_to_cpup((__be32 *)p);
+ p += 8;
+ if (be32_to_cpu(initial_boot_params->version) < 0x10)
+ p = _ALIGN(p, sz >= 8 ? 8 : 4);
+ p += sz;
+ p = _ALIGN(p, 4);
+ continue;
+ }
+ if (tag != OF_DT_BEGIN_NODE) {
+ pr_err("Invalid tag %x in flat device tree!\n", tag);
+ return -EINVAL;
+ }
+ depth++;
+ pathp = (char *)p;
+ p = _ALIGN(p + strlen(pathp) + 1, 4);
+ if ((*pathp) == '/') {
+ char *lp, *np;
+ for (lp = NULL, np = pathp; *np; np++)
+ if ((*np) == '/')
+ lp = np+1;
+ if (lp != NULL)
+ pathp = lp;
+ }
+ rc = it(p, pathp, depth, data);
+ if (rc != 0)
+ break;
+ } while (1);
+
+ return rc;
+}
+
+/**
+ * of_get_flat_dt_root - find the root node in the flat blob
+ */
+unsigned long __init of_get_flat_dt_root(void)
+{
+ unsigned long p = ((unsigned long)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_struct);
+
+ while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
+ p += 4;
+ BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
+ p += 4;
+ return _ALIGN(p + strlen((char *)p) + 1, 4);
+}
+
+/**
+ * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
+ *
+ * This function can be used within scan_flattened_dt callback to get
+ * access to properties
+ */
+void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
+ unsigned long *size)
+{
+ unsigned long p = node;
+
+ do {
+ u32 tag = be32_to_cpup((__be32 *)p);
+ u32 sz, noff;
+ const char *nstr;
+
+ p += 4;
+ if (tag == OF_DT_NOP)
+ continue;
+ if (tag != OF_DT_PROP)
+ return NULL;
+
+ sz = be32_to_cpup((__be32 *)p);
+ noff = be32_to_cpup((__be32 *)(p + 4));
+ p += 8;
+ if (be32_to_cpu(initial_boot_params->version) < 0x10)
+ p = _ALIGN(p, sz >= 8 ? 8 : 4);
+
+ nstr = find_flat_dt_string(noff);
+ if (nstr == NULL) {
+ pr_warning("Can't find property index name !\n");
+ return NULL;
+ }
+ if (strcmp(name, nstr) == 0) {
+ if (size)
+ *size = sz;
+ return (void *)p;
+ }
+ p += sz;
+ p = _ALIGN(p, 4);
+ } while (1);
+}
+
+/**
+ * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
+ * @node: node to test
+ * @compat: compatible string to compare with compatible list.
+ */
+int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
+{
+ const char *cp;
+ unsigned long cplen, l;
+
+ cp = of_get_flat_dt_prop(node, "compatible", &cplen);
+ if (cp == NULL)
+ return 0;
+ while (cplen > 0) {
+ if (strncasecmp(cp, compat, strlen(compat)) == 0)
+ return 1;
+ l = strlen(cp) + 1;
+ cp += l;
+ cplen -= l;
+ }
+
+ return 0;
+}
+
+static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
+ unsigned long align)
+{
+ void *res;
+
+ *mem = _ALIGN(*mem, align);
+ res = (void *)*mem;
+ *mem += size;
+
+ return res;
+}
+
+/**
+ * unflatten_dt_node - Alloc and populate a device_node from the flat tree
+ * @p: pointer to node in flat tree
+ * @dad: Parent struct device_node
+ * @allnextpp: pointer to ->allnext from last allocated device_node
+ * @fpsize: Size of the node path up at the current depth.
+ */
+unsigned long __init unflatten_dt_node(unsigned long mem,
+ unsigned long *p,
+ struct device_node *dad,
+ struct device_node ***allnextpp,
+ unsigned long fpsize)
+{
+ struct device_node *np;
+ struct property *pp, **prev_pp = NULL;
+ char *pathp;
+ u32 tag;
+ unsigned int l, allocl;
+ int has_name = 0;
+ int new_format = 0;
+
+ tag = be32_to_cpup((__be32 *)(*p));
+ if (tag != OF_DT_BEGIN_NODE) {
+ pr_err("Weird tag at start of node: %x\n", tag);
+ return mem;
+ }
+ *p += 4;
+ pathp = (char *)*p;
+ l = allocl = strlen(pathp) + 1;
+ *p = _ALIGN(*p + l, 4);
+
+ /* version 0x10 has a more compact unit name here instead of the full
+ * path. we accumulate the full path size using "fpsize", we'll rebuild
+ * it later. We detect this because the first character of the name is
+ * not '/'.
+ */
+ if ((*pathp) != '/') {
+ new_format = 1;
+ if (fpsize == 0) {
+ /* root node: special case. fpsize accounts for path
+ * plus terminating zero. root node only has '/', so
+ * fpsize should be 2, but we want to avoid the first
+ * level nodes to have two '/' so we use fpsize 1 here
+ */
+ fpsize = 1;
+ allocl = 2;
+ } else {
+ /* account for '/' and path size minus terminal 0
+ * already in 'l'
+ */
+ fpsize += l;
+ allocl = fpsize;
+ }
+ }
+
+ np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
+ __alignof__(struct device_node));
+ if (allnextpp) {
+ memset(np, 0, sizeof(*np));
+ np->full_name = ((char *)np) + sizeof(struct device_node);
+ if (new_format) {
+ char *fn = np->full_name;
+ /* rebuild full path for new format */
+ if (dad && dad->parent) {
+ strcpy(fn, dad->full_name);
+#ifdef DEBUG
+ if ((strlen(fn) + l + 1) != allocl) {
+ pr_debug("%s: p: %d, l: %d, a: %d\n",
+ pathp, (int)strlen(fn),
+ l, allocl);
+ }
+#endif
+ fn += strlen(fn);
+ }
+ *(fn++) = '/';
+ memcpy(fn, pathp, l);
+ } else
+ memcpy(np->full_name, pathp, l);
+ prev_pp = &np->properties;
+ **allnextpp = np;
+ *allnextpp = &np->allnext;
+ if (dad != NULL) {
+ np->parent = dad;
+ /* we temporarily use the next field as `last_child'*/
+ if (dad->next == NULL)
+ dad->child = np;
+ else
+ dad->next->sibling = np;
+ dad->next = np;
+ }
+ kref_init(&np->kref);
+ }
+ while (1) {
+ u32 sz, noff;
+ char *pname;
+
+ tag = be32_to_cpup((__be32 *)(*p));
+ if (tag == OF_DT_NOP) {
+ *p += 4;
+ continue;
+ }
+ if (tag != OF_DT_PROP)
+ break;
+ *p += 4;
+ sz = be32_to_cpup((__be32 *)(*p));
+ noff = be32_to_cpup((__be32 *)((*p) + 4));
+ *p += 8;
+ if (be32_to_cpu(initial_boot_params->version) < 0x10)
+ *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
+
+ pname = find_flat_dt_string(noff);
+ if (pname == NULL) {
+ pr_info("Can't find property name in list !\n");
+ break;
+ }
+ if (strcmp(pname, "name") == 0)
+ has_name = 1;
+ l = strlen(pname) + 1;
+ pp = unflatten_dt_alloc(&mem, sizeof(struct property),
+ __alignof__(struct property));
+ if (allnextpp) {
+ /* We accept flattened tree phandles either in
+ * ePAPR-style "phandle" properties, or the
+ * legacy "linux,phandle" properties. If both
+ * appear and have different values, things
+ * will get weird. Don't do that. */
+ if ((strcmp(pname, "phandle") == 0) ||
+ (strcmp(pname, "linux,phandle") == 0)) {
+ if (np->phandle == 0)
+ np->phandle = *((u32 *)*p);
+ }
+ /* And we process the "ibm,phandle" property
+ * used in pSeries dynamic device tree
+ * stuff */
+ if (strcmp(pname, "ibm,phandle") == 0)
+ np->phandle = *((u32 *)*p);
+ pp->name = pname;
+ pp->length = sz;
+ pp->value = (void *)*p;
+ *prev_pp = pp;
+ prev_pp = &pp->next;
+ }
+ *p = _ALIGN((*p) + sz, 4);
+ }
+ /* with version 0x10 we may not have the name property, recreate
+ * it here from the unit name if absent
+ */
+ if (!has_name) {
+ char *p1 = pathp, *ps = pathp, *pa = NULL;
+ int sz;
+
+ while (*p1) {
+ if ((*p1) == '@')
+ pa = p1;
+ if ((*p1) == '/')
+ ps = p1 + 1;
+ p1++;
+ }
+ if (pa < ps)
+ pa = p1;
+ sz = (pa - ps) + 1;
+ pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
+ __alignof__(struct property));
+ if (allnextpp) {
+ pp->name = "name";
+ pp->length = sz;
+ pp->value = pp + 1;
+ *prev_pp = pp;
+ prev_pp = &pp->next;
+ memcpy(pp->value, ps, sz - 1);
+ ((char *)pp->value)[sz - 1] = 0;
+ pr_debug("fixed up name for %s -> %s\n", pathp,
+ (char *)pp->value);
+ }
+ }
+ if (allnextpp) {
+ *prev_pp = NULL;
+ np->name = of_get_property(np, "name", NULL);
+ np->type = of_get_property(np, "device_type", NULL);
+
+ if (!np->name)
+ np->name = "<NULL>";
+ if (!np->type)
+ np->type = "<NULL>";
+ }
+ while (tag == OF_DT_BEGIN_NODE) {
+ mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
+ tag = be32_to_cpup((__be32 *)(*p));
+ }
+ if (tag != OF_DT_END_NODE) {
+ pr_err("Weird tag at end of node: %x\n", tag);
+ return mem;
+ }
+ *p += 4;
+ return mem;
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+/**
+ * early_init_dt_check_for_initrd - Decode initrd location from flat tree
+ * @node: reference to node containing initrd location ('chosen')
+ */
+void __init early_init_dt_check_for_initrd(unsigned long node)
+{
+ unsigned long start, end, len;
+ __be32 *prop;
+
+ pr_debug("Looking for initrd properties... ");
+
+ prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
+ if (!prop)
+ return;
+ start = of_read_ulong(prop, len/4);
+
+ prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
+ if (!prop)
+ return;
+ end = of_read_ulong(prop, len/4);
+
+ early_init_dt_setup_initrd_arch(start, end);
+ pr_debug("initrd_start=0x%lx initrd_end=0x%lx\n", start, end);
+}
+#else
+inline void early_init_dt_check_for_initrd(unsigned long node)
+{
+}
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+/**
+ * early_init_dt_scan_root - fetch the top level address and size cells
+ */
+int __init early_init_dt_scan_root(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ __be32 *prop;
+
+ if (depth != 0)
+ return 0;
+
+ dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
+ dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
+
+ prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
+ if (prop)
+ dt_root_size_cells = be32_to_cpup(prop);
+ pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);
+
+ prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
+ if (prop)
+ dt_root_addr_cells = be32_to_cpup(prop);
+ pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);
+
+ /* break now */
+ return 1;
+}
+
+u64 __init dt_mem_next_cell(int s, __be32 **cellp)
+{
+ __be32 *p = *cellp;
+
+ *cellp = p + s;
+ return of_read_number(p, s);
+}
+
+/**
+ * early_init_dt_scan_memory - Look for an parse memory nodes
+ */
+int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+ __be32 *reg, *endp;
+ unsigned long l;
+
+ /* We are scanning "memory" nodes only */
+ if (type == NULL) {
+ /*
+ * The longtrail doesn't have a device_type on the
+ * /memory node, so look for the node called /memory@0.
+ */
+ if (depth != 1 || strcmp(uname, "memory@0") != 0)
+ return 0;
+ } else if (strcmp(type, "memory") != 0)
+ return 0;
+
+ reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
+ if (reg == NULL)
+ reg = of_get_flat_dt_prop(node, "reg", &l);
+ if (reg == NULL)
+ return 0;
+
+ endp = reg + (l / sizeof(__be32));
+
+ pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
+ uname, l, reg[0], reg[1], reg[2], reg[3]);
+
+ while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+ u64 base, size;
+
+ base = dt_mem_next_cell(dt_root_addr_cells, &reg);
+ size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+ if (size == 0)
+ continue;
+ pr_debug(" - %llx , %llx\n", (unsigned long long)base,
+ (unsigned long long)size);
+
+ early_init_dt_add_memory_arch(base, size);
+ }
+
+ return 0;
+}
+
+int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ unsigned long l;
+ char *p;
+
+ pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
+
+ if (depth != 1 ||
+ (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
+ return 0;
+
+ early_init_dt_check_for_initrd(node);
+
+ /* Retreive command line */
+ p = of_get_flat_dt_prop(node, "bootargs", &l);
+ if (p != NULL && l > 0)
+ strlcpy(cmd_line, p, min((int)l, COMMAND_LINE_SIZE));
+
+#ifdef CONFIG_CMDLINE
+#ifndef CONFIG_CMDLINE_FORCE
+ if (p == NULL || l == 0 || (l == 1 && (*p) == 0))
+#endif
+ strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+#endif /* CONFIG_CMDLINE */
+
+ early_init_dt_scan_chosen_arch(node);
+
+ pr_debug("Command line is: %s\n", cmd_line);
+
+ /* break now */
+ return 1;
+}
+
+/**
+ * unflatten_device_tree - create tree of device_nodes from flat blob
+ *
+ * unflattens the device-tree passed by the firmware, creating the
+ * tree of struct device_node. It also fills the "name" and "type"
+ * pointers of the nodes so the normal device-tree walking functions
+ * can be used.
+ */
+void __init unflatten_device_tree(void)
+{
+ unsigned long start, mem, size;
+ struct device_node **allnextp = &allnodes;
+
+ pr_debug(" -> unflatten_device_tree()\n");
+
+ /* First pass, scan for size */
+ start = ((unsigned long)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_struct);
+ size = unflatten_dt_node(0, &start, NULL, NULL, 0);
+ size = (size | 3) + 1;
+
+ pr_debug(" size is %lx, allocating...\n", size);
+
+ /* Allocate memory for the expanded device tree */
+ mem = early_init_dt_alloc_memory_arch(size + 4,
+ __alignof__(struct device_node));
+ mem = (unsigned long) __va(mem);
+
+ ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
+
+ pr_debug(" unflattening %lx...\n", mem);
+
+ /* Second pass, do actual unflattening */
+ start = ((unsigned long)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_struct);
+ unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
+ if (be32_to_cpup((__be32 *)start) != OF_DT_END)
+ pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
+ if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
+ pr_warning("End of tree marker overwritten: %08x\n",
+ be32_to_cpu(((__be32 *)mem)[size / 4]));
+ *allnextp = NULL;
+
+ /* Get pointer to OF "/chosen" node for use everywhere */
+ of_chosen = of_find_node_by_path("/chosen");
+ if (of_chosen == NULL)
+ of_chosen = of_find_node_by_path("/chosen@0");
+
+ pr_debug(" <- unflatten_device_tree()\n");
+}
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 6eea601a920..24c3606217f 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -36,7 +36,7 @@ int of_get_gpio_flags(struct device_node *np, int index,
struct of_gpio_chip *of_gc = NULL;
int size;
const void *gpio_spec;
- const u32 *gpio_cells;
+ const __be32 *gpio_cells;
ret = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", index,
&gc, &gpio_spec);
@@ -55,7 +55,7 @@ int of_get_gpio_flags(struct device_node *np, int index,
gpio_cells = of_get_property(gc, "#gpio-cells", &size);
if (!gpio_cells || size != sizeof(*gpio_cells) ||
- *gpio_cells != of_gc->gpio_cells) {
+ be32_to_cpup(gpio_cells) != of_gc->gpio_cells) {
pr_debug("%s: wrong #gpio-cells for %s\n",
np->full_name, gc->full_name);
ret = -EINVAL;
@@ -127,7 +127,8 @@ EXPORT_SYMBOL(of_gpio_count);
int of_gpio_simple_xlate(struct of_gpio_chip *of_gc, struct device_node *np,
const void *gpio_spec, enum of_gpio_flags *flags)
{
- const u32 *gpio = gpio_spec;
+ const __be32 *gpio = gpio_spec;
+ const u32 n = be32_to_cpup(gpio);
/*
* We're discouraging gpio_cells < 2, since that way you'll have to
@@ -140,13 +141,13 @@ int of_gpio_simple_xlate(struct of_gpio_chip *of_gc, struct device_node *np,
return -EINVAL;
}
- if (*gpio > of_gc->gc.ngpio)
+ if (n > of_gc->gc.ngpio)
return -EINVAL;
if (flags)
- *flags = gpio[1];
+ *flags = be32_to_cpu(gpio[1]);
- return *gpio;
+ return n;
}
EXPORT_SYMBOL(of_gpio_simple_xlate);
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index fa65a2b2ae2..a3a708e590d 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -25,7 +25,7 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
for_each_child_of_node(adap_node, node) {
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
- const u32 *addr;
+ const __be32 *addr;
int len;
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)
@@ -40,7 +40,7 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
info.irq = irq_of_parse_and_map(node, 0);
- info.addr = *addr;
+ info.addr = be32_to_cpup(addr);
dev_archdata_set_node(&dev_ad, node);
info.archdata = &dev_ad;
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 4b22ba568b1..18ecae4a437 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -51,7 +51,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
/* Loop over the child nodes and register a phy_device for each one */
for_each_child_of_node(np, child) {
- const u32 *addr;
+ const __be32 *addr;
int len;
/* A PHY must have a reg property in the range [0-31] */
@@ -68,7 +68,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
mdio->irq[*addr] = PHY_POLL;
}
- phy = get_phy_device(mdio, *addr);
+ phy = get_phy_device(mdio, be32_to_cpup(addr));
if (!phy) {
dev_err(&mdio->dev, "error probing PHY at address %i\n",
*addr);
@@ -160,7 +160,7 @@ struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
struct device_node *net_np;
char bus_id[MII_BUS_ID_SIZE + 3];
struct phy_device *phy;
- const u32 *phy_id;
+ const __be32 *phy_id;
int sz;
if (!dev->dev.parent)
@@ -174,7 +174,7 @@ struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
if (!phy_id || sz < sizeof(*phy_id))
return NULL;
- sprintf(bus_id, PHY_ID_FMT, "0", phy_id[0]);
+ sprintf(bus_id, PHY_ID_FMT, "0", be32_to_cpu(phy_id[0]));
phy = phy_connect(dev, bus_id, hndlr, 0, iface);
return IS_ERR(phy) ? NULL : phy;
diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c
index bed0ed6dcdc..f65f48b9844 100644
--- a/drivers/of/of_spi.c
+++ b/drivers/of/of_spi.c
@@ -23,7 +23,7 @@ void of_register_spi_devices(struct spi_master *master, struct device_node *np)
{
struct spi_device *spi;
struct device_node *nc;
- const u32 *prop;
+ const __be32 *prop;
int rc;
int len;
@@ -54,7 +54,7 @@ void of_register_spi_devices(struct spi_master *master, struct device_node *np)
spi_dev_put(spi);
continue;
}
- spi->chip_select = *prop;
+ spi->chip_select = be32_to_cpup(prop);
/* Mode (clock phase/polarity/etc.) */
if (of_find_property(nc, "spi-cpha", NULL))
@@ -72,7 +72,7 @@ void of_register_spi_devices(struct spi_master *master, struct device_node *np)
spi_dev_put(spi);
continue;
}
- spi->max_speed_hz = *prop;
+ spi->max_speed_hz = be32_to_cpup(prop);
/* IRQ */
spi->irq = irq_of_parse_and_map(nc, 0);
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index b1ecefa2a23..7858a117e80 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -21,17 +21,6 @@ config PCI_MSI
If you don't know what to do here, say N.
-config PCI_LEGACY
- bool "Enable deprecated pci_find_* API"
- depends on PCI
- default y
- help
- Say Y here if you want to include support for the deprecated
- pci_find_device() API. Most drivers have been converted over
- to using the proper hotplug APIs, so this option serves to
- include/exclude only a few drivers that are still using this
- API.
-
config PCI_DEBUG
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 4df48d58eaa..8674c1ebe97 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,14 +2,13 @@
# Makefile for the PCI bus specific drivers.
#
-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y += access.o bus.o probe.o remove.o pci.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
irq.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSFS) += slot.o
-obj-$(CONFIG_PCI_LEGACY) += legacy.o
-CFLAGS_legacy.o += -Wno-deprecated-declarations
+obj-$(CONFIG_PCI_QUIRKS) += quirks.o
# Build PCI Express stuff if needed
obj-$(CONFIG_PCIEPORTBUS) += pcie/
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index cef28a79103..712250f5874 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -17,6 +17,52 @@
#include "pci.h"
+void pci_bus_add_resource(struct pci_bus *bus, struct resource *res,
+ unsigned int flags)
+{
+ struct pci_bus_resource *bus_res;
+
+ bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
+ if (!bus_res) {
+ dev_err(&bus->dev, "can't add %pR resource\n", res);
+ return;
+ }
+
+ bus_res->res = res;
+ bus_res->flags = flags;
+ list_add_tail(&bus_res->list, &bus->resources);
+}
+
+struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n)
+{
+ struct pci_bus_resource *bus_res;
+
+ if (n < PCI_BRIDGE_RESOURCE_NUM)
+ return bus->resource[n];
+
+ n -= PCI_BRIDGE_RESOURCE_NUM;
+ list_for_each_entry(bus_res, &bus->resources, list) {
+ if (n-- == 0)
+ return bus_res->res;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(pci_bus_resource_n);
+
+void pci_bus_remove_resources(struct pci_bus *bus)
+{
+ struct pci_bus_resource *bus_res, *tmp;
+ int i;
+
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+ bus->resource[i] = 0;
+
+ list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
+ list_del(&bus_res->list);
+ kfree(bus_res);
+ }
+}
+
/**
* pci_bus_alloc_resource - allocate a resource from a parent bus
* @bus: PCI bus
@@ -36,11 +82,14 @@ int
pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
resource_size_t size, resource_size_t align,
resource_size_t min, unsigned int type_mask,
- void (*alignf)(void *, struct resource *, resource_size_t,
- resource_size_t),
+ resource_size_t (*alignf)(void *,
+ const struct resource *,
+ resource_size_t,
+ resource_size_t),
void *alignf_data)
{
int i, ret = -ENOMEM;
+ struct resource *r;
resource_size_t max = -1;
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
@@ -49,8 +98,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
if (!(res->flags & IORESOURCE_MEM_64))
max = PCIBIOS_MAX_MEM_32;
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *r = bus->resource[i];
+ pci_bus_for_each_resource(bus, r, i) {
if (!r)
continue;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index 4dd7114964a..efa9f2de51c 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -332,8 +332,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info->attention_status = 0;
slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
- slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
- slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
acpiphp_slot->slot = slot;
snprintf(name, SLOT_NAME_SIZE, "%llu", slot->acpi_slot->sun);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 8e952fdab76..cb2fd01edda 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -720,12 +720,6 @@ static int acpiphp_bus_add(struct acpiphp_func *func)
-ret_val);
goto acpiphp_bus_add_out;
}
- /*
- * try to start anyway. We could have failed to add
- * simply because this bus had previously been added
- * on another add. Don't bother with the return value
- * we just keep going.
- */
ret_val = acpi_bus_start(device);
acpiphp_bus_add_out:
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
index 148fb463b81..fb3f84661bd 100644
--- a/drivers/pci/hotplug/cpcihp_generic.c
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -162,6 +162,7 @@ static int __init cpcihp_generic_init(void)
dev = pci_get_slot(bus, PCI_DEVFN(bridge_slot, 0));
if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
err("Invalid bridge device %s", bridge);
+ pci_dev_put(dev);
return -EINVAL;
}
bus = dev->subordinate;
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h
index 9c6a9fd2681..d8ffc736680 100644
--- a/drivers/pci/hotplug/cpqphp.h
+++ b/drivers/pci/hotplug/cpqphp.h
@@ -310,8 +310,6 @@ struct controller {
u8 first_slot;
u8 add_support;
u8 push_flag;
- enum pci_bus_speed speed;
- enum pci_bus_speed speed_capability;
u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */
u8 slot_switch_type; /* 0 = no switch, 1 = switch present */
u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 075b4f4b6e0..f184d1d2ecb 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -583,30 +583,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
-static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- struct slot *slot = hotplug_slot->private;
- struct controller *ctrl = slot->ctrl;
-
- dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
-
- *value = ctrl->speed_capability;
-
- return 0;
-}
-
-static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- struct slot *slot = hotplug_slot->private;
- struct controller *ctrl = slot->ctrl;
-
- dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
-
- *value = ctrl->speed;
-
- return 0;
-}
-
static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
.set_attention_status = set_attention_status,
.enable_slot = process_SI,
@@ -616,8 +592,6 @@ static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_max_bus_speed = get_max_bus_speed,
- .get_cur_bus_speed = get_cur_bus_speed,
};
#define SLOT_NAME_SIZE 10
@@ -629,6 +603,7 @@ static int ctrl_slot_setup(struct controller *ctrl,
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *hotplug_slot_info;
+ struct pci_bus *bus = ctrl->pci_bus;
u8 number_of_slots;
u8 slot_device;
u8 slot_number;
@@ -694,7 +669,7 @@ static int ctrl_slot_setup(struct controller *ctrl,
slot->capabilities |= PCISLOT_64_BIT_SUPPORTED;
if (is_slot66mhz(slot))
slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED;
- if (ctrl->speed == PCI_SPEED_66MHz)
+ if (bus->cur_bus_speed == PCI_SPEED_66MHz)
slot->capabilities |= PCISLOT_66_MHZ_OPERATION;
ctrl_slot =
@@ -844,6 +819,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
u32 rc;
struct controller *ctrl;
struct pci_func *func;
+ struct pci_bus *bus;
int err;
err = pci_enable_device(pdev);
@@ -852,6 +828,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_name(pdev), err);
return err;
}
+ bus = pdev->subordinate;
/* Need to read VID early b/c it's used to differentiate CPQ and INTC
* discovery
@@ -929,22 +906,22 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_read_config_byte(pdev, 0x41, &bus_cap);
if (bus_cap & 0x80) {
dbg("bus max supports 133MHz PCI-X\n");
- ctrl->speed_capability = PCI_SPEED_133MHz_PCIX;
+ bus->max_bus_speed = PCI_SPEED_133MHz_PCIX;
break;
}
if (bus_cap & 0x40) {
dbg("bus max supports 100MHz PCI-X\n");
- ctrl->speed_capability = PCI_SPEED_100MHz_PCIX;
+ bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
break;
}
if (bus_cap & 20) {
dbg("bus max supports 66MHz PCI-X\n");
- ctrl->speed_capability = PCI_SPEED_66MHz_PCIX;
+ bus->max_bus_speed = PCI_SPEED_66MHz_PCIX;
break;
}
if (bus_cap & 10) {
dbg("bus max supports 66MHz PCI\n");
- ctrl->speed_capability = PCI_SPEED_66MHz;
+ bus->max_bus_speed = PCI_SPEED_66MHz;
break;
}
@@ -955,7 +932,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
case PCI_SUB_HPC_ID:
/* Original 6500/7000 implementation */
ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_33MHz;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
ctrl->push_button = 0;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
@@ -966,7 +943,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* First Pushbutton implementation */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_33MHz;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
@@ -976,7 +953,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
case PCI_SUB_HPC_ID_INTC:
/* Third party (6500/7000) */
ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_33MHz;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
ctrl->push_button = 0;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
@@ -987,7 +964,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* First 66 Mhz implementation */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_66MHz;
+ bus->max_bus_speed = PCI_SPEED_66MHz;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
@@ -998,7 +975,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* First PCI-X implementation, 100MHz */
ctrl->push_flag = 1;
ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_100MHz_PCIX;
+ bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
ctrl->push_button = 1;
ctrl->pci_config_space = 1;
ctrl->defeature_PHP = 1;
@@ -1015,9 +992,9 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
case PCI_VENDOR_ID_INTEL:
/* Check for speed capability (0=33, 1=66) */
if (subsystem_deviceid & 0x0001)
- ctrl->speed_capability = PCI_SPEED_66MHz;
+ bus->max_bus_speed = PCI_SPEED_66MHz;
else
- ctrl->speed_capability = PCI_SPEED_33MHz;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
/* Check for push button */
if (subsystem_deviceid & 0x0002)
@@ -1079,7 +1056,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pdev->bus->number);
dbg("Hotplug controller capabilities:\n");
- dbg(" speed_capability %d\n", ctrl->speed_capability);
+ dbg(" speed_capability %d\n", bus->max_bus_speed);
dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ?
"switch present" : "no switch");
dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ?
@@ -1142,7 +1119,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
/* Check for 66Mhz operation */
- ctrl->speed = get_controller_speed(ctrl);
+ bus->cur_bus_speed = get_controller_speed(ctrl);
/********************************************************
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index 0ff689afa75..e43908d9b5d 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -1130,12 +1130,13 @@ static int is_bridge(struct pci_func * func)
static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
{
struct slot *slot;
+ struct pci_bus *bus = ctrl->pci_bus;
u8 reg;
u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
u16 reg16;
u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
- if (ctrl->speed == adapter_speed)
+ if (bus->cur_bus_speed == adapter_speed)
return 0;
/* We don't allow freq/mode changes if we find another adapter running
@@ -1152,7 +1153,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
* lower speed/mode, we allow the new adapter to function at
* this rate if supported
*/
- if (ctrl->speed < adapter_speed)
+ if (bus->cur_bus_speed < adapter_speed)
return 0;
return 1;
@@ -1161,20 +1162,20 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
/* If the controller doesn't support freq/mode changes and the
* controller is running at a higher mode, we bail
*/
- if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability))
+ if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability))
return 1;
/* But we allow the adapter to run at a lower rate if possible */
- if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability))
+ if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability))
return 0;
/* We try to set the max speed supported by both the adapter and
* controller
*/
- if (ctrl->speed_capability < adapter_speed) {
- if (ctrl->speed == ctrl->speed_capability)
+ if (bus->max_bus_speed < adapter_speed) {
+ if (bus->cur_bus_speed == bus->max_bus_speed)
return 0;
- adapter_speed = ctrl->speed_capability;
+ adapter_speed = bus->max_bus_speed;
}
writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
@@ -1229,8 +1230,8 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
/* Only if mode change...*/
- if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
- ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
+ if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
+ ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
set_SOGO(ctrl);
wait_for_ctrl_irq(ctrl);
@@ -1243,7 +1244,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
set_SOGO(ctrl);
wait_for_ctrl_irq(ctrl);
- ctrl->speed = adapter_speed;
+ bus->cur_bus_speed = adapter_speed;
slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
info("Successfully changed frequency/mode for adapter in slot %d\n",
@@ -1269,6 +1270,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
*/
static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
{
+ struct pci_bus *bus = ctrl->pci_bus;
u8 hp_slot;
u8 temp_byte;
u8 adapter_speed;
@@ -1309,7 +1311,7 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
wait_for_ctrl_irq (ctrl);
adapter_speed = get_adapter_speed(ctrl, hp_slot);
- if (ctrl->speed != adapter_speed)
+ if (bus->cur_bus_speed != adapter_speed)
if (set_controller_speed(ctrl, adapter_speed, hp_slot))
rc = WRONG_BUS_FREQUENCY;
@@ -1426,6 +1428,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
u32 temp_register = 0xFFFFFFFF;
u32 rc = 0;
struct pci_func *new_slot = NULL;
+ struct pci_bus *bus = ctrl->pci_bus;
struct slot *p_slot;
struct resource_lists res_lists;
@@ -1456,7 +1459,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
wait_for_ctrl_irq (ctrl);
adapter_speed = get_adapter_speed(ctrl, hp_slot);
- if (ctrl->speed != adapter_speed)
+ if (bus->cur_bus_speed != adapter_speed)
if (set_controller_speed(ctrl, adapter_speed, hp_slot))
rc = WRONG_BUS_FREQUENCY;
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index 7485ffda950..d934dd4fa87 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -395,89 +395,40 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value)
return rc;
}
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct slot *slot)
{
- int rc = -ENODEV;
- struct slot *pslot;
+ int rc;
u8 mode = 0;
+ enum pci_bus_speed speed;
+ struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus;
- debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __func__,
- hotplug_slot, value);
+ debug("%s - Entry slot[%p]\n", __func__, slot);
ibmphp_lock_operations();
-
- if (hotplug_slot) {
- pslot = hotplug_slot->private;
- if (pslot) {
- rc = 0;
- mode = pslot->supported_bus_mode;
- *value = pslot->supported_speed;
- switch (*value) {
- case BUS_SPEED_33:
- break;
- case BUS_SPEED_66:
- if (mode == BUS_MODE_PCIX)
- *value += 0x01;
- break;
- case BUS_SPEED_100:
- case BUS_SPEED_133:
- *value = pslot->supported_speed + 0x01;
- break;
- default:
- /* Note (will need to change): there would be soon 256, 512 also */
- rc = -ENODEV;
- }
- }
- }
-
+ mode = slot->supported_bus_mode;
+ speed = slot->supported_speed;
ibmphp_unlock_operations();
- debug("%s - Exit rc[%d] value[%x]\n", __func__, rc, *value);
- return rc;
-}
-static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- int rc = -ENODEV;
- struct slot *pslot;
- u8 mode = 0;
-
- debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __func__,
- hotplug_slot, value);
-
- ibmphp_lock_operations();
-
- if (hotplug_slot) {
- pslot = hotplug_slot->private;
- if (pslot) {
- rc = get_cur_bus_info(&pslot);
- if (!rc) {
- mode = pslot->bus_on->current_bus_mode;
- *value = pslot->bus_on->current_speed;
- switch (*value) {
- case BUS_SPEED_33:
- break;
- case BUS_SPEED_66:
- if (mode == BUS_MODE_PCIX)
- *value += 0x01;
- else if (mode == BUS_MODE_PCI)
- ;
- else
- *value = PCI_SPEED_UNKNOWN;
- break;
- case BUS_SPEED_100:
- case BUS_SPEED_133:
- *value += 0x01;
- break;
- default:
- /* Note of change: there would also be 256, 512 soon */
- rc = -ENODEV;
- }
- }
- }
+ switch (speed) {
+ case BUS_SPEED_33:
+ break;
+ case BUS_SPEED_66:
+ if (mode == BUS_MODE_PCIX)
+ speed += 0x01;
+ break;
+ case BUS_SPEED_100:
+ case BUS_SPEED_133:
+ speed += 0x01;
+ break;
+ default:
+ /* Note (will need to change): there would be soon 256, 512 also */
+ rc = -ENODEV;
}
- ibmphp_unlock_operations();
- debug("%s - Exit rc[%d] value[%x]\n", __func__, rc, *value);
+ if (!rc)
+ bus->max_bus_speed = speed;
+
+ debug("%s - Exit rc[%d] speed[%x]\n", __func__, rc, speed);
return rc;
}
@@ -572,6 +523,7 @@ static int __init init_ops(void)
if (slot_cur->bus_on->current_speed == 0xFF)
if (get_cur_bus_info(&slot_cur))
return -1;
+ get_max_bus_speed(slot_cur);
if (slot_cur->ctrl->options == 0xFF)
if (get_hpc_options(slot_cur, &slot_cur->ctrl->options))
@@ -655,6 +607,7 @@ static int validate(struct slot *slot_cur, int opn)
int ibmphp_update_slot_info(struct slot *slot_cur)
{
struct hotplug_slot_info *info;
+ struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus;
int rc;
u8 bus_speed;
u8 mode;
@@ -700,8 +653,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
bus_speed = PCI_SPEED_UNKNOWN;
}
- info->cur_bus_speed = bus_speed;
- info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed;
+ bus->cur_bus_speed = bus_speed;
// To do: bus_names
rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info);
@@ -1326,8 +1278,6 @@ struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_present,
- .get_max_bus_speed = get_max_bus_speed,
- .get_cur_bus_speed = get_cur_bus_speed,
/* .get_max_adapter_speed = get_max_adapter_speed,
.get_bus_name_status = get_bus_name,
*/
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index c1abac8ab5c..5becbdee402 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -245,7 +245,7 @@ static void __init print_ebda_hpc (void)
int __init ibmphp_access_ebda (void)
{
- u8 format, num_ctlrs, rio_complete, hs_complete;
+ u8 format, num_ctlrs, rio_complete, hs_complete, ebda_sz;
u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, re, rc_id, re_id, base;
int rc = 0;
@@ -260,7 +260,16 @@ int __init ibmphp_access_ebda (void)
iounmap (io_mem);
debug ("returned ebda segment: %x\n", ebda_seg);
- io_mem = ioremap(ebda_seg<<4, 1024);
+ io_mem = ioremap(ebda_seg<<4, 1);
+ if (!io_mem)
+ return -ENOMEM;
+ ebda_sz = readb(io_mem);
+ iounmap(io_mem);
+ debug("ebda size: %d(KiB)\n", ebda_sz);
+ if (ebda_sz == 0)
+ return -ENOMEM;
+
+ io_mem = ioremap(ebda_seg<<4, (ebda_sz * 1024));
if (!io_mem )
return -ENOMEM;
next_offset = 0x180;
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
index c7084f0eca5..1aaf3f32d3c 100644
--- a/drivers/pci/hotplug/ibmphp_hpc.c
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/sched.h>
+#include <linux/semaphore.h>
#include <linux/kthread.h>
#include "ibmphp.h"
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 38183a534b6..728b119f71a 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -64,32 +64,6 @@ static int debug;
static LIST_HEAD(pci_hotplug_slot_list);
static DEFINE_MUTEX(pci_hp_mutex);
-/* these strings match up with the values in pci_bus_speed */
-static char *pci_bus_speed_strings[] = {
- "33 MHz PCI", /* 0x00 */
- "66 MHz PCI", /* 0x01 */
- "66 MHz PCI-X", /* 0x02 */
- "100 MHz PCI-X", /* 0x03 */
- "133 MHz PCI-X", /* 0x04 */
- NULL, /* 0x05 */
- NULL, /* 0x06 */
- NULL, /* 0x07 */
- NULL, /* 0x08 */
- "66 MHz PCI-X 266", /* 0x09 */
- "100 MHz PCI-X 266", /* 0x0a */
- "133 MHz PCI-X 266", /* 0x0b */
- NULL, /* 0x0c */
- NULL, /* 0x0d */
- NULL, /* 0x0e */
- NULL, /* 0x0f */
- NULL, /* 0x10 */
- "66 MHz PCI-X 533", /* 0x11 */
- "100 MHz PCI-X 533", /* 0x12 */
- "133 MHz PCI-X 533", /* 0x13 */
- "2.5 GT/s PCIe", /* 0x14 */
- "5.0 GT/s PCIe", /* 0x15 */
-};
-
#ifdef CONFIG_HOTPLUG_PCI_CPCI
extern int cpci_hotplug_init(int debug);
extern void cpci_hotplug_exit(void);
@@ -118,8 +92,6 @@ GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
-GET_STATUS(max_bus_speed, enum pci_bus_speed)
-GET_STATUS(cur_bus_speed, enum pci_bus_speed)
static ssize_t power_read_file(struct pci_slot *slot, char *buf)
{
@@ -263,60 +235,6 @@ static struct pci_slot_attribute hotplug_slot_attr_presence = {
.show = presence_read_file,
};
-static char *unknown_speed = "Unknown bus speed";
-
-static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
-{
- char *speed_string;
- int retval;
- enum pci_bus_speed value;
-
- retval = get_max_bus_speed(slot->hotplug, &value);
- if (retval)
- goto exit;
-
- if (value == PCI_SPEED_UNKNOWN)
- speed_string = unknown_speed;
- else
- speed_string = pci_bus_speed_strings[value];
-
- retval = sprintf (buf, "%s\n", speed_string);
-
-exit:
- return retval;
-}
-
-static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
- .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
- .show = max_bus_speed_read_file,
-};
-
-static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
-{
- char *speed_string;
- int retval;
- enum pci_bus_speed value;
-
- retval = get_cur_bus_speed(slot->hotplug, &value);
- if (retval)
- goto exit;
-
- if (value == PCI_SPEED_UNKNOWN)
- speed_string = unknown_speed;
- else
- speed_string = pci_bus_speed_strings[value];
-
- retval = sprintf (buf, "%s\n", speed_string);
-
-exit:
- return retval;
-}
-
-static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
- .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
- .show = cur_bus_speed_read_file,
-};
-
static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
@@ -391,26 +309,6 @@ static bool has_adapter_file(struct pci_slot *pci_slot)
return false;
}
-static bool has_max_bus_speed_file(struct pci_slot *pci_slot)
-{
- struct hotplug_slot *slot = pci_slot->hotplug;
- if ((!slot) || (!slot->ops))
- return false;
- if (slot->ops->get_max_bus_speed)
- return true;
- return false;
-}
-
-static bool has_cur_bus_speed_file(struct pci_slot *pci_slot)
-{
- struct hotplug_slot *slot = pci_slot->hotplug;
- if ((!slot) || (!slot->ops))
- return false;
- if (slot->ops->get_cur_bus_speed)
- return true;
- return false;
-}
-
static bool has_test_file(struct pci_slot *pci_slot)
{
struct hotplug_slot *slot = pci_slot->hotplug;
@@ -456,20 +354,6 @@ static int fs_add_slot(struct pci_slot *slot)
goto exit_adapter;
}
- if (has_max_bus_speed_file(slot)) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_max_bus_speed.attr);
- if (retval)
- goto exit_max_speed;
- }
-
- if (has_cur_bus_speed_file(slot)) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_cur_bus_speed.attr);
- if (retval)
- goto exit_cur_speed;
- }
-
if (has_test_file(slot)) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_test.attr);
@@ -480,14 +364,6 @@ static int fs_add_slot(struct pci_slot *slot)
goto exit;
exit_test:
- if (has_cur_bus_speed_file(slot))
- sysfs_remove_file(&slot->kobj,
- &hotplug_slot_attr_cur_bus_speed.attr);
-exit_cur_speed:
- if (has_max_bus_speed_file(slot))
- sysfs_remove_file(&slot->kobj,
- &hotplug_slot_attr_max_bus_speed.attr);
-exit_max_speed:
if (has_adapter_file(slot))
sysfs_remove_file(&slot->kobj,
&hotplug_slot_attr_presence.attr);
@@ -523,14 +399,6 @@ static void fs_remove_slot(struct pci_slot *slot)
sysfs_remove_file(&slot->kobj,
&hotplug_slot_attr_presence.attr);
- if (has_max_bus_speed_file(slot))
- sysfs_remove_file(&slot->kobj,
- &hotplug_slot_attr_max_bus_speed.attr);
-
- if (has_cur_bus_speed_file(slot))
- sysfs_remove_file(&slot->kobj,
- &hotplug_slot_attr_cur_bus_speed.attr);
-
if (has_test_file(slot))
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 5674b2075bd..920f820edf8 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -69,8 +69,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
-static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
/**
* release_slot - free up the memory used by a slot
@@ -113,8 +111,6 @@ static int init_slot(struct controller *ctrl)
ops->disable_slot = disable_slot;
ops->get_power_status = get_power_status;
ops->get_adapter_status = get_adapter_status;
- ops->get_max_bus_speed = get_max_bus_speed;
- ops->get_cur_bus_speed = get_cur_bus_speed;
if (MRL_SENS(ctrl))
ops->get_latch_status = get_latch_status;
if (ATTN_LED(ctrl)) {
@@ -227,27 +223,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return pciehp_get_adapter_status(slot, value);
}
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
- enum pci_bus_speed *value)
-{
- struct slot *slot = hotplug_slot->private;
-
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
- return pciehp_get_max_link_speed(slot, value);
-}
-
-static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- struct slot *slot = hotplug_slot->private;
-
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
- return pciehp_get_cur_link_speed(slot, value);
-}
-
static int pciehp_probe(struct pcie_device *dev)
{
int rc;
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index d6ac1b261dd..9a7f247e8ac 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -341,6 +341,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
p_slot->state = POWERON_STATE;
break;
default:
+ kfree(info);
goto out;
}
queue_work(pciehp_wq, &info->work);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 10040d58c8e..40b48f569b1 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -492,6 +492,7 @@ int pciehp_power_on_slot(struct slot * slot)
u16 slot_cmd;
u16 cmd_mask;
u16 slot_status;
+ u16 lnk_status;
int retval = 0;
/* Clear sticky power-fault bit from previous power failures */
@@ -523,6 +524,14 @@ int pciehp_power_on_slot(struct slot * slot)
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
+ retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status);
+ if (retval) {
+ ctrl_err(ctrl, "%s: Cannot read LNKSTA register\n",
+ __func__);
+ return retval;
+ }
+ pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status);
+
return retval;
}
@@ -610,37 +619,6 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *value)
-{
- struct controller *ctrl = slot->ctrl;
- enum pcie_link_speed lnk_speed;
- u32 lnk_cap;
- int retval = 0;
-
- retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap);
- if (retval) {
- ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__);
- return retval;
- }
-
- switch (lnk_cap & 0x000F) {
- case 1:
- lnk_speed = PCIE_2_5GB;
- break;
- case 2:
- lnk_speed = PCIE_5_0GB;
- break;
- default:
- lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
- break;
- }
-
- *value = lnk_speed;
- ctrl_dbg(ctrl, "Max link speed = %d\n", lnk_speed);
-
- return retval;
-}
-
int pciehp_get_max_lnk_width(struct slot *slot,
enum pcie_link_width *value)
{
@@ -691,38 +669,6 @@ int pciehp_get_max_lnk_width(struct slot *slot,
return retval;
}
-int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *value)
-{
- struct controller *ctrl = slot->ctrl;
- enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN;
- int retval = 0;
- u16 lnk_status;
-
- retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status);
- if (retval) {
- ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n",
- __func__);
- return retval;
- }
-
- switch (lnk_status & PCI_EXP_LNKSTA_CLS) {
- case 1:
- lnk_speed = PCIE_2_5GB;
- break;
- case 2:
- lnk_speed = PCIE_5_0GB;
- break;
- default:
- lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
- break;
- }
-
- *value = lnk_speed;
- ctrl_dbg(ctrl, "Current link speed = %d\n", lnk_speed);
-
- return retval;
-}
-
int pciehp_get_cur_lnk_width(struct slot *slot,
enum pcie_link_width *value)
{
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
index 21733108add..0a16444c14c 100644
--- a/drivers/pci/hotplug/pciehp_pci.c
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -53,17 +53,15 @@ static int __ref pciehp_add_bridge(struct pci_dev *dev)
busnr = pci_scan_bridge(parent, dev, busnr, pass);
if (!dev->subordinate)
return -1;
- pci_bus_size_bridges(dev->subordinate);
- pci_bus_assign_resources(parent);
- pci_enable_bridges(parent);
- pci_bus_add_devices(parent);
+
return 0;
}
int pciehp_configure_device(struct slot *p_slot)
{
struct pci_dev *dev;
- struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
+ struct pci_dev *bridge = p_slot->ctrl->pcie->port;
+ struct pci_bus *parent = bridge->subordinate;
int num, fn;
struct controller *ctrl = p_slot->ctrl;
@@ -96,12 +94,25 @@ int pciehp_configure_device(struct slot *p_slot)
(dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
pciehp_add_bridge(dev);
}
+ pci_dev_put(dev);
+ }
+
+ pci_assign_unassigned_bridge_resources(bridge);
+
+ for (fn = 0; fn < 8; fn++) {
+ dev = pci_get_slot(parent, PCI_DEVFN(0, fn));
+ if (!dev)
+ continue;
+ if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
+ pci_dev_put(dev);
+ continue;
+ }
pci_configure_slot(dev);
pci_dev_put(dev);
}
- pci_bus_assign_resources(parent);
pci_bus_add_devices(parent);
+
return 0;
}
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
index c159223389e..dcaae725fd7 100644
--- a/drivers/pci/hotplug/rpaphp_core.c
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -130,10 +130,9 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
return 0;
}
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
{
- struct slot *slot = (struct slot *)hotplug_slot->private;
-
+ enum pci_bus_speed speed;
switch (slot->type) {
case 1:
case 2:
@@ -141,30 +140,30 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe
case 4:
case 5:
case 6:
- *value = PCI_SPEED_33MHz; /* speed for case 1-6 */
+ speed = PCI_SPEED_33MHz; /* speed for case 1-6 */
break;
case 7:
case 8:
- *value = PCI_SPEED_66MHz;
+ speed = PCI_SPEED_66MHz;
break;
case 11:
case 14:
- *value = PCI_SPEED_66MHz_PCIX;
+ speed = PCI_SPEED_66MHz_PCIX;
break;
case 12:
case 15:
- *value = PCI_SPEED_100MHz_PCIX;
+ speed = PCI_SPEED_100MHz_PCIX;
break;
case 13:
case 16:
- *value = PCI_SPEED_133MHz_PCIX;
+ speed = PCI_SPEED_133MHz_PCIX;
break;
default:
- *value = PCI_SPEED_UNKNOWN;
+ speed = PCI_SPEED_UNKNOWN;
break;
-
}
- return 0;
+
+ return speed;
}
static int get_children_props(struct device_node *dn, const int **drc_indexes,
@@ -408,6 +407,8 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
slot->state = NOT_VALID;
return -EINVAL;
}
+
+ slot->bus->max_bus_speed = get_max_bus_speed(slot);
return 0;
}
@@ -429,7 +430,6 @@ struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_adapter_status = get_adapter_status,
- .get_max_bus_speed = get_max_bus_speed,
};
module_init(rpaphp_init);
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 8e210cd76e5..d2627e1c3ac 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -333,8 +333,6 @@ struct hpc_ops {
int (*set_attention_status)(struct slot *slot, u8 status);
int (*get_latch_status)(struct slot *slot, u8 *status);
int (*get_adapter_status)(struct slot *slot, u8 *status);
- int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
- int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed);
int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode);
int (*get_prog_int)(struct slot *slot, u8 *prog_int);
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 8a520a3d0f5..a5062297f48 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -65,8 +65,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
-static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.set_attention_status = set_attention_status,
@@ -76,8 +74,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_max_bus_speed = get_max_bus_speed,
- .get_cur_bus_speed = get_cur_bus_speed,
};
/**
@@ -279,37 +275,6 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
- enum pci_bus_speed *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- int retval;
-
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
- retval = slot->hpc_ops->get_max_bus_speed(slot, value);
- if (retval < 0)
- *value = PCI_SPEED_UNKNOWN;
-
- return 0;
-}
-
-static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- int retval;
-
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
- retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
- if (retval < 0)
- *value = PCI_SPEED_UNKNOWN;
-
- return 0;
-}
-
static int is_shpc_capable(struct pci_dev *dev)
{
if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
index b8ab2796e66..3bba0c0888f 100644
--- a/drivers/pci/hotplug/shpchp_ctrl.c
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -285,17 +285,8 @@ static int board_added(struct slot *p_slot)
return WRONG_BUS_FREQUENCY;
}
- rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp);
- if (rc) {
- ctrl_err(ctrl, "Can't get bus operation speed\n");
- return WRONG_BUS_FREQUENCY;
- }
-
- rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp);
- if (rc) {
- ctrl_err(ctrl, "Can't get max bus operation speed\n");
- msp = bsp;
- }
+ bsp = ctrl->pci_dev->bus->cur_bus_speed;
+ msp = ctrl->pci_dev->bus->max_bus_speed;
/* Check if there are other slots or devices on the same bus */
if (!list_empty(&ctrl->pci_dev->subordinate->devices))
@@ -462,6 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
p_slot->state = POWERON_STATE;
break;
default:
+ kfree(info);
goto out;
}
queue_work(shpchp_wq, &info->work);
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 86dc3984776..5f5e8d2e355 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -660,6 +660,75 @@ static int hpc_slot_disable(struct slot * slot)
return retval;
}
+static int shpc_get_cur_bus_speed(struct controller *ctrl)
+{
+ int retval = 0;
+ struct pci_bus *bus = ctrl->pci_dev->subordinate;
+ enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
+ u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG);
+ u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
+ u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7);
+
+ if ((pi == 1) && (speed_mode > 4)) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ switch (speed_mode) {
+ case 0x0:
+ bus_speed = PCI_SPEED_33MHz;
+ break;
+ case 0x1:
+ bus_speed = PCI_SPEED_66MHz;
+ break;
+ case 0x2:
+ bus_speed = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 0x3:
+ bus_speed = PCI_SPEED_100MHz_PCIX;
+ break;
+ case 0x4:
+ bus_speed = PCI_SPEED_133MHz_PCIX;
+ break;
+ case 0x5:
+ bus_speed = PCI_SPEED_66MHz_PCIX_ECC;
+ break;
+ case 0x6:
+ bus_speed = PCI_SPEED_100MHz_PCIX_ECC;
+ break;
+ case 0x7:
+ bus_speed = PCI_SPEED_133MHz_PCIX_ECC;
+ break;
+ case 0x8:
+ bus_speed = PCI_SPEED_66MHz_PCIX_266;
+ break;
+ case 0x9:
+ bus_speed = PCI_SPEED_100MHz_PCIX_266;
+ break;
+ case 0xa:
+ bus_speed = PCI_SPEED_133MHz_PCIX_266;
+ break;
+ case 0xb:
+ bus_speed = PCI_SPEED_66MHz_PCIX_533;
+ break;
+ case 0xc:
+ bus_speed = PCI_SPEED_100MHz_PCIX_533;
+ break;
+ case 0xd:
+ bus_speed = PCI_SPEED_133MHz_PCIX_533;
+ break;
+ default:
+ retval = -ENODEV;
+ break;
+ }
+
+ out:
+ bus->cur_bus_speed = bus_speed;
+ dbg("Current bus speed = %d\n", bus_speed);
+ return retval;
+}
+
+
static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
{
int retval;
@@ -720,6 +789,8 @@ static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
retval = shpc_write_cmd(slot, 0, cmd);
if (retval)
ctrl_err(ctrl, "%s: Write command failed!\n", __func__);
+ else
+ shpc_get_cur_bus_speed(ctrl);
return retval;
}
@@ -803,10 +874,10 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
+static int shpc_get_max_bus_speed(struct controller *ctrl)
{
int retval = 0;
- struct controller *ctrl = slot->ctrl;
+ struct pci_bus *bus = ctrl->pci_dev->subordinate;
enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
u32 slot_avail1 = shpc_readl(ctrl, SLOT_AVAIL1);
@@ -842,79 +913,12 @@ static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
retval = -ENODEV;
}
- *value = bus_speed;
+ bus->max_bus_speed = bus_speed;
ctrl_dbg(ctrl, "Max bus speed = %d\n", bus_speed);
return retval;
}
-static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value)
-{
- int retval = 0;
- struct controller *ctrl = slot->ctrl;
- enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
- u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG);
- u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
- u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7);
-
- if ((pi == 1) && (speed_mode > 4)) {
- *value = PCI_SPEED_UNKNOWN;
- return -ENODEV;
- }
-
- switch (speed_mode) {
- case 0x0:
- *value = PCI_SPEED_33MHz;
- break;
- case 0x1:
- *value = PCI_SPEED_66MHz;
- break;
- case 0x2:
- *value = PCI_SPEED_66MHz_PCIX;
- break;
- case 0x3:
- *value = PCI_SPEED_100MHz_PCIX;
- break;
- case 0x4:
- *value = PCI_SPEED_133MHz_PCIX;
- break;
- case 0x5:
- *value = PCI_SPEED_66MHz_PCIX_ECC;
- break;
- case 0x6:
- *value = PCI_SPEED_100MHz_PCIX_ECC;
- break;
- case 0x7:
- *value = PCI_SPEED_133MHz_PCIX_ECC;
- break;
- case 0x8:
- *value = PCI_SPEED_66MHz_PCIX_266;
- break;
- case 0x9:
- *value = PCI_SPEED_100MHz_PCIX_266;
- break;
- case 0xa:
- *value = PCI_SPEED_133MHz_PCIX_266;
- break;
- case 0xb:
- *value = PCI_SPEED_66MHz_PCIX_533;
- break;
- case 0xc:
- *value = PCI_SPEED_100MHz_PCIX_533;
- break;
- case 0xd:
- *value = PCI_SPEED_133MHz_PCIX_533;
- break;
- default:
- *value = PCI_SPEED_UNKNOWN;
- retval = -ENODEV;
- break;
- }
-
- ctrl_dbg(ctrl, "Current bus speed = %d\n", bus_speed);
- return retval;
-}
-
static struct hpc_ops shpchp_hpc_ops = {
.power_on_slot = hpc_power_on_slot,
.slot_enable = hpc_slot_enable,
@@ -926,8 +930,6 @@ static struct hpc_ops shpchp_hpc_ops = {
.get_latch_status = hpc_get_latch_status,
.get_adapter_status = hpc_get_adapter_status,
- .get_max_bus_speed = hpc_get_max_bus_speed,
- .get_cur_bus_speed = hpc_get_cur_bus_speed,
.get_adapter_speed = hpc_get_adapter_speed,
.get_mode1_ECC_cap = hpc_get_mode1_ECC_cap,
.get_prog_int = hpc_get_prog_int,
@@ -1086,6 +1088,9 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
}
ctrl_dbg(ctrl, "HPC at %s irq=%x\n", pci_name(pdev), pdev->irq);
+ shpc_get_max_bus_speed(ctrl);
+ shpc_get_cur_bus_speed(ctrl);
+
/*
* If this is the first controller to be initialized,
* initialize the shpchpd work queue
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
index 29fa9d26ada..071b7dc0094 100644
--- a/drivers/pci/hotplug/shpchp_sysfs.c
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -47,8 +47,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
bus = pdev->subordinate;
out += sprintf(buf, "Free resources: memory\n");
- for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
- res = bus->resource[index];
+ pci_bus_for_each_resource(bus, res, index) {
if (res && (res->flags & IORESOURCE_MEM) &&
!(res->flags & IORESOURCE_PREFETCH)) {
out += sprintf(out, "start = %8.8llx, "
@@ -58,8 +57,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
}
}
out += sprintf(out, "Free resources: prefetchable memory\n");
- for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
- res = bus->resource[index];
+ pci_bus_for_each_resource(bus, res, index) {
if (res && (res->flags & IORESOURCE_MEM) &&
(res->flags & IORESOURCE_PREFETCH)) {
out += sprintf(out, "start = %8.8llx, "
@@ -69,8 +67,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
}
}
out += sprintf(out, "Free resources: IO\n");
- for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
- res = bus->resource[index];
+ pci_bus_for_each_resource(bus, res, index) {
if (res && (res->flags & IORESOURCE_IO)) {
out += sprintf(out, "start = %8.8llx, "
"length = %8.8llx\n",
diff --git a/drivers/pci/legacy.c b/drivers/pci/legacy.c
deleted file mode 100644
index 871f65c1593..00000000000
--- a/drivers/pci/legacy.c
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include "pci.h"
-
-/**
- * pci_find_device - begin or continue searching for a PCI device by vendor/device id
- * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
- * @from: Previous PCI device found in search, or %NULL for new search.
- *
- * Iterates through the list of known PCI devices. If a PCI device is found
- * with a matching @vendor and @device, a pointer to its device structure is
- * returned. Otherwise, %NULL is returned.
- * A new search is initiated by passing %NULL as the @from argument.
- * Otherwise if @from is not %NULL, searches continue from next device
- * on the global list.
- *
- * NOTE: Do not use this function any more; use pci_get_device() instead, as
- * the PCI device returned by this function can disappear at any moment in
- * time.
- */
-struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device,
- struct pci_dev *from)
-{
- struct pci_dev *pdev;
-
- pci_dev_get(from);
- pdev = pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
- pci_dev_put(pdev);
- return pdev;
-}
-EXPORT_SYMBOL(pci_find_device);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 7e2829538a4..c0c73913833 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -16,8 +16,144 @@
#include <acpi/acpi_bus.h>
#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
#include "pci.h"
+static DEFINE_MUTEX(pci_acpi_pm_notify_mtx);
+
+/**
+ * pci_acpi_wake_bus - Wake-up notification handler for root buses.
+ * @handle: ACPI handle of a device the notification is for.
+ * @event: Type of the signaled event.
+ * @context: PCI root bus to wake up devices on.
+ */
+static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context)
+{
+ struct pci_bus *pci_bus = context;
+
+ if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus)
+ pci_pme_wakeup_bus(pci_bus);
+}
+
+/**
+ * pci_acpi_wake_dev - Wake-up notification handler for PCI devices.
+ * @handle: ACPI handle of a device the notification is for.
+ * @event: Type of the signaled event.
+ * @context: PCI device object to wake up.
+ */
+static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
+{
+ struct pci_dev *pci_dev = context;
+
+ if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) {
+ pci_check_pme_status(pci_dev);
+ pm_runtime_resume(&pci_dev->dev);
+ if (pci_dev->subordinate)
+ pci_pme_wakeup_bus(pci_dev->subordinate);
+ }
+}
+
+/**
+ * add_pm_notifier - Register PM notifier for given ACPI device.
+ * @dev: ACPI device to add the notifier for.
+ * @context: PCI device or bus to check for PME status if an event is signaled.
+ *
+ * NOTE: @dev need not be a run-wake or wake-up device to be a valid source of
+ * PM wake-up events. For example, wake-up events may be generated for bridges
+ * if one of the devices below the bridge is signaling PME, even if the bridge
+ * itself doesn't have a wake-up GPE associated with it.
+ */
+static acpi_status add_pm_notifier(struct acpi_device *dev,
+ acpi_notify_handler handler,
+ void *context)
+{
+ acpi_status status = AE_ALREADY_EXISTS;
+
+ mutex_lock(&pci_acpi_pm_notify_mtx);
+
+ if (dev->wakeup.flags.notifier_present)
+ goto out;
+
+ status = acpi_install_notify_handler(dev->handle,
+ ACPI_SYSTEM_NOTIFY,
+ handler, context);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ dev->wakeup.flags.notifier_present = true;
+
+ out:
+ mutex_unlock(&pci_acpi_pm_notify_mtx);
+ return status;
+}
+
+/**
+ * remove_pm_notifier - Unregister PM notifier from given ACPI device.
+ * @dev: ACPI device to remove the notifier from.
+ */
+static acpi_status remove_pm_notifier(struct acpi_device *dev,
+ acpi_notify_handler handler)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+
+ mutex_lock(&pci_acpi_pm_notify_mtx);
+
+ if (!dev->wakeup.flags.notifier_present)
+ goto out;
+
+ status = acpi_remove_notify_handler(dev->handle,
+ ACPI_SYSTEM_NOTIFY,
+ handler);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ dev->wakeup.flags.notifier_present = false;
+
+ out:
+ mutex_unlock(&pci_acpi_pm_notify_mtx);
+ return status;
+}
+
+/**
+ * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus.
+ * @dev: ACPI device to add the notifier for.
+ * @pci_bus: PCI bus to walk checking for PME status if an event is signaled.
+ */
+acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev,
+ struct pci_bus *pci_bus)
+{
+ return add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus);
+}
+
+/**
+ * pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier.
+ * @dev: ACPI device to remove the notifier from.
+ */
+acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
+{
+ return remove_pm_notifier(dev, pci_acpi_wake_bus);
+}
+
+/**
+ * pci_acpi_add_pm_notifier - Register PM notifier for given PCI device.
+ * @dev: ACPI device to add the notifier for.
+ * @pci_dev: PCI device to check for the PME status if an event is signaled.
+ */
+acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
+ struct pci_dev *pci_dev)
+{
+ return add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev);
+}
+
+/**
+ * pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier.
+ * @dev: ACPI device to remove the notifier from.
+ */
+acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
+{
+ return remove_pm_notifier(dev, pci_acpi_wake_dev);
+}
+
/*
* _SxD returns the D-state with the highest power
* (lowest D-state number) supported in the S-state "x".
@@ -131,12 +267,87 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
return 0;
}
+/**
+ * acpi_dev_run_wake - Enable/disable wake-up for given device.
+ * @phys_dev: Device to enable/disable the platform to wake-up the system for.
+ * @enable: Whether enable or disable the wake-up functionality.
+ *
+ * Find the ACPI device object corresponding to @pci_dev and try to
+ * enable/disable the GPE associated with it.
+ */
+static int acpi_dev_run_wake(struct device *phys_dev, bool enable)
+{
+ struct acpi_device *dev;
+ acpi_handle handle;
+ int error = -ENODEV;
+
+ if (!device_run_wake(phys_dev))
+ return -EINVAL;
+
+ handle = DEVICE_ACPI_HANDLE(phys_dev);
+ if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
+ dev_dbg(phys_dev, "ACPI handle has no context in %s!\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (enable) {
+ if (!dev->wakeup.run_wake_count++) {
+ acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
+ acpi_enable_gpe(dev->wakeup.gpe_device,
+ dev->wakeup.gpe_number,
+ ACPI_GPE_TYPE_RUNTIME);
+ }
+ } else if (dev->wakeup.run_wake_count > 0) {
+ if (!--dev->wakeup.run_wake_count) {
+ acpi_disable_gpe(dev->wakeup.gpe_device,
+ dev->wakeup.gpe_number,
+ ACPI_GPE_TYPE_RUNTIME);
+ acpi_disable_wakeup_device_power(dev);
+ }
+ } else {
+ error = -EALREADY;
+ }
+
+ return error;
+}
+
+static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
+{
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ if (bridge->pme_interrupt)
+ return;
+ if (!acpi_dev_run_wake(&bridge->dev, enable))
+ return;
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ acpi_dev_run_wake(bus->bridge, enable);
+}
+
+static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
+{
+ if (dev->pme_interrupt)
+ return 0;
+
+ if (!acpi_dev_run_wake(&dev->dev, enable))
+ return 0;
+
+ acpi_pci_propagate_run_wake(dev->bus, enable);
+ return 0;
+}
+
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
.is_manageable = acpi_pci_power_manageable,
.set_state = acpi_pci_set_power_state,
.choose_state = acpi_pci_choose_state,
.can_wakeup = acpi_pci_can_wakeup,
.sleep_wake = acpi_pci_sleep_wake,
+ .run_wake = acpi_pci_run_wake,
};
/* ACPI bus type */
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e5d47be3c6d..f9a0aec3abc 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/cpu.h>
+#include <linux/pm_runtime.h>
#include "pci.h"
struct pci_dynid {
@@ -404,6 +405,35 @@ static void pci_device_shutdown(struct device *dev)
pci_msix_shutdown(pci_dev);
}
+#ifdef CONFIG_PM_OPS
+
+/* Auxiliary functions used for system resume and run-time resume. */
+
+/**
+ * pci_restore_standard_config - restore standard config registers of PCI device
+ * @pci_dev: PCI device to handle
+ */
+static int pci_restore_standard_config(struct pci_dev *pci_dev)
+{
+ pci_update_current_state(pci_dev, PCI_UNKNOWN);
+
+ if (pci_dev->current_state != PCI_D0) {
+ int error = pci_set_power_state(pci_dev, PCI_D0);
+ if (error)
+ return error;
+ }
+
+ return pci_restore_state(pci_dev);
+}
+
+static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
+{
+ pci_restore_standard_config(pci_dev);
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+}
+
+#endif
+
#ifdef CONFIG_PM_SLEEP
/*
@@ -520,29 +550,6 @@ static int pci_legacy_resume(struct device *dev)
/* Auxiliary functions used by the new power management framework */
-/**
- * pci_restore_standard_config - restore standard config registers of PCI device
- * @pci_dev: PCI device to handle
- */
-static int pci_restore_standard_config(struct pci_dev *pci_dev)
-{
- pci_update_current_state(pci_dev, PCI_UNKNOWN);
-
- if (pci_dev->current_state != PCI_D0) {
- int error = pci_set_power_state(pci_dev, PCI_D0);
- if (error)
- return error;
- }
-
- return pci_restore_state(pci_dev);
-}
-
-static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
-{
- pci_restore_standard_config(pci_dev);
- pci_fixup_device(pci_fixup_resume_early, pci_dev);
-}
-
static void pci_pm_default_resume(struct pci_dev *pci_dev)
{
pci_fixup_device(pci_fixup_resume, pci_dev);
@@ -581,6 +588,17 @@ static int pci_pm_prepare(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
+ /*
+ * PCI devices suspended at run time need to be resumed at this
+ * point, because in general it is necessary to reconfigure them for
+ * system suspend. Namely, if the device is supposed to wake up the
+ * system from the sleep state, we may need to reconfigure it for this
+ * purpose. In turn, if the device is not supposed to wake up the
+ * system from the sleep state, we'll have to prevent it from signaling
+ * wake-up.
+ */
+ pm_runtime_resume(dev);
+
if (drv && drv->pm && drv->pm->prepare)
error = drv->pm->prepare(dev);
@@ -595,6 +613,13 @@ static void pci_pm_complete(struct device *dev)
drv->pm->complete(dev);
}
+#else /* !CONFIG_PM_SLEEP */
+
+#define pci_pm_prepare NULL
+#define pci_pm_complete NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
#ifdef CONFIG_SUSPEND
static int pci_pm_suspend(struct device *dev)
@@ -681,7 +706,7 @@ static int pci_pm_resume_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
- pci_pm_default_resume_noirq(pci_dev);
+ pci_pm_default_resume_early(pci_dev);
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev);
@@ -879,7 +904,7 @@ static int pci_pm_restore_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
- pci_pm_default_resume_noirq(pci_dev);
+ pci_pm_default_resume_early(pci_dev);
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev);
@@ -931,6 +956,84 @@ static int pci_pm_restore(struct device *dev)
#endif /* !CONFIG_HIBERNATION */
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ pci_power_t prev = pci_dev->current_state;
+ int error;
+
+ if (!pm || !pm->runtime_suspend)
+ return -ENOSYS;
+
+ error = pm->runtime_suspend(dev);
+ suspend_report_result(pm->runtime_suspend, error);
+ if (error)
+ return error;
+
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
+ && pci_dev->current_state != PCI_UNKNOWN) {
+ WARN_ONCE(pci_dev->current_state != prev,
+ "PCI PM: State of device not saved by %pF\n",
+ pm->runtime_suspend);
+ return 0;
+ }
+
+ if (!pci_dev->state_saved)
+ pci_save_state(pci_dev);
+
+ pci_finish_runtime_suspend(pci_dev);
+
+ return 0;
+}
+
+static int pci_pm_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (!pm || !pm->runtime_resume)
+ return -ENOSYS;
+
+ pci_pm_default_resume_early(pci_dev);
+ __pci_enable_wake(pci_dev, PCI_D0, true, false);
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ return pm->runtime_resume(dev);
+}
+
+static int pci_pm_runtime_idle(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (!pm)
+ return -ENOSYS;
+
+ if (pm->runtime_idle) {
+ int ret = pm->runtime_idle(dev);
+ if (ret)
+ return ret;
+ }
+
+ pm_runtime_suspend(dev);
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+#define pci_pm_runtime_suspend NULL
+#define pci_pm_runtime_resume NULL
+#define pci_pm_runtime_idle NULL
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_OPS
+
const struct dev_pm_ops pci_dev_pm_ops = {
.prepare = pci_pm_prepare,
.complete = pci_pm_complete,
@@ -946,15 +1049,18 @@ const struct dev_pm_ops pci_dev_pm_ops = {
.thaw_noirq = pci_pm_thaw_noirq,
.poweroff_noirq = pci_pm_poweroff_noirq,
.restore_noirq = pci_pm_restore_noirq,
+ .runtime_suspend = pci_pm_runtime_suspend,
+ .runtime_resume = pci_pm_runtime_resume,
+ .runtime_idle = pci_pm_runtime_idle,
};
#define PCI_PM_OPS_PTR (&pci_dev_pm_ops)
-#else /* !CONFIG_PM_SLEEP */
+#else /* !COMFIG_PM_OPS */
#define PCI_PM_OPS_PTR NULL
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* !COMFIG_PM_OPS */
/**
* __pci_register_driver - register a new pci driver
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 315fea47e78..5b548aee9cb 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -19,8 +19,8 @@
#include <linux/pci-aspm.h>
#include <linux/pm_wakeup.h>
#include <linux/interrupt.h>
-#include <asm/dma.h> /* isa_dma_bridge_buggy */
#include <linux/device.h>
+#include <linux/pm_runtime.h>
#include <asm/setup.h>
#include "pci.h"
@@ -29,6 +29,12 @@ const char *pci_power_names[] = {
};
EXPORT_SYMBOL_GPL(pci_power_names);
+int isa_dma_bridge_buggy;
+EXPORT_SYMBOL(isa_dma_bridge_buggy);
+
+int pci_pci_problems;
+EXPORT_SYMBOL(pci_pci_problems);
+
unsigned int pci_pm_d3_delay;
static void pci_dev_d3_sleep(struct pci_dev *dev)
@@ -380,10 +386,9 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
{
const struct pci_bus *bus = dev->bus;
int i;
- struct resource *best = NULL;
+ struct resource *best = NULL, *r;
- for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *r = bus->resource[i];
+ pci_bus_for_each_resource(bus, r, i) {
if (!r)
continue;
if (res->start && !(res->start >= r->start && res->end <= r->end))
@@ -457,6 +462,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
}
+static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable)
+{
+ return pci_platform_pm ?
+ pci_platform_pm->run_wake(dev, enable) : -ENODEV;
+}
+
/**
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
* given PCI device
@@ -1190,6 +1201,66 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
}
/**
+ * pci_check_pme_status - Check if given device has generated PME.
+ * @dev: Device to check.
+ *
+ * Check the PME status of the device and if set, clear it and clear PME enable
+ * (if set). Return 'true' if PME status and PME enable were both set or
+ * 'false' otherwise.
+ */
+bool pci_check_pme_status(struct pci_dev *dev)
+{
+ int pmcsr_pos;
+ u16 pmcsr;
+ bool ret = false;
+
+ if (!dev->pm_cap)
+ return false;
+
+ pmcsr_pos = dev->pm_cap + PCI_PM_CTRL;
+ pci_read_config_word(dev, pmcsr_pos, &pmcsr);
+ if (!(pmcsr & PCI_PM_CTRL_PME_STATUS))
+ return false;
+
+ /* Clear PME status. */
+ pmcsr |= PCI_PM_CTRL_PME_STATUS;
+ if (pmcsr & PCI_PM_CTRL_PME_ENABLE) {
+ /* Disable PME to avoid interrupt flood. */
+ pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
+ ret = true;
+ }
+
+ pci_write_config_word(dev, pmcsr_pos, pmcsr);
+
+ return ret;
+}
+
+/**
+ * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set.
+ * @dev: Device to handle.
+ * @ign: Ignored.
+ *
+ * Check if @dev has generated PME and queue a resume request for it in that
+ * case.
+ */
+static int pci_pme_wakeup(struct pci_dev *dev, void *ign)
+{
+ if (pci_check_pme_status(dev))
+ pm_request_resume(&dev->dev);
+ return 0;
+}
+
+/**
+ * pci_pme_wakeup_bus - Walk given bus and wake up devices on it, if necessary.
+ * @bus: Top bus of the subtree to walk.
+ */
+void pci_pme_wakeup_bus(struct pci_bus *bus)
+{
+ if (bus)
+ pci_walk_bus(bus, pci_pme_wakeup, NULL);
+}
+
+/**
* pci_pme_capable - check the capability of PCI device to generate PME#
* @dev: PCI device to handle.
* @state: PCI state from which device will issue PME#.
@@ -1230,9 +1301,10 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
}
/**
- * pci_enable_wake - enable PCI device as wakeup event source
+ * __pci_enable_wake - enable PCI device as wakeup event source
* @dev: PCI device affected
* @state: PCI state from which device will issue wakeup events
+ * @runtime: True if the events are to be generated at run time
* @enable: True to enable event generation; false to disable
*
* This enables the device as a wakeup event source, or disables it.
@@ -1248,11 +1320,12 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
* Error code depending on the platform is returned if both the platform and
* the native mechanism fail to enable the generation of wake-up events
*/
-int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
+int __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
+ bool runtime, bool enable)
{
int ret = 0;
- if (enable && !device_may_wakeup(&dev->dev))
+ if (enable && !runtime && !device_may_wakeup(&dev->dev))
return -EINVAL;
/* Don't do the same thing twice in a row for one device. */
@@ -1272,19 +1345,24 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
pci_pme_active(dev, true);
else
ret = 1;
- error = platform_pci_sleep_wake(dev, true);
+ error = runtime ? platform_pci_run_wake(dev, true) :
+ platform_pci_sleep_wake(dev, true);
if (ret)
ret = error;
if (!ret)
dev->wakeup_prepared = true;
} else {
- platform_pci_sleep_wake(dev, false);
+ if (runtime)
+ platform_pci_run_wake(dev, false);
+ else
+ platform_pci_sleep_wake(dev, false);
pci_pme_active(dev, false);
dev->wakeup_prepared = false;
}
return ret;
}
+EXPORT_SYMBOL(__pci_enable_wake);
/**
* pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
@@ -1394,6 +1472,66 @@ int pci_back_from_sleep(struct pci_dev *dev)
}
/**
+ * pci_finish_runtime_suspend - Carry out PCI-specific part of runtime suspend.
+ * @dev: PCI device being suspended.
+ *
+ * Prepare @dev to generate wake-up events at run time and put it into a low
+ * power state.
+ */
+int pci_finish_runtime_suspend(struct pci_dev *dev)
+{
+ pci_power_t target_state = pci_target_state(dev);
+ int error;
+
+ if (target_state == PCI_POWER_ERROR)
+ return -EIO;
+
+ __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev));
+
+ error = pci_set_power_state(dev, target_state);
+
+ if (error)
+ __pci_enable_wake(dev, target_state, true, false);
+
+ return error;
+}
+
+/**
+ * pci_dev_run_wake - Check if device can generate run-time wake-up events.
+ * @dev: Device to check.
+ *
+ * Return true if the device itself is cabable of generating wake-up events
+ * (through the platform or using the native PCIe PME) or if the device supports
+ * PME and one of its upstream bridges can generate wake-up events.
+ */
+bool pci_dev_run_wake(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->bus;
+
+ if (device_run_wake(&dev->dev))
+ return true;
+
+ if (!dev->pme_support)
+ return false;
+
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ if (device_run_wake(&bridge->dev))
+ return true;
+
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ return device_run_wake(bus->bridge);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(pci_dev_run_wake);
+
+/**
* pci_pm_init - Initialize PM functions of given PCI device
* @dev: PCI device to handle.
*/
@@ -1402,6 +1540,7 @@ void pci_pm_init(struct pci_dev *dev)
int pm;
u16 pmc;
+ device_enable_async_suspend(&dev->dev);
dev->wakeup_prepared = false;
dev->pm_cap = 0;
@@ -2615,6 +2754,23 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
return 0;
}
+/* Some architectures require additional programming to enable VGA */
+static arch_set_vga_state_t arch_set_vga_state;
+
+void __init pci_register_set_vga_state(arch_set_vga_state_t func)
+{
+ arch_set_vga_state = func; /* NULL disables */
+}
+
+static int pci_set_vga_state_arch(struct pci_dev *dev, bool decode,
+ unsigned int command_bits, bool change_bridge)
+{
+ if (arch_set_vga_state)
+ return arch_set_vga_state(dev, decode, command_bits,
+ change_bridge);
+ return 0;
+}
+
/**
* pci_set_vga_state - set VGA decode state on device and parents if requested
* @dev: the PCI device
@@ -2628,9 +2784,15 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
struct pci_bus *bus;
struct pci_dev *bridge;
u16 cmd;
+ int rc;
WARN_ON(command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY));
+ /* ARCH specific VGA enables */
+ rc = pci_set_vga_state_arch(dev, decode, command_bits, change_bridge);
+ if (rc)
+ return rc;
+
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (decode == true)
cmd |= command_bits;
@@ -2845,6 +3007,7 @@ EXPORT_SYMBOL(pcim_pin_device);
EXPORT_SYMBOL(pci_disable_device);
EXPORT_SYMBOL(pci_find_capability);
EXPORT_SYMBOL(pci_bus_find_capability);
+EXPORT_SYMBOL(pci_register_set_vga_state);
EXPORT_SYMBOL(pci_release_regions);
EXPORT_SYMBOL(pci_request_regions);
EXPORT_SYMBOL(pci_request_regions_exclusive);
@@ -2871,10 +3034,8 @@ EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
EXPORT_SYMBOL(pci_pme_capable);
EXPORT_SYMBOL(pci_pme_active);
-EXPORT_SYMBOL(pci_enable_wake);
EXPORT_SYMBOL(pci_wake_from_d3);
EXPORT_SYMBOL(pci_target_state);
EXPORT_SYMBOL(pci_prepare_to_sleep);
EXPORT_SYMBOL(pci_back_from_sleep);
EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
-
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fbd0e3adbca..4eb10f48d27 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -35,6 +35,10 @@ int pci_probe_reset_function(struct pci_dev *dev);
*
* @sleep_wake: enables/disables the system wake up capability of given device
*
+ * @run_wake: enables/disables the platform to generate run-time wake-up events
+ * for given device (the device's wake-up capability has to be
+ * enabled by @sleep_wake for this feature to work)
+ *
* If given platform is generally capable of power managing PCI devices, all of
* these callbacks are mandatory.
*/
@@ -44,11 +48,16 @@ struct pci_platform_pm_ops {
pci_power_t (*choose_state)(struct pci_dev *dev);
bool (*can_wakeup)(struct pci_dev *dev);
int (*sleep_wake)(struct pci_dev *dev, bool enable);
+ int (*run_wake)(struct pci_dev *dev, bool enable);
};
extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
extern void pci_disable_enabled_device(struct pci_dev *dev);
+extern bool pci_check_pme_status(struct pci_dev *dev);
+extern int pci_finish_runtime_suspend(struct pci_dev *dev);
+extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
+extern void pci_pme_wakeup_bus(struct pci_bus *bus);
extern void pci_pm_init(struct pci_dev *dev);
extern void platform_pci_wakeup_init(struct pci_dev *dev);
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);
@@ -319,6 +328,13 @@ struct pci_dev_reset_methods {
int (*reset)(struct pci_dev *dev, int probe);
};
+#ifdef CONFIG_PCI_QUIRKS
extern int pci_dev_specific_reset(struct pci_dev *dev, int probe);
+#else
+static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
+{
+ return -ENOTTY;
+}
+#endif
#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 5a0c6ad53f8..b8b494b3e0d 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -46,3 +46,7 @@ config PCIEASPM_DEBUG
help
This enables PCI Express ASPM debug support. It will add per-device
interface to control ASPM.
+
+config PCIE_PME
+ def_bool y
+ depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 11f6bb1eae2..ea654545e7c 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
# Build PCI Express AER if needed
obj-$(CONFIG_PCIEAER) += aer/
+
+obj-$(CONFIG_PCIE_PME) += pme/
diff --git a/drivers/pci/pcie/pme/Makefile b/drivers/pci/pcie/pme/Makefile
new file mode 100644
index 00000000000..8b923805308
--- /dev/null
+++ b/drivers/pci/pcie/pme/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for PCI-Express Root Port PME signaling driver
+#
+
+obj-$(CONFIG_PCIE_PME) += pmedriver.o
+
+pmedriver-objs := pcie_pme.o
+pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c
new file mode 100644
index 00000000000..7b3cbff547e
--- /dev/null
+++ b/drivers/pci/pcie/pme/pcie_pme.c
@@ -0,0 +1,505 @@
+/*
+ * PCIe Native PME support
+ *
+ * Copyright (C) 2007 - 2009 Intel Corp
+ * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pcieport_if.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
+
+#include "../../pci.h"
+#include "pcie_pme.h"
+
+#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */
+
+/*
+ * If set, this switch will prevent the PCIe root port PME service driver from
+ * being registered. Consequently, the interrupt-based PCIe PME signaling will
+ * not be used by any PCIe root ports in that case.
+ */
+static bool pcie_pme_disabled;
+
+/*
+ * The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
+ * "In order to maintain compatibility with non-PCI Express-aware system
+ * software, system power management logic must be configured by firmware to use
+ * the legacy mechanism of signaling PME by default. PCI Express-aware system
+ * software must notify the firmware prior to enabling native, interrupt-based
+ * PME signaling." However, if the platform doesn't provide us with a suitable
+ * notification mechanism or the notification fails, it is not clear whether or
+ * not we are supposed to use the interrupt-based PCIe PME signaling. The
+ * switch below can be used to indicate the desired behaviour. When set, it
+ * will make the kernel use the interrupt-based PCIe PME signaling regardless of
+ * the platform notification status, although the kernel will attempt to notify
+ * the platform anyway. When unset, it will prevent the kernel from using the
+ * the interrupt-based PCIe PME signaling if the platform notification fails,
+ * which is the default.
+ */
+static bool pcie_pme_force_enable;
+
+/*
+ * If this switch is set, MSI will not be used for PCIe PME signaling. This
+ * causes the PCIe port driver to use INTx interrupts only, but it turns out
+ * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
+ * wake-up from system sleep states.
+ */
+bool pcie_pme_msi_disabled;
+
+static int __init pcie_pme_setup(char *str)
+{
+ if (!strcmp(str, "off"))
+ pcie_pme_disabled = true;
+ else if (!strcmp(str, "force"))
+ pcie_pme_force_enable = true;
+ else if (!strcmp(str, "nomsi"))
+ pcie_pme_msi_disabled = true;
+ return 1;
+}
+__setup("pcie_pme=", pcie_pme_setup);
+
+/**
+ * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
+ * @srv: PCIe PME root port service to use for carrying out the check.
+ *
+ * Notify the platform that the native PCIe PME is going to be used and return
+ * 'true' if the control of the PCIe PME registers has been acquired from the
+ * platform.
+ */
+static bool pcie_pme_platform_setup(struct pcie_device *srv)
+{
+ if (!pcie_pme_platform_notify(srv))
+ return true;
+ return pcie_pme_force_enable;
+}
+
+struct pcie_pme_service_data {
+ spinlock_t lock;
+ struct pcie_device *srv;
+ struct work_struct work;
+ bool noirq; /* Don't enable the PME interrupt used by this service. */
+};
+
+/**
+ * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
+ * @dev: PCIe root port or event collector.
+ * @enable: Enable or disable the interrupt.
+ */
+static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+{
+ int rtctl_pos;
+ u16 rtctl;
+
+ rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
+
+ pci_read_config_word(dev, rtctl_pos, &rtctl);
+ if (enable)
+ rtctl |= PCI_EXP_RTCTL_PMEIE;
+ else
+ rtctl &= ~PCI_EXP_RTCTL_PMEIE;
+ pci_write_config_word(dev, rtctl_pos, rtctl);
+}
+
+/**
+ * pcie_pme_clear_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+static void pcie_pme_clear_status(struct pci_dev *dev)
+{
+ int rtsta_pos;
+ u32 rtsta;
+
+ rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
+
+ pci_read_config_dword(dev, rtsta_pos, &rtsta);
+ rtsta |= PCI_EXP_RTSTA_PME;
+ pci_write_config_dword(dev, rtsta_pos, rtsta);
+}
+
+/**
+ * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
+ * @bus: PCI bus to scan.
+ *
+ * Scan given PCI bus and all buses under it for devices asserting PME#.
+ */
+static bool pcie_pme_walk_bus(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ bool ret = false;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ /* Skip PCIe devices in case we started from a root port. */
+ if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
+ pm_request_resume(&dev->dev);
+ ret = true;
+ }
+
+ if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
+ ret = true;
+ }
+
+ return ret;
+}
+
+/**
+ * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
+ * @bus: Secondary bus of the bridge.
+ * @devfn: Device/function number to check.
+ *
+ * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
+ * PCIe PME message. In such that case the bridge should use the Requester ID
+ * of device/function number 0 on its secondary bus.
+ */
+static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
+{
+ struct pci_dev *dev;
+ bool found = false;
+
+ if (devfn)
+ return false;
+
+ dev = pci_dev_get(bus->self);
+ if (!dev)
+ return false;
+
+ if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+ down_read(&pci_bus_sem);
+ if (pcie_pme_walk_bus(bus))
+ found = true;
+ up_read(&pci_bus_sem);
+ }
+
+ pci_dev_put(dev);
+ return found;
+}
+
+/**
+ * pcie_pme_handle_request - Find device that generated PME and handle it.
+ * @port: Root port or event collector that generated the PME interrupt.
+ * @req_id: PCIe Requester ID of the device that generated the PME.
+ */
+static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
+{
+ u8 busnr = req_id >> 8, devfn = req_id & 0xff;
+ struct pci_bus *bus;
+ struct pci_dev *dev;
+ bool found = false;
+
+ /* First, check if the PME is from the root port itself. */
+ if (port->devfn == devfn && port->bus->number == busnr) {
+ if (pci_check_pme_status(port)) {
+ pm_request_resume(&port->dev);
+ found = true;
+ } else {
+ /*
+ * Apparently, the root port generated the PME on behalf
+ * of a non-PCIe device downstream. If this is done by
+ * a root port, the Requester ID field in its status
+ * register may contain either the root port's, or the
+ * source device's information (PCI Express Base
+ * Specification, Rev. 2.0, Section 6.1.9).
+ */
+ down_read(&pci_bus_sem);
+ found = pcie_pme_walk_bus(port->subordinate);
+ up_read(&pci_bus_sem);
+ }
+ goto out;
+ }
+
+ /* Second, find the bus the source device is on. */
+ bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
+ if (!bus)
+ goto out;
+
+ /* Next, check if the PME is from a PCIe-PCI bridge. */
+ found = pcie_pme_from_pci_bridge(bus, devfn);
+ if (found)
+ goto out;
+
+ /* Finally, try to find the PME source on the bus. */
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_get(dev);
+ if (dev->devfn == devfn) {
+ found = true;
+ break;
+ }
+ pci_dev_put(dev);
+ }
+ up_read(&pci_bus_sem);
+
+ if (found) {
+ /* The device is there, but we have to check its PME status. */
+ found = pci_check_pme_status(dev);
+ if (found)
+ pm_request_resume(&dev->dev);
+ pci_dev_put(dev);
+ } else if (devfn) {
+ /*
+ * The device is not there, but we can still try to recover by
+ * assuming that the PME was reported by a PCIe-PCI bridge that
+ * used devfn different from zero.
+ */
+ dev_dbg(&port->dev, "PME interrupt generated for "
+ "non-existent device %02x:%02x.%d\n",
+ busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ found = pcie_pme_from_pci_bridge(bus, 0);
+ }
+
+ out:
+ if (!found)
+ dev_dbg(&port->dev, "Spurious native PME interrupt!\n");
+}
+
+/**
+ * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
+ * @work: Work structure giving access to service data.
+ */
+static void pcie_pme_work_fn(struct work_struct *work)
+{
+ struct pcie_pme_service_data *data =
+ container_of(work, struct pcie_pme_service_data, work);
+ struct pci_dev *port = data->srv->port;
+ int rtsta_pos;
+ u32 rtsta;
+
+ rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
+
+ spin_lock_irq(&data->lock);
+
+ for (;;) {
+ if (data->noirq)
+ break;
+
+ pci_read_config_dword(port, rtsta_pos, &rtsta);
+ if (rtsta & PCI_EXP_RTSTA_PME) {
+ /*
+ * Clear PME status of the port. If there are other
+ * pending PMEs, the status will be set again.
+ */
+ pcie_pme_clear_status(port);
+
+ spin_unlock_irq(&data->lock);
+ pcie_pme_handle_request(port, rtsta & 0xffff);
+ spin_lock_irq(&data->lock);
+
+ continue;
+ }
+
+ /* No need to loop if there are no more PMEs pending. */
+ if (!(rtsta & PCI_EXP_RTSTA_PENDING))
+ break;
+
+ spin_unlock_irq(&data->lock);
+ cpu_relax();
+ spin_lock_irq(&data->lock);
+ }
+
+ if (!data->noirq)
+ pcie_pme_interrupt_enable(port, true);
+
+ spin_unlock_irq(&data->lock);
+}
+
+/**
+ * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
+ * @irq: Interrupt vector.
+ * @context: Interrupt context pointer.
+ */
+static irqreturn_t pcie_pme_irq(int irq, void *context)
+{
+ struct pci_dev *port;
+ struct pcie_pme_service_data *data;
+ int rtsta_pos;
+ u32 rtsta;
+ unsigned long flags;
+
+ port = ((struct pcie_device *)context)->port;
+ data = get_service_data((struct pcie_device *)context);
+
+ rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
+
+ spin_lock_irqsave(&data->lock, flags);
+ pci_read_config_dword(port, rtsta_pos, &rtsta);
+
+ if (!(rtsta & PCI_EXP_RTSTA_PME)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return IRQ_NONE;
+ }
+
+ pcie_pme_interrupt_enable(port, false);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* We don't use pm_wq, because it's freezable. */
+ schedule_work(&data->work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * pcie_pme_set_native - Set the PME interrupt flag for given device.
+ * @dev: PCI device to handle.
+ * @ign: Ignored.
+ */
+static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
+{
+ dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
+
+ device_set_run_wake(&dev->dev, true);
+ dev->pme_interrupt = true;
+ return 0;
+}
+
+/**
+ * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
+ * @port: PCIe root port or event collector to handle.
+ *
+ * For each device below given root port, including the port itself (or for each
+ * root complex integrated endpoint if @port is a root complex event collector)
+ * set the flag indicating that it can signal run-time wake-up events via PCIe
+ * PME interrupts.
+ */
+static void pcie_pme_mark_devices(struct pci_dev *port)
+{
+ pcie_pme_set_native(port, NULL);
+ if (port->subordinate) {
+ pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
+ } else {
+ struct pci_bus *bus = port->bus;
+ struct pci_dev *dev;
+
+ /* Check if this is a root port event collector. */
+ if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
+ return;
+
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ if (pci_is_pcie(dev)
+ && dev->pcie_type == PCI_EXP_TYPE_RC_END)
+ pcie_pme_set_native(dev, NULL);
+ up_read(&pci_bus_sem);
+ }
+}
+
+/**
+ * pcie_pme_probe - Initialize PCIe PME service for given root port.
+ * @srv: PCIe service to initialize.
+ */
+static int pcie_pme_probe(struct pcie_device *srv)
+{
+ struct pci_dev *port;
+ struct pcie_pme_service_data *data;
+ int ret;
+
+ if (!pcie_pme_platform_setup(srv))
+ return -EACCES;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spin_lock_init(&data->lock);
+ INIT_WORK(&data->work, pcie_pme_work_fn);
+ data->srv = srv;
+ set_service_data(srv, data);
+
+ port = srv->port;
+ pcie_pme_interrupt_enable(port, false);
+ pcie_pme_clear_status(port);
+
+ ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
+ if (ret) {
+ kfree(data);
+ } else {
+ pcie_pme_mark_devices(port);
+ pcie_pme_interrupt_enable(port, true);
+ }
+
+ return ret;
+}
+
+/**
+ * pcie_pme_suspend - Suspend PCIe PME service device.
+ * @srv: PCIe service device to suspend.
+ */
+static int pcie_pme_suspend(struct pcie_device *srv)
+{
+ struct pcie_pme_service_data *data = get_service_data(srv);
+ struct pci_dev *port = srv->port;
+
+ spin_lock_irq(&data->lock);
+ pcie_pme_interrupt_enable(port, false);
+ pcie_pme_clear_status(port);
+ data->noirq = true;
+ spin_unlock_irq(&data->lock);
+
+ synchronize_irq(srv->irq);
+
+ return 0;
+}
+
+/**
+ * pcie_pme_resume - Resume PCIe PME service device.
+ * @srv - PCIe service device to resume.
+ */
+static int pcie_pme_resume(struct pcie_device *srv)
+{
+ struct pcie_pme_service_data *data = get_service_data(srv);
+ struct pci_dev *port = srv->port;
+
+ spin_lock_irq(&data->lock);
+ data->noirq = false;
+ pcie_pme_clear_status(port);
+ pcie_pme_interrupt_enable(port, true);
+ spin_unlock_irq(&data->lock);
+
+ return 0;
+}
+
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to resume.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+ pcie_pme_suspend(srv);
+ free_irq(srv->irq, srv);
+ kfree(get_service_data(srv));
+}
+
+static struct pcie_port_service_driver pcie_pme_driver = {
+ .name = "pcie_pme",
+ .port_type = PCI_EXP_TYPE_ROOT_PORT,
+ .service = PCIE_PORT_SERVICE_PME,
+
+ .probe = pcie_pme_probe,
+ .suspend = pcie_pme_suspend,
+ .resume = pcie_pme_resume,
+ .remove = pcie_pme_remove,
+};
+
+/**
+ * pcie_pme_service_init - Register the PCIe PME service driver.
+ */
+static int __init pcie_pme_service_init(void)
+{
+ return pcie_pme_disabled ?
+ -ENODEV : pcie_port_service_register(&pcie_pme_driver);
+}
+
+module_init(pcie_pme_service_init);
diff --git a/drivers/pci/pcie/pme/pcie_pme.h b/drivers/pci/pcie/pme/pcie_pme.h
new file mode 100644
index 00000000000..b30d2b7c977
--- /dev/null
+++ b/drivers/pci/pcie/pme/pcie_pme.h
@@ -0,0 +1,28 @@
+/*
+ * drivers/pci/pcie/pme/pcie_pme.h
+ *
+ * PCI Express Root Port PME signaling support
+ *
+ * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ */
+
+#ifndef _PCIE_PME_H_
+#define _PCIE_PME_H_
+
+struct pcie_device;
+
+#ifdef CONFIG_ACPI
+extern int pcie_pme_acpi_setup(struct pcie_device *srv);
+
+static inline int pcie_pme_platform_notify(struct pcie_device *srv)
+{
+ return pcie_pme_acpi_setup(srv);
+}
+#else /* !CONFIG_ACPI */
+static inline int pcie_pme_platform_notify(struct pcie_device *srv)
+{
+ return 0;
+}
+#endif /* !CONFIG_ACPI */
+
+#endif
diff --git a/drivers/pci/pcie/pme/pcie_pme_acpi.c b/drivers/pci/pcie/pme/pcie_pme_acpi.c
new file mode 100644
index 00000000000..83ab2287ae3
--- /dev/null
+++ b/drivers/pci/pcie/pme/pcie_pme_acpi.c
@@ -0,0 +1,54 @@
+/*
+ * PCIe Native PME support, ACPI-related part
+ *
+ * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pcieport_if.h>
+
+/**
+ * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
+ * @srv - PCIe PME service for a root port or event collector.
+ *
+ * Invoked when the PCIe bus type loads PCIe PME service driver. To avoid
+ * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
+ * control to the kernel.
+ */
+int pcie_pme_acpi_setup(struct pcie_device *srv)
+{
+ acpi_status status = AE_NOT_FOUND;
+ struct pci_dev *port = srv->port;
+ acpi_handle handle;
+ int error = 0;
+
+ if (acpi_pci_disabled)
+ return -ENOSYS;
+
+ dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
+
+ handle = acpi_find_root_bridge_handle(port);
+ if (!handle)
+ return -EINVAL;
+
+ status = acpi_pci_osc_control_set(handle,
+ OSC_PCI_EXPRESS_PME_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ if (ACPI_FAILURE(status)) {
+ dev_info(&port->dev,
+ "Failed to receive control of PCIe PME service: %s\n",
+ (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
+ "no _OSC support" : "ACPI _OSC failed");
+ error = -ENODEV;
+ }
+
+ return error;
+}
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index aaeb9d21cba..813a5c3427b 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -30,4 +30,21 @@ extern void pcie_port_device_remove(struct pci_dev *dev);
extern int __must_check pcie_port_bus_register(void);
extern void pcie_port_bus_unregister(void);
+#ifdef CONFIG_PCIE_PME
+extern bool pcie_pme_msi_disabled;
+
+static inline void pcie_pme_disable_msi(void)
+{
+ pcie_pme_msi_disabled = true;
+}
+
+static inline bool pcie_pme_no_msi(void)
+{
+ return pcie_pme_msi_disabled;
+}
+#else /* !CONFIG_PCIE_PME */
+static inline void pcie_pme_disable_msi(void) {}
+static inline bool pcie_pme_no_msi(void) { return false; }
+#endif /* !CONFIG_PCIE_PME */
+
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index b174188ac12..e73effbe402 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -186,16 +186,24 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
*/
static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
- int i, irq;
+ int i, irq = -1;
+
+ /* We have to use INTx if MSI cannot be used for PCIe PME. */
+ if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) {
+ if (dev->pin)
+ irq = dev->irq;
+ goto no_msi;
+ }
/* Try to use MSI-X if supported */
if (!pcie_port_enable_msix(dev, irqs, mask))
return 0;
+
/* We're not going to use MSI-X, so try MSI and fall back to INTx */
- irq = -1;
if (!pci_enable_msi(dev) || dev->pin)
irq = dev->irq;
+ no_msi:
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
irqs[i] = irq;
irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
@@ -277,6 +285,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
pci_name(pdev),
get_descriptor_id(pdev->pcie_type, service));
device->parent = &pdev->dev;
+ device_enable_async_suspend(device);
retval = device_register(device);
if (retval)
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 13c8972886e..127e8f169d9 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/pcieport_if.h>
#include <linux/aer.h>
+#include <linux/dmi.h>
#include "portdrv.h"
#include "aer/aerdrv.h"
@@ -273,10 +274,36 @@ static struct pci_driver pcie_portdriver = {
.driver.pm = PCIE_PORTDRV_PM_OPS,
};
+static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
+{
+ pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
+ d->ident);
+ pcie_pme_disable_msi();
+ return 0;
+}
+
+static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = {
+ /*
+ * Boxes that should not use MSI for PCIe PME signaling.
+ */
+ {
+ .callback = dmi_pcie_pme_disable_msi,
+ .ident = "MSI Wind U-100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "MICRO-STAR INTERNATIONAL CO., LTD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
+ },
+ },
+ {}
+};
+
static int __init pcie_portdrv_init(void)
{
int retval;
+ dmi_check_system(pcie_portdrv_dmi_table);
+
retval = pcie_port_bus_register();
if (retval) {
printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 446e4a94d7d..2a943090a3b 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -89,6 +89,7 @@ static void release_pcibus_dev(struct device *dev)
if (pci_bus->bridge)
put_device(pci_bus->bridge);
+ pci_bus_remove_resources(pci_bus);
kfree(pci_bus);
}
@@ -281,26 +282,12 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
}
}
-void __devinit pci_read_bridge_bases(struct pci_bus *child)
+static void __devinit pci_read_bridge_io(struct pci_bus *child)
{
struct pci_dev *dev = child->self;
u8 io_base_lo, io_limit_lo;
- u16 mem_base_lo, mem_limit_lo;
unsigned long base, limit;
struct resource *res;
- int i;
-
- if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */
- return;
-
- dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n",
- child->secondary, child->subordinate,
- dev->transparent ? " (subtractive decode)": "");
-
- if (dev->transparent) {
- for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
- child->resource[i] = child->parent->resource[i - 3];
- }
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
@@ -316,26 +303,50 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
limit |= (io_limit_hi << 16);
}
- if (base <= limit) {
+ if (base && base <= limit) {
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
if (!res->start)
res->start = base;
if (!res->end)
res->end = limit + 0xfff;
dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
+ } else {
+ dev_printk(KERN_DEBUG, &dev->dev,
+ " bridge window [io %04lx - %04lx] reg reading\n",
+ base, limit);
}
+}
+
+static void __devinit pci_read_bridge_mmio(struct pci_bus *child)
+{
+ struct pci_dev *dev = child->self;
+ u16 mem_base_lo, mem_limit_lo;
+ unsigned long base, limit;
+ struct resource *res;
res = child->resource[1];
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
- if (base <= limit) {
+ if (base && base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
res->start = base;
res->end = limit + 0xfffff;
dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
+ } else {
+ dev_printk(KERN_DEBUG, &dev->dev,
+ " bridge window [mem 0x%08lx - 0x%08lx] reg reading\n",
+ base, limit + 0xfffff);
}
+}
+
+static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child)
+{
+ struct pci_dev *dev = child->self;
+ u16 mem_base_lo, mem_limit_lo;
+ unsigned long base, limit;
+ struct resource *res;
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
@@ -366,7 +377,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
#endif
}
}
- if (base <= limit) {
+ if (base && base <= limit) {
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (res->flags & PCI_PREF_RANGE_TYPE_64)
@@ -374,6 +385,44 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
res->start = base;
res->end = limit + 0xfffff;
dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
+ } else {
+ dev_printk(KERN_DEBUG, &dev->dev,
+ " bridge window [mem 0x%08lx - %08lx pref] reg reading\n",
+ base, limit + 0xfffff);
+ }
+}
+
+void __devinit pci_read_bridge_bases(struct pci_bus *child)
+{
+ struct pci_dev *dev = child->self;
+ struct resource *res;
+ int i;
+
+ if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */
+ return;
+
+ dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n",
+ child->secondary, child->subordinate,
+ dev->transparent ? " (subtractive decode)" : "");
+
+ pci_bus_remove_resources(child);
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+ child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
+
+ pci_read_bridge_io(child);
+ pci_read_bridge_mmio(child);
+ pci_read_bridge_mmio_pref(child);
+
+ if (dev->transparent) {
+ pci_bus_for_each_resource(child->parent, res, i) {
+ if (res) {
+ pci_bus_add_resource(child, res,
+ PCI_SUBTRACTIVE_DECODE);
+ dev_printk(KERN_DEBUG, &dev->dev,
+ " bridge window %pR (subtractive decode)\n",
+ res);
+ }
+ }
}
}
@@ -387,10 +436,147 @@ static struct pci_bus * pci_alloc_bus(void)
INIT_LIST_HEAD(&b->children);
INIT_LIST_HEAD(&b->devices);
INIT_LIST_HEAD(&b->slots);
+ INIT_LIST_HEAD(&b->resources);
+ b->max_bus_speed = PCI_SPEED_UNKNOWN;
+ b->cur_bus_speed = PCI_SPEED_UNKNOWN;
}
return b;
}
+static unsigned char pcix_bus_speed[] = {
+ PCI_SPEED_UNKNOWN, /* 0 */
+ PCI_SPEED_66MHz_PCIX, /* 1 */
+ PCI_SPEED_100MHz_PCIX, /* 2 */
+ PCI_SPEED_133MHz_PCIX, /* 3 */
+ PCI_SPEED_UNKNOWN, /* 4 */
+ PCI_SPEED_66MHz_PCIX_ECC, /* 5 */
+ PCI_SPEED_100MHz_PCIX_ECC, /* 6 */
+ PCI_SPEED_133MHz_PCIX_ECC, /* 7 */
+ PCI_SPEED_UNKNOWN, /* 8 */
+ PCI_SPEED_66MHz_PCIX_266, /* 9 */
+ PCI_SPEED_100MHz_PCIX_266, /* A */
+ PCI_SPEED_133MHz_PCIX_266, /* B */
+ PCI_SPEED_UNKNOWN, /* C */
+ PCI_SPEED_66MHz_PCIX_533, /* D */
+ PCI_SPEED_100MHz_PCIX_533, /* E */
+ PCI_SPEED_133MHz_PCIX_533 /* F */
+};
+
+static unsigned char pcie_link_speed[] = {
+ PCI_SPEED_UNKNOWN, /* 0 */
+ PCIE_SPEED_2_5GT, /* 1 */
+ PCIE_SPEED_5_0GT, /* 2 */
+ PCIE_SPEED_8_0GT, /* 3 */
+ PCI_SPEED_UNKNOWN, /* 4 */
+ PCI_SPEED_UNKNOWN, /* 5 */
+ PCI_SPEED_UNKNOWN, /* 6 */
+ PCI_SPEED_UNKNOWN, /* 7 */
+ PCI_SPEED_UNKNOWN, /* 8 */
+ PCI_SPEED_UNKNOWN, /* 9 */
+ PCI_SPEED_UNKNOWN, /* A */
+ PCI_SPEED_UNKNOWN, /* B */
+ PCI_SPEED_UNKNOWN, /* C */
+ PCI_SPEED_UNKNOWN, /* D */
+ PCI_SPEED_UNKNOWN, /* E */
+ PCI_SPEED_UNKNOWN /* F */
+};
+
+void pcie_update_link_speed(struct pci_bus *bus, u16 linksta)
+{
+ bus->cur_bus_speed = pcie_link_speed[linksta & 0xf];
+}
+EXPORT_SYMBOL_GPL(pcie_update_link_speed);
+
+static unsigned char agp_speeds[] = {
+ AGP_UNKNOWN,
+ AGP_1X,
+ AGP_2X,
+ AGP_4X,
+ AGP_8X
+};
+
+static enum pci_bus_speed agp_speed(int agp3, int agpstat)
+{
+ int index = 0;
+
+ if (agpstat & 4)
+ index = 3;
+ else if (agpstat & 2)
+ index = 2;
+ else if (agpstat & 1)
+ index = 1;
+ else
+ goto out;
+
+ if (agp3) {
+ index += 2;
+ if (index == 5)
+ index = 0;
+ }
+
+ out:
+ return agp_speeds[index];
+}
+
+
+static void pci_set_bus_speed(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ int pos;
+
+ pos = pci_find_capability(bridge, PCI_CAP_ID_AGP);
+ if (!pos)
+ pos = pci_find_capability(bridge, PCI_CAP_ID_AGP3);
+ if (pos) {
+ u32 agpstat, agpcmd;
+
+ pci_read_config_dword(bridge, pos + PCI_AGP_STATUS, &agpstat);
+ bus->max_bus_speed = agp_speed(agpstat & 8, agpstat & 7);
+
+ pci_read_config_dword(bridge, pos + PCI_AGP_COMMAND, &agpcmd);
+ bus->cur_bus_speed = agp_speed(agpstat & 8, agpcmd & 7);
+ }
+
+ pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX);
+ if (pos) {
+ u16 status;
+ enum pci_bus_speed max;
+ pci_read_config_word(bridge, pos + 2, &status);
+
+ if (status & 0x8000) {
+ max = PCI_SPEED_133MHz_PCIX_533;
+ } else if (status & 0x4000) {
+ max = PCI_SPEED_133MHz_PCIX_266;
+ } else if (status & 0x0002) {
+ if (((status >> 12) & 0x3) == 2) {
+ max = PCI_SPEED_133MHz_PCIX_ECC;
+ } else {
+ max = PCI_SPEED_133MHz_PCIX;
+ }
+ } else {
+ max = PCI_SPEED_66MHz_PCIX;
+ }
+
+ bus->max_bus_speed = max;
+ bus->cur_bus_speed = pcix_bus_speed[(status >> 6) & 0xf];
+
+ return;
+ }
+
+ pos = pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ if (pos) {
+ u32 linkcap;
+ u16 linksta;
+
+ pci_read_config_dword(bridge, pos + PCI_EXP_LNKCAP, &linkcap);
+ bus->max_bus_speed = pcie_link_speed[linkcap & 0xf];
+
+ pci_read_config_word(bridge, pos + PCI_EXP_LNKSTA, &linksta);
+ pcie_update_link_speed(bus, linksta);
+ }
+}
+
+
static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
struct pci_dev *bridge, int busnr)
{
@@ -430,6 +616,8 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
child->self = bridge;
child->bridge = get_device(&bridge->dev);
+ pci_set_bus_speed(child);
+
/* Set up default resource pointers and names.. */
for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i];
@@ -1081,6 +1269,45 @@ struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)
}
EXPORT_SYMBOL(pci_scan_single_device);
+static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn)
+{
+ u16 cap;
+ unsigned pos, next_fn;
+
+ if (!dev)
+ return 0;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
+ if (!pos)
+ return 0;
+ pci_read_config_word(dev, pos + 4, &cap);
+ next_fn = cap >> 8;
+ if (next_fn <= fn)
+ return 0;
+ return next_fn;
+}
+
+static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn)
+{
+ return (fn + 1) % 8;
+}
+
+static unsigned no_next_fn(struct pci_dev *dev, unsigned fn)
+{
+ return 0;
+}
+
+static int only_one_child(struct pci_bus *bus)
+{
+ struct pci_dev *parent = bus->self;
+ if (!parent || !pci_is_pcie(parent))
+ return 0;
+ if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
+ parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
+ return 1;
+ return 0;
+}
+
/**
* pci_scan_slot - scan a PCI slot on a bus for devices.
* @bus: PCI bus to scan
@@ -1094,21 +1321,30 @@ EXPORT_SYMBOL(pci_scan_single_device);
*/
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
- int fn, nr = 0;
+ unsigned fn, nr = 0;
struct pci_dev *dev;
+ unsigned (*next_fn)(struct pci_dev *, unsigned) = no_next_fn;
+
+ if (only_one_child(bus) && (devfn > 0))
+ return 0; /* Already scanned the entire slot */
dev = pci_scan_single_device(bus, devfn);
- if (dev && !dev->is_added) /* new device? */
+ if (!dev)
+ return 0;
+ if (!dev->is_added)
nr++;
- if (dev && dev->multifunction) {
- for (fn = 1; fn < 8; fn++) {
- dev = pci_scan_single_device(bus, devfn + fn);
- if (dev) {
- if (!dev->is_added)
- nr++;
- dev->multifunction = 1;
- }
+ if (pci_ari_enabled(bus))
+ next_fn = next_ari_fn;
+ else if (dev->multifunction)
+ next_fn = next_trad_fn;
+
+ for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) {
+ dev = pci_scan_single_device(bus, devfn + fn);
+ if (dev) {
+ if (!dev->is_added)
+ nr++;
+ dev->multifunction = 1;
}
}
@@ -1200,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent,
if (error)
goto dev_reg_err;
b->bridge = get_device(dev);
+ device_enable_async_suspend(b->bridge);
if (!parent)
set_dev_node(b->bridge, pcibus_to_node(b));
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index d58b94030ef..790eb69a4aa 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -25,14 +25,9 @@
#include <linux/dmi.h>
#include <linux/pci-aspm.h>
#include <linux/ioport.h>
+#include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h"
-int isa_dma_bridge_buggy;
-EXPORT_SYMBOL(isa_dma_bridge_buggy);
-int pci_pci_problems;
-EXPORT_SYMBOL(pci_pci_problems);
-
-#ifdef CONFIG_PCI_QUIRKS
/*
* This quirk function disables memory decoding and releases memory resources
* of the device specified by kernel's boot parameter 'pci=resource_alignment='.
@@ -2612,6 +2607,7 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
}
pci_do_fixups(dev, start, end);
}
+EXPORT_SYMBOL(pci_fixup_device);
static int __init pci_apply_final_quirks(void)
{
@@ -2723,9 +2719,3 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)
return -ENOTTY;
}
-
-#else
-void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {}
-int pci_dev_specific_reset(struct pci_dev *dev, int probe) { return -ENOTTY; }
-#endif
-EXPORT_SYMBOL(pci_fixup_device);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index c48cd377b3f..4fe36d2e104 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -27,37 +27,91 @@
#include <linux/slab.h>
#include "pci.h"
-static void pbus_assign_resources_sorted(const struct pci_bus *bus)
-{
- struct pci_dev *dev;
+struct resource_list_x {
+ struct resource_list_x *next;
struct resource *res;
- struct resource_list head, *list, *tmp;
- int idx;
+ struct pci_dev *dev;
+ resource_size_t start;
+ resource_size_t end;
+ unsigned long flags;
+};
- head.next = NULL;
- list_for_each_entry(dev, &bus->devices, bus_list) {
- u16 class = dev->class >> 8;
+static void add_to_failed_list(struct resource_list_x *head,
+ struct pci_dev *dev, struct resource *res)
+{
+ struct resource_list_x *list = head;
+ struct resource_list_x *ln = list->next;
+ struct resource_list_x *tmp;
- /* Don't touch classless devices or host bridges or ioapics. */
- if (class == PCI_CLASS_NOT_DEFINED ||
- class == PCI_CLASS_BRIDGE_HOST)
- continue;
+ tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp) {
+ pr_warning("add_to_failed_list: kmalloc() failed!\n");
+ return;
+ }
- /* Don't touch ioapic devices already enabled by firmware */
- if (class == PCI_CLASS_SYSTEM_PIC) {
- u16 command;
- pci_read_config_word(dev, PCI_COMMAND, &command);
- if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
- continue;
- }
+ tmp->next = ln;
+ tmp->res = res;
+ tmp->dev = dev;
+ tmp->start = res->start;
+ tmp->end = res->end;
+ tmp->flags = res->flags;
+ list->next = tmp;
+}
+
+static void free_failed_list(struct resource_list_x *head)
+{
+ struct resource_list_x *list, *tmp;
- pdev_sort_resources(dev, &head);
+ for (list = head->next; list;) {
+ tmp = list;
+ list = list->next;
+ kfree(tmp);
}
- for (list = head.next; list;) {
+ head->next = NULL;
+}
+
+static void __dev_sort_resources(struct pci_dev *dev,
+ struct resource_list *head)
+{
+ u16 class = dev->class >> 8;
+
+ /* Don't touch classless devices or host bridges or ioapics. */
+ if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST)
+ return;
+
+ /* Don't touch ioapic devices already enabled by firmware */
+ if (class == PCI_CLASS_SYSTEM_PIC) {
+ u16 command;
+ pci_read_config_word(dev, PCI_COMMAND, &command);
+ if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+ return;
+ }
+
+ pdev_sort_resources(dev, head);
+}
+
+static void __assign_resources_sorted(struct resource_list *head,
+ struct resource_list_x *fail_head)
+{
+ struct resource *res;
+ struct resource_list *list, *tmp;
+ int idx;
+
+ for (list = head->next; list;) {
res = list->res;
idx = res - &list->dev->resource[0];
+
if (pci_assign_resource(list->dev, idx)) {
+ if (fail_head && !pci_is_root_bus(list->dev->bus)) {
+ /*
+ * if the failed res is for ROM BAR, and it will
+ * be enabled later, don't add it to the list
+ */
+ if (!((idx == PCI_ROM_RESOURCE) &&
+ (!(res->flags & IORESOURCE_ROM_ENABLE))))
+ add_to_failed_list(fail_head, list->dev, res);
+ }
res->start = 0;
res->end = 0;
res->flags = 0;
@@ -68,6 +122,30 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus)
}
}
+static void pdev_assign_resources_sorted(struct pci_dev *dev,
+ struct resource_list_x *fail_head)
+{
+ struct resource_list head;
+
+ head.next = NULL;
+ __dev_sort_resources(dev, &head);
+ __assign_resources_sorted(&head, fail_head);
+
+}
+
+static void pbus_assign_resources_sorted(const struct pci_bus *bus,
+ struct resource_list_x *fail_head)
+{
+ struct pci_dev *dev;
+ struct resource_list head;
+
+ head.next = NULL;
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ __dev_sort_resources(dev, &head);
+
+ __assign_resources_sorted(&head, fail_head);
+}
+
void pci_setup_cardbus(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
@@ -134,18 +212,12 @@ EXPORT_SYMBOL(pci_setup_cardbus);
config space writes, so it's quite possible that an I/O window of
the bridge will have some undesirable address (e.g. 0) after the
first write. Ditto 64-bit prefetchable MMIO. */
-static void pci_setup_bridge(struct pci_bus *bus)
+static void pci_setup_bridge_io(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct resource *res;
struct pci_bus_region region;
- u32 l, bu, lu, io_upper16;
-
- if (pci_is_enabled(bridge))
- return;
-
- dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n",
- bus->secondary, bus->subordinate);
+ u32 l, io_upper16;
/* Set up the top and bottom of the PCI I/O segment for this bus. */
res = bus->resource[0];
@@ -158,8 +230,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
/* Set up upper 16 bits of I/O base/limit. */
io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
dev_info(&bridge->dev, " bridge window %pR\n", res);
- }
- else {
+ } else {
/* Clear upper 16 bits of I/O base/limit. */
io_upper16 = 0;
l = 0x00f0;
@@ -171,21 +242,35 @@ static void pci_setup_bridge(struct pci_bus *bus)
pci_write_config_dword(bridge, PCI_IO_BASE, l);
/* Update upper 16 bits of I/O base/limit. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16);
+}
+
+static void pci_setup_bridge_mmio(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ struct resource *res;
+ struct pci_bus_region region;
+ u32 l;
- /* Set up the top and bottom of the PCI Memory segment
- for this bus. */
+ /* Set up the top and bottom of the PCI Memory segment for this bus. */
res = bus->resource[1];
pcibios_resource_to_bus(bridge, &region, res);
if (res->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
dev_info(&bridge->dev, " bridge window %pR\n", res);
- }
- else {
+ } else {
l = 0x0000fff0;
dev_info(&bridge->dev, " bridge window [mem disabled]\n");
}
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
+}
+
+static void pci_setup_bridge_mmio_pref(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ struct resource *res;
+ struct pci_bus_region region;
+ u32 l, bu, lu;
/* Clear out the upper 32 bits of PREF limit.
If PCI_PREF_BASE_UPPER32 was non-zero, this temporarily
@@ -204,8 +289,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
lu = upper_32_bits(region.end);
}
dev_info(&bridge->dev, " bridge window %pR\n", res);
- }
- else {
+ } else {
l = 0x0000fff0;
dev_info(&bridge->dev, " bridge window [mem pref disabled]\n");
}
@@ -214,10 +298,35 @@ static void pci_setup_bridge(struct pci_bus *bus)
/* Set the upper 32 bits of PREF base & limit. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
+}
+
+static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type)
+{
+ struct pci_dev *bridge = bus->self;
+
+ dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n",
+ bus->secondary, bus->subordinate);
+
+ if (type & IORESOURCE_IO)
+ pci_setup_bridge_io(bus);
+
+ if (type & IORESOURCE_MEM)
+ pci_setup_bridge_mmio(bus);
+
+ if (type & IORESOURCE_PREFETCH)
+ pci_setup_bridge_mmio_pref(bus);
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
}
+static void pci_setup_bridge(struct pci_bus *bus)
+{
+ unsigned long type = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+
+ __pci_setup_bridge(bus, type);
+}
+
/* Check whether the bridge supports optional I/O and
prefetchable memory ranges. If not, the respective
base/limit registers must be read-only and read as 0. */
@@ -253,8 +362,11 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
}
if (pmem) {
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
- if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64)
+ if ((pmem & PCI_PREF_RANGE_TYPE_MASK) ==
+ PCI_PREF_RANGE_TYPE_64) {
b_res[2].flags |= IORESOURCE_MEM_64;
+ b_res[2].flags |= PCI_PREF_RANGE_TYPE_64;
+ }
}
/* double check if bridge does support 64 bit pref */
@@ -283,8 +395,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- r = bus->resource[i];
+ pci_bus_for_each_resource(bus, r, i) {
if (r == &ioport_resource || r == &iomem_resource)
continue;
if (r && (r->flags & type_mask) == type && !r->parent)
@@ -301,7 +412,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size)
{
struct pci_dev *dev;
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
- unsigned long size = 0, size1 = 0;
+ unsigned long size = 0, size1 = 0, old_size;
if (!b_res)
return;
@@ -326,12 +437,17 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size)
}
if (size < min_size)
size = min_size;
+ old_size = resource_size(b_res);
+ if (old_size == 1)
+ old_size = 0;
/* To be fixed in 2.5: we should have sort of HAVE_ISA
flag in the struct pci_bus. */
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
size = (size & 0xff) + ((size & ~0xffUL) << 2);
#endif
size = ALIGN(size + size1, 4096);
+ if (size < old_size)
+ size = old_size;
if (!size) {
if (b_res->start || b_res->end)
dev_info(&bus->self->dev, "disabling bridge window "
@@ -352,7 +468,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
unsigned long type, resource_size_t min_size)
{
struct pci_dev *dev;
- resource_size_t min_align, align, size;
+ resource_size_t min_align, align, size, old_size;
resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */
int order, max_order;
struct resource *b_res = find_free_bus_resource(bus, type);
@@ -402,6 +518,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
}
if (size < min_size)
size = min_size;
+ old_size = resource_size(b_res);
+ if (old_size == 1)
+ old_size = 0;
+ if (size < old_size)
+ size = old_size;
align = 0;
min_align = 0;
@@ -538,23 +659,25 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
}
EXPORT_SYMBOL(pci_bus_size_bridges);
-void __ref pci_bus_assign_resources(const struct pci_bus *bus)
+static void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
+ struct resource_list_x *fail_head)
{
struct pci_bus *b;
struct pci_dev *dev;
- pbus_assign_resources_sorted(bus);
+ pbus_assign_resources_sorted(bus, fail_head);
list_for_each_entry(dev, &bus->devices, bus_list) {
b = dev->subordinate;
if (!b)
continue;
- pci_bus_assign_resources(b);
+ __pci_bus_assign_resources(b, fail_head);
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
- pci_setup_bridge(b);
+ if (!pci_is_enabled(dev))
+ pci_setup_bridge(b);
break;
case PCI_CLASS_BRIDGE_CARDBUS:
@@ -568,15 +691,130 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus)
}
}
}
+
+void __ref pci_bus_assign_resources(const struct pci_bus *bus)
+{
+ __pci_bus_assign_resources(bus, NULL);
+}
EXPORT_SYMBOL(pci_bus_assign_resources);
+static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge,
+ struct resource_list_x *fail_head)
+{
+ struct pci_bus *b;
+
+ pdev_assign_resources_sorted((struct pci_dev *)bridge, fail_head);
+
+ b = bridge->subordinate;
+ if (!b)
+ return;
+
+ __pci_bus_assign_resources(b, fail_head);
+
+ switch (bridge->class >> 8) {
+ case PCI_CLASS_BRIDGE_PCI:
+ pci_setup_bridge(b);
+ break;
+
+ case PCI_CLASS_BRIDGE_CARDBUS:
+ pci_setup_cardbus(b);
+ break;
+
+ default:
+ dev_info(&bridge->dev, "not setting up bridge for bus "
+ "%04x:%02x\n", pci_domain_nr(b), b->number);
+ break;
+ }
+}
+static void pci_bridge_release_resources(struct pci_bus *bus,
+ unsigned long type)
+{
+ int idx;
+ bool changed = false;
+ struct pci_dev *dev;
+ struct resource *r;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+
+ dev = bus->self;
+ for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END;
+ idx++) {
+ r = &dev->resource[idx];
+ if ((r->flags & type_mask) != type)
+ continue;
+ if (!r->parent)
+ continue;
+ /*
+ * if there are children under that, we should release them
+ * all
+ */
+ release_child_resources(r);
+ if (!release_resource(r)) {
+ dev_printk(KERN_DEBUG, &dev->dev,
+ "resource %d %pR released\n", idx, r);
+ /* keep the old size */
+ r->end = resource_size(r) - 1;
+ r->start = 0;
+ r->flags = 0;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ /* avoiding touch the one without PREF */
+ if (type & IORESOURCE_PREFETCH)
+ type = IORESOURCE_PREFETCH;
+ __pci_setup_bridge(bus, type);
+ }
+}
+
+enum release_type {
+ leaf_only,
+ whole_subtree,
+};
+/*
+ * try to release pci bridge resources that is from leaf bridge,
+ * so we can allocate big new one later
+ */
+static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus,
+ unsigned long type,
+ enum release_type rel_type)
+{
+ struct pci_dev *dev;
+ bool is_leaf_bridge = true;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ struct pci_bus *b = dev->subordinate;
+ if (!b)
+ continue;
+
+ is_leaf_bridge = false;
+
+ if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+ continue;
+
+ if (rel_type == whole_subtree)
+ pci_bus_release_bridge_resources(b, type,
+ whole_subtree);
+ }
+
+ if (pci_is_root_bus(bus))
+ return;
+
+ if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+ return;
+
+ if ((rel_type == whole_subtree) || is_leaf_bridge)
+ pci_bridge_release_resources(bus, type);
+}
+
static void pci_bus_dump_res(struct pci_bus *bus)
{
- int i;
+ struct resource *res;
+ int i;
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *res = bus->resource[i];
- if (!res || !res->end)
+ pci_bus_for_each_resource(bus, res, i) {
+ if (!res || !res->end || !res->flags)
continue;
dev_printk(KERN_DEBUG, &bus->dev, "resource %d %pR\n", i, res);
@@ -600,11 +838,65 @@ static void pci_bus_dump_resources(struct pci_bus *bus)
}
}
+static int __init pci_bus_get_depth(struct pci_bus *bus)
+{
+ int depth = 0;
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ int ret;
+ struct pci_bus *b = dev->subordinate;
+ if (!b)
+ continue;
+
+ ret = pci_bus_get_depth(b);
+ if (ret + 1 > depth)
+ depth = ret + 1;
+ }
+
+ return depth;
+}
+static int __init pci_get_max_depth(void)
+{
+ int depth = 0;
+ struct pci_bus *bus;
+
+ list_for_each_entry(bus, &pci_root_buses, node) {
+ int ret;
+
+ ret = pci_bus_get_depth(bus);
+ if (ret > depth)
+ depth = ret;
+ }
+
+ return depth;
+}
+
+/*
+ * first try will not touch pci bridge res
+ * second and later try will clear small leaf bridge res
+ * will stop till to the max deepth if can not find good one
+ */
void __init
pci_assign_unassigned_resources(void)
{
struct pci_bus *bus;
+ int tried_times = 0;
+ enum release_type rel_type = leaf_only;
+ struct resource_list_x head, *list;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+ unsigned long failed_type;
+ int max_depth = pci_get_max_depth();
+ int pci_try_num;
+
+ head.next = NULL;
+
+ pci_try_num = max_depth + 1;
+ printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
+ max_depth, pci_try_num);
+again:
/* Depth first, calculate sizes and alignments of all
subordinate buses. */
list_for_each_entry(bus, &pci_root_buses, node) {
@@ -612,12 +904,130 @@ pci_assign_unassigned_resources(void)
}
/* Depth last, allocate resources and update the hardware. */
list_for_each_entry(bus, &pci_root_buses, node) {
- pci_bus_assign_resources(bus);
- pci_enable_bridges(bus);
+ __pci_bus_assign_resources(bus, &head);
}
+ tried_times++;
+
+ /* any device complain? */
+ if (!head.next)
+ goto enable_and_dump;
+ failed_type = 0;
+ for (list = head.next; list;) {
+ failed_type |= list->flags;
+ list = list->next;
+ }
+ /*
+ * io port are tight, don't try extra
+ * or if reach the limit, don't want to try more
+ */
+ failed_type &= type_mask;
+ if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) {
+ free_failed_list(&head);
+ goto enable_and_dump;
+ }
+
+ printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n",
+ tried_times + 1);
+
+ /* third times and later will not check if it is leaf */
+ if ((tried_times + 1) > 2)
+ rel_type = whole_subtree;
+
+ /*
+ * Try to release leaf bridge's resources that doesn't fit resource of
+ * child device under that bridge
+ */
+ for (list = head.next; list;) {
+ bus = list->dev->bus;
+ pci_bus_release_bridge_resources(bus, list->flags & type_mask,
+ rel_type);
+ list = list->next;
+ }
+ /* restore size and flags */
+ for (list = head.next; list;) {
+ struct resource *res = list->res;
+
+ res->start = list->start;
+ res->end = list->end;
+ res->flags = list->flags;
+ if (list->dev->subordinate)
+ res->flags = 0;
+
+ list = list->next;
+ }
+ free_failed_list(&head);
+
+ goto again;
+
+enable_and_dump:
+ /* Depth last, update the hardware. */
+ list_for_each_entry(bus, &pci_root_buses, node)
+ pci_enable_bridges(bus);
/* dump the resource on buses */
list_for_each_entry(bus, &pci_root_buses, node) {
pci_bus_dump_resources(bus);
}
}
+
+void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
+{
+ struct pci_bus *parent = bridge->subordinate;
+ int tried_times = 0;
+ struct resource_list_x head, *list;
+ int retval;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+
+ head.next = NULL;
+
+again:
+ pci_bus_size_bridges(parent);
+ __pci_bridge_assign_resources(bridge, &head);
+ retval = pci_reenable_device(bridge);
+ pci_set_master(bridge);
+ pci_enable_bridges(parent);
+
+ tried_times++;
+
+ if (!head.next)
+ return;
+
+ if (tried_times >= 2) {
+ /* still fail, don't need to try more */
+ free_failed_list(&head);
+ return;
+ }
+
+ printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n",
+ tried_times + 1);
+
+ /*
+ * Try to release leaf bridge's resources that doesn't fit resource of
+ * child device under that bridge
+ */
+ for (list = head.next; list;) {
+ struct pci_bus *bus = list->dev->bus;
+ unsigned long flags = list->flags;
+
+ pci_bus_release_bridge_resources(bus, flags & type_mask,
+ whole_subtree);
+ list = list->next;
+ }
+ /* restore size and flags */
+ for (list = head.next; list;) {
+ struct resource *res = list->res;
+
+ res->start = list->start;
+ res->end = list->end;
+ res->flags = list->flags;
+ if (list->dev->subordinate)
+ res->flags = 0;
+
+ list = list->next;
+ }
+ free_failed_list(&head);
+
+ goto again;
+}
+EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 8c02b6c53bd..49c9e6c9779 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -47,6 +47,55 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf)
slot->number);
}
+/* these strings match up with the values in pci_bus_speed */
+static char *pci_bus_speed_strings[] = {
+ "33 MHz PCI", /* 0x00 */
+ "66 MHz PCI", /* 0x01 */
+ "66 MHz PCI-X", /* 0x02 */
+ "100 MHz PCI-X", /* 0x03 */
+ "133 MHz PCI-X", /* 0x04 */
+ NULL, /* 0x05 */
+ NULL, /* 0x06 */
+ NULL, /* 0x07 */
+ NULL, /* 0x08 */
+ "66 MHz PCI-X 266", /* 0x09 */
+ "100 MHz PCI-X 266", /* 0x0a */
+ "133 MHz PCI-X 266", /* 0x0b */
+ "Unknown AGP", /* 0x0c */
+ "1x AGP", /* 0x0d */
+ "2x AGP", /* 0x0e */
+ "4x AGP", /* 0x0f */
+ "8x AGP", /* 0x10 */
+ "66 MHz PCI-X 533", /* 0x11 */
+ "100 MHz PCI-X 533", /* 0x12 */
+ "133 MHz PCI-X 533", /* 0x13 */
+ "2.5 GT/s PCIe", /* 0x14 */
+ "5.0 GT/s PCIe", /* 0x15 */
+ "8.0 GT/s PCIe", /* 0x16 */
+};
+
+static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
+{
+ const char *speed_string;
+
+ if (speed < ARRAY_SIZE(pci_bus_speed_strings))
+ speed_string = pci_bus_speed_strings[speed];
+ else
+ speed_string = "Unknown";
+
+ return sprintf(buf, "%s\n", speed_string);
+}
+
+static ssize_t max_speed_read_file(struct pci_slot *slot, char *buf)
+{
+ return bus_speed_read(slot->bus->max_bus_speed, buf);
+}
+
+static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf)
+{
+ return bus_speed_read(slot->bus->cur_bus_speed, buf);
+}
+
static void pci_slot_release(struct kobject *kobj)
{
struct pci_dev *dev;
@@ -66,9 +115,15 @@ static void pci_slot_release(struct kobject *kobj)
static struct pci_slot_attribute pci_slot_attr_address =
__ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
+static struct pci_slot_attribute pci_slot_attr_max_speed =
+ __ATTR(max_bus_speed, (S_IFREG | S_IRUGO), max_speed_read_file, NULL);
+static struct pci_slot_attribute pci_slot_attr_cur_speed =
+ __ATTR(cur_bus_speed, (S_IFREG | S_IRUGO), cur_speed_read_file, NULL);
static struct attribute *pci_slot_default_attrs[] = {
&pci_slot_attr_address.attr,
+ &pci_slot_attr_max_speed.attr,
+ &pci_slot_attr_cur_speed.attr,
NULL,
};
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 9f3adbd9f70..0a6601c7680 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -84,7 +84,7 @@ config YENTA
tristate "CardBus yenta-compatible bridge support"
depends on PCI
select CARDBUS if !EMBEDDED
- select PCCARD_NONSTATIC
+ select PCCARD_NONSTATIC if PCMCIA != n
---help---
This option enables support for CardBus host bridges. Virtually
all modern PCMCIA bridges are CardBus compatible. A "bridge" is
@@ -161,9 +161,8 @@ config TCIC
config PCMCIA_M8XX
tristate "MPC8xx PCMCIA support"
- depends on PCMCIA && PPC && 8xx
- select PCCARD_IODYN
- select PCCARD_NONSTATIC
+ depends on PCCARD && PPC && 8xx
+ select PCCARD_IODYN if PCMCIA != n
help
Say Y here to include support for PowerPC 8xx series PCMCIA
controller.
@@ -174,6 +173,27 @@ config PCMCIA_AU1X00
tristate "Au1x00 pcmcia support"
depends on SOC_AU1X00 && PCMCIA
+config PCMCIA_ALCHEMY_DEVBOARD
+ tristate "Alchemy Db/Pb1xxx PCMCIA socket services"
+ depends on SOC_AU1X00 && PCMCIA
+ select 64BIT_PHYS_ADDR
+ help
+ Enable this driver of you want PCMCIA support on your Alchemy
+ Db1000, Db/Pb1100, Db/Pb1500, Db/Pb1550, Db/Pb1200 board.
+ NOT suitable for the PB1000!
+
+ This driver is also available as a module called db1xxx_ss.ko
+
+config PCMCIA_XXS1500
+ tristate "MyCable XXS1500 PCMCIA socket support"
+ depends on PCMCIA && MIPS_XXS1500
+ select 64BIT_PHYS_ADDR
+ help
+ Support for the PCMCIA/CF socket interface on MyCable XXS1500
+ systems.
+
+ This driver is also available as a module called xxs1500_ss.ko
+
config PCMCIA_BCM63XX
tristate "bcm63xx pcmcia support"
depends on BCM63XX && PCMCIA
@@ -238,14 +258,12 @@ config PCMCIA_PROBE
config M32R_PCC
bool "M32R PCMCIA I/F"
depends on M32R && CHIP_M32700 && PCMCIA
- select PCCARD_NONSTATIC
help
Say Y here to use the M32R PCMCIA controller.
config M32R_CFC
bool "M32R CF I/F Controller"
depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT)
- select PCCARD_NONSTATIC
help
Say Y here to use the M32R CompactFlash controller.
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 83ff802de54..381b031d9d7 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -2,11 +2,11 @@
# Makefile for the kernel pcmcia subsystem (c/o David Hinds)
#
-pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
+pcmcia_core-y += cs.o rsrc_mgr.o socket_sysfs.o
pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
obj-$(CONFIG_PCCARD) += pcmcia_core.o
-pcmcia-y += ds.o pcmcia_resource.o
+pcmcia-y += ds.o pcmcia_resource.o cistpl.o
pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o
obj-$(CONFIG_PCMCIA) += pcmcia.o
@@ -35,18 +35,10 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o
obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o
obj-$(CONFIG_AT91_CF) += at91_cf.o
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
+obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o
au1x00_ss-y += au1000_generic.o
au1x00_ss-$(CONFIG_MIPS_PB1000) += au1000_pb1x00.o
-au1x00_ss-$(CONFIG_MIPS_PB1100) += au1000_pb1x00.o
-au1x00_ss-$(CONFIG_MIPS_PB1200) += au1000_db1x00.o
-au1x00_ss-$(CONFIG_MIPS_PB1500) += au1000_pb1x00.o
-au1x00_ss-$(CONFIG_MIPS_DB1000) += au1000_db1x00.o
-au1x00_ss-$(CONFIG_MIPS_DB1100) += au1000_db1x00.o
-au1x00_ss-$(CONFIG_MIPS_DB1200) += au1000_db1x00.o
-au1x00_ss-$(CONFIG_MIPS_DB1500) += au1000_db1x00.o
-au1x00_ss-$(CONFIG_MIPS_DB1550) += au1000_db1x00.o
-au1x00_ss-$(CONFIG_MIPS_XXS1500) += au1000_xxs1500.o
sa1111_cs-y += sa1111_generic.o
sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o
@@ -76,3 +68,5 @@ pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
+
+obj-$(CONFIG_PCMCIA_XXS1500) += xxs1500_ss.o
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
index e1dccedc596..5d228071ec6 100644
--- a/drivers/pcmcia/at91_cf.c
+++ b/drivers/pcmcia/at91_cf.c
@@ -52,8 +52,6 @@ struct at91_cf_socket {
unsigned long phys_baseaddr;
};
-#define SZ_2K (2 * SZ_1K)
-
static inline int at91_cf_present(struct at91_cf_socket *cf)
{
return !gpio_get_value(cf->board->det_pin);
diff --git a/drivers/pcmcia/au1000_db1x00.c b/drivers/pcmcia/au1000_db1x00.c
deleted file mode 100644
index c78d77fd7e3..00000000000
--- a/drivers/pcmcia/au1000_db1x00.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- *
- * Alchemy Semi Db1x00 boards specific pcmcia routines.
- *
- * Copyright 2002 MontaVista Software Inc.
- * Author: MontaVista Software, Inc.
- * ppopov@mvista.com or source@mvista.com
- *
- * Copyright 2004 Pete Popov, updated the driver to 2.6.
- * Followed the sa11xx API and largely copied many of the hardware
- * independent functions.
- *
- * ########################################################################
- *
- * This program is free software; you can distribute 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 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/kernel.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/init.h>
-
-#include <asm/irq.h>
-#include <asm/signal.h>
-#include <asm/mach-au1x00/au1000.h>
-
-#if defined(CONFIG_MIPS_DB1200)
- #include <db1200.h>
-#elif defined(CONFIG_MIPS_PB1200)
- #include <pb1200.h>
-#else
- #include <asm/mach-db1x00/db1x00.h>
- static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
-#endif
-
-#include "au1000_generic.h"
-
-#if 0
-#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
-#else
-#define debug(x,args...)
-#endif
-
-
-struct au1000_pcmcia_socket au1000_pcmcia_socket[PCMCIA_NUM_SOCKS];
-extern int au1x00_pcmcia_socket_probe(struct device *, struct pcmcia_low_level *, int, int);
-
-static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt)
-{
-#ifdef CONFIG_MIPS_DB1550
- skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_3;
-#elif defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200)
- skt->irq = skt->nr ? BOARD_PC1_INT : BOARD_PC0_INT;
-#else
- skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_2;
-#endif
- return 0;
-}
-
-static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt)
-{
- bcsr->pcmcia = 0; /* turn off power */
- au_sync_delay(2);
-}
-
-static void
-db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state *state)
-{
- u32 inserted;
- unsigned char vs;
-
- state->ready = 0;
- state->vs_Xv = 0;
- state->vs_3v = 0;
- state->detect = 0;
-
- switch (skt->nr) {
- case 0:
- vs = bcsr->status & 0x3;
-#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200)
- inserted = BOARD_CARD_INSERTED(0);
-#else
- inserted = !(bcsr->status & (1<<4));
-#endif
- break;
- case 1:
- vs = (bcsr->status & 0xC)>>2;
-#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200)
- inserted = BOARD_CARD_INSERTED(1);
-#else
- inserted = !(bcsr->status & (1<<5));
-#endif
- break;
- default:/* should never happen */
- return;
- }
-
- if (inserted)
- debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n",
- skt->nr, inserted, vs, bcsr->pcmcia);
-
- if (inserted) {
- switch (vs) {
- case 0:
- case 2:
- state->vs_3v=1;
- break;
- case 3: /* 5V */
- break;
- default:
- /* return without setting 'detect' */
- printk(KERN_ERR "db1x00 bad VS (%d)\n",
- vs);
- }
- state->detect = 1;
- state->ready = 1;
- }
- else {
- /* if the card was previously inserted and then ejected,
- * we should turn off power to it
- */
- if ((skt->nr == 0) && (bcsr->pcmcia & BCSR_PCMCIA_PC0RST)) {
- bcsr->pcmcia &= ~(BCSR_PCMCIA_PC0RST |
- BCSR_PCMCIA_PC0DRVEN |
- BCSR_PCMCIA_PC0VPP |
- BCSR_PCMCIA_PC0VCC);
- au_sync_delay(10);
- }
- else if ((skt->nr == 1) && bcsr->pcmcia & BCSR_PCMCIA_PC1RST) {
- bcsr->pcmcia &= ~(BCSR_PCMCIA_PC1RST |
- BCSR_PCMCIA_PC1DRVEN |
- BCSR_PCMCIA_PC1VPP |
- BCSR_PCMCIA_PC1VCC);
- au_sync_delay(10);
- }
- }
-
- state->bvd1=1;
- state->bvd2=1;
- state->wrprot=0;
-}
-
-static int
-db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_state_t *state)
-{
- u16 pwr;
- int sock = skt->nr;
-
- debug("config_skt %d Vcc %dV Vpp %dV, reset %d\n",
- sock, state->Vcc, state->Vpp,
- state->flags & SS_RESET);
-
- /* pcmcia reg was set to zero at init time. Be careful when
- * initializing a socket not to wipe out the settings of the
- * other socket.
- */
- pwr = bcsr->pcmcia;
- pwr &= ~(0xf << sock*8); /* clear voltage settings */
-
- state->Vpp = 0;
- switch(state->Vcc){
- case 0: /* Vcc 0 */
- pwr |= SET_VCC_VPP(0,0,sock);
- break;
- case 50: /* Vcc 5V */
- switch(state->Vpp) {
- case 0:
- pwr |= SET_VCC_VPP(2,0,sock);
- break;
- case 50:
- pwr |= SET_VCC_VPP(2,1,sock);
- break;
- case 12:
- pwr |= SET_VCC_VPP(2,2,sock);
- break;
- case 33:
- default:
- pwr |= SET_VCC_VPP(0,0,sock);
- printk("%s: bad Vcc/Vpp (%d:%d)\n",
- __func__,
- state->Vcc,
- state->Vpp);
- break;
- }
- break;
- case 33: /* Vcc 3.3V */
- switch(state->Vpp) {
- case 0:
- pwr |= SET_VCC_VPP(1,0,sock);
- break;
- case 12:
- pwr |= SET_VCC_VPP(1,2,sock);
- break;
- case 33:
- pwr |= SET_VCC_VPP(1,1,sock);
- break;
- case 50:
- default:
- pwr |= SET_VCC_VPP(0,0,sock);
- printk("%s: bad Vcc/Vpp (%d:%d)\n",
- __func__,
- state->Vcc,
- state->Vpp);
- break;
- }
- break;
- default: /* what's this ? */
- pwr |= SET_VCC_VPP(0,0,sock);
- printk(KERN_ERR "%s: bad Vcc %d\n",
- __func__, state->Vcc);
- break;
- }
-
- bcsr->pcmcia = pwr;
- au_sync_delay(300);
-
- if (sock == 0) {
- if (!(state->flags & SS_RESET)) {
- pwr |= BCSR_PCMCIA_PC0DRVEN;
- bcsr->pcmcia = pwr;
- au_sync_delay(300);
- pwr |= BCSR_PCMCIA_PC0RST;
- bcsr->pcmcia = pwr;
- au_sync_delay(100);
- }
- else {
- pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN);
- bcsr->pcmcia = pwr;
- au_sync_delay(100);
- }
- }
- else {
- if (!(state->flags & SS_RESET)) {
- pwr |= BCSR_PCMCIA_PC1DRVEN;
- bcsr->pcmcia = pwr;
- au_sync_delay(300);
- pwr |= BCSR_PCMCIA_PC1RST;
- bcsr->pcmcia = pwr;
- au_sync_delay(100);
- }
- else {
- pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN);
- bcsr->pcmcia = pwr;
- au_sync_delay(100);
- }
- }
- return 0;
-}
-
-/*
- * Enable card status IRQs on (re-)initialisation. This can
- * be called at initialisation, power management event, or
- * pcmcia event.
- */
-void db1x00_socket_init(struct au1000_pcmcia_socket *skt)
-{
- /* nothing to do for now */
-}
-
-/*
- * Disable card status IRQs and PCMCIA bus on suspend.
- */
-void db1x00_socket_suspend(struct au1000_pcmcia_socket *skt)
-{
- /* nothing to do for now */
-}
-
-struct pcmcia_low_level db1x00_pcmcia_ops = {
- .owner = THIS_MODULE,
-
- .hw_init = db1x00_pcmcia_hw_init,
- .hw_shutdown = db1x00_pcmcia_shutdown,
-
- .socket_state = db1x00_pcmcia_socket_state,
- .configure_socket = db1x00_pcmcia_configure_socket,
-
- .socket_init = db1x00_socket_init,
- .socket_suspend = db1x00_socket_suspend
-};
-
-int au1x_board_init(struct device *dev)
-{
- int ret = -ENODEV;
- bcsr->pcmcia = 0; /* turn off power, if it's not already off */
- au_sync_delay(2);
- ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2);
- return ret;
-}
diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c
index 02088704ac2..171c8a65488 100644
--- a/drivers/pcmcia/au1000_generic.c
+++ b/drivers/pcmcia/au1000_generic.c
@@ -405,18 +405,16 @@ int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops,
skt->virt_io = (void *)
(ioremap((phys_t)AU1X_SOCK0_IO, 0x1000) -
(u32)mips_io_port_base);
- skt->phys_attr = AU1X_SOCK0_PSEUDO_PHYS_ATTR;
- skt->phys_mem = AU1X_SOCK0_PSEUDO_PHYS_MEM;
+ skt->phys_attr = AU1X_SOCK0_PHYS_ATTR;
+ skt->phys_mem = AU1X_SOCK0_PHYS_MEM;
}
-#ifndef CONFIG_MIPS_XXS1500
else {
skt->virt_io = (void *)
(ioremap((phys_t)AU1X_SOCK1_IO, 0x1000) -
(u32)mips_io_port_base);
- skt->phys_attr = AU1X_SOCK1_PSEUDO_PHYS_ATTR;
- skt->phys_mem = AU1X_SOCK1_PSEUDO_PHYS_MEM;
+ skt->phys_attr = AU1X_SOCK1_PHYS_ATTR;
+ skt->phys_mem = AU1X_SOCK1_PHYS_MEM;
}
-#endif
pcmcia_base_vaddrs[i] = (u32 *)skt->virt_io;
ret = ops->hw_init(skt);
diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h
index 13a4fbc5871..a324d329dea 100644
--- a/drivers/pcmcia/au1000_generic.h
+++ b/drivers/pcmcia/au1000_generic.h
@@ -36,30 +36,14 @@
#define AU1X_SOCK0_IO 0xF00000000ULL
#define AU1X_SOCK0_PHYS_ATTR 0xF40000000ULL
#define AU1X_SOCK0_PHYS_MEM 0xF80000000ULL
-/* pseudo 32 bit phys addresses, which get fixed up to the
- * real 36 bit address in fixup_bigphys_addr() */
-#define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000
-#define AU1X_SOCK0_PSEUDO_PHYS_MEM 0xF8000000
/* pcmcia socket 1 needs external glue logic so the memory map
* differs from board to board.
*/
-#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || \
- defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || \
- defined(CONFIG_MIPS_PB1200)
+#if defined(CONFIG_MIPS_PB1000)
#define AU1X_SOCK1_IO 0xF08000000ULL
#define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL
#define AU1X_SOCK1_PHYS_MEM 0xF88000000ULL
-#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000
-#define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8800000
-#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || \
- defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || \
- defined(CONFIG_MIPS_DB1200)
-#define AU1X_SOCK1_IO 0xF04000000ULL
-#define AU1X_SOCK1_PHYS_ATTR 0xF44000000ULL
-#define AU1X_SOCK1_PHYS_MEM 0xF84000000ULL
-#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000
-#define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8400000
#endif
struct pcmcia_state {
diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c
index b1984ed72d1..5a979cb8f3e 100644
--- a/drivers/pcmcia/au1000_pb1x00.c
+++ b/drivers/pcmcia/au1000_pb1x00.c
@@ -1,6 +1,6 @@
/*
*
- * Alchemy Semi Pb1x00 boards specific pcmcia routines.
+ * Alchemy Semi Pb1000 boards specific pcmcia routines.
*
* Copyright 2002 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
@@ -46,20 +46,11 @@
#define debug(fmt, arg...) do { } while (0)
-#ifdef CONFIG_MIPS_PB1000
#include <asm/pb1000.h>
#define PCMCIA_IRQ AU1000_GPIO_15
-#elif defined (CONFIG_MIPS_PB1500)
-#include <asm/pb1500.h>
-#define PCMCIA_IRQ AU1500_GPIO_203
-#elif defined (CONFIG_MIPS_PB1100)
-#include <asm/pb1100.h>
-#define PCMCIA_IRQ AU1000_GPIO_11
-#endif
static int pb1x00_pcmcia_init(struct pcmcia_init *init)
{
-#ifdef CONFIG_MIPS_PB1000
u16 pcr;
pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST;
@@ -74,21 +65,10 @@ static int pb1x00_pcmcia_init(struct pcmcia_init *init)
au_sync_delay(20);
return PCMCIA_NUM_SOCKS;
-
-#else /* fixme -- take care of the Pb1500 at some point */
-
- u16 pcr;
- pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */
- pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN);
- au_writew(pcr, PCMCIA_BOARD_REG);
- au_sync_delay(500);
- return PCMCIA_NUM_SOCKS;
-#endif
}
static int pb1x00_pcmcia_shutdown(void)
{
-#ifdef CONFIG_MIPS_PB1000
u16 pcr;
pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST;
pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0);
@@ -96,14 +76,6 @@ static int pb1x00_pcmcia_shutdown(void)
au_writel(pcr, PB1000_PCR);
au_sync_delay(20);
return 0;
-#else
- u16 pcr;
- pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */
- pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN);
- au_writew(pcr, PCMCIA_BOARD_REG);
- au_sync_delay(2);
- return 0;
-#endif
}
static int
@@ -112,21 +84,11 @@ pb1x00_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
u32 inserted0, inserted1;
u16 vs0, vs1;
-#ifdef CONFIG_MIPS_PB1000
vs0 = vs1 = (u16)au_readl(PB1000_ACR1);
inserted0 = !(vs0 & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2));
inserted1 = !(vs1 & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2));
vs0 = (vs0 >> 4) & 0x3;
vs1 = (vs1 >> 12) & 0x3;
-#else
- vs0 = (au_readw(BOARD_STATUS_REG) >> 4) & 0x3;
-#ifdef CONFIG_MIPS_PB1500
- inserted0 = !((au_readl(GPIO2_PINSTATE) >> 1) & 0x1); /* gpio 201 */
-#else /* Pb1100 */
- inserted0 = !((au_readl(SYS_PINSTATERD) >> 9) & 0x1); /* gpio 9 */
-#endif
- inserted1 = 0;
-#endif
state->ready = 0;
state->vs_Xv = 0;
@@ -203,7 +165,6 @@ pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure)
if(configure->sock > PCMCIA_MAX_SOCK) return -1;
-#ifdef CONFIG_MIPS_PB1000
pcr = au_readl(PB1000_PCR);
if (configure->sock == 0) {
@@ -323,84 +284,6 @@ pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure)
au_writel(pcr, PB1000_PCR);
au_sync_delay(300);
-#else
-
- pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf;
-
- debug("Vcc %dV Vpp %dV, pcr %x, reset %d\n",
- configure->vcc, configure->vpp, pcr, configure->reset);
-
-
- switch(configure->vcc){
- case 0: /* Vcc 0 */
- pcr |= SET_VCC_VPP(0,0);
- break;
- case 50: /* Vcc 5V */
- switch(configure->vpp) {
- case 0:
- pcr |= SET_VCC_VPP(2,0);
- break;
- case 50:
- pcr |= SET_VCC_VPP(2,1);
- break;
- case 12:
- pcr |= SET_VCC_VPP(2,2);
- break;
- case 33:
- default:
- pcr |= SET_VCC_VPP(0,0);
- printk("%s: bad Vcc/Vpp (%d:%d)\n",
- __func__,
- configure->vcc,
- configure->vpp);
- break;
- }
- break;
- case 33: /* Vcc 3.3V */
- switch(configure->vpp) {
- case 0:
- pcr |= SET_VCC_VPP(1,0);
- break;
- case 12:
- pcr |= SET_VCC_VPP(1,2);
- break;
- case 33:
- pcr |= SET_VCC_VPP(1,1);
- break;
- case 50:
- default:
- pcr |= SET_VCC_VPP(0,0);
- printk("%s: bad Vcc/Vpp (%d:%d)\n",
- __func__,
- configure->vcc,
- configure->vpp);
- break;
- }
- break;
- default: /* what's this ? */
- pcr |= SET_VCC_VPP(0,0);
- printk(KERN_ERR "%s: bad Vcc %d\n",
- __func__, configure->vcc);
- break;
- }
-
- au_writew(pcr, PCMCIA_BOARD_REG);
- au_sync_delay(300);
-
- if (!configure->reset) {
- pcr |= PC_DRV_EN;
- au_writew(pcr, PCMCIA_BOARD_REG);
- au_sync_delay(100);
- pcr |= PC_DEASSERT_RST;
- au_writew(pcr, PCMCIA_BOARD_REG);
- au_sync_delay(100);
- }
- else {
- pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN);
- au_writew(pcr, PCMCIA_BOARD_REG);
- au_sync_delay(100);
- }
-#endif
return 0;
}
diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c
deleted file mode 100644
index b43d47b5081..00000000000
--- a/drivers/pcmcia/au1000_xxs1500.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- *
- * MyCable board specific pcmcia routines.
- *
- * Copyright 2003 MontaVista Software Inc.
- * Author: Pete Popov, MontaVista Software, Inc.
- * ppopov@mvista.com or source@mvista.com
- *
- * ########################################################################
- *
- * This program is free software; you can distribute 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 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/delay.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/proc_fs.h>
-#include <linux/types.h>
-
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/ss.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/bus_ops.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include <asm/au1000.h>
-#include <asm/au1000_pcmcia.h>
-
-#define PCMCIA_MAX_SOCK 0
-#define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK + 1)
-#define PCMCIA_IRQ AU1000_GPIO_4
-
-#if 0
-#define DEBUG(x, args...) printk(__func__ ": " x, ##args)
-#else
-#define DEBUG(x,args...)
-#endif
-
-static int xxs1500_pcmcia_init(struct pcmcia_init *init)
-{
- return PCMCIA_NUM_SOCKS;
-}
-
-static int xxs1500_pcmcia_shutdown(void)
-{
- /* turn off power */
- au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30),
- GPIO2_OUTPUT);
- au_sync_delay(100);
-
- /* assert reset */
- au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20),
- GPIO2_OUTPUT);
- au_sync_delay(100);
- return 0;
-}
-
-
-static int
-xxs1500_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
-{
- u32 inserted; u32 vs;
- unsigned long gpio, gpio2;
-
- if(sock > PCMCIA_MAX_SOCK) return -1;
-
- gpio = au_readl(SYS_PINSTATERD);
- gpio2 = au_readl(GPIO2_PINSTATE);
-
- vs = gpio2 & ((1<<8) | (1<<9));
- inserted = (!(gpio & 0x1) && !(gpio & 0x2));
-
- state->ready = 0;
- state->vs_Xv = 0;
- state->vs_3v = 0;
- state->detect = 0;
-
- if (inserted) {
- switch (vs) {
- case 0:
- case 1:
- case 2:
- state->vs_3v=1;
- break;
- case 3: /* 5V */
- default:
- /* return without setting 'detect' */
- printk(KERN_ERR "au1x00_cs: unsupported VS\n",
- vs);
- return;
- }
- state->detect = 1;
- }
-
- if (state->detect) {
- state->ready = 1;
- }
-
- state->bvd1= gpio2 & (1<<10);
- state->bvd2 = gpio2 & (1<<11);
- state->wrprot=0;
- return 1;
-}
-
-
-static int xxs1500_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
-{
-
- if(info->sock > PCMCIA_MAX_SOCK) return -1;
- info->irq = PCMCIA_IRQ;
- return 0;
-}
-
-
-static int
-xxs1500_pcmcia_configure_socket(const struct pcmcia_configure *configure)
-{
-
- if(configure->sock > PCMCIA_MAX_SOCK) return -1;
-
- DEBUG("Vcc %dV Vpp %dV, reset %d\n",
- configure->vcc, configure->vpp, configure->reset);
-
- switch(configure->vcc){
- case 33: /* Vcc 3.3V */
- /* turn on power */
- DEBUG("turn on power\n");
- au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<14))|(1<<30),
- GPIO2_OUTPUT);
- au_sync_delay(100);
- break;
- case 50: /* Vcc 5V */
- default: /* what's this ? */
- printk(KERN_ERR "au1x00_cs: unsupported VCC\n");
- case 0: /* Vcc 0 */
- /* turn off power */
- au_sync_delay(100);
- au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30),
- GPIO2_OUTPUT);
- break;
- }
-
- if (!configure->reset) {
- DEBUG("deassert reset\n");
- au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<4))|(1<<20),
- GPIO2_OUTPUT);
- au_sync_delay(100);
- au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<5))|(1<<21),
- GPIO2_OUTPUT);
- }
- else {
- DEBUG("assert reset\n");
- au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20),
- GPIO2_OUTPUT);
- }
- au_sync_delay(100);
- return 0;
-}
-
-struct pcmcia_low_level xxs1500_pcmcia_ops = {
- xxs1500_pcmcia_init,
- xxs1500_pcmcia_shutdown,
- xxs1500_pcmcia_socket_state,
- xxs1500_pcmcia_get_irq_info,
- xxs1500_pcmcia_configure_socket
-};
diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c
index 300b368605c..2482ce7ac6d 100644
--- a/drivers/pcmcia/bfin_cf_pcmcia.c
+++ b/drivers/pcmcia/bfin_cf_pcmcia.c
@@ -205,7 +205,7 @@ static int __devinit bfin_cf_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
irq = platform_get_irq(pdev, 0);
- if (!irq)
+ if (irq <= 0)
return -EINVAL;
cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index d99f846451a..ac0686efbf7 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -20,170 +20,12 @@
*/
-#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <asm/irq.h>
-#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include "cs_internal.h"
-
-/*====================================================================*/
-
-/* Offsets in the Expansion ROM Image Header */
-#define ROM_SIGNATURE 0x0000 /* 2 bytes */
-#define ROM_DATA_PTR 0x0018 /* 2 bytes */
-
-/* Offsets in the CardBus PC Card Data Structure */
-#define PCDATA_SIGNATURE 0x0000 /* 4 bytes */
-#define PCDATA_VPD_PTR 0x0008 /* 2 bytes */
-#define PCDATA_LENGTH 0x000a /* 2 bytes */
-#define PCDATA_REVISION 0x000c
-#define PCDATA_IMAGE_SZ 0x0010 /* 2 bytes */
-#define PCDATA_ROM_LEVEL 0x0012 /* 2 bytes */
-#define PCDATA_CODE_TYPE 0x0014
-#define PCDATA_INDICATOR 0x0015
-
-/*=====================================================================
-
- Expansion ROM's have a special layout, and pointers specify an
- image number and an offset within that image. xlate_rom_addr()
- converts an image/offset address to an absolute offset from the
- ROM's base address.
-
-=====================================================================*/
-
-static u_int xlate_rom_addr(void __iomem *b, u_int addr)
-{
- u_int img = 0, ofs = 0, sz;
- u_short data;
- while ((readb(b) == 0x55) && (readb(b + 1) == 0xaa)) {
- if (img == (addr >> 28))
- return (addr & 0x0fffffff) + ofs;
- data = readb(b + ROM_DATA_PTR) + (readb(b + ROM_DATA_PTR + 1) << 8);
- sz = 512 * (readb(b + data + PCDATA_IMAGE_SZ) +
- (readb(b + data + PCDATA_IMAGE_SZ + 1) << 8));
- if ((sz == 0) || (readb(b + data + PCDATA_INDICATOR) & 0x80))
- break;
- b += sz;
- ofs += sz;
- img++;
- }
- return 0;
-}
-
-/*=====================================================================
-
- These are similar to setup_cis_mem and release_cis_mem for 16-bit
- cards. The "result" that is used externally is the cb_cis_virt
- pointer in the struct pcmcia_socket structure.
-
-=====================================================================*/
-
-static void cb_release_cis_mem(struct pcmcia_socket *s)
-{
- if (s->cb_cis_virt) {
- dev_dbg(&s->dev, "cb_release_cis_mem()\n");
- iounmap(s->cb_cis_virt);
- s->cb_cis_virt = NULL;
- s->cb_cis_res = NULL;
- }
-}
-
-static int cb_setup_cis_mem(struct pcmcia_socket *s, struct resource *res)
-{
- unsigned int start, size;
-
- if (res == s->cb_cis_res)
- return 0;
-
- if (s->cb_cis_res)
- cb_release_cis_mem(s);
-
- start = res->start;
- size = res->end - start + 1;
- s->cb_cis_virt = ioremap(start, size);
-
- if (!s->cb_cis_virt)
- return -1;
-
- s->cb_cis_res = res;
-
- return 0;
-}
-
-/*=====================================================================
-
- This is used by the CIS processing code to read CIS information
- from a CardBus device.
-
-=====================================================================*/
-
-int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len,
- void *ptr)
-{
- struct pci_dev *dev;
- struct resource *res;
-
- 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)
- goto fail;
-
- /* Config space? */
- if (space == 0) {
- if (addr + len > 0x100)
- goto failput;
- for (; len; addr++, ptr++, len--)
- pci_read_config_byte(dev, addr, ptr);
- return 0;
- }
-
- res = dev->resource + space - 1;
-
- pci_dev_put(dev);
-
- if (!res->flags)
- goto fail;
-
- if (cb_setup_cis_mem(s, res) != 0)
- goto fail;
-
- if (space == 7) {
- addr = xlate_rom_addr(s->cb_cis_virt, addr);
- if (addr == 0)
- goto fail;
- }
-
- if (addr + len > res->end - res->start)
- goto fail;
-
- memcpy_fromio(ptr, s->cb_cis_virt + addr, len);
- return 0;
-
-failput:
- pci_dev_put(dev);
-fail:
- memset(ptr, 0xff, len);
- return -1;
-}
-
-/*=====================================================================
-
- cb_alloc() and cb_free() allocate and free the kernel data
- structures for a Cardbus device, and handle the lowest level PCI
- device setup issues.
-
-=====================================================================*/
static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
{
@@ -215,6 +57,13 @@ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
}
}
+/**
+ * cb_alloc() - add CardBus device
+ * @s: the pcmcia_socket where the CardBus device is located
+ *
+ * cb_alloc() allocates the kernel data structures for a Cardbus device
+ * and handles the lowest level PCI device setup issues.
+ */
int __ref cb_alloc(struct pcmcia_socket *s)
{
struct pci_bus *bus = s->cb_dev->subordinate;
@@ -249,12 +98,16 @@ int __ref cb_alloc(struct pcmcia_socket *s)
return 0;
}
+/**
+ * cb_free() - remove CardBus device
+ * @s: the pcmcia_socket where the CardBus device was located
+ *
+ * cb_free() handles the lowest level PCI device cleanup.
+ */
void cb_free(struct pcmcia_socket *s)
{
struct pci_dev *bridge = s->cb_dev;
- cb_release_cis_mem(s);
-
if (bridge)
pci_remove_behind_bridge(bridge);
}
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 25b1cd219e3..2f3622dd4b6 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -64,6 +64,7 @@ module_param(cis_width, int, 0444);
void release_cis_mem(struct pcmcia_socket *s)
{
+ mutex_lock(&s->ops_mutex);
if (s->cis_mem.flags & MAP_ACTIVE) {
s->cis_mem.flags &= ~MAP_ACTIVE;
s->ops->set_mem_map(s, &s->cis_mem);
@@ -75,13 +76,15 @@ void release_cis_mem(struct pcmcia_socket *s)
iounmap(s->cis_virt);
s->cis_virt = NULL;
}
+ mutex_unlock(&s->ops_mutex);
}
-EXPORT_SYMBOL(release_cis_mem);
/*
* Map the card memory at "card_offset" into virtual space.
* If flags & MAP_ATTRIB, map the attribute space, otherwise
* map the memory space.
+ *
+ * Must be called with ops_mutex held.
*/
static void __iomem *
set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
@@ -140,6 +143,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ mutex_lock(&s->ops_mutex);
if (attr & IS_INDIRECT) {
/* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
@@ -151,7 +155,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
if (!sys) {
+ dev_dbg(&s->dev, "could not map memory\n");
memset(ptr, 0xff, len);
+ mutex_unlock(&s->ops_mutex);
return -1;
}
@@ -165,6 +171,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
} else {
u_int inc = 1, card_offset, flags;
+ if (addr > CISTPL_MAX_CIS_SIZE)
+ dev_dbg(&s->dev, "attempt to read CIS mem at addr %#x", addr);
+
flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
if (attr) {
flags |= MAP_ATTRIB;
@@ -176,7 +185,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
while (len) {
sys = set_cis_map(s, card_offset, flags);
if (!sys) {
+ dev_dbg(&s->dev, "could not map memory\n");
memset(ptr, 0xff, len);
+ mutex_unlock(&s->ops_mutex);
return -1;
}
end = sys + s->map_size;
@@ -190,12 +201,12 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
addr = 0;
}
}
+ mutex_unlock(&s->ops_mutex);
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;
}
-EXPORT_SYMBOL(pcmcia_read_cis_mem);
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
@@ -206,6 +217,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ mutex_lock(&s->ops_mutex);
if (attr & IS_INDIRECT) {
/* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
@@ -216,8 +228,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
}
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
- if (!sys)
+ if (!sys) {
+ dev_dbg(&s->dev, "could not map memory\n");
+ mutex_unlock(&s->ops_mutex);
return; /* FIXME: Error */
+ }
writeb(flags, sys+CISREG_ICTRL0);
writeb(addr & 0xff, sys+CISREG_IADDR0);
@@ -239,8 +254,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
card_offset = addr & ~(s->map_size-1);
while (len) {
sys = set_cis_map(s, card_offset, flags);
- if (!sys)
+ if (!sys) {
+ dev_dbg(&s->dev, "could not map memory\n");
+ mutex_unlock(&s->ops_mutex);
return; /* FIXME: error */
+ }
end = sys + s->map_size;
sys = sys + (addr & (s->map_size-1));
@@ -253,8 +271,8 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
addr = 0;
}
}
+ mutex_unlock(&s->ops_mutex);
}
-EXPORT_SYMBOL(pcmcia_write_cis_mem);
/*======================================================================
@@ -265,32 +283,36 @@ EXPORT_SYMBOL(pcmcia_write_cis_mem);
======================================================================*/
-static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
- size_t len, void *ptr)
+static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
+ size_t len, void *ptr)
{
- struct cis_cache_entry *cis;
- int ret;
+ struct cis_cache_entry *cis;
+ int ret = 0;
- if (s->fake_cis) {
- if (s->fake_cis_len >= addr+len)
- memcpy(ptr, s->fake_cis+addr, len);
- else
- memset(ptr, 0xff, len);
- return;
- }
+ if (s->state & SOCKET_CARDBUS)
+ return -EINVAL;
- list_for_each_entry(cis, &s->cis_cache, node) {
- if (cis->addr == addr && cis->len == len && cis->attr == attr) {
- memcpy(ptr, cis->cache, len);
- return;
+ mutex_lock(&s->ops_mutex);
+ if (s->fake_cis) {
+ if (s->fake_cis_len >= addr+len)
+ memcpy(ptr, s->fake_cis+addr, len);
+ else {
+ memset(ptr, 0xff, len);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&s->ops_mutex);
+ return ret;
}
- }
-#ifdef CONFIG_CARDBUS
- if (s->state & SOCKET_CARDBUS)
- ret = read_cb_mem(s, attr, addr, len, ptr);
- else
-#endif
+ list_for_each_entry(cis, &s->cis_cache, node) {
+ if (cis->addr == addr && cis->len == len && cis->attr == attr) {
+ memcpy(ptr, cis->cache, len);
+ mutex_unlock(&s->ops_mutex);
+ return 0;
+ }
+ }
+ mutex_unlock(&s->ops_mutex);
+
ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
if (ret == 0) {
@@ -301,9 +323,12 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
cis->len = len;
cis->attr = attr;
memcpy(cis->cache, ptr, len);
+ mutex_lock(&s->ops_mutex);
list_add(&cis->node, &s->cis_cache);
+ mutex_unlock(&s->ops_mutex);
}
}
+ return ret;
}
static void
@@ -311,32 +336,35 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
{
struct cis_cache_entry *cis;
+ mutex_lock(&s->ops_mutex);
list_for_each_entry(cis, &s->cis_cache, node)
if (cis->addr == addr && cis->len == len && cis->attr == attr) {
list_del(&cis->node);
kfree(cis);
break;
}
+ mutex_unlock(&s->ops_mutex);
}
+/**
+ * destroy_cis_cache() - destroy the CIS cache
+ * @s: pcmcia_socket for which CIS cache shall be destroyed
+ *
+ * This destroys the CIS cache but keeps any fake CIS alive. Must be
+ * called with ops_mutex held.
+ */
+
void destroy_cis_cache(struct pcmcia_socket *s)
{
struct list_head *l, *n;
+ struct cis_cache_entry *cis;
list_for_each_safe(l, n, &s->cis_cache) {
- struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node);
-
+ cis = list_entry(l, struct cis_cache_entry, node);
list_del(&cis->node);
kfree(cis);
}
-
- /*
- * If there was a fake CIS, destroy that as well.
- */
- kfree(s->fake_cis);
- s->fake_cis = NULL;
}
-EXPORT_SYMBOL(destroy_cis_cache);
/*======================================================================
@@ -349,6 +377,10 @@ int verify_cis_cache(struct pcmcia_socket *s)
{
struct cis_cache_entry *cis;
char *buf;
+ int ret;
+
+ if (s->state & SOCKET_CARDBUS)
+ return -EINVAL;
buf = kmalloc(256, GFP_KERNEL);
if (buf == NULL) {
@@ -361,14 +393,9 @@ int verify_cis_cache(struct pcmcia_socket *s)
if (len > 256)
len = 256;
-#ifdef CONFIG_CARDBUS
- if (s->state & SOCKET_CARDBUS)
- read_cb_mem(s, cis->attr, cis->addr, len, buf);
- else
-#endif
- pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
- if (memcmp(buf, cis->cache, len) != 0) {
+ ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
+ if (ret || memcmp(buf, cis->cache, len) != 0) {
kfree(buf);
return -1;
}
@@ -391,17 +418,20 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
return -EINVAL;
}
+ mutex_lock(&s->ops_mutex);
kfree(s->fake_cis);
s->fake_cis = kmalloc(len, GFP_KERNEL);
if (s->fake_cis == NULL) {
dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
+ mutex_unlock(&s->ops_mutex);
return -ENOMEM;
}
s->fake_cis_len = len;
memcpy(s->fake_cis, data, len);
+ dev_info(&s->dev, "Using replacement CIS\n");
+ mutex_unlock(&s->ops_mutex);
return 0;
}
-EXPORT_SYMBOL(pcmcia_replace_cis);
/*======================================================================
@@ -425,25 +455,16 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple
{
if (!s)
return -EINVAL;
- if (!(s->state & SOCKET_PRESENT))
+
+ if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
return -ENODEV;
tuple->TupleLink = tuple->Flags = 0;
-#ifdef CONFIG_CARDBUS
- if (s->state & SOCKET_CARDBUS) {
- struct pci_dev *dev = s->cb_dev;
- u_int ptr;
- pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr);
- tuple->CISOffset = ptr & ~7;
- SPACE(tuple->Flags) = (ptr & 7);
- } else
-#endif
- {
- /* Assume presence of a LONGLINK_C to address 0 */
- tuple->CISOffset = tuple->LinkOffset = 0;
- SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
- }
- if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) &&
- !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
+
+ /* Assume presence of a LONGLINK_C to address 0 */
+ tuple->CISOffset = tuple->LinkOffset = 0;
+ SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
+
+ if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
cisdata_t req = tuple->DesiredTuple;
tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
if (pccard_get_next_tuple(s, function, tuple) == 0) {
@@ -456,17 +477,19 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple
}
return pccard_get_next_tuple(s, function, tuple);
}
-EXPORT_SYMBOL(pccard_get_first_tuple);
static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
{
u_char link[5];
u_int ofs;
+ int ret;
if (MFC_FN(tuple->Flags)) {
/* Get indirect link from the MFC tuple */
- read_cis_cache(s, LINK_SPACE(tuple->Flags),
+ ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
tuple->LinkOffset, 5, link);
+ if (ret)
+ return -1;
ofs = get_unaligned_le32(link + 1);
SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
/* Move to the next indirect link */
@@ -479,10 +502,12 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
} else {
return -1;
}
- if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) {
+ if (SPACE(tuple->Flags)) {
/* This is ugly, but a common CIS error is to code the long
link offset incorrectly, so we check the right spot... */
- read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+ ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+ if (ret)
+ return -1;
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
(strncmp(link+2, "CIS", 3) == 0))
return ofs;
@@ -490,7 +515,9 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
/* Then, we try the wrong spot... */
ofs = ofs >> 1;
}
- read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+ ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+ if (ret)
+ return -1;
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
(strncmp(link+2, "CIS", 3) == 0))
return ofs;
@@ -502,10 +529,11 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
{
u_char link[2], tmp;
int ofs, i, attr;
+ int ret;
if (!s)
return -EINVAL;
- if (!(s->state & SOCKET_PRESENT))
+ if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
return -ENODEV;
link[1] = tuple->TupleLink;
@@ -516,7 +544,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
if (link[1] == 0xff) {
link[0] = CISTPL_END;
} else {
- read_cis_cache(s, attr, ofs, 2, link);
+ ret = read_cis_cache(s, attr, ofs, 2, link);
+ if (ret)
+ return -1;
if (link[0] == CISTPL_NULL) {
ofs++; continue;
}
@@ -528,7 +558,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
if (ofs < 0)
return -ENOSPC;
attr = SPACE(tuple->Flags);
- read_cis_cache(s, attr, ofs, 2, link);
+ ret = read_cis_cache(s, attr, ofs, 2, link);
+ if (ret)
+ return -1;
}
/* Is this a link tuple? Make a note of it */
@@ -542,12 +574,16 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
case CISTPL_LONGLINK_A:
HAS_LINK(tuple->Flags) = 1;
LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
- read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ if (ret)
+ return -1;
break;
case CISTPL_LONGLINK_C:
HAS_LINK(tuple->Flags) = 1;
LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
- read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ if (ret)
+ return -1;
break;
case CISTPL_INDIRECT:
HAS_LINK(tuple->Flags) = 1;
@@ -559,7 +595,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
LINK_SPACE(tuple->Flags) = attr;
if (function == BIND_FN_ALL) {
/* Follow all the MFC links */
- read_cis_cache(s, attr, ofs+2, 1, &tmp);
+ ret = read_cis_cache(s, attr, ofs+2, 1, &tmp);
+ if (ret)
+ return -1;
MFC_FN(tuple->Flags) = tmp;
} else {
/* Follow exactly one of the links */
@@ -592,7 +630,6 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
tuple->CISOffset = ofs + 2;
return 0;
}
-EXPORT_SYMBOL(pccard_get_next_tuple);
/*====================================================================*/
@@ -601,6 +638,7 @@ EXPORT_SYMBOL(pccard_get_next_tuple);
int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
{
u_int len;
+ int ret;
if (!s)
return -EINVAL;
@@ -611,12 +649,13 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
tuple->TupleDataLen = tuple->TupleLink;
if (len == 0)
return 0;
- read_cis_cache(s, SPACE(tuple->Flags),
+ ret = read_cis_cache(s, SPACE(tuple->Flags),
tuple->CISOffset + tuple->TupleOffset,
_MIN(len, tuple->TupleDataMax), tuple->TupleData);
+ if (ret)
+ return -1;
return 0;
}
-EXPORT_SYMBOL(pccard_get_tuple_data);
/*======================================================================
@@ -1190,119 +1229,6 @@ static int parse_cftable_entry(tuple_t *tuple,
/*====================================================================*/
-#ifdef CONFIG_CARDBUS
-
-static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
-{
- u_char *p;
- if (tuple->TupleDataLen < 6)
- return -EINVAL;
- p = (u_char *)tuple->TupleData;
- bar->attr = *p;
- p += 2;
- bar->size = get_unaligned_le32(p);
- return 0;
-}
-
-static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
-{
- u_char *p;
-
- p = (u_char *)tuple->TupleData;
- if ((*p != 3) || (tuple->TupleDataLen < 6))
- return -EINVAL;
- config->last_idx = *(++p);
- p++;
- config->base = get_unaligned_le32(p);
- config->subtuples = tuple->TupleDataLen - 6;
- return 0;
-}
-
-static int parse_cftable_entry_cb(tuple_t *tuple,
- cistpl_cftable_entry_cb_t *entry)
-{
- u_char *p, *q, features;
-
- p = tuple->TupleData;
- q = p + tuple->TupleDataLen;
- entry->index = *p & 0x3f;
- entry->flags = 0;
- if (*p & 0x40)
- entry->flags |= CISTPL_CFTABLE_DEFAULT;
-
- /* Process optional features */
- if (++p == q)
- return -EINVAL;
- features = *p; p++;
-
- /* Power options */
- if ((features & 3) > 0) {
- p = parse_power(p, q, &entry->vcc);
- if (p == NULL)
- return -EINVAL;
- } else
- entry->vcc.present = 0;
- if ((features & 3) > 1) {
- p = parse_power(p, q, &entry->vpp1);
- if (p == NULL)
- return -EINVAL;
- } else
- entry->vpp1.present = 0;
- if ((features & 3) > 2) {
- p = parse_power(p, q, &entry->vpp2);
- if (p == NULL)
- return -EINVAL;
- } else
- entry->vpp2.present = 0;
-
- /* I/O window options */
- if (features & 0x08) {
- if (p == q)
- return -EINVAL;
- entry->io = *p; p++;
- } else
- entry->io = 0;
-
- /* Interrupt options */
- if (features & 0x10) {
- p = parse_irq(p, q, &entry->irq);
- if (p == NULL)
- return -EINVAL;
- } else
- entry->irq.IRQInfo1 = 0;
-
- if (features & 0x20) {
- if (p == q)
- return -EINVAL;
- entry->mem = *p; p++;
- } else
- entry->mem = 0;
-
- /* Misc features */
- if (features & 0x80) {
- if (p == q)
- return -EINVAL;
- entry->flags |= (*p << 8);
- if (*p & 0x80) {
- if (++p == q)
- return -EINVAL;
- entry->flags |= (*p << 16);
- }
- while (*p & 0x80)
- if (++p == q)
- return -EINVAL;
- p++;
- }
-
- entry->subtuples = q-p;
-
- return 0;
-}
-
-#endif
-
-/*====================================================================*/
-
static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
{
u_char *p, *q;
@@ -1404,17 +1330,6 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
case CISTPL_DEVICE_A:
ret = parse_device(tuple, &parse->device);
break;
-#ifdef CONFIG_CARDBUS
- case CISTPL_BAR:
- ret = parse_bar(tuple, &parse->bar);
- break;
- case CISTPL_CONFIG_CB:
- ret = parse_config_cb(tuple, &parse->config);
- break;
- case CISTPL_CFTABLE_ENTRY_CB:
- ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
- break;
-#endif
case CISTPL_CHECKSUM:
ret = parse_checksum(tuple, &parse->checksum);
break;
@@ -1513,7 +1428,6 @@ done:
kfree(buf);
return ret;
}
-EXPORT_SYMBOL(pccard_read_tuple);
/**
@@ -1573,84 +1487,238 @@ next_entry:
kfree(buf);
return ret;
}
-EXPORT_SYMBOL(pccard_loop_tuple);
-/*======================================================================
-
- This tries to determine if a card has a sensible CIS. It returns
- the number of tuples in the CIS, or 0 if the CIS looks bad. The
- checks include making sure several critical tuples are present and
- valid; seeing if the total number of tuples is reasonable; and
- looking for tuples that use reserved codes.
-
-======================================================================*/
-
+/**
+ * pccard_validate_cis() - check whether card has a sensible CIS
+ * @s: the struct pcmcia_socket we are to check
+ * @info: returns the number of tuples in the (valid) CIS, or 0
+ *
+ * This tries to determine if a card has a sensible CIS. In @info, it
+ * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The
+ * checks include making sure several critical tuples are present and
+ * valid; seeing if the total number of tuples is reasonable; and
+ * looking for tuples that use reserved codes.
+ *
+ * The function returns 0 on success.
+ */
int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
{
- tuple_t *tuple;
- cisparse_t *p;
- unsigned int count = 0;
- int ret, reserved, dev_ok = 0, ident_ok = 0;
+ tuple_t *tuple;
+ cisparse_t *p;
+ unsigned int count = 0;
+ int ret, reserved, dev_ok = 0, ident_ok = 0;
- if (!s)
- return -EINVAL;
+ if (!s)
+ return -EINVAL;
- tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
- if (tuple == NULL) {
- dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
- return -ENOMEM;
- }
- p = kmalloc(sizeof(*p), GFP_KERNEL);
- if (p == NULL) {
- kfree(tuple);
- dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
- return -ENOMEM;
- }
+ /* We do not want to validate the CIS cache... */
+ mutex_lock(&s->ops_mutex);
+ destroy_cis_cache(s);
+ mutex_unlock(&s->ops_mutex);
- count = reserved = 0;
- tuple->DesiredTuple = RETURN_FIRST_TUPLE;
- tuple->Attributes = TUPLE_RETURN_COMMON;
- ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
- if (ret != 0)
- goto done;
-
- /* First tuple should be DEVICE; we should really have either that
- or a CFTABLE_ENTRY of some sort */
- if ((tuple->TupleCode == CISTPL_DEVICE) ||
- (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p) == 0) ||
- (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p) == 0))
- dev_ok++;
-
- /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
- tuple, for card identification. Certain old D-Link and Linksys
- cards have only a broken VERS_2 tuple; hence the bogus test. */
- if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
- (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
- (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
- ident_ok++;
-
- if (!dev_ok && !ident_ok)
- goto done;
-
- for (count = 1; count < MAX_TUPLES; count++) {
- ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
+ tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
+ if (tuple == NULL) {
+ dev_warn(&s->dev, "no memory to validate CIS\n");
+ return -ENOMEM;
+ }
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ kfree(tuple);
+ dev_warn(&s->dev, "no memory to validate CIS\n");
+ return -ENOMEM;
+ }
+
+ count = reserved = 0;
+ tuple->DesiredTuple = RETURN_FIRST_TUPLE;
+ tuple->Attributes = TUPLE_RETURN_COMMON;
+ ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
if (ret != 0)
- break;
- if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
- ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
- ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
- reserved++;
- }
- if ((count == MAX_TUPLES) || (reserved > 5) ||
- ((!dev_ok || !ident_ok) && (count > 10)))
- count = 0;
+ goto done;
+
+ /* First tuple should be DEVICE; we should really have either that
+ or a CFTABLE_ENTRY of some sort */
+ if ((tuple->TupleCode == CISTPL_DEVICE) ||
+ (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
+ (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
+ dev_ok++;
+
+ /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
+ tuple, for card identification. Certain old D-Link and Linksys
+ cards have only a broken VERS_2 tuple; hence the bogus test. */
+ if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
+ (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
+ (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
+ ident_ok++;
+
+ if (!dev_ok && !ident_ok)
+ goto done;
+
+ for (count = 1; count < MAX_TUPLES; count++) {
+ ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
+ if (ret != 0)
+ break;
+ if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
+ ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
+ ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
+ reserved++;
+ }
+ if ((count == MAX_TUPLES) || (reserved > 5) ||
+ ((!dev_ok || !ident_ok) && (count > 10)))
+ count = 0;
+
+ ret = 0;
done:
- if (info)
- *info = count;
- kfree(tuple);
- kfree(p);
- return 0;
+ /* invalidate CIS cache on failure */
+ if (!dev_ok || !ident_ok || !count) {
+ mutex_lock(&s->ops_mutex);
+ destroy_cis_cache(s);
+ mutex_unlock(&s->ops_mutex);
+ ret = -EIO;
+ }
+
+ if (info)
+ *info = count;
+ kfree(tuple);
+ kfree(p);
+ return ret;
}
-EXPORT_SYMBOL(pccard_validate_cis);
+
+
+#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
+
+static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
+ loff_t off, size_t count)
+{
+ tuple_t tuple;
+ int status, i;
+ loff_t pointer = 0;
+ ssize_t ret = 0;
+ u_char *tuplebuffer;
+ u_char *tempbuffer;
+
+ tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL);
+ if (!tuplebuffer)
+ return -ENOMEM;
+
+ tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL);
+ if (!tempbuffer) {
+ ret = -ENOMEM;
+ goto free_tuple;
+ }
+
+ memset(&tuple, 0, sizeof(tuple_t));
+
+ tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
+ tuple.DesiredTuple = RETURN_FIRST_TUPLE;
+ tuple.TupleOffset = 0;
+
+ status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
+ while (!status) {
+ tuple.TupleData = tuplebuffer;
+ tuple.TupleDataMax = 255;
+ memset(tuplebuffer, 0, sizeof(u_char) * 255);
+
+ status = pccard_get_tuple_data(s, &tuple);
+ if (status)
+ break;
+
+ if (off < (pointer + 2 + tuple.TupleDataLen)) {
+ tempbuffer[0] = tuple.TupleCode & 0xff;
+ tempbuffer[1] = tuple.TupleLink & 0xff;
+ for (i = 0; i < tuple.TupleDataLen; i++)
+ tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
+
+ for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
+ if (((i + pointer) >= off) &&
+ (i + pointer) < (off + count)) {
+ buf[ret] = tempbuffer[i];
+ ret++;
+ }
+ }
+ }
+
+ pointer += 2 + tuple.TupleDataLen;
+
+ if (pointer >= (off + count))
+ break;
+
+ if (tuple.TupleCode == CISTPL_END)
+ break;
+ status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
+ }
+
+ kfree(tempbuffer);
+ free_tuple:
+ kfree(tuplebuffer);
+
+ return ret;
+}
+
+
+static ssize_t pccard_show_cis(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ unsigned int size = 0x200;
+
+ if (off >= size)
+ count = 0;
+ else {
+ struct pcmcia_socket *s;
+ unsigned int chains;
+
+ if (off + count > size)
+ count = size - off;
+
+ s = to_socket(container_of(kobj, struct device, kobj));
+
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+ if (pccard_validate_cis(s, &chains))
+ return -EIO;
+ if (!chains)
+ return -ENODATA;
+
+ count = pccard_extract_cis(s, buf, off, count);
+ }
+
+ return count;
+}
+
+
+static ssize_t pccard_store_cis(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct pcmcia_socket *s;
+ int error;
+
+ s = to_socket(container_of(kobj, struct device, kobj));
+
+ if (off)
+ return -EINVAL;
+
+ if (count >= CISTPL_MAX_CIS_SIZE)
+ return -EINVAL;
+
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+
+ error = pcmcia_replace_cis(s, buf, count);
+ if (error)
+ return -EIO;
+
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
+
+ return count;
+}
+
+
+struct bin_attribute pccard_cis_attr = {
+ .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
+ .size = 0x200,
+ .read = pccard_show_cis,
+ .write = pccard_store_cis,
+};
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 6d6f82b38a6..e679e708db6 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -140,19 +140,13 @@ struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt)
struct device *dev = get_device(&skt->dev);
if (!dev)
return NULL;
- skt = dev_get_drvdata(dev);
- if (!try_module_get(skt->owner)) {
- put_device(&skt->dev);
- return NULL;
- }
- return skt;
+ return dev_get_drvdata(dev);
}
EXPORT_SYMBOL(pcmcia_get_socket);
void pcmcia_put_socket(struct pcmcia_socket *skt)
{
- module_put(skt->owner);
put_device(&skt->dev);
}
EXPORT_SYMBOL(pcmcia_put_socket);
@@ -181,8 +175,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
- spin_lock_init(&socket->lock);
-
/* try to obtain a socket number [yes, it gets ugly if we
* register more than 2^sizeof(unsigned int) pcmcia
* sockets... but the socket number is deprecated
@@ -228,10 +220,13 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
init_completion(&socket->socket_released);
init_completion(&socket->thread_done);
mutex_init(&socket->skt_mutex);
+ mutex_init(&socket->ops_mutex);
spin_lock_init(&socket->thread_lock);
if (socket->resource_ops->init) {
+ mutex_lock(&socket->ops_mutex);
ret = socket->resource_ops->init(socket);
+ mutex_unlock(&socket->ops_mutex);
if (ret)
goto err;
}
@@ -283,15 +278,17 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket)
if (socket->thread)
kthread_stop(socket->thread);
- release_cis_mem(socket);
-
/* remove from our own list */
down_write(&pcmcia_socket_list_rwsem);
list_del(&socket->socket_list);
up_write(&pcmcia_socket_list_rwsem);
/* wait for sysfs to drop all references */
- release_resource_db(socket);
+ if (socket->resource_ops->exit) {
+ mutex_lock(&socket->ops_mutex);
+ socket->resource_ops->exit(socket);
+ mutex_unlock(&socket->ops_mutex);
+ }
wait_for_completion(&socket->socket_released);
} /* pcmcia_unregister_socket */
EXPORT_SYMBOL(pcmcia_unregister_socket);
@@ -328,7 +325,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
{
int ret;
- if (s->state & SOCKET_CARDBUS)
+ if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL))
return 0;
dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
@@ -346,13 +343,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
return ret;
}
-static void socket_remove_drivers(struct pcmcia_socket *skt)
-{
- dev_dbg(&skt->dev, "remove_drivers\n");
-
- send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
-}
-
static int socket_reset(struct pcmcia_socket *skt)
{
int status, i;
@@ -395,7 +385,9 @@ static void socket_shutdown(struct pcmcia_socket *s)
dev_dbg(&s->dev, "shutdown\n");
- socket_remove_drivers(s);
+ send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+
+ mutex_lock(&s->ops_mutex);
s->state &= SOCKET_INUSE | SOCKET_PRESENT;
msleep(shutdown_delay * 10);
s->state &= SOCKET_INUSE;
@@ -406,11 +398,21 @@ static void socket_shutdown(struct pcmcia_socket *s)
s->ops->set_socket(s, &s->socket);
s->irq.AssignedIRQ = s->irq.Config = 0;
s->lock_count = 0;
- destroy_cis_cache(s);
+ kfree(s->fake_cis);
+ s->fake_cis = NULL;
+ s->functions = 0;
+
+ /* From here on we can be sure that only we (that is, the
+ * pccardd thread) accesses this socket, and all (16-bit)
+ * PCMCIA interactions are gone. Therefore, release
+ * ops_mutex so that we don't get a sysfs-related lockdep
+ * warning.
+ */
+ mutex_unlock(&s->ops_mutex);
+
#ifdef CONFIG_CARDBUS
cb_free(s);
#endif
- s->functions = 0;
/* give socket some time to power down */
msleep(100);
@@ -421,7 +423,7 @@ static void socket_shutdown(struct pcmcia_socket *s)
"*** DANGER *** unable to remove socket power\n");
}
- cs_socket_put(s);
+ s->state &= ~SOCKET_INUSE;
}
static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
@@ -460,7 +462,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
return -EINVAL;
}
skt->state |= SOCKET_CARDBUS;
- }
+ } else
+ skt->state &= ~SOCKET_CARDBUS;
/*
* Decode the card voltage requirements, and apply power to the card.
@@ -509,8 +512,12 @@ static int socket_insert(struct pcmcia_socket *skt)
dev_dbg(&skt->dev, "insert\n");
- if (!cs_socket_get(skt))
- return -ENODEV;
+ mutex_lock(&skt->ops_mutex);
+ if (skt->state & SOCKET_INUSE) {
+ mutex_unlock(&skt->ops_mutex);
+ return -EINVAL;
+ }
+ skt->state |= SOCKET_INUSE;
ret = socket_setup(skt, setup_delay);
if (ret == 0) {
@@ -528,9 +535,11 @@ static int socket_insert(struct pcmcia_socket *skt)
}
#endif
dev_dbg(&skt->dev, "insert done\n");
+ mutex_unlock(&skt->ops_mutex);
send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
} else {
+ mutex_unlock(&skt->ops_mutex);
socket_shutdown(skt);
}
@@ -542,58 +551,66 @@ static int socket_suspend(struct pcmcia_socket *skt)
if (skt->state & SOCKET_SUSPEND)
return -EBUSY;
+ mutex_lock(&skt->ops_mutex);
+ skt->suspended_state = skt->state;
+
send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
skt->socket = dead_socket;
skt->ops->set_socket(skt, &skt->socket);
if (skt->ops->suspend)
skt->ops->suspend(skt);
skt->state |= SOCKET_SUSPEND;
-
+ mutex_unlock(&skt->ops_mutex);
return 0;
}
static int socket_early_resume(struct pcmcia_socket *skt)
{
+ mutex_lock(&skt->ops_mutex);
skt->socket = dead_socket;
skt->ops->init(skt);
skt->ops->set_socket(skt, &skt->socket);
if (skt->state & SOCKET_PRESENT)
skt->resume_status = socket_setup(skt, resume_delay);
+ mutex_unlock(&skt->ops_mutex);
return 0;
}
static int socket_late_resume(struct pcmcia_socket *skt)
{
- if (!(skt->state & SOCKET_PRESENT)) {
- skt->state &= ~SOCKET_SUSPEND;
+ mutex_lock(&skt->ops_mutex);
+ skt->state &= ~SOCKET_SUSPEND;
+ mutex_unlock(&skt->ops_mutex);
+
+ if (!(skt->state & SOCKET_PRESENT))
return socket_insert(skt);
+
+ if (skt->resume_status) {
+ socket_shutdown(skt);
+ return 0;
}
- if (skt->resume_status == 0) {
- /*
- * FIXME: need a better check here for cardbus cards.
- */
- if (verify_cis_cache(skt) != 0) {
- dev_dbg(&skt->dev, "cis mismatch - different card\n");
- socket_remove_drivers(skt);
- destroy_cis_cache(skt);
- /*
- * Workaround: give DS time to schedule removal.
- * Remove me once the 100ms delay is eliminated
- * in ds.c
- */
- msleep(200);
- send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
- } else {
- dev_dbg(&skt->dev, "cis matches cache\n");
- send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
- }
- } else {
+ if (skt->suspended_state != skt->state) {
+ dev_dbg(&skt->dev,
+ "suspend state 0x%x != resume state 0x%x\n",
+ skt->suspended_state, skt->state);
+
socket_shutdown(skt);
+ return socket_insert(skt);
}
- skt->state &= ~SOCKET_SUSPEND;
+#ifdef CONFIG_CARDBUS
+ if (skt->state & SOCKET_CARDBUS) {
+ /* We can't be sure the CardBus card is the same
+ * as the one previously inserted. Therefore, remove
+ * and re-add... */
+ cb_free(skt);
+ cb_alloc(skt);
+ return 0;
+ }
+#endif
+ send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
return 0;
}
@@ -672,20 +689,26 @@ static int pccardd(void *__skt)
complete(&skt->thread_done);
+ /* wait for userspace to catch up */
+ msleep(250);
+
set_freezable();
for (;;) {
unsigned long flags;
unsigned int events;
+ unsigned int sysfs_events;
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&skt->thread_lock, flags);
events = skt->thread_events;
skt->thread_events = 0;
+ sysfs_events = skt->sysfs_events;
+ skt->sysfs_events = 0;
spin_unlock_irqrestore(&skt->thread_lock, flags);
+ mutex_lock(&skt->skt_mutex);
if (events) {
- mutex_lock(&skt->skt_mutex);
if (events & SS_DETECT)
socket_detect_change(skt);
if (events & SS_BATDEAD)
@@ -694,10 +717,39 @@ static int pccardd(void *__skt)
send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
if (events & SS_READY)
send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
- mutex_unlock(&skt->skt_mutex);
- continue;
}
+ if (sysfs_events) {
+ if (sysfs_events & PCMCIA_UEVENT_EJECT)
+ socket_remove(skt);
+ if (sysfs_events & PCMCIA_UEVENT_INSERT)
+ socket_insert(skt);
+ if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
+ !(skt->state & SOCKET_CARDBUS)) {
+ ret = socket_resume(skt);
+ if (!ret && skt->callback)
+ skt->callback->resume(skt);
+ }
+ if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
+ !(skt->state & SOCKET_CARDBUS)) {
+ if (skt->callback)
+ ret = skt->callback->suspend(skt);
+ else
+ ret = 0;
+ if (!ret)
+ socket_suspend(skt);
+ }
+ if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
+ !(skt->state & SOCKET_CARDBUS)) {
+ if (!ret && skt->callback)
+ skt->callback->requery(skt);
+ }
+ }
+ mutex_unlock(&skt->skt_mutex);
+
+ if (events || sysfs_events)
+ continue;
+
if (kthread_should_stop())
break;
@@ -707,6 +759,13 @@ static int pccardd(void *__skt)
/* make sure we are running before we exit */
set_current_state(TASK_RUNNING);
+ /* shut down socket, if a device is still present */
+ if (skt->state & SOCKET_PRESENT) {
+ mutex_lock(&skt->skt_mutex);
+ socket_remove(skt);
+ mutex_unlock(&skt->skt_mutex);
+ }
+
/* remove from the device core */
pccard_sysfs_remove_socket(&skt->dev);
device_unregister(&skt->dev);
@@ -732,6 +791,31 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
} /* pcmcia_parse_events */
EXPORT_SYMBOL(pcmcia_parse_events);
+/**
+ * pcmcia_parse_uevents() - tell pccardd to issue manual commands
+ * @s: the PCMCIA socket we wan't to command
+ * @events: events to pass to pccardd
+ *
+ * userspace-issued insert, eject, suspend and resume commands must be
+ * handled by pccardd to avoid any sysfs-related deadlocks. Valid events
+ * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert),
+ * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend)
+ * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card).
+ */
+void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events)
+{
+ unsigned long flags;
+ dev_dbg(&s->dev, "parse_uevents: events %08x\n", events);
+ if (s->thread) {
+ spin_lock_irqsave(&s->thread_lock, flags);
+ s->sysfs_events |= events;
+ spin_unlock_irqrestore(&s->thread_lock, flags);
+
+ wake_up_process(s->thread);
+ }
+}
+EXPORT_SYMBOL(pcmcia_parse_uevents);
+
/* register pcmcia_callback */
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
@@ -796,7 +880,10 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
if (skt->callback)
skt->callback->suspend(skt);
- if (socket_reset(skt) == 0) {
+ mutex_lock(&skt->ops_mutex);
+ ret = socket_reset(skt);
+ mutex_unlock(&skt->ops_mutex);
+ if (ret == 0) {
send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
if (skt->callback)
skt->callback->resume(skt);
@@ -812,121 +899,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
EXPORT_SYMBOL(pcmcia_reset_card);
-/* These shut down or wake up a socket. They are sort of user
- * initiated versions of the APM suspend and resume actions.
- */
-int pcmcia_suspend_card(struct pcmcia_socket *skt)
-{
- int ret;
-
- dev_dbg(&skt->dev, "suspending socket\n");
-
- mutex_lock(&skt->skt_mutex);
- do {
- if (!(skt->state & SOCKET_PRESENT)) {
- ret = -ENODEV;
- break;
- }
- if (skt->state & SOCKET_CARDBUS) {
- ret = -EPERM;
- break;
- }
- if (skt->callback) {
- ret = skt->callback->suspend(skt);
- if (ret)
- break;
- }
- ret = socket_suspend(skt);
- } while (0);
- mutex_unlock(&skt->skt_mutex);
-
- return ret;
-} /* suspend_card */
-EXPORT_SYMBOL(pcmcia_suspend_card);
-
-
-int pcmcia_resume_card(struct pcmcia_socket *skt)
-{
- int ret;
-
- dev_dbg(&skt->dev, "waking up socket\n");
-
- mutex_lock(&skt->skt_mutex);
- do {
- if (!(skt->state & SOCKET_PRESENT)) {
- ret = -ENODEV;
- break;
- }
- if (skt->state & SOCKET_CARDBUS) {
- ret = -EPERM;
- break;
- }
- ret = socket_resume(skt);
- if (!ret && skt->callback)
- skt->callback->resume(skt);
- } while (0);
- mutex_unlock(&skt->skt_mutex);
-
- return ret;
-} /* resume_card */
-EXPORT_SYMBOL(pcmcia_resume_card);
-
-
-/* These handle user requests to eject or insert a card. */
-int pcmcia_eject_card(struct pcmcia_socket *skt)
-{
- int ret;
-
- dev_dbg(&skt->dev, "user eject request\n");
-
- mutex_lock(&skt->skt_mutex);
- do {
- if (!(skt->state & SOCKET_PRESENT)) {
- ret = -ENODEV;
- break;
- }
-
- ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
-
- socket_remove(skt);
- ret = 0;
- } while (0);
- mutex_unlock(&skt->skt_mutex);
-
- return ret;
-} /* eject_card */
-EXPORT_SYMBOL(pcmcia_eject_card);
-
-
-int pcmcia_insert_card(struct pcmcia_socket *skt)
-{
- int ret;
-
- dev_dbg(&skt->dev, "user insert request\n");
-
- mutex_lock(&skt->skt_mutex);
- do {
- if (skt->state & SOCKET_PRESENT) {
- ret = -EBUSY;
- break;
- }
- if (socket_insert(skt) == -ENODEV) {
- ret = -ENODEV;
- break;
- }
- ret = 0;
- } while (0);
- mutex_unlock(&skt->skt_mutex);
-
- return ret;
-} /* insert_card */
-EXPORT_SYMBOL(pcmcia_insert_card);
-
-
static int pcmcia_socket_uevent(struct device *dev,
struct kobj_uevent_env *env)
{
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 3bc02d53a3a..f95864c2191 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -87,37 +87,11 @@ struct pccard_resource_ops {
#define SOCKET_CARDBUS 0x8000
#define SOCKET_CARDBUS_CONFIG 0x10000
-static inline int cs_socket_get(struct pcmcia_socket *skt)
-{
- int ret;
-
- WARN_ON(skt->state & SOCKET_INUSE);
-
- ret = try_module_get(skt->owner);
- if (ret)
- skt->state |= SOCKET_INUSE;
- return ret;
-}
-
-static inline void cs_socket_put(struct pcmcia_socket *skt)
-{
- if (skt->state & SOCKET_INUSE) {
- skt->state &= ~SOCKET_INUSE;
- module_put(skt->owner);
- }
-}
-
/*
* Stuff internal to module "pcmcia_core":
*/
-/* cistpl.c */
-int verify_cis_cache(struct pcmcia_socket *s);
-
-/* rsrc_mgr.c */
-void release_resource_db(struct pcmcia_socket *s);
-
/* socket_sysfs.c */
extern int pccard_sysfs_add_socket(struct device *dev);
extern void pccard_sysfs_remove_socket(struct device *dev);
@@ -125,8 +99,6 @@ extern void pccard_sysfs_remove_socket(struct device *dev);
/* cardbus.c */
int cb_alloc(struct pcmcia_socket *s);
void cb_free(struct pcmcia_socket *s);
-int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len,
- void *ptr);
@@ -138,7 +110,8 @@ struct pcmcia_callback{
struct module *owner;
int (*event) (struct pcmcia_socket *s,
event_t event, int priority);
- void (*requery) (struct pcmcia_socket *s, int new_cis);
+ void (*requery) (struct pcmcia_socket *s);
+ int (*validate) (struct pcmcia_socket *s, unsigned int *i);
int (*suspend) (struct pcmcia_socket *s);
int (*resume) (struct pcmcia_socket *s);
};
@@ -151,16 +124,35 @@ extern struct class pcmcia_socket_class;
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
-int pcmcia_suspend_card(struct pcmcia_socket *skt);
-int pcmcia_resume_card(struct pcmcia_socket *skt);
-
-int pcmcia_eject_card(struct pcmcia_socket *skt);
-int pcmcia_insert_card(struct pcmcia_socket *skt);
+void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events);
+#define PCMCIA_UEVENT_EJECT 0x0001
+#define PCMCIA_UEVENT_INSERT 0x0002
+#define PCMCIA_UEVENT_SUSPEND 0x0004
+#define PCMCIA_UEVENT_RESUME 0x0008
+#define PCMCIA_UEVENT_REQUERY 0x0010
struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
void pcmcia_put_socket(struct pcmcia_socket *skt);
+/*
+ * Stuff internal to module "pcmcia".
+ */
+/* ds.c */
+extern struct bus_type pcmcia_bus_type;
+
+/* pcmcia_resource.c */
+extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
+extern int pcmcia_validate_mem(struct pcmcia_socket *s);
+extern struct resource *pcmcia_find_mem_region(u_long base,
+ u_long num,
+ u_long align,
+ int low,
+ struct pcmcia_socket *s);
+
+
/* cistpl.c */
+extern struct bin_attribute pccard_cis_attr;
+
int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr,
u_int addr, u_int len, void *ptr);
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr,
@@ -172,8 +164,8 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
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);
+int verify_cis_cache(struct pcmcia_socket *s);
-/* 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,
@@ -189,35 +181,8 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
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,
- int num,
- unsigned long align,
- struct pcmcia_socket *s);
-int pcmcia_adjust_io_region(struct resource *res,
- unsigned long r_start,
- unsigned long r_end,
- struct pcmcia_socket *s);
-struct resource *pcmcia_find_mem_region(u_long base,
- u_long num,
- u_long align,
- int low,
- struct pcmcia_socket *s);
-
-/*
- * Stuff internal to module "pcmcia".
- */
-/* ds.c */
-extern struct bus_type pcmcia_bus_type;
-
-/* pcmcia_resource.c */
-extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
-
#ifdef CONFIG_PCMCIA_IOCTL
/* ds.c */
-extern spinlock_t pcmcia_dev_list_lock;
-
extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev);
extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c
new file mode 100644
index 00000000000..3889cf07d6c
--- /dev/null
+++ b/drivers/pcmcia/db1xxx_ss.c
@@ -0,0 +1,623 @@
+/*
+ * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards.
+ *
+ * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com>
+ *
+ */
+
+/* This is a fairly generic PCMCIA socket driver suitable for the
+ * following Alchemy Development boards:
+ * Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200.
+ *
+ * The Db1000 is used as a reference: Per-socket card-, carddetect- and
+ * statuschange IRQs connected to SoC GPIOs, control and status register
+ * bits arranged in per-socket groups in an external PLD. All boards
+ * listed here use this layout, including bit positions and meanings.
+ * Of course there are exceptions in later boards:
+ *
+ * - Pb1100/Pb1500: single socket only; voltage key bits VS are
+ * at STATUS[5:4] (instead of STATUS[1:0]).
+ * - Au1200-based: additional card-eject irqs, irqs not gpios!
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/spinlock.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-db1x00/bcsr.h>
+
+#define MEM_MAP_SIZE 0x400000
+#define IO_MAP_SIZE 0x1000
+
+struct db1x_pcmcia_sock {
+ struct pcmcia_socket socket;
+ int nr; /* socket number */
+ void *virt_io;
+
+ /* the "pseudo" addresses of the PCMCIA space. */
+ phys_addr_t phys_io;
+ phys_addr_t phys_attr;
+ phys_addr_t phys_mem;
+
+ /* previous flags for set_socket() */
+ unsigned int old_flags;
+
+ /* interrupt sources: linux irq numbers! */
+ int insert_irq; /* default carddetect irq */
+ int stschg_irq; /* card-status-change irq */
+ int card_irq; /* card irq */
+ int eject_irq; /* db1200/pb1200 have these */
+
+#define BOARD_TYPE_DEFAULT 0 /* most boards */
+#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */
+#define BOARD_TYPE_PB1100 2 /* VS bits slightly different */
+ int board_type;
+};
+
+#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket)
+
+/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */
+static int db1200_card_inserted(struct db1x_pcmcia_sock *sock)
+{
+ unsigned short sigstat;
+
+ sigstat = bcsr_read(BCSR_SIGSTAT);
+ return sigstat & 1 << (8 + 2 * sock->nr);
+}
+
+/* carddetect gpio: low-active */
+static int db1000_card_inserted(struct db1x_pcmcia_sock *sock)
+{
+ return !gpio_get_value(irq_to_gpio(sock->insert_irq));
+}
+
+static int db1x_card_inserted(struct db1x_pcmcia_sock *sock)
+{
+ switch (sock->board_type) {
+ case BOARD_TYPE_DB1200:
+ return db1200_card_inserted(sock);
+ default:
+ return db1000_card_inserted(sock);
+ }
+}
+
+/* STSCHG tends to bounce heavily when cards are inserted/ejected.
+ * To avoid this, the interrupt is normally disabled and only enabled
+ * after reset to a card has been de-asserted.
+ */
+static inline void set_stschg(struct db1x_pcmcia_sock *sock, int en)
+{
+ if (sock->stschg_irq != -1) {
+ if (en)
+ enable_irq(sock->stschg_irq);
+ else
+ disable_irq(sock->stschg_irq);
+ }
+}
+
+static irqreturn_t db1000_pcmcia_cdirq(int irq, void *data)
+{
+ struct db1x_pcmcia_sock *sock = data;
+
+ pcmcia_parse_events(&sock->socket, SS_DETECT);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data)
+{
+ struct db1x_pcmcia_sock *sock = data;
+
+ pcmcia_parse_events(&sock->socket, SS_STSCHG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data)
+{
+ struct db1x_pcmcia_sock *sock = data;
+
+ /* Db/Pb1200 have separate per-socket insertion and ejection
+ * interrupts which stay asserted as long as the card is
+ * inserted/missing. The one which caused us to be called
+ * needs to be disabled and the other one enabled.
+ */
+ if (irq == sock->insert_irq) {
+ disable_irq_nosync(sock->insert_irq);
+ enable_irq(sock->eject_irq);
+ } else {
+ disable_irq_nosync(sock->eject_irq);
+ enable_irq(sock->insert_irq);
+ }
+
+ pcmcia_parse_events(&sock->socket, SS_DETECT);
+
+ return IRQ_HANDLED;
+}
+
+static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
+{
+ int ret;
+ unsigned long flags;
+
+ if (sock->stschg_irq != -1) {
+ ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq,
+ 0, "pcmcia_stschg", sock);
+ if (ret)
+ return ret;
+ }
+
+ /* Db/Pb1200 have separate per-socket insertion and ejection
+ * interrupts, which should show edge behaviour but don't.
+ * So interrupts are disabled until both insertion and
+ * ejection handler have been registered and the currently
+ * active one disabled.
+ */
+ if (sock->board_type == BOARD_TYPE_DB1200) {
+ local_irq_save(flags);
+
+ ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq,
+ IRQF_DISABLED, "pcmcia_insert", sock);
+ if (ret)
+ goto out1;
+
+ ret = request_irq(sock->eject_irq, db1200_pcmcia_cdirq,
+ IRQF_DISABLED, "pcmcia_eject", sock);
+ if (ret) {
+ free_irq(sock->insert_irq, sock);
+ local_irq_restore(flags);
+ goto out1;
+ }
+
+ /* disable the currently active one */
+ if (db1200_card_inserted(sock))
+ disable_irq_nosync(sock->insert_irq);
+ else
+ disable_irq_nosync(sock->eject_irq);
+
+ local_irq_restore(flags);
+ } else {
+ /* all other (older) Db1x00 boards use a GPIO to show
+ * card detection status: use both-edge triggers.
+ */
+ set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH);
+ ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq,
+ 0, "pcmcia_carddetect", sock);
+
+ if (ret)
+ goto out1;
+ }
+
+ return 0; /* all done */
+
+out1:
+ if (sock->stschg_irq != -1)
+ free_irq(sock->stschg_irq, sock);
+
+ return ret;
+}
+
+static void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock)
+{
+ if (sock->stschg_irq != -1)
+ free_irq(sock->stschg_irq, sock);
+
+ free_irq(sock->insert_irq, sock);
+ if (sock->eject_irq != -1)
+ free_irq(sock->eject_irq, sock);
+}
+
+/*
+ * configure a PCMCIA socket on the Db1x00 series of boards (and
+ * compatibles).
+ *
+ * 2 external registers are involved:
+ * pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id
+ * pcmcia_control(offset 0x10):
+ * bits[0:1] set vcc for card
+ * bits[2:3] set vpp for card
+ * bit 4: enable data buffers
+ * bit 7: reset# for card
+ * add 8 for second socket.
+ */
+static int db1x_pcmcia_configure(struct pcmcia_socket *skt,
+ struct socket_state_t *state)
+{
+ struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
+ unsigned short cr_clr, cr_set;
+ unsigned int changed;
+ int v, p, ret;
+
+ /* card voltage setup */
+ cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */
+ cr_set = 0;
+ v = p = ret = 0;
+
+ switch (state->Vcc) {
+ case 50:
+ ++v;
+ case 33:
+ ++v;
+ case 0:
+ break;
+ default:
+ printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n",
+ sock->nr, state->Vcc);
+ }
+
+ switch (state->Vpp) {
+ case 12:
+ ++p;
+ case 33:
+ case 50:
+ ++p;
+ case 0:
+ break;
+ default:
+ printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n",
+ sock->nr, state->Vpp);
+ }
+
+ /* sanity check: Vpp must be 0, 12, or Vcc */
+ if (((state->Vcc == 33) && (state->Vpp == 50)) ||
+ ((state->Vcc == 50) && (state->Vpp == 33))) {
+ printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n",
+ sock->nr, state->Vcc, state->Vpp);
+ v = p = 0;
+ ret = -EINVAL;
+ }
+
+ /* create new voltage code */
+ cr_set |= ((v << 2) | p) << (sock->nr * 8);
+
+ changed = state->flags ^ sock->old_flags;
+
+ if (changed & SS_RESET) {
+ if (state->flags & SS_RESET) {
+ set_stschg(sock, 0);
+ /* assert reset, disable io buffers */
+ cr_clr |= (1 << (7 + (sock->nr * 8)));
+ cr_clr |= (1 << (4 + (sock->nr * 8)));
+ } else {
+ /* de-assert reset, enable io buffers */
+ cr_set |= 1 << (7 + (sock->nr * 8));
+ cr_set |= 1 << (4 + (sock->nr * 8));
+ }
+ }
+
+ /* update PCMCIA configuration */
+ bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set);
+
+ sock->old_flags = state->flags;
+
+ /* reset was taken away: give card time to initialize properly */
+ if ((changed & SS_RESET) && !(state->flags & SS_RESET)) {
+ msleep(500);
+ set_stschg(sock, 1);
+ }
+
+ return ret;
+}
+
+/* VCC bits at [3:2]/[11:10] */
+#define GET_VCC(cr, socknr) \
+ ((((cr) >> 2) >> ((socknr) * 8)) & 3)
+
+/* VS bits at [0:1]/[3:2] */
+#define GET_VS(sr, socknr) \
+ (((sr) >> (2 * (socknr))) & 3)
+
+/* reset bits at [7]/[15] */
+#define GET_RESET(cr, socknr) \
+ ((cr) & (1 << (7 + (8 * (socknr)))))
+
+static int db1x_pcmcia_get_status(struct pcmcia_socket *skt,
+ unsigned int *value)
+{
+ struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
+ unsigned short cr, sr;
+ unsigned int status;
+
+ status = db1x_card_inserted(sock) ? SS_DETECT : 0;
+
+ cr = bcsr_read(BCSR_PCMCIA);
+ sr = bcsr_read(BCSR_STATUS);
+
+ /* PB1100/PB1500: voltage key bits are at [5:4] */
+ if (sock->board_type == BOARD_TYPE_PB1100)
+ sr >>= 4;
+
+ /* determine card type */
+ switch (GET_VS(sr, sock->nr)) {
+ case 0:
+ case 2:
+ status |= SS_3VCARD; /* 3V card */
+ case 3:
+ break; /* 5V card: set nothing */
+ default:
+ status |= SS_XVCARD; /* treated as unsupported in core */
+ }
+
+ /* if Vcc is not zero, we have applied power to a card */
+ status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0;
+
+ /* reset de-asserted? then we're ready */
+ status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET;
+
+ *value = status;
+
+ return 0;
+}
+
+static int db1x_pcmcia_sock_init(struct pcmcia_socket *skt)
+{
+ return 0;
+}
+
+static int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt)
+{
+ return 0;
+}
+
+static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt,
+ struct pccard_io_map *map)
+{
+ struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
+
+ map->start = (u32)sock->virt_io;
+ map->stop = map->start + IO_MAP_SIZE;
+
+ return 0;
+}
+
+static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt,
+ struct pccard_mem_map *map)
+{
+ struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
+
+ if (map->flags & MAP_ATTRIB)
+ map->static_start = sock->phys_attr + map->card_start;
+ else
+ map->static_start = sock->phys_mem + map->card_start;
+
+ return 0;
+}
+
+static struct pccard_operations db1x_pcmcia_operations = {
+ .init = db1x_pcmcia_sock_init,
+ .suspend = db1x_pcmcia_sock_suspend,
+ .get_status = db1x_pcmcia_get_status,
+ .set_socket = db1x_pcmcia_configure,
+ .set_io_map = au1x00_pcmcia_set_io_map,
+ .set_mem_map = au1x00_pcmcia_set_mem_map,
+};
+
+static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev)
+{
+ struct db1x_pcmcia_sock *sock;
+ struct resource *r;
+ int ret, bid;
+
+ sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL);
+ if (!sock)
+ return -ENOMEM;
+
+ sock->nr = pdev->id;
+
+ bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
+ switch (bid) {
+ case BCSR_WHOAMI_PB1500:
+ case BCSR_WHOAMI_PB1500R2:
+ case BCSR_WHOAMI_PB1100:
+ sock->board_type = BOARD_TYPE_PB1100;
+ break;
+ case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR:
+ sock->board_type = BOARD_TYPE_DEFAULT;
+ break;
+ case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200:
+ sock->board_type = BOARD_TYPE_DB1200;
+ break;
+ default:
+ printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid);
+ ret = -ENODEV;
+ goto out0;
+ };
+
+ /*
+ * gather resources necessary and optional nice-to-haves to
+ * operate a socket:
+ * This includes IRQs for Carddetection/ejection, the card
+ * itself and optional status change detection.
+ * Also, the memory areas covered by a socket. For these
+ * we require the 32bit "pseudo" addresses (see the au1000.h
+ * header for more information).
+ */
+
+ /* card: irq assigned to the card itself. */
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card");
+ sock->card_irq = r ? r->start : 0;
+
+ /* insert: irq which triggers on card insertion/ejection */
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert");
+ sock->insert_irq = r ? r->start : -1;
+
+ /* stschg: irq which trigger on card status change (optional) */
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg");
+ sock->stschg_irq = r ? r->start : -1;
+
+ /* eject: irq which triggers on ejection (DB1200/PB1200 only) */
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject");
+ sock->eject_irq = r ? r->start : -1;
+
+ ret = -ENODEV;
+
+ /*
+ * pseudo-attr: The 32bit address of the PCMCIA attribute space
+ * for this socket (usually the 36bit address shifted 4 to the
+ * right).
+ */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
+ if (!r) {
+ printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n",
+ sock->nr);
+ goto out0;
+ }
+ sock->phys_attr = r->start;
+
+ /*
+ * pseudo-mem: The 32bit address of the PCMCIA memory space for
+ * this socket (usually the 36bit address shifted 4 to the right)
+ */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
+ if (!r) {
+ printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n",
+ sock->nr);
+ goto out0;
+ }
+ sock->phys_mem = r->start;
+
+ /*
+ * pseudo-io: The 32bit address of the PCMCIA IO space for this
+ * socket (usually the 36bit address shifted 4 to the right).
+ */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
+ if (!r) {
+ printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n",
+ sock->nr);
+ goto out0;
+ }
+ sock->phys_io = r->start;
+
+ /*
+ * PCMCIA client drivers use the inb/outb macros to access
+ * the IO registers. Since mips_io_port_base is added
+ * to the access address of the mips implementation of
+ * inb/outb, we need to subtract it here because we want
+ * to access the I/O or MEM address directly, without
+ * going through this "mips_io_port_base" mechanism.
+ */
+ sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
+ mips_io_port_base);
+
+ if (!sock->virt_io) {
+ printk(KERN_ERR "pcmcia%d: cannot remap IO area\n",
+ sock->nr);
+ ret = -ENOMEM;
+ goto out0;
+ }
+
+ sock->socket.ops = &db1x_pcmcia_operations;
+ sock->socket.owner = THIS_MODULE;
+ sock->socket.pci_irq = sock->card_irq;
+ sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
+ sock->socket.map_size = MEM_MAP_SIZE;
+ sock->socket.io_offset = (unsigned long)sock->virt_io;
+ sock->socket.dev.parent = &pdev->dev;
+ sock->socket.resource_ops = &pccard_static_ops;
+
+ platform_set_drvdata(pdev, sock);
+
+ ret = db1x_pcmcia_setup_irqs(sock);
+ if (ret) {
+ printk(KERN_ERR "pcmcia%d cannot setup interrupts\n",
+ sock->nr);
+ goto out1;
+ }
+
+ set_stschg(sock, 0);
+
+ ret = pcmcia_register_socket(&sock->socket);
+ if (ret) {
+ printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr);
+ goto out2;
+ }
+
+ printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx"
+ "(%p) %09llx %09llx card/insert/stschg/eject irqs @ %d "
+ "%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io,
+ sock->phys_attr, sock->phys_mem, sock->card_irq,
+ sock->insert_irq, sock->stschg_irq, sock->eject_irq);
+
+ return 0;
+
+out2:
+ db1x_pcmcia_free_irqs(sock);
+out1:
+ iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
+out0:
+ kfree(sock);
+ return ret;
+}
+
+static int __devexit db1x_pcmcia_socket_remove(struct platform_device *pdev)
+{
+ struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev);
+
+ db1x_pcmcia_free_irqs(sock);
+ pcmcia_unregister_socket(&sock->socket);
+ iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
+ kfree(sock);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int db1x_pcmcia_suspend(struct device *dev)
+{
+ return pcmcia_socket_dev_suspend(dev);
+}
+
+static int db1x_pcmcia_resume(struct device *dev)
+{
+ return pcmcia_socket_dev_resume(dev);
+}
+
+static struct dev_pm_ops db1x_pcmcia_pmops = {
+ .resume = db1x_pcmcia_resume,
+ .suspend = db1x_pcmcia_suspend,
+ .thaw = db1x_pcmcia_resume,
+ .freeze = db1x_pcmcia_suspend,
+};
+
+#define DB1XXX_SS_PMOPS &db1x_pcmcia_pmops
+
+#else
+
+#define DB1XXX_SS_PMOPS NULL
+
+#endif
+
+static struct platform_driver db1x_pcmcia_socket_driver = {
+ .driver = {
+ .name = "db1xxx_pcmcia",
+ .owner = THIS_MODULE,
+ .pm = DB1XXX_SS_PMOPS
+ },
+ .probe = db1x_pcmcia_socket_probe,
+ .remove = __devexit_p(db1x_pcmcia_socket_remove),
+};
+
+int __init db1x_pcmcia_socket_load(void)
+{
+ return platform_driver_register(&db1x_pcmcia_socket_driver);
+}
+
+void __exit db1x_pcmcia_socket_unload(void)
+{
+ platform_driver_unregister(&db1x_pcmcia_socket_driver);
+}
+
+module_init(db1x_pcmcia_socket_load);
+module_exit(db1x_pcmcia_socket_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 1a4a3c49cc1..0f98be4450b 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -42,8 +42,6 @@ MODULE_DESCRIPTION("PCMCIA Driver Services");
MODULE_LICENSE("GPL");
-spinlock_t pcmcia_dev_list_lock;
-
/*====================================================================*/
static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
@@ -126,9 +124,9 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count)
dynid->id.device_no = device_no;
memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);
- spin_lock(&pdrv->dynids.lock);
+ mutex_lock(&pdrv->dynids.lock);
list_add_tail(&dynid->node, &pdrv->dynids.list);
- spin_unlock(&pdrv->dynids.lock);
+ mutex_unlock(&pdrv->dynids.lock);
if (get_driver(&pdrv->drv)) {
retval = driver_attach(&pdrv->drv);
@@ -146,12 +144,12 @@ pcmcia_free_dynids(struct pcmcia_driver *drv)
{
struct pcmcia_dynid *dynid, *n;
- spin_lock(&drv->dynids.lock);
+ mutex_lock(&drv->dynids.lock);
list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
- spin_unlock(&drv->dynids.lock);
+ mutex_unlock(&drv->dynids.lock);
}
static int
@@ -182,7 +180,7 @@ int pcmcia_register_driver(struct pcmcia_driver *driver)
/* initialize common fields */
driver->drv.bus = &pcmcia_bus_type;
driver->drv.owner = driver->owner;
- spin_lock_init(&driver->dynids.lock);
+ mutex_init(&driver->dynids.lock);
INIT_LIST_HEAD(&driver->dynids.list);
pr_debug("registering driver %s\n", driver->drv.name);
@@ -239,30 +237,21 @@ static void pcmcia_release_function(struct kref *ref)
static void pcmcia_release_dev(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+ int i;
dev_dbg(dev, "releasing device\n");
pcmcia_put_socket(p_dev->socket);
+ for (i = 0; i < 4; i++)
+ kfree(p_dev->prod_id[i]);
kfree(p_dev->devname);
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
kfree(p_dev);
}
-static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
-{
- if (!s->pcmcia_state.device_add_pending) {
- 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;
- schedule_work(&s->device_add);
- }
- return;
-}
static int pcmcia_device_probe(struct device *dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
- struct pcmcia_device_id *did;
struct pcmcia_socket *s;
cistpl_config_t cis_config;
int ret = 0;
@@ -275,18 +264,6 @@ static int pcmcia_device_probe(struct device *dev)
p_drv = to_pcmcia_drv(dev->driver);
s = p_dev->socket;
- /* The PCMCIA code passes the match data in via dev_set_drvdata(dev)
- * which is an ugly hack. Once the driver probe is called it may
- * and often will overwrite the match data so we must save it first
- *
- * handle pseudo multifunction devices:
- * there are at most two pseudo multifunction devices.
- * if we're matching against the first, schedule a
- * call which will then check whether there are two
- * pseudo devices, and if not, add the second one.
- */
- did = dev_get_drvdata(&p_dev->dev);
-
dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name);
if ((!p_drv->probe) || (!p_dev->function_config) ||
@@ -315,9 +292,11 @@ static int pcmcia_device_probe(struct device *dev)
goto put_module;
}
- if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+ mutex_lock(&s->ops_mutex);
+ if ((s->pcmcia_state.has_pfc) &&
(p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
- pcmcia_add_device_later(p_dev->socket, 0);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
+ mutex_unlock(&s->ops_mutex);
put_module:
if (ret)
@@ -336,26 +315,27 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
{
struct pcmcia_device *p_dev;
struct pcmcia_device *tmp;
- unsigned long flags;
dev_dbg(leftover ? &leftover->dev : &s->dev,
"pcmcia_card_remove(%d) %s\n", s->sock,
leftover ? leftover->devname : "");
+ mutex_lock(&s->ops_mutex);
if (!leftover)
s->device_count = 0;
else
s->device_count = 1;
+ mutex_unlock(&s->ops_mutex);
/* unregister all pcmcia_devices registered with this socket, except leftover */
list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
if (p_dev == leftover)
continue;
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
list_del(&p_dev->socket_device_list);
p_dev->_removed = 1;
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
dev_dbg(&p_dev->dev, "unregistering device\n");
device_unregister(&p_dev->dev);
@@ -368,7 +348,6 @@ static int pcmcia_device_remove(struct device *dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
- struct pcmcia_device_id *did;
int i;
p_dev = to_pcmcia_dev(dev);
@@ -380,9 +359,8 @@ static int pcmcia_device_remove(struct device *dev)
* pseudo multi-function card, we need to unbind
* all devices
*/
- did = dev_get_drvdata(&p_dev->dev);
- if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
- (p_dev->socket->device_count != 0) &&
+ if ((p_dev->socket->pcmcia_state.has_pfc) &&
+ (p_dev->socket->device_count > 0) &&
(p_dev->device_no == 0))
pcmcia_card_remove(p_dev->socket, p_dev);
@@ -431,16 +409,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
CISTPL_MANFID, &manf_id)) {
+ mutex_lock(&p_dev->socket->ops_mutex);
p_dev->manf_id = manf_id.manf;
p_dev->card_id = manf_id.card;
p_dev->has_manf_id = 1;
p_dev->has_card_id = 1;
+ mutex_unlock(&p_dev->socket->ops_mutex);
}
if (!pccard_read_tuple(p_dev->socket, p_dev->func,
CISTPL_FUNCID, &func_id)) {
+ mutex_lock(&p_dev->socket->ops_mutex);
p_dev->func_id = func_id.func;
p_dev->has_func_id = 1;
+ mutex_unlock(&p_dev->socket->ops_mutex);
} else {
/* rule of thumb: cards with no FUNCID, but with
* common memory device geometry information, are
@@ -457,17 +439,21 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
dev_dbg(&p_dev->dev,
"mem device geometry probably means "
"FUNCID_MEMORY\n");
+ mutex_lock(&p_dev->socket->ops_mutex);
p_dev->func_id = CISTPL_FUNCID_MEMORY;
p_dev->has_func_id = 1;
+ mutex_unlock(&p_dev->socket->ops_mutex);
}
kfree(devgeo);
}
if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
vers1)) {
+ mutex_lock(&p_dev->socket->ops_mutex);
for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
char *tmp;
unsigned int length;
+ char *new;
tmp = vers1->str + vers1->ofs[i];
@@ -475,14 +461,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
if ((length < 2) || (length > 255))
continue;
- p_dev->prod_id[i] = kmalloc(sizeof(char) * length,
- GFP_KERNEL);
- if (!p_dev->prod_id[i])
+ new = kmalloc(sizeof(char) * length, GFP_KERNEL);
+ if (!new)
continue;
- p_dev->prod_id[i] = strncpy(p_dev->prod_id[i],
- tmp, length);
+ new = strncpy(new, tmp, length);
+
+ tmp = p_dev->prod_id[i];
+ p_dev->prod_id[i] = new;
+ kfree(tmp);
}
+ mutex_unlock(&p_dev->socket->ops_mutex);
}
kfree(vers1);
@@ -502,7 +491,7 @@ static DEFINE_MUTEX(device_add_lock);
struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
{
struct pcmcia_device *p_dev, *tmp_dev;
- unsigned long flags;
+ int i;
s = pcmcia_get_socket(s);
if (!s)
@@ -512,16 +501,19 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
pr_debug("adding device to %d, function %d\n", s->sock, function);
- /* max of 4 devices per card */
- if (s->device_count == 4)
- goto err_put;
-
p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
if (!p_dev)
goto err_put;
- p_dev->socket = s;
+ mutex_lock(&s->ops_mutex);
p_dev->device_no = (s->device_count++);
+ mutex_unlock(&s->ops_mutex);
+
+ /* max of 2 devices per card */
+ if (p_dev->device_no >= 2)
+ goto err_free;
+
+ p_dev->socket = s;
p_dev->func = function;
p_dev->dev.bus = &pcmcia_bus_type;
@@ -538,7 +530,7 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
goto err_free;
dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname);
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
/*
* p_dev->function_config must be the same for all card functions.
@@ -556,7 +548,7 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
/* Add to the list in pcmcia_bus_socket */
list_add(&p_dev->socket_device_list, &s->devices_list);
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
if (!p_dev->function_config) {
dev_dbg(&p_dev->dev, "creating config_t\n");
@@ -581,14 +573,19 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
return p_dev;
err_unreg:
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
list_del(&p_dev->socket_device_list);
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
err_free:
+ mutex_lock(&s->ops_mutex);
+ s->device_count--;
+ mutex_unlock(&s->ops_mutex);
+
+ for (i = 0; i < 4; i++)
+ kfree(p_dev->prod_id[i]);
kfree(p_dev->devname);
kfree(p_dev);
- s->device_count--;
err_put:
mutex_unlock(&device_add_lock);
pcmcia_put_socket(s);
@@ -601,19 +598,23 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
{
cistpl_longlink_mfc_t mfc;
unsigned int no_funcs, i, no_chains;
- int ret = 0;
+ int ret = -EAGAIN;
+ mutex_lock(&s->ops_mutex);
if (!(s->resource_setup_done)) {
dev_dbg(&s->dev,
"no resources available, delaying card_add\n");
+ mutex_unlock(&s->ops_mutex);
return -EAGAIN; /* try again, but later... */
}
if (pcmcia_validate_mem(s)) {
dev_dbg(&s->dev, "validating mem resources failed, "
"delaying card_add\n");
+ mutex_unlock(&s->ops_mutex);
return -EAGAIN; /* try again, but later... */
}
+ mutex_unlock(&s->ops_mutex);
ret = pccard_validate_cis(s, &no_chains);
if (ret || !no_chains) {
@@ -634,17 +635,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
}
-static void pcmcia_delayed_add_device(struct work_struct *work)
-{
- struct pcmcia_socket *s =
- container_of(work, struct pcmcia_socket, device_add);
- 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;
-}
-
-static int pcmcia_requery(struct device *dev, void * _data)
+static int pcmcia_requery_callback(struct device *dev, void * _data)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
if (!p_dev->dev.driver) {
@@ -655,45 +646,67 @@ static int pcmcia_requery(struct device *dev, void * _data)
return 0;
}
-static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis)
-{
- int no_devices = 0;
- int ret = 0;
- unsigned long flags;
- /* must be called with skt_mutex held */
- dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock);
+static void pcmcia_requery(struct pcmcia_socket *s)
+{
+ int present, has_pfc;
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- if (list_empty(&skt->devices_list))
- no_devices = 1;
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
+ present = s->pcmcia_state.present;
+ mutex_unlock(&s->ops_mutex);
- /* If this is because of a CIS override, start over */
- if (new_cis && !no_devices)
- pcmcia_card_remove(skt, NULL);
+ if (!present)
+ return;
- /* if no devices were added for this socket yet because of
- * missing resource information or other trouble, we need to
- * do this now. */
- if (no_devices || new_cis) {
- ret = pcmcia_card_add(skt);
- if (ret)
- return;
+ if (s->functions == 0) {
+ pcmcia_card_add(s);
+ return;
}
/* some device information might have changed because of a CIS
* update or because we can finally read it correctly... so
* determine it again, overwriting old values if necessary. */
- bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery);
+ bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback);
+
+ /* if the CIS changed, we need to check whether the number of
+ * functions changed. */
+ if (s->fake_cis) {
+ int old_funcs, new_funcs;
+ cistpl_longlink_mfc_t mfc;
+
+ /* does this cis override add or remove functions? */
+ old_funcs = s->functions;
+
+ if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
+ &mfc))
+ new_funcs = mfc.nfn;
+ else
+ new_funcs = 1;
+ if (old_funcs > new_funcs) {
+ pcmcia_card_remove(s, NULL);
+ pcmcia_card_add(s);
+ } else if (new_funcs > old_funcs) {
+ s->functions = new_funcs;
+ pcmcia_device_add(s, 1);
+ }
+ }
+
+ /* If the PCMCIA device consists of two pseudo devices,
+ * call pcmcia_device_add() -- which will fail if both
+ * devices are already registered. */
+ mutex_lock(&s->ops_mutex);
+ has_pfc = s->pcmcia_state.has_pfc;
+ mutex_unlock(&s->ops_mutex);
+ if (has_pfc)
+ pcmcia_device_add(s, 0);
/* we re-scan all devices, not just the ones connected to this
* socket. This does not matter, though. */
- ret = bus_rescan_devices(&pcmcia_bus_type);
- if (ret)
- printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");
+ if (bus_rescan_devices(&pcmcia_bus_type))
+ dev_warn(&s->dev, "rescanning the bus failed\n");
}
+
#ifdef CONFIG_PCMCIA_LOAD_CIS
/**
@@ -710,9 +723,6 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
struct pcmcia_socket *s = dev->socket;
const struct firmware *fw;
int ret = -ENOMEM;
- int no_funcs;
- int old_funcs;
- cistpl_longlink_mfc_t mfc;
if (!filename)
return -EINVAL;
@@ -739,19 +749,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
/* update information */
pcmcia_device_query(dev);
- /* does this cis override add or remove functions? */
- old_funcs = s->functions;
-
- if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
- no_funcs = mfc.nfn;
- else
- no_funcs = 1;
- s->functions = no_funcs;
-
- if (old_funcs > no_funcs)
- pcmcia_card_remove(s, dev);
- else if (no_funcs > old_funcs)
- pcmcia_add_device_later(s, 1);
+ /* requery (as number of functions might have changed) */
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
}
release:
release_firmware(fw);
@@ -818,9 +817,14 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
if (dev->device_no != did->device_no)
return 0;
+ mutex_lock(&dev->socket->ops_mutex);
+ dev->socket->pcmcia_state.has_pfc = 1;
+ mutex_unlock(&dev->socket->ops_mutex);
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
+ int ret;
+
if ((!dev->has_func_id) || (dev->func_id != did->func_id))
return 0;
@@ -835,10 +839,15 @@ 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.
*/
- dev_dbg(&dev->dev,
- "skipping FUNC_ID match until userspace interaction\n");
- if (!dev->allow_func_id_match)
+ mutex_lock(&dev->socket->ops_mutex);
+ ret = dev->allow_func_id_match;
+ mutex_unlock(&dev->socket->ops_mutex);
+
+ if (!ret) {
+ dev_dbg(&dev->dev,
+ "skipping FUNC_ID match until userspace ACK\n");
return 0;
+ }
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
@@ -859,8 +868,6 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
return 0;
}
- dev_set_drvdata(&dev->dev, did);
-
return 1;
}
@@ -873,16 +880,16 @@ static int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
struct pcmcia_dynid *dynid;
/* match dynamic devices first */
- spin_lock(&p_drv->dynids.lock);
+ mutex_lock(&p_drv->dynids.lock);
list_for_each_entry(dynid, &p_drv->dynids.list, node) {
dev_dbg(dev, "trying to match to %s\n", drv->name);
if (pcmcia_devmatch(p_dev, &dynid->id)) {
dev_dbg(dev, "matched to %s\n", drv->name);
- spin_unlock(&p_drv->dynids.lock);
+ mutex_unlock(&p_drv->dynids.lock);
return 1;
}
}
- spin_unlock(&p_drv->dynids.lock);
+ mutex_unlock(&p_drv->dynids.lock);
#ifdef CONFIG_PCMCIA_IOCTL
/* matching by cardmgr */
@@ -970,13 +977,14 @@ static int runtime_suspend(struct device *dev)
return rc;
}
-static void runtime_resume(struct device *dev)
+static int runtime_resume(struct device *dev)
{
int rc;
down(&dev->sem);
rc = pcmcia_dev_resume(dev);
up(&dev->sem);
+ return rc;
}
/************************ per-device sysfs output ***************************/
@@ -1027,7 +1035,7 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute
if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
ret = runtime_suspend(dev);
else if (p_dev->suspended && !strncmp(buf, "on", 2))
- runtime_resume(dev);
+ ret = runtime_resume(dev);
return ret ? ret : count;
}
@@ -1059,19 +1067,14 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- int ret;
if (!count)
return -EINVAL;
- mutex_lock(&p_dev->socket->skt_mutex);
+ mutex_lock(&p_dev->socket->ops_mutex);
p_dev->allow_func_id_match = 1;
- mutex_unlock(&p_dev->socket->skt_mutex);
-
- ret = bus_rescan_devices(&pcmcia_bus_type);
- if (ret)
- printk(KERN_INFO "pcmcia: bus_rescan_devices failed after "
- "allowing func_id matches\n");
+ mutex_unlock(&p_dev->socket->ops_mutex);
+ pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY);
return count;
}
@@ -1099,8 +1102,13 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
struct pcmcia_driver *p_drv = NULL;
int ret = 0;
- if (p_dev->suspended)
+ mutex_lock(&p_dev->socket->ops_mutex);
+ if (p_dev->suspended) {
+ mutex_unlock(&p_dev->socket->ops_mutex);
return 0;
+ }
+ p_dev->suspended = 1;
+ mutex_unlock(&p_dev->socket->ops_mutex);
dev_dbg(dev, "suspending\n");
@@ -1117,6 +1125,9 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
"pcmcia: device %s (driver %s) did "
"not want to go to sleep (%d)\n",
p_dev->devname, p_drv->drv.name, ret);
+ mutex_lock(&p_dev->socket->ops_mutex);
+ p_dev->suspended = 0;
+ mutex_unlock(&p_dev->socket->ops_mutex);
goto out;
}
}
@@ -1127,8 +1138,6 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
}
out:
- if (!ret)
- p_dev->suspended = 1;
return ret;
}
@@ -1139,8 +1148,13 @@ static int pcmcia_dev_resume(struct device *dev)
struct pcmcia_driver *p_drv = NULL;
int ret = 0;
- if (!p_dev->suspended)
+ mutex_lock(&p_dev->socket->ops_mutex);
+ if (!p_dev->suspended) {
+ mutex_unlock(&p_dev->socket->ops_mutex);
return 0;
+ }
+ p_dev->suspended = 0;
+ mutex_unlock(&p_dev->socket->ops_mutex);
dev_dbg(dev, "resuming\n");
@@ -1161,8 +1175,6 @@ static int pcmcia_dev_resume(struct device *dev)
ret = p_drv->resume(p_dev);
out:
- if (!ret)
- p_dev->suspended = 0;
return ret;
}
@@ -1237,13 +1249,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
switch (event) {
case CS_EVENT_CARD_REMOVAL:
+ mutex_lock(&s->ops_mutex);
s->pcmcia_state.present = 0;
+ mutex_unlock(&s->ops_mutex);
pcmcia_card_remove(skt, NULL);
handle_event(skt, event);
+ mutex_lock(&s->ops_mutex);
+ destroy_cis_cache(s);
+ mutex_unlock(&s->ops_mutex);
break;
case CS_EVENT_CARD_INSERTION:
+ mutex_lock(&s->ops_mutex);
+ s->pcmcia_state.has_pfc = 0;
s->pcmcia_state.present = 1;
+ destroy_cis_cache(s); /* to be on the safe side... */
+ mutex_unlock(&s->ops_mutex);
pcmcia_card_add(skt);
handle_event(skt, event);
break;
@@ -1251,8 +1272,24 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
case CS_EVENT_EJECTION_REQUEST:
break;
- case CS_EVENT_PM_SUSPEND:
case CS_EVENT_PM_RESUME:
+ if (verify_cis_cache(skt) != 0) {
+ dev_dbg(&skt->dev, "cis mismatch - different card\n");
+ /* first, remove the card */
+ ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+ mutex_lock(&s->ops_mutex);
+ destroy_cis_cache(skt);
+ kfree(skt->fake_cis);
+ skt->fake_cis = NULL;
+ mutex_unlock(&s->ops_mutex);
+ /* now, add the new card */
+ ds_event(skt, CS_EVENT_CARD_INSERTION,
+ CS_EVENT_PRI_LOW);
+ }
+ handle_event(skt, event);
+ break;
+
+ case CS_EVENT_PM_SUSPEND:
case CS_EVENT_RESET_PHYSICAL:
case CS_EVENT_CARD_RESET:
default:
@@ -1275,9 +1312,13 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
if (!p_dev)
return NULL;
+ mutex_lock(&p_dev->socket->ops_mutex);
if (!p_dev->socket->pcmcia_state.present)
goto out;
+ if (p_dev->socket->pcmcia_state.dead)
+ goto out;
+
if (p_dev->_removed)
goto out;
@@ -1286,6 +1327,7 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
ret = p_dev;
out:
+ mutex_unlock(&p_dev->socket->ops_mutex);
pcmcia_put_dev(p_dev);
return ret;
}
@@ -1295,7 +1337,8 @@ EXPORT_SYMBOL(pcmcia_dev_present);
static struct pcmcia_callback pcmcia_bus_callback = {
.owner = THIS_MODULE,
.event = ds_event,
- .requery = pcmcia_bus_rescan,
+ .requery = pcmcia_requery,
+ .validate = pccard_validate_cis,
.suspend = pcmcia_bus_suspend,
.resume = pcmcia_bus_resume,
};
@@ -1313,17 +1356,17 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev,
return -ENODEV;
}
- /*
- * Ugly. But we want to wait for the socket threads to have started up.
- * We really should let the drivers themselves drive some of this..
- */
- msleep(250);
+ ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
+ if (ret) {
+ dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
+ pcmcia_put_socket(socket);
+ return ret;
+ }
#ifdef CONFIG_PCMCIA_IOCTL
init_waitqueue_head(&socket->queue);
#endif
INIT_LIST_HEAD(&socket->devices_list);
- INIT_WORK(&socket->device_add, pcmcia_delayed_add_device);
memset(&socket->pcmcia_state, 0, sizeof(u8));
socket->device_count = 0;
@@ -1345,14 +1388,20 @@ static void pcmcia_bus_remove_socket(struct device *dev,
if (!socket)
return;
+ mutex_lock(&socket->ops_mutex);
socket->pcmcia_state.dead = 1;
+ mutex_unlock(&socket->ops_mutex);
+
pccard_register_pcmcia(socket, NULL);
/* unregister any unbound devices */
mutex_lock(&socket->skt_mutex);
pcmcia_card_remove(socket, NULL);
+ release_cis_mem(socket);
mutex_unlock(&socket->skt_mutex);
+ sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr);
+
pcmcia_put_socket(socket);
return;
@@ -1383,8 +1432,6 @@ static int __init init_pcmcia_bus(void)
{
int ret;
- spin_lock_init(&pcmcia_dev_list_lock);
-
ret = bus_register(&pcmcia_bus_type);
if (ret < 0) {
printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret);
diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c
index d187ba4c5e0..89cfddca089 100644
--- a/drivers/pcmcia/electra_cf.c
+++ b/drivers/pcmcia/electra_cf.c
@@ -347,7 +347,7 @@ static int __devexit electra_cf_remove(struct of_device *ofdev)
return 0;
}
-static struct of_device_id electra_cf_match[] = {
+static const struct of_device_id electra_cf_match[] = {
{
.compatible = "electra-cf",
},
diff --git a/drivers/pcmcia/i82365.h b/drivers/pcmcia/i82365.h
index 622860c689d..849ef1b5d68 100644
--- a/drivers/pcmcia/i82365.h
+++ b/drivers/pcmcia/i82365.h
@@ -77,8 +77,8 @@
#define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */
#define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */
#define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */
-#define I365_VPP1_5V 0x01 /* Vpp2 = 5.0v */
-#define I365_VPP1_12V 0x02 /* Vpp2 = 12.0v */
+#define I365_VPP1_5V 0x01 /* Vpp1 = 5.0v */
+#define I365_VPP1_12V 0x02 /* Vpp1 = 12.0v */
/* Flags for I365_INTCTL */
#define I365_RING_ENA 0x80
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
index 26a621c9e2f..0ece2cd4a85 100644
--- a/drivers/pcmcia/m32r_cfc.c
+++ b/drivers/pcmcia/m32r_cfc.c
@@ -764,7 +764,7 @@ static int __init init_m32r_pcc(void)
for (i = 0 ; i < pcc_sockets ; i++) {
socket[i].socket.dev.parent = &pcc_device.dev;
socket[i].socket.ops = &pcc_operations;
- socket[i].socket.resource_ops = &pccard_nonstatic_ops;
+ socket[i].socket.resource_ops = &pccard_static_ops;
socket[i].socket.owner = THIS_MODULE;
socket[i].number = i;
ret = pcmcia_register_socket(&socket[i].socket);
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index 7f79c4e169a..61c21591812 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -1233,7 +1233,7 @@ static int __init m8xx_probe(struct of_device *ofdev,
socket[i].socket.io_offset = 0;
socket[i].socket.pci_irq = pcmcia_schlvl;
socket[i].socket.ops = &m8xx_services;
- socket[i].socket.resource_ops = &pccard_nonstatic_ops;
+ socket[i].socket.resource_ops = &pccard_iodyn_ops;
socket[i].socket.cb_dev = NULL;
socket[i].socket.dev.parent = &ofdev->dev;
socket[i].pcmcia = pcmcia;
@@ -1303,7 +1303,7 @@ static int m8xx_resume(struct platform_device *pdev)
#define m8xx_resume NULL
#endif
-static struct of_device_id m8xx_pcmcia_match[] = {
+static const struct of_device_id m8xx_pcmcia_match[] = {
{
.type = "pcmcia",
.compatible = "fsl,pq-pcmcia",
diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h
index 624442fc0d3..e74bebac269 100644
--- a/drivers/pcmcia/o2micro.h
+++ b/drivers/pcmcia/o2micro.h
@@ -116,13 +116,12 @@ static int o2micro_override(struct yenta_socket *socket)
* from Eric Still, 02Micro.
*/
u8 a, b;
+ bool use_speedup;
if (PCI_FUNC(socket->dev->devfn) == 0) {
a = config_readb(socket, O2_RESERVED1);
b = config_readb(socket, O2_RESERVED2);
-
- dev_printk(KERN_INFO, &socket->dev->dev,
- "O2: res at 0x94/0xD4: %02x/%02x\n", a, b);
+ dev_dbg(&socket->dev->dev, "O2: 0x94/0xD4: %02x/%02x\n", a, b);
switch (socket->dev->device) {
/*
@@ -135,23 +134,37 @@ static int o2micro_override(struct yenta_socket *socket)
case PCI_DEVICE_ID_O2_6812:
case PCI_DEVICE_ID_O2_6832:
case PCI_DEVICE_ID_O2_6836:
- case PCI_DEVICE_ID_O2_6933:
- dev_printk(KERN_INFO, &socket->dev->dev,
- "Yenta O2: old bridge, disabling read "
- "prefetch/write burst\n");
- config_writeb(socket, O2_RESERVED1,
- a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
- config_writeb(socket, O2_RESERVED2,
- b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
+ case PCI_DEVICE_ID_O2_6933:
+ use_speedup = false;
break;
-
default:
- dev_printk(KERN_INFO , &socket->dev->dev,
- "O2: enabling read prefetch/write burst\n");
+ use_speedup = true;
+ break;
+ }
+
+ /* the user may override our decision */
+ if (strcasecmp(o2_speedup, "on") == 0)
+ use_speedup = true;
+ else if (strcasecmp(o2_speedup, "off") == 0)
+ use_speedup = false;
+ else if (strcasecmp(o2_speedup, "default") != 0)
+ dev_warn(&socket->dev->dev,
+ "O2: Unknown parameter, using 'default'");
+
+ if (use_speedup) {
+ dev_info(&socket->dev->dev,
+ "O2: enabling read prefetch/write burst\n");
+ config_writeb(socket, O2_RESERVED1,
+ a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+ config_writeb(socket, O2_RESERVED2,
+ b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+ } else {
+ dev_info(&socket->dev->dev,
+ "O2: disabling read prefetch/write burst\n");
config_writeb(socket, O2_RESERVED1,
- a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+ a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
config_writeb(socket, O2_RESERVED2,
- b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+ b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
}
}
diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c
index 663781d2012..3ef99155239 100644
--- a/drivers/pcmcia/omap_cf.c
+++ b/drivers/pcmcia/omap_cf.c
@@ -71,8 +71,6 @@ struct omap_cf_socket {
#define POLL_INTERVAL (2 * HZ)
-#define SZ_2K (2 * SZ_1K)
-
/*--------------------------------------------------------------------------*/
static int omap_cf_ss_init(struct pcmcia_socket *s)
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c
index f73fd5beaa3..13a7132cf68 100644
--- a/drivers/pcmcia/pcmcia_ioctl.c
+++ b/drivers/pcmcia/pcmcia_ioctl.c
@@ -62,16 +62,15 @@ static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
unsigned int function)
{
struct pcmcia_device *p_dev = NULL;
- unsigned long flags;
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == function) {
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
return pcmcia_get_dev(p_dev);
}
}
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
return NULL;
}
@@ -169,7 +168,6 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
{
struct pcmcia_socket *s;
int ret = -ENOSYS;
- unsigned long flags;
down_read(&pcmcia_socket_list_rwsem);
list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
@@ -182,14 +180,13 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
/* you can't use the old interface if the new
* one was used before */
- spin_lock_irqsave(&s->lock, flags);
+ mutex_lock(&s->ops_mutex);
if ((s->resource_setup_new) &&
!(s->resource_setup_old)) {
- spin_unlock_irqrestore(&s->lock, flags);
+ mutex_unlock(&s->ops_mutex);
continue;
} else if (!(s->resource_setup_old))
s->resource_setup_old = 1;
- spin_unlock_irqrestore(&s->lock, flags);
switch (adj->Resource) {
case RES_MEMORY_RANGE:
@@ -208,10 +205,9 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
* last call to adjust_resource_info, we
* always need to assume this is the latest
* one... */
- spin_lock_irqsave(&s->lock, flags);
s->resource_setup_done = 1;
- spin_unlock_irqrestore(&s->lock, flags);
}
+ mutex_unlock(&s->ops_mutex);
}
}
up_read(&pcmcia_socket_list_rwsem);
@@ -470,7 +466,6 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
struct pcmcia_driver *p_drv;
struct pcmcia_device *p_dev;
int ret = 0;
- unsigned long flags;
s = pcmcia_get_socket(s);
if (!s)
@@ -490,7 +485,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
goto err_put_driver;
}
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == bind_info->function) {
if ((p_dev->dev.driver == &p_drv->drv)) {
@@ -499,7 +494,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
* registered, and it was registered
* by userspace before, we need to
* return the "instance". */
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
bind_info->instance = p_dev;
ret = -EBUSY;
goto err_put_module;
@@ -507,7 +502,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
/* the correct driver managed to bind
* itself magically to the correct
* device. */
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
p_dev->cardmgr = p_drv;
ret = 0;
goto err_put_module;
@@ -516,12 +511,12 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
/* there's already a device available where
* no device has been bound to yet. So we don't
* need to register a device! */
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
goto rescan;
}
}
}
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
p_dev = pcmcia_device_add(s, bind_info->function);
if (!p_dev) {
@@ -578,7 +573,6 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
dev_node_t *node;
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
- unsigned long flags;
int ret = 0;
#ifdef CONFIG_CARDBUS
@@ -617,7 +611,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
}
#endif
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ mutex_lock(&s->ops_mutex);
list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == bind_info->function) {
p_dev = pcmcia_get_dev(p_dev);
@@ -626,11 +620,11 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
goto found;
}
}
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
return -ENODEV;
found:
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ mutex_unlock(&s->ops_mutex);
p_drv = to_pcmcia_drv(p_dev->dev.driver);
if (p_drv && !p_dev->_locked) {
@@ -931,16 +925,16 @@ static int ds_ioctl(struct inode *inode, struct file *file,
ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
break;
case DS_SUSPEND_CARD:
- ret = pcmcia_suspend_card(s);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
break;
case DS_RESUME_CARD:
- ret = pcmcia_resume_card(s);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
break;
case DS_EJECT_CARD:
- err = pcmcia_eject_card(s);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
break;
case DS_INSERT_CARD:
- err = pcmcia_insert_card(s);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
break;
case DS_ACCESS_CONFIGURATION_REGISTER:
if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index d5db95644b6..b2df04199a2 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -43,6 +43,39 @@ module_param(io_speed, int, 0444);
static u8 pcmcia_used_irq[NR_IRQS];
#endif
+static int pcmcia_adjust_io_region(struct resource *res, unsigned long start,
+ unsigned long end, struct pcmcia_socket *s)
+{
+ if (s->resource_ops->adjust_io_region)
+ return s->resource_ops->adjust_io_region(res, start, end, s);
+ return -ENOMEM;
+}
+
+static struct resource *pcmcia_find_io_region(unsigned long base, int num,
+ unsigned long align,
+ struct pcmcia_socket *s)
+{
+ if (s->resource_ops->find_io)
+ return s->resource_ops->find_io(base, num, align, s);
+ return NULL;
+}
+
+int pcmcia_validate_mem(struct pcmcia_socket *s)
+{
+ if (s->resource_ops->validate_mem)
+ return s->resource_ops->validate_mem(s);
+ /* if there is no callback, we can assume that everything is OK */
+ return 0;
+}
+
+struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
+ int low, struct pcmcia_socket *s)
+{
+ if (s->resource_ops->find_mem)
+ return s->resource_ops->find_mem(base, num, align, low, s);
+ return NULL;
+}
+
/** alloc_io_space
*
@@ -158,14 +191,18 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
return -EINVAL;
s = p_dev->socket;
+
+ mutex_lock(&s->ops_mutex);
c = p_dev->function_config;
if (!(c->state & CONFIG_LOCKED)) {
dev_dbg(&s->dev, "Configuration isnt't locked\n");
+ mutex_unlock(&s->ops_mutex);
return -EACCES;
}
addr = (c->ConfigBase + reg->Offset) >> 1;
+ mutex_unlock(&s->ops_mutex);
switch (reg->Action) {
case CS_READ:
@@ -190,6 +227,7 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
memreq_t *req)
{
struct pcmcia_socket *s = p_dev->socket;
+ int ret;
wh--;
if (wh >= MAX_WIN)
@@ -198,12 +236,13 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
dev_dbg(&s->dev, "failure: requested page is zero\n");
return -EINVAL;
}
+ mutex_lock(&s->ops_mutex);
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;
+ ret = s->ops->set_mem_map(s, &s->win[wh]);
+ if (ret)
+ dev_warn(&s->dev, "failed to set_mem_map\n");
+ mutex_unlock(&s->ops_mutex);
+ return ret;
} /* pcmcia_map_mem_page */
EXPORT_SYMBOL(pcmcia_map_mem_page);
@@ -219,14 +258,18 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
config_t *c;
s = p_dev->socket;
+
+ mutex_lock(&s->ops_mutex);
c = p_dev->function_config;
if (!(s->state & SOCKET_PRESENT)) {
dev_dbg(&s->dev, "No card present\n");
+ mutex_unlock(&s->ops_mutex);
return -ENODEV;
}
if (!(c->state & CONFIG_LOCKED)) {
dev_dbg(&s->dev, "Configuration isnt't locked\n");
+ mutex_unlock(&s->ops_mutex);
return -EACCES;
}
@@ -251,10 +294,12 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
(mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
if (mod->Vpp1 != mod->Vpp2) {
dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n");
+ mutex_unlock(&s->ops_mutex);
return -EINVAL;
}
s->socket.Vpp = mod->Vpp1;
if (s->ops->set_socket(s, &s->socket)) {
+ mutex_unlock(&s->ops_mutex);
dev_printk(KERN_WARNING, &s->dev,
"Unable to set VPP\n");
return -EIO;
@@ -262,6 +307,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
} else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
(mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
+ mutex_unlock(&s->ops_mutex);
return -EINVAL;
}
@@ -286,6 +332,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
s->ops->set_io_map(s, &io_on);
}
}
+ mutex_unlock(&s->ops_mutex);
return 0;
} /* modify_configuration */
@@ -296,9 +343,11 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
{
pccard_io_map io = { 0, 0, 0, 0, 1 };
struct pcmcia_socket *s = p_dev->socket;
- config_t *c = p_dev->function_config;
+ config_t *c;
int i;
+ mutex_lock(&s->ops_mutex);
+ c = p_dev->function_config;
if (p_dev->_locked) {
p_dev->_locked = 0;
if (--(s->lock_count) == 0) {
@@ -321,6 +370,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
s->ops->set_io_map(s, &io);
}
}
+ mutex_unlock(&s->ops_mutex);
return 0;
} /* pcmcia_release_configuration */
@@ -337,10 +387,14 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
{
struct pcmcia_socket *s = p_dev->socket;
- config_t *c = p_dev->function_config;
+ int ret = -EINVAL;
+ config_t *c;
+
+ mutex_lock(&s->ops_mutex);
+ c = p_dev->function_config;
if (!p_dev->_io)
- return -EINVAL;
+ goto out;
p_dev->_io = 0;
@@ -348,7 +402,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
(c->io.NumPorts1 != req->NumPorts1) ||
(c->io.BasePort2 != req->BasePort2) ||
(c->io.NumPorts2 != req->NumPorts2))
- return -EINVAL;
+ goto out;
c->state &= ~CONFIG_IO_REQ;
@@ -356,28 +410,38 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
if (req->NumPorts2)
release_io_space(s, req->BasePort2, req->NumPorts2);
- return 0;
+out:
+ mutex_unlock(&s->ops_mutex);
+
+ return ret;
} /* pcmcia_release_io */
static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
{
struct pcmcia_socket *s = p_dev->socket;
- config_t *c = p_dev->function_config;
+ config_t *c;
+ int ret = -EINVAL;
+
+ mutex_lock(&s->ops_mutex);
+
+ c = p_dev->function_config;
if (!p_dev->_irq)
- return -EINVAL;
+ goto out;
+
p_dev->_irq = 0;
if (c->state & CONFIG_LOCKED)
- return -EACCES;
+ goto out;
+
if (c->irq.Attributes != req->Attributes) {
dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n");
- return -EINVAL;
+ goto out;
}
if (s->irq.AssignedIRQ != req->AssignedIRQ) {
dev_dbg(&s->dev, "IRQ must match assigned one\n");
- return -EINVAL;
+ goto out;
}
if (--s->irq.Config == 0) {
c->state &= ~CONFIG_IRQ_REQ;
@@ -390,8 +454,12 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[req->AssignedIRQ]--;
#endif
+ ret = 0;
- return 0;
+out:
+ mutex_unlock(&s->ops_mutex);
+
+ return ret;
} /* pcmcia_release_irq */
@@ -404,10 +472,12 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
if (wh >= MAX_WIN)
return -EINVAL;
+ mutex_lock(&s->ops_mutex);
win = &s->win[wh];
if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) {
dev_dbg(&s->dev, "not releasing unknown window\n");
+ mutex_unlock(&s->ops_mutex);
return -EINVAL;
}
@@ -423,6 +493,7 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
win->res = NULL;
}
p_dev->_win &= ~CLIENT_WIN_REQ(wh);
+ mutex_unlock(&s->ops_mutex);
return 0;
} /* pcmcia_release_window */
@@ -445,8 +516,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n");
return -EINVAL;
}
+
+ mutex_lock(&s->ops_mutex);
c = p_dev->function_config;
if (c->state & CONFIG_LOCKED) {
+ mutex_unlock(&s->ops_mutex);
dev_dbg(&s->dev, "Configuration is locked\n");
return -EACCES;
}
@@ -454,6 +528,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
/* Do power control. We don't allow changes in Vcc. */
s->socket.Vpp = req->Vpp;
if (s->ops->set_socket(s, &s->socket)) {
+ mutex_unlock(&s->ops_mutex);
dev_printk(KERN_WARNING, &s->dev,
"Unable to set socket state\n");
return -EINVAL;
@@ -476,6 +551,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
s->socket.io_irq = 0;
s->ops->set_socket(s, &s->socket);
s->lock_count++;
+ mutex_unlock(&s->ops_mutex);
/* Set up CIS configuration registers */
base = c->ConfigBase = req->ConfigBase;
@@ -524,6 +600,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
/* Configure I/O windows */
if (c->state & CONFIG_IO_REQ) {
+ mutex_lock(&s->ops_mutex);
iomap.speed = io_speed;
for (i = 0; i < MAX_IO_WIN; i++)
if (s->io[i].res) {
@@ -542,6 +619,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
s->ops->set_io_map(s, &iomap);
s->io[i].Config++;
}
+ mutex_unlock(&s->ops_mutex);
}
c->state |= CONFIG_LOCKED;
@@ -560,54 +638,65 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req)
{
struct pcmcia_socket *s = p_dev->socket;
config_t *c;
+ int ret = -EINVAL;
+
+ mutex_lock(&s->ops_mutex);
if (!(s->state & SOCKET_PRESENT)) {
dev_dbg(&s->dev, "No card present\n");
- return -ENODEV;
+ goto out;
}
if (!req)
- return -EINVAL;
+ goto out;
+
c = p_dev->function_config;
if (c->state & CONFIG_LOCKED) {
dev_dbg(&s->dev, "Configuration is locked\n");
- return -EACCES;
+ goto out;
}
if (c->state & CONFIG_IO_REQ) {
dev_dbg(&s->dev, "IO already configured\n");
- return -EBUSY;
+ goto out;
}
if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) {
dev_dbg(&s->dev, "bad attribute setting for IO region 1\n");
- return -EINVAL;
+ goto out;
}
if ((req->NumPorts2 > 0) &&
(req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) {
dev_dbg(&s->dev, "bad attribute setting for IO region 2\n");
- return -EINVAL;
+ goto out;
}
dev_dbg(&s->dev, "trying to allocate resource 1\n");
- if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
- req->NumPorts1, req->IOAddrLines)) {
+ ret = alloc_io_space(s, req->Attributes1, &req->BasePort1,
+ req->NumPorts1, req->IOAddrLines);
+ if (ret) {
dev_dbg(&s->dev, "allocation of resource 1 failed\n");
- return -EBUSY;
+ goto out;
}
if (req->NumPorts2) {
dev_dbg(&s->dev, "trying to allocate resource 2\n");
- if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
- req->NumPorts2, req->IOAddrLines)) {
+ ret = alloc_io_space(s, req->Attributes2, &req->BasePort2,
+ req->NumPorts2, req->IOAddrLines);
+ if (ret) {
dev_dbg(&s->dev, "allocation of resource 2 failed\n");
release_io_space(s, req->BasePort1, req->NumPorts1);
- return -EBUSY;
+ goto out;
}
}
c->io = *req;
c->state |= CONFIG_IO_REQ;
p_dev->_io = 1;
- return 0;
+ dev_dbg(&s->dev, "allocating resources succeeded: %d\n", ret);
+
+out:
+ mutex_unlock(&s->ops_mutex);
+
+ return ret;
} /* pcmcia_request_io */
EXPORT_SYMBOL(pcmcia_request_io);
@@ -636,18 +725,20 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
int ret = -EINVAL, irq = 0;
int type;
+ mutex_lock(&s->ops_mutex);
+
if (!(s->state & SOCKET_PRESENT)) {
dev_dbg(&s->dev, "No card present\n");
- return -ENODEV;
+ goto out;
}
c = p_dev->function_config;
if (c->state & CONFIG_LOCKED) {
dev_dbg(&s->dev, "Configuration is locked\n");
- return -EACCES;
+ goto out;
}
if (c->state & CONFIG_IRQ_REQ) {
dev_dbg(&s->dev, "IRQ already configured\n");
- return -EBUSY;
+ goto out;
}
/* Decide what type of interrupt we are registering */
@@ -708,7 +799,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
if (ret && !s->irq.AssignedIRQ) {
if (!s->pci_irq) {
dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
- return ret;
+ goto out;
}
type = IRQF_SHARED;
irq = s->pci_irq;
@@ -720,7 +811,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
if (ret) {
dev_printk(KERN_INFO, &s->dev,
"request_irq() failed\n");
- return ret;
+ goto out;
}
}
@@ -743,7 +834,10 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
pcmcia_used_irq[irq]++;
#endif
- return 0;
+ ret = 0;
+out:
+ mutex_unlock(&s->ops_mutex);
+ return ret;
} /* pcmcia_request_irq */
EXPORT_SYMBOL(pcmcia_request_irq);
@@ -796,6 +890,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
return -EINVAL;
}
+ mutex_lock(&s->ops_mutex);
win = &s->win[w];
if (!(s->features & SS_CAP_STATIC_MAP)) {
@@ -803,6 +898,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
(req->Attributes & WIN_MAP_BELOW_1MB), s);
if (!win->res) {
dev_dbg(&s->dev, "allocating mem region failed\n");
+ mutex_unlock(&s->ops_mutex);
return -EINVAL;
}
}
@@ -821,8 +917,10 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
if (req->Attributes & WIN_USE_WAIT)
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");
+ mutex_unlock(&s->ops_mutex);
return -EIO;
}
s->state |= SOCKET_WIN_REQ(w);
@@ -833,6 +931,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
else
req->Base = win->res->start;
+ mutex_unlock(&s->ops_mutex);
*wh = w + 1;
return 0;
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
index 52db17263d8..e6f7d410aed 100644
--- a/drivers/pcmcia/rsrc_mgr.c
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -21,60 +21,12 @@
#include <pcmcia/cistpl.h>
#include "cs_internal.h"
-
-int pcmcia_validate_mem(struct pcmcia_socket *s)
-{
- if (s->resource_ops->validate_mem)
- return s->resource_ops->validate_mem(s);
- /* if there is no callback, we can assume that everything is OK */
- return 0;
-}
-EXPORT_SYMBOL(pcmcia_validate_mem);
-
-int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start,
- unsigned long r_end, struct pcmcia_socket *s)
-{
- if (s->resource_ops->adjust_io_region)
- return s->resource_ops->adjust_io_region(res, r_start, r_end, s);
- return -ENOMEM;
-}
-EXPORT_SYMBOL(pcmcia_adjust_io_region);
-
-struct resource *pcmcia_find_io_region(unsigned long base, int num,
- unsigned long align, struct pcmcia_socket *s)
-{
- if (s->resource_ops->find_io)
- return s->resource_ops->find_io(base, num, align, s);
- return NULL;
-}
-EXPORT_SYMBOL(pcmcia_find_io_region);
-
-struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
- int low, struct pcmcia_socket *s)
-{
- if (s->resource_ops->find_mem)
- return s->resource_ops->find_mem(base, num, align, low, s);
- return NULL;
-}
-EXPORT_SYMBOL(pcmcia_find_mem_region);
-
-void release_resource_db(struct pcmcia_socket *s)
-{
- if (s->resource_ops->exit)
- s->resource_ops->exit(s);
-}
-
-
static int static_init(struct pcmcia_socket *s)
{
- unsigned long flags;
-
/* the good thing about SS_CAP_STATIC_MAP sockets is
* that they don't need a resource database */
- spin_lock_irqsave(&s->lock, flags);
s->resource_setup_done = 1;
- spin_unlock_irqrestore(&s->lock, flags);
return 0;
}
@@ -114,22 +66,21 @@ struct pcmcia_align_data {
unsigned long offset;
};
-static void pcmcia_align(void *align_data, struct resource *res,
- unsigned long size, unsigned long align)
+static resource_size_t pcmcia_align(void *align_data,
+ const struct resource *res,
+ resource_size_t size, resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
- unsigned long start;
+ resource_size_t start;
start = (res->start & ~data->mask) + data->offset;
if (start < res->start)
start += data->mask + 1;
- res->start = start;
#ifdef CONFIG_X86
if (res->flags & IORESOURCE_IO) {
if (start & 0x300) {
start = (start + 0x3ff) & ~0x3ff;
- res->start = start;
}
}
#endif
@@ -137,9 +88,11 @@ static void pcmcia_align(void *align_data, struct resource *res,
#ifdef CONFIG_M68K
if (res->flags & IORESOURCE_IO) {
if ((res->start + size - 1) >= 1024)
- res->start = res->end;
+ start = res->end;
}
#endif
+
+ return start;
}
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 9b0dc433a8c..4663b3fa9f9 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -55,11 +55,10 @@ struct resource_map {
struct socket_data {
struct resource_map mem_db;
+ struct resource_map mem_db_valid;
struct resource_map io_db;
- unsigned int rsrc_mem_probe;
};
-static DEFINE_MUTEX(rsrc_mutex);
#define MEM_PROBE_LOW (1 << 0)
#define MEM_PROBE_HIGH (1 << 1)
@@ -125,8 +124,10 @@ static int add_interval(struct resource_map *map, u_long base, u_long num)
struct resource_map *p, *q;
for (p = map; ; p = p->next) {
- if ((p != map) && (p->base+p->num-1 >= base))
- return -1;
+ if ((p != map) && (p->base+p->num >= base)) {
+ p->num = max(num + base - p->base, p->num);
+ return 0;
+ }
if ((p->next == map) || (p->next->base > base+num-1))
break;
}
@@ -264,36 +265,44 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
}
#endif
-/*======================================================================
-
- This is tricky... when we set up CIS memory, we try to validate
- the memory window space allocations.
-
-======================================================================*/
+/*======================================================================*/
-/* Validation function for cards with a valid CIS */
+/**
+ * readable() - iomem validation function for cards with a valid CIS
+ */
static int readable(struct pcmcia_socket *s, struct resource *res,
unsigned int *count)
{
- int ret = -1;
+ int ret = -EINVAL;
+
+ if (s->fake_cis) {
+ dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
+ return 0;
+ }
s->cis_mem.res = res;
s->cis_virt = ioremap(res->start, s->map_size);
if (s->cis_virt) {
- ret = pccard_validate_cis(s, count);
- /* invalidate mapping and CIS cache */
+ mutex_unlock(&s->ops_mutex);
+ /* as we're only called from pcmcia.c, we're safe */
+ if (s->callback->validate)
+ ret = s->callback->validate(s, count);
+ /* invalidate mapping */
+ mutex_lock(&s->ops_mutex);
iounmap(s->cis_virt);
s->cis_virt = NULL;
- destroy_cis_cache(s);
}
s->cis_mem.res = NULL;
- if ((ret != 0) || (*count == 0))
- return 0;
- return 1;
+ if ((ret) || (*count == 0))
+ return -EINVAL;
+ return 0;
}
-/* Validation function for simple memory cards */
-static int checksum(struct pcmcia_socket *s, struct resource *res)
+/**
+ * checksum() - iomem validation function for simple memory cards
+ */
+static int checksum(struct pcmcia_socket *s, struct resource *res,
+ unsigned int *value)
{
pccard_mem_map map;
int i, a = 0, b = -1, d;
@@ -321,61 +330,90 @@ static int checksum(struct pcmcia_socket *s, struct resource *res)
iounmap(virt);
}
- return (b == -1) ? -1 : (a>>1);
+ if (b == -1)
+ return -EINVAL;
+
+ *value = a;
+
+ return 0;
}
-static int
-cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+/**
+ * do_validate_mem() - low level validate a memory region for PCMCIA use
+ * @s: PCMCIA socket to validate
+ * @base: start address of resource to check
+ * @size: size of resource to check
+ * @validate: validation function to use
+ *
+ * do_validate_mem() splits up the memory region which is to be checked
+ * into two parts. Both are passed to the @validate() function. If
+ * @validate() returns non-zero, or the value parameter to @validate()
+ * is zero, or the value parameter is different between both calls,
+ * the check fails, and -EINVAL is returned. Else, 0 is returned.
+ */
+static int do_validate_mem(struct pcmcia_socket *s,
+ unsigned long base, unsigned long size,
+ int validate (struct pcmcia_socket *s,
+ struct resource *res,
+ unsigned int *value))
{
+ struct socket_data *s_data = s->resource_data;
struct resource *res1, *res2;
- unsigned int info1, info2;
- int ret = 0;
+ unsigned int info1 = 1, info2 = 1;
+ int ret = -EINVAL;
res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
"PCMCIA memprobe");
if (res1 && res2) {
- ret = readable(s, res1, &info1);
- ret += readable(s, res2, &info2);
+ ret = 0;
+ if (validate) {
+ ret = validate(s, res1, &info1);
+ ret += validate(s, res2, &info2);
+ }
}
free_region(res2);
free_region(res1);
- return (ret == 2) && (info1 == info2);
-}
-
-static int
-checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
-{
- struct resource *res1, *res2;
- int a = -1, b = -1;
+ dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
+ base, base+size-1, res1, res2, ret, info1, info2);
- res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
- res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
- "PCMCIA memprobe");
+ if ((ret) || (info1 != info2) || (info1 == 0))
+ return -EINVAL;
- if (res1 && res2) {
- a = checksum(s, res1);
- b = checksum(s, res2);
+ if (validate && !s->fake_cis) {
+ /* move it to the validated data set */
+ add_interval(&s_data->mem_db_valid, base, size);
+ sub_interval(&s_data->mem_db, base, size);
}
- free_region(res2);
- free_region(res1);
-
- return (a == b) && (a >= 0);
+ return 0;
}
-/*======================================================================
-
- The memory probe. If the memory list includes a 64K-aligned block
- below 1MB, we probe in 64K chunks, and as soon as we accumulate at
- least mem_limit free space, we quit.
-======================================================================*/
-
-static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
+/**
+ * do_mem_probe() - validate a memory region for PCMCIA use
+ * @s: PCMCIA socket to validate
+ * @base: start address of resource to check
+ * @num: size of resource to check
+ * @validate: validation function to use
+ * @fallback: validation function to use if validate fails
+ *
+ * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
+ * To do so, the area is split up into sensible parts, and then passed
+ * into the @validate() function. Only if @validate() and @fallback() fail,
+ * the area is marked as unavaibale for use by the PCMCIA subsystem. The
+ * function returns the size of the usable memory area.
+ */
+static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
+ int validate (struct pcmcia_socket *s,
+ struct resource *res,
+ unsigned int *value),
+ int fallback (struct pcmcia_socket *s,
+ struct resource *res,
+ unsigned int *value))
{
struct socket_data *s_data = s->resource_data;
u_long i, j, bad, fail, step;
@@ -393,15 +431,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
for (i = j = base; i < base+num; i = j + step) {
if (!fail) {
for (j = i; j < base+num; j += step) {
- if (cis_readable(s, j, step))
+ if (!do_validate_mem(s, j, step, validate))
break;
}
fail = ((i == base) && (j == base+num));
}
- if (fail) {
- for (j = i; j < base+num; j += 2*step)
- if (checksum_match(s, j, step) &&
- checksum_match(s, j + step, step))
+ if ((fail) && (fallback)) {
+ for (j = i; j < base+num; j += step)
+ if (!do_validate_mem(s, j, step, fallback))
break;
}
if (i != j) {
@@ -416,8 +453,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
return num - bad;
}
+
#ifdef CONFIG_PCMCIA_PROBE
+/**
+ * inv_probe() - top-to-bottom search for one usuable high memory area
+ * @s: PCMCIA socket to validate
+ * @m: resource_map to check
+ */
static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
{
struct socket_data *s_data = s->resource_data;
@@ -432,9 +475,18 @@ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
}
if (m->base < 0x100000)
return 0;
- return do_mem_probe(m->base, m->num, s);
+ return do_mem_probe(s, m->base, m->num, readable, checksum);
}
+/**
+ * validate_mem() - memory probe function
+ * @s: PCMCIA socket to validate
+ * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
+ *
+ * The memory probe. If the memory list includes a 64K-aligned block
+ * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
+ * least mem_limit free space, we quit. Returns 0 on usuable ports.
+ */
static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
{
struct resource_map *m, mm;
@@ -446,6 +498,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
if (probe_mask & MEM_PROBE_HIGH) {
if (inv_probe(s_data->mem_db.next, s) > 0)
return 0;
+ if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
+ return 0;
dev_printk(KERN_NOTICE, &s->dev,
"cs: warning: no high memory space available!\n");
return -ENODEV;
@@ -457,7 +511,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
if (mm.base >= 0x100000)
continue;
if ((mm.base | mm.num) & 0xffff) {
- ok += do_mem_probe(mm.base, mm.num, s);
+ ok += do_mem_probe(s, mm.base, mm.num, readable,
+ checksum);
continue;
}
/* Special probe for 64K-aligned block */
@@ -467,7 +522,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
if (ok >= mem_limit)
sub_interval(&s_data->mem_db, b, 0x10000);
else
- ok += do_mem_probe(b, 0x10000, s);
+ ok += do_mem_probe(s, b, 0x10000,
+ readable, checksum);
}
}
}
@@ -480,6 +536,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
#else /* CONFIG_PCMCIA_PROBE */
+/**
+ * validate_mem() - memory probe function
+ * @s: PCMCIA socket to validate
+ * @probe_mask: ignored
+ *
+ * Returns 0 on usuable ports.
+ */
static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
{
struct resource_map *m, mm;
@@ -488,7 +551,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
mm = *m;
- ok += do_mem_probe(mm.base, mm.num, s);
+ ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
}
if (ok > 0)
return 0;
@@ -498,31 +561,31 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
#endif /* CONFIG_PCMCIA_PROBE */
-/*
+/**
+ * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
+ * @s: PCMCIA socket to validate
+ *
+ * This is tricky... when we set up CIS memory, we try to validate
+ * the memory window space allocations.
+ *
* Locking note: Must be called with skt_mutex held!
*/
static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
{
struct socket_data *s_data = s->resource_data;
unsigned int probe_mask = MEM_PROBE_LOW;
- int ret = 0;
+ int ret;
- if (!probe_mem)
+ if (!probe_mem || !(s->state & SOCKET_PRESENT))
return 0;
- mutex_lock(&rsrc_mutex);
-
if (s->features & SS_CAP_PAGE_REGS)
probe_mask = MEM_PROBE_HIGH;
- if (probe_mask & ~s_data->rsrc_mem_probe) {
- if (s->state & SOCKET_PRESENT)
- ret = validate_mem(s, probe_mask);
- if (!ret)
- s_data->rsrc_mem_probe |= probe_mask;
- }
+ ret = validate_mem(s, probe_mask);
- mutex_unlock(&rsrc_mutex);
+ if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
+ return 0;
return ret;
}
@@ -533,8 +596,8 @@ struct pcmcia_align_data {
struct resource_map *map;
};
-static void
-pcmcia_common_align(void *align_data, struct resource *res,
+static resource_size_t
+pcmcia_common_align(void *align_data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
@@ -545,17 +608,18 @@ pcmcia_common_align(void *align_data, struct resource *res,
start = (res->start & ~data->mask) + data->offset;
if (start < res->start)
start += data->mask + 1;
- res->start = start;
+ return start;
}
-static void
-pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
- resource_size_t align)
+static resource_size_t
+pcmcia_align(void *align_data, const struct resource *res,
+ resource_size_t size, resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
struct resource_map *m;
+ resource_size_t start;
- pcmcia_common_align(data, res, size, align);
+ start = pcmcia_common_align(data, res, size, align);
for (m = data->map->next; m != data->map; m = m->next) {
unsigned long start = m->base;
@@ -567,8 +631,7 @@ pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
* fit here.
*/
if (res->start < start) {
- res->start = start;
- pcmcia_common_align(data, res, size, align);
+ start = pcmcia_common_align(data, res, size, align);
}
/*
@@ -586,7 +649,9 @@ pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
* If we failed to find something suitable, ensure we fail.
*/
if (m == data->map)
- res->start = res->end;
+ start = res->end;
+
+ return start;
}
/*
@@ -600,7 +665,6 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
struct socket_data *s_data = s->resource_data;
int ret = -ENOMEM;
- mutex_lock(&rsrc_mutex);
for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
unsigned long start = m->base;
unsigned long end = m->base + m->num - 1;
@@ -611,7 +675,6 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
ret = adjust_resource(res, r_start, r_end - r_start + 1);
break;
}
- mutex_unlock(&rsrc_mutex);
return ret;
}
@@ -645,7 +708,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
data.offset = base & data.mask;
data.map = &s_data->io_db;
- mutex_lock(&rsrc_mutex);
#ifdef CONFIG_PCI
if (s->cb_dev) {
ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
@@ -654,7 +716,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
#endif
ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
1, pcmcia_align, &data);
- mutex_unlock(&rsrc_mutex);
if (ret != 0) {
kfree(res);
@@ -670,15 +731,15 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
struct socket_data *s_data = s->resource_data;
struct pcmcia_align_data data;
unsigned long min, max;
- int ret, i;
+ int ret, i, j;
low = low || !(s->features & SS_CAP_PAGE_REGS);
data.mask = align - 1;
data.offset = base & data.mask;
- data.map = &s_data->mem_db;
for (i = 0; i < 2; i++) {
+ data.map = &s_data->mem_db_valid;
if (low) {
max = 0x100000UL;
min = base < max ? base : 0;
@@ -687,17 +748,23 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
min = 0x100000UL + base;
}
- mutex_lock(&rsrc_mutex);
+ for (j = 0; j < 2; j++) {
#ifdef CONFIG_PCI
- if (s->cb_dev) {
- ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
- 1, min, 0,
- pcmcia_align, &data);
- } else
+ if (s->cb_dev) {
+ ret = pci_bus_alloc_resource(s->cb_dev->bus,
+ res, num, 1, min, 0,
+ pcmcia_align, &data);
+ } else
#endif
- ret = allocate_resource(&iomem_resource, res, num, min,
- max, 1, pcmcia_align, &data);
- mutex_unlock(&rsrc_mutex);
+ {
+ ret = allocate_resource(&iomem_resource,
+ res, num, min, max, 1,
+ pcmcia_align, &data);
+ }
+ if (ret == 0)
+ break;
+ data.map = &s_data->mem_db;
+ }
if (ret == 0 || low)
break;
low = 1;
@@ -720,25 +787,18 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
if (end < start)
return -EINVAL;
- mutex_lock(&rsrc_mutex);
switch (action) {
case ADD_MANAGED_RESOURCE:
ret = add_interval(&data->mem_db, start, size);
+ if (!ret)
+ do_mem_probe(s, start, size, NULL, NULL);
break;
case REMOVE_MANAGED_RESOURCE:
ret = sub_interval(&data->mem_db, start, size);
- if (!ret) {
- struct pcmcia_socket *socket;
- down_read(&pcmcia_socket_list_rwsem);
- list_for_each_entry(socket, &pcmcia_socket_list, socket_list)
- release_cis_mem(socket);
- up_read(&pcmcia_socket_list_rwsem);
- }
break;
default:
ret = -EINVAL;
}
- mutex_unlock(&rsrc_mutex);
return ret;
}
@@ -756,7 +816,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
if (end > IO_SPACE_LIMIT)
return -EINVAL;
- mutex_lock(&rsrc_mutex);
switch (action) {
case ADD_MANAGED_RESOURCE:
if (add_interval(&data->io_db, start, size) != 0) {
@@ -775,7 +834,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
ret = -EINVAL;
break;
}
- mutex_unlock(&rsrc_mutex);
return ret;
}
@@ -801,8 +859,7 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
return -EINVAL;
#endif
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- res = s->cb_dev->bus->resource[i];
+ pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
if (!res)
continue;
@@ -859,6 +916,7 @@ static int nonstatic_init(struct pcmcia_socket *s)
return -ENOMEM;
data->mem_db.next = &data->mem_db;
+ data->mem_db_valid.next = &data->mem_db_valid;
data->io_db.next = &data->io_db;
s->resource_data = (void *) data;
@@ -873,7 +931,10 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
struct socket_data *data = s->resource_data;
struct resource_map *p, *q;
- mutex_lock(&rsrc_mutex);
+ for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
+ q = p->next;
+ kfree(p);
+ }
for (p = data->mem_db.next; p != &data->mem_db; p = q) {
q = p->next;
kfree(p);
@@ -882,7 +943,6 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
q = p->next;
kfree(p);
}
- mutex_unlock(&rsrc_mutex);
}
@@ -909,7 +969,7 @@ static ssize_t show_io_db(struct device *dev,
struct resource_map *p;
ssize_t ret = 0;
- mutex_lock(&rsrc_mutex);
+ mutex_lock(&s->ops_mutex);
data = s->resource_data;
for (p = data->io_db.next; p != &data->io_db; p = p->next) {
@@ -921,7 +981,7 @@ static ssize_t show_io_db(struct device *dev,
((unsigned long) p->base + p->num - 1));
}
- mutex_unlock(&rsrc_mutex);
+ mutex_unlock(&s->ops_mutex);
return ret;
}
@@ -949,9 +1009,11 @@ static ssize_t store_io_db(struct device *dev,
if (end_addr < start_addr)
return -EINVAL;
+ mutex_lock(&s->ops_mutex);
ret = adjust_io(s, add, start_addr, end_addr);
if (!ret)
s->resource_setup_new = 1;
+ mutex_unlock(&s->ops_mutex);
return ret ? ret : count;
}
@@ -965,9 +1027,19 @@ static ssize_t show_mem_db(struct device *dev,
struct resource_map *p;
ssize_t ret = 0;
- mutex_lock(&rsrc_mutex);
+ mutex_lock(&s->ops_mutex);
data = s->resource_data;
+ for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
+ p = p->next) {
+ if (ret > (PAGE_SIZE - 10))
+ continue;
+ ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
+ "0x%08lx - 0x%08lx\n",
+ ((unsigned long) p->base),
+ ((unsigned long) p->base + p->num - 1));
+ }
+
for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
if (ret > (PAGE_SIZE - 10))
continue;
@@ -977,7 +1049,7 @@ static ssize_t show_mem_db(struct device *dev,
((unsigned long) p->base + p->num - 1));
}
- mutex_unlock(&rsrc_mutex);
+ mutex_unlock(&s->ops_mutex);
return ret;
}
@@ -1005,9 +1077,11 @@ static ssize_t store_mem_db(struct device *dev,
if (end_addr < start_addr)
return -EINVAL;
+ mutex_lock(&s->ops_mutex);
ret = adjust_memory(s, add, start_addr, end_addr);
if (!ret)
s->resource_setup_new = 1;
+ mutex_unlock(&s->ops_mutex);
return ret ? ret : count;
}
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index 7a456000332..08278016e58 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -88,15 +88,14 @@ static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL);
static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- ssize_t ret;
struct pcmcia_socket *s = to_socket(dev);
if (!count)
return -EINVAL;
- ret = pcmcia_insert_card(s);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
- return ret ? ret : count;
+ return count;
}
static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
@@ -113,18 +112,22 @@ static ssize_t pccard_store_card_pm_state(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- ssize_t ret = -EINVAL;
struct pcmcia_socket *s = to_socket(dev);
+ ssize_t ret = count;
if (!count)
return -EINVAL;
- if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3))
- ret = pcmcia_suspend_card(s);
- else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2))
- ret = pcmcia_resume_card(s);
+ if (!strncmp(buf, "off", 3))
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
+ else {
+ if (!strncmp(buf, "on", 2))
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
+ else
+ ret = -EINVAL;
+ }
- return ret ? -ENODEV : count;
+ return ret;
}
static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state);
@@ -132,15 +135,14 @@ static ssize_t pccard_store_eject(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- ssize_t ret;
struct pcmcia_socket *s = to_socket(dev);
if (!count)
return -EINVAL;
- ret = pcmcia_eject_card(s);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
- return ret ? ret : count;
+ return count;
}
static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
@@ -167,7 +169,9 @@ static ssize_t pccard_store_irq_mask(struct device *dev,
ret = sscanf(buf, "0x%x\n", &mask);
if (ret == 1) {
+ mutex_lock(&s->ops_mutex);
s->irq_mask &= mask;
+ mutex_unlock(&s->ops_mutex);
ret = 0;
}
@@ -187,163 +191,21 @@ static ssize_t pccard_store_resource(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- unsigned long flags;
struct pcmcia_socket *s = to_socket(dev);
if (!count)
return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
+ mutex_lock(&s->ops_mutex);
if (!s->resource_setup_done)
s->resource_setup_done = 1;
- spin_unlock_irqrestore(&s->lock, flags);
-
- mutex_lock(&s->skt_mutex);
- if ((s->callback) &&
- (s->state & SOCKET_PRESENT) &&
- !(s->state & SOCKET_CARDBUS)) {
- if (try_module_get(s->callback->owner)) {
- s->callback->requery(s, 0);
- module_put(s->callback->owner);
- }
- }
- mutex_unlock(&s->skt_mutex);
-
- return count;
-}
-static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource);
+ mutex_unlock(&s->ops_mutex);
-
-static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count)
-{
- tuple_t tuple;
- int status, i;
- loff_t pointer = 0;
- ssize_t ret = 0;
- u_char *tuplebuffer;
- u_char *tempbuffer;
-
- tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL);
- if (!tuplebuffer)
- return -ENOMEM;
-
- tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL);
- if (!tempbuffer) {
- ret = -ENOMEM;
- goto free_tuple;
- }
-
- memset(&tuple, 0, sizeof(tuple_t));
-
- tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
- tuple.DesiredTuple = RETURN_FIRST_TUPLE;
- tuple.TupleOffset = 0;
-
- status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
- while (!status) {
- tuple.TupleData = tuplebuffer;
- tuple.TupleDataMax = 255;
- memset(tuplebuffer, 0, sizeof(u_char) * 255);
-
- status = pccard_get_tuple_data(s, &tuple);
- if (status)
- break;
-
- if (off < (pointer + 2 + tuple.TupleDataLen)) {
- tempbuffer[0] = tuple.TupleCode & 0xff;
- tempbuffer[1] = tuple.TupleLink & 0xff;
- for (i = 0; i < tuple.TupleDataLen; i++)
- tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
-
- for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
- if (((i + pointer) >= off) &&
- (i + pointer) < (off + count)) {
- buf[ret] = tempbuffer[i];
- ret++;
- }
- }
- }
-
- pointer += 2 + tuple.TupleDataLen;
-
- if (pointer >= (off + count))
- break;
-
- if (tuple.TupleCode == CISTPL_END)
- break;
- status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
- }
-
- kfree(tempbuffer);
- free_tuple:
- kfree(tuplebuffer);
-
- return ret;
-}
-
-static ssize_t pccard_show_cis(struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- unsigned int size = 0x200;
-
- if (off >= size)
- count = 0;
- else {
- struct pcmcia_socket *s;
- unsigned int chains;
-
- if (off + count > size)
- count = size - off;
-
- s = to_socket(container_of(kobj, struct device, kobj));
-
- if (!(s->state & SOCKET_PRESENT))
- return -ENODEV;
- if (pccard_validate_cis(s, &chains))
- return -EIO;
- if (!chains)
- return -ENODATA;
-
- count = pccard_extract_cis(s, buf, off, count);
- }
-
- return count;
-}
-
-static ssize_t pccard_store_cis(struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj));
- int error;
-
- if (off)
- return -EINVAL;
-
- if (count >= CISTPL_MAX_CIS_SIZE)
- return -EINVAL;
-
- if (!(s->state & SOCKET_PRESENT))
- return -ENODEV;
-
- error = pcmcia_replace_cis(s, buf, count);
- if (error)
- return -EIO;
-
- mutex_lock(&s->skt_mutex);
- if ((s->callback) && (s->state & SOCKET_PRESENT) &&
- !(s->state & SOCKET_CARDBUS)) {
- if (try_module_get(s->callback->owner)) {
- s->callback->requery(s, 1);
- module_put(s->callback->owner);
- }
- }
- mutex_unlock(&s->skt_mutex);
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
return count;
}
-
+static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource);
static struct attribute *pccard_socket_attributes[] = {
&dev_attr_card_type.attr,
@@ -362,28 +224,12 @@ static const struct attribute_group socket_attrs = {
.attrs = pccard_socket_attributes,
};
-static struct bin_attribute pccard_cis_attr = {
- .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
- .size = 0x200,
- .read = pccard_show_cis,
- .write = pccard_store_cis,
-};
-
int pccard_sysfs_add_socket(struct device *dev)
{
- int ret = 0;
-
- ret = sysfs_create_group(&dev->kobj, &socket_attrs);
- if (!ret) {
- ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
- if (ret)
- sysfs_remove_group(&dev->kobj, &socket_attrs);
- }
- return ret;
+ return sysfs_create_group(&dev->kobj, &socket_attrs);
}
void pccard_sysfs_remove_socket(struct device *dev)
{
- sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr);
sysfs_remove_group(&dev->kobj, &socket_attrs);
}
diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c
new file mode 100644
index 00000000000..61560cd6e28
--- /dev/null
+++ b/drivers/pcmcia/xxs1500_ss.c
@@ -0,0 +1,350 @@
+/*
+ * PCMCIA socket code for the MyCable XXS1500 system.
+ *
+ * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/resource.h>
+#include <linux/spinlock.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cistpl.h>
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#define MEM_MAP_SIZE 0x400000
+#define IO_MAP_SIZE 0x1000
+
+
+/*
+ * 3.3V cards only; all interfacing is done via gpios:
+ *
+ * 0/1: carddetect (00 = card present, xx = huh)
+ * 4: card irq
+ * 204: reset (high-act)
+ * 205: buffer enable (low-act)
+ * 208/209: card voltage key (00,01,10,11)
+ * 210: battwarn
+ * 211: batdead
+ * 214: power (low-act)
+ */
+#define GPIO_CDA 0
+#define GPIO_CDB 1
+#define GPIO_CARDIRQ 4
+#define GPIO_RESET 204
+#define GPIO_OUTEN 205
+#define GPIO_VSL 208
+#define GPIO_VSH 209
+#define GPIO_BATTDEAD 210
+#define GPIO_BATTWARN 211
+#define GPIO_POWER 214
+
+struct xxs1500_pcmcia_sock {
+ struct pcmcia_socket socket;
+ void *virt_io;
+
+ phys_addr_t phys_io;
+ phys_addr_t phys_attr;
+ phys_addr_t phys_mem;
+
+ /* previous flags for set_socket() */
+ unsigned int old_flags;
+};
+
+#define to_xxs_socket(x) container_of(x, struct xxs1500_pcmcia_sock, socket)
+
+static irqreturn_t cdirq(int irq, void *data)
+{
+ struct xxs1500_pcmcia_sock *sock = data;
+
+ pcmcia_parse_events(&sock->socket, SS_DETECT);
+
+ return IRQ_HANDLED;
+}
+
+static int xxs1500_pcmcia_configure(struct pcmcia_socket *skt,
+ struct socket_state_t *state)
+{
+ struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
+ unsigned int changed;
+
+ /* power control */
+ switch (state->Vcc) {
+ case 0:
+ gpio_set_value(GPIO_POWER, 1); /* power off */
+ break;
+ case 33:
+ gpio_set_value(GPIO_POWER, 0); /* power on */
+ break;
+ case 50:
+ default:
+ return -EINVAL;
+ }
+
+ changed = state->flags ^ sock->old_flags;
+
+ if (changed & SS_RESET) {
+ if (state->flags & SS_RESET) {
+ gpio_set_value(GPIO_RESET, 1); /* assert reset */
+ gpio_set_value(GPIO_OUTEN, 1); /* buffers off */
+ } else {
+ gpio_set_value(GPIO_RESET, 0); /* deassert reset */
+ gpio_set_value(GPIO_OUTEN, 0); /* buffers on */
+ msleep(500);
+ }
+ }
+
+ sock->old_flags = state->flags;
+
+ return 0;
+}
+
+static int xxs1500_pcmcia_get_status(struct pcmcia_socket *skt,
+ unsigned int *value)
+{
+ unsigned int status;
+ int i;
+
+ status = 0;
+
+ /* check carddetects: GPIO[0:1] must both be low */
+ if (!gpio_get_value(GPIO_CDA) && !gpio_get_value(GPIO_CDB))
+ status |= SS_DETECT;
+
+ /* determine card voltage: GPIO[208:209] binary value */
+ i = (!!gpio_get_value(GPIO_VSL)) | ((!!gpio_get_value(GPIO_VSH)) << 1);
+
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ status |= SS_3VCARD; /* 3V card */
+ break;
+ case 3: /* 5V card, unsupported */
+ default:
+ status |= SS_XVCARD; /* treated as unsupported in core */
+ }
+
+ /* GPIO214: low active power switch */
+ status |= gpio_get_value(GPIO_POWER) ? 0 : SS_POWERON;
+
+ /* GPIO204: high-active reset line */
+ status |= gpio_get_value(GPIO_RESET) ? SS_RESET : SS_READY;
+
+ /* other stuff */
+ status |= gpio_get_value(GPIO_BATTDEAD) ? 0 : SS_BATDEAD;
+ status |= gpio_get_value(GPIO_BATTWARN) ? 0 : SS_BATWARN;
+
+ *value = status;
+
+ return 0;
+}
+
+static int xxs1500_pcmcia_sock_init(struct pcmcia_socket *skt)
+{
+ gpio_direction_input(GPIO_CDA);
+ gpio_direction_input(GPIO_CDB);
+ gpio_direction_input(GPIO_VSL);
+ gpio_direction_input(GPIO_VSH);
+ gpio_direction_input(GPIO_BATTDEAD);
+ gpio_direction_input(GPIO_BATTWARN);
+ gpio_direction_output(GPIO_RESET, 1); /* assert reset */
+ gpio_direction_output(GPIO_OUTEN, 1); /* disable buffers */
+ gpio_direction_output(GPIO_POWER, 1); /* power off */
+
+ return 0;
+}
+
+static int xxs1500_pcmcia_sock_suspend(struct pcmcia_socket *skt)
+{
+ return 0;
+}
+
+static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt,
+ struct pccard_io_map *map)
+{
+ struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
+
+ map->start = (u32)sock->virt_io;
+ map->stop = map->start + IO_MAP_SIZE;
+
+ return 0;
+}
+
+static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt,
+ struct pccard_mem_map *map)
+{
+ struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt);
+
+ if (map->flags & MAP_ATTRIB)
+ map->static_start = sock->phys_attr + map->card_start;
+ else
+ map->static_start = sock->phys_mem + map->card_start;
+
+ return 0;
+}
+
+static struct pccard_operations xxs1500_pcmcia_operations = {
+ .init = xxs1500_pcmcia_sock_init,
+ .suspend = xxs1500_pcmcia_sock_suspend,
+ .get_status = xxs1500_pcmcia_get_status,
+ .set_socket = xxs1500_pcmcia_configure,
+ .set_io_map = au1x00_pcmcia_set_io_map,
+ .set_mem_map = au1x00_pcmcia_set_mem_map,
+};
+
+static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev)
+{
+ struct xxs1500_pcmcia_sock *sock;
+ struct resource *r;
+ int ret, irq;
+
+ sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL);
+ if (!sock)
+ return -ENOMEM;
+
+ ret = -ENODEV;
+
+ /*
+ * pseudo-attr: The 32bit address of the PCMCIA attribute space
+ * for this socket (usually the 36bit address shifted 4 to the
+ * right).
+ */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
+ if (!r) {
+ dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n");
+ goto out0;
+ }
+ sock->phys_attr = r->start;
+
+ /*
+ * pseudo-mem: The 32bit address of the PCMCIA memory space for
+ * this socket (usually the 36bit address shifted 4 to the right)
+ */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
+ if (!r) {
+ dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n");
+ goto out0;
+ }
+ sock->phys_mem = r->start;
+
+ /*
+ * pseudo-io: The 32bit address of the PCMCIA IO space for this
+ * socket (usually the 36bit address shifted 4 to the right).
+ */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
+ if (!r) {
+ dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n");
+ goto out0;
+ }
+ sock->phys_io = r->start;
+
+
+ /*
+ * PCMCIA client drivers use the inb/outb macros to access
+ * the IO registers. Since mips_io_port_base is added
+ * to the access address of the mips implementation of
+ * inb/outb, we need to subtract it here because we want
+ * to access the I/O or MEM address directly, without
+ * going through this "mips_io_port_base" mechanism.
+ */
+ sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
+ mips_io_port_base);
+
+ if (!sock->virt_io) {
+ dev_err(&pdev->dev, "cannot remap IO area\n");
+ ret = -ENOMEM;
+ goto out0;
+ }
+
+ sock->socket.ops = &xxs1500_pcmcia_operations;
+ sock->socket.owner = THIS_MODULE;
+ sock->socket.pci_irq = gpio_to_irq(GPIO_CARDIRQ);
+ sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
+ sock->socket.map_size = MEM_MAP_SIZE;
+ sock->socket.io_offset = (unsigned long)sock->virt_io;
+ sock->socket.dev.parent = &pdev->dev;
+ sock->socket.resource_ops = &pccard_static_ops;
+
+ platform_set_drvdata(pdev, sock);
+
+ /* setup carddetect irq: use one of the 2 GPIOs as an
+ * edge detector.
+ */
+ irq = gpio_to_irq(GPIO_CDA);
+ set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
+ ret = request_irq(irq, cdirq, 0, "pcmcia_carddetect", sock);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot setup cd irq\n");
+ goto out1;
+ }
+
+ ret = pcmcia_register_socket(&sock->socket);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register\n");
+ goto out2;
+ }
+
+ printk(KERN_INFO "MyCable XXS1500 PCMCIA socket services\n");
+
+ return 0;
+
+out2:
+ free_irq(gpio_to_irq(GPIO_CDA), sock);
+out1:
+ iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
+out0:
+ kfree(sock);
+ return ret;
+}
+
+static int __devexit xxs1500_pcmcia_remove(struct platform_device *pdev)
+{
+ struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev);
+
+ pcmcia_unregister_socket(&sock->socket);
+ free_irq(gpio_to_irq(GPIO_CDA), sock);
+ iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
+ kfree(sock);
+
+ return 0;
+}
+
+static struct platform_driver xxs1500_pcmcia_socket_driver = {
+ .driver = {
+ .name = "xxs1500_pcmcia",
+ .owner = THIS_MODULE,
+ },
+ .probe = xxs1500_pcmcia_probe,
+ .remove = __devexit_p(xxs1500_pcmcia_remove),
+};
+
+int __init xxs1500_pcmcia_socket_load(void)
+{
+ return platform_driver_register(&xxs1500_pcmcia_socket_driver);
+}
+
+void __exit xxs1500_pcmcia_socket_unload(void)
+{
+ platform_driver_unregister(&xxs1500_pcmcia_socket_driver);
+}
+
+module_init(xxs1500_pcmcia_socket_load);
+module_exit(xxs1500_pcmcia_socket_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index e4d12acdd52..b85375f8762 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -37,6 +37,11 @@ static int pwr_irqs_off;
module_param(pwr_irqs_off, bool, 0644);
MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!");
+static char o2_speedup[] = "default";
+module_param_string(o2_speedup, o2_speedup, sizeof(o2_speedup), 0444);
+MODULE_PARM_DESC(o2_speedup, "Use prefetch/burst for O2-bridges: 'on', 'off' "
+ "or 'default' (uses recommended behaviour for the detected bridge)");
+
#define debug(x, s, args...) dev_dbg(&s->dev->dev, x, ##args)
/* Don't ask.. */
@@ -649,9 +654,10 @@ static int yenta_search_one_res(struct resource *root, struct resource *res,
static int yenta_search_res(struct yenta_socket *socket, struct resource *res,
u32 min)
{
+ struct resource *root;
int i;
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *root = socket->dev->bus->resource[i];
+
+ pci_bus_for_each_resource(socket->dev->bus, root, i) {
if (!root)
continue;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index f526e735c5a..6848f213eb5 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -79,6 +79,7 @@ config DELL_LAPTOP
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on POWER_SUPPLY
+ depends on SERIO_I8042
default n
---help---
This driver adds support for rfkill and backlight control to Dell
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 07d14dfdf0b..226b3e93498 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -934,7 +934,7 @@ static int __devinit acer_backlight_init(struct device *dev)
acer_backlight_device = bd;
bd->props.power = FB_BLANK_UNBLANK;
- bd->props.brightness = max_brightness;
+ bd->props.brightness = read_brightness(bd);
bd->props.max_brightness = max_brightness;
backlight_update_status(bd);
return 0;
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index ed90082cdf1..8cb20e45bad 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -34,6 +34,11 @@ struct cmpc_accel {
#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_HID "ACCE0000"
+#define CMPC_TABLET_HID "TBLT0000"
+#define CMPC_BL_HID "IPML200"
+#define CMPC_KEYS_HID "FnBT0000"
+
/*
* Generic input device code.
*/
@@ -282,10 +287,9 @@ static int cmpc_accel_remove(struct acpi_device *acpi, int type)
}
static const struct acpi_device_id cmpc_accel_device_ids[] = {
- {"ACCE0000", 0},
+ {CMPC_ACCEL_HID, 0},
{"", 0}
};
-MODULE_DEVICE_TABLE(acpi, cmpc_accel_device_ids);
static struct acpi_driver cmpc_accel_acpi_driver = {
.owner = THIS_MODULE,
@@ -366,10 +370,9 @@ static int cmpc_tablet_resume(struct acpi_device *acpi)
}
static const struct acpi_device_id cmpc_tablet_device_ids[] = {
- {"TBLT0000", 0},
+ {CMPC_TABLET_HID, 0},
{"", 0}
};
-MODULE_DEVICE_TABLE(acpi, cmpc_tablet_device_ids);
static struct acpi_driver cmpc_tablet_acpi_driver = {
.owner = THIS_MODULE,
@@ -477,17 +480,16 @@ static int cmpc_bl_remove(struct acpi_device *acpi, int type)
return 0;
}
-static const struct acpi_device_id cmpc_device_ids[] = {
- {"IPML200", 0},
+static const struct acpi_device_id cmpc_bl_device_ids[] = {
+ {CMPC_BL_HID, 0},
{"", 0}
};
-MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
static struct acpi_driver cmpc_bl_acpi_driver = {
.owner = THIS_MODULE,
.name = "cmpc",
.class = "cmpc",
- .ids = cmpc_device_ids,
+ .ids = cmpc_bl_device_ids,
.ops = {
.add = cmpc_bl_add,
.remove = cmpc_bl_remove
@@ -540,10 +542,9 @@ static int cmpc_keys_remove(struct acpi_device *acpi, int type)
}
static const struct acpi_device_id cmpc_keys_device_ids[] = {
- {"FnBT0000", 0},
+ {CMPC_KEYS_HID, 0},
{"", 0}
};
-MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids);
static struct acpi_driver cmpc_keys_acpi_driver = {
.owner = THIS_MODULE,
@@ -607,3 +608,13 @@ static void cmpc_exit(void)
module_init(cmpc_init);
module_exit(cmpc_exit);
+
+static const struct acpi_device_id cmpc_device_ids[] = {
+ {CMPC_ACCEL_HID, 0},
+ {CMPC_TABLET_HID, 0},
+ {CMPC_BL_HID, 0},
+ {CMPC_KEYS_HID, 0},
+ {"", 0}
+};
+
+MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index 1a387e79f71..2740b40aad9 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -26,17 +26,8 @@
/*
* comapl-laptop.c - Compal laptop support.
*
- * This driver exports a few files in /sys/devices/platform/compal-laptop/:
- *
- * wlan - wlan subsystem state: contains 0 or 1 (rw)
- *
- * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
- *
- * raw - raw value taken from embedded controller register (ro)
- *
- * In addition to these platform device attributes the driver
- * registers itself in the Linux backlight control subsystem and is
- * available to userspace under /sys/class/backlight/compal-laptop/.
+ * The driver registers itself with the rfkill subsystem and
+ * the Linux backlight control subsystem.
*
* This driver might work on other laptops produced by Compal. If you
* want to try it you can pass force=1 as argument to the module which
@@ -51,6 +42,7 @@
#include <linux/dmi.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
+#include <linux/rfkill.h>
#define COMPAL_DRIVER_VERSION "0.2.6"
@@ -63,6 +55,10 @@
#define WLAN_MASK 0x01
#define BT_MASK 0x02
+static struct rfkill *wifi_rfkill;
+static struct rfkill *bt_rfkill;
+static struct platform_device *compal_device;
+
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
@@ -88,65 +84,75 @@ static int get_lcd_level(void)
return (int) result;
}
-static int set_wlan_state(int state)
+static int compal_rfkill_set(void *data, bool blocked)
{
+ unsigned long radio = (unsigned long) data;
u8 result, value;
ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
- if ((result & KILLSWITCH_MASK) == 0)
- return -EINVAL;
- else {
- if (state)
- value = (u8) (result | WLAN_MASK);
- else
- value = (u8) (result & ~WLAN_MASK);
- ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
- }
+ if (!blocked)
+ value = (u8) (result | radio);
+ else
+ value = (u8) (result & ~radio);
+ ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
return 0;
}
-static int set_bluetooth_state(int state)
+static void compal_rfkill_poll(struct rfkill *rfkill, void *data)
{
- u8 result, value;
+ u8 result;
+ bool hw_blocked;
ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
- if ((result & KILLSWITCH_MASK) == 0)
- return -EINVAL;
- else {
- if (state)
- value = (u8) (result | BT_MASK);
- else
- value = (u8) (result & ~BT_MASK);
- ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
- }
-
- return 0;
+ hw_blocked = !(result & KILLSWITCH_MASK);
+ rfkill_set_hw_state(rfkill, hw_blocked);
}
-static int get_wireless_state(int *wlan, int *bluetooth)
+static const struct rfkill_ops compal_rfkill_ops = {
+ .poll = compal_rfkill_poll,
+ .set_block = compal_rfkill_set,
+};
+
+static int setup_rfkill(void)
{
- u8 result;
+ int ret;
- ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+ wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev,
+ RFKILL_TYPE_WLAN, &compal_rfkill_ops,
+ (void *) WLAN_MASK);
+ if (!wifi_rfkill)
+ return -ENOMEM;
- if (wlan) {
- if ((result & KILLSWITCH_MASK) == 0)
- *wlan = 0;
- else
- *wlan = result & WLAN_MASK;
- }
+ ret = rfkill_register(wifi_rfkill);
+ if (ret)
+ goto err_wifi;
- if (bluetooth) {
- if ((result & KILLSWITCH_MASK) == 0)
- *bluetooth = 0;
- else
- *bluetooth = (result & BT_MASK) >> 1;
+ bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev,
+ RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops,
+ (void *) BT_MASK);
+ if (!bt_rfkill) {
+ ret = -ENOMEM;
+ goto err_allocate_bt;
}
+ ret = rfkill_register(bt_rfkill);
+ if (ret)
+ goto err_register_bt;
return 0;
+
+err_register_bt:
+ rfkill_destroy(bt_rfkill);
+
+err_allocate_bt:
+ rfkill_unregister(wifi_rfkill);
+
+err_wifi:
+ rfkill_destroy(wifi_rfkill);
+
+ return ret;
}
/* Backlight device stuff */
@@ -169,86 +175,6 @@ static struct backlight_ops compalbl_ops = {
static struct backlight_device *compalbl_device;
-/* Platform device */
-
-static ssize_t show_wlan(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int ret, enabled;
-
- ret = get_wireless_state(&enabled, NULL);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%i\n", enabled);
-}
-
-static ssize_t show_raw(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u8 result;
-
- ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
-
- return sprintf(buf, "%i\n", result);
-}
-
-static ssize_t show_bluetooth(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int ret, enabled;
-
- ret = get_wireless_state(NULL, &enabled);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%i\n", enabled);
-}
-
-static ssize_t store_wlan_state(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int state, ret;
-
- if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
- return -EINVAL;
-
- ret = set_wlan_state(state);
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static ssize_t store_bluetooth_state(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int state, ret;
-
- if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
- return -EINVAL;
-
- ret = set_bluetooth_state(state);
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
-static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
-static DEVICE_ATTR(raw, 0444, show_raw, NULL);
-
-static struct attribute *compal_attributes[] = {
- &dev_attr_bluetooth.attr,
- &dev_attr_wlan.attr,
- &dev_attr_raw.attr,
- NULL
-};
-
-static struct attribute_group compal_attribute_group = {
- .attrs = compal_attributes
-};
static struct platform_driver compal_driver = {
.driver = {
@@ -257,8 +183,6 @@ static struct platform_driver compal_driver = {
}
};
-static struct platform_device *compal_device;
-
/* Initialization */
static int dmi_check_cb(const struct dmi_system_id *id)
@@ -310,6 +234,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = {
},
.callback = dmi_check_cb
},
+ {
+ .ident = "Dell Mini 9",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "Dell Mini 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "Dell Mini 10v",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "Dell Inspiron 11z",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "Dell Mini 12",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
+ },
+ .callback = dmi_check_cb
+ },
+
{ }
};
@@ -348,23 +313,21 @@ static int __init compal_init(void)
ret = platform_device_add(compal_device);
if (ret)
- goto fail_platform_device1;
+ goto fail_platform_device;
- ret = sysfs_create_group(&compal_device->dev.kobj,
- &compal_attribute_group);
+ ret = setup_rfkill();
if (ret)
- goto fail_platform_device2;
+ goto fail_rfkill;
printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
" successfully loaded.\n");
return 0;
-fail_platform_device2:
-
+fail_rfkill:
platform_device_del(compal_device);
-fail_platform_device1:
+fail_platform_device:
platform_device_put(compal_device);
@@ -382,10 +345,13 @@ fail_backlight:
static void __exit compal_cleanup(void)
{
- sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
platform_device_unregister(compal_device);
platform_driver_unregister(&compal_driver);
backlight_device_unregister(compalbl_device);
+ rfkill_unregister(wifi_rfkill);
+ rfkill_destroy(wifi_rfkill);
+ rfkill_unregister(bt_rfkill);
+ rfkill_destroy(bt_rfkill);
printk(KERN_INFO "compal-laptop: driver unloaded.\n");
}
@@ -403,3 +369,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*");
+MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*");
+MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*");
+MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*");
+MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*");
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 3780994dc8f..b7f4d270591 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -22,6 +22,8 @@
#include <linux/rfkill.h>
#include <linux/power_supply.h>
#include <linux/acpi.h>
+#include <linux/mm.h>
+#include <linux/i8042.h>
#include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d
@@ -79,9 +81,73 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
},
},
+ {
+ .ident = "Dell Computer Corporation",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
+ },
+ },
{ }
};
+static struct dmi_system_id __devinitdata dell_blacklist[] = {
+ /* Supported by compal-laptop */
+ {
+ .ident = "Dell Mini 9",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
+ },
+ },
+ {
+ .ident = "Dell Mini 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
+ },
+ },
+ {
+ .ident = "Dell Mini 10v",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
+ },
+ },
+ {
+ .ident = "Dell Inspiron 11z",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
+ },
+ },
+ {
+ .ident = "Dell Mini 12",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
+ },
+ },
+ {}
+};
+
+static struct calling_interface_buffer *buffer;
+struct page *bufferpage;
+DEFINE_MUTEX(buffer_mutex);
+
+static int hwswitch_state;
+
+static void get_buffer(void)
+{
+ mutex_lock(&buffer_mutex);
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+}
+
+static void release_buffer(void)
+{
+ mutex_unlock(&buffer_mutex);
+}
+
static void __init parse_da_table(const struct dmi_header *dm)
{
/* Final token is a terminator, so we don't want to copy it */
@@ -160,6 +226,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
/* Derived from information in DellWirelessCtl.cpp:
Class 17, select 11 is radio control. It returns an array of 32-bit values.
+ Input byte 0 = 0: Wireless information
+
result[0]: return code
result[1]:
Bit 0: Hardware switch supported
@@ -180,33 +248,62 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
Bits 20-31: Reserved
result[2]: NVRAM size in bytes
result[3]: NVRAM format version number
+
+ Input byte 0 = 2: Wireless switch configuration
+ result[0]: return code
+ result[1]:
+ Bit 0: Wifi controlled by switch
+ Bit 1: Bluetooth controlled by switch
+ Bit 2: WWAN controlled by switch
+ Bits 3-6: Reserved
+ Bit 7: Wireless switch config locked
+ Bit 8: Wifi locator enabled
+ Bits 9-14: Reserved
+ Bit 15: Wifi locator setting locked
+ Bits 16-31: Reserved
*/
static int dell_rfkill_set(void *data, bool blocked)
{
- struct calling_interface_buffer buffer;
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
+ int hwswitch_bit = (unsigned long)data - 1;
+ int ret = 0;
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+
+ /* If the hardware switch controls this radio, and the hardware
+ switch is disabled, don't allow changing the software state */
+ if ((hwswitch_state & BIT(hwswitch_bit)) &&
+ !(buffer->output[1] & BIT(16))) {
+ ret = -EINVAL;
+ goto out;
+ }
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- buffer.input[0] = (1 | (radio<<8) | (disable << 16));
- dell_send_request(&buffer, 17, 11);
+ buffer->input[0] = (1 | (radio<<8) | (disable << 16));
+ dell_send_request(buffer, 17, 11);
- return 0;
+out:
+ release_buffer();
+ return ret;
}
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
- struct calling_interface_buffer buffer;
int status;
int bit = (unsigned long)data + 16;
+ int hwswitch_bit = (unsigned long)data - 1;
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- dell_send_request(&buffer, 17, 11);
- status = buffer.output[1];
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+ status = buffer->output[1];
+ release_buffer();
rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
- rfkill_set_hw_state(rfkill, !(status & BIT(16)));
+
+ if (hwswitch_state & (BIT(hwswitch_bit)))
+ rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
static const struct rfkill_ops dell_rfkill_ops = {
@@ -214,15 +311,36 @@ static const struct rfkill_ops dell_rfkill_ops = {
.query = dell_rfkill_query,
};
+static void dell_update_rfkill(struct work_struct *ignored)
+{
+ if (wifi_rfkill)
+ dell_rfkill_query(wifi_rfkill, (void *)1);
+ if (bluetooth_rfkill)
+ dell_rfkill_query(bluetooth_rfkill, (void *)2);
+ if (wwan_rfkill)
+ dell_rfkill_query(wwan_rfkill, (void *)3);
+}
+static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
+
+
static int __init dell_setup_rfkill(void)
{
- struct calling_interface_buffer buffer;
int status;
int ret;
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- dell_send_request(&buffer, 17, 11);
- status = buffer.output[1];
+ if (dmi_check_system(dell_blacklist)) {
+ printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - "
+ "not enabling rfkill\n");
+ return 0;
+ }
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+ status = buffer->output[1];
+ buffer->input[0] = 0x2;
+ dell_send_request(buffer, 17, 11);
+ hwswitch_state = buffer->output[1];
+ release_buffer();
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
@@ -298,39 +416,49 @@ static void dell_cleanup_rfkill(void)
static int dell_send_intensity(struct backlight_device *bd)
{
- struct calling_interface_buffer buffer;
+ int ret = 0;
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
- buffer.input[1] = bd->props.brightness;
+ get_buffer();
+ buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
+ buffer->input[1] = bd->props.brightness;
- if (buffer.input[0] == -1)
- return -ENODEV;
+ if (buffer->input[0] == -1) {
+ ret = -ENODEV;
+ goto out;
+ }
if (power_supply_is_system_supplied() > 0)
- dell_send_request(&buffer, 1, 2);
+ dell_send_request(buffer, 1, 2);
else
- dell_send_request(&buffer, 1, 1);
+ dell_send_request(buffer, 1, 1);
+out:
+ release_buffer();
return 0;
}
static int dell_get_intensity(struct backlight_device *bd)
{
- struct calling_interface_buffer buffer;
+ int ret = 0;
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
+ get_buffer();
+ buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
- if (buffer.input[0] == -1)
- return -ENODEV;
+ if (buffer->input[0] == -1) {
+ ret = -ENODEV;
+ goto out;
+ }
if (power_supply_is_system_supplied() > 0)
- dell_send_request(&buffer, 0, 2);
+ dell_send_request(buffer, 0, 2);
else
- dell_send_request(&buffer, 0, 1);
+ dell_send_request(buffer, 0, 1);
- return buffer.output[1];
+out:
+ release_buffer();
+ if (ret)
+ return ret;
+ return buffer->output[1];
}
static struct backlight_ops dell_ops = {
@@ -338,9 +466,32 @@ static struct backlight_ops dell_ops = {
.update_status = dell_send_intensity,
};
+bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
+ struct serio *port)
+{
+ static bool extended;
+
+ if (str & 0x20)
+ return false;
+
+ if (unlikely(data == 0xe0)) {
+ extended = true;
+ return false;
+ } else if (unlikely(extended)) {
+ switch (data) {
+ case 0x8:
+ schedule_delayed_work(&dell_rfkill_work,
+ round_jiffies_relative(HZ));
+ break;
+ }
+ extended = false;
+ }
+
+ return false;
+}
+
static int __init dell_init(void)
{
- struct calling_interface_buffer buffer;
int max_intensity = 0;
int ret;
@@ -366,6 +517,17 @@ static int __init dell_init(void)
if (ret)
goto fail_platform_device2;
+ /*
+ * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
+ * is passed to SMI handler.
+ */
+ bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
+
+ if (!bufferpage)
+ goto fail_buffer;
+ buffer = page_address(bufferpage);
+ mutex_init(&buffer_mutex);
+
ret = dell_setup_rfkill();
if (ret) {
@@ -373,6 +535,13 @@ static int __init dell_init(void)
goto fail_rfkill;
}
+ ret = i8042_install_filter(dell_laptop_i8042_filter);
+ if (ret) {
+ printk(KERN_WARNING
+ "dell-laptop: Unable to install key filter\n");
+ goto fail_filter;
+ }
+
#ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't
* register the platform controller.
@@ -381,13 +550,13 @@ static int __init dell_init(void)
return 0;
#endif
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
-
- if (buffer.input[0] != -1) {
- dell_send_request(&buffer, 0, 2);
- max_intensity = buffer.output[3];
+ get_buffer();
+ buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
+ if (buffer->input[0] != -1) {
+ dell_send_request(buffer, 0, 2);
+ max_intensity = buffer->output[3];
}
+ release_buffer();
if (max_intensity) {
dell_backlight_device = backlight_device_register(
@@ -410,8 +579,12 @@ static int __init dell_init(void)
return 0;
fail_backlight:
+ i8042_remove_filter(dell_laptop_i8042_filter);
+fail_filter:
dell_cleanup_rfkill();
fail_rfkill:
+ free_page((unsigned long)bufferpage);
+fail_buffer:
platform_device_del(platform_device);
fail_platform_device2:
platform_device_put(platform_device);
@@ -424,8 +597,16 @@ fail_platform_driver:
static void __exit dell_exit(void)
{
+ cancel_delayed_work_sync(&dell_rfkill_work);
+ i8042_remove_filter(dell_laptop_i8042_filter);
backlight_device_unregister(dell_backlight_device);
dell_cleanup_rfkill();
+ if (platform_device) {
+ platform_device_del(platform_device);
+ platform_driver_unregister(&platform_driver);
+ }
+ kfree(da_tokens);
+ free_page((unsigned long)buffer);
}
module_init(dell_init);
@@ -435,3 +616,4 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
+MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index ad4c414dbfb..3aa57da8b43 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -89,6 +89,7 @@ static struct key_entry hp_wmi_keymap[] = {
{KE_KEY, 0x20e6, KEY_PROG1},
{KE_KEY, 0x2142, KEY_MEDIA},
{KE_KEY, 0x213b, KEY_INFO},
+ {KE_KEY, 0x2169, KEY_DIRECTION},
{KE_KEY, 0x231b, KEY_HELP},
{KE_END, 0}
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index e67e4feb35c..eb603f1d55c 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -5771,7 +5771,7 @@ static void thermal_exit(void)
case TPACPI_THERMAL_ACPI_TMP07:
case TPACPI_THERMAL_ACPI_UPDT:
sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
- &thermal_temp_input16_group);
+ &thermal_temp_input8_group);
break;
case TPACPI_THERMAL_NONE:
default:
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 77bf5d8f893..26c211724ac 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -46,6 +46,7 @@
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
+#include <linux/input.h>
#include <asm/uaccess.h>
@@ -62,9 +63,10 @@ MODULE_LICENSE("GPL");
/* Toshiba ACPI method paths */
#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
-#define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
-#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
+#define TOSH_INTERFACE_1 "\\_SB_.VALD"
+#define TOSH_INTERFACE_2 "\\_SB_.VALZ"
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
+#define GHCI_METHOD ".GHCI"
/* Toshiba HCI interface definitions
*
@@ -116,6 +118,36 @@ static const struct acpi_device_id toshiba_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
+struct key_entry {
+ char type;
+ u16 code;
+ u16 keycode;
+};
+
+enum {KE_KEY, KE_END};
+
+static struct key_entry toshiba_acpi_keymap[] = {
+ {KE_KEY, 0x101, KEY_MUTE},
+ {KE_KEY, 0x13b, KEY_COFFEE},
+ {KE_KEY, 0x13c, KEY_BATTERY},
+ {KE_KEY, 0x13d, KEY_SLEEP},
+ {KE_KEY, 0x13e, KEY_SUSPEND},
+ {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
+ {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
+ {KE_KEY, 0x141, KEY_BRIGHTNESSUP},
+ {KE_KEY, 0x142, KEY_WLAN},
+ {KE_KEY, 0x143, KEY_PROG1},
+ {KE_KEY, 0xb05, KEY_PROG2},
+ {KE_KEY, 0xb06, KEY_WWW},
+ {KE_KEY, 0xb07, KEY_MAIL},
+ {KE_KEY, 0xb30, KEY_STOP},
+ {KE_KEY, 0xb31, KEY_PREVIOUSSONG},
+ {KE_KEY, 0xb32, KEY_NEXTSONG},
+ {KE_KEY, 0xb33, KEY_PLAYPAUSE},
+ {KE_KEY, 0xb5a, KEY_MEDIA},
+ {KE_END, 0, 0},
+};
+
/* utility
*/
@@ -251,6 +283,8 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
struct toshiba_acpi_dev {
struct platform_device *p_dev;
struct rfkill *bt_rfk;
+ struct input_dev *hotkey_dev;
+ acpi_handle handle;
const char *bt_name;
@@ -711,8 +745,159 @@ static struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+static struct key_entry *toshiba_acpi_get_entry_by_scancode(int code)
+{
+ struct key_entry *key;
+
+ for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
+ if (code == key->code)
+ return key;
+
+ return NULL;
+}
+
+static struct key_entry *toshiba_acpi_get_entry_by_keycode(int code)
+{
+ struct key_entry *key;
+
+ for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
+ if (code == key->keycode && key->type == KE_KEY)
+ return key;
+
+ return NULL;
+}
+
+static int toshiba_acpi_getkeycode(struct input_dev *dev, int scancode,
+ int *keycode)
+{
+ struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
+
+ if (key && key->type == KE_KEY) {
+ *keycode = key->keycode;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int toshiba_acpi_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 = toshiba_acpi_get_entry_by_scancode(scancode);
+ if (key && key->type == KE_KEY) {
+ old_keycode = key->keycode;
+ key->keycode = keycode;
+ set_bit(keycode, dev->keybit);
+ if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
+ clear_bit(old_keycode, dev->keybit);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
+{
+ u32 hci_result, value;
+ struct key_entry *key;
+
+ if (event != 0x80)
+ return;
+ do {
+ hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+ if (hci_result == HCI_SUCCESS) {
+ if (value == 0x100)
+ continue;
+ else if (value & 0x80) {
+ key = toshiba_acpi_get_entry_by_scancode
+ (value & ~0x80);
+ if (!key) {
+ printk(MY_INFO "Unknown key %x\n",
+ value & ~0x80);
+ continue;
+ }
+ input_report_key(toshiba_acpi.hotkey_dev,
+ key->keycode, 1);
+ input_sync(toshiba_acpi.hotkey_dev);
+ input_report_key(toshiba_acpi.hotkey_dev,
+ key->keycode, 0);
+ input_sync(toshiba_acpi.hotkey_dev);
+ }
+ } else if (hci_result == HCI_NOT_SUPPORTED) {
+ /* This is a workaround for an unresolved issue on
+ * some machines where system events sporadically
+ * become disabled. */
+ hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+ printk(MY_NOTICE "Re-enabled hotkeys\n");
+ }
+ } while (hci_result != HCI_EMPTY);
+}
+
+static int toshiba_acpi_setup_keyboard(char *device)
+{
+ acpi_status status;
+ acpi_handle handle;
+ int result;
+ const struct key_entry *key;
+
+ status = acpi_get_handle(NULL, device, &handle);
+ if (ACPI_FAILURE(status)) {
+ printk(MY_INFO "Unable to get notification device\n");
+ return -ENODEV;
+ }
+
+ toshiba_acpi.handle = handle;
+
+ status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(MY_INFO "Unable to enable hotkeys\n");
+ return -ENODEV;
+ }
+
+ status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
+ toshiba_acpi_notify, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(MY_INFO "Unable to install hotkey notification\n");
+ return -ENODEV;
+ }
+
+ toshiba_acpi.hotkey_dev = input_allocate_device();
+ if (!toshiba_acpi.hotkey_dev) {
+ printk(MY_INFO "Unable to register input device\n");
+ return -ENOMEM;
+ }
+
+ toshiba_acpi.hotkey_dev->name = "Toshiba input device";
+ toshiba_acpi.hotkey_dev->phys = device;
+ toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
+ toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
+ toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
+
+ for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
+ set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
+ set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
+ }
+
+ result = input_register_device(toshiba_acpi.hotkey_dev);
+ if (result) {
+ printk(MY_INFO "Unable to register input device\n");
+ return result;
+ }
+
+ return 0;
+}
+
static void toshiba_acpi_exit(void)
{
+ if (toshiba_acpi.hotkey_dev)
+ input_unregister_device(toshiba_acpi.hotkey_dev);
+
if (toshiba_acpi.bt_rfk) {
rfkill_unregister(toshiba_acpi.bt_rfk);
rfkill_destroy(toshiba_acpi.bt_rfk);
@@ -726,6 +911,9 @@ static void toshiba_acpi_exit(void)
if (toshiba_proc_dir)
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
+ toshiba_acpi_notify);
+
platform_device_unregister(toshiba_acpi.p_dev);
return;
@@ -742,11 +930,15 @@ static int __init toshiba_acpi_init(void)
return -ENODEV;
/* simple device detection: look for HCI method */
- if (is_valid_acpi_path(METHOD_HCI_1))
- method_hci = METHOD_HCI_1;
- else if (is_valid_acpi_path(METHOD_HCI_2))
- method_hci = METHOD_HCI_2;
- else
+ if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
+ method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
+ if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
+ printk(MY_INFO "Unable to activate hotkeys\n");
+ } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
+ method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
+ if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
+ printk(MY_INFO "Unable to activate hotkeys\n");
+ } else
return -ENODEV;
printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c
index e82d8c9c6cd..95a689befc8 100644
--- a/drivers/ps3/ps3av.c
+++ b/drivers/ps3/ps3av.c
@@ -532,7 +532,7 @@ static void ps3av_set_videomode_packet(u32 id)
res = ps3av_cmd_avb_param(&avb_param, len);
if (res == PS3AV_STATUS_NO_SYNC_HEAD)
printk(KERN_WARNING
- "%s: Command failed. Please try your request again. \n",
+ "%s: Command failed. Please try your request again.\n",
__func__);
else if (res)
dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8167e9e6827..2bb8a8b7ffa 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -868,4 +868,14 @@ config RTC_DRV_MC13783
help
This enables support for the Freescale MC13783 PMIC RTC
+config RTC_DRV_MPC5121
+ tristate "Freescale MPC5121 built-in RTC"
+ depends on PPC_MPC512x && RTC_CLASS
+ help
+ If you say yes here you will get support for the
+ built-in RTC MPC5121.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-mpc5121.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e5160fddc44..b7148afb8f5 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
+obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
new file mode 100644
index 00000000000..4313ca03a96
--- /dev/null
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -0,0 +1,387 @@
+/*
+ * Real-time clock driver for MPC5121
+ *
+ * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
+ * Copyright 2008, Freescale Semiconductor, 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+
+struct mpc5121_rtc_regs {
+ u8 set_time; /* RTC + 0x00 */
+ u8 hour_set; /* RTC + 0x01 */
+ u8 minute_set; /* RTC + 0x02 */
+ u8 second_set; /* RTC + 0x03 */
+
+ u8 set_date; /* RTC + 0x04 */
+ u8 month_set; /* RTC + 0x05 */
+ u8 weekday_set; /* RTC + 0x06 */
+ u8 date_set; /* RTC + 0x07 */
+
+ u8 write_sw; /* RTC + 0x08 */
+ u8 sw_set; /* RTC + 0x09 */
+ u16 year_set; /* RTC + 0x0a */
+
+ u8 alm_enable; /* RTC + 0x0c */
+ u8 alm_hour_set; /* RTC + 0x0d */
+ u8 alm_min_set; /* RTC + 0x0e */
+ u8 int_enable; /* RTC + 0x0f */
+
+ u8 reserved1;
+ u8 hour; /* RTC + 0x11 */
+ u8 minute; /* RTC + 0x12 */
+ u8 second; /* RTC + 0x13 */
+
+ u8 month; /* RTC + 0x14 */
+ u8 wday_mday; /* RTC + 0x15 */
+ u16 year; /* RTC + 0x16 */
+
+ u8 int_alm; /* RTC + 0x18 */
+ u8 int_sw; /* RTC + 0x19 */
+ u8 alm_status; /* RTC + 0x1a */
+ u8 sw_minute; /* RTC + 0x1b */
+
+ u8 bus_error_1; /* RTC + 0x1c */
+ u8 int_day; /* RTC + 0x1d */
+ u8 int_min; /* RTC + 0x1e */
+ u8 int_sec; /* RTC + 0x1f */
+
+ /*
+ * target_time:
+ * intended to be used for hibernation but hibernation
+ * does not work on silicon rev 1.5 so use it for non-volatile
+ * storage of offset between the actual_time register and linux
+ * time
+ */
+ u32 target_time; /* RTC + 0x20 */
+ /*
+ * actual_time:
+ * readonly time since VBAT_RTC was last connected
+ */
+ u32 actual_time; /* RTC + 0x24 */
+ u32 keep_alive; /* RTC + 0x28 */
+};
+
+struct mpc5121_rtc_data {
+ unsigned irq;
+ unsigned irq_periodic;
+ struct mpc5121_rtc_regs __iomem *regs;
+ struct rtc_device *rtc;
+ struct rtc_wkalrm wkalarm;
+};
+
+/*
+ * Update second/minute/hour registers.
+ *
+ * This is just so alarm will work.
+ */
+static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
+ struct rtc_time *tm)
+{
+ out_8(&regs->second_set, tm->tm_sec);
+ out_8(&regs->minute_set, tm->tm_min);
+ out_8(&regs->hour_set, tm->tm_hour);
+
+ /* set time sequence */
+ out_8(&regs->set_time, 0x1);
+ out_8(&regs->set_time, 0x3);
+ out_8(&regs->set_time, 0x1);
+ out_8(&regs->set_time, 0x0);
+}
+
+static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ unsigned long now;
+
+ /*
+ * linux time is actual_time plus the offset saved in target_time
+ */
+ now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
+
+ rtc_time_to_tm(now, tm);
+
+ /*
+ * update second minute hour registers
+ * so alarms will work
+ */
+ mpc5121_rtc_update_smh(regs, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int ret;
+ unsigned long now;
+
+ /*
+ * The actual_time register is read only so we write the offset
+ * between it and linux time to the target_time register.
+ */
+ ret = rtc_tm_to_time(tm, &now);
+ if (ret == 0)
+ out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
+
+ /*
+ * update second minute hour registers
+ * so alarms will work
+ */
+ mpc5121_rtc_update_smh(regs, tm);
+
+ return 0;
+}
+
+static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ *alarm = rtc->wkalarm;
+
+ alarm->pending = in_8(&regs->alm_status);
+
+ return 0;
+}
+
+static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ /*
+ * the alarm has no seconds so deal with it
+ */
+ if (alarm->time.tm_sec) {
+ alarm->time.tm_sec = 0;
+ alarm->time.tm_min++;
+ if (alarm->time.tm_min >= 60) {
+ alarm->time.tm_min = 0;
+ alarm->time.tm_hour++;
+ if (alarm->time.tm_hour >= 24)
+ alarm->time.tm_hour = 0;
+ }
+ }
+
+ alarm->time.tm_mday = -1;
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_year = -1;
+
+ out_8(&regs->alm_min_set, alarm->time.tm_min);
+ out_8(&regs->alm_hour_set, alarm->time.tm_hour);
+
+ out_8(&regs->alm_enable, alarm->enabled);
+
+ rtc->wkalarm = *alarm;
+ return 0;
+}
+
+static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ if (in_8(&regs->int_alm)) {
+ /* acknowledge and clear status */
+ out_8(&regs->int_alm, 1);
+ out_8(&regs->alm_status, 1);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
+ /* acknowledge */
+ out_8(&regs->int_sec, 1);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int val;
+
+ if (enabled)
+ val = 1;
+ else
+ val = 0;
+
+ out_8(&regs->alm_enable, val);
+ rtc->wkalarm.enabled = val;
+
+ return 0;
+}
+
+static int mpc5121_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int val;
+
+ val = in_8(&regs->int_enable);
+
+ if (enabled)
+ val = (val & ~0x8) | 0x1;
+ else
+ val &= ~0x1;
+
+ out_8(&regs->int_enable, val);
+
+ return 0;
+}
+
+static const struct rtc_class_ops mpc5121_rtc_ops = {
+ .read_time = mpc5121_rtc_read_time,
+ .set_time = mpc5121_rtc_set_time,
+ .read_alarm = mpc5121_rtc_read_alarm,
+ .set_alarm = mpc5121_rtc_set_alarm,
+ .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
+ .update_irq_enable = mpc5121_rtc_update_irq_enable,
+};
+
+static int __devinit mpc5121_rtc_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct mpc5121_rtc_data *rtc;
+ int err = 0;
+ u32 ka;
+
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regs = of_iomap(op->node, 0);
+ if (!rtc->regs) {
+ dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
+ err = -ENOSYS;
+ goto out_free;
+ }
+
+ device_init_wakeup(&op->dev, 1);
+
+ dev_set_drvdata(&op->dev, rtc);
+
+ rtc->irq = irq_of_parse_and_map(op->node, 1);
+ err = request_irq(rtc->irq, mpc5121_rtc_handler, IRQF_DISABLED,
+ "mpc5121-rtc", &op->dev);
+ if (err) {
+ dev_err(&op->dev, "%s: could not request irq: %i\n",
+ __func__, rtc->irq);
+ goto out_dispose;
+ }
+
+ rtc->irq_periodic = irq_of_parse_and_map(op->node, 0);
+ err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
+ IRQF_DISABLED, "mpc5121-rtc_upd", &op->dev);
+ if (err) {
+ dev_err(&op->dev, "%s: could not request irq: %i\n",
+ __func__, rtc->irq_periodic);
+ goto out_dispose2;
+ }
+
+ ka = in_be32(&rtc->regs->keep_alive);
+ if (ka & 0x02) {
+ dev_warn(&op->dev,
+ "mpc5121-rtc: Battery or oscillator failure!\n");
+ out_be32(&rtc->regs->keep_alive, ka);
+ }
+
+ rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
+ &mpc5121_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ err = PTR_ERR(rtc->rtc);
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ free_irq(rtc->irq_periodic, &op->dev);
+out_dispose2:
+ irq_dispose_mapping(rtc->irq_periodic);
+ free_irq(rtc->irq, &op->dev);
+out_dispose:
+ irq_dispose_mapping(rtc->irq);
+ iounmap(rtc->regs);
+out_free:
+ kfree(rtc);
+
+ return err;
+}
+
+static int __devexit mpc5121_rtc_remove(struct of_device *op)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ /* disable interrupt, so there are no nasty surprises */
+ out_8(&regs->alm_enable, 0);
+ out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
+
+ rtc_device_unregister(rtc->rtc);
+ iounmap(rtc->regs);
+ free_irq(rtc->irq, &op->dev);
+ free_irq(rtc->irq_periodic, &op->dev);
+ irq_dispose_mapping(rtc->irq);
+ irq_dispose_mapping(rtc->irq_periodic);
+ dev_set_drvdata(&op->dev, NULL);
+ kfree(rtc);
+
+ return 0;
+}
+
+static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
+ { .compatible = "fsl,mpc5121-rtc", },
+ {},
+};
+
+static struct of_platform_driver mpc5121_rtc_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc5121-rtc",
+ .match_table = mpc5121_rtc_match,
+ .probe = mpc5121_rtc_probe,
+ .remove = __devexit_p(mpc5121_rtc_remove),
+};
+
+static int __init mpc5121_rtc_init(void)
+{
+ return of_register_platform_driver(&mpc5121_rtc_driver);
+}
+module_init(mpc5121_rtc_init);
+
+static void __exit mpc5121_rtc_exit(void)
+{
+ of_unregister_platform_driver(&mpc5121_rtc_driver);
+}
+module_exit(mpc5121_rtc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 0264b117893..c256aacfa95 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -7,6 +7,9 @@
*
* Copyright 2006 (c) MontaVista Software, Inc.
*
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Copyright 2010 (c) ST-Ericsson AB
+ *
* 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
@@ -18,6 +21,9 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/version.h>
/*
* Register definitions
@@ -30,35 +36,207 @@
#define RTC_RIS 0x14 /* Raw interrupt status register */
#define RTC_MIS 0x18 /* Masked interrupt status register */
#define RTC_ICR 0x1c /* Interrupt clear register */
+/* ST variants have additional timer functionality */
+#define RTC_TDR 0x20 /* Timer data read register */
+#define RTC_TLR 0x24 /* Timer data load register */
+#define RTC_TCR 0x28 /* Timer control register */
+#define RTC_YDR 0x30 /* Year data read register */
+#define RTC_YMR 0x34 /* Year match register */
+#define RTC_YLR 0x38 /* Year data load register */
+
+#define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */
+
+#define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */
+
+/* Common bit definitions for Interrupt status and control registers */
+#define RTC_BIT_AI (1 << 0) /* Alarm interrupt bit */
+#define RTC_BIT_PI (1 << 1) /* Periodic interrupt bit. ST variants only. */
+
+/* Common bit definations for ST v2 for reading/writing time */
+#define RTC_SEC_SHIFT 0
+#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */
+#define RTC_MIN_SHIFT 6
+#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */
+#define RTC_HOUR_SHIFT 12
+#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */
+#define RTC_WDAY_SHIFT 17
+#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */
+#define RTC_MDAY_SHIFT 20
+#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */
+#define RTC_MON_SHIFT 25
+#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */
+
+#define RTC_TIMER_FREQ 32768
struct pl031_local {
struct rtc_device *rtc;
void __iomem *base;
+ u8 hw_designer;
+ u8 hw_revision:4;
};
-static irqreturn_t pl031_interrupt(int irq, void *dev_id)
+static int pl031_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long imsc;
+
+ /* Clear any pending alarm interrupts. */
+ writel(RTC_BIT_AI, ldata->base + RTC_ICR);
+
+ imsc = readl(ldata->base + RTC_IMSC);
+
+ if (enabled == 1)
+ writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC);
+ else
+ writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC);
+
+ return 0;
+}
+
+/*
+ * Convert Gregorian date to ST v2 RTC format.
+ */
+static int pl031_stv2_tm_to_time(struct device *dev,
+ struct rtc_time *tm, unsigned long *st_time,
+ unsigned long *bcd_year)
+{
+ int year = tm->tm_year + 1900;
+ int wday = tm->tm_wday;
+
+ /* wday masking is not working in hardware so wday must be valid */
+ if (wday < -1 || wday > 6) {
+ dev_err(dev, "invalid wday value %d\n", tm->tm_wday);
+ return -EINVAL;
+ } else if (wday == -1) {
+ /* wday is not provided, calculate it here */
+ unsigned long time;
+ struct rtc_time calc_tm;
+
+ rtc_tm_to_time(tm, &time);
+ rtc_time_to_tm(time, &calc_tm);
+ wday = calc_tm.tm_wday;
+ }
+
+ *bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8);
+
+ *st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT)
+ | (tm->tm_mday << RTC_MDAY_SHIFT)
+ | ((wday + 1) << RTC_WDAY_SHIFT)
+ | (tm->tm_hour << RTC_HOUR_SHIFT)
+ | (tm->tm_min << RTC_MIN_SHIFT)
+ | (tm->tm_sec << RTC_SEC_SHIFT);
+
+ return 0;
+}
+
+/*
+ * Convert ST v2 RTC format to Gregorian date.
+ */
+static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year,
+ struct rtc_time *tm)
+{
+ tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100);
+ tm->tm_mon = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1;
+ tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT);
+ tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1;
+ tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT);
+ tm->tm_min = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT);
+ tm->tm_sec = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT);
+
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year -= 1900;
+
+ return 0;
+}
+
+static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR),
+ readl(ldata->base + RTC_YDR), tm);
+
+ return 0;
+}
+
+static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ unsigned long bcd_year;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year);
+ if (ret == 0) {
+ writel(bcd_year, ldata->base + RTC_YLR);
+ writel(time, ldata->base + RTC_LR);
+ }
+
+ return ret;
+}
+
+static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct rtc_device *rtc = dev_id;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
- rtc_update_irq(rtc, 1, RTC_AF);
+ ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR),
+ readl(ldata->base + RTC_YMR), &alarm->time);
- return IRQ_HANDLED;
+ alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
+ alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
+
+ return ret;
}
-static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long time;
+ unsigned long bcd_year;
+ int ret;
+
+ /* At the moment, we can only deal with non-wildcarded alarm times. */
+ ret = rtc_valid_tm(&alarm->time);
+ if (ret == 0) {
+ ret = pl031_stv2_tm_to_time(dev, &alarm->time,
+ &time, &bcd_year);
+ if (ret == 0) {
+ writel(bcd_year, ldata->base + RTC_YMR);
+ writel(time, ldata->base + RTC_MR);
+
+ pl031_alarm_irq_enable(dev, alarm->enabled);
+ }
+ }
+
+ return ret;
+}
+
+static irqreturn_t pl031_interrupt(int irq, void *dev_id)
+{
+ struct pl031_local *ldata = dev_id;
+ unsigned long rtcmis;
+ unsigned long events = 0;
+
+ rtcmis = readl(ldata->base + RTC_MIS);
+ if (rtcmis) {
+ writel(rtcmis, ldata->base + RTC_ICR);
+
+ if (rtcmis & RTC_BIT_AI)
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* Timer interrupt is only available in ST variants */
+ if ((rtcmis & RTC_BIT_PI) &&
+ (ldata->hw_designer == AMBA_VENDOR_ST))
+ events |= (RTC_PF | RTC_IRQF);
+
+ rtc_update_irq(ldata->rtc, 1, events);
- switch (cmd) {
- case RTC_AIE_OFF:
- writel(1, ldata->base + RTC_MIS);
- return 0;
- case RTC_AIE_ON:
- writel(0, ldata->base + RTC_MIS);
- return 0;
+ return IRQ_HANDLED;
}
- return -ENOIOCTLCMD;
+ return IRQ_NONE;
}
static int pl031_read_time(struct device *dev, struct rtc_time *tm)
@@ -74,11 +252,14 @@ static int pl031_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned long time;
struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
- rtc_tm_to_time(tm, &time);
- writel(time, ldata->base + RTC_LR);
+ ret = rtc_tm_to_time(tm, &time);
- return 0;
+ if (ret == 0)
+ writel(time, ldata->base + RTC_LR);
+
+ return ret;
}
static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
@@ -86,8 +267,9 @@ static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
struct pl031_local *ldata = dev_get_drvdata(dev);
rtc_time_to_tm(readl(ldata->base + RTC_MR), &alarm->time);
- alarm->pending = readl(ldata->base + RTC_RIS);
- alarm->enabled = readl(ldata->base + RTC_IMSC);
+
+ alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
+ alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
return 0;
}
@@ -96,22 +278,71 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
unsigned long time;
+ int ret;
+
+ /* At the moment, we can only deal with non-wildcarded alarm times. */
+ ret = rtc_valid_tm(&alarm->time);
+ if (ret == 0) {
+ ret = rtc_tm_to_time(&alarm->time, &time);
+ if (ret == 0) {
+ writel(time, ldata->base + RTC_MR);
+ pl031_alarm_irq_enable(dev, alarm->enabled);
+ }
+ }
+
+ return ret;
+}
+
+/* Periodic interrupt is only available in ST variants. */
+static int pl031_irq_set_state(struct device *dev, int enabled)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ if (enabled == 1) {
+ /* Clear any pending timer interrupt. */
+ writel(RTC_BIT_PI, ldata->base + RTC_ICR);
+
+ writel(readl(ldata->base + RTC_IMSC) | RTC_BIT_PI,
+ ldata->base + RTC_IMSC);
- rtc_tm_to_time(&alarm->time, &time);
+ /* Now start the timer */
+ writel(readl(ldata->base + RTC_TCR) | RTC_TCR_EN,
+ ldata->base + RTC_TCR);
- writel(time, ldata->base + RTC_MR);
- writel(!alarm->enabled, ldata->base + RTC_MIS);
+ } else {
+ writel(readl(ldata->base + RTC_IMSC) & (~RTC_BIT_PI),
+ ldata->base + RTC_IMSC);
+
+ /* Also stop the timer */
+ writel(readl(ldata->base + RTC_TCR) & (~RTC_TCR_EN),
+ ldata->base + RTC_TCR);
+ }
+ /* Wait at least 1 RTC32 clock cycle to ensure next access
+ * to RTC_TCR will succeed.
+ */
+ udelay(40);
return 0;
}
-static const struct rtc_class_ops pl031_ops = {
- .ioctl = pl031_ioctl,
- .read_time = pl031_read_time,
- .set_time = pl031_set_time,
- .read_alarm = pl031_read_alarm,
- .set_alarm = pl031_set_alarm,
-};
+static int pl031_irq_set_freq(struct device *dev, int freq)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ /* Cant set timer if it is already enabled */
+ if (readl(ldata->base + RTC_TCR) & RTC_TCR_EN) {
+ dev_err(dev, "can't change frequency while timer enabled\n");
+ return -EINVAL;
+ }
+
+ /* If self start bit in RTC_TCR is set timer will start here,
+ * but we never set that bit. Instead we start the timer when
+ * set_state is called with enabled == 1.
+ */
+ writel(RTC_TIMER_FREQ / freq, ldata->base + RTC_TLR);
+
+ return 0;
+}
static int pl031_remove(struct amba_device *adev)
{
@@ -131,18 +362,20 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
{
int ret;
struct pl031_local *ldata;
+ struct rtc_class_ops *ops = id->data;
ret = amba_request_regions(adev, NULL);
if (ret)
goto err_req;
- ldata = kmalloc(sizeof(struct pl031_local), GFP_KERNEL);
+ ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL);
if (!ldata) {
ret = -ENOMEM;
goto out;
}
ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
+
if (!ldata->base) {
ret = -ENOMEM;
goto out_no_remap;
@@ -150,24 +383,36 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
amba_set_drvdata(adev, ldata);
- if (request_irq(adev->irq[0], pl031_interrupt, IRQF_DISABLED,
- "rtc-pl031", ldata->rtc)) {
- ret = -EIO;
- goto out_no_irq;
- }
+ ldata->hw_designer = amba_manf(adev);
+ ldata->hw_revision = amba_rev(adev);
+
+ dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer);
+ dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision);
- ldata->rtc = rtc_device_register("pl031", &adev->dev, &pl031_ops,
- THIS_MODULE);
+ /* Enable the clockwatch on ST Variants */
+ if ((ldata->hw_designer == AMBA_VENDOR_ST) &&
+ (ldata->hw_revision > 1))
+ writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
+ ldata->base + RTC_CR);
+
+ ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
+ THIS_MODULE);
if (IS_ERR(ldata->rtc)) {
ret = PTR_ERR(ldata->rtc);
goto out_no_rtc;
}
+ if (request_irq(adev->irq[0], pl031_interrupt,
+ IRQF_DISABLED | IRQF_SHARED, "rtc-pl031", ldata)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+
return 0;
-out_no_rtc:
- free_irq(adev->irq[0], ldata->rtc);
out_no_irq:
+ rtc_device_unregister(ldata->rtc);
+out_no_rtc:
iounmap(ldata->base);
amba_set_drvdata(adev, NULL);
out_no_remap:
@@ -175,13 +420,57 @@ out_no_remap:
out:
amba_release_regions(adev);
err_req:
+
return ret;
}
+/* Operations for the original ARM version */
+static struct rtc_class_ops arm_pl031_ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+};
+
+/* The First ST derivative */
+static struct rtc_class_ops stv1_pl031_ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ .irq_set_state = pl031_irq_set_state,
+ .irq_set_freq = pl031_irq_set_freq,
+};
+
+/* And the second ST derivative */
+static struct rtc_class_ops stv2_pl031_ops = {
+ .read_time = pl031_stv2_read_time,
+ .set_time = pl031_stv2_set_time,
+ .read_alarm = pl031_stv2_read_alarm,
+ .set_alarm = pl031_stv2_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ .irq_set_state = pl031_irq_set_state,
+ .irq_set_freq = pl031_irq_set_freq,
+};
+
static struct amba_id pl031_ids[] __initdata = {
{
.id = 0x00041031,
.mask = 0x000fffff,
+ .data = &arm_pl031_ops,
+ },
+ /* ST Micro variants */
+ {
+ .id = 0x00180031,
+ .mask = 0x00ffffff,
+ .data = &stv1_pl031_ops,
+ },
+ {
+ .id = 0x00280031,
+ .mask = 0x00ffffff,
+ .data = &stv2_pl031_ops,
},
{0, 0},
};
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 5905936c7c6..4951aa82e9f 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -20,6 +20,7 @@
#include <linux/buffer_head.h>
#include <linux/hdreg.h>
#include <linux/async.h>
+#include <linux/mutex.h>
#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
@@ -112,6 +113,7 @@ struct dasd_device *dasd_alloc_device(void)
INIT_WORK(&device->restore_device, do_restore_device);
device->state = DASD_STATE_NEW;
device->target = DASD_STATE_NEW;
+ mutex_init(&device->state_mutex);
return device;
}
@@ -321,8 +323,8 @@ static int dasd_state_ready_to_basic(struct dasd_device *device)
device->state = DASD_STATE_READY;
return rc;
}
- dasd_destroy_partitions(block);
dasd_flush_request_queue(block);
+ dasd_destroy_partitions(block);
block->blocks = 0;
block->bp_block = 0;
block->s2b_shift = 0;
@@ -484,10 +486,8 @@ static void dasd_change_state(struct dasd_device *device)
if (rc)
device->target = device->state;
- if (device->state == device->target) {
+ if (device->state == device->target)
wake_up(&dasd_init_waitq);
- dasd_put_device(device);
- }
/* let user-space know that the device status changed */
kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
@@ -502,7 +502,9 @@ static void dasd_change_state(struct dasd_device *device)
static void do_kick_device(struct work_struct *work)
{
struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
+ mutex_lock(&device->state_mutex);
dasd_change_state(device);
+ mutex_unlock(&device->state_mutex);
dasd_schedule_device_bh(device);
dasd_put_device(device);
}
@@ -539,18 +541,19 @@ void dasd_restore_device(struct dasd_device *device)
void dasd_set_target_state(struct dasd_device *device, int target)
{
dasd_get_device(device);
+ mutex_lock(&device->state_mutex);
/* If we are in probeonly mode stop at DASD_STATE_READY. */
if (dasd_probeonly && target > DASD_STATE_READY)
target = DASD_STATE_READY;
if (device->target != target) {
- if (device->state == target) {
+ if (device->state == target)
wake_up(&dasd_init_waitq);
- dasd_put_device(device);
- }
device->target = target;
}
if (device->state != device->target)
dasd_change_state(device);
+ mutex_unlock(&device->state_mutex);
+ dasd_put_device(device);
}
/*
@@ -1000,12 +1003,20 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
return;
}
- device = (struct dasd_device *) cqr->startdev;
- if (device == NULL ||
- device != dasd_device_from_cdev_locked(cdev) ||
- strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
+ device = dasd_device_from_cdev_locked(cdev);
+ if (IS_ERR(device)) {
+ DBF_EVENT_DEVID(DBF_DEBUG, cdev, "%s",
+ "unable to get device from cdev");
+ return;
+ }
+
+ if (!cqr->startdev ||
+ device != cqr->startdev ||
+ strncmp(cqr->startdev->discipline->ebcname,
+ (char *) &cqr->magic, 4)) {
DBF_EVENT_DEVID(DBF_DEBUG, cdev, "%s",
"invalid device in request");
+ dasd_put_device(device);
return;
}
@@ -1692,7 +1703,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr)
cqr, rc);
} else {
cqr->stopclk = get_clock();
- rc = 1;
}
break;
default: /* already finished or clear pending - do nothing */
@@ -2129,9 +2139,8 @@ static void dasd_setup_queue(struct dasd_block *block)
blk_queue_logical_block_size(block->request_queue, block->bp_block);
max = block->base->discipline->max_blocks << block->s2b_shift;
- blk_queue_max_sectors(block->request_queue, max);
- blk_queue_max_phys_segments(block->request_queue, -1L);
- blk_queue_max_hw_segments(block->request_queue, -1L);
+ blk_queue_max_hw_sectors(block->request_queue, max);
+ blk_queue_max_segments(block->request_queue, -1L);
/* with page sized segments we can translate each segement into
* one idaw/tidaw
*/
@@ -2170,9 +2179,13 @@ static void dasd_flush_request_queue(struct dasd_block *block)
static int dasd_open(struct block_device *bdev, fmode_t mode)
{
struct dasd_block *block = bdev->bd_disk->private_data;
- struct dasd_device *base = block->base;
+ struct dasd_device *base;
int rc;
+ if (!block)
+ return -ENODEV;
+
+ base = block->base;
atomic_inc(&block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
rc = -ENODEV;
@@ -2285,11 +2298,6 @@ static void dasd_generic_auto_online(void *data, async_cookie_t cookie)
if (ret)
pr_warning("%s: Setting the DASD online failed with rc=%d\n",
dev_name(&cdev->dev), ret);
- else {
- struct dasd_device *device = dasd_device_from_cdev(cdev);
- wait_event(dasd_init_waitq, _wait_for_device(device));
- dasd_put_device(device);
- }
}
/*
@@ -2424,6 +2432,9 @@ int dasd_generic_set_online(struct ccw_device *cdev,
} else
pr_debug("dasd_generic device %s found\n",
dev_name(&cdev->dev));
+
+ wait_event(dasd_init_waitq, _wait_for_device(device));
+
dasd_put_device(device);
return rc;
}
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 4cac5b54f26..d49766f3b94 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -874,12 +874,19 @@ dasd_discipline_show(struct device *dev, struct device_attribute *attr,
ssize_t len;
device = dasd_device_from_cdev(to_ccwdev(dev));
- if (!IS_ERR(device) && device->discipline) {
+ if (IS_ERR(device))
+ goto out;
+ else if (!device->discipline) {
+ dasd_put_device(device);
+ goto out;
+ } else {
len = snprintf(buf, PAGE_SIZE, "%s\n",
device->discipline->name);
dasd_put_device(device);
- } else
- len = snprintf(buf, PAGE_SIZE, "none\n");
+ return len;
+ }
+out:
+ len = snprintf(buf, PAGE_SIZE, "none\n");
return len;
}
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index d3198303b93..94f92a1247f 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -88,6 +88,7 @@ void dasd_gendisk_free(struct dasd_block *block)
if (block->gdp) {
del_gendisk(block->gdp);
block->gdp->queue = NULL;
+ block->gdp->private_data = NULL;
put_disk(block->gdp);
block->gdp = NULL;
}
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index e4c2143dabf..ed73ce55082 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -368,6 +368,7 @@ struct dasd_device {
/* Device state and target state. */
int state, target;
+ struct mutex state_mutex;
int stopped; /* device (ccw_device_start) was stopped */
/* reference count. */
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 71f95f54866..f13a0bdd148 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -165,51 +165,32 @@ static const struct file_operations dasd_devices_file_ops = {
.release = seq_release,
};
-static int
-dasd_calc_metrics(char *page, char **start, off_t off,
- int count, int *eof, int len)
-{
- len = (len > off) ? len - off : 0;
- if (len > count)
- len = count;
- if (len < count)
- *eof = 1;
- *start = page + off;
- return len;
-}
-
#ifdef CONFIG_DASD_PROFILE
-static char *
-dasd_statistics_array(char *str, unsigned int *array, int factor)
+static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor)
{
int i;
for (i = 0; i < 32; i++) {
- str += sprintf(str, "%7d ", array[i] / factor);
+ seq_printf(m, "%7d ", array[i] / factor);
if (i == 15)
- str += sprintf(str, "\n");
+ seq_putc(m, '\n');
}
- str += sprintf(str,"\n");
- return str;
+ seq_putc(m, '\n');
}
#endif /* CONFIG_DASD_PROFILE */
-static int
-dasd_statistics_read(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int dasd_stats_proc_show(struct seq_file *m, void *v)
{
- unsigned long len;
#ifdef CONFIG_DASD_PROFILE
struct dasd_profile_info_t *prof;
- char *str;
int factor;
/* check for active profiling */
if (dasd_profile_level == DASD_PROFILE_OFF) {
- len = sprintf(page, "Statistics are off - they might be "
+ seq_printf(m, "Statistics are off - they might be "
"switched on using 'echo set on > "
"/proc/dasd/statistics'\n");
- return dasd_calc_metrics(page, start, off, count, eof, len);
+ return 0;
}
prof = &dasd_global_profile;
@@ -217,47 +198,49 @@ dasd_statistics_read(char *page, char **start, off_t off,
for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
factor *= 10);
- str = page;
- str += sprintf(str, "%d dasd I/O requests\n", prof->dasd_io_reqs);
- str += sprintf(str, "with %u sectors(512B each)\n",
+ seq_printf(m, "%d dasd I/O requests\n", prof->dasd_io_reqs);
+ seq_printf(m, "with %u sectors(512B each)\n",
prof->dasd_io_sects);
- str += sprintf(str, "Scale Factor is %d\n", factor);
- str += sprintf(str,
+ seq_printf(m, "Scale Factor is %d\n", factor);
+ seq_printf(m,
" __<4 ___8 __16 __32 __64 _128 "
" _256 _512 __1k __2k __4k __8k "
" _16k _32k _64k 128k\n");
- str += sprintf(str,
+ seq_printf(m,
" _256 _512 __1M __2M __4M __8M "
" _16M _32M _64M 128M 256M 512M "
" __1G __2G __4G " " _>4G\n");
- str += sprintf(str, "Histogram of sizes (512B secs)\n");
- str = dasd_statistics_array(str, prof->dasd_io_secs, factor);
- str += sprintf(str, "Histogram of I/O times (microseconds)\n");
- str = dasd_statistics_array(str, prof->dasd_io_times, factor);
- str += sprintf(str, "Histogram of I/O times per sector\n");
- str = dasd_statistics_array(str, prof->dasd_io_timps, factor);
- str += sprintf(str, "Histogram of I/O time till ssch\n");
- str = dasd_statistics_array(str, prof->dasd_io_time1, factor);
- str += sprintf(str, "Histogram of I/O time between ssch and irq\n");
- str = dasd_statistics_array(str, prof->dasd_io_time2, factor);
- str += sprintf(str, "Histogram of I/O time between ssch "
+ seq_printf(m, "Histogram of sizes (512B secs)\n");
+ dasd_statistics_array(m, prof->dasd_io_secs, factor);
+ seq_printf(m, "Histogram of I/O times (microseconds)\n");
+ dasd_statistics_array(m, prof->dasd_io_times, factor);
+ seq_printf(m, "Histogram of I/O times per sector\n");
+ dasd_statistics_array(m, prof->dasd_io_timps, factor);
+ seq_printf(m, "Histogram of I/O time till ssch\n");
+ dasd_statistics_array(m, prof->dasd_io_time1, factor);
+ seq_printf(m, "Histogram of I/O time between ssch and irq\n");
+ dasd_statistics_array(m, prof->dasd_io_time2, factor);
+ seq_printf(m, "Histogram of I/O time between ssch "
"and irq per sector\n");
- str = dasd_statistics_array(str, prof->dasd_io_time2ps, factor);
- str += sprintf(str, "Histogram of I/O time between irq and end\n");
- str = dasd_statistics_array(str, prof->dasd_io_time3, factor);
- str += sprintf(str, "# of req in chanq at enqueuing (1..32) \n");
- str = dasd_statistics_array(str, prof->dasd_io_nr_req, factor);
- len = str - page;
+ dasd_statistics_array(m, prof->dasd_io_time2ps, factor);
+ seq_printf(m, "Histogram of I/O time between irq and end\n");
+ dasd_statistics_array(m, prof->dasd_io_time3, factor);
+ seq_printf(m, "# of req in chanq at enqueuing (1..32) \n");
+ dasd_statistics_array(m, prof->dasd_io_nr_req, factor);
#else
- len = sprintf(page, "Statistics are not activated in this kernel\n");
+ seq_printf(m, "Statistics are not activated in this kernel\n");
#endif
- return dasd_calc_metrics(page, start, off, count, eof, len);
+ return 0;
}
-static int
-dasd_statistics_write(struct file *file, const char __user *user_buf,
- unsigned long user_len, void *data)
+static int dasd_stats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dasd_stats_proc_show, NULL);
+}
+
+static ssize_t dasd_stats_proc_write(struct file *file,
+ const char __user *user_buf, size_t user_len, loff_t *pos)
{
#ifdef CONFIG_DASD_PROFILE
char *buffer, *str;
@@ -308,6 +291,15 @@ out_error:
#endif /* CONFIG_DASD_PROFILE */
}
+static const struct file_operations dasd_stats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = dasd_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = dasd_stats_proc_write,
+};
+
/*
* Create dasd proc-fs entries.
* In case creation failed, cleanup and return -ENOENT.
@@ -324,13 +316,12 @@ dasd_proc_init(void)
&dasd_devices_file_ops);
if (!dasd_devices_entry)
goto out_nodevices;
- dasd_statistics_entry = create_proc_entry("statistics",
- S_IFREG | S_IRUGO | S_IWUSR,
- dasd_proc_root_entry);
+ dasd_statistics_entry = proc_create("statistics",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ dasd_proc_root_entry,
+ &dasd_stats_proc_fops);
if (!dasd_statistics_entry)
goto out_nostatistics;
- dasd_statistics_entry->read_proc = dasd_statistics_read;
- dasd_statistics_entry->write_proc = dasd_statistics_write;
return 0;
out_nostatistics:
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 8d3d720737d..097da8ce6be 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -222,9 +222,8 @@ tapeblock_setup_device(struct tape_device * device)
goto cleanup_queue;
blk_queue_logical_block_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
- blk_queue_max_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
- blk_queue_max_phys_segments(blkdat->request_queue, -1L);
- blk_queue_max_hw_segments(blkdat->request_queue, -1L);
+ blk_queue_max_hw_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
+ blk_queue_max_segments(blkdat->request_queue, -1L);
blk_queue_max_segment_size(blkdat->request_queue, -1L);
blk_queue_segment_boundary(blkdat->request_queue, -1L);
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 82daa3c1dc9..3438658b66b 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/debugfs.h>
+#include <asm/asm-offsets.h>
#include <asm/ipl.h>
#include <asm/sclp.h>
#include <asm/setup.h>
@@ -40,12 +41,12 @@ enum arch_id {
/* dump system info */
struct sys_info {
- enum arch_id arch;
- unsigned long sa_base;
- u32 sa_size;
- int cpu_map[NR_CPUS];
- unsigned long mem_size;
- union save_area lc_mask;
+ enum arch_id arch;
+ unsigned long sa_base;
+ u32 sa_size;
+ int cpu_map[NR_CPUS];
+ unsigned long mem_size;
+ struct save_area lc_mask;
};
struct ipib_info {
@@ -183,52 +184,9 @@ static int memcpy_real_user(void __user *dest, unsigned long src, size_t count)
return 0;
}
-#ifdef __s390x__
-/*
- * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info
- */
-static void __init s390x_to_s390_regs(union save_area *out, union save_area *in,
- int cpu)
-{
- int i;
-
- for (i = 0; i < 16; i++) {
- out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff;
- out->s390.acc_regs[i] = in->s390x.acc_regs[i];
- out->s390.ctrl_regs[i] =
- in->s390x.ctrl_regs[i] & 0x00000000ffffffff;
- }
- /* locore for 31 bit has only space for fpregs 0,2,4,6 */
- out->s390.fp_regs[0] = in->s390x.fp_regs[0];
- out->s390.fp_regs[1] = in->s390x.fp_regs[2];
- out->s390.fp_regs[2] = in->s390x.fp_regs[4];
- out->s390.fp_regs[3] = in->s390x.fp_regs[6];
- memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4);
- out->s390.psw[1] |= 0x8; /* set bit 12 */
- memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4);
- out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */
- out->s390.pref_reg = in->s390x.pref_reg;
- out->s390.timer = in->s390x.timer;
- out->s390.clk_cmp = in->s390x.clk_cmp;
-}
-
-static void __init s390x_to_s390_save_areas(void)
-{
- int i = 1;
- static union save_area tmp;
-
- while (zfcpdump_save_areas[i]) {
- s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i);
- memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp));
- i++;
- }
-}
-
-#endif /* __s390x__ */
-
static int __init init_cpu_info(enum arch_id arch)
{
- union save_area *sa;
+ struct save_area *sa;
/* get info for boot cpu from lowcore, stored in the HSA */
@@ -241,20 +199,12 @@ static int __init init_cpu_info(enum arch_id arch)
return -EIO;
}
zfcpdump_save_areas[0] = sa;
-
-#ifdef __s390x__
- /* convert s390x regs to s390, if we are dumping an s390 Linux */
-
- if (arch == ARCH_S390)
- s390x_to_s390_save_areas();
-#endif
-
return 0;
}
static DEFINE_MUTEX(zcore_mutex);
-#define DUMP_VERSION 0x3
+#define DUMP_VERSION 0x5
#define DUMP_MAGIC 0xa8190173618f23fdULL
#define DUMP_ARCH_S390X 2
#define DUMP_ARCH_S390 1
@@ -279,7 +229,14 @@ struct zcore_header {
u32 volnr;
u32 build_arch;
u64 rmem_size;
- char pad2[4016];
+ u8 mvdump;
+ u16 cpu_cnt;
+ u16 real_cpu_cnt;
+ u8 end_pad1[0x200-0x061];
+ u64 mvdump_sign;
+ u64 mvdump_zipl_time;
+ u8 end_pad2[0x800-0x210];
+ u32 lc_vec[512];
} __attribute__((packed,__aligned__(16)));
static struct zcore_header zcore_header = {
@@ -289,7 +246,7 @@ static struct zcore_header zcore_header = {
.dump_level = 0,
.page_size = PAGE_SIZE,
.mem_start = 0,
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
.build_arch = DUMP_ARCH_S390X,
#else
.build_arch = DUMP_ARCH_S390,
@@ -340,11 +297,7 @@ static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
unsigned long prefix;
unsigned long sa_off, len, buf_off;
- if (sys_info.arch == ARCH_S390)
- prefix = zfcpdump_save_areas[i]->s390.pref_reg;
- else
- prefix = zfcpdump_save_areas[i]->s390x.pref_reg;
-
+ prefix = zfcpdump_save_areas[i]->pref_reg;
sa_start = prefix + sys_info.sa_base;
sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
@@ -561,34 +514,39 @@ static const struct file_operations zcore_reipl_fops = {
.release = zcore_reipl_release,
};
+#ifdef CONFIG_32BIT
-static void __init set_s390_lc_mask(union save_area *map)
+static void __init set_lc_mask(struct save_area *map)
{
- memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save));
- memset(&map->s390.timer, 0xff, sizeof(map->s390.timer));
- memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp));
- memset(&map->s390.psw, 0xff, sizeof(map->s390.psw));
- memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg));
- memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs));
- memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs));
- memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs));
- memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs));
+ memset(&map->ext_save, 0xff, sizeof(map->ext_save));
+ memset(&map->timer, 0xff, sizeof(map->timer));
+ memset(&map->clk_cmp, 0xff, sizeof(map->clk_cmp));
+ memset(&map->psw, 0xff, sizeof(map->psw));
+ memset(&map->pref_reg, 0xff, sizeof(map->pref_reg));
+ memset(&map->acc_regs, 0xff, sizeof(map->acc_regs));
+ memset(&map->fp_regs, 0xff, sizeof(map->fp_regs));
+ memset(&map->gp_regs, 0xff, sizeof(map->gp_regs));
+ memset(&map->ctrl_regs, 0xff, sizeof(map->ctrl_regs));
}
-static void __init set_s390x_lc_mask(union save_area *map)
+#else /* CONFIG_32BIT */
+
+static void __init set_lc_mask(struct save_area *map)
{
- memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs));
- memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs));
- memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw));
- memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg));
- memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg));
- memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg));
- memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer));
- memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp));
- memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs));
- memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs));
+ memset(&map->fp_regs, 0xff, sizeof(map->fp_regs));
+ memset(&map->gp_regs, 0xff, sizeof(map->gp_regs));
+ memset(&map->psw, 0xff, sizeof(map->psw));
+ memset(&map->pref_reg, 0xff, sizeof(map->pref_reg));
+ memset(&map->fp_ctrl_reg, 0xff, sizeof(map->fp_ctrl_reg));
+ memset(&map->tod_reg, 0xff, sizeof(map->tod_reg));
+ memset(&map->timer, 0xff, sizeof(map->timer));
+ memset(&map->clk_cmp, 0xff, sizeof(map->clk_cmp));
+ memset(&map->acc_regs, 0xff, sizeof(map->acc_regs));
+ memset(&map->ctrl_regs, 0xff, sizeof(map->ctrl_regs));
}
+#endif /* CONFIG_32BIT */
+
/*
* Initialize dump globals for a given architecture
*/
@@ -599,21 +557,18 @@ static int __init sys_info_init(enum arch_id arch)
switch (arch) {
case ARCH_S390X:
pr_alert("DETECTED 'S390X (64 bit) OS'\n");
- sys_info.sa_base = SAVE_AREA_BASE_S390X;
- sys_info.sa_size = sizeof(struct save_area_s390x);
- set_s390x_lc_mask(&sys_info.lc_mask);
break;
case ARCH_S390:
pr_alert("DETECTED 'S390 (32 bit) OS'\n");
- sys_info.sa_base = SAVE_AREA_BASE_S390;
- sys_info.sa_size = sizeof(struct save_area_s390);
- set_s390_lc_mask(&sys_info.lc_mask);
break;
default:
pr_alert("0x%x is an unknown architecture.\n",arch);
return -EINVAL;
}
+ sys_info.sa_base = SAVE_AREA_BASE;
+ sys_info.sa_size = sizeof(struct save_area);
sys_info.arch = arch;
+ set_lc_mask(&sys_info.lc_mask);
rc = init_cpu_info(arch);
if (rc)
return rc;
@@ -660,8 +615,9 @@ static int __init get_mem_size(unsigned long *mem)
static int __init zcore_header_init(int arch, struct zcore_header *hdr)
{
- int rc;
+ int rc, i;
unsigned long memory = 0;
+ u32 prefix;
if (arch == ARCH_S390X)
hdr->arch_id = DUMP_ARCH_S390X;
@@ -676,6 +632,14 @@ static int __init zcore_header_init(int arch, struct zcore_header *hdr)
hdr->num_pages = memory / PAGE_SIZE;
hdr->tod = get_clock();
get_cpu_id(&hdr->cpu_id);
+ for (i = 0; zfcpdump_save_areas[i]; i++) {
+ prefix = zfcpdump_save_areas[i]->pref_reg;
+ hdr->real_cpu_cnt++;
+ if (!prefix)
+ continue;
+ hdr->lc_vec[hdr->cpu_cnt] = prefix;
+ hdr->cpu_cnt++;
+ }
return 0;
}
@@ -741,14 +705,21 @@ static int __init zcore_init(void)
if (rc)
goto fail;
-#ifndef __s390x__
+#ifdef CONFIG_64BIT
+ if (arch == ARCH_S390) {
+ pr_alert("The 64-bit dump tool cannot be used for a "
+ "32-bit system\n");
+ rc = -EINVAL;
+ goto fail;
+ }
+#else /* CONFIG_64BIT */
if (arch == ARCH_S390X) {
pr_alert("The 32-bit dump tool cannot be used for a "
"64-bit system\n");
rc = -EINVAL;
goto fail;
}
-#endif
+#endif /* CONFIG_64BIT */
rc = sys_info_init(arch);
if (rc)
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c
index 7a28a3029a3..37df42af05e 100644
--- a/drivers/s390/cio/ccwreq.c
+++ b/drivers/s390/cio/ccwreq.c
@@ -224,8 +224,8 @@ static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status)
*/
void ccw_request_handler(struct ccw_device *cdev)
{
+ struct irb *irb = (struct irb *)&S390_lowcore.irb;
struct ccw_request *req = &cdev->private->req;
- struct irb *irb = (struct irb *) __LC_IRB;
enum io_status status;
int rc = -EOPNOTSUPP;
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 1ecd3e56764..4038f5b4f14 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -574,7 +574,7 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
secm_area->request.length = 0x0050;
secm_area->request.code = 0x0016;
- secm_area->key = PAGE_DEFAULT_KEY;
+ secm_area->key = PAGE_DEFAULT_KEY >> 4;
secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index c84ac944307..852612f5dba 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -51,7 +51,7 @@ static void chsc_subchannel_irq(struct subchannel *sch)
{
struct chsc_private *private = sch->private;
struct chsc_request *request = private->request;
- struct irb *irb = (struct irb *)__LC_IRB;
+ struct irb *irb = (struct irb *)&S390_lowcore.irb;
CHSC_LOG(4, "irb");
CHSC_LOG_HEX(4, irb, sizeof(*irb));
@@ -237,7 +237,7 @@ static int chsc_async(struct chsc_async_area *chsc_area,
int ret = -ENODEV;
char dbf[10];
- chsc_area->header.key = PAGE_DEFAULT_KEY;
+ chsc_area->header.key = PAGE_DEFAULT_KEY >> 4;
while ((sch = chsc_get_next_subchannel(sch))) {
spin_lock(sch->lock);
private = sch->private;
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 126f240715a..f736cdcf08a 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -625,8 +625,8 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
/*
* Get interrupt information from lowcore
*/
- tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
- irb = (struct irb *) __LC_IRB;
+ tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id;
+ irb = (struct irb *)&S390_lowcore.irb;
do {
kstat_cpu(smp_processor_id()).irqs[IO_INTERRUPT]++;
/*
@@ -661,7 +661,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
* We don't do this for VM because a tpi drops the cpu
* out of the sie which costs more cycles than it saves.
*/
- } while (!MACHINE_IS_VM && tpi (NULL) != 0);
+ } while (MACHINE_IS_LPAR && tpi(NULL) != 0);
irq_exit();
set_irq_regs(old_regs);
}
@@ -682,10 +682,10 @@ static int cio_tpi(void)
struct irb *irb;
int irq_context;
- tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
+ tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id;
if (tpi(NULL) != 1)
return 0;
- irb = (struct irb *) __LC_IRB;
+ irb = (struct irb *)&S390_lowcore.irb;
/* Store interrupt response block to lowcore. */
if (tsch(tpi_info->schid, irb) != 0)
/* Not status pending or not operational. */
@@ -885,7 +885,7 @@ __clear_io_subchannel_easy(struct subchannel_id schid)
struct tpi_info ti;
if (tpi(&ti)) {
- tsch(ti.schid, (struct irb *)__LC_IRB);
+ tsch(ti.schid, (struct irb *)&S390_lowcore.irb);
if (schid_equal(&ti.schid, &schid))
return 0;
}
@@ -1083,7 +1083,7 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
struct subchannel_id schid;
struct schib schib;
- schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
+ schid = *(struct subchannel_id *)&S390_lowcore.subchannel_id;
if (!schid.one)
return -ENODEV;
if (stsch(schid, &schib))
diff --git a/drivers/s390/cio/crw.c b/drivers/s390/cio/crw.c
index d157665d0e7..425f741a280 100644
--- a/drivers/s390/cio/crw.c
+++ b/drivers/s390/cio/crw.c
@@ -8,15 +8,16 @@
* Heiko Carstens <heiko.carstens@de.ibm.com>,
*/
-#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/init.h>
+#include <linux/wait.h>
#include <asm/crw.h>
-static struct semaphore crw_semaphore;
static DEFINE_MUTEX(crw_handler_mutex);
static crw_handler_t crw_handlers[NR_RSCS];
+static atomic_t crw_nr_req = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(crw_handler_wait_q);
/**
* crw_register_handler() - register a channel report word handler
@@ -59,12 +60,14 @@ void crw_unregister_handler(int rsc)
static int crw_collect_info(void *unused)
{
struct crw crw[2];
- int ccode;
+ int ccode, signal;
unsigned int chain;
- int ignore;
repeat:
- ignore = down_interruptible(&crw_semaphore);
+ signal = wait_event_interruptible(crw_handler_wait_q,
+ atomic_read(&crw_nr_req) > 0);
+ if (unlikely(signal))
+ atomic_inc(&crw_nr_req);
chain = 0;
while (1) {
crw_handler_t handler;
@@ -122,25 +125,23 @@ repeat:
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}
+ if (atomic_dec_and_test(&crw_nr_req))
+ wake_up(&crw_handler_wait_q);
goto repeat;
return 0;
}
void crw_handle_channel_report(void)
{
- up(&crw_semaphore);
+ atomic_inc(&crw_nr_req);
+ wake_up(&crw_handler_wait_q);
}
-/*
- * Separate initcall needed for semaphore initialization since
- * crw_handle_channel_report might be called before crw_machine_check_init.
- */
-static int __init crw_init_semaphore(void)
+void crw_wait_for_channel_report(void)
{
- init_MUTEX_LOCKED(&crw_semaphore);
- return 0;
+ crw_handle_channel_report();
+ wait_event(crw_handler_wait_q, atomic_read(&crw_nr_req) == 0);
}
-pure_initcall(crw_init_semaphore);
/*
* Machine checks for the channel subsystem must be enabled
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 7679aee6fa1..2769da54f2b 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -18,6 +18,7 @@
#include <linux/list.h>
#include <linux/reboot.h>
#include <linux/suspend.h>
+#include <linux/proc_fs.h>
#include <asm/isc.h>
#include <asm/crw.h>
@@ -232,7 +233,7 @@ void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
if (!get_device(&sch->dev))
return;
sch->todo = todo;
- if (!queue_work(slow_path_wq, &sch->todo_work)) {
+ if (!queue_work(cio_work_q, &sch->todo_work)) {
/* Already queued, release workqueue ref. */
put_device(&sch->dev);
}
@@ -543,7 +544,7 @@ static void css_slow_path_func(struct work_struct *unused)
}
static DECLARE_WORK(slow_path_work, css_slow_path_func);
-struct workqueue_struct *slow_path_wq;
+struct workqueue_struct *cio_work_q;
void css_schedule_eval(struct subchannel_id schid)
{
@@ -552,7 +553,7 @@ void css_schedule_eval(struct subchannel_id schid)
spin_lock_irqsave(&slow_subchannel_lock, flags);
idset_sch_add(slow_subchannel_set, schid);
atomic_set(&css_eval_scheduled, 1);
- queue_work(slow_path_wq, &slow_path_work);
+ queue_work(cio_work_q, &slow_path_work);
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
}
@@ -563,7 +564,7 @@ void css_schedule_eval_all(void)
spin_lock_irqsave(&slow_subchannel_lock, flags);
idset_fill(slow_subchannel_set);
atomic_set(&css_eval_scheduled, 1);
- queue_work(slow_path_wq, &slow_path_work);
+ queue_work(cio_work_q, &slow_path_work);
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
}
@@ -594,14 +595,14 @@ void css_schedule_eval_all_unreg(void)
spin_lock_irqsave(&slow_subchannel_lock, flags);
idset_add_set(slow_subchannel_set, unreg_set);
atomic_set(&css_eval_scheduled, 1);
- queue_work(slow_path_wq, &slow_path_work);
+ queue_work(cio_work_q, &slow_path_work);
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
idset_free(unreg_set);
}
void css_wait_for_slow_path(void)
{
- flush_workqueue(slow_path_wq);
+ flush_workqueue(cio_work_q);
}
/* Schedule reprobing of all unregistered subchannels. */
@@ -992,12 +993,21 @@ static int __init channel_subsystem_init(void)
ret = css_bus_init();
if (ret)
return ret;
-
+ cio_work_q = create_singlethread_workqueue("cio");
+ if (!cio_work_q) {
+ ret = -ENOMEM;
+ goto out_bus;
+ }
ret = io_subchannel_init();
if (ret)
- css_bus_cleanup();
+ goto out_wq;
return ret;
+out_wq:
+ destroy_workqueue(cio_work_q);
+out_bus:
+ css_bus_cleanup();
+ return ret;
}
subsys_initcall(channel_subsystem_init);
@@ -1006,10 +1016,25 @@ static int css_settle(struct device_driver *drv, void *unused)
struct css_driver *cssdrv = to_cssdriver(drv);
if (cssdrv->settle)
- cssdrv->settle();
+ return cssdrv->settle();
return 0;
}
+int css_complete_work(void)
+{
+ int ret;
+
+ /* Wait for the evaluation of subchannels to finish. */
+ ret = wait_event_interruptible(css_eval_wq,
+ atomic_read(&css_eval_scheduled) == 0);
+ if (ret)
+ return -EINTR;
+ flush_workqueue(cio_work_q);
+ /* Wait for the subchannel type specific initialization to finish */
+ return bus_for_each_drv(&css_bus_type, NULL, NULL, css_settle);
+}
+
+
/*
* Wait for the initialization of devices to finish, to make sure we are
* done with our setup if the search for the root device starts.
@@ -1018,13 +1043,41 @@ static int __init channel_subsystem_init_sync(void)
{
/* Start initial subchannel evaluation. */
css_schedule_eval_all();
- /* Wait for the evaluation of subchannels to finish. */
- wait_event(css_eval_wq, atomic_read(&css_eval_scheduled) == 0);
- /* Wait for the subchannel type specific initialization to finish */
- return bus_for_each_drv(&css_bus_type, NULL, NULL, css_settle);
+ css_complete_work();
+ return 0;
}
subsys_initcall_sync(channel_subsystem_init_sync);
+#ifdef CONFIG_PROC_FS
+static ssize_t cio_settle_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+
+ /* Handle pending CRW's. */
+ crw_wait_for_channel_report();
+ ret = css_complete_work();
+
+ return ret ? ret : count;
+}
+
+static const struct file_operations cio_settle_proc_fops = {
+ .write = cio_settle_write,
+};
+
+static int __init cio_settle_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ entry = proc_create("cio_settle", S_IWUSR, NULL,
+ &cio_settle_proc_fops);
+ if (!entry)
+ return -ENOMEM;
+ return 0;
+}
+device_initcall(cio_settle_init);
+#endif /*CONFIG_PROC_FS*/
+
int sch_is_pseudo_sch(struct subchannel *sch)
{
return sch == to_css(sch->dev.parent)->pseudo_subchannel;
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index fe84b92cde6..7e37886de23 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -95,7 +95,7 @@ struct css_driver {
int (*freeze)(struct subchannel *);
int (*thaw) (struct subchannel *);
int (*restore)(struct subchannel *);
- void (*settle)(void);
+ int (*settle)(void);
const char *name;
};
@@ -146,12 +146,13 @@ extern struct channel_subsystem *channel_subsystems[];
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
+int css_complete_work(void);
int sch_is_pseudo_sch(struct subchannel *);
struct schib;
int css_sch_is_valid(struct schib *);
-extern struct workqueue_struct *slow_path_wq;
+extern struct workqueue_struct *cio_work_q;
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 a6c7d5426fb..c6abb75c461 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -136,7 +136,6 @@ static int io_subchannel_sch_event(struct subchannel *, int);
static int io_subchannel_chp_event(struct subchannel *, struct chp_link *,
int);
static void recovery_func(unsigned long data);
-struct workqueue_struct *ccw_device_work;
wait_queue_head_t ccw_device_init_wq;
atomic_t ccw_device_init_count;
@@ -159,11 +158,16 @@ static int io_subchannel_prepare(struct subchannel *sch)
return 0;
}
-static void io_subchannel_settle(void)
+static int io_subchannel_settle(void)
{
- wait_event(ccw_device_init_wq,
- atomic_read(&ccw_device_init_count) == 0);
- flush_workqueue(ccw_device_work);
+ int ret;
+
+ ret = wait_event_interruptible(ccw_device_init_wq,
+ atomic_read(&ccw_device_init_count) == 0);
+ if (ret)
+ return -EINTR;
+ flush_workqueue(cio_work_q);
+ return 0;
}
static struct css_driver io_subchannel_driver = {
@@ -188,27 +192,13 @@ int __init io_subchannel_init(void)
atomic_set(&ccw_device_init_count, 0);
setup_timer(&recovery_timer, recovery_func, 0);
- ccw_device_work = create_singlethread_workqueue("cio");
- if (!ccw_device_work)
- return -ENOMEM;
- slow_path_wq = create_singlethread_workqueue("kslowcrw");
- if (!slow_path_wq) {
- ret = -ENOMEM;
- goto out_err;
- }
- if ((ret = bus_register (&ccw_bus_type)))
- goto out_err;
-
+ ret = bus_register(&ccw_bus_type);
+ if (ret)
+ return ret;
ret = css_driver_register(&io_subchannel_driver);
if (ret)
- goto out_err;
+ bus_unregister(&ccw_bus_type);
- return 0;
-out_err:
- if (ccw_device_work)
- destroy_workqueue(ccw_device_work);
- if (slow_path_wq)
- destroy_workqueue(slow_path_wq);
return ret;
}
@@ -1348,7 +1338,7 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
/* Not operational. */
if (!cdev)
return IO_SCH_UNREG;
- if (!ccw_device_notify(cdev, CIO_GONE))
+ if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
return IO_SCH_UNREG;
return IO_SCH_ORPH_UNREG;
}
@@ -1356,12 +1346,12 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
if (!cdev)
return IO_SCH_ATTACH;
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
- if (!ccw_device_notify(cdev, CIO_GONE))
+ if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
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))
+ if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK)
return IO_SCH_UNREG;
return IO_SCH_DISC;
}
@@ -1410,6 +1400,12 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
rc = 0;
goto out_unlock;
case IO_SCH_VERIFY:
+ if (cdev->private->flags.resuming == 1) {
+ if (cio_enable_subchannel(sch, (u32)(addr_t)sch)) {
+ ccw_device_set_notoper(cdev);
+ break;
+ }
+ }
/* Trigger path verification. */
io_subchannel_verify(sch);
rc = 0;
@@ -1448,7 +1444,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
break;
case IO_SCH_UNREG_ATTACH:
/* Unregister ccw device. */
- ccw_device_unregister(cdev);
+ if (!cdev->private->flags.resuming)
+ ccw_device_unregister(cdev);
break;
default:
break;
@@ -1457,7 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
switch (action) {
case IO_SCH_ORPH_UNREG:
case IO_SCH_UNREG:
- css_sch_device_unregister(sch);
+ if (!cdev || !cdev->private->flags.resuming)
+ css_sch_device_unregister(sch);
break;
case IO_SCH_ORPH_ATTACH:
case IO_SCH_UNREG_ATTACH:
@@ -1779,26 +1777,42 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
- if (cio_is_console(sch->schid))
- goto out;
+ spin_lock_irq(sch->lock);
+ if (cio_is_console(sch->schid)) {
+ cio_enable_subchannel(sch, (u32)(addr_t)sch);
+ goto out_unlock;
+ }
/*
* While we were sleeping, devices may have gone or become
* available again. Kick re-detection.
*/
- spin_lock_irq(sch->lock);
cdev->private->flags.resuming = 1;
+ css_schedule_eval(sch->schid);
+ spin_unlock_irq(sch->lock);
+ css_complete_work();
+
+ /* cdev may have been moved to a different subchannel. */
+ sch = to_subchannel(cdev->dev.parent);
+ spin_lock_irq(sch->lock);
+ if (cdev->private->state != DEV_STATE_ONLINE &&
+ cdev->private->state != DEV_STATE_OFFLINE)
+ goto out_unlock;
+
ccw_device_recognition(cdev);
spin_unlock_irq(sch->lock);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) ||
cdev->private->state == DEV_STATE_DISCONNECTED);
-out:
+ spin_lock_irq(sch->lock);
+
+out_unlock:
cdev->private->flags.resuming = 0;
+ spin_unlock_irq(sch->lock);
}
static int resume_handle_boxed(struct ccw_device *cdev)
{
cdev->private->state = DEV_STATE_BOXED;
- if (ccw_device_notify(cdev, CIO_BOXED))
+ if (ccw_device_notify(cdev, CIO_BOXED) == NOTIFY_OK)
return 0;
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
return -ENODEV;
@@ -1807,7 +1821,7 @@ static int resume_handle_boxed(struct ccw_device *cdev)
static int resume_handle_disc(struct ccw_device *cdev)
{
cdev->private->state = DEV_STATE_DISCONNECTED;
- if (ccw_device_notify(cdev, CIO_GONE))
+ if (ccw_device_notify(cdev, CIO_GONE) == NOTIFY_OK)
return 0;
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
return -ENODEV;
@@ -1816,40 +1830,31 @@ static int resume_handle_disc(struct ccw_device *cdev)
static int ccw_device_pm_restore(struct device *dev)
{
struct ccw_device *cdev = to_ccwdev(dev);
- struct subchannel *sch = to_subchannel(cdev->dev.parent);
- int ret = 0, cm_enabled;
+ struct subchannel *sch;
+ int ret = 0;
__ccw_device_pm_restore(cdev);
+ sch = to_subchannel(cdev->dev.parent);
spin_lock_irq(sch->lock);
- if (cio_is_console(sch->schid)) {
- cio_enable_subchannel(sch, (u32)(addr_t)sch);
- spin_unlock_irq(sch->lock);
+ if (cio_is_console(sch->schid))
goto out_restore;
- }
- cdev->private->flags.donotify = 0;
+
/* check recognition results */
switch (cdev->private->state) {
case DEV_STATE_OFFLINE:
+ case DEV_STATE_ONLINE:
+ cdev->private->flags.donotify = 0;
break;
case DEV_STATE_BOXED:
ret = resume_handle_boxed(cdev);
- spin_unlock_irq(sch->lock);
if (ret)
- goto out;
+ goto out_unlock;
goto out_restore;
- case DEV_STATE_DISCONNECTED:
- goto out_disc_unlock;
default:
- goto out_unreg_unlock;
- }
- /* check if the device id has changed */
- if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
- CIO_MSG_EVENT(0, "resume: sch 0.%x.%04x: failed (devno "
- "changed from %04x to %04x)\n",
- sch->schid.ssid, sch->schid.sch_no,
- cdev->private->dev_id.devno,
- sch->schib.pmcw.dev);
- goto out_unreg_unlock;
+ ret = resume_handle_disc(cdev);
+ if (ret)
+ goto out_unlock;
+ goto out_restore;
}
/* check if the device type has changed */
if (!ccw_device_test_sense_data(cdev)) {
@@ -1858,24 +1863,30 @@ static int ccw_device_pm_restore(struct device *dev)
ret = -ENODEV;
goto out_unlock;
}
- if (!cdev->online) {
- ret = 0;
+ if (!cdev->online)
goto out_unlock;
- }
- ret = ccw_device_online(cdev);
- if (ret)
- goto out_disc_unlock;
- cm_enabled = cdev->private->cmb != NULL;
+ if (ccw_device_online(cdev)) {
+ ret = resume_handle_disc(cdev);
+ if (ret)
+ goto out_unlock;
+ goto out_restore;
+ }
spin_unlock_irq(sch->lock);
-
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
- if (cdev->private->state != DEV_STATE_ONLINE) {
- spin_lock_irq(sch->lock);
- goto out_disc_unlock;
+ spin_lock_irq(sch->lock);
+
+ if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_BAD) {
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+ ret = -ENODEV;
+ goto out_unlock;
}
- if (cm_enabled) {
+
+ /* reenable cmf, if needed */
+ if (cdev->private->cmb) {
+ spin_unlock_irq(sch->lock);
ret = ccw_set_cmf(cdev, 1);
+ spin_lock_irq(sch->lock);
if (ret) {
CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed "
"(rc=%d)\n", cdev->private->dev_id.ssid,
@@ -1885,21 +1896,11 @@ static int ccw_device_pm_restore(struct device *dev)
}
out_restore:
+ spin_unlock_irq(sch->lock);
if (cdev->online && cdev->drv && cdev->drv->restore)
ret = cdev->drv->restore(cdev);
-out:
return ret;
-out_disc_unlock:
- ret = resume_handle_disc(cdev);
- spin_unlock_irq(sch->lock);
- if (ret)
- return ret;
- goto out_restore;
-
-out_unreg_unlock:
- ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
- ret = -ENODEV;
out_unlock:
spin_unlock_irq(sch->lock);
return ret;
@@ -2028,7 +2029,7 @@ void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
/* Get workqueue ref. */
if (!get_device(&cdev->dev))
return;
- if (!queue_work(slow_path_wq, &cdev->private->todo_work)) {
+ if (!queue_work(cio_work_q, &cdev->private->todo_work)) {
/* Already queued, release workqueue ref. */
put_device(&cdev->dev);
}
@@ -2041,5 +2042,4 @@ EXPORT_SYMBOL(ccw_driver_register);
EXPORT_SYMBOL(ccw_driver_unregister);
EXPORT_SYMBOL(get_ccwdev_by_busid);
EXPORT_SYMBOL(ccw_bus_type);
-EXPORT_SYMBOL(ccw_device_work);
EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index bcfe13e4263..379de2d1ec4 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -4,7 +4,7 @@
#include <asm/ccwdev.h>
#include <asm/atomic.h>
#include <linux/wait.h>
-
+#include <linux/notifier.h>
#include "io_sch.h"
/*
@@ -71,7 +71,6 @@ dev_fsm_final_state(struct ccw_device *cdev)
cdev->private->state == DEV_STATE_BOXED);
}
-extern struct workqueue_struct *ccw_device_work;
extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
int __init io_subchannel_init(void);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index ae760658a13..c56ab94612f 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -313,21 +313,43 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
}
}
+/**
+ * ccw_device_notify() - inform the device's driver about an event
+ * @cdev: device for which an event occured
+ * @event: event that occurred
+ *
+ * Returns:
+ * -%EINVAL if the device is offline or has no driver.
+ * -%EOPNOTSUPP if the device's driver has no notifier registered.
+ * %NOTIFY_OK if the driver wants to keep the device.
+ * %NOTIFY_BAD if the driver doesn't want to keep the device.
+ */
int ccw_device_notify(struct ccw_device *cdev, int event)
{
+ int ret = -EINVAL;
+
if (!cdev->drv)
- return 0;
+ goto out;
if (!cdev->online)
- return 0;
+ goto out;
CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
event);
- return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+ if (!cdev->drv->notify) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+ if (cdev->drv->notify(cdev, event))
+ ret = NOTIFY_OK;
+ else
+ ret = NOTIFY_BAD;
+out:
+ return ret;
}
static void ccw_device_oper_notify(struct ccw_device *cdev)
{
- if (ccw_device_notify(cdev, CIO_OPER)) {
+ if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
/* Reenable channel measurements, if needed. */
ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
return;
@@ -361,14 +383,15 @@ ccw_device_done(struct ccw_device *cdev, int state)
case DEV_STATE_BOXED:
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))
+ if (cdev->online &&
+ ccw_device_notify(cdev, CIO_BOXED) != NOTIFY_OK)
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))
+ if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else
ccw_device_set_disconnected(cdev);
@@ -378,7 +401,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel "
"%04x\n", cdev->private->dev_id.devno,
sch->schid.sch_no);
- if (!ccw_device_notify(cdev, CIO_NO_PATH))
+ if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK)
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else
ccw_device_set_disconnected(cdev);
@@ -586,7 +609,7 @@ ccw_device_offline(struct ccw_device *cdev)
static void ccw_device_generic_notoper(struct ccw_device *cdev,
enum dev_event dev_event)
{
- if (!ccw_device_notify(cdev, CIO_GONE))
+ if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else
ccw_device_set_disconnected(cdev);
@@ -667,7 +690,7 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
struct irb *irb;
int is_cmd;
- irb = (struct irb *) __LC_IRB;
+ irb = (struct irb *)&S390_lowcore.irb;
is_cmd = !scsw_is_tm(&irb->scsw);
/* Check for unsolicited interrupt. */
if (!scsw_is_solicited(&irb->scsw)) {
@@ -732,7 +755,7 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
- irb = (struct irb *) __LC_IRB;
+ irb = (struct irb *)&S390_lowcore.irb;
/* Check for unsolicited interrupt. */
if (scsw_stctl(&irb->scsw) ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 44f2f6a97f3..48aa0647432 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -208,18 +208,27 @@ struct qdio_dev_perf_stat {
unsigned int eqbs_partial;
unsigned int sqbs;
unsigned int sqbs_partial;
+} ____cacheline_aligned;
+
+struct qdio_queue_perf_stat {
+ /*
+ * Sorted into order-2 buckets: 1, 2-3, 4-7, ... 64-127, 128.
+ * Since max. 127 SBALs are scanned reuse entry for 128 as queue full
+ * aka 127 SBALs found.
+ */
+ unsigned int nr_sbals[8];
+ unsigned int nr_sbal_error;
+ unsigned int nr_sbal_nop;
+ unsigned int nr_sbal_total;
};
struct qdio_input_q {
/* input buffer acknowledgement flag */
int polling;
-
/* first ACK'ed buffer */
int ack_start;
-
/* how much sbals are acknowledged with qebsm */
int ack_count;
-
/* last time of noticing incoming data */
u64 timestamp;
};
@@ -227,40 +236,27 @@ struct qdio_input_q {
struct qdio_output_q {
/* PCIs are enabled for the queue */
int pci_out_enabled;
-
/* IQDIO: output multiple buffers (enhanced SIGA) */
int use_enh_siga;
-
/* timer to check for more outbound work */
struct timer_list timer;
};
+/*
+ * Note on cache alignment: grouped slsb and write mostly data at the beginning
+ * sbal[] is read-only and starts on a new cacheline followed by read mostly.
+ */
struct qdio_q {
struct slsb slsb;
+
union {
struct qdio_input_q in;
struct qdio_output_q out;
} u;
- /* queue number */
- int nr;
-
- /* bitmask of queue number */
- int mask;
-
- /* input or output queue */
- int is_input_q;
-
- /* list of thinint input queues */
- struct list_head entry;
-
- /* upper-layer program handler */
- qdio_handler_t (*handler);
-
/*
* inbound: next buffer the program should check for
- * outbound: next buffer to check for having been processed
- * by the card
+ * outbound: next buffer to check if adapter processed it
*/
int first_to_check;
@@ -273,16 +269,32 @@ struct qdio_q {
/* number of buffers in use by the adapter */
atomic_t nr_buf_used;
- struct qdio_irq *irq_ptr;
- struct dentry *debugfs_q;
- struct tasklet_struct tasklet;
-
/* error condition during a data transfer */
unsigned int qdio_error;
- struct sl *sl;
- struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
+ struct tasklet_struct tasklet;
+ struct qdio_queue_perf_stat q_stats;
+
+ struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q] ____cacheline_aligned;
+
+ /* queue number */
+ int nr;
+
+ /* bitmask of queue number */
+ int mask;
+
+ /* input or output queue */
+ int is_input_q;
+
+ /* list of thinint input queues */
+ struct list_head entry;
+ /* upper-layer program handler */
+ qdio_handler_t (*handler);
+
+ struct dentry *debugfs_q;
+ struct qdio_irq *irq_ptr;
+ struct sl *sl;
/*
* Warning: Leave this member at the end so it won't be cleared in
* qdio_fill_qs. A page is allocated under this pointer and used for
@@ -317,12 +329,8 @@ struct qdio_irq {
struct qdio_ssqd_desc ssqd_desc;
void (*orig_handler) (struct ccw_device *, unsigned long, struct irb *);
- struct qdio_dev_perf_stat perf_stat;
int perf_stat_enabled;
- /*
- * Warning: Leave these members together at the end so they won't be
- * cleared in qdio_setup_irq.
- */
+
struct qdr *qdr;
unsigned long chsc_page;
@@ -331,6 +339,7 @@ struct qdio_irq {
debug_info_t *debug_area;
struct mutex setup_mutex;
+ struct qdio_dev_perf_stat perf_stat;
};
/* helper functions */
@@ -341,9 +350,20 @@ struct qdio_irq {
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \
css_general_characteristics.aif_osa)
-#define qperf(qdev,attr) qdev->perf_stat.attr
-#define qperf_inc(q,attr) if (q->irq_ptr->perf_stat_enabled) \
- q->irq_ptr->perf_stat.attr++
+#define qperf(__qdev, __attr) ((__qdev)->perf_stat.(__attr))
+
+#define qperf_inc(__q, __attr) \
+({ \
+ struct qdio_irq *qdev = (__q)->irq_ptr; \
+ if (qdev->perf_stat_enabled) \
+ (qdev->perf_stat.__attr)++; \
+})
+
+static inline void account_sbals_error(struct qdio_q *q, int count)
+{
+ q->q_stats.nr_sbal_error += count;
+ q->q_stats.nr_sbal_total += count;
+}
/* the highest iqdio queue is used for multicast */
static inline int multicast_outbound(struct qdio_q *q)
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index f49761ff9a0..c94eb2a0fa2 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -60,7 +60,7 @@ static int qstat_show(struct seq_file *m, void *v)
seq_printf(m, "ftc: %d last_move: %d\n", q->first_to_check, q->last_move);
seq_printf(m, "polling: %d ack start: %d ack count: %d\n",
q->u.in.polling, q->u.in.ack_start, q->u.in.ack_count);
- seq_printf(m, "slsb buffer states:\n");
+ seq_printf(m, "SBAL states:\n");
seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n");
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
@@ -97,6 +97,20 @@ static int qstat_show(struct seq_file *m, void *v)
}
seq_printf(m, "\n");
seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n");
+
+ seq_printf(m, "\nSBAL statistics:");
+ if (!q->irq_ptr->perf_stat_enabled) {
+ seq_printf(m, " disabled\n");
+ return 0;
+ }
+
+ seq_printf(m, "\n1 2.. 4.. 8.. "
+ "16.. 32.. 64.. 127\n");
+ for (i = 0; i < ARRAY_SIZE(q->q_stats.nr_sbals); i++)
+ seq_printf(m, "%-10u ", q->q_stats.nr_sbals[i]);
+ seq_printf(m, "\nError NOP Total\n%-10u %-10u %-10u\n\n",
+ q->q_stats.nr_sbal_error, q->q_stats.nr_sbal_nop,
+ q->q_stats.nr_sbal_total);
return 0;
}
@@ -181,9 +195,10 @@ static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
{
struct seq_file *seq = file->private_data;
struct qdio_irq *irq_ptr = seq->private;
+ struct qdio_q *q;
unsigned long val;
char buf[8];
- int ret;
+ int ret, i;
if (!irq_ptr)
return 0;
@@ -201,6 +216,10 @@ static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
case 0:
irq_ptr->perf_stat_enabled = 0;
memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
+ for_each_input_queue(irq_ptr, q, i)
+ memset(&q->q_stats, 0, sizeof(q->q_stats));
+ for_each_output_queue(irq_ptr, q, i)
+ memset(&q->q_stats, 0, sizeof(q->q_stats));
break;
case 1:
irq_ptr->perf_stat_enabled = 1;
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 62b654af923..232ef047ba3 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -392,6 +392,20 @@ static inline void qdio_stop_polling(struct qdio_q *q)
set_buf_state(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT);
}
+static inline void account_sbals(struct qdio_q *q, int count)
+{
+ int pos = 0;
+
+ q->q_stats.nr_sbal_total += count;
+ if (count == QDIO_MAX_BUFFERS_MASK) {
+ q->q_stats.nr_sbals[7]++;
+ return;
+ }
+ while (count >>= 1)
+ pos++;
+ q->q_stats.nr_sbals[pos]++;
+}
+
static void announce_buffer_error(struct qdio_q *q, int count)
{
q->qdio_error |= QDIO_ERROR_SLSB_STATE;
@@ -487,16 +501,22 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
q->first_to_check = add_buf(q->first_to_check, count);
if (atomic_sub(count, &q->nr_buf_used) == 0)
qperf_inc(q, inbound_queue_full);
+ if (q->irq_ptr->perf_stat_enabled)
+ account_sbals(q, count);
break;
case SLSB_P_INPUT_ERROR:
announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
+ if (q->irq_ptr->perf_stat_enabled)
+ account_sbals_error(q, count);
break;
case SLSB_CU_INPUT_EMPTY:
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_INPUT_ACK:
+ if (q->irq_ptr->perf_stat_enabled)
+ q->q_stats.nr_sbal_nop++;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
break;
default:
@@ -514,7 +534,7 @@ static int qdio_inbound_q_moved(struct qdio_q *q)
if ((bufnr != q->last_move) || q->qdio_error) {
q->last_move = bufnr;
- if (!is_thinint_irq(q->irq_ptr) && !MACHINE_IS_VM)
+ if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR)
q->u.in.timestamp = get_usecs();
return 1;
} else
@@ -643,15 +663,21 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
atomic_sub(count, &q->nr_buf_used);
q->first_to_check = add_buf(q->first_to_check, count);
+ if (q->irq_ptr->perf_stat_enabled)
+ account_sbals(q, count);
break;
case SLSB_P_OUTPUT_ERROR:
announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
+ if (q->irq_ptr->perf_stat_enabled)
+ account_sbals_error(q, count);
break;
case SLSB_CU_OUTPUT_PRIMED:
/* the adapter has not fetched the output yet */
+ if (q->irq_ptr->perf_stat_enabled)
+ q->q_stats.nr_sbal_nop++;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr);
break;
case SLSB_P_OUTPUT_NOT_INIT:
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 8c2dea5fa2b..7f4a7546514 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -333,10 +333,10 @@ static void __qdio_allocate_fill_qdr(struct qdio_irq *irq_ptr,
irq_ptr->qdr->qdf0[i + nr].slsba =
(unsigned long)&irq_ptr_qs[i]->slsb.val[0];
- irq_ptr->qdr->qdf0[i + nr].akey = PAGE_DEFAULT_KEY;
- irq_ptr->qdr->qdf0[i + nr].bkey = PAGE_DEFAULT_KEY;
- irq_ptr->qdr->qdf0[i + nr].ckey = PAGE_DEFAULT_KEY;
- irq_ptr->qdr->qdf0[i + nr].dkey = PAGE_DEFAULT_KEY;
+ irq_ptr->qdr->qdf0[i + nr].akey = PAGE_DEFAULT_KEY >> 4;
+ irq_ptr->qdr->qdf0[i + nr].bkey = PAGE_DEFAULT_KEY >> 4;
+ irq_ptr->qdr->qdf0[i + nr].ckey = PAGE_DEFAULT_KEY >> 4;
+ irq_ptr->qdr->qdf0[i + nr].dkey = PAGE_DEFAULT_KEY >> 4;
}
static void setup_qdr(struct qdio_irq *irq_ptr,
@@ -350,7 +350,7 @@ static void setup_qdr(struct qdio_irq *irq_ptr,
irq_ptr->qdr->iqdsz = sizeof(struct qdesfmt0) / 4; /* size in words */
irq_ptr->qdr->oqdsz = sizeof(struct qdesfmt0) / 4;
irq_ptr->qdr->qiba = (unsigned long)&irq_ptr->qib;
- irq_ptr->qdr->qkey = PAGE_DEFAULT_KEY;
+ irq_ptr->qdr->qkey = PAGE_DEFAULT_KEY >> 4;
for (i = 0; i < qdio_init->no_input_qs; i++)
__qdio_allocate_fill_qdr(irq_ptr, irq_ptr->input_qs, i, 0);
@@ -382,7 +382,15 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
struct qdio_irq *irq_ptr = init_data->cdev->private->qdio_data;
int rc;
- memset(irq_ptr, 0, ((char *)&irq_ptr->qdr) - ((char *)irq_ptr));
+ memset(&irq_ptr->qib, 0, sizeof(irq_ptr->qib));
+ memset(&irq_ptr->siga_flag, 0, sizeof(irq_ptr->siga_flag));
+ memset(&irq_ptr->ccw, 0, sizeof(irq_ptr->ccw));
+ memset(&irq_ptr->ssqd_desc, 0, sizeof(irq_ptr->ssqd_desc));
+ memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
+
+ irq_ptr->debugfs_dev = irq_ptr->debugfs_perf = NULL;
+ irq_ptr->sch_token = irq_ptr->state = irq_ptr->perf_stat_enabled = 0;
+
/* wipes qib.ac, required by ar7063 */
memset(irq_ptr->qdr, 0, sizeof(struct qdr));
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 091d904d318..9942c1031b2 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -198,8 +198,8 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
.code = 0x0021,
};
scssc_area->operation_code = 0;
- scssc_area->ks = PAGE_DEFAULT_KEY;
- scssc_area->kc = PAGE_DEFAULT_KEY;
+ scssc_area->ks = PAGE_DEFAULT_KEY >> 4;
+ scssc_area->kc = PAGE_DEFAULT_KEY >> 4;
scssc_area->isc = QDIO_AIRQ_ISC;
scssc_area->schid = irq_ptr->schid;
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index c68be24e27d..ba50fe02e57 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -33,6 +33,7 @@
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/compat.h>
#include <linux/smp_lock.h>
#include <asm/atomic.h>
@@ -912,126 +913,105 @@ static struct miscdevice zcrypt_misc_device = {
*/
static struct proc_dir_entry *zcrypt_entry;
-static int sprintcl(unsigned char *outaddr, unsigned char *addr,
- unsigned int len)
+static void sprintcl(struct seq_file *m, unsigned char *addr, unsigned int len)
{
- int hl, i;
+ int i;
- hl = 0;
for (i = 0; i < len; i++)
- hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]);
- hl += sprintf(outaddr+hl, " ");
- return hl;
+ seq_printf(m, "%01x", (unsigned int) addr[i]);
+ seq_putc(m, ' ');
}
-static int sprintrw(unsigned char *outaddr, unsigned char *addr,
- unsigned int len)
+static void sprintrw(struct seq_file *m, unsigned char *addr, unsigned int len)
{
- int hl, inl, c, cx;
+ int inl, c, cx;
- hl = sprintf(outaddr, " ");
+ seq_printf(m, " ");
inl = 0;
for (c = 0; c < (len / 16); c++) {
- hl += sprintcl(outaddr+hl, addr+inl, 16);
+ sprintcl(m, addr+inl, 16);
inl += 16;
}
cx = len%16;
if (cx) {
- hl += sprintcl(outaddr+hl, addr+inl, cx);
+ sprintcl(m, addr+inl, cx);
inl += cx;
}
- hl += sprintf(outaddr+hl, "\n");
- return hl;
+ seq_putc(m, '\n');
}
-static int sprinthx(unsigned char *title, unsigned char *outaddr,
- unsigned char *addr, unsigned int len)
+static void sprinthx(unsigned char *title, struct seq_file *m,
+ unsigned char *addr, unsigned int len)
{
- int hl, inl, r, rx;
+ int inl, r, rx;
- hl = sprintf(outaddr, "\n%s\n", title);
+ seq_printf(m, "\n%s\n", title);
inl = 0;
for (r = 0; r < (len / 64); r++) {
- hl += sprintrw(outaddr+hl, addr+inl, 64);
+ sprintrw(m, addr+inl, 64);
inl += 64;
}
rx = len % 64;
if (rx) {
- hl += sprintrw(outaddr+hl, addr+inl, rx);
+ sprintrw(m, addr+inl, rx);
inl += rx;
}
- hl += sprintf(outaddr+hl, "\n");
- return hl;
+ seq_putc(m, '\n');
}
-static int sprinthx4(unsigned char *title, unsigned char *outaddr,
- unsigned int *array, unsigned int len)
+static void sprinthx4(unsigned char *title, struct seq_file *m,
+ unsigned int *array, unsigned int len)
{
- int hl, r;
+ int r;
- hl = sprintf(outaddr, "\n%s\n", title);
+ seq_printf(m, "\n%s\n", title);
for (r = 0; r < len; r++) {
if ((r % 8) == 0)
- hl += sprintf(outaddr+hl, " ");
- hl += sprintf(outaddr+hl, "%08X ", array[r]);
+ seq_printf(m, " ");
+ seq_printf(m, "%08X ", array[r]);
if ((r % 8) == 7)
- hl += sprintf(outaddr+hl, "\n");
+ seq_putc(m, '\n');
}
- hl += sprintf(outaddr+hl, "\n");
- return hl;
+ seq_putc(m, '\n');
}
-static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
- int count, int *eof, void *data)
+static int zcrypt_proc_show(struct seq_file *m, void *v)
{
- unsigned char *workarea;
- int len;
-
- len = 0;
-
- /* resp_buff is a page. Use the right half for a work area */
- workarea = resp_buff + 2000;
- len += sprintf(resp_buff + len, "\nzcrypt version: %d.%d.%d\n",
- ZCRYPT_VERSION, ZCRYPT_RELEASE, ZCRYPT_VARIANT);
- len += sprintf(resp_buff + len, "Cryptographic domain: %d\n",
- ap_domain_index);
- len += sprintf(resp_buff + len, "Total device count: %d\n",
- zcrypt_device_count);
- len += sprintf(resp_buff + len, "PCICA count: %d\n",
- zcrypt_count_type(ZCRYPT_PCICA));
- len += sprintf(resp_buff + len, "PCICC count: %d\n",
- zcrypt_count_type(ZCRYPT_PCICC));
- len += sprintf(resp_buff + len, "PCIXCC MCL2 count: %d\n",
- zcrypt_count_type(ZCRYPT_PCIXCC_MCL2));
- len += sprintf(resp_buff + len, "PCIXCC MCL3 count: %d\n",
- zcrypt_count_type(ZCRYPT_PCIXCC_MCL3));
- len += sprintf(resp_buff + len, "CEX2C count: %d\n",
- 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",
- zcrypt_pendingq_count());
- len += sprintf(resp_buff + len, "Total open handles: %d\n\n",
- atomic_read(&zcrypt_open_count));
+ char workarea[sizeof(int) * AP_DEVICES];
+
+ seq_printf(m, "\nzcrypt version: %d.%d.%d\n",
+ ZCRYPT_VERSION, ZCRYPT_RELEASE, ZCRYPT_VARIANT);
+ seq_printf(m, "Cryptographic domain: %d\n", ap_domain_index);
+ seq_printf(m, "Total device count: %d\n", zcrypt_device_count);
+ seq_printf(m, "PCICA count: %d\n", zcrypt_count_type(ZCRYPT_PCICA));
+ seq_printf(m, "PCICC count: %d\n", zcrypt_count_type(ZCRYPT_PCICC));
+ seq_printf(m, "PCIXCC MCL2 count: %d\n",
+ zcrypt_count_type(ZCRYPT_PCIXCC_MCL2));
+ seq_printf(m, "PCIXCC MCL3 count: %d\n",
+ zcrypt_count_type(ZCRYPT_PCIXCC_MCL3));
+ seq_printf(m, "CEX2C count: %d\n", zcrypt_count_type(ZCRYPT_CEX2C));
+ seq_printf(m, "CEX2A count: %d\n", zcrypt_count_type(ZCRYPT_CEX2A));
+ seq_printf(m, "CEX3C count: %d\n", zcrypt_count_type(ZCRYPT_CEX3C));
+ seq_printf(m, "CEX3A count: %d\n", zcrypt_count_type(ZCRYPT_CEX3A));
+ seq_printf(m, "requestq count: %d\n", zcrypt_requestq_count());
+ seq_printf(m, "pendingq count: %d\n", zcrypt_pendingq_count());
+ seq_printf(m, "Total open handles: %d\n\n",
+ 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 7=CEX3C 8=CEX3A",
- resp_buff+len, workarea, AP_DEVICES);
+ sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
+ "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A",
+ m, workarea, AP_DEVICES);
zcrypt_qdepth_mask(workarea);
- len += sprinthx("Waiting work element counts",
- resp_buff+len, workarea, AP_DEVICES);
+ sprinthx("Waiting work element counts", m, workarea, AP_DEVICES);
zcrypt_perdev_reqcnt((int *) workarea);
- len += sprinthx4("Per-device successfully completed request counts",
- resp_buff+len,(unsigned int *) workarea, AP_DEVICES);
- *eof = 1;
- memset((void *) workarea, 0x00, AP_DEVICES * sizeof(unsigned int));
- return len;
+ sprinthx4("Per-device successfully completed request counts",
+ m, (unsigned int *) workarea, AP_DEVICES);
+ return 0;
+}
+
+static int zcrypt_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, zcrypt_proc_show, NULL);
}
static void zcrypt_disable_card(int index)
@@ -1061,11 +1041,11 @@ static void zcrypt_enable_card(int index)
spin_unlock_bh(&zcrypt_device_lock);
}
-static int zcrypt_status_write(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static ssize_t zcrypt_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos)
{
unsigned char *lbuf, *ptr;
- unsigned long local_count;
+ size_t local_count;
int j;
if (count <= 0)
@@ -1115,6 +1095,15 @@ out:
return count;
}
+static const struct file_operations zcrypt_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = zcrypt_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = zcrypt_proc_write,
+};
+
static int zcrypt_rng_device_count;
static u32 *zcrypt_rng_buffer;
static int zcrypt_rng_buffer_index;
@@ -1197,14 +1186,11 @@ int __init zcrypt_api_init(void)
goto out;
/* Set up the proc file system */
- zcrypt_entry = create_proc_entry("driver/z90crypt", 0644, NULL);
+ zcrypt_entry = proc_create("driver/z90crypt", 0644, NULL, &zcrypt_proc_fops);
if (!zcrypt_entry) {
rc = -ENOMEM;
goto out_misc;
}
- zcrypt_entry->data = NULL;
- zcrypt_entry->read_proc = zcrypt_status_read;
- zcrypt_entry->write_proc = zcrypt_status_write;
return 0;
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 2930fc763ac..b2fc4fd63f7 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -340,11 +340,11 @@ static void kvm_extint_handler(u16 code)
return;
/* The LSB might be overloaded, we have to mask it */
- vq = (struct virtqueue *) ((*(long *) __LC_PFAULT_INTPARM) & ~1UL);
+ vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL);
/* We use the LSB of extparam, to decide, if this interrupt is a config
* change or a "standard" interrupt */
- config_changed = (*(int *) __LC_EXT_PARAMS & 1);
+ config_changed = S390_lowcore.ext_params & 1;
if (config_changed) {
struct virtio_driver *drv;
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 9d0c941b7d3..66d6c01fcf3 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -3,7 +3,7 @@
*
* Module interface and handling of zfcp data structures.
*
- * Copyright IBM Corporation 2002, 2009
+ * Copyright IBM Corporation 2002, 2010
*/
/*
@@ -32,6 +32,7 @@
#include <linux/seq_file.h>
#include "zfcp_ext.h"
#include "zfcp_fc.h"
+#include "zfcp_reqlist.h"
#define ZFCP_BUS_ID_SIZE 20
@@ -49,36 +50,6 @@ static struct kmem_cache *zfcp_cache_hw_align(const char *name,
return kmem_cache_create(name, size, roundup_pow_of_two(size), 0, NULL);
}
-static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
-{
- int idx;
-
- adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head),
- GFP_KERNEL);
- if (!adapter->req_list)
- return -ENOMEM;
-
- for (idx = 0; idx < REQUEST_LIST_SIZE; idx++)
- INIT_LIST_HEAD(&adapter->req_list[idx]);
- return 0;
-}
-
-/**
- * zfcp_reqlist_isempty - is the request list empty
- * @adapter: pointer to struct zfcp_adapter
- *
- * Returns: true if list is empty, false otherwise
- */
-int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
-{
- unsigned int idx;
-
- for (idx = 0; idx < REQUEST_LIST_SIZE; idx++)
- if (!list_empty(&adapter->req_list[idx]))
- return 0;
- return 1;
-}
-
static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
{
struct ccw_device *cdev;
@@ -110,7 +81,7 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
flush_work(&unit->scsi_work);
out_unit:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
out_port:
zfcp_ccw_adapter_put(adapter);
out_ccw_device:
@@ -255,7 +226,7 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
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))
+ if (!get_device(&unit->dev))
unit = NULL;
read_unlock_irqrestore(&port->unit_list_lock, flags);
return unit;
@@ -280,7 +251,7 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
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))
+ if (!get_device(&port->dev))
port = NULL;
read_unlock_irqrestore(&adapter->port_list_lock, flags);
return port;
@@ -298,10 +269,9 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
*/
static void zfcp_unit_release(struct device *dev)
{
- struct zfcp_unit *unit = container_of(dev, struct zfcp_unit,
- sysfs_device);
+ struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
- put_device(&unit->port->sysfs_device);
+ put_device(&unit->port->dev);
kfree(unit);
}
@@ -318,11 +288,11 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
struct zfcp_unit *unit;
int retval = -ENOMEM;
- get_device(&port->sysfs_device);
+ get_device(&port->dev);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
if (unit) {
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
retval = -EEXIST;
goto err_out;
}
@@ -333,10 +303,10 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
unit->port = port;
unit->fcp_lun = fcp_lun;
- unit->sysfs_device.parent = &port->sysfs_device;
- unit->sysfs_device.release = zfcp_unit_release;
+ unit->dev.parent = &port->dev;
+ unit->dev.release = zfcp_unit_release;
- if (dev_set_name(&unit->sysfs_device, "0x%016llx",
+ if (dev_set_name(&unit->dev, "0x%016llx",
(unsigned long long) fcp_lun)) {
kfree(unit);
goto err_out;
@@ -353,13 +323,12 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
unit->latencies.cmd.channel.min = 0xFFFFFFFF;
unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
- if (device_register(&unit->sysfs_device)) {
- put_device(&unit->sysfs_device);
+ if (device_register(&unit->dev)) {
+ put_device(&unit->dev);
goto err_out;
}
- if (sysfs_create_group(&unit->sysfs_device.kobj,
- &zfcp_sysfs_unit_attrs))
+ if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs))
goto err_out_put;
write_lock_irq(&port->unit_list_lock);
@@ -371,9 +340,9 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
return unit;
err_out_put:
- device_unregister(&unit->sysfs_device);
+ device_unregister(&unit->dev);
err_out:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
return ERR_PTR(retval);
}
@@ -539,7 +508,8 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
if (zfcp_allocate_low_mem_buffers(adapter))
goto failed;
- if (zfcp_reqlist_alloc(adapter))
+ adapter->req_list = zfcp_reqlist_alloc();
+ if (!adapter->req_list)
goto failed;
if (zfcp_dbf_adapter_register(adapter))
@@ -560,8 +530,6 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
INIT_LIST_HEAD(&adapter->erp_ready_head);
INIT_LIST_HEAD(&adapter->erp_running_head);
- spin_lock_init(&adapter->req_list_lock);
-
rwlock_init(&adapter->erp_lock);
rwlock_init(&adapter->abort_lock);
@@ -640,8 +608,7 @@ void zfcp_device_unregister(struct device *dev,
static void zfcp_port_release(struct device *dev)
{
- struct zfcp_port *port = container_of(dev, struct zfcp_port,
- sysfs_device);
+ struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
zfcp_ccw_adapter_put(port->adapter);
kfree(port);
@@ -669,7 +636,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
port = zfcp_get_port_by_wwpn(adapter, wwpn);
if (port) {
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
retval = -EEXIST;
goto err_out;
}
@@ -689,22 +656,21 @@ 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;
- port->sysfs_device.parent = &adapter->ccw_device->dev;
- port->sysfs_device.release = zfcp_port_release;
+ port->dev.parent = &adapter->ccw_device->dev;
+ port->dev.release = zfcp_port_release;
- if (dev_set_name(&port->sysfs_device, "0x%016llx",
- (unsigned long long)wwpn)) {
+ if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) {
kfree(port);
goto err_out;
}
retval = -EINVAL;
- if (device_register(&port->sysfs_device)) {
- put_device(&port->sysfs_device);
+ if (device_register(&port->dev)) {
+ put_device(&port->dev);
goto err_out;
}
- if (sysfs_create_group(&port->sysfs_device.kobj,
+ if (sysfs_create_group(&port->dev.kobj,
&zfcp_sysfs_port_attrs))
goto err_out_put;
@@ -717,7 +683,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
return port;
err_out_put:
- device_unregister(&port->sysfs_device);
+ device_unregister(&port->dev);
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 c22cb72a5ae..ce1cc7a11fb 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -3,13 +3,14 @@
*
* Registration and callback for the s390 common I/O layer.
*
- * Copyright IBM Corporation 2002, 2009
+ * Copyright IBM Corporation 2002, 2010
*/
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include "zfcp_ext.h"
+#include "zfcp_reqlist.h"
#define ZFCP_MODEL_PRIV 0x4
@@ -122,12 +123,10 @@ static void zfcp_ccw_remove(struct ccw_device *cdev)
zfcp_ccw_adapter_put(adapter); /* put from zfcp_ccw_adapter_by_cdev */
list_for_each_entry_safe(unit, u, &unit_remove_lh, list)
- zfcp_device_unregister(&unit->sysfs_device,
- &zfcp_sysfs_unit_attrs);
+ zfcp_device_unregister(&unit->dev, &zfcp_sysfs_unit_attrs);
list_for_each_entry_safe(port, p, &port_remove_lh, list)
- zfcp_device_unregister(&port->sysfs_device,
- &zfcp_sysfs_port_attrs);
+ zfcp_device_unregister(&port->dev, &zfcp_sysfs_port_attrs);
zfcp_adapter_unregister(adapter);
}
@@ -162,7 +161,7 @@ static int zfcp_ccw_set_online(struct ccw_device *cdev)
}
/* initialize request counter */
- BUG_ON(!zfcp_reqlist_isempty(adapter));
+ BUG_ON(!zfcp_reqlist_isempty(adapter->req_list));
adapter->req_no = 0;
zfcp_erp_modify_adapter_status(adapter, "ccsonl1", NULL,
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 7369c8911bc..7a149fd85f6 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -140,9 +140,9 @@ void _zfcp_dbf_hba_fsf_response(const char *tag2, int level,
memcpy(response->fsf_status_qual,
fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
response->fsf_req_status = fsf_req->status;
- response->sbal_first = fsf_req->queue_req.sbal_first;
- response->sbal_last = fsf_req->queue_req.sbal_last;
- response->sbal_response = fsf_req->queue_req.sbal_response;
+ response->sbal_first = fsf_req->qdio_req.sbal_first;
+ response->sbal_last = fsf_req->qdio_req.sbal_last;
+ response->sbal_response = fsf_req->qdio_req.sbal_response;
response->pool = fsf_req->pool != NULL;
response->erp_action = (unsigned long)fsf_req->erp_action;
@@ -576,7 +576,8 @@ void zfcp_dbf_rec_adapter(char *id, void *ref, struct zfcp_dbf *dbf)
struct zfcp_adapter *adapter = dbf->adapter;
zfcp_dbf_rec_target(id, ref, dbf, &adapter->status,
- &adapter->erp_counter, 0, 0, 0);
+ &adapter->erp_counter, 0, 0,
+ ZFCP_DBF_INVALID_LUN);
}
/**
@@ -590,8 +591,8 @@ void zfcp_dbf_rec_port(char *id, void *ref, struct zfcp_port *port)
struct zfcp_dbf *dbf = port->adapter->dbf;
zfcp_dbf_rec_target(id, ref, dbf, &port->status,
- &port->erp_counter, port->wwpn, port->d_id,
- 0);
+ &port->erp_counter, port->wwpn, port->d_id,
+ ZFCP_DBF_INVALID_LUN);
}
/**
@@ -642,10 +643,9 @@ void zfcp_dbf_rec_trigger(char *id2, void *ref, u8 want, u8 need, void *action,
r->u.trigger.ps = atomic_read(&port->status);
r->u.trigger.wwpn = port->wwpn;
}
- if (unit) {
+ if (unit)
r->u.trigger.us = atomic_read(&unit->status);
- r->u.trigger.fcp_lun = unit->fcp_lun;
- }
+ r->u.trigger.fcp_lun = unit ? unit->fcp_lun : ZFCP_DBF_INVALID_LUN;
debug_event(dbf->rec, action ? 1 : 4, r, sizeof(*r));
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
@@ -668,7 +668,7 @@ void zfcp_dbf_rec_action(char *id2, struct zfcp_erp_action *erp_action)
r->u.action.action = (unsigned long)erp_action;
r->u.action.status = erp_action->status;
r->u.action.step = erp_action->step;
- r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
+ r->u.action.fsf_req = erp_action->fsf_req_id;
debug_event(dbf->rec, 5, r, sizeof(*r));
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 8b7fd9a1033..457e046f2d2 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -30,6 +30,8 @@
#define ZFCP_DBF_TAG_SIZE 4
#define ZFCP_DBF_ID_SIZE 7
+#define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull
+
struct zfcp_dbf_dump {
u8 tag[ZFCP_DBF_TAG_SIZE];
u32 total_size; /* size of total dump data */
@@ -192,10 +194,10 @@ struct zfcp_dbf_san_record {
struct zfcp_dbf_san_record_ct_response ct_resp;
struct zfcp_dbf_san_record_els els;
} u;
-#define ZFCP_DBF_SAN_MAX_PAYLOAD 1024
- u8 payload[32];
} __attribute__ ((packed));
+#define ZFCP_DBF_SAN_MAX_PAYLOAD 1024
+
struct zfcp_dbf_scsi_record {
u8 tag[ZFCP_DBF_TAG_SIZE];
u8 tag2[ZFCP_DBF_TAG_SIZE];
@@ -301,17 +303,31 @@ void zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
/**
* zfcp_dbf_scsi_result - trace event for SCSI command completion
- * @tag: tag indicating success or failure of SCSI command
- * @level: trace level applicable for this event
- * @adapter: adapter that has been used to issue the SCSI command
+ * @dbf: adapter dbf trace
+ * @scmd: SCSI command pointer
+ * @req: FSF request used to issue SCSI command
+ */
+static inline
+void zfcp_dbf_scsi_result(struct zfcp_dbf *dbf, struct scsi_cmnd *scmd,
+ struct zfcp_fsf_req *req)
+{
+ if (scmd->result != 0)
+ zfcp_dbf_scsi("rslt", "erro", 3, dbf, scmd, req, 0);
+ else if (scmd->retries > 0)
+ zfcp_dbf_scsi("rslt", "retr", 4, dbf, scmd, req, 0);
+ else
+ zfcp_dbf_scsi("rslt", "norm", 6, dbf, scmd, req, 0);
+}
+
+/**
+ * zfcp_dbf_scsi_fail_send - trace event for failure to send SCSI command
+ * @dbf: adapter dbf trace
* @scmd: SCSI command pointer
- * @fsf_req: request used to issue SCSI command (might be NULL)
*/
static inline
-void zfcp_dbf_scsi_result(const char *tag, int level, struct zfcp_dbf *dbf,
- struct scsi_cmnd *scmd, struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_scsi_fail_send(struct zfcp_dbf *dbf, struct scsi_cmnd *scmd)
{
- zfcp_dbf_scsi("rslt", tag, level, dbf, scmd, fsf_req, 0);
+ zfcp_dbf_scsi("rslt", "fail", 4, dbf, scmd, NULL, 0);
}
/**
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index e1b5b88e2dd..7131c7db1f0 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -3,7 +3,7 @@
*
* Global definitions for the zfcp device driver.
*
- * Copyright IBM Corporation 2002, 2009
+ * Copyright IBM Corporation 2002, 2010
*/
#ifndef ZFCP_DEF_H
@@ -33,15 +33,13 @@
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_bsg_fc.h>
#include <asm/ccwdev.h>
-#include <asm/qdio.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
#include <asm/sysinfo.h>
#include "zfcp_fsf.h"
+#include "zfcp_qdio.h"
-/********************* GENERAL DEFINES *********************************/
-
-#define REQUEST_LIST_SIZE 128
+struct zfcp_reqlist;
/********************* SCSI SPECIFIC DEFINES *********************************/
#define ZFCP_SCSI_ER_TIMEOUT (10*HZ)
@@ -129,12 +127,6 @@ struct zfcp_adapter_mempool {
mempool_t *qtcb_pool;
};
-struct zfcp_qdio_queue {
- struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
- u8 first; /* index of next free bfr in queue */
- atomic_t count; /* number of free buffers in queue */
-};
-
struct zfcp_erp_action {
struct list_head list;
int action; /* requested action code */
@@ -143,8 +135,7 @@ struct zfcp_erp_action {
struct zfcp_unit *unit;
u32 status; /* recovery status */
u32 step; /* active step of this erp action */
- struct zfcp_fsf_req *fsf_req; /* fsf request currently pending
- for this action */
+ unsigned long fsf_req_id;
struct timer_list timer;
};
@@ -167,29 +158,6 @@ struct zfcp_latencies {
spinlock_t lock;
};
-/** struct zfcp_qdio - basic QDIO data structure
- * @resp_q: response queue
- * @req_q: request queue
- * @stat_lock: lock to protect req_q_util and req_q_time
- * @req_q_lock; lock to serialize access to request queue
- * @req_q_time: time of last fill level change
- * @req_q_util: used for accounting
- * @req_q_full: queue full incidents
- * @req_q_wq: used to wait for SBAL availability
- * @adapter: adapter used in conjunction with this QDIO structure
- */
-struct zfcp_qdio {
- struct zfcp_qdio_queue resp_q;
- struct zfcp_qdio_queue req_q;
- spinlock_t stat_lock;
- spinlock_t req_q_lock;
- unsigned long long req_q_time;
- u64 req_q_util;
- atomic_t req_q_full;
- wait_queue_head_t req_q_wq;
- struct zfcp_adapter *adapter;
-};
-
struct zfcp_adapter {
struct kref ref;
u64 peer_wwnn; /* P2P peer WWNN */
@@ -207,8 +175,7 @@ struct zfcp_adapter {
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 */
+ struct zfcp_reqlist *req_list;
u32 fsf_req_seq_no; /* FSF cmnd seq number */
rwlock_t abort_lock; /* Protects against SCSI
stack abort/command
@@ -241,7 +208,7 @@ struct zfcp_adapter {
};
struct zfcp_port {
- struct device sysfs_device; /* sysfs device */
+ struct device dev;
struct fc_rport *rport; /* rport of fc transport class */
struct list_head list; /* list of remote ports */
struct zfcp_adapter *adapter; /* adapter used to access port */
@@ -263,7 +230,7 @@ struct zfcp_port {
};
struct zfcp_unit {
- struct device sysfs_device; /* sysfs device */
+ struct device dev;
struct list_head list; /* list of logical units */
struct zfcp_port *port; /* remote port of unit */
atomic_t status; /* status of this logical unit */
@@ -277,33 +244,11 @@ struct zfcp_unit {
};
/**
- * struct zfcp_queue_req - queue related values for a request
- * @sbal_number: number of free SBALs
- * @sbal_first: first SBAL for this request
- * @sbal_last: last SBAL for this request
- * @sbal_limit: last possible SBAL for this request
- * @sbale_curr: current SBALE at creation of this request
- * @sbal_response: SBAL used in interrupt
- * @qdio_outb_usage: usage of outbound queue
- * @qdio_inb_usage: usage of inbound queue
- */
-struct zfcp_queue_req {
- u8 sbal_number;
- u8 sbal_first;
- u8 sbal_last;
- u8 sbal_limit;
- u8 sbale_curr;
- u8 sbal_response;
- u16 qdio_outb_usage;
- u16 qdio_inb_usage;
-};
-
-/**
* struct zfcp_fsf_req - basic FSF request structure
* @list: list of FSF requests
* @req_id: unique request ID
* @adapter: adapter this request belongs to
- * @queue_req: queue related values
+ * @qdio_req: qdio queue related values
* @completion: used to signal the completion of the request
* @status: status of the request
* @fsf_command: FSF command issued
@@ -321,7 +266,7 @@ struct zfcp_fsf_req {
struct list_head list;
unsigned long req_id;
struct zfcp_adapter *adapter;
- struct zfcp_queue_req queue_req;
+ struct zfcp_qdio_req qdio_req;
struct completion completion;
u32 status;
u32 fsf_command;
@@ -352,45 +297,4 @@ struct zfcp_data {
#define ZFCP_SET 0x00000100
#define ZFCP_CLEAR 0x00000200
-/*
- * Helper functions for request ID management.
- */
-static inline int zfcp_reqlist_hash(unsigned long req_id)
-{
- return req_id % REQUEST_LIST_SIZE;
-}
-
-static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter,
- struct zfcp_fsf_req *fsf_req)
-{
- list_del(&fsf_req->list);
-}
-
-static inline struct zfcp_fsf_req *
-zfcp_reqlist_find(struct zfcp_adapter *adapter, unsigned long req_id)
-{
- struct zfcp_fsf_req *request;
- unsigned int idx;
-
- idx = zfcp_reqlist_hash(req_id);
- list_for_each_entry(request, &adapter->req_list[idx], list)
- if (request->req_id == req_id)
- return request;
- return NULL;
-}
-
-static inline struct zfcp_fsf_req *
-zfcp_reqlist_find_safe(struct zfcp_adapter *adapter, struct zfcp_fsf_req *req)
-{
- struct zfcp_fsf_req *request;
- unsigned int idx;
-
- for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) {
- list_for_each_entry(request, &adapter->req_list[idx], list)
- if (request == req)
- return request;
- }
- return NULL;
-}
-
#endif /* ZFCP_DEF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index b51a11a82e6..0be5e7ea282 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corporation 2002, 2009
+ * Copyright IBM Corporation 2002, 2010
*/
#define KMSG_COMPONENT "zfcp"
@@ -11,6 +11,7 @@
#include <linux/kthread.h>
#include "zfcp_ext.h"
+#include "zfcp_reqlist.h"
#define ZFCP_MAX_ERPS 3
@@ -174,7 +175,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
switch (need) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- if (!get_device(&unit->sysfs_device))
+ if (!get_device(&unit->dev))
return NULL;
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
erp_action = &unit->erp_action;
@@ -184,7 +185,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
case ZFCP_ERP_ACTION_REOPEN_PORT:
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- if (!get_device(&port->sysfs_device))
+ if (!get_device(&port->dev))
return NULL;
zfcp_erp_action_dismiss_port(port);
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
@@ -478,26 +479,27 @@ static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
{
struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_fsf_req *req;
- if (!act->fsf_req)
+ if (!act->fsf_req_id)
return;
- spin_lock(&adapter->req_list_lock);
- if (zfcp_reqlist_find_safe(adapter, act->fsf_req) &&
- act->fsf_req->erp_action == act) {
+ spin_lock(&adapter->req_list->lock);
+ req = _zfcp_reqlist_find(adapter->req_list, act->fsf_req_id);
+ if (req && req->erp_action == act) {
if (act->status & (ZFCP_STATUS_ERP_DISMISSED |
ZFCP_STATUS_ERP_TIMEDOUT)) {
- act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+ req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
zfcp_dbf_rec_action("erscf_1", act);
- act->fsf_req->erp_action = NULL;
+ req->erp_action = NULL;
}
if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
zfcp_dbf_rec_action("erscf_2", act);
- if (act->fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED)
- act->fsf_req = NULL;
+ if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED)
+ act->fsf_req_id = 0;
} else
- act->fsf_req = NULL;
- spin_unlock(&adapter->req_list_lock);
+ act->fsf_req_id = 0;
+ spin_unlock(&adapter->req_list->lock);
}
/**
@@ -1179,19 +1181,19 @@ 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) {
- get_device(&unit->sysfs_device);
+ get_device(&unit->dev);
if (scsi_queue_work(unit->port->adapter->scsi_host,
&unit->scsi_work) <= 0)
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
}
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
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);
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 66bdb34143c..8786a79c7f8 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -21,7 +21,6 @@ extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32,
u32);
extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, u64);
-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 *,
@@ -144,13 +143,9 @@ extern void zfcp_fsf_reqid_check(struct zfcp_qdio *, int);
/* zfcp_qdio.c */
extern int zfcp_qdio_setup(struct zfcp_adapter *);
extern void zfcp_qdio_destroy(struct zfcp_qdio *);
-extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_queue_req *);
-extern struct qdio_buffer_element
- *zfcp_qdio_sbale_req(struct zfcp_qdio *, struct zfcp_queue_req *);
-extern struct qdio_buffer_element
- *zfcp_qdio_sbale_curr(struct zfcp_qdio *, struct zfcp_queue_req *);
+extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *);
extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *,
- struct zfcp_queue_req *, unsigned long,
+ struct zfcp_qdio_req *, unsigned long,
struct scatterlist *, int);
extern int zfcp_qdio_open(struct zfcp_qdio *);
extern void zfcp_qdio_close(struct zfcp_qdio *);
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 271399f62f1..5219670f0c9 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -3,7 +3,7 @@
*
* Fibre Channel related functions for the zfcp device driver.
*
- * Copyright IBM Corporation 2008, 2009
+ * Copyright IBM Corporation 2008, 2010
*/
#define KMSG_COMPONENT "zfcp"
@@ -316,7 +316,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work)
zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL);
out:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
/**
@@ -325,9 +325,9 @@ out:
*/
void zfcp_fc_trigger_did_lookup(struct zfcp_port *port)
{
- get_device(&port->sysfs_device);
+ get_device(&port->dev);
if (!queue_work(port->adapter->work_queue, &port->gid_pn_work))
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
/**
@@ -389,7 +389,7 @@ static void zfcp_fc_adisc_handler(void *data)
zfcp_scsi_schedule_rport_register(port);
out:
atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
kmem_cache_free(zfcp_data.adisc_cache, adisc);
}
@@ -436,7 +436,7 @@ void zfcp_fc_link_test_work(struct work_struct *work)
container_of(work, struct zfcp_port, test_link_work);
int retval;
- get_device(&port->sysfs_device);
+ get_device(&port->dev);
port->rport_task = RPORT_DEL;
zfcp_scsi_rport_work(&port->rport_work);
@@ -455,7 +455,7 @@ void zfcp_fc_link_test_work(struct work_struct *work)
zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
out:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
/**
@@ -468,9 +468,9 @@ out:
*/
void zfcp_fc_test_link(struct zfcp_port *port)
{
- get_device(&port->sysfs_device);
+ get_device(&port->dev);
if (!queue_work(port->adapter->work_queue, &port->test_link_work))
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
static void zfcp_free_sg_env(struct zfcp_fc_gpn_ft *gpn_ft, int buf_num)
@@ -617,8 +617,7 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
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);
+ zfcp_device_unregister(&port->dev, &zfcp_sysfs_port_attrs);
}
return ret;
@@ -731,7 +730,7 @@ static int zfcp_fc_exec_els_job(struct fc_bsg_job *job,
return -EINVAL;
d_id = port->d_id;
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
} else
d_id = ntoh24(job->request->rqst_data.h_els.port_id);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index e8fb4d9baa8..6538742b421 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -3,7 +3,7 @@
*
* Implementation of FSF commands.
*
- * Copyright IBM Corporation 2002, 2009
+ * Copyright IBM Corporation 2002, 2010
*/
#define KMSG_COMPONENT "zfcp"
@@ -14,6 +14,8 @@
#include "zfcp_ext.h"
#include "zfcp_fc.h"
#include "zfcp_dbf.h"
+#include "zfcp_qdio.h"
+#include "zfcp_reqlist.h"
static void zfcp_fsf_request_timeout_handler(unsigned long data)
{
@@ -393,7 +395,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
case FSF_PROT_LINK_DOWN:
zfcp_fsf_link_down_info_eval(req, "fspse_5",
&psq->link_down_info);
- /* FIXME: reopening adapter now? better wait for link up */
+ /* go through reopen to flush pending requests */
zfcp_erp_adapter_reopen(adapter, 0, "fspse_6", req);
break;
case FSF_PROT_REEST_QUEUE:
@@ -457,15 +459,10 @@ static void zfcp_fsf_req_complete(struct zfcp_fsf_req *req)
void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
{
struct zfcp_fsf_req *req, *tmp;
- unsigned long flags;
LIST_HEAD(remove_queue);
- unsigned int i;
BUG_ON(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP);
- spin_lock_irqsave(&adapter->req_list_lock, flags);
- for (i = 0; i < REQUEST_LIST_SIZE; i++)
- list_splice_init(&adapter->req_list[i], &remove_queue);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+ zfcp_reqlist_move(adapter->req_list, &remove_queue);
list_for_each_entry_safe(req, tmp, &remove_queue, list) {
list_del(&req->list);
@@ -495,8 +492,6 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
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;
@@ -619,6 +614,10 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
fc_host_supported_speeds(shost) = bottom->supported_speed;
+ memcpy(fc_host_supported_fc4s(shost), bottom->supported_fc4_types,
+ FC_FC4_LIST_SIZE);
+ memcpy(fc_host_active_fc4s(shost), bottom->active_fc4_types,
+ FC_FC4_LIST_SIZE);
}
static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
@@ -725,12 +724,12 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio,
req->adapter = adapter;
req->fsf_command = fsf_cmd;
req->req_id = adapter->req_no;
- req->queue_req.sbal_number = 1;
- req->queue_req.sbal_first = req_q->first;
- req->queue_req.sbal_last = req_q->first;
- req->queue_req.sbale_curr = 1;
+ req->qdio_req.sbal_number = 1;
+ req->qdio_req.sbal_first = req_q->first;
+ req->qdio_req.sbal_last = req_q->first;
+ req->qdio_req.sbale_curr = 1;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].addr = (void *) req->req_id;
sbale[0].flags |= SBAL_FLAGS0_COMMAND;
@@ -745,6 +744,7 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio,
return ERR_PTR(-ENOMEM);
}
+ req->seq_no = adapter->fsf_req_seq_no;
req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
req->qtcb->prefix.req_id = req->req_id;
req->qtcb->prefix.ulp_info = 26;
@@ -752,8 +752,6 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio,
req->qtcb->prefix.qtcb_version = FSF_QTCB_CURRENT_VERSION;
req->qtcb->header.req_handle = req->req_id;
req->qtcb->header.fsf_command = req->fsf_command;
- req->seq_no = adapter->fsf_req_seq_no;
- req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
sbale[1].addr = (void *) req->qtcb;
sbale[1].length = sizeof(struct fsf_qtcb);
}
@@ -770,25 +768,17 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
{
struct zfcp_adapter *adapter = req->adapter;
struct zfcp_qdio *qdio = adapter->qdio;
- unsigned long flags;
- int idx;
- int with_qtcb = (req->qtcb != NULL);
+ int with_qtcb = (req->qtcb != NULL);
+ int req_id = req->req_id;
- /* put allocated FSF request into hash table */
- spin_lock_irqsave(&adapter->req_list_lock, flags);
- idx = zfcp_reqlist_hash(req->req_id);
- list_add_tail(&req->list, &adapter->req_list[idx]);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+ zfcp_reqlist_add(adapter->req_list, req);
- req->queue_req.qdio_outb_usage = atomic_read(&qdio->req_q.count);
+ req->qdio_req.qdio_outb_usage = atomic_read(&qdio->req_q.count);
req->issued = get_clock();
- if (zfcp_qdio_send(qdio, &req->queue_req)) {
+ if (zfcp_qdio_send(qdio, &req->qdio_req)) {
del_timer(&req->timer);
- spin_lock_irqsave(&adapter->req_list_lock, flags);
/* lookup request again, list might have changed */
- if (zfcp_reqlist_find_safe(adapter, req))
- zfcp_reqlist_remove(adapter, req);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+ zfcp_reqlist_find_rm(adapter->req_list, req_id);
zfcp_erp_adapter_reopen(adapter, 0, "fsrs__1", req);
return -EIO;
}
@@ -826,9 +816,9 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
goto out;
}
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
- req->queue_req.sbale_curr = 2;
+ req->qdio_req.sbale_curr = 2;
sr_buf = mempool_alloc(adapter->pool.status_read_data, GFP_ATOMIC);
if (!sr_buf) {
@@ -837,7 +827,7 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
}
memset(sr_buf, 0, sizeof(*sr_buf));
req->data = sr_buf;
- sbale = zfcp_qdio_sbale_curr(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_curr(qdio, &req->qdio_req);
sbale->addr = (void *) sr_buf;
sbale->length = sizeof(*sr_buf);
@@ -934,7 +924,7 @@ struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
ZFCP_STATUS_COMMON_UNBLOCKED)))
goto out_error_free;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1029,7 +1019,7 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
{
struct zfcp_adapter *adapter = req->adapter;
struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(adapter->qdio,
- &req->queue_req);
+ &req->qdio_req);
u32 feat = adapter->adapter_features;
int bytes;
@@ -1047,15 +1037,15 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
return 0;
}
- bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->queue_req,
+ bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req,
SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
return -EIO;
req->qtcb->bottom.support.req_buf_length = bytes;
- req->queue_req.sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
+ req->qdio_req.sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
- bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->queue_req,
+ bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req,
SBAL_FLAGS0_TYPE_WRITE_READ,
sg_resp, max_sbals);
req->qtcb->bottom.support.resp_buf_length = bytes;
@@ -1251,7 +1241,7 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1262,13 +1252,13 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
FSF_FEATURE_UPDATE_ALERT;
req->erp_action = erp_action;
req->handler = zfcp_fsf_exchange_config_data_handler;
- erp_action->fsf_req = req;
+ erp_action->fsf_req_id = req->req_id;
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
+ erp_action->fsf_req_id = 0;
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -1293,7 +1283,7 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio,
goto out_unlock;
}
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_exchange_config_data_handler;
@@ -1349,19 +1339,19 @@ int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_exchange_port_data_handler;
req->erp_action = erp_action;
- erp_action->fsf_req = req;
+ erp_action->fsf_req_id = req->req_id;
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
+ erp_action->fsf_req_id = 0;
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -1398,7 +1388,7 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio,
if (data)
req->data = data;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1484,7 +1474,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
}
out:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
/**
@@ -1513,7 +1503,7 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1521,15 +1511,15 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
hton24(req->qtcb->bottom.support.d_id, port->d_id);
req->data = port;
req->erp_action = erp_action;
- erp_action->fsf_req = req;
- get_device(&port->sysfs_device);
+ erp_action->fsf_req_id = req->req_id;
+ get_device(&port->dev);
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
- put_device(&port->sysfs_device);
+ erp_action->fsf_req_id = 0;
+ put_device(&port->dev);
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -1583,7 +1573,7 @@ int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1591,13 +1581,13 @@ int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
req->data = erp_action->port;
req->erp_action = erp_action;
req->qtcb->header.port_handle = erp_action->port->handle;
- erp_action->fsf_req = req;
+ erp_action->fsf_req_id = req->req_id;
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
+ erp_action->fsf_req_id = 0;
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -1660,7 +1650,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1715,7 +1705,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1809,7 +1799,7 @@ int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1817,13 +1807,13 @@ int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
req->qtcb->header.port_handle = erp_action->port->handle;
req->erp_action = erp_action;
req->handler = zfcp_fsf_close_physical_port_handler;
- erp_action->fsf_req = req;
+ erp_action->fsf_req_id = req->req_id;
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
+ erp_action->fsf_req_id = 0;
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -1982,7 +1972,7 @@ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1991,7 +1981,7 @@ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
req->handler = zfcp_fsf_open_unit_handler;
req->data = erp_action->unit;
req->erp_action = erp_action;
- erp_action->fsf_req = req;
+ erp_action->fsf_req_id = req->req_id;
if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE))
req->qtcb->bottom.support.option = FSF_OPEN_LUN_SUPPRESS_BOXING;
@@ -2000,7 +1990,7 @@ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
+ erp_action->fsf_req_id = 0;
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -2068,7 +2058,7 @@ int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2077,13 +2067,13 @@ int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
req->handler = zfcp_fsf_close_unit_handler;
req->data = erp_action->unit;
req->erp_action = erp_action;
- erp_action->fsf_req = req;
+ erp_action->fsf_req_id = req->req_id;
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
- erp_action->fsf_req = NULL;
+ erp_action->fsf_req_id = 0;
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -2111,8 +2101,8 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi)
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;
+ blktrc.inb_usage = req->qdio_req.qdio_inb_usage;
+ blktrc.outb_usage = req->qdio_req.qdio_outb_usage;
if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) {
blktrc.flags |= ZFCP_BLK_LAT_VALID;
@@ -2169,12 +2159,7 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
zfcp_fsf_req_trace(req, scpnt);
skip_fsfstatus:
- if (scpnt->result != 0)
- zfcp_dbf_scsi_result("erro", 3, req->adapter->dbf, scpnt, req);
- else if (scpnt->retries > 0)
- zfcp_dbf_scsi_result("retr", 4, req->adapter->dbf, scpnt, req);
- else
- zfcp_dbf_scsi_result("norm", 6, req->adapter->dbf, scpnt, req);
+ zfcp_dbf_scsi_result(req->adapter->dbf, scpnt, req);
scpnt->host_scribble = NULL;
(scpnt->scsi_done) (scpnt);
@@ -2274,7 +2259,7 @@ skip_fsfstatus:
else {
zfcp_fsf_send_fcp_command_task_handler(req);
req->unit = NULL;
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
}
}
@@ -2312,7 +2297,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- get_device(&unit->sysfs_device);
+ get_device(&unit->dev);
req->unit = unit;
req->data = scsi_cmnd;
req->handler = zfcp_fsf_send_fcp_command_handler;
@@ -2346,11 +2331,11 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
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,
+ real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sbtype,
scsi_sglist(scsi_cmnd),
FSF_MAX_SBALS_PER_REQ);
if (unlikely(real_bytes < 0)) {
- if (req->queue_req.sbal_number >= FSF_MAX_SBALS_PER_REQ) {
+ if (req->qdio_req.sbal_number >= FSF_MAX_SBALS_PER_REQ) {
dev_err(&adapter->ccw_device->dev,
"Oversize data package, unit 0x%016Lx "
"on port 0x%016Lx closed\n",
@@ -2369,7 +2354,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
goto out;
failed_scsi_cmnd:
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
zfcp_fsf_req_free(req);
scsi_cmnd->host_scribble = NULL;
out:
@@ -2415,7 +2400,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
req->qtcb->bottom.io.service_class = FSF_CLASS_3;
req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2478,14 +2463,14 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
req->handler = zfcp_fsf_control_file_handler;
- sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req);
sbale[0].flags |= direction;
bottom = &req->qtcb->bottom.support;
bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
bottom->option = fsf_cfdc->option;
- bytes = zfcp_qdio_sbals_from_sg(qdio, &req->queue_req,
+ bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req,
direction, fsf_cfdc->sg,
FSF_MAX_SBALS_PER_REQ);
if (bytes != ZFCP_CFDC_MAX_SIZE) {
@@ -2516,15 +2501,14 @@ void zfcp_fsf_reqid_check(struct zfcp_qdio *qdio, int sbal_idx)
struct qdio_buffer *sbal = qdio->resp_q.sbal[sbal_idx];
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *fsf_req;
- unsigned long flags, req_id;
+ unsigned long req_id;
int idx;
for (idx = 0; idx < QDIO_MAX_ELEMENTS_PER_BUFFER; idx++) {
sbale = &sbal->element[idx];
req_id = (unsigned long) sbale->addr;
- spin_lock_irqsave(&adapter->req_list_lock, flags);
- fsf_req = zfcp_reqlist_find(adapter, req_id);
+ fsf_req = zfcp_reqlist_find_rm(adapter->req_list, req_id);
if (!fsf_req)
/*
@@ -2534,11 +2518,8 @@ void zfcp_fsf_reqid_check(struct zfcp_qdio *qdio, int sbal_idx)
panic("error: unknown req_id (%lx) on adapter %s.\n",
req_id, dev_name(&adapter->ccw_device->dev));
- list_del(&fsf_req->list);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
-
- fsf_req->queue_req.sbal_response = sbal_idx;
- fsf_req->queue_req.qdio_inb_usage =
+ fsf_req->qdio_req.sbal_response = sbal_idx;
+ fsf_req->qdio_req.qdio_inb_usage =
atomic_read(&qdio->resp_q.count);
zfcp_fsf_req_complete(fsf_req);
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 6c5228b627f..71b97ff77cf 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -10,6 +10,7 @@
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include "zfcp_ext.h"
+#include "zfcp_qdio.h"
#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
@@ -28,12 +29,6 @@ static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)
return 0;
}
-static struct qdio_buffer_element *
-zfcp_qdio_sbale(struct zfcp_qdio_queue *q, int sbal_idx, int sbale_idx)
-{
- return &q->sbal[sbal_idx]->element[sbale_idx];
-}
-
static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *id)
{
struct zfcp_adapter *adapter = qdio->adapter;
@@ -106,7 +101,7 @@ static void zfcp_qdio_resp_put_back(struct zfcp_qdio *qdio, int processed)
if (unlikely(retval)) {
atomic_set(&queue->count, count);
- /* FIXME: Recover this with an adapter reopen? */
+ zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdrpb_1", NULL);
} else {
queue->first += count;
queue->first %= QDIO_MAX_BUFFERS_PER_Q;
@@ -145,32 +140,8 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
zfcp_qdio_resp_put_back(qdio, count);
}
-/**
- * zfcp_qdio_sbale_req - return ptr to SBALE of req_q for a struct zfcp_fsf_req
- * @qdio: pointer to struct zfcp_qdio
- * @q_rec: pointer to struct zfcp_queue_rec
- * Returns: pointer to qdio_buffer_element (SBALE) structure
- */
-struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_qdio *qdio,
- struct zfcp_queue_req *q_req)
-{
- return zfcp_qdio_sbale(&qdio->req_q, q_req->sbal_last, 0);
-}
-
-/**
- * zfcp_qdio_sbale_curr - return curr SBALE on req_q for a struct zfcp_fsf_req
- * @fsf_req: pointer to struct fsf_req
- * Returns: pointer to qdio_buffer_element (SBALE) structure
- */
-struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_qdio *qdio,
- struct zfcp_queue_req *q_req)
-{
- return zfcp_qdio_sbale(&qdio->req_q, q_req->sbal_last,
- q_req->sbale_curr);
-}
-
static void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio,
- struct zfcp_queue_req *q_req, int max_sbals)
+ struct zfcp_qdio_req *q_req, int max_sbals)
{
int count = atomic_read(&qdio->req_q.count);
count = min(count, max_sbals);
@@ -179,7 +150,7 @@ static void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio,
}
static struct qdio_buffer_element *
-zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req,
+zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
unsigned long sbtype)
{
struct qdio_buffer_element *sbale;
@@ -214,7 +185,7 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req,
}
static struct qdio_buffer_element *
-zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req,
+zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
unsigned int sbtype)
{
if (q_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
@@ -224,7 +195,7 @@ zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req,
}
static void zfcp_qdio_undo_sbals(struct zfcp_qdio *qdio,
- struct zfcp_queue_req *q_req)
+ struct zfcp_qdio_req *q_req)
{
struct qdio_buffer **sbal = qdio->req_q.sbal;
int first = q_req->sbal_first;
@@ -235,7 +206,7 @@ static void zfcp_qdio_undo_sbals(struct zfcp_qdio *qdio,
}
static int zfcp_qdio_fill_sbals(struct zfcp_qdio *qdio,
- struct zfcp_queue_req *q_req,
+ struct zfcp_qdio_req *q_req,
unsigned int sbtype, void *start_addr,
unsigned int total_length)
{
@@ -271,8 +242,7 @@ static int zfcp_qdio_fill_sbals(struct zfcp_qdio *qdio,
* @max_sbals: upper bound for number of SBALs to be used
* Returns: number of bytes, or error (negativ)
*/
-int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio,
- struct zfcp_queue_req *q_req,
+int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
unsigned long sbtype, struct scatterlist *sg,
int max_sbals)
{
@@ -304,10 +274,10 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio,
/**
* zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO
* @qdio: pointer to struct zfcp_qdio
- * @q_req: pointer to struct zfcp_queue_req
+ * @q_req: pointer to struct zfcp_qdio_req
* Returns: 0 on success, error otherwise
*/
-int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req)
+int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
{
struct zfcp_qdio_queue *req_q = &qdio->req_q;
int first = q_req->sbal_first;
diff --git a/drivers/s390/scsi/zfcp_qdio.h b/drivers/s390/scsi/zfcp_qdio.h
new file mode 100644
index 00000000000..8cca54631e1
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_qdio.h
@@ -0,0 +1,109 @@
+/*
+ * zfcp device driver
+ *
+ * Header file for zfcp qdio interface
+ *
+ * Copyright IBM Corporation 2010
+ */
+
+#ifndef ZFCP_QDIO_H
+#define ZFCP_QDIO_H
+
+#include <asm/qdio.h>
+
+/**
+ * struct zfcp_qdio_queue - qdio queue buffer, zfcp index and free count
+ * @sbal: qdio buffers
+ * @first: index of next free buffer in queue
+ * @count: number of free buffers in queue
+ */
+struct zfcp_qdio_queue {
+ struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
+ u8 first;
+ atomic_t count;
+};
+
+/**
+ * struct zfcp_qdio - basic qdio data structure
+ * @resp_q: response queue
+ * @req_q: request queue
+ * @stat_lock: lock to protect req_q_util and req_q_time
+ * @req_q_lock: lock to serialize access to request queue
+ * @req_q_time: time of last fill level change
+ * @req_q_util: used for accounting
+ * @req_q_full: queue full incidents
+ * @req_q_wq: used to wait for SBAL availability
+ * @adapter: adapter used in conjunction with this qdio structure
+ */
+struct zfcp_qdio {
+ struct zfcp_qdio_queue resp_q;
+ struct zfcp_qdio_queue req_q;
+ spinlock_t stat_lock;
+ spinlock_t req_q_lock;
+ unsigned long long req_q_time;
+ u64 req_q_util;
+ atomic_t req_q_full;
+ wait_queue_head_t req_q_wq;
+ struct zfcp_adapter *adapter;
+};
+
+/**
+ * struct zfcp_qdio_req - qdio queue related values for a request
+ * @sbal_number: number of free sbals
+ * @sbal_first: first sbal for this request
+ * @sbal_last: last sbal for this request
+ * @sbal_limit: last possible sbal for this request
+ * @sbale_curr: current sbale at creation of this request
+ * @sbal_response: sbal used in interrupt
+ * @qdio_outb_usage: usage of outbound queue
+ * @qdio_inb_usage: usage of inbound queue
+ */
+struct zfcp_qdio_req {
+ u8 sbal_number;
+ u8 sbal_first;
+ u8 sbal_last;
+ u8 sbal_limit;
+ u8 sbale_curr;
+ u8 sbal_response;
+ u16 qdio_outb_usage;
+ u16 qdio_inb_usage;
+};
+
+/**
+ * zfcp_qdio_sbale - return pointer to sbale in qdio queue
+ * @q: queue where to find sbal
+ * @sbal_idx: sbal index in queue
+ * @sbale_idx: sbale index in sbal
+ */
+static inline struct qdio_buffer_element *
+zfcp_qdio_sbale(struct zfcp_qdio_queue *q, int sbal_idx, int sbale_idx)
+{
+ return &q->sbal[sbal_idx]->element[sbale_idx];
+}
+
+/**
+ * zfcp_qdio_sbale_req - return pointer to sbale on req_q for a request
+ * @qdio: pointer to struct zfcp_qdio
+ * @q_rec: pointer to struct zfcp_qdio_req
+ * Returns: pointer to qdio_buffer_element (sbale) structure
+ */
+static inline struct qdio_buffer_element *
+zfcp_qdio_sbale_req(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
+{
+ return zfcp_qdio_sbale(&qdio->req_q, q_req->sbal_last, 0);
+}
+
+/**
+ * zfcp_qdio_sbale_curr - return current sbale on req_q for a request
+ * @qdio: pointer to struct zfcp_qdio
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ * Returns: pointer to qdio_buffer_element (sbale) structure
+ */
+static inline struct qdio_buffer_element *
+zfcp_qdio_sbale_curr(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
+{
+ return zfcp_qdio_sbale(&qdio->req_q, q_req->sbal_last,
+ q_req->sbale_curr);
+}
+
+#endif /* ZFCP_QDIO_H */
diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h
new file mode 100644
index 00000000000..a72d1b730ab
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_reqlist.h
@@ -0,0 +1,183 @@
+/*
+ * zfcp device driver
+ *
+ * Data structure and helper functions for tracking pending FSF
+ * requests.
+ *
+ * Copyright IBM Corporation 2009
+ */
+
+#ifndef ZFCP_REQLIST_H
+#define ZFCP_REQLIST_H
+
+/* number of hash buckets */
+#define ZFCP_REQ_LIST_BUCKETS 128
+
+/**
+ * struct zfcp_reqlist - Container for request list (reqlist)
+ * @lock: Spinlock for protecting the hash list
+ * @list: Array of hashbuckets, each is a list of requests in this bucket
+ */
+struct zfcp_reqlist {
+ spinlock_t lock;
+ struct list_head buckets[ZFCP_REQ_LIST_BUCKETS];
+};
+
+static inline int zfcp_reqlist_hash(unsigned long req_id)
+{
+ return req_id % ZFCP_REQ_LIST_BUCKETS;
+}
+
+/**
+ * zfcp_reqlist_alloc - Allocate and initialize reqlist
+ *
+ * Returns pointer to allocated reqlist on success, or NULL on
+ * allocation failure.
+ */
+static inline struct zfcp_reqlist *zfcp_reqlist_alloc(void)
+{
+ unsigned int i;
+ struct zfcp_reqlist *rl;
+
+ rl = kzalloc(sizeof(struct zfcp_reqlist), GFP_KERNEL);
+ if (!rl)
+ return NULL;
+
+ spin_lock_init(&rl->lock);
+
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ INIT_LIST_HEAD(&rl->buckets[i]);
+
+ return rl;
+}
+
+/**
+ * zfcp_reqlist_isempty - Check whether the request list empty
+ * @rl: pointer to reqlist
+ *
+ * Returns: 1 if list is empty, 0 if not
+ */
+static inline int zfcp_reqlist_isempty(struct zfcp_reqlist *rl)
+{
+ unsigned int i;
+
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ if (!list_empty(&rl->buckets[i]))
+ return 0;
+ return 1;
+}
+
+/**
+ * zfcp_reqlist_free - Free allocated memory for reqlist
+ * @rl: The reqlist where to free memory
+ */
+static inline void zfcp_reqlist_free(struct zfcp_reqlist *rl)
+{
+ /* sanity check */
+ BUG_ON(!zfcp_reqlist_isempty(rl));
+
+ kfree(rl);
+}
+
+static inline struct zfcp_fsf_req *
+_zfcp_reqlist_find(struct zfcp_reqlist *rl, unsigned long req_id)
+{
+ struct zfcp_fsf_req *req;
+ unsigned int i;
+
+ i = zfcp_reqlist_hash(req_id);
+ list_for_each_entry(req, &rl->buckets[i], list)
+ if (req->req_id == req_id)
+ return req;
+ return NULL;
+}
+
+/**
+ * zfcp_reqlist_find - Lookup FSF request by its request id
+ * @rl: The reqlist where to lookup the FSF request
+ * @req_id: The request id to look for
+ *
+ * Returns a pointer to the FSF request with the specified request id
+ * or NULL if there is no known FSF request with this id.
+ */
+static inline struct zfcp_fsf_req *
+zfcp_reqlist_find(struct zfcp_reqlist *rl, unsigned long req_id)
+{
+ unsigned long flags;
+ struct zfcp_fsf_req *req;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ req = _zfcp_reqlist_find(rl, req_id);
+ spin_unlock_irqrestore(&rl->lock, flags);
+
+ return req;
+}
+
+/**
+ * zfcp_reqlist_find_rm - Lookup request by id and remove it from reqlist
+ * @rl: reqlist where to search and remove entry
+ * @req_id: The request id of the request to look for
+ *
+ * This functions tries to find the FSF request with the specified
+ * id and then removes it from the reqlist. The reqlist lock is held
+ * during both steps of the operation.
+ *
+ * Returns: Pointer to the FSF request if the request has been found,
+ * NULL if it has not been found.
+ */
+static inline struct zfcp_fsf_req *
+zfcp_reqlist_find_rm(struct zfcp_reqlist *rl, unsigned long req_id)
+{
+ unsigned long flags;
+ struct zfcp_fsf_req *req;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ req = _zfcp_reqlist_find(rl, req_id);
+ if (req)
+ list_del(&req->list);
+ spin_unlock_irqrestore(&rl->lock, flags);
+
+ return req;
+}
+
+/**
+ * zfcp_reqlist_add - Add entry to reqlist
+ * @rl: reqlist where to add the entry
+ * @req: The entry to add
+ *
+ * The request id always increases. As an optimization new requests
+ * are added here with list_add_tail at the end of the bucket lists
+ * while old requests are looked up starting at the beginning of the
+ * lists.
+ */
+static inline void zfcp_reqlist_add(struct zfcp_reqlist *rl,
+ struct zfcp_fsf_req *req)
+{
+ unsigned int i;
+ unsigned long flags;
+
+ i = zfcp_reqlist_hash(req->req_id);
+
+ spin_lock_irqsave(&rl->lock, flags);
+ list_add_tail(&req->list, &rl->buckets[i]);
+ spin_unlock_irqrestore(&rl->lock, flags);
+}
+
+/**
+ * zfcp_reqlist_move - Move all entries from reqlist to simple list
+ * @rl: The zfcp_reqlist where to remove all entries
+ * @list: The list where to move all entries
+ */
+static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl,
+ struct list_head *list)
+{
+ unsigned int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ list_splice_init(&rl->buckets[i], list);
+ spin_unlock_irqrestore(&rl->lock, flags);
+}
+
+#endif /* ZFCP_REQLIST_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 8e6fc68d6bd..c3c4178888a 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corporation 2002, 2009
+ * Copyright IBM Corporation 2002, 2010
*/
#define KMSG_COMPONENT "zfcp"
@@ -15,6 +15,7 @@
#include "zfcp_ext.h"
#include "zfcp_dbf.h"
#include "zfcp_fc.h"
+#include "zfcp_reqlist.h"
static unsigned int default_depth = 32;
module_param_named(queue_depth, default_depth, uint, 0600);
@@ -43,7 +44,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
{
struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
unit->device = NULL;
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
}
static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
@@ -59,10 +60,9 @@ static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
{
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+
set_host_byte(scpnt, result);
- if ((scpnt->device != NULL) && (scpnt->device->host != NULL))
- zfcp_dbf_scsi_result("fail", 4, adapter->dbf, scpnt, NULL);
- /* return directly */
+ zfcp_dbf_scsi_fail_send(adapter->dbf, scpnt);
scpnt->scsi_done(scpnt);
}
@@ -86,18 +86,10 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
unit = scpnt->device->hostdata;
- BUG_ON(!adapter || (adapter != unit->port->adapter));
- BUG_ON(!scpnt->scsi_done);
-
- if (unlikely(!unit)) {
- zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
- return 0;
- }
-
scsi_result = fc_remote_port_chkready(rport);
if (unlikely(scsi_result)) {
scpnt->result = scsi_result;
- zfcp_dbf_scsi_result("fail", 4, adapter->dbf, scpnt, NULL);
+ zfcp_dbf_scsi_fail_send(adapter->dbf, scpnt);
scpnt->scsi_done(scpnt);
return 0;
}
@@ -189,9 +181,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
/* avoid race condition between late normal completion and abort */
write_lock_irqsave(&adapter->abort_lock, flags);
- spin_lock(&adapter->req_list_lock);
- old_req = zfcp_reqlist_find(adapter, old_reqid);
- spin_unlock(&adapter->req_list_lock);
+ old_req = zfcp_reqlist_find(adapter->req_list, old_reqid);
if (!old_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
zfcp_dbf_scsi_abort("lte1", adapter->dbf, scpnt, NULL,
@@ -521,7 +511,7 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
if (port) {
zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
}
@@ -563,23 +553,23 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
{
- get_device(&port->sysfs_device);
+ get_device(&port->dev);
port->rport_task = RPORT_ADD;
if (!queue_work(port->adapter->work_queue, &port->rport_work))
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
void zfcp_scsi_schedule_rport_block(struct zfcp_port *port)
{
- get_device(&port->sysfs_device);
+ get_device(&port->dev);
port->rport_task = RPORT_DEL;
if (port->rport && queue_work(port->adapter->work_queue,
&port->rport_work))
return;
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter)
@@ -608,7 +598,7 @@ void zfcp_scsi_rport_work(struct work_struct *work)
}
}
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
}
@@ -626,7 +616,7 @@ void zfcp_scsi_scan(struct work_struct *work)
scsilun_to_int((struct scsi_lun *)
&unit->fcp_lun), 0);
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
}
struct fc_function_template zfcp_transport_functions = {
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index f539e006683..a43035d4bd7 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, 2009
+ * Copyright IBM Corporation 2008, 2010
*/
#define KMSG_COMPONENT "zfcp"
@@ -19,8 +19,7 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \
struct device_attribute *at,\
char *buf) \
{ \
- struct _feat_def *_feat = container_of(dev, struct _feat_def, \
- sysfs_device); \
+ struct _feat_def *_feat = container_of(dev, struct _feat_def, dev); \
\
return sprintf(buf, _format, _value); \
} \
@@ -87,8 +86,7 @@ static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- struct _feat_def *_feat = container_of(dev, struct _feat_def, \
- sysfs_device); \
+ struct _feat_def *_feat = container_of(dev, struct _feat_def, dev); \
\
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED) \
return sprintf(buf, "1\n"); \
@@ -99,12 +97,11 @@ 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 = container_of(dev, struct _feat_def, \
- sysfs_device); \
+ struct _feat_def *_feat = container_of(dev, struct _feat_def, dev); \
unsigned long val; \
int retval = 0; \
\
- if (!(_feat && get_device(&_feat->sysfs_device))) \
+ if (!(_feat && get_device(&_feat->dev))) \
return -EBUSY; \
\
if (strict_strtoul(buf, 0, &val) || val != 0) { \
@@ -118,7 +115,7 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
_reopen_id, NULL); \
zfcp_erp_wait(_adapter); \
out: \
- put_device(&_feat->sysfs_device); \
+ put_device(&_feat->dev); \
return retval ? retval : (ssize_t) count; \
} \
static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
@@ -224,10 +221,10 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
list_del(&port->list);
write_unlock_irq(&adapter->port_list_lock);
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
zfcp_erp_port_shutdown(port, 0, "syprs_1", NULL);
- zfcp_device_unregister(&port->sysfs_device, &zfcp_sysfs_port_attrs);
+ zfcp_device_unregister(&port->dev, &zfcp_sysfs_port_attrs);
out:
zfcp_ccw_adapter_put(adapter);
return retval ? retval : (ssize_t) count;
@@ -258,13 +255,12 @@ 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 = container_of(dev, struct zfcp_port,
- sysfs_device);
+ struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
struct zfcp_unit *unit;
u64 fcp_lun;
int retval = -EINVAL;
- if (!(port && get_device(&port->sysfs_device)))
+ if (!(port && get_device(&port->dev)))
return -EBUSY;
if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
@@ -280,7 +276,7 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
zfcp_erp_wait(unit->port->adapter);
flush_work(&unit->scsi_work);
out:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
@@ -289,13 +285,12 @@ 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 = container_of(dev, struct zfcp_port,
- sysfs_device);
+ struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
struct zfcp_unit *unit;
u64 fcp_lun;
int retval = -EINVAL;
- if (!(port && get_device(&port->sysfs_device)))
+ if (!(port && get_device(&port->dev)))
return -EBUSY;
if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
@@ -314,12 +309,12 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
list_del(&unit->list);
write_unlock_irq(&port->unit_list_lock);
- put_device(&unit->sysfs_device);
+ put_device(&unit->dev);
zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
- zfcp_device_unregister(&unit->sysfs_device, &zfcp_sysfs_unit_attrs);
+ zfcp_device_unregister(&unit->dev, &zfcp_sysfs_unit_attrs);
out:
- put_device(&port->sysfs_device);
+ put_device(&port->dev);
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/openprom.c b/drivers/sbus/char/openprom.c
index 75ac19b1192..fc2f676e984 100644
--- a/drivers/sbus/char/openprom.c
+++ b/drivers/sbus/char/openprom.c
@@ -233,7 +233,7 @@ static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp
ph = 0;
if (dp)
- ph = dp->node;
+ ph = dp->phandle;
data->current_node = dp;
*((int *) op->oprom_array) = ph;
@@ -256,7 +256,7 @@ static int oprompci2node(void __user *argp, struct device_node *dp, struct openp
dp = pci_device_to_OF_node(pdev);
data->current_node = dp;
- *((int *)op->oprom_array) = dp->node;
+ *((int *)op->oprom_array) = dp->phandle;
op->oprom_size = sizeof(int);
err = copyout(argp, op, bufsize + sizeof(int));
@@ -273,7 +273,7 @@ static int oprompath2node(void __user *argp, struct device_node *dp, struct open
dp = of_find_node_by_path(op->oprom_array);
if (dp)
- ph = dp->node;
+ ph = dp->phandle;
data->current_node = dp;
*((int *)op->oprom_array) = ph;
op->oprom_size = sizeof(int);
@@ -540,7 +540,7 @@ static int opiocgetnext(unsigned int cmd, void __user *argp)
}
}
if (dp)
- nd = dp->node;
+ nd = dp->phandle;
if (copy_to_user(argp, &nd, sizeof(phandle)))
return -EFAULT;
@@ -570,7 +570,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
case OPIOCGETOPTNODE:
BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
- if (copy_to_user(argp, &options_node->node, sizeof(phandle)))
+ if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
return -EFAULT;
return 0;
diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c
index b898d382b7b..e40cdfb7541 100644
--- a/drivers/scsi/FlashPoint.c
+++ b/drivers/scsi/FlashPoint.c
@@ -3924,7 +3924,7 @@ static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card)
{
struct sccb_mgr_tar_info *currTar_Info;
- if ((p_sccb->TargID > MAX_SCSI_TAR) || (p_sccb->Lun > MAX_LUN)) {
+ if ((p_sccb->TargID >= MAX_SCSI_TAR) || (p_sccb->Lun >= MAX_LUN)) {
return;
}
currTar_Info = &FPT_sccbMgrTbl[p_card][p_sccb->TargID];
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
index 47754260228..9e71ac61114 100644
--- a/drivers/scsi/arm/fas216.c
+++ b/drivers/scsi/arm/fas216.c
@@ -2516,7 +2516,7 @@ int fas216_eh_device_reset(struct scsi_cmnd *SCpnt)
if (info->scsi.phase == PHASE_IDLE)
fas216_kick(info);
- mod_timer(&info->eh_timer, 30 * HZ);
+ mod_timer(&info->eh_timer, jiffies + 30 * HZ);
spin_unlock_irqrestore(&info->host_lock, flags);
/*
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index a93a5040f08..136b49cea79 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -24,6 +24,10 @@
#define FW_VER_LEN 32
#define MCC_Q_LEN 128
#define MCC_CQ_LEN 256
+#define MAX_MCC_CMD 16
+/* BladeEngine Generation numbers */
+#define BE_GEN2 2
+#define BE_GEN3 3
struct be_dma_mem {
void *va;
@@ -57,6 +61,11 @@ static inline void *queue_head_node(struct be_queue_info *q)
return q->dma_mem.va + q->head * q->entry_size;
}
+static inline void *queue_get_wrb(struct be_queue_info *q, unsigned int wrb_num)
+{
+ return q->dma_mem.va + wrb_num * q->entry_size;
+}
+
static inline void *queue_tail_node(struct be_queue_info *q)
{
return q->dma_mem.va + q->tail * q->entry_size;
@@ -104,15 +113,19 @@ struct be_ctrl_info {
spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */
spinlock_t mcc_cq_lock;
- /* MCC Async callback */
- void (*async_cb) (void *adapter, bool link_up);
- void *adapter_ctxt;
+ wait_queue_head_t mcc_wait[MAX_MCC_CMD + 1];
+ unsigned int mcc_tag[MAX_MCC_CMD];
+ unsigned int mcc_numtag[MAX_MCC_CMD + 1];
+ unsigned short mcc_alloc_index;
+ unsigned short mcc_free_index;
+ unsigned int mcc_tag_available;
};
#include "be_cmds.h"
#define PAGE_SHIFT_4K 12
#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K)
+#define mcc_timeout 120000 /* 5s timeout */
/* Returns number of pages spanned by the data starting at the given addr */
#define PAGES_4K_SPANNED(_address, size) \
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index f008708f1b0..67098578fba 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@
#include "be_mgmt.h"
#include "be_main.h"
-static void be_mcc_notify(struct beiscsi_hba *phba)
+void be_mcc_notify(struct beiscsi_hba *phba)
{
struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
u32 val = 0;
@@ -29,6 +29,52 @@ static void be_mcc_notify(struct beiscsi_hba *phba)
iowrite32(val, phba->db_va + DB_MCCQ_OFFSET);
}
+unsigned int alloc_mcc_tag(struct beiscsi_hba *phba)
+{
+ unsigned int tag = 0;
+ unsigned int num = 0;
+
+mcc_tag_rdy:
+ if (phba->ctrl.mcc_tag_available) {
+ tag = phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index];
+ phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0;
+ phba->ctrl.mcc_numtag[tag] = 0;
+ } else {
+ udelay(100);
+ num++;
+ if (num < mcc_timeout)
+ goto mcc_tag_rdy;
+ }
+ if (tag) {
+ phba->ctrl.mcc_tag_available--;
+ if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1))
+ phba->ctrl.mcc_alloc_index = 0;
+ else
+ phba->ctrl.mcc_alloc_index++;
+ }
+ return tag;
+}
+
+void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag)
+{
+ spin_lock(&ctrl->mbox_lock);
+ tag = tag & 0x000000FF;
+ ctrl->mcc_tag[ctrl->mcc_free_index] = tag;
+ if (ctrl->mcc_free_index == (MAX_MCC_CMD - 1))
+ ctrl->mcc_free_index = 0;
+ else
+ ctrl->mcc_free_index++;
+ ctrl->mcc_tag_available++;
+ spin_unlock(&ctrl->mbox_lock);
+}
+
+bool is_link_state_evt(u32 trailer)
+{
+ return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
+ ASYNC_TRAILER_EVENT_CODE_MASK) ==
+ ASYNC_EVENT_CODE_LINK_STATE);
+}
+
static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
{
if (compl->flags != 0) {
@@ -64,12 +110,30 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
return 0;
}
-
-static inline bool is_link_state_evt(u32 trailer)
+int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
+ struct be_mcc_compl *compl)
{
- return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
- ASYNC_TRAILER_EVENT_CODE_MASK) ==
- ASYNC_EVENT_CODE_LINK_STATE);
+ u16 compl_status, extd_status;
+ unsigned short tag;
+
+ be_dws_le_to_cpu(compl, 4);
+
+ compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
+ CQE_STATUS_COMPL_MASK;
+ /* The ctrl.mcc_numtag[tag] is filled with
+ * [31] = valid, [30:24] = Rsvd, [23:16] = wrb, [15:8] = extd_status,
+ * [7:0] = compl_status
+ */
+ tag = (compl->tag0 & 0x000000FF);
+ extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
+ CQE_STATUS_EXTD_MASK;
+
+ ctrl->mcc_numtag[tag] = 0x80000000;
+ ctrl->mcc_numtag[tag] |= (compl->tag0 & 0x00FF0000);
+ ctrl->mcc_numtag[tag] |= (extd_status & 0x000000FF) << 8;
+ ctrl->mcc_numtag[tag] |= (compl_status & 0x000000FF);
+ wake_up_interruptible(&ctrl->mcc_wait[tag]);
+ return 0;
}
static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba)
@@ -89,7 +153,7 @@ 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,
+void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
struct be_async_event_link_state *evt)
{
switch (evt->port_link_status) {
@@ -97,13 +161,13 @@ static void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
SE_DEBUG(DBG_LVL_1, "Link Down on Physical Port %d \n",
evt->physical_port);
phba->state |= BE_ADAPTER_LINK_DOWN;
+ iscsi_host_for_each_session(phba->shost,
+ be2iscsi_fail_session);
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"
@@ -162,7 +226,6 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba)
/* 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);
@@ -372,9 +435,10 @@ struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba)
BUG_ON(atomic_read(&mccq->used) >= mccq->len);
wrb = queue_head_node(mccq);
+ memset(wrb, 0, sizeof(*wrb));
+ wrb->tag0 = (mccq->head & 0x000000FF) << 16;
queue_head_inc(mccq);
atomic_inc(&mccq->used);
- memset(wrb, 0, sizeof(*wrb));
return wrb;
}
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index 5de8acb924c..49fcc787ee8 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -425,14 +425,20 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
int be_poll_mcc(struct be_ctrl_info *ctrl);
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);
-
+unsigned int be_cmd_get_mac_addr(struct beiscsi_hba *phba);
+void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag);
/*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);
+void be_mcc_notify(struct beiscsi_hba *phba);
+unsigned int alloc_mcc_tag(struct beiscsi_hba *phba);
+void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
+ struct be_async_event_link_state *evt);
+int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
+ struct be_mcc_compl *compl);
int be_mbox_notify(struct be_ctrl_info *ctrl);
@@ -448,6 +454,8 @@ int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
struct be_queue_info *wrbq);
+bool is_link_state_evt(u32 trailer);
+
struct be_default_pdu_context {
u32 dw[4];
} __packed;
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index d587b0362f1..29a3aaf35f9 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -101,6 +101,7 @@ void beiscsi_session_destroy(struct iscsi_cls_session *cls_session)
struct iscsi_session *sess = cls_session->dd_data;
struct beiscsi_session *beiscsi_sess = sess->dd_data;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_session_destroy\n");
pci_pool_destroy(beiscsi_sess->bhs_pool);
iscsi_session_teardown(cls_session);
}
@@ -224,6 +225,7 @@ int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
int len = 0;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_param, param= %d\n", param);
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep) {
SE_DEBUG(DBG_LVL_1,
@@ -254,6 +256,7 @@ int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
struct iscsi_session *session = conn->session;
int ret;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_set_param, param= %d\n", param);
ret = iscsi_set_param(cls_conn, param, buf, buflen);
if (ret)
return ret;
@@ -271,8 +274,8 @@ int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
conn->max_recv_dlength = 65536;
break;
case ISCSI_PARAM_MAX_BURST:
- if (session->first_burst > 262144)
- session->first_burst = 262144;
+ if (session->max_burst > 262144)
+ session->max_burst = 262144;
break;
default:
return 0;
@@ -293,12 +296,41 @@ int beiscsi_get_host_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost);
+ struct be_cmd_resp_get_mac_addr *resp;
+ struct be_mcc_wrb *wrb;
+ unsigned int tag, wrb_num;
int len = 0;
+ unsigned short status, extd_status;
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_get_host_param, param= %d\n", param);
switch (param) {
case ISCSI_HOST_PARAM_HWADDRESS:
- be_cmd_get_mac_addr(phba, phba->mac_address);
- len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
+ tag = be_cmd_get_mac_addr(phba);
+ if (!tag) {
+ SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed \n");
+ return -1;
+ } else
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+
+ wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+ extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+ status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ if (status || extd_status) {
+ SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed"
+ " status = %d extd_status = %d \n",
+ status, extd_status);
+ free_mcc_tag(&phba->ctrl, tag);
+ return -1;
+ } else {
+ wrb = queue_get_wrb(mccq, wrb_num);
+ free_mcc_tag(&phba->ctrl, tag);
+ resp = embedded_payload(wrb);
+ memcpy(phba->mac_address, resp->mac_address, ETH_ALEN);
+ len = sysfs_format_mac(buf, phba->mac_address,
+ ETH_ALEN);
+ }
break;
default:
return iscsi_host_get_param(shost, param, buf);
@@ -378,6 +410,7 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_offload_params params;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_start\n");
memset(&params, 0, sizeof(struct beiscsi_offload_params));
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep)
@@ -422,8 +455,14 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ struct be_mcc_wrb *wrb;
+ struct tcp_connect_and_offload_out *ptcpcnct_out;
+ unsigned short status, extd_status;
+ unsigned int tag, wrb_num;
int ret = -1;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn\n");
beiscsi_ep->ep_cid = beiscsi_get_cid(phba);
if (beiscsi_ep->ep_cid == 0xFFFF) {
SE_DEBUG(DBG_LVL_1, "No free cid available\n");
@@ -431,15 +470,44 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
}
SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d ",
beiscsi_ep->ep_cid);
- phba->ep_array[beiscsi_ep->ep_cid] = ep;
- if (beiscsi_ep->ep_cid >
- (phba->fw_config.iscsi_cid_start + phba->params.cxns_per_ctrl)) {
+ phba->ep_array[beiscsi_ep->ep_cid -
+ phba->fw_config.iscsi_cid_start] = ep;
+ if (beiscsi_ep->ep_cid > (phba->fw_config.iscsi_cid_start +
+ phba->params.cxns_per_ctrl * 2)) {
SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
return ret;
}
beiscsi_ep->cid_vld = 0;
- return mgmt_open_connection(phba, dst_addr, beiscsi_ep);
+ tag = mgmt_open_connection(phba, dst_addr, beiscsi_ep);
+ if (!tag) {
+ SE_DEBUG(DBG_LVL_1,
+ "mgmt_invalidate_connection Failed for cid=%d \n",
+ beiscsi_ep->ep_cid);
+ } else {
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+ }
+ wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+ extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+ status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ if (status || extd_status) {
+ SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed"
+ " status = %d extd_status = %d \n",
+ status, extd_status);
+ free_mcc_tag(&phba->ctrl, tag);
+ return -1;
+ } else {
+ wrb = queue_get_wrb(mccq, wrb_num);
+ free_mcc_tag(&phba->ctrl, tag);
+
+ ptcpcnct_out = embedded_payload(wrb);
+ beiscsi_ep = ep->dd_data;
+ beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
+ beiscsi_ep->cid_vld = 1;
+ SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
+ }
+ return 0;
}
/**
@@ -459,14 +527,12 @@ static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
* beiscsi_free_ep - free endpoint
* @ep: pointer to iscsi endpoint structure
*/
-static void beiscsi_free_ep(struct iscsi_endpoint *ep)
+static void beiscsi_free_ep(struct beiscsi_endpoint *beiscsi_ep)
{
- struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
beiscsi_ep->phba = NULL;
- iscsi_destroy_endpoint(ep);
}
/**
@@ -495,9 +561,9 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
return ERR_PTR(ret);
}
- if (phba->state) {
+ if (phba->state != BE_ADAPTER_UP) {
ret = -EBUSY;
- SE_DEBUG(DBG_LVL_1, "The Adapet state is Not UP \n");
+ SE_DEBUG(DBG_LVL_1, "The Adapter state is Not UP \n");
return ERR_PTR(ret);
}
@@ -509,9 +575,9 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
beiscsi_ep = ep->dd_data;
beiscsi_ep->phba = phba;
-
+ beiscsi_ep->openiscsi_ep = ep;
if (beiscsi_open_conn(ep, NULL, dst_addr, non_blocking)) {
- SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
+ SE_DEBUG(DBG_LVL_1, "Failed in beiscsi_open_conn \n");
ret = -ENOMEM;
goto free_ep;
}
@@ -519,7 +585,7 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
return ep;
free_ep:
- beiscsi_free_ep(ep);
+ beiscsi_free_ep(beiscsi_ep);
return ERR_PTR(ret);
}
@@ -546,20 +612,22 @@ int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
* @ep: The iscsi endpoint
* @flag: The type of connection closure
*/
-static int beiscsi_close_conn(struct iscsi_endpoint *ep, int flag)
+static int beiscsi_close_conn(struct beiscsi_endpoint *beiscsi_ep, int flag)
{
int ret = 0;
- struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
+ unsigned int tag;
struct beiscsi_hba *phba = beiscsi_ep->phba;
- if (MGMT_STATUS_SUCCESS !=
- mgmt_upload_connection(phba, beiscsi_ep->ep_cid,
- CONNECTION_UPLOAD_GRACEFUL)) {
+ tag = mgmt_upload_connection(phba, beiscsi_ep->ep_cid, flag);
+ if (!tag) {
SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x",
beiscsi_ep->ep_cid);
ret = -1;
+ } else {
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+ free_mcc_tag(&phba->ctrl, tag);
}
-
return ret;
}
@@ -574,19 +642,17 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
struct beiscsi_conn *beiscsi_conn;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_hba *phba;
- int flag = 0;
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect\n");
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect for ep_cid = %d\n",
+ beiscsi_ep->ep_cid);
if (beiscsi_ep->conn) {
beiscsi_conn = beiscsi_ep->conn;
iscsi_suspend_queue(beiscsi_conn->conn);
- beiscsi_close_conn(ep, flag);
}
- beiscsi_free_ep(ep);
}
/**
@@ -619,23 +685,31 @@ void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
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);
- unsigned int status;
+ unsigned int tag;
unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop\n");
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep) {
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop , no beiscsi_ep\n");
return;
}
- status = mgmt_invalidate_connection(phba, beiscsi_ep,
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop ep_cid = %d\n",
+ beiscsi_ep->ep_cid);
+ tag = mgmt_invalidate_connection(phba, beiscsi_ep,
beiscsi_ep->ep_cid, 1,
savecfg_flag);
- if (status != MGMT_STATUS_SUCCESS) {
+ if (!tag) {
SE_DEBUG(DBG_LVL_1,
"mgmt_invalidate_connection Failed for cid=%d \n",
beiscsi_ep->ep_cid);
+ } else {
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+ free_mcc_tag(&phba->ctrl, tag);
}
+ beiscsi_close_conn(beiscsi_ep, CONNECTION_UPLOAD_GRACEFUL);
+ beiscsi_free_ep(beiscsi_ep);
+ iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep);
beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
iscsi_conn_stop(cls_conn, flag);
}
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index f92ffc5349f..1f512c28cbf 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 1a557fa7788..7c22616ab14 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -40,7 +40,6 @@
static unsigned int be_iopoll_budget = 10;
static unsigned int be_max_phys_size = 64;
static unsigned int enable_msix = 1;
-static unsigned int ring_mode;
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
@@ -62,10 +61,10 @@ static int beiscsi_slave_configure(struct scsi_device *sdev)
/*------------------- 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, 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) },
- { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID4) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
@@ -112,6 +111,7 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
memset(phba, 0, sizeof(*phba));
phba->shost = shost;
phba->pcidev = pci_dev_get(pcidev);
+ pci_set_drvdata(pcidev, phba);
if (iscsi_host_add(shost, &phba->pcidev->dev))
goto free_devices;
@@ -143,6 +143,7 @@ static int beiscsi_map_pci_bars(struct beiscsi_hba *phba,
struct pci_dev *pcidev)
{
u8 __iomem *addr;
+ int pcicfg_reg;
addr = ioremap_nocache(pci_resource_start(pcidev, 2),
pci_resource_len(pcidev, 2));
@@ -159,13 +160,19 @@ static int beiscsi_map_pci_bars(struct beiscsi_hba *phba,
phba->db_va = addr;
phba->db_pa.u.a64.address = pci_resource_start(pcidev, 4);
- addr = ioremap_nocache(pci_resource_start(pcidev, 1),
- pci_resource_len(pcidev, 1));
+ if (phba->generation == BE_GEN2)
+ pcicfg_reg = 1;
+ else
+ pcicfg_reg = 0;
+
+ addr = ioremap_nocache(pci_resource_start(pcidev, pcicfg_reg),
+ pci_resource_len(pcidev, pcicfg_reg));
+
if (addr == NULL)
goto pci_map_err;
phba->ctrl.pcicfg = addr;
phba->pci_va = addr;
- phba->pci_pa.u.a64.address = pci_resource_start(pcidev, 1);
+ phba->pci_pa.u.a64.address = pci_resource_start(pcidev, pcicfg_reg);
return 0;
pci_map_err:
@@ -230,29 +237,27 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
static void beiscsi_get_params(struct beiscsi_hba *phba)
{
- phba->params.ios_per_ctrl = BE2_IO_DEPTH;
- phba->params.cxns_per_ctrl = BE2_MAX_SESSIONS;
- phba->params.asyncpdus_per_ctrl = BE2_ASYNCPDUS;
- phba->params.icds_per_ctrl = BE2_MAX_ICDS / 2;
+ phba->params.ios_per_ctrl = (phba->fw_config.iscsi_icd_count
+ - (phba->fw_config.iscsi_cid_count
+ + BE2_TMFS
+ + BE2_NOPOUT_REQ));
+ phba->params.cxns_per_ctrl = phba->fw_config.iscsi_cid_count;
+ phba->params.asyncpdus_per_ctrl = phba->fw_config.iscsi_cid_count;;
+ phba->params.icds_per_ctrl = phba->fw_config.iscsi_icd_count;;
phba->params.num_sge_per_io = BE2_SGE;
phba->params.defpdu_hdr_sz = BE2_DEFPDU_HDR_SZ;
phba->params.defpdu_data_sz = BE2_DEFPDU_DATA_SZ;
phba->params.eq_timer = 64;
phba->params.num_eq_entries =
- (((BE2_CMDS_PER_CXN * 2 + BE2_LOGOUTS + BE2_TMFS + BE2_ASYNCPDUS) /
- 512) + 1) * 512;
+ (((BE2_CMDS_PER_CXN * 2 + phba->fw_config.iscsi_cid_count * 2
+ + BE2_TMFS) / 512) + 1) * 512;
phba->params.num_eq_entries = (phba->params.num_eq_entries < 1024)
? 1024 : phba->params.num_eq_entries;
SE_DEBUG(DBG_LVL_8, "phba->params.num_eq_entries=%d \n",
- phba->params.num_eq_entries);
+ phba->params.num_eq_entries);
phba->params.num_cq_entries =
- (((BE2_CMDS_PER_CXN * 2 + BE2_LOGOUTS + BE2_TMFS + BE2_ASYNCPDUS) /
- 512) + 1) * 512;
- SE_DEBUG(DBG_LVL_8,
- "phba->params.num_cq_entries=%d BE2_CMDS_PER_CXN=%d"
- "BE2_LOGOUTS=%d BE2_TMFS=%d BE2_ASYNCPDUS=%d \n",
- phba->params.num_cq_entries, BE2_CMDS_PER_CXN,
- BE2_LOGOUTS, BE2_TMFS, BE2_ASYNCPDUS);
+ (((BE2_CMDS_PER_CXN * 2 + phba->fw_config.iscsi_cid_count * 2
+ + BE2_TMFS) / 512) + 1) * 512;
phba->params.wrbs_per_cxn = 256;
}
@@ -443,7 +448,7 @@ static irqreturn_t be_isr(int irq, void *dev_id)
if (phba->todo_mcc_cq)
queue_work(phba->wq, &phba->work_cqs);
- if ((num_mcceq_processed) && (!num_ioeq_processed))
+ if ((num_mcceq_processed) && (!num_ioeq_processed))
hwi_ring_eq_db(phba, eq->id, 0,
(num_ioeq_processed +
num_mcceq_processed) , 1, 1);
@@ -561,6 +566,7 @@ 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:
+ case ISCSI_OP_TEXT_RSP:
task = conn->login_task;
io_task = task->dd_data;
login_hdr = (struct iscsi_hdr *)ppdu;
@@ -631,29 +637,29 @@ free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
* alloc_wrb_handle - To allocate a wrb handle
* @phba: The hba pointer
* @cid: The cid to use for allocation
- * @index: index allocation and wrb index
*
* This happens under session_lock until submission to chip
*/
-struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
- int index)
+struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid)
{
struct hwi_wrb_context *pwrb_context;
struct hwi_controller *phwi_ctrlr;
- struct wrb_handle *pwrb_handle;
+ struct wrb_handle *pwrb_handle, *pwrb_handle_tmp;
phwi_ctrlr = phba->phwi_ctrlr;
pwrb_context = &phwi_ctrlr->wrb_context[cid];
- if (pwrb_context->wrb_handles_available) {
+ if (pwrb_context->wrb_handles_available >= 2) {
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++;
+ pwrb_handle_tmp = pwrb_context->pwrb_handle_base[
+ pwrb_context->alloc_index];
+ pwrb_handle->nxt_wrb_index = pwrb_handle_tmp->wrb_index;
} else
pwrb_handle = NULL;
return pwrb_handle;
@@ -671,9 +677,7 @@ 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->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;
@@ -790,6 +794,7 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
memcpy(task->sc->sense_buffer, sense,
min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
}
+
if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ) {
if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
& SOL_RES_CNT_MASK)
@@ -811,6 +816,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
struct iscsi_conn *conn = beiscsi_conn->conn;
hdr = (struct iscsi_logout_rsp *)task->hdr;
+ hdr->opcode = ISCSI_OP_LOGOUT_RSP;
hdr->t2wait = 5;
hdr->t2retain = 0;
hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
@@ -825,6 +831,9 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
& SOL_EXP_CMD_SN_MASK) +
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->dlength[0] = 0;
+ hdr->dlength[1] = 0;
+ hdr->dlength[2] = 0;
hdr->hlength = 0;
hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
@@ -839,6 +848,7 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_io_task *io_task = task->dd_data;
hdr = (struct iscsi_tm_rsp *)task->hdr;
+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
& SOL_FLAGS_MASK) >> 24) | 0x80;
hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
@@ -859,7 +869,6 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
{
struct hwi_wrb_context *pwrb_context;
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;
@@ -867,22 +876,14 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct iscsi_session *session = conn->session;
phwi_ctrlr = phba->phwi_ctrlr;
- 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->
+ 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->
+ SOL_CID_MASK) >> 6) -
+ phba->fw_config.iscsi_cid_start];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
dw[offsetof(struct amap_sol_cqe, wrb_index) /
32] & SOL_WRB_INDEX_MASK) >> 16)];
- task = pwrb_handle->pio_handle;
- }
+ task = pwrb_handle->pio_handle;
io_task = task->dd_data;
spin_lock(&phba->mgmt_sgl_lock);
@@ -923,31 +924,23 @@ 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 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;
- 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
+ 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->
+ & SOL_CID_MASK) >> 6) -
+ phba->fw_config.iscsi_cid_start];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
dw[offsetof(struct amap_sol_cqe, wrb_index) /
32] & SOL_WRB_INDEX_MASK) >> 16)];
- task = pwrb_handle->pio_handle;
- pwrb = pwrb_handle->pwrb;
- type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
- WRB_TYPE_MASK) >> 28;
- }
+ 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);
switch (type) {
case HWH_TYPE_IO:
@@ -978,15 +971,7 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
break;
default:
- 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,
+ 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,
@@ -1077,7 +1062,8 @@ hwi_get_async_handle(struct beiscsi_hba *phba,
WARN_ON(!pasync_handle);
- pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid;
+ pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid -
+ phba->fw_config.iscsi_cid_start;
pasync_handle->is_header = is_header;
pasync_handle->buffer_len = ((pdpdu_cqe->
dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
@@ -1327,9 +1313,10 @@ hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn,
}
status = beiscsi_process_async_pdu(beiscsi_conn, phba,
- beiscsi_conn->beiscsi_conn_cid,
- phdr, hdr_len, pfirst_buffer,
- buf_len);
+ (beiscsi_conn->beiscsi_conn_cid -
+ phba->fw_config.iscsi_cid_start),
+ phdr, hdr_len, pfirst_buffer,
+ buf_len);
if (status == 0)
hwi_free_async_msg(phba, cri);
@@ -1422,6 +1409,48 @@ static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
hwi_post_async_buffers(phba, pasync_handle->is_header);
}
+static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mcc_cq;
+ struct be_mcc_compl *mcc_compl;
+ unsigned int num_processed = 0;
+
+ mcc_cq = &phba->ctrl.mcc_obj.cq;
+ mcc_compl = queue_tail_node(mcc_cq);
+ mcc_compl->flags = le32_to_cpu(mcc_compl->flags);
+ while (mcc_compl->flags & CQE_FLAGS_VALID_MASK) {
+
+ if (num_processed >= 32) {
+ hwi_ring_cq_db(phba, mcc_cq->id,
+ num_processed, 0, 0);
+ num_processed = 0;
+ }
+ if (mcc_compl->flags & CQE_FLAGS_ASYNC_MASK) {
+ /* Interpret flags as an async trailer */
+ if (is_link_state_evt(mcc_compl->flags))
+ /* Interpret compl as a async link evt */
+ beiscsi_async_link_state_process(phba,
+ (struct be_async_event_link_state *) mcc_compl);
+ else
+ SE_DEBUG(DBG_LVL_1,
+ " Unsupported Async Event, flags"
+ " = 0x%08x \n", mcc_compl->flags);
+ } else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) {
+ be_mcc_compl_process_isr(&phba->ctrl, mcc_compl);
+ atomic_dec(&phba->ctrl.mcc_obj.q.used);
+ }
+
+ mcc_compl->flags = 0;
+ queue_tail_inc(mcc_cq);
+ mcc_compl = queue_tail_node(mcc_cq);
+ mcc_compl->flags = le32_to_cpu(mcc_compl->flags);
+ num_processed++;
+ }
+
+ if (num_processed > 0)
+ hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1, 0);
+
+}
static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
{
@@ -1431,7 +1460,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
unsigned int num_processed = 0;
unsigned int tot_nump = 0;
struct beiscsi_conn *beiscsi_conn;
- struct sgl_handle *psgl_handle = NULL;
+ struct beiscsi_endpoint *beiscsi_ep;
+ struct iscsi_endpoint *ep;
struct beiscsi_hba *phba;
cq = pbe_eq->cq;
@@ -1442,32 +1472,13 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
CQE_VALID_MASK) {
be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
- 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;
- }
+ ep = phba->ep_array[(u32) ((sol->
+ dw[offsetof(struct amap_sol_cqe, cid) / 32] &
+ SOL_CID_MASK) >> 6) -
+ phba->fw_config.iscsi_cid_start];
- } 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,
- "Connection table empty for cid = %d\n",
- (u32)(sol->dw[offsetof(struct amap_sol_cqe,
- cid) / 32] & SOL_CID_MASK) >> 6);
- return 0;
- }
- }
+ beiscsi_ep = ep->dd_data;
+ beiscsi_conn = beiscsi_ep->conn;
if (num_processed >= 32) {
hwi_ring_cq_db(phba, cq->id,
@@ -1511,21 +1522,13 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CMD_CXN_KILLED_ITT_INVALID:
case CMD_CXN_KILLED_SEQ_OUTOFORDER:
case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
- 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,
+ 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,
@@ -1547,37 +1550,23 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CXN_KILLED_OVER_RUN_RESIDUAL:
case CXN_KILLED_UNDER_RUN_RESIDUAL:
case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
- 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 "
+ 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);
- }
+ (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:
- 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"
+ 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);
- }
+ (sol->dw[offsetof(struct amap_sol_cqe, cid) /
+ 32] & CQE_CID_MASK));
iscsi_conn_failure(beiscsi_conn->conn,
ISCSI_ERR_CONN_FAILED);
break;
@@ -1586,8 +1575,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
"received 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);
+ (sol->dw[offsetof(struct amap_sol_cqe, cid) /
+ 32] & CQE_CID_MASK));
break;
}
@@ -1604,7 +1593,7 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
return tot_nump;
}
-static void beiscsi_process_all_cqs(struct work_struct *work)
+void beiscsi_process_all_cqs(struct work_struct *work)
{
unsigned long flags;
struct hwi_controller *phwi_ctrlr;
@@ -1624,6 +1613,7 @@ static void beiscsi_process_all_cqs(struct work_struct *work)
spin_lock_irqsave(&phba->isr_lock, flags);
phba->todo_mcc_cq = 0;
spin_unlock_irqrestore(&phba->isr_lock, flags);
+ beiscsi_process_mcc_isr(phba);
}
if (phba->todo_cq) {
@@ -1668,7 +1658,8 @@ hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
io_task->bhs_pa.u.a32.address_hi);
l_sg = sg;
- for (index = 0; (index < num_sg) && (index < 2); index++, sg_next(sg)) {
+ for (index = 0; (index < num_sg) && (index < 2); index++,
+ sg = sg_next(sg)) {
if (index == 0) {
sg_len = sg_dma_len(sg);
addr = (u64) sg_dma_address(sg);
@@ -1679,11 +1670,7 @@ hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb,
sg_len);
sge_len = sg_len;
- AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
- 1);
} else {
- AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
- 0);
AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_r2t_offset,
pwrb, sge_len);
sg_len = sg_dma_len(sg);
@@ -1706,13 +1693,27 @@ hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
io_task->bhs_pa.u.a32.address_lo);
- if (num_sg == 2)
- AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb, 1);
+ if (num_sg == 1) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
+ 1);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb,
+ 0);
+ } else if (num_sg == 2) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
+ 0);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb,
+ 1);
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
+ 0);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb,
+ 0);
+ }
sg = l_sg;
psgl++;
psgl++;
offset = 0;
- for (index = 0; index < num_sg; index++, sg_next(sg), psgl++) {
+ for (index = 0; index < num_sg; index++, sg = sg_next(sg), psgl++) {
sg_len = sg_dma_len(sg);
addr = (u64) sg_dma_address(sg);
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
@@ -2048,10 +2049,9 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
}
idx = 0;
pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
- num_cxn_wrb =
- ((mem_descr_wrb->mem_array[idx].size) / (sizeof(struct iscsi_wrb)) *
- phba->params.wrbs_per_cxn);
-
+ num_cxn_wrb = (mem_descr_wrb->mem_array[idx].size) /
+ ((sizeof(struct iscsi_wrb) *
+ phba->params.wrbs_per_cxn));
for (index = 0; index < phba->params.cxns_per_ctrl; index += 2) {
pwrb_context = &phwi_ctrlr->wrb_context[index];
if (num_cxn_wrb) {
@@ -2064,9 +2064,9 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
} else {
idx++;
pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
- num_cxn_wrb = ((mem_descr_wrb->mem_array[idx].size) /
- (sizeof(struct iscsi_wrb)) *
- phba->params.wrbs_per_cxn);
+ num_cxn_wrb = (mem_descr_wrb->mem_array[idx].size) /
+ ((sizeof(struct iscsi_wrb) *
+ phba->params.wrbs_per_cxn));
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
pwrb_handle = pwrb_context->pwrb_handle_base[j];
pwrb_handle->pwrb = pwrb;
@@ -2383,7 +2383,7 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba,
&paddr);
if (!cq_vaddress)
goto create_cq_error;
- ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
+ ret = be_fill_queue(cq, phba->params.num_cq_entries,
sizeof(struct sol_cqe), cq_vaddress);
if (ret) {
shost_printk(KERN_ERR, phba->shost,
@@ -2634,7 +2634,8 @@ beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
"wrbq create failed.");
return status;
}
- phwi_ctrlr->wrb_context[i].cid = phwi_context->be_wrbq[i].id;
+ phwi_ctrlr->wrb_context[i * 2].cid = phwi_context->be_wrbq[i].
+ id;
}
kfree(pwrb_arr);
return 0;
@@ -2803,17 +2804,6 @@ static int hwi_init_port(struct beiscsi_hba *phba)
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,
- "Error getting fw config\n");
- goto error;
- }
-
status = beiscsi_create_cqs(phba, phwi_context);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost, "CQ not created\n");
@@ -2941,17 +2931,6 @@ 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) {
@@ -2959,8 +2938,6 @@ 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;
@@ -3032,7 +3009,7 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, pfrag, 0);
pfrag += phba->params.num_sge_per_io;
psgl_handle->sgl_index =
- phba->fw_config.iscsi_cid_start + arr_index++;
+ phba->fw_config.iscsi_icd_start + arr_index++;
}
idx++;
}
@@ -3047,7 +3024,7 @@ static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
{
int i, new_cid;
- phba->cid_array = kmalloc(sizeof(void *) * phba->params.cxns_per_ctrl,
+ phba->cid_array = kzalloc(sizeof(void *) * phba->params.cxns_per_ctrl,
GFP_KERNEL);
if (!phba->cid_array) {
shost_printk(KERN_ERR, phba->shost,
@@ -3055,7 +3032,7 @@ static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
"hba_setup_cid_tbls\n");
return -ENOMEM;
}
- phba->ep_array = kmalloc(sizeof(struct iscsi_endpoint *) *
+ phba->ep_array = kzalloc(sizeof(struct iscsi_endpoint *) *
phba->params.cxns_per_ctrl * 2, GFP_KERNEL);
if (!phba->ep_array) {
shost_printk(KERN_ERR, phba->shost,
@@ -3064,7 +3041,7 @@ static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
kfree(phba->cid_array);
return -ENOMEM;
}
- new_cid = phba->fw_config.iscsi_icd_start;
+ new_cid = phba->fw_config.iscsi_cid_start;
for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
phba->cid_array[i] = new_cid;
new_cid += 2;
@@ -3145,8 +3122,6 @@ 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;
@@ -3166,6 +3141,7 @@ static void hwi_purge_eq(struct beiscsi_hba *phba)
struct be_queue_info *eq;
struct be_eq_entry *eqe = NULL;
int i, eq_msix;
+ unsigned int num_processed;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
@@ -3177,13 +3153,17 @@ static void hwi_purge_eq(struct beiscsi_hba *phba)
for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
eq = &phwi_context->be_eq[i].q;
eqe = queue_tail_node(eq);
-
+ num_processed = 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);
eqe = queue_tail_node(eq);
+ num_processed++;
}
+
+ if (num_processed)
+ hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1);
}
}
@@ -3195,10 +3175,9 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
if (mgmt_status)
shost_printk(KERN_WARNING, phba->shost,
"mgmt_epfw_cleanup FAILED \n");
- hwi_cleanup(phba);
+
hwi_purge_eq(phba);
- if (ring_mode)
- kfree(phba->sgl_hndl_array);
+ hwi_cleanup(phba);
kfree(phba->io_sgl_hndl_base);
kfree(phba->eh_sgl_hndl_base);
kfree(phba->cid_array);
@@ -3219,7 +3198,8 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
* We can always use 0 here because it is reserved by libiscsi for
* login/startup related tasks.
*/
- pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid, 0);
+ pwrb_handle = alloc_wrb_handle(phba, (beiscsi_conn->beiscsi_conn_cid -
+ phba->fw_config.iscsi_cid_start));
pwrb = (struct iscsi_target_context_update_wrb *)pwrb_handle->pwrb;
memset(pwrb, 0, sizeof(*pwrb));
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
@@ -3283,8 +3263,7 @@ 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;
- if (!ring_mode)
- doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK)
+ 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;
@@ -3328,8 +3307,9 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
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);
+ beiscsi_conn->beiscsi_conn_cid -
+ phba->fw_config.iscsi_cid_start
+ );
io_task->conn = beiscsi_conn;
task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr;
@@ -3343,7 +3323,7 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
goto free_hndls;
} else {
io_task->scsi_cmnd = NULL;
- if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
+ if ((opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
if (!beiscsi_conn->login_in_progress) {
spin_lock(&phba->mgmt_sgl_lock);
io_task->psgl_handle = (struct sgl_handle *)
@@ -3370,21 +3350,16 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
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->pwrb_handle->pio_handle = task;
io_task->cmd_bhs->iscsi_hdr.itt = itt;
return 0;
free_hndls:
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid];
+ pwrb_context = &phwi_ctrlr->wrb_context[
+ beiscsi_conn->beiscsi_conn_cid -
+ phba->fw_config.iscsi_cid_start];
free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
io_task->pwrb_handle = NULL;
pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
@@ -3404,7 +3379,8 @@ static void beiscsi_cleanup_task(struct iscsi_task *task)
struct hwi_controller *phwi_ctrlr;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid];
+ pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid
+ - phba->fw_config.iscsi_cid_start];
if (io_task->pwrb_handle) {
free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
io_task->pwrb_handle = NULL;
@@ -3460,18 +3436,12 @@ 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);
- 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, type, pwrb,
+ INI_WR_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
} else {
- 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, type, pwrb,
+ INI_RD_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
}
memcpy(&io_task->cmd_bhs->iscsi_data_pdu.
@@ -3496,8 +3466,7 @@ 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;
- if (!ring_mode)
- doorbell |= (io_task->pwrb_handle->wrb_index &
+ 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;
@@ -3519,49 +3488,46 @@ static int beiscsi_mtask(struct iscsi_task *task)
unsigned int doorbell = 0;
unsigned int i, cid;
struct iscsi_task *aborted_task;
+ unsigned int tag;
cid = beiscsi_conn->beiscsi_conn_cid;
pwrb = io_task->pwrb_handle->pwrb;
+ memset(pwrb, 0, sizeof(*pwrb));
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
be32_to_cpu(task->cmdsn));
AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
io_task->pwrb_handle->wrb_index);
AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
io_task->psgl_handle->sgl_index);
-
switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN:
- 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, 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:
- if (ring_mode)
- io_task->psgl_handle->type = INI_RD_CMD;
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_RD_CMD);
+ if (task->hdr->ttt == ISCSI_RESERVED_TAG)
+ AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
else
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- INI_RD_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 1);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_TEXT:
- 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);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ TGT_DM_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_SCSI_TMFUNC:
session = conn->session;
i = ((struct iscsi_tm *)task->hdr)->rtt;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[cid];
+ pwrb_context = &phwi_ctrlr->wrb_context[cid -
+ phba->fw_config.iscsi_cid_start];
pwrb_handle = pwrb_context->pwrb_handle_basestd[be32_to_cpu(i)
>> 16];
aborted_task = pwrb_handle->pio_handle;
@@ -3572,22 +3538,25 @@ static int beiscsi_mtask(struct iscsi_task *task)
if (!aborted_io_task->scsi_cmnd)
return 0;
- mgmt_invalidate_icds(phba,
+ tag = mgmt_invalidate_icds(phba,
aborted_io_task->psgl_handle->sgl_index,
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);
+ if (!tag) {
+ shost_printk(KERN_WARNING, phba->shost,
+ "mgmt_invalidate_icds could not be"
+ " submitted\n");
+ } else {
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+ free_mcc_tag(&phba->ctrl, tag);
+ }
+ 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);
@@ -3600,14 +3569,13 @@ static int beiscsi_mtask(struct iscsi_task *task)
}
AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb,
- be32_to_cpu(task->data_count));
+ task->data_count);
AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
io_task->pwrb_handle->nxt_wrb_index);
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
doorbell |= cid & DB_WRB_POST_CID_MASK;
- if (!ring_mode)
- doorbell |= (io_task->pwrb_handle->wrb_index &
+ 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);
@@ -3649,7 +3617,6 @@ 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;
@@ -3734,7 +3701,20 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
}
SE_DEBUG(DBG_LVL_8, " phba = %p \n", phba);
- pci_set_drvdata(pcidev, phba);
+ switch (pcidev->device) {
+ case BE_DEVICE_ID1:
+ case OC_DEVICE_ID1:
+ case OC_DEVICE_ID2:
+ phba->generation = BE_GEN2;
+ break;
+ case BE_DEVICE_ID2:
+ case OC_DEVICE_ID3:
+ phba->generation = BE_GEN3;
+ break;
+ default:
+ phba->generation = 0;
+ }
+
if (enable_msix)
num_cpus = find_num_cpus();
else
@@ -3754,7 +3734,15 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
spin_lock_init(&phba->io_sgl_lock);
spin_lock_init(&phba->mgmt_sgl_lock);
spin_lock_init(&phba->isr_lock);
+ ret = mgmt_get_fw_config(&phba->ctrl, phba);
+ if (ret != 0) {
+ shost_printk(KERN_ERR, phba->shost,
+ "Error getting fw config\n");
+ goto free_port;
+ }
+ phba->shost->max_id = phba->fw_config.iscsi_cid_count;
beiscsi_get_params(phba);
+ phba->shost->can_queue = phba->params.ios_per_ctrl;
ret = beiscsi_init_port(phba);
if (ret < 0) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3762,6 +3750,15 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
goto free_port;
}
+ for (i = 0; i < MAX_MCC_CMD ; i++) {
+ init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]);
+ phba->ctrl.mcc_tag[i] = i + 1;
+ phba->ctrl.mcc_numtag[i + 1] = 0;
+ phba->ctrl.mcc_tag_available++;
+ }
+
+ phba->ctrl.mcc_alloc_index = phba->ctrl.mcc_free_index = 0;
+
snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
phba->shost->host_no);
phba->wq = create_workqueue(phba->wq_name);
@@ -3836,7 +3833,7 @@ disable_pci:
struct iscsi_transport beiscsi_iscsi_transport = {
.owner = THIS_MODULE,
.name = DRV_NAME,
- .caps = CAP_RECOVERY_L0 | CAP_HDRDGST |
+ .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_TEXT_NEGO |
CAP_MULTI_R2T | CAP_DATADGST | CAP_DATA_PATH_OFFLOAD,
.param_mask = ISCSI_MAX_RECV_DLENGTH |
ISCSI_MAX_XMIT_DLENGTH |
@@ -3859,7 +3856,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_TGT_RESET_TMO |
+ ISCSI_LU_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
@@ -3905,7 +3902,7 @@ static int __init beiscsi_module_init(void)
SE_DEBUG(DBG_LVL_1,
"beiscsi_module_init - Unable to register beiscsi"
"transport.\n");
- ret = -ENOMEM;
+ return -ENOMEM;
}
SE_DEBUG(DBG_LVL_8, "In beiscsi_module_init, tt=%p \n",
&beiscsi_iscsi_transport);
@@ -3917,7 +3914,6 @@ 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 25e6b208b77..c53a80ab796 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -40,31 +40,29 @@
#define DRV_DESC BE_NAME " " "Driver"
#define BE_VENDOR_ID 0x19A2
+/* DEVICE ID's for BE2 */
#define BE_DEVICE_ID1 0x212
#define OC_DEVICE_ID1 0x702
#define OC_DEVICE_ID2 0x703
+
+/* DEVICE ID's for BE3 */
+#define BE_DEVICE_ID2 0x222
#define OC_DEVICE_ID3 0x712
-#define OC_DEVICE_ID4 0x222
-#define BE2_MAX_SESSIONS 64
+#define BE2_IO_DEPTH 1024
+#define BE2_MAX_SESSIONS 256
#define BE2_CMDS_PER_CXN 128
-#define BE2_LOGOUTS BE2_MAX_SESSIONS
#define BE2_TMFS 16
#define BE2_NOPOUT_REQ 16
-#define BE2_ASYNCPDUS BE2_MAX_SESSIONS
-#define BE2_MAX_ICDS 2048
#define BE2_SGE 32
#define BE2_DEFPDU_HDR_SZ 64
#define BE2_DEFPDU_DATA_SZ 8192
-#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_SGLIST_ELEMENTS 30
-#define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */
#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */
-#define BEISCSI_MAX_SECTORS 2048 /* scsi_host->max_sectors */
+#define BEISCSI_MAX_SECTORS 256 /* scsi_host->max_sectors */
#define BEISCSI_MAX_CMD_LEN 16 /* scsi_host->max_cmd_len */
#define BEISCSI_NUM_MAX_LUN 256 /* scsi_host->max_lun */
@@ -330,6 +328,7 @@ struct beiscsi_hba {
struct workqueue_struct *wq; /* The actuak work queue */
struct work_struct work_cqs; /* The work being queued */
struct be_ctrl_info ctrl;
+ unsigned int generation;
};
struct beiscsi_session {
@@ -656,11 +655,12 @@ struct amap_iscsi_wrb {
} __packed;
-struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
- int index);
+struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid);
void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle);
+void beiscsi_process_all_cqs(struct work_struct *work);
+
struct pdu_nop_out {
u32 dw[12];
};
@@ -802,7 +802,6 @@ struct hwi_controller {
struct be_ring default_pdu_hdr;
struct be_ring default_pdu_data;
struct hwi_context_memory *phwi_ctxt;
- unsigned short cq_errors[CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN];
};
enum hwh_type_enum {
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 79c2bd525a8..317bcd042ce 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -48,6 +48,14 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
pfw_cfg->ulp[0].sq_base;
phba->fw_config.iscsi_cid_count =
pfw_cfg->ulp[0].sq_count;
+ if (phba->fw_config.iscsi_cid_count > (BE2_MAX_SESSIONS / 2)) {
+ SE_DEBUG(DBG_LVL_8,
+ "FW reported MAX CXNS as %d \t"
+ "Max Supported = %d.\n",
+ phba->fw_config.iscsi_cid_count,
+ BE2_MAX_SESSIONS);
+ phba->fw_config.iscsi_cid_count = BE2_MAX_SESSIONS / 2;
+ }
} else {
shost_printk(KERN_WARNING, phba->shost,
"Failed in mgmt_get_fw_config \n");
@@ -77,6 +85,7 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
}
nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes);
req = nonemb_cmd.va;
+ memset(req, 0, sizeof(*req));
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
@@ -140,10 +149,17 @@ 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_mccq(phba);
- struct be_sge *sge = nonembedded_sgl(wrb);
+ struct be_mcc_wrb *wrb;
+ struct be_sge *sge;
struct invalidate_commands_params_in *req;
- int status = 0;
+ unsigned int tag = 0;
+
+ spin_lock(&ctrl->mbox_lock);
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+ }
nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev,
sizeof(struct invalidate_commands_params_in),
@@ -156,8 +172,10 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
}
nonemb_cmd.size = sizeof(struct invalidate_commands_params_in);
req = nonemb_cmd.va;
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ memset(req, 0, sizeof(*req));
+ wrb = wrb_from_mccq(phba);
+ sge = nonembedded_sgl(wrb);
+ wrb->tag0 |= tag;
be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
@@ -172,14 +190,12 @@ 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_mcc_notify_wait(phba);
- if (status)
- SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n");
+ be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock);
if (nonemb_cmd.va)
pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
- return status;
+ return tag;
}
unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
@@ -189,13 +205,19 @@ 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_mccq(phba);
- struct iscsi_invalidate_connection_params_in *req =
- embedded_payload(wrb);
- int status = 0;
+ struct be_mcc_wrb *wrb;
+ struct iscsi_invalidate_connection_params_in *req;
+ unsigned int tag = 0;
spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+ }
+ wrb = wrb_from_mccq(phba);
+ wrb->tag0 |= tag;
+ req = embedded_payload(wrb);
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
@@ -208,35 +230,37 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
else
req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE;
req->save_cfg = savecfg_flag;
- status = be_mcc_notify_wait(phba);
- if (status)
- SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n");
-
+ be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock);
- return status;
+ return tag;
}
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_mccq(phba);
- struct tcp_upload_params_in *req = embedded_payload(wrb);
- int status = 0;
+ struct be_mcc_wrb *wrb;
+ struct tcp_upload_params_in *req;
+ unsigned int tag = 0;
spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+ }
+ wrb = wrb_from_mccq(phba);
+ req = embedded_payload(wrb);
+ wrb->tag0 |= tag;
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_COMMON_TCP_UPLOAD,
OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
req->id = (unsigned short)cid;
req->upload_type = (unsigned char)upload_flag;
- status = be_mcc_notify_wait(phba);
- if (status)
- SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n");
+ be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock);
- return status;
+ return tag;
}
int mgmt_open_connection(struct beiscsi_hba *phba,
@@ -248,13 +272,13 @@ 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_mccq(phba);
- struct tcp_connect_and_offload_in *req = embedded_payload(wrb);
+ struct be_mcc_wrb *wrb;
+ struct tcp_connect_and_offload_in *req;
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 tag = 0;
unsigned int i;
unsigned short cid = beiscsi_ep->ep_cid;
@@ -266,7 +290,14 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
ptemplate_address = &template_address;
ISCSI_GET_PDU_TEMPLATE_ADDRESS(phba, ptemplate_address);
spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+ }
+ wrb = wrb_from_mccq(phba);
+ req = embedded_payload(wrb);
+ wrb->tag0 |= tag;
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
@@ -311,46 +342,36 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
req->do_offload = 1;
req->dataout_template_pa.lo = ptemplate_address->lo;
req->dataout_template_pa.hi = ptemplate_address->hi;
- status = be_mcc_notify_wait(phba);
- if (!status) {
- struct iscsi_endpoint *ep;
- struct tcp_connect_and_offload_out *ptcpcnct_out =
- embedded_payload(wrb);
-
- ep = phba->ep_array[ptcpcnct_out->cid];
- beiscsi_ep = ep->dd_data;
- beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
- beiscsi_ep->cid_vld = 1;
- SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
- } else
- SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed\n");
+ be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock);
- return status;
+ return tag;
}
-int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr)
+unsigned int be_cmd_get_mac_addr(struct beiscsi_hba *phba)
{
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;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_get_mac_addr *req;
+ unsigned int tag = 0;
SE_DEBUG(DBG_LVL_8, "In be_cmd_get_mac_addr\n");
spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+ }
+
+ wrb = wrb_from_mccq(phba);
+ req = embedded_payload(wrb);
+ wrb->tag0 |= tag;
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);
- }
-
+ be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock);
- return status;
+ return tag;
}
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index 24eaff923f8..ecead6a5aa5 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -231,6 +231,7 @@ struct beiscsi_endpoint {
struct beiscsi_hba *phba;
struct beiscsi_sess *sess;
struct beiscsi_conn *conn;
+ struct iscsi_endpoint *openiscsi_ep;
unsigned short ip_type;
char dst6_addr[ISCSI_ADDRESS_BUF_LEN];
unsigned long dst_addr;
@@ -249,7 +250,4 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
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/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 33b2294625b..1c4d1215769 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1426,8 +1426,8 @@ static int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
break;
case ISCSI_PARAM_CONN_ADDRESS:
if (bnx2i_conn->ep)
- len = sprintf(buf, NIPQUAD_FMT "\n",
- NIPQUAD(bnx2i_conn->ep->cm_sk->dst_ip));
+ len = sprintf(buf, "%pI4\n",
+ &bnx2i_conn->ep->cm_sk->dst_ip);
break;
default:
return iscsi_conn_get_param(cls_conn, param, buf);
@@ -1990,6 +1990,7 @@ static struct scsi_host_template bnx2i_host_template = {
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_target_reset,
+ .change_queue_depth = iscsi_change_queue_depth,
.can_queue = 1024,
.max_sectors = 127,
.cmd_per_lun = 32,
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 9129bcf117c..cd05e049d5f 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -219,18 +219,15 @@ static void print_opcode_name(unsigned char * cdbp, int cdb_len)
break;
}
sa = (cdbp[8] << 8) + cdbp[9];
- name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
- if (name) {
+ name = get_sa_name(variable_length_arr, VARIABLE_LENGTH_SZ, sa);
+ if (name)
printk("%s", name);
- if ((cdb_len > 0) && (len != cdb_len))
- printk(", in_cdb_len=%d, ext_len=%d",
- len, cdb_len);
- } else {
+ else
printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- if ((cdb_len > 0) && (len != cdb_len))
- printk(", in_cdb_len=%d, ext_len=%d",
- len, cdb_len);
- }
+
+ if ((cdb_len > 0) && (len != cdb_len))
+ printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
+
break;
case MAINTENANCE_IN:
sa = cdbp[1] & 0x1f;
@@ -349,6 +346,9 @@ void scsi_print_command(struct scsi_cmnd *cmd)
{
int k;
+ if (cmd->cmnd == NULL)
+ return;
+
scmd_printk(KERN_INFO, cmd, "CDB: ");
print_opcode_name(cmd->cmnd, cmd->cmd_len);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
index 969c83162cc..412853c6537 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -591,8 +591,7 @@ static int cxgb3i_conn_bind(struct iscsi_cls_session *cls_session,
cxgb3i_conn_max_recv_dlength(conn);
spin_lock_bh(&conn->session->lock);
- sprintf(conn->portal_address, NIPQUAD_FMT,
- NIPQUAD(c3cn->daddr.sin_addr.s_addr));
+ sprintf(conn->portal_address, "%pI4", &c3cn->daddr.sin_addr.s_addr);
conn->portal_port = ntohs(c3cn->daddr.sin_port);
spin_unlock_bh(&conn->session->lock);
@@ -709,6 +708,12 @@ static int cxgb3i_host_set_param(struct Scsi_Host *shost,
{
struct cxgb3i_hba *hba = iscsi_host_priv(shost);
+ if (!hba->ndev) {
+ shost_printk(KERN_ERR, shost, "Could not set host param. "
+ "Netdev for host not set.\n");
+ return -ENODEV;
+ }
+
cxgb3i_api_debug("param %d, buf %s.\n", param, buf);
switch (param) {
@@ -739,6 +744,12 @@ static int cxgb3i_host_get_param(struct Scsi_Host *shost,
struct cxgb3i_hba *hba = iscsi_host_priv(shost);
int len = 0;
+ if (!hba->ndev) {
+ shost_printk(KERN_ERR, shost, "Could not set host param. "
+ "Netdev for host not set.\n");
+ return -ENODEV;
+ }
+
cxgb3i_api_debug("hba %s, param %d.\n", hba->ndev->name, param);
switch (param) {
@@ -753,7 +764,7 @@ static int cxgb3i_host_get_param(struct Scsi_Host *shost,
__be32 addr;
addr = cxgb3i_get_private_ipv4addr(hba->ndev);
- len = sprintf(buf, NIPQUAD_FMT, NIPQUAD(addr));
+ len = sprintf(buf, "%pI4", &addr);
break;
}
default:
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.c b/drivers/scsi/cxgb3i/cxgb3i_offload.c
index 15a00e8b712..3e08c430ff2 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_offload.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.c
@@ -1675,10 +1675,11 @@ int cxgb3i_c3cn_connect(struct net_device *dev, struct s3_conn *c3cn,
} else
c3cn->saddr.sin_addr.s_addr = sipv4;
- c3cn_conn_debug("c3cn 0x%p, %u.%u.%u.%u,%u-%u.%u.%u.%u,%u SYN_SENT.\n",
- c3cn, NIPQUAD(c3cn->saddr.sin_addr.s_addr),
+ c3cn_conn_debug("c3cn 0x%p, %pI4,%u-%pI4,%u SYN_SENT.\n",
+ c3cn,
+ &c3cn->saddr.sin_addr.s_addr,
ntohs(c3cn->saddr.sin_port),
- NIPQUAD(c3cn->daddr.sin_addr.s_addr),
+ &c3cn->daddr.sin_addr.s_addr,
ntohs(c3cn->daddr.sin_port));
c3cn_set_state(c3cn, C3CN_STATE_CONNECTING);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.c b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
index 1fe3b0f1f3c..9c38539557f 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_pdu.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
@@ -461,10 +461,8 @@ void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn)
skb = skb_peek(&c3cn->receive_queue);
}
read_unlock(&c3cn->callback_lock);
- if (c3cn) {
- c3cn->copied_seq += read;
- cxgb3i_c3cn_rx_credits(c3cn, read);
- }
+ c3cn->copied_seq += read;
+ cxgb3i_c3cn_rx_credits(c3cn, read);
conn->rxdata_octets += read;
if (err) {
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 4f0d0138f48..bc9e94f5915 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -717,6 +717,8 @@ static const struct scsi_dh_devlist alua_dev_list[] = {
{"IBM", "2145" },
{"Pillar", "Axiom" },
{"Intel", "Multi-Flex"},
+ {"NETAPP", "LUN"},
+ {"AIX", "NVDISK"},
{NULL, NULL}
};
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index c7076ce25e2..3c5abf7cd76 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -1509,7 +1509,7 @@ static int option_setup(char *str)
char *cur = str;
int i = 1;
- while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+ while (cur && isdigit(*cur) && i < MAX_INT_PARAM) {
ints[i++] = simple_strtoul(cur, NULL, 0);
if ((cur = strchr(cur, ',')) != NULL)
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index a680e18b5f3..e2bc779f86c 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -1449,9 +1449,6 @@ static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
if (offset > 15)
goto do_reject;
- if (esp->flags & ESP_FLAG_DISABLE_SYNC)
- offset = 0;
-
if (offset) {
int one_clock;
@@ -2405,12 +2402,6 @@ static int esp_slave_configure(struct scsi_device *dev)
struct esp_target_data *tp = &esp->target[dev->id];
int goal_tags, queue_depth;
- if (esp->flags & ESP_FLAG_DISABLE_SYNC) {
- /* Bypass async domain validation */
- dev->ppr = 0;
- dev->sdtr = 0;
- }
-
goal_tags = 0;
if (dev->tagged_supported) {
@@ -2660,7 +2651,10 @@ static void esp_set_offset(struct scsi_target *target, int offset)
struct esp *esp = shost_priv(host);
struct esp_target_data *tp = &esp->target[target->id];
- tp->nego_goal_offset = offset;
+ if (esp->flags & ESP_FLAG_DISABLE_SYNC)
+ tp->nego_goal_offset = 0;
+ else
+ tp->nego_goal_offset = offset;
tp->flags |= ESP_TGT_CHECK_NEGO;
}
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 10be9f36a4c..2f47ae7cce9 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -2009,6 +2009,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
fcoe_interface_cleanup(fcoe);
rtnl_unlock();
fcoe_if_destroy(fcoe->ctlr.lp);
+ module_put(THIS_MODULE);
+
out_putdev:
dev_put(netdev);
out_nodev:
@@ -2059,6 +2061,11 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
}
#endif
+ if (!try_module_get(THIS_MODULE)) {
+ rc = -EINVAL;
+ goto out_nomod;
+ }
+
rtnl_lock();
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
@@ -2099,17 +2106,24 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
if (!fcoe_link_ok(lport))
fcoe_ctlr_link_up(&fcoe->ctlr);
- rc = 0;
-out_free:
/*
* Release from init in fcoe_interface_create(), on success lport
* should be holding a reference taken in fcoe_if_create().
*/
fcoe_interface_put(fcoe);
+ dev_put(netdev);
+ rtnl_unlock();
+ mutex_unlock(&fcoe_config_mutex);
+
+ return 0;
+out_free:
+ fcoe_interface_put(fcoe);
out_putdev:
dev_put(netdev);
out_nodev:
rtnl_unlock();
+ module_put(THIS_MODULE);
+out_nomod:
mutex_unlock(&fcoe_config_mutex);
return rc;
}
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 9823291395a..511cb6b371e 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -1187,7 +1187,7 @@ static void fcoe_ctlr_timeout(unsigned long arg)
next_timer = fip->ctlr_ka_time;
if (time_after_eq(jiffies, fip->port_ka_time)) {
- fip->port_ka_time += jiffies +
+ fip->port_ka_time = jiffies +
msecs_to_jiffies(FIP_VN_KA_PERIOD);
fip->send_port_ka = 1;
}
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index bb208a6091e..3966c71d009 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -36,7 +36,7 @@
#define DRV_NAME "fnic"
#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
-#define DRV_VERSION "1.0.0.1121"
+#define DRV_VERSION "1.4.0.98"
#define PFX DRV_NAME ": "
#define DFX DRV_NAME "%d: "
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index fe1b1031f7a..507e26c1c29 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -620,6 +620,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
if (fnic->config.flags & VFCF_FIP_CAPABLE) {
shost_printk(KERN_INFO, fnic->lport->host,
"firmware supports FIP\n");
+ /* enable directed and multicast */
+ vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0);
vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
} else {
@@ -698,6 +700,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
goto err_out_remove_scsi_host;
}
+ fc_lport_init_stats(lp);
+
fc_lport_config(lp);
if (fc_set_mfs(lp, fnic->config.maxdatafieldsize +
diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h
index d62b9061bf1..7c9ccbd4134 100644
--- a/drivers/scsi/fnic/vnic_devcmd.h
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -94,7 +94,7 @@ enum vnic_devcmd_cmd {
CMD_STATS_DUMP = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 4),
/* set Rx packet filter: (u32)a0=filters (see CMD_PFILTER_*) */
- CMD_PACKET_FILTER = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 7),
+ CMD_PACKET_FILTER = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 7),
/* hang detection notification */
CMD_HANG_NOTIFY = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 8),
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index 9e8fce0f0c1..ba3c94c9c25 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -140,40 +140,40 @@
#include "gdth.h"
static void gdth_delay(int milliseconds);
-static void gdth_eval_mapping(ulong32 size, ulong32 *cyls, int *heads, int *secs);
+static void gdth_eval_mapping(u32 size, u32 *cyls, int *heads, int *secs);
static irqreturn_t gdth_interrupt(int irq, void *dev_id);
static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
int gdth_from_wait, int* pIndex);
-static int gdth_sync_event(gdth_ha_str *ha, int service, unchar index,
+static int gdth_sync_event(gdth_ha_str *ha, int service, u8 index,
Scsi_Cmnd *scp);
static int gdth_async_event(gdth_ha_str *ha);
static void gdth_log_event(gdth_evt_data *dvr, char *buffer);
-static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar priority);
+static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 priority);
static void gdth_next(gdth_ha_str *ha);
-static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b);
+static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 b);
static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp);
-static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, ushort source,
- ushort idx, gdth_evt_data *evt);
+static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, u16 source,
+ u16 idx, gdth_evt_data *evt);
static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr);
-static void gdth_readapp_event(gdth_ha_str *ha, unchar application,
+static void gdth_readapp_event(gdth_ha_str *ha, u8 application,
gdth_evt_str *estr);
static void gdth_clear_events(void);
static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp,
- char *buffer, ushort count);
+ char *buffer, u16 count);
static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp);
-static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive);
+static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u16 hdrive);
static void gdth_enable_int(gdth_ha_str *ha);
static int gdth_test_busy(gdth_ha_str *ha);
static int gdth_get_cmd_index(gdth_ha_str *ha);
static void gdth_release_event(gdth_ha_str *ha);
-static int gdth_wait(gdth_ha_str *ha, int index,ulong32 time);
-static int gdth_internal_cmd(gdth_ha_str *ha, unchar service, ushort opcode,
- ulong32 p1, ulong64 p2,ulong64 p3);
+static int gdth_wait(gdth_ha_str *ha, int index,u32 time);
+static int gdth_internal_cmd(gdth_ha_str *ha, u8 service, u16 opcode,
+ u32 p1, u64 p2,u64 p3);
static int gdth_search_drives(gdth_ha_str *ha);
-static int gdth_analyse_hdrive(gdth_ha_str *ha, ushort hdrive);
+static int gdth_analyse_hdrive(gdth_ha_str *ha, u16 hdrive);
static const char *gdth_ctr_name(gdth_ha_str *ha);
@@ -189,7 +189,7 @@ static int __gdth_queuecommand(gdth_ha_str *ha, struct scsi_cmnd *scp,
static void gdth_scsi_done(struct scsi_cmnd *scp);
#ifdef DEBUG_GDTH
-static unchar DebugState = DEBUG_GDTH;
+static u8 DebugState = DEBUG_GDTH;
#ifdef __SERIAL__
#define MAX_SERBUF 160
@@ -270,30 +270,30 @@ static int ser_printk(const char *fmt, ...)
#endif
#ifdef GDTH_STATISTICS
-static ulong32 max_rq=0, max_index=0, max_sg=0;
+static u32 max_rq=0, max_index=0, max_sg=0;
#ifdef INT_COAL
-static ulong32 max_int_coal=0;
+static u32 max_int_coal=0;
#endif
-static ulong32 act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+static u32 act_ints=0, act_ios=0, act_stats=0, act_rq=0;
static struct timer_list gdth_timer;
#endif
-#define PTR2USHORT(a) (ushort)(ulong)(a)
+#define PTR2USHORT(a) (u16)(unsigned long)(a)
#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b)
#define INDEX_OK(i,t) ((i)<ARRAY_SIZE(t))
#define BUS_L2P(a,b) ((b)>(a)->virt_bus ? (b-1):(b))
#ifdef CONFIG_ISA
-static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
+static u8 gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
#endif
#if defined(CONFIG_EISA) || defined(CONFIG_ISA)
-static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
+static u8 gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
#endif
-static unchar gdth_polling; /* polling if TRUE */
+static u8 gdth_polling; /* polling if TRUE */
static int gdth_ctr_count = 0; /* controller count */
static LIST_HEAD(gdth_instances); /* controller list */
-static unchar gdth_write_through = FALSE; /* write through */
+static u8 gdth_write_through = FALSE; /* write through */
static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */
static int elastidx;
static int eoldidx;
@@ -303,7 +303,7 @@ static int major;
#define DOU 2 /* OUT data direction */
#define DNO DIN /* no data transfer */
#define DUN DIN /* unknown data direction */
-static unchar gdth_direction_tab[0x100] = {
+static u8 gdth_direction_tab[0x100] = {
DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DOU,DNO,DUN,DIN,DOU,DOU,
@@ -390,7 +390,7 @@ static gdth_ha_str *gdth_find_ha(int hanum)
static struct gdth_cmndinfo *gdth_get_cmndinfo(gdth_ha_str *ha)
{
struct gdth_cmndinfo *priv = NULL;
- ulong flags;
+ unsigned long flags;
int i;
spin_lock_irqsave(&ha->smp_lock, flags);
@@ -493,7 +493,7 @@ int gdth_execute(struct Scsi_Host *shost, gdth_cmd_str *gdtcmd, char *cmnd,
return rval;
}
-static void gdth_eval_mapping(ulong32 size, ulong32 *cyls, int *heads, int *secs)
+static void gdth_eval_mapping(u32 size, u32 *cyls, int *heads, int *secs)
{
*cyls = size /HEADS/SECS;
if (*cyls <= MAXCYLS) {
@@ -514,9 +514,9 @@ static void gdth_eval_mapping(ulong32 size, ulong32 *cyls, int *heads, int *secs
/* controller search and initialization functions */
#ifdef CONFIG_EISA
-static int __init gdth_search_eisa(ushort eisa_adr)
+static int __init gdth_search_eisa(u16 eisa_adr)
{
- ulong32 id;
+ u32 id;
TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
id = inl(eisa_adr+ID0REG);
@@ -533,13 +533,13 @@ static int __init gdth_search_eisa(ushort eisa_adr)
#endif /* CONFIG_EISA */
#ifdef CONFIG_ISA
-static int __init gdth_search_isa(ulong32 bios_adr)
+static int __init gdth_search_isa(u32 bios_adr)
{
void __iomem *addr;
- ulong32 id;
+ u32 id;
TRACE(("gdth_search_isa() bios adr. %x\n",bios_adr));
- if ((addr = ioremap(bios_adr+BIOS_ID_OFFS, sizeof(ulong32))) != NULL) {
+ if ((addr = ioremap(bios_adr+BIOS_ID_OFFS, sizeof(u32))) != NULL) {
id = readl(addr);
iounmap(addr);
if (id == GDT2_ID) /* GDT2000 */
@@ -551,7 +551,7 @@ static int __init gdth_search_isa(ulong32 bios_adr)
#ifdef CONFIG_PCI
-static bool gdth_search_vortex(ushort device)
+static bool gdth_search_vortex(u16 device)
{
if (device <= PCI_DEVICE_ID_VORTEX_GDT6555)
return true;
@@ -603,9 +603,9 @@ static void __devexit gdth_pci_remove_one(struct pci_dev *pdev)
static int __devinit gdth_pci_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- ushort vendor = pdev->vendor;
- ushort device = pdev->device;
- ulong base0, base1, base2;
+ u16 vendor = pdev->vendor;
+ u16 device = pdev->device;
+ unsigned long base0, base1, base2;
int rc;
gdth_pci_str gdth_pcistr;
gdth_ha_str *ha = NULL;
@@ -658,10 +658,10 @@ static int __devinit gdth_pci_init_one(struct pci_dev *pdev,
#endif /* CONFIG_PCI */
#ifdef CONFIG_EISA
-static int __init gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
+static int __init gdth_init_eisa(u16 eisa_adr,gdth_ha_str *ha)
{
- ulong32 retries,id;
- unchar prot_ver,eisacf,i,irq_found;
+ u32 retries,id;
+ u8 prot_ver,eisacf,i,irq_found;
TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
@@ -688,7 +688,7 @@ static int __init gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
return 0;
}
ha->bmic = eisa_adr;
- ha->brd_phys = (ulong32)eisa_adr >> 12;
+ ha->brd_phys = (u32)eisa_adr >> 12;
outl(0,eisa_adr+MAILBOXREG);
outl(0,eisa_adr+MAILBOXREG+4);
@@ -752,12 +752,12 @@ static int __init gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
#endif /* CONFIG_EISA */
#ifdef CONFIG_ISA
-static int __init gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha)
+static int __init gdth_init_isa(u32 bios_adr,gdth_ha_str *ha)
{
register gdt2_dpram_str __iomem *dp2_ptr;
int i;
- unchar irq_drq,prot_ver;
- ulong32 retries;
+ u8 irq_drq,prot_ver;
+ u32 retries;
TRACE(("gdth_init_isa() bios adr. %x\n",bios_adr));
@@ -812,7 +812,7 @@ static int __init gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha)
}
gdth_delay(1);
}
- prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]);
+ prot_ver = (u8)readl(&dp2_ptr->u.ic.S_Info[0]);
writeb(0, &dp2_ptr->u.ic.Status);
writeb(0xff, &dp2_ptr->io.irqdel);
if (prot_ver != PROTOCOL_VERSION) {
@@ -859,9 +859,9 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
register gdt6_dpram_str __iomem *dp6_ptr;
register gdt6c_dpram_str __iomem *dp6c_ptr;
register gdt6m_dpram_str __iomem *dp6m_ptr;
- ulong32 retries;
- unchar prot_ver;
- ushort command;
+ u32 retries;
+ u8 prot_ver;
+ u16 command;
int i, found = FALSE;
TRACE(("gdth_init_pci()\n"));
@@ -871,7 +871,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
else
ha->oem_id = OEM_ID_ICP;
ha->brd_phys = (pdev->bus->number << 8) | (pdev->devfn & 0xf8);
- ha->stype = (ulong32)pdev->device;
+ ha->stype = (u32)pdev->device;
ha->irq = pdev->irq;
ha->pdev = pdev;
@@ -891,7 +891,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
found = FALSE;
for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
iounmap(ha->brd);
- ha->brd = ioremap(i, sizeof(ushort));
+ ha->brd = ioremap(i, sizeof(u16));
if (ha->brd == NULL) {
printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
return 0;
@@ -947,7 +947,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
}
gdth_delay(1);
}
- prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]);
+ prot_ver = (u8)readl(&dp6_ptr->u.ic.S_Info[0]);
writeb(0, &dp6_ptr->u.ic.S_Status);
writeb(0xff, &dp6_ptr->io.irqdel);
if (prot_ver != PROTOCOL_VERSION) {
@@ -1000,7 +1000,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
found = FALSE;
for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
iounmap(ha->brd);
- ha->brd = ioremap(i, sizeof(ushort));
+ ha->brd = ioremap(i, sizeof(u16));
if (ha->brd == NULL) {
printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
return 0;
@@ -1059,7 +1059,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
}
gdth_delay(1);
}
- prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]);
+ prot_ver = (u8)readl(&dp6c_ptr->u.ic.S_Info[0]);
writeb(0, &dp6c_ptr->u.ic.Status);
if (prot_ver != PROTOCOL_VERSION) {
printk("GDT-PCI: Illegal protocol version\n");
@@ -1128,7 +1128,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
found = FALSE;
for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
iounmap(ha->brd);
- ha->brd = ioremap(i, sizeof(ushort));
+ ha->brd = ioremap(i, sizeof(u16));
if (ha->brd == NULL) {
printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
return 0;
@@ -1180,7 +1180,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
}
gdth_delay(1);
}
- prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]);
+ prot_ver = (u8)readl(&dp6m_ptr->u.ic.S_Info[0]);
writeb(0, &dp6m_ptr->u.ic.S_Status);
if (prot_ver != PROTOCOL_VERSION) {
printk("GDT-PCI: Illegal protocol version\n");
@@ -1223,7 +1223,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
}
gdth_delay(1);
}
- prot_ver = (unchar)(readl(&dp6m_ptr->u.ic.S_Info[0]) >> 16);
+ prot_ver = (u8)(readl(&dp6m_ptr->u.ic.S_Info[0]) >> 16);
writeb(0, &dp6m_ptr->u.ic.S_Status);
if (prot_ver < 0x2b) /* FW < x.43: no 64-bit DMA support */
ha->dma64_support = 0;
@@ -1239,7 +1239,7 @@ static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
static void __devinit gdth_enable_int(gdth_ha_str *ha)
{
- ulong flags;
+ unsigned long flags;
gdt2_dpram_str __iomem *dp2_ptr;
gdt6_dpram_str __iomem *dp6_ptr;
gdt6m_dpram_str __iomem *dp6m_ptr;
@@ -1274,14 +1274,14 @@ static void __devinit gdth_enable_int(gdth_ha_str *ha)
}
/* return IStatus if interrupt was from this card else 0 */
-static unchar gdth_get_status(gdth_ha_str *ha)
+static u8 gdth_get_status(gdth_ha_str *ha)
{
- unchar IStatus = 0;
+ u8 IStatus = 0;
TRACE(("gdth_get_status() irq %d ctr_count %d\n", ha->irq, gdth_ctr_count));
if (ha->type == GDT_EISA)
- IStatus = inb((ushort)ha->bmic + EDOORREG);
+ IStatus = inb((u16)ha->bmic + EDOORREG);
else if (ha->type == GDT_ISA)
IStatus =
readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
@@ -1329,7 +1329,7 @@ static int gdth_get_cmd_index(gdth_ha_str *ha)
if (ha->cmd_tab[i].cmnd == UNUSED_CMND) {
ha->cmd_tab[i].cmnd = ha->pccb->RequestBuffer;
ha->cmd_tab[i].service = ha->pccb->Service;
- ha->pccb->CommandIndex = (ulong32)i+2;
+ ha->pccb->CommandIndex = (u32)i+2;
return (i+2);
}
}
@@ -1362,7 +1362,7 @@ static void gdth_copy_command(gdth_ha_str *ha)
register gdt6c_dpram_str __iomem *dp6c_ptr;
gdt6_dpram_str __iomem *dp6_ptr;
gdt2_dpram_str __iomem *dp2_ptr;
- ushort cp_count,dp_offset,cmd_no;
+ u16 cp_count,dp_offset,cmd_no;
TRACE(("gdth_copy_command() hanum %d\n", ha->hanum));
@@ -1386,28 +1386,28 @@ static void gdth_copy_command(gdth_ha_str *ha)
dp2_ptr = ha->brd;
writew(dp_offset + DPMEM_COMMAND_OFFSET,
&dp2_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((ushort)cmd_ptr->Service,
+ writew((u16)cmd_ptr->Service,
&dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
} else if (ha->type == GDT_PCI) {
dp6_ptr = ha->brd;
writew(dp_offset + DPMEM_COMMAND_OFFSET,
&dp6_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((ushort)cmd_ptr->Service,
+ writew((u16)cmd_ptr->Service,
&dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
} else if (ha->type == GDT_PCINEW) {
dp6c_ptr = ha->brd;
writew(dp_offset + DPMEM_COMMAND_OFFSET,
&dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((ushort)cmd_ptr->Service,
+ writew((u16)cmd_ptr->Service,
&dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
} else if (ha->type == GDT_PCIMPR) {
dp6m_ptr = ha->brd;
writew(dp_offset + DPMEM_COMMAND_OFFSET,
&dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((ushort)cmd_ptr->Service,
+ writew((u16)cmd_ptr->Service,
&dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
}
@@ -1420,14 +1420,14 @@ static void gdth_release_event(gdth_ha_str *ha)
#ifdef GDTH_STATISTICS
{
- ulong32 i,j;
+ u32 i,j;
for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
if (ha->cmd_tab[j].cmnd != UNUSED_CMND)
++i;
}
if (max_index < i) {
max_index = i;
- TRACE3(("GDT: max_index = %d\n",(ushort)i));
+ TRACE3(("GDT: max_index = %d\n",(u16)i));
}
}
#endif
@@ -1450,7 +1450,7 @@ static void gdth_release_event(gdth_ha_str *ha)
}
}
-static int gdth_wait(gdth_ha_str *ha, int index, ulong32 time)
+static int gdth_wait(gdth_ha_str *ha, int index, u32 time)
{
int answer_found = FALSE;
int wait_index = 0;
@@ -1476,8 +1476,8 @@ static int gdth_wait(gdth_ha_str *ha, int index, ulong32 time)
}
-static int gdth_internal_cmd(gdth_ha_str *ha, unchar service, ushort opcode,
- ulong32 p1, ulong64 p2, ulong64 p3)
+static int gdth_internal_cmd(gdth_ha_str *ha, u8 service, u16 opcode,
+ u32 p1, u64 p2, u64 p3)
{
register gdth_cmd_str *cmd_ptr;
int retries,index;
@@ -1501,35 +1501,35 @@ static int gdth_internal_cmd(gdth_ha_str *ha, unchar service, ushort opcode,
if (service == CACHESERVICE) {
if (opcode == GDT_IOCTL) {
cmd_ptr->u.ioctl.subfunc = p1;
- cmd_ptr->u.ioctl.channel = (ulong32)p2;
- cmd_ptr->u.ioctl.param_size = (ushort)p3;
+ cmd_ptr->u.ioctl.channel = (u32)p2;
+ cmd_ptr->u.ioctl.param_size = (u16)p3;
cmd_ptr->u.ioctl.p_param = ha->scratch_phys;
} else {
if (ha->cache_feat & GDT_64BIT) {
- cmd_ptr->u.cache64.DeviceNo = (ushort)p1;
+ cmd_ptr->u.cache64.DeviceNo = (u16)p1;
cmd_ptr->u.cache64.BlockNo = p2;
} else {
- cmd_ptr->u.cache.DeviceNo = (ushort)p1;
- cmd_ptr->u.cache.BlockNo = (ulong32)p2;
+ cmd_ptr->u.cache.DeviceNo = (u16)p1;
+ cmd_ptr->u.cache.BlockNo = (u32)p2;
}
}
} else if (service == SCSIRAWSERVICE) {
if (ha->raw_feat & GDT_64BIT) {
cmd_ptr->u.raw64.direction = p1;
- cmd_ptr->u.raw64.bus = (unchar)p2;
- cmd_ptr->u.raw64.target = (unchar)p3;
- cmd_ptr->u.raw64.lun = (unchar)(p3 >> 8);
+ cmd_ptr->u.raw64.bus = (u8)p2;
+ cmd_ptr->u.raw64.target = (u8)p3;
+ cmd_ptr->u.raw64.lun = (u8)(p3 >> 8);
} else {
cmd_ptr->u.raw.direction = p1;
- cmd_ptr->u.raw.bus = (unchar)p2;
- cmd_ptr->u.raw.target = (unchar)p3;
- cmd_ptr->u.raw.lun = (unchar)(p3 >> 8);
+ cmd_ptr->u.raw.bus = (u8)p2;
+ cmd_ptr->u.raw.target = (u8)p3;
+ cmd_ptr->u.raw.lun = (u8)(p3 >> 8);
}
} else if (service == SCREENSERVICE) {
if (opcode == GDT_REALTIME) {
- *(ulong32 *)&cmd_ptr->u.screen.su.data[0] = p1;
- *(ulong32 *)&cmd_ptr->u.screen.su.data[4] = (ulong32)p2;
- *(ulong32 *)&cmd_ptr->u.screen.su.data[8] = (ulong32)p3;
+ *(u32 *)&cmd_ptr->u.screen.su.data[0] = p1;
+ *(u32 *)&cmd_ptr->u.screen.su.data[4] = (u32)p2;
+ *(u32 *)&cmd_ptr->u.screen.su.data[8] = (u32)p3;
}
}
ha->cmd_len = sizeof(gdth_cmd_str);
@@ -1555,9 +1555,9 @@ static int gdth_internal_cmd(gdth_ha_str *ha, unchar service, ushort opcode,
static int __devinit gdth_search_drives(gdth_ha_str *ha)
{
- ushort cdev_cnt, i;
+ u16 cdev_cnt, i;
int ok;
- ulong32 bus_no, drv_cnt, drv_no, j;
+ u32 bus_no, drv_cnt, drv_no, j;
gdth_getch_str *chn;
gdth_drlist_str *drl;
gdth_iochan_str *ioc;
@@ -1570,8 +1570,8 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
#endif
#ifdef GDTH_RTC
- unchar rtc[12];
- ulong flags;
+ u8 rtc[12];
+ unsigned long flags;
#endif
TRACE(("gdth_search_drives() hanum %d\n", ha->hanum));
@@ -1584,7 +1584,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
if (ok)
ha->screen_feat = GDT_64BIT;
}
- if (force_dma32 || (!ok && ha->status == (ushort)S_NOFUNC))
+ if (force_dma32 || (!ok && ha->status == (u16)S_NOFUNC))
ok = gdth_internal_cmd(ha, SCREENSERVICE, GDT_INIT, 0, 0, 0);
if (!ok) {
printk("GDT-HA %d: Initialization error screen service (code %d)\n",
@@ -1609,11 +1609,11 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
rtc[j] = CMOS_READ(j);
} while (rtc[0] != CMOS_READ(0));
spin_unlock_irqrestore(&rtc_lock, flags);
- TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(ulong32 *)&rtc[0],
- *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]));
+ TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(u32 *)&rtc[0],
+ *(u32 *)&rtc[4], *(u32 *)&rtc[8]));
/* 3. send to controller firmware */
- gdth_internal_cmd(ha, SCREENSERVICE, GDT_REALTIME, *(ulong32 *)&rtc[0],
- *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]);
+ gdth_internal_cmd(ha, SCREENSERVICE, GDT_REALTIME, *(u32 *)&rtc[0],
+ *(u32 *)&rtc[4], *(u32 *)&rtc[8]);
#endif
/* unfreeze all IOs */
@@ -1627,7 +1627,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
if (ok)
ha->cache_feat = GDT_64BIT;
}
- if (force_dma32 || (!ok && ha->status == (ushort)S_NOFUNC))
+ if (force_dma32 || (!ok && ha->status == (u16)S_NOFUNC))
ok = gdth_internal_cmd(ha, CACHESERVICE, GDT_INIT, LINUX_OS, 0, 0);
if (!ok) {
printk("GDT-HA %d: Initialization error cache service (code %d)\n",
@@ -1635,7 +1635,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
return 0;
}
TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
- cdev_cnt = (ushort)ha->info;
+ cdev_cnt = (u16)ha->info;
ha->fw_vers = ha->service;
#ifdef INT_COAL
@@ -1644,7 +1644,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
pmod = (gdth_perf_modes *)ha->pscratch;
pmod->version = 1;
pmod->st_mode = 1; /* enable one status buffer */
- *((ulong64 *)&pmod->st_buff_addr1) = ha->coal_stat_phys;
+ *((u64 *)&pmod->st_buff_addr1) = ha->coal_stat_phys;
pmod->st_buff_indx1 = COALINDEX;
pmod->st_buff_addr2 = 0;
pmod->st_buff_u_addr2 = 0;
@@ -1705,7 +1705,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
else
ha->bus_id[bus_no] = 0xff;
}
- ha->bus_cnt = (unchar)bus_no;
+ ha->bus_cnt = (u8)bus_no;
}
TRACE2(("gdth_search_drives() %d channels\n",ha->bus_cnt));
@@ -1789,12 +1789,12 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
/* logical drives */
if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, CACHE_DRV_CNT,
- INVALID_CHANNEL,sizeof(ulong32))) {
- drv_cnt = *(ulong32 *)ha->pscratch;
+ INVALID_CHANNEL,sizeof(u32))) {
+ drv_cnt = *(u32 *)ha->pscratch;
if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, CACHE_DRV_LIST,
- INVALID_CHANNEL,drv_cnt * sizeof(ulong32))) {
+ INVALID_CHANNEL,drv_cnt * sizeof(u32))) {
for (j = 0; j < drv_cnt; ++j) {
- drv_no = ((ulong32 *)ha->pscratch)[j];
+ drv_no = ((u32 *)ha->pscratch)[j];
if (drv_no < MAX_LDRIVES) {
ha->hdr[drv_no].is_logdrv = TRUE;
TRACE2(("Drive %d is log. drive\n",drv_no));
@@ -1838,7 +1838,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
if (ok)
ha->raw_feat = GDT_64BIT;
}
- if (force_dma32 || (!ok && ha->status == (ushort)S_NOFUNC))
+ if (force_dma32 || (!ok && ha->status == (u16)S_NOFUNC))
ok = gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_INIT, 0, 0, 0);
if (!ok) {
printk("GDT-HA %d: Initialization error raw service (code %d)\n",
@@ -1854,7 +1854,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
if (gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_GET_FEAT, 0, 0, 0)) {
TRACE2(("gdth_search_dr(): get feat RAWSERVICE %d\n",
ha->info));
- ha->raw_feat |= (ushort)ha->info;
+ ha->raw_feat |= (u16)ha->info;
}
}
@@ -1865,7 +1865,7 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
if (gdth_internal_cmd(ha, CACHESERVICE, GDT_GET_FEAT, 0, 0, 0)) {
TRACE2(("gdth_search_dr(): get feat CACHESERV. %d\n",
ha->info));
- ha->cache_feat |= (ushort)ha->info;
+ ha->cache_feat |= (u16)ha->info;
}
}
@@ -1923,9 +1923,9 @@ static int __devinit gdth_search_drives(gdth_ha_str *ha)
return 1;
}
-static int gdth_analyse_hdrive(gdth_ha_str *ha, ushort hdrive)
+static int gdth_analyse_hdrive(gdth_ha_str *ha, u16 hdrive)
{
- ulong32 drv_cyls;
+ u32 drv_cyls;
int drv_hds, drv_secs;
TRACE(("gdth_analyse_hdrive() hanum %d drive %d\n", ha->hanum, hdrive));
@@ -1944,17 +1944,17 @@ static int gdth_analyse_hdrive(gdth_ha_str *ha, ushort hdrive)
} else {
drv_hds = ha->info2 & 0xff;
drv_secs = (ha->info2 >> 8) & 0xff;
- drv_cyls = (ulong32)ha->hdr[hdrive].size / drv_hds / drv_secs;
+ drv_cyls = (u32)ha->hdr[hdrive].size / drv_hds / drv_secs;
}
- ha->hdr[hdrive].heads = (unchar)drv_hds;
- ha->hdr[hdrive].secs = (unchar)drv_secs;
+ ha->hdr[hdrive].heads = (u8)drv_hds;
+ ha->hdr[hdrive].secs = (u8)drv_secs;
/* round size */
ha->hdr[hdrive].size = drv_cyls * drv_hds * drv_secs;
if (ha->cache_feat & GDT_64BIT) {
if (gdth_internal_cmd(ha, CACHESERVICE, GDT_X_INFO, hdrive, 0, 0)
&& ha->info2 != 0) {
- ha->hdr[hdrive].size = ((ulong64)ha->info2 << 32) | ha->info;
+ ha->hdr[hdrive].size = ((u64)ha->info2 << 32) | ha->info;
}
}
TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n",
@@ -1964,7 +1964,7 @@ static int gdth_analyse_hdrive(gdth_ha_str *ha, ushort hdrive)
if (gdth_internal_cmd(ha, CACHESERVICE, GDT_DEVTYPE, hdrive, 0, 0)) {
TRACE2(("gdth_search_dr() cache drive %d devtype %d\n",
hdrive,ha->info));
- ha->hdr[hdrive].devtype = (ushort)ha->info;
+ ha->hdr[hdrive].devtype = (u16)ha->info;
}
/* cluster info */
@@ -1972,14 +1972,14 @@ static int gdth_analyse_hdrive(gdth_ha_str *ha, ushort hdrive)
TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
hdrive,ha->info));
if (!shared_access)
- ha->hdr[hdrive].cluster_type = (unchar)ha->info;
+ ha->hdr[hdrive].cluster_type = (u8)ha->info;
}
/* R/W attributes */
if (gdth_internal_cmd(ha, CACHESERVICE, GDT_RW_ATTRIBS, hdrive, 0, 0)) {
TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
hdrive,ha->info));
- ha->hdr[hdrive].rw_attribs = (unchar)ha->info;
+ ha->hdr[hdrive].rw_attribs = (u8)ha->info;
}
return 1;
@@ -1988,12 +1988,12 @@ static int gdth_analyse_hdrive(gdth_ha_str *ha, ushort hdrive)
/* command queueing/sending functions */
-static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar priority)
+static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 priority)
{
struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
register Scsi_Cmnd *pscp;
register Scsi_Cmnd *nscp;
- ulong flags;
+ unsigned long flags;
TRACE(("gdth_putq() priority %d\n",priority));
spin_lock_irqsave(&ha->smp_lock, flags);
@@ -2023,7 +2023,7 @@ static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar priority)
++flags;
if (max_rq < flags) {
max_rq = flags;
- TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq));
+ TRACE3(("GDT: max_rq = %d\n",(u16)max_rq));
}
#endif
}
@@ -2032,9 +2032,9 @@ static void gdth_next(gdth_ha_str *ha)
{
register Scsi_Cmnd *pscp;
register Scsi_Cmnd *nscp;
- unchar b, t, l, firsttime;
- unchar this_cmd, next_cmd;
- ulong flags = 0;
+ u8 b, t, l, firsttime;
+ u8 this_cmd, next_cmd;
+ unsigned long flags = 0;
int cmd_index;
TRACE(("gdth_next() hanum %d\n", ha->hanum));
@@ -2282,20 +2282,20 @@ static void gdth_next(gdth_ha_str *ha)
* buffers, kmap_atomic() as needed.
*/
static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp,
- char *buffer, ushort count)
+ char *buffer, u16 count)
{
- ushort cpcount,i, max_sg = scsi_sg_count(scp);
- ushort cpsum,cpnow;
+ u16 cpcount,i, max_sg = scsi_sg_count(scp);
+ u16 cpsum,cpnow;
struct scatterlist *sl;
char *address;
- cpcount = min_t(ushort, count, scsi_bufflen(scp));
+ cpcount = min_t(u16, count, scsi_bufflen(scp));
if (cpcount) {
cpsum=0;
scsi_for_each_sg(scp, sl, max_sg, i) {
unsigned long flags;
- cpnow = (ushort)sl->length;
+ cpnow = (u16)sl->length;
TRACE(("copy_internal() now %d sum %d count %d %d\n",
cpnow, cpsum, cpcount, scsi_bufflen(scp)));
if (cpsum+cpnow > cpcount)
@@ -2325,7 +2325,7 @@ static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp,
static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
{
- unchar t;
+ u8 t;
gdth_inq_data inq;
gdth_rdcap_data rdc;
gdth_sense_data sd;
@@ -2389,7 +2389,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
case READ_CAPACITY:
TRACE2(("Read capacity hdrive %d\n",t));
- if (ha->hdr[t].size > (ulong64)0xffffffff)
+ if (ha->hdr[t].size > (u64)0xffffffff)
rdc.last_block_no = 0xffffffff;
else
rdc.last_block_no = cpu_to_be32(ha->hdr[t].size-1);
@@ -2425,12 +2425,12 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
return 0;
}
-static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
+static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u16 hdrive)
{
register gdth_cmd_str *cmdp;
struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- ulong32 cnt, blockcnt;
- ulong64 no, blockno;
+ u32 cnt, blockcnt;
+ u64 no, blockno;
int i, cmd_index, read_write, sgcnt, mode64;
cmdp = ha->pccb;
@@ -2498,17 +2498,17 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
if (read_write) {
if (scp->cmd_len == 16) {
- memcpy(&no, &scp->cmnd[2], sizeof(ulong64));
+ memcpy(&no, &scp->cmnd[2], sizeof(u64));
blockno = be64_to_cpu(no);
- memcpy(&cnt, &scp->cmnd[10], sizeof(ulong32));
+ memcpy(&cnt, &scp->cmnd[10], sizeof(u32));
blockcnt = be32_to_cpu(cnt);
} else if (scp->cmd_len == 10) {
- memcpy(&no, &scp->cmnd[2], sizeof(ulong32));
+ memcpy(&no, &scp->cmnd[2], sizeof(u32));
blockno = be32_to_cpu(no);
- memcpy(&cnt, &scp->cmnd[7], sizeof(ushort));
+ memcpy(&cnt, &scp->cmnd[7], sizeof(u16));
blockcnt = be16_to_cpu(cnt);
} else {
- memcpy(&no, &scp->cmnd[0], sizeof(ulong32));
+ memcpy(&no, &scp->cmnd[0], sizeof(u32));
blockno = be32_to_cpu(no) & 0x001fffffUL;
blockcnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
}
@@ -2516,7 +2516,7 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
cmdp->u.cache64.BlockNo = blockno;
cmdp->u.cache64.BlockCnt = blockcnt;
} else {
- cmdp->u.cache.BlockNo = (ulong32)blockno;
+ cmdp->u.cache.BlockNo = (u32)blockno;
cmdp->u.cache.BlockCnt = blockcnt;
}
@@ -2528,12 +2528,12 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
if (mode64) {
struct scatterlist *sl;
- cmdp->u.cache64.DestAddr= (ulong64)-1;
+ cmdp->u.cache64.DestAddr= (u64)-1;
cmdp->u.cache64.sg_canz = sgcnt;
scsi_for_each_sg(scp, sl, sgcnt, i) {
cmdp->u.cache64.sg_lst[i].sg_ptr = sg_dma_address(sl);
#ifdef GDTH_DMA_STATISTICS
- if (cmdp->u.cache64.sg_lst[i].sg_ptr > (ulong64)0xffffffff)
+ if (cmdp->u.cache64.sg_lst[i].sg_ptr > (u64)0xffffffff)
ha->dma64_cnt++;
else
ha->dma32_cnt++;
@@ -2555,8 +2555,8 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
}
#ifdef GDTH_STATISTICS
- if (max_sg < (ulong32)sgcnt) {
- max_sg = (ulong32)sgcnt;
+ if (max_sg < (u32)sgcnt) {
+ max_sg = (u32)sgcnt;
TRACE3(("GDT: max_sg = %d\n",max_sg));
}
#endif
@@ -2572,7 +2572,7 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
TRACE(("cache cmd: cmd %d blockno. %d, blockcnt %d\n",
cmdp->OpCode,cmdp->u.cache64.BlockNo,cmdp->u.cache64.BlockCnt));
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache64.sg_lst) +
- (ushort)cmdp->u.cache64.sg_canz * sizeof(gdth_sg64_str);
+ (u16)cmdp->u.cache64.sg_canz * sizeof(gdth_sg64_str);
} else {
TRACE(("cache cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
@@ -2581,7 +2581,7 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
TRACE(("cache cmd: cmd %d blockno. %d, blockcnt %d\n",
cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
- (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
+ (u16)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
}
if (ha->cmd_len & 3)
ha->cmd_len += (4 - (ha->cmd_len & 3));
@@ -2600,15 +2600,15 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive)
return cmd_index;
}
-static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b)
+static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 b)
{
register gdth_cmd_str *cmdp;
- ushort i;
+ u16 i;
dma_addr_t sense_paddr;
int cmd_index, sgcnt, mode64;
- unchar t,l;
+ u8 t,l;
struct page *page;
- ulong offset;
+ unsigned long offset;
struct gdth_cmndinfo *cmndinfo;
t = scp->device->id;
@@ -2654,7 +2654,7 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b)
} else {
page = virt_to_page(scp->sense_buffer);
- offset = (ulong)scp->sense_buffer & ~PAGE_MASK;
+ offset = (unsigned long)scp->sense_buffer & ~PAGE_MASK;
sense_paddr = pci_map_page(ha->pdev,page,offset,
16,PCI_DMA_FROMDEVICE);
@@ -2703,12 +2703,12 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b)
if (mode64) {
struct scatterlist *sl;
- cmdp->u.raw64.sdata = (ulong64)-1;
+ cmdp->u.raw64.sdata = (u64)-1;
cmdp->u.raw64.sg_ranz = sgcnt;
scsi_for_each_sg(scp, sl, sgcnt, i) {
cmdp->u.raw64.sg_lst[i].sg_ptr = sg_dma_address(sl);
#ifdef GDTH_DMA_STATISTICS
- if (cmdp->u.raw64.sg_lst[i].sg_ptr > (ulong64)0xffffffff)
+ if (cmdp->u.raw64.sg_lst[i].sg_ptr > (u64)0xffffffff)
ha->dma64_cnt++;
else
ha->dma32_cnt++;
@@ -2744,7 +2744,7 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b)
cmdp->u.raw64.sg_lst[0].sg_len));
/* evaluate command size */
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst) +
- (ushort)cmdp->u.raw64.sg_ranz * sizeof(gdth_sg64_str);
+ (u16)cmdp->u.raw64.sg_ranz * sizeof(gdth_sg64_str);
} else {
TRACE(("raw cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
@@ -2752,7 +2752,7 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b)
cmdp->u.raw.sg_lst[0].sg_len));
/* evaluate command size */
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
- (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
+ (u16)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
}
}
/* check space */
@@ -2802,7 +2802,7 @@ static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
if (cmdp->OpCode == GDT_IOCTL) {
TRACE2(("IOCTL\n"));
ha->cmd_len =
- GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong64);
+ GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(u64);
} else if (cmdp->Service == CACHESERVICE) {
TRACE2(("cache command %d\n",cmdp->OpCode));
if (ha->cache_feat & GDT_64BIT)
@@ -2840,8 +2840,8 @@ static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
/* Controller event handling functions */
-static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, ushort source,
- ushort idx, gdth_evt_data *evt)
+static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, u16 source,
+ u16 idx, gdth_evt_data *evt)
{
gdth_evt_str *e;
struct timeval tv;
@@ -2890,7 +2890,7 @@ static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr)
{
gdth_evt_str *e;
int eindex;
- ulong flags;
+ unsigned long flags;
TRACE2(("gdth_read_event() handle %d\n", handle));
spin_lock_irqsave(&ha->smp_lock, flags);
@@ -2919,12 +2919,12 @@ static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr)
}
static void gdth_readapp_event(gdth_ha_str *ha,
- unchar application, gdth_evt_str *estr)
+ u8 application, gdth_evt_str *estr)
{
gdth_evt_str *e;
int eindex;
- ulong flags;
- unchar found = FALSE;
+ unsigned long flags;
+ u8 found = FALSE;
TRACE2(("gdth_readapp_event() app. %d\n", application));
spin_lock_irqsave(&ha->smp_lock, flags);
@@ -2969,9 +2969,9 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
gdt2_dpram_str __iomem *dp2_ptr;
Scsi_Cmnd *scp;
int rval, i;
- unchar IStatus;
- ushort Service;
- ulong flags = 0;
+ u8 IStatus;
+ u16 Service;
+ unsigned long flags = 0;
#ifdef INT_COAL
int coalesced = FALSE;
int next = FALSE;
@@ -3018,7 +3018,7 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
if (coalesced) {
/* For coalesced requests all status
information is found in the status buffer */
- IStatus = (unchar)(pcs->status & 0xff);
+ IStatus = (u8)(pcs->status & 0xff);
}
#endif
@@ -3197,7 +3197,7 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
++act_int_coal;
if (act_int_coal > max_int_coal) {
max_int_coal = act_int_coal;
- printk("GDT: max_int_coal = %d\n",(ushort)max_int_coal);
+ printk("GDT: max_int_coal = %d\n",(u16)max_int_coal);
}
#endif
/* see if there is another status */
@@ -3225,12 +3225,12 @@ static irqreturn_t gdth_interrupt(int irq, void *dev_id)
return __gdth_interrupt(ha, false, NULL);
}
-static int gdth_sync_event(gdth_ha_str *ha, int service, unchar index,
+static int gdth_sync_event(gdth_ha_str *ha, int service, u8 index,
Scsi_Cmnd *scp)
{
gdth_msg_str *msg;
gdth_cmd_str *cmdp;
- unchar b, t;
+ u8 b, t;
struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
cmdp = ha->pccb;
@@ -3263,7 +3263,7 @@ static int gdth_sync_event(gdth_ha_str *ha, int service, unchar index,
cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
ha->cmd_offs_dpmem = 0;
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
- + sizeof(ulong64);
+ + sizeof(u64);
ha->cmd_cnt = 0;
gdth_copy_command(ha);
gdth_release_event(ha);
@@ -3297,7 +3297,7 @@ static int gdth_sync_event(gdth_ha_str *ha, int service, unchar index,
cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
ha->cmd_offs_dpmem = 0;
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
- + sizeof(ulong64);
+ + sizeof(u64);
ha->cmd_cnt = 0;
gdth_copy_command(ha);
gdth_release_event(ha);
@@ -3335,7 +3335,7 @@ static int gdth_sync_event(gdth_ha_str *ha, int service, unchar index,
cmndinfo->OpCode));
/* special commands GDT_CLUST_INFO/GDT_MOUNT ? */
if (cmndinfo->OpCode == GDT_CLUST_INFO) {
- ha->hdr[t].cluster_type = (unchar)ha->info;
+ ha->hdr[t].cluster_type = (u8)ha->info;
if (!(ha->hdr[t].cluster_type &
CLUSTER_MOUNTED)) {
/* NOT MOUNTED -> MOUNT */
@@ -3397,7 +3397,7 @@ static int gdth_sync_event(gdth_ha_str *ha, int service, unchar index,
ha->hdr[t].cluster_type &= ~CLUSTER_RESERVED;
}
memset((char*)scp->sense_buffer,0,16);
- if (ha->status == (ushort)S_CACHE_RESERV) {
+ if (ha->status == (u16)S_CACHE_RESERV) {
scp->result = (DID_OK << 16) | (RESERVATION_CONFLICT << 1);
} else {
scp->sense_buffer[0] = 0x70;
@@ -3614,16 +3614,16 @@ static int gdth_async_event(gdth_ha_str *ha)
cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
ha->cmd_offs_dpmem = 0;
ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
- + sizeof(ulong64);
+ + sizeof(u64);
ha->cmd_cnt = 0;
gdth_copy_command(ha);
if (ha->type == GDT_EISA)
- printk("[EISA slot %d] ",(ushort)ha->brd_phys);
+ printk("[EISA slot %d] ",(u16)ha->brd_phys);
else if (ha->type == GDT_ISA)
- printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys);
+ printk("[DPMEM 0x%4X] ",(u16)ha->brd_phys);
else
- printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8),
- (ushort)((ha->brd_phys>>3)&0x1f));
+ printk("[PCI %d/%d] ",(u16)(ha->brd_phys>>8),
+ (u16)((ha->brd_phys>>3)&0x1f));
gdth_release_event(ha);
}
@@ -3640,7 +3640,7 @@ static int gdth_async_event(gdth_ha_str *ha)
ha->dvr.eu.async.service = ha->service;
ha->dvr.eu.async.status = ha->status;
ha->dvr.eu.async.info = ha->info;
- *(ulong32 *)ha->dvr.eu.async.scsi_coord = ha->info2;
+ *(u32 *)ha->dvr.eu.async.scsi_coord = ha->info2;
}
gdth_store_event( ha, ES_ASYNC, ha->service, &ha->dvr );
gdth_log_event( &ha->dvr, NULL );
@@ -3648,8 +3648,8 @@ static int gdth_async_event(gdth_ha_str *ha)
/* new host drive from expand? */
if (ha->service == CACHESERVICE && ha->status == 56) {
TRACE2(("gdth_async_event(): new host drive %d created\n",
- (ushort)ha->info));
- /* gdth_analyse_hdrive(hanum, (ushort)ha->info); */
+ (u16)ha->info));
+ /* gdth_analyse_hdrive(hanum, (u16)ha->info); */
}
}
return 1;
@@ -3680,13 +3680,13 @@ static void gdth_log_event(gdth_evt_data *dvr, char *buffer)
for (j=0,i=1; i < f[0]; i+=2) {
switch (f[i+1]) {
case 4:
- stack.b[j++] = *(ulong32*)&dvr->eu.stream[(int)f[i]];
+ stack.b[j++] = *(u32*)&dvr->eu.stream[(int)f[i]];
break;
case 2:
- stack.b[j++] = *(ushort*)&dvr->eu.stream[(int)f[i]];
+ stack.b[j++] = *(u16*)&dvr->eu.stream[(int)f[i]];
break;
case 1:
- stack.b[j++] = *(unchar*)&dvr->eu.stream[(int)f[i]];
+ stack.b[j++] = *(u8*)&dvr->eu.stream[(int)f[i]];
break;
default:
break;
@@ -3712,14 +3712,14 @@ static void gdth_log_event(gdth_evt_data *dvr, char *buffer)
}
#ifdef GDTH_STATISTICS
-static unchar gdth_timer_running;
+static u8 gdth_timer_running;
-static void gdth_timeout(ulong data)
+static void gdth_timeout(unsigned long data)
{
- ulong32 i;
+ u32 i;
Scsi_Cmnd *nscp;
gdth_ha_str *ha;
- ulong flags;
+ unsigned long flags;
if(unlikely(list_empty(&gdth_instances))) {
gdth_timer_running = 0;
@@ -3891,8 +3891,8 @@ static enum blk_eh_timer_return gdth_timed_out(struct scsi_cmnd *scp)
{
gdth_ha_str *ha = shost_priv(scp->device->host);
struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- unchar b, t;
- ulong flags;
+ u8 b, t;
+ unsigned long flags;
enum blk_eh_timer_return retval = BLK_EH_NOT_HANDLED;
TRACE(("%s() cmd 0x%x\n", scp->cmnd[0], __func__));
@@ -3924,9 +3924,9 @@ static int gdth_eh_bus_reset(Scsi_Cmnd *scp)
{
gdth_ha_str *ha = shost_priv(scp->device->host);
int i;
- ulong flags;
+ unsigned long flags;
Scsi_Cmnd *cmnd;
- unchar b;
+ u8 b;
TRACE2(("gdth_eh_bus_reset()\n"));
@@ -3974,7 +3974,7 @@ static int gdth_eh_bus_reset(Scsi_Cmnd *scp)
static int gdth_bios_param(struct scsi_device *sdev,struct block_device *bdev,sector_t cap,int *ip)
{
- unchar b, t;
+ u8 b, t;
gdth_ha_str *ha = shost_priv(sdev->host);
struct scsi_device *sd;
unsigned capacity;
@@ -4062,7 +4062,7 @@ static int ioc_event(void __user *arg)
{
gdth_ioctl_event evt;
gdth_ha_str *ha;
- ulong flags;
+ unsigned long flags;
if (copy_from_user(&evt, arg, sizeof(gdth_ioctl_event)))
return -EFAULT;
@@ -4098,8 +4098,8 @@ static int ioc_event(void __user *arg)
static int ioc_lockdrv(void __user *arg)
{
gdth_ioctl_lockdrv ldrv;
- unchar i, j;
- ulong flags;
+ u8 i, j;
+ unsigned long flags;
gdth_ha_str *ha;
if (copy_from_user(&ldrv, arg, sizeof(gdth_ioctl_lockdrv)))
@@ -4165,7 +4165,7 @@ static int ioc_general(void __user *arg, char *cmnd)
{
gdth_ioctl_general gen;
char *buf = NULL;
- ulong64 paddr;
+ u64 paddr;
gdth_ha_str *ha;
int rval;
@@ -4194,7 +4194,7 @@ static int ioc_general(void __user *arg, char *cmnd)
gen.command.u.cache64.DeviceNo = gen.command.u.cache.DeviceNo;
/* addresses */
if (ha->cache_feat & SCATTER_GATHER) {
- gen.command.u.cache64.DestAddr = (ulong64)-1;
+ gen.command.u.cache64.DestAddr = (u64)-1;
gen.command.u.cache64.sg_canz = 1;
gen.command.u.cache64.sg_lst[0].sg_ptr = paddr;
gen.command.u.cache64.sg_lst[0].sg_len = gen.data_len;
@@ -4207,7 +4207,7 @@ static int ioc_general(void __user *arg, char *cmnd)
if (ha->cache_feat & SCATTER_GATHER) {
gen.command.u.cache.DestAddr = 0xffffffff;
gen.command.u.cache.sg_canz = 1;
- gen.command.u.cache.sg_lst[0].sg_ptr = (ulong32)paddr;
+ gen.command.u.cache.sg_lst[0].sg_ptr = (u32)paddr;
gen.command.u.cache.sg_lst[0].sg_len = gen.data_len;
gen.command.u.cache.sg_lst[1].sg_len = 0;
} else {
@@ -4230,7 +4230,7 @@ static int ioc_general(void __user *arg, char *cmnd)
gen.command.u.raw64.direction = gen.command.u.raw.direction;
/* addresses */
if (ha->raw_feat & SCATTER_GATHER) {
- gen.command.u.raw64.sdata = (ulong64)-1;
+ gen.command.u.raw64.sdata = (u64)-1;
gen.command.u.raw64.sg_ranz = 1;
gen.command.u.raw64.sg_lst[0].sg_ptr = paddr;
gen.command.u.raw64.sg_lst[0].sg_len = gen.data_len;
@@ -4244,14 +4244,14 @@ static int ioc_general(void __user *arg, char *cmnd)
if (ha->raw_feat & SCATTER_GATHER) {
gen.command.u.raw.sdata = 0xffffffff;
gen.command.u.raw.sg_ranz = 1;
- gen.command.u.raw.sg_lst[0].sg_ptr = (ulong32)paddr;
+ gen.command.u.raw.sg_lst[0].sg_ptr = (u32)paddr;
gen.command.u.raw.sg_lst[0].sg_len = gen.data_len;
gen.command.u.raw.sg_lst[1].sg_len = 0;
} else {
gen.command.u.raw.sdata = paddr;
gen.command.u.raw.sg_ranz = 0;
}
- gen.command.u.raw.sense_data = (ulong32)paddr + gen.data_len;
+ gen.command.u.raw.sense_data = (u32)paddr + gen.data_len;
}
} else {
gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
@@ -4283,7 +4283,7 @@ static int ioc_hdrlist(void __user *arg, char *cmnd)
gdth_ioctl_rescan *rsc;
gdth_cmd_str *cmd;
gdth_ha_str *ha;
- unchar i;
+ u8 i;
int rc = -ENOMEM;
u32 cluster_type = 0;
@@ -4335,11 +4335,11 @@ static int ioc_rescan(void __user *arg, char *cmnd)
{
gdth_ioctl_rescan *rsc;
gdth_cmd_str *cmd;
- ushort i, status, hdr_cnt;
- ulong32 info;
+ u16 i, status, hdr_cnt;
+ u32 info;
int cyls, hds, secs;
int rc = -ENOMEM;
- ulong flags;
+ unsigned long flags;
gdth_ha_str *ha;
rsc = kmalloc(sizeof(*rsc), GFP_KERNEL);
@@ -4367,7 +4367,7 @@ static int ioc_rescan(void __user *arg, char *cmnd)
status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
i = 0;
- hdr_cnt = (status == S_OK ? (ushort)info : 0);
+ hdr_cnt = (status == S_OK ? (u16)info : 0);
} else {
i = rsc->hdr_no;
hdr_cnt = i + 1;
@@ -4418,7 +4418,7 @@ static int ioc_rescan(void __user *arg, char *cmnd)
status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[i].devtype = (status == S_OK ? (ushort)info : 0);
+ ha->hdr[i].devtype = (status == S_OK ? (u16)info : 0);
spin_unlock_irqrestore(&ha->smp_lock, flags);
cmd->Service = CACHESERVICE;
@@ -4432,7 +4432,7 @@ static int ioc_rescan(void __user *arg, char *cmnd)
spin_lock_irqsave(&ha->smp_lock, flags);
ha->hdr[i].cluster_type =
- ((status == S_OK && !shared_access) ? (ushort)info : 0);
+ ((status == S_OK && !shared_access) ? (u16)info : 0);
spin_unlock_irqrestore(&ha->smp_lock, flags);
rsc->hdr_list[i].cluster_type = ha->hdr[i].cluster_type;
@@ -4446,7 +4446,7 @@ static int ioc_rescan(void __user *arg, char *cmnd)
status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[i].rw_attribs = (status == S_OK ? (ushort)info : 0);
+ ha->hdr[i].rw_attribs = (status == S_OK ? (u16)info : 0);
spin_unlock_irqrestore(&ha->smp_lock, flags);
}
@@ -4466,7 +4466,7 @@ static int gdth_ioctl(struct inode *inode, struct file *filep,
{
gdth_ha_str *ha;
Scsi_Cmnd *scp;
- ulong flags;
+ unsigned long flags;
char cmnd[MAX_COMMAND_SIZE];
void __user *argp = (void __user *)arg;
@@ -4495,9 +4495,9 @@ static int gdth_ioctl(struct inode *inode, struct file *filep,
{
gdth_ioctl_osvers osv;
- osv.version = (unchar)(LINUX_VERSION_CODE >> 16);
- osv.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
- osv.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+ osv.version = (u8)(LINUX_VERSION_CODE >> 16);
+ osv.subversion = (u8)(LINUX_VERSION_CODE >> 8);
+ osv.revision = (u16)(LINUX_VERSION_CODE & 0xff);
if (copy_to_user(argp, &osv, sizeof(gdth_ioctl_osvers)))
return -EFAULT;
break;
@@ -4512,10 +4512,10 @@ static int gdth_ioctl(struct inode *inode, struct file *filep,
return -EFAULT;
if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
- ctrt.type = (unchar)((ha->stype>>20) - 0x10);
+ ctrt.type = (u8)((ha->stype>>20) - 0x10);
} else {
if (ha->type != GDT_PCIMPR) {
- ctrt.type = (unchar)((ha->stype<<4) + 6);
+ ctrt.type = (u8)((ha->stype<<4) + 6);
} else {
ctrt.type =
(ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe);
@@ -4546,7 +4546,7 @@ static int gdth_ioctl(struct inode *inode, struct file *filep,
case GDTIOCTL_LOCKCHN:
{
gdth_ioctl_lockchn lchn;
- unchar i, j;
+ u8 i, j;
if (copy_from_user(&lchn, argp, sizeof(gdth_ioctl_lockchn)) ||
(NULL == (ha = gdth_find_ha(lchn.ionode))))
@@ -4670,7 +4670,7 @@ static struct scsi_host_template gdth_template = {
};
#ifdef CONFIG_ISA
-static int __init gdth_isa_probe_one(ulong32 isa_bios)
+static int __init gdth_isa_probe_one(u32 isa_bios)
{
struct Scsi_Host *shp;
gdth_ha_str *ha;
@@ -4802,7 +4802,7 @@ static int __init gdth_isa_probe_one(ulong32 isa_bios)
#endif /* CONFIG_ISA */
#ifdef CONFIG_EISA
-static int __init gdth_eisa_probe_one(ushort eisa_slot)
+static int __init gdth_eisa_probe_one(u16 eisa_slot)
{
struct Scsi_Host *shp;
gdth_ha_str *ha;
@@ -5120,7 +5120,7 @@ static void gdth_remove_one(gdth_ha_str *ha)
scsi_host_put(shp);
}
-static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+static int gdth_halt(struct notifier_block *nb, unsigned long event, void *buf)
{
gdth_ha_str *ha;
@@ -5158,14 +5158,14 @@ static int __init gdth_init(void)
if (probe_eisa_isa) {
/* scanning for controllers, at first: ISA controller */
#ifdef CONFIG_ISA
- ulong32 isa_bios;
+ u32 isa_bios;
for (isa_bios = 0xc8000UL; isa_bios <= 0xd8000UL;
isa_bios += 0x8000UL)
gdth_isa_probe_one(isa_bios);
#endif
#ifdef CONFIG_EISA
{
- ushort eisa_slot;
+ u16 eisa_slot;
for (eisa_slot = 0x1000; eisa_slot <= 0x8000;
eisa_slot += 0x1000)
gdth_eisa_probe_one(eisa_slot);
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
index 1646444e9bd..120a0625a7b 100644
--- a/drivers/scsi/gdth.h
+++ b/drivers/scsi/gdth.h
@@ -321,524 +321,524 @@
/* screenservice message */
typedef struct {
- ulong32 msg_handle; /* message handle */
- ulong32 msg_len; /* size of message */
- ulong32 msg_alen; /* answer length */
- unchar msg_answer; /* answer flag */
- unchar msg_ext; /* more messages */
- unchar msg_reserved[2];
+ u32 msg_handle; /* message handle */
+ u32 msg_len; /* size of message */
+ u32 msg_alen; /* answer length */
+ u8 msg_answer; /* answer flag */
+ u8 msg_ext; /* more messages */
+ u8 msg_reserved[2];
char msg_text[MSGLEN+2]; /* the message text */
-} PACKED gdth_msg_str;
+} __attribute__((packed)) gdth_msg_str;
/* IOCTL data structures */
/* Status coalescing buffer for returning multiple requests per interrupt */
typedef struct {
- ulong32 status;
- ulong32 ext_status;
- ulong32 info0;
- ulong32 info1;
-} PACKED gdth_coal_status;
+ u32 status;
+ u32 ext_status;
+ u32 info0;
+ u32 info1;
+} __attribute__((packed)) gdth_coal_status;
/* performance mode data structure */
typedef struct {
- ulong32 version; /* The version of this IOCTL structure. */
- ulong32 st_mode; /* 0=dis., 1=st_buf_addr1 valid, 2=both */
- ulong32 st_buff_addr1; /* physical address of status buffer 1 */
- ulong32 st_buff_u_addr1; /* reserved for 64 bit addressing */
- ulong32 st_buff_indx1; /* reserved command idx. for this buffer */
- ulong32 st_buff_addr2; /* physical address of status buffer 1 */
- ulong32 st_buff_u_addr2; /* reserved for 64 bit addressing */
- ulong32 st_buff_indx2; /* reserved command idx. for this buffer */
- ulong32 st_buff_size; /* size of each buffer in bytes */
- ulong32 cmd_mode; /* 0 = mode disabled, 1 = cmd_buff_addr1 */
- ulong32 cmd_buff_addr1; /* physical address of cmd buffer 1 */
- ulong32 cmd_buff_u_addr1; /* reserved for 64 bit addressing */
- ulong32 cmd_buff_indx1; /* cmd buf addr1 unique identifier */
- ulong32 cmd_buff_addr2; /* physical address of cmd buffer 1 */
- ulong32 cmd_buff_u_addr2; /* reserved for 64 bit addressing */
- ulong32 cmd_buff_indx2; /* cmd buf addr1 unique identifier */
- ulong32 cmd_buff_size; /* size of each cmd bufer in bytes */
- ulong32 reserved1;
- ulong32 reserved2;
-} PACKED gdth_perf_modes;
+ u32 version; /* The version of this IOCTL structure. */
+ u32 st_mode; /* 0=dis., 1=st_buf_addr1 valid, 2=both */
+ u32 st_buff_addr1; /* physical address of status buffer 1 */
+ u32 st_buff_u_addr1; /* reserved for 64 bit addressing */
+ u32 st_buff_indx1; /* reserved command idx. for this buffer */
+ u32 st_buff_addr2; /* physical address of status buffer 1 */
+ u32 st_buff_u_addr2; /* reserved for 64 bit addressing */
+ u32 st_buff_indx2; /* reserved command idx. for this buffer */
+ u32 st_buff_size; /* size of each buffer in bytes */
+ u32 cmd_mode; /* 0 = mode disabled, 1 = cmd_buff_addr1 */
+ u32 cmd_buff_addr1; /* physical address of cmd buffer 1 */
+ u32 cmd_buff_u_addr1; /* reserved for 64 bit addressing */
+ u32 cmd_buff_indx1; /* cmd buf addr1 unique identifier */
+ u32 cmd_buff_addr2; /* physical address of cmd buffer 1 */
+ u32 cmd_buff_u_addr2; /* reserved for 64 bit addressing */
+ u32 cmd_buff_indx2; /* cmd buf addr1 unique identifier */
+ u32 cmd_buff_size; /* size of each cmd bufer in bytes */
+ u32 reserved1;
+ u32 reserved2;
+} __attribute__((packed)) gdth_perf_modes;
/* SCSI drive info */
typedef struct {
- unchar vendor[8]; /* vendor string */
- unchar product[16]; /* product string */
- unchar revision[4]; /* revision */
- ulong32 sy_rate; /* current rate for sync. tr. */
- ulong32 sy_max_rate; /* max. rate for sync. tr. */
- ulong32 no_ldrive; /* belongs to this log. drv.*/
- ulong32 blkcnt; /* number of blocks */
- ushort blksize; /* size of block in bytes */
- unchar available; /* flag: access is available */
- unchar init; /* medium is initialized */
- unchar devtype; /* SCSI devicetype */
- unchar rm_medium; /* medium is removable */
- unchar wp_medium; /* medium is write protected */
- unchar ansi; /* SCSI I/II or III? */
- unchar protocol; /* same as ansi */
- unchar sync; /* flag: sync. transfer enab. */
- unchar disc; /* flag: disconnect enabled */
- unchar queueing; /* flag: command queing enab. */
- unchar cached; /* flag: caching enabled */
- unchar target_id; /* target ID of device */
- unchar lun; /* LUN id of device */
- unchar orphan; /* flag: drive fragment */
- ulong32 last_error; /* sense key or drive state */
- ulong32 last_result; /* result of last command */
- ulong32 check_errors; /* err. in last surface check */
- unchar percent; /* progress for surface check */
- unchar last_check; /* IOCTRL operation */
- unchar res[2];
- ulong32 flags; /* from 1.19/2.19: raw reserv.*/
- unchar multi_bus; /* multi bus dev? (fibre ch.) */
- unchar mb_status; /* status: available? */
- unchar res2[2];
- unchar mb_alt_status; /* status on second bus */
- unchar mb_alt_bid; /* number of second bus */
- unchar mb_alt_tid; /* target id on second bus */
- unchar res3;
- unchar fc_flag; /* from 1.22/2.22: info valid?*/
- unchar res4;
- ushort fc_frame_size; /* frame size (bytes) */
+ u8 vendor[8]; /* vendor string */
+ u8 product[16]; /* product string */
+ u8 revision[4]; /* revision */
+ u32 sy_rate; /* current rate for sync. tr. */
+ u32 sy_max_rate; /* max. rate for sync. tr. */
+ u32 no_ldrive; /* belongs to this log. drv.*/
+ u32 blkcnt; /* number of blocks */
+ u16 blksize; /* size of block in bytes */
+ u8 available; /* flag: access is available */
+ u8 init; /* medium is initialized */
+ u8 devtype; /* SCSI devicetype */
+ u8 rm_medium; /* medium is removable */
+ u8 wp_medium; /* medium is write protected */
+ u8 ansi; /* SCSI I/II or III? */
+ u8 protocol; /* same as ansi */
+ u8 sync; /* flag: sync. transfer enab. */
+ u8 disc; /* flag: disconnect enabled */
+ u8 queueing; /* flag: command queing enab. */
+ u8 cached; /* flag: caching enabled */
+ u8 target_id; /* target ID of device */
+ u8 lun; /* LUN id of device */
+ u8 orphan; /* flag: drive fragment */
+ u32 last_error; /* sense key or drive state */
+ u32 last_result; /* result of last command */
+ u32 check_errors; /* err. in last surface check */
+ u8 percent; /* progress for surface check */
+ u8 last_check; /* IOCTRL operation */
+ u8 res[2];
+ u32 flags; /* from 1.19/2.19: raw reserv.*/
+ u8 multi_bus; /* multi bus dev? (fibre ch.) */
+ u8 mb_status; /* status: available? */
+ u8 res2[2];
+ u8 mb_alt_status; /* status on second bus */
+ u8 mb_alt_bid; /* number of second bus */
+ u8 mb_alt_tid; /* target id on second bus */
+ u8 res3;
+ u8 fc_flag; /* from 1.22/2.22: info valid?*/
+ u8 res4;
+ u16 fc_frame_size; /* frame size (bytes) */
char wwn[8]; /* world wide name */
-} PACKED gdth_diskinfo_str;
+} __attribute__((packed)) gdth_diskinfo_str;
/* get SCSI channel count */
typedef struct {
- ulong32 channel_no; /* number of channel */
- ulong32 drive_cnt; /* drive count */
- unchar siop_id; /* SCSI processor ID */
- unchar siop_state; /* SCSI processor state */
-} PACKED gdth_getch_str;
+ u32 channel_no; /* number of channel */
+ u32 drive_cnt; /* drive count */
+ u8 siop_id; /* SCSI processor ID */
+ u8 siop_state; /* SCSI processor state */
+} __attribute__((packed)) gdth_getch_str;
/* get SCSI drive numbers */
typedef struct {
- ulong32 sc_no; /* SCSI channel */
- ulong32 sc_cnt; /* sc_list[] elements */
- ulong32 sc_list[MAXID]; /* minor device numbers */
-} PACKED gdth_drlist_str;
+ u32 sc_no; /* SCSI channel */
+ u32 sc_cnt; /* sc_list[] elements */
+ u32 sc_list[MAXID]; /* minor device numbers */
+} __attribute__((packed)) gdth_drlist_str;
/* get grown/primary defect count */
typedef struct {
- unchar sddc_type; /* 0x08: grown, 0x10: prim. */
- unchar sddc_format; /* list entry format */
- unchar sddc_len; /* list entry length */
- unchar sddc_res;
- ulong32 sddc_cnt; /* entry count */
-} PACKED gdth_defcnt_str;
+ u8 sddc_type; /* 0x08: grown, 0x10: prim. */
+ u8 sddc_format; /* list entry format */
+ u8 sddc_len; /* list entry length */
+ u8 sddc_res;
+ u32 sddc_cnt; /* entry count */
+} __attribute__((packed)) gdth_defcnt_str;
/* disk statistics */
typedef struct {
- ulong32 bid; /* SCSI channel */
- ulong32 first; /* first SCSI disk */
- ulong32 entries; /* number of elements */
- ulong32 count; /* (R) number of init. el. */
- ulong32 mon_time; /* time stamp */
+ u32 bid; /* SCSI channel */
+ u32 first; /* first SCSI disk */
+ u32 entries; /* number of elements */
+ u32 count; /* (R) number of init. el. */
+ u32 mon_time; /* time stamp */
struct {
- unchar tid; /* target ID */
- unchar lun; /* LUN */
- unchar res[2];
- ulong32 blk_size; /* block size in bytes */
- ulong32 rd_count; /* bytes read */
- ulong32 wr_count; /* bytes written */
- ulong32 rd_blk_count; /* blocks read */
- ulong32 wr_blk_count; /* blocks written */
- ulong32 retries; /* retries */
- ulong32 reassigns; /* reassigns */
- } PACKED list[1];
-} PACKED gdth_dskstat_str;
+ u8 tid; /* target ID */
+ u8 lun; /* LUN */
+ u8 res[2];
+ u32 blk_size; /* block size in bytes */
+ u32 rd_count; /* bytes read */
+ u32 wr_count; /* bytes written */
+ u32 rd_blk_count; /* blocks read */
+ u32 wr_blk_count; /* blocks written */
+ u32 retries; /* retries */
+ u32 reassigns; /* reassigns */
+ } __attribute__((packed)) list[1];
+} __attribute__((packed)) gdth_dskstat_str;
/* IO channel header */
typedef struct {
- ulong32 version; /* version (-1UL: newest) */
- unchar list_entries; /* list entry count */
- unchar first_chan; /* first channel number */
- unchar last_chan; /* last channel number */
- unchar chan_count; /* (R) channel count */
- ulong32 list_offset; /* offset of list[0] */
-} PACKED gdth_iochan_header;
+ u32 version; /* version (-1UL: newest) */
+ u8 list_entries; /* list entry count */
+ u8 first_chan; /* first channel number */
+ u8 last_chan; /* last channel number */
+ u8 chan_count; /* (R) channel count */
+ u32 list_offset; /* offset of list[0] */
+} __attribute__((packed)) gdth_iochan_header;
/* get IO channel description */
typedef struct {
gdth_iochan_header hdr;
struct {
- ulong32 address; /* channel address */
- unchar type; /* type (SCSI, FCAL) */
- unchar local_no; /* local number */
- ushort features; /* channel features */
- } PACKED list[MAXBUS];
-} PACKED gdth_iochan_str;
+ u32 address; /* channel address */
+ u8 type; /* type (SCSI, FCAL) */
+ u8 local_no; /* local number */
+ u16 features; /* channel features */
+ } __attribute__((packed)) list[MAXBUS];
+} __attribute__((packed)) gdth_iochan_str;
/* get raw IO channel description */
typedef struct {
gdth_iochan_header hdr;
struct {
- unchar proc_id; /* processor id */
- unchar proc_defect; /* defect ? */
- unchar reserved[2];
- } PACKED list[MAXBUS];
-} PACKED gdth_raw_iochan_str;
+ u8 proc_id; /* processor id */
+ u8 proc_defect; /* defect ? */
+ u8 reserved[2];
+ } __attribute__((packed)) list[MAXBUS];
+} __attribute__((packed)) gdth_raw_iochan_str;
/* array drive component */
typedef struct {
- ulong32 al_controller; /* controller ID */
- unchar al_cache_drive; /* cache drive number */
- unchar al_status; /* cache drive state */
- unchar al_res[2];
-} PACKED gdth_arraycomp_str;
+ u32 al_controller; /* controller ID */
+ u8 al_cache_drive; /* cache drive number */
+ u8 al_status; /* cache drive state */
+ u8 al_res[2];
+} __attribute__((packed)) gdth_arraycomp_str;
/* array drive information */
typedef struct {
- unchar ai_type; /* array type (RAID0,4,5) */
- unchar ai_cache_drive_cnt; /* active cachedrives */
- unchar ai_state; /* array drive state */
- unchar ai_master_cd; /* master cachedrive */
- ulong32 ai_master_controller; /* ID of master controller */
- ulong32 ai_size; /* user capacity [sectors] */
- ulong32 ai_striping_size; /* striping size [sectors] */
- ulong32 ai_secsize; /* sector size [bytes] */
- ulong32 ai_err_info; /* failed cache drive */
- unchar ai_name[8]; /* name of the array drive */
- unchar ai_controller_cnt; /* number of controllers */
- unchar ai_removable; /* flag: removable */
- unchar ai_write_protected; /* flag: write protected */
- unchar ai_devtype; /* type: always direct access */
+ u8 ai_type; /* array type (RAID0,4,5) */
+ u8 ai_cache_drive_cnt; /* active cachedrives */
+ u8 ai_state; /* array drive state */
+ u8 ai_master_cd; /* master cachedrive */
+ u32 ai_master_controller; /* ID of master controller */
+ u32 ai_size; /* user capacity [sectors] */
+ u32 ai_striping_size; /* striping size [sectors] */
+ u32 ai_secsize; /* sector size [bytes] */
+ u32 ai_err_info; /* failed cache drive */
+ u8 ai_name[8]; /* name of the array drive */
+ u8 ai_controller_cnt; /* number of controllers */
+ u8 ai_removable; /* flag: removable */
+ u8 ai_write_protected; /* flag: write protected */
+ u8 ai_devtype; /* type: always direct access */
gdth_arraycomp_str ai_drives[35]; /* drive components: */
- unchar ai_drive_entries; /* number of drive components */
- unchar ai_protected; /* protection flag */
- unchar ai_verify_state; /* state of a parity verify */
- unchar ai_ext_state; /* extended array drive state */
- unchar ai_expand_state; /* array expand state (>=2.18)*/
- unchar ai_reserved[3];
-} PACKED gdth_arrayinf_str;
+ u8 ai_drive_entries; /* number of drive components */
+ u8 ai_protected; /* protection flag */
+ u8 ai_verify_state; /* state of a parity verify */
+ u8 ai_ext_state; /* extended array drive state */
+ u8 ai_expand_state; /* array expand state (>=2.18)*/
+ u8 ai_reserved[3];
+} __attribute__((packed)) gdth_arrayinf_str;
/* get array drive list */
typedef struct {
- ulong32 controller_no; /* controller no. */
- unchar cd_handle; /* master cachedrive */
- unchar is_arrayd; /* Flag: is array drive? */
- unchar is_master; /* Flag: is array master? */
- unchar is_parity; /* Flag: is parity drive? */
- unchar is_hotfix; /* Flag: is hotfix drive? */
- unchar res[3];
-} PACKED gdth_alist_str;
+ u32 controller_no; /* controller no. */
+ u8 cd_handle; /* master cachedrive */
+ u8 is_arrayd; /* Flag: is array drive? */
+ u8 is_master; /* Flag: is array master? */
+ u8 is_parity; /* Flag: is parity drive? */
+ u8 is_hotfix; /* Flag: is hotfix drive? */
+ u8 res[3];
+} __attribute__((packed)) gdth_alist_str;
typedef struct {
- ulong32 entries_avail; /* allocated entries */
- ulong32 entries_init; /* returned entries */
- ulong32 first_entry; /* first entry number */
- ulong32 list_offset; /* offset of following list */
+ u32 entries_avail; /* allocated entries */
+ u32 entries_init; /* returned entries */
+ u32 first_entry; /* first entry number */
+ u32 list_offset; /* offset of following list */
gdth_alist_str list[1]; /* list */
-} PACKED gdth_arcdl_str;
+} __attribute__((packed)) gdth_arcdl_str;
/* cache info/config IOCTL */
typedef struct {
- ulong32 version; /* firmware version */
- ushort state; /* cache state (on/off) */
- ushort strategy; /* cache strategy */
- ushort write_back; /* write back state (on/off) */
- ushort block_size; /* cache block size */
-} PACKED gdth_cpar_str;
+ u32 version; /* firmware version */
+ u16 state; /* cache state (on/off) */
+ u16 strategy; /* cache strategy */
+ u16 write_back; /* write back state (on/off) */
+ u16 block_size; /* cache block size */
+} __attribute__((packed)) gdth_cpar_str;
typedef struct {
- ulong32 csize; /* cache size */
- ulong32 read_cnt; /* read/write counter */
- ulong32 write_cnt;
- ulong32 tr_hits; /* hits */
- ulong32 sec_hits;
- ulong32 sec_miss; /* misses */
-} PACKED gdth_cstat_str;
+ u32 csize; /* cache size */
+ u32 read_cnt; /* read/write counter */
+ u32 write_cnt;
+ u32 tr_hits; /* hits */
+ u32 sec_hits;
+ u32 sec_miss; /* misses */
+} __attribute__((packed)) gdth_cstat_str;
typedef struct {
gdth_cpar_str cpar;
gdth_cstat_str cstat;
-} PACKED gdth_cinfo_str;
+} __attribute__((packed)) gdth_cinfo_str;
/* cache drive info */
typedef struct {
- unchar cd_name[8]; /* cache drive name */
- ulong32 cd_devtype; /* SCSI devicetype */
- ulong32 cd_ldcnt; /* number of log. drives */
- ulong32 cd_last_error; /* last error */
- unchar cd_initialized; /* drive is initialized */
- unchar cd_removable; /* media is removable */
- unchar cd_write_protected; /* write protected */
- unchar cd_flags; /* Pool Hot Fix? */
- ulong32 ld_blkcnt; /* number of blocks */
- ulong32 ld_blksize; /* blocksize */
- ulong32 ld_dcnt; /* number of disks */
- ulong32 ld_slave; /* log. drive index */
- ulong32 ld_dtype; /* type of logical drive */
- ulong32 ld_last_error; /* last error */
- unchar ld_name[8]; /* log. drive name */
- unchar ld_error; /* error */
-} PACKED gdth_cdrinfo_str;
+ u8 cd_name[8]; /* cache drive name */
+ u32 cd_devtype; /* SCSI devicetype */
+ u32 cd_ldcnt; /* number of log. drives */
+ u32 cd_last_error; /* last error */
+ u8 cd_initialized; /* drive is initialized */
+ u8 cd_removable; /* media is removable */
+ u8 cd_write_protected; /* write protected */
+ u8 cd_flags; /* Pool Hot Fix? */
+ u32 ld_blkcnt; /* number of blocks */
+ u32 ld_blksize; /* blocksize */
+ u32 ld_dcnt; /* number of disks */
+ u32 ld_slave; /* log. drive index */
+ u32 ld_dtype; /* type of logical drive */
+ u32 ld_last_error; /* last error */
+ u8 ld_name[8]; /* log. drive name */
+ u8 ld_error; /* error */
+} __attribute__((packed)) gdth_cdrinfo_str;
/* OEM string */
typedef struct {
- ulong32 ctl_version;
- ulong32 file_major_version;
- ulong32 file_minor_version;
- ulong32 buffer_size;
- ulong32 cpy_count;
- ulong32 ext_error;
- ulong32 oem_id;
- ulong32 board_id;
-} PACKED gdth_oem_str_params;
-
-typedef struct {
- unchar product_0_1_name[16];
- unchar product_4_5_name[16];
- unchar product_cluster_name[16];
- unchar product_reserved[16];
- unchar scsi_cluster_target_vendor_id[16];
- unchar cluster_raid_fw_name[16];
- unchar oem_brand_name[16];
- unchar oem_raid_type[16];
- unchar bios_type[13];
- unchar bios_title[50];
- unchar oem_company_name[37];
- ulong32 pci_id_1;
- ulong32 pci_id_2;
- unchar validation_status[80];
- unchar reserved_1[4];
- unchar scsi_host_drive_inquiry_vendor_id[16];
- unchar library_file_template[16];
- unchar reserved_2[16];
- unchar tool_name_1[32];
- unchar tool_name_2[32];
- unchar tool_name_3[32];
- unchar oem_contact_1[84];
- unchar oem_contact_2[84];
- unchar oem_contact_3[84];
-} PACKED gdth_oem_str;
+ u32 ctl_version;
+ u32 file_major_version;
+ u32 file_minor_version;
+ u32 buffer_size;
+ u32 cpy_count;
+ u32 ext_error;
+ u32 oem_id;
+ u32 board_id;
+} __attribute__((packed)) gdth_oem_str_params;
+
+typedef struct {
+ u8 product_0_1_name[16];
+ u8 product_4_5_name[16];
+ u8 product_cluster_name[16];
+ u8 product_reserved[16];
+ u8 scsi_cluster_target_vendor_id[16];
+ u8 cluster_raid_fw_name[16];
+ u8 oem_brand_name[16];
+ u8 oem_raid_type[16];
+ u8 bios_type[13];
+ u8 bios_title[50];
+ u8 oem_company_name[37];
+ u32 pci_id_1;
+ u32 pci_id_2;
+ u8 validation_status[80];
+ u8 reserved_1[4];
+ u8 scsi_host_drive_inquiry_vendor_id[16];
+ u8 library_file_template[16];
+ u8 reserved_2[16];
+ u8 tool_name_1[32];
+ u8 tool_name_2[32];
+ u8 tool_name_3[32];
+ u8 oem_contact_1[84];
+ u8 oem_contact_2[84];
+ u8 oem_contact_3[84];
+} __attribute__((packed)) gdth_oem_str;
typedef struct {
gdth_oem_str_params params;
gdth_oem_str text;
-} PACKED gdth_oem_str_ioctl;
+} __attribute__((packed)) gdth_oem_str_ioctl;
/* board features */
typedef struct {
- unchar chaining; /* Chaining supported */
- unchar striping; /* Striping (RAID-0) supp. */
- unchar mirroring; /* Mirroring (RAID-1) supp. */
- unchar raid; /* RAID-4/5/10 supported */
-} PACKED gdth_bfeat_str;
+ u8 chaining; /* Chaining supported */
+ u8 striping; /* Striping (RAID-0) supp. */
+ u8 mirroring; /* Mirroring (RAID-1) supp. */
+ u8 raid; /* RAID-4/5/10 supported */
+} __attribute__((packed)) gdth_bfeat_str;
/* board info IOCTL */
typedef struct {
- ulong32 ser_no; /* serial no. */
- unchar oem_id[2]; /* OEM ID */
- ushort ep_flags; /* eprom flags */
- ulong32 proc_id; /* processor ID */
- ulong32 memsize; /* memory size (bytes) */
- unchar mem_banks; /* memory banks */
- unchar chan_type; /* channel type */
- unchar chan_count; /* channel count */
- unchar rdongle_pres; /* dongle present? */
- ulong32 epr_fw_ver; /* (eprom) firmware version */
- ulong32 upd_fw_ver; /* (update) firmware version */
- ulong32 upd_revision; /* update revision */
+ u32 ser_no; /* serial no. */
+ u8 oem_id[2]; /* OEM ID */
+ u16 ep_flags; /* eprom flags */
+ u32 proc_id; /* processor ID */
+ u32 memsize; /* memory size (bytes) */
+ u8 mem_banks; /* memory banks */
+ u8 chan_type; /* channel type */
+ u8 chan_count; /* channel count */
+ u8 rdongle_pres; /* dongle present? */
+ u32 epr_fw_ver; /* (eprom) firmware version */
+ u32 upd_fw_ver; /* (update) firmware version */
+ u32 upd_revision; /* update revision */
char type_string[16]; /* controller name */
char raid_string[16]; /* RAID firmware name */
- unchar update_pres; /* update present? */
- unchar xor_pres; /* XOR engine present? */
- unchar prom_type; /* ROM type (eprom/flash) */
- unchar prom_count; /* number of ROM devices */
- ulong32 dup_pres; /* duplexing module present? */
- ulong32 chan_pres; /* number of expansion chn. */
- ulong32 mem_pres; /* memory expansion inst. ? */
- unchar ft_bus_system; /* fault bus supported? */
- unchar subtype_valid; /* board_subtype valid? */
- unchar board_subtype; /* subtype/hardware level */
- unchar ramparity_pres; /* RAM parity check hardware? */
-} PACKED gdth_binfo_str;
+ u8 update_pres; /* update present? */
+ u8 xor_pres; /* XOR engine present? */
+ u8 prom_type; /* ROM type (eprom/flash) */
+ u8 prom_count; /* number of ROM devices */
+ u32 dup_pres; /* duplexing module present? */
+ u32 chan_pres; /* number of expansion chn. */
+ u32 mem_pres; /* memory expansion inst. ? */
+ u8 ft_bus_system; /* fault bus supported? */
+ u8 subtype_valid; /* board_subtype valid? */
+ u8 board_subtype; /* subtype/hardware level */
+ u8 ramparity_pres; /* RAM parity check hardware? */
+} __attribute__((packed)) gdth_binfo_str;
/* get host drive info */
typedef struct {
char name[8]; /* host drive name */
- ulong32 size; /* size (sectors) */
- unchar host_drive; /* host drive number */
- unchar log_drive; /* log. drive (master) */
- unchar reserved;
- unchar rw_attribs; /* r/w attribs */
- ulong32 start_sec; /* start sector */
-} PACKED gdth_hentry_str;
-
-typedef struct {
- ulong32 entries; /* entry count */
- ulong32 offset; /* offset of entries */
- unchar secs_p_head; /* sectors/head */
- unchar heads_p_cyl; /* heads/cylinder */
- unchar reserved;
- unchar clust_drvtype; /* cluster drive type */
- ulong32 location; /* controller number */
+ u32 size; /* size (sectors) */
+ u8 host_drive; /* host drive number */
+ u8 log_drive; /* log. drive (master) */
+ u8 reserved;
+ u8 rw_attribs; /* r/w attribs */
+ u32 start_sec; /* start sector */
+} __attribute__((packed)) gdth_hentry_str;
+
+typedef struct {
+ u32 entries; /* entry count */
+ u32 offset; /* offset of entries */
+ u8 secs_p_head; /* sectors/head */
+ u8 heads_p_cyl; /* heads/cylinder */
+ u8 reserved;
+ u8 clust_drvtype; /* cluster drive type */
+ u32 location; /* controller number */
gdth_hentry_str entry[MAX_HDRIVES]; /* entries */
-} PACKED gdth_hget_str;
+} __attribute__((packed)) gdth_hget_str;
/* DPRAM structures */
/* interface area ISA/PCI */
typedef struct {
- unchar S_Cmd_Indx; /* special command */
- unchar volatile S_Status; /* status special command */
- ushort reserved1;
- ulong32 S_Info[4]; /* add. info special command */
- unchar volatile Sema0; /* command semaphore */
- unchar reserved2[3];
- unchar Cmd_Index; /* command number */
- unchar reserved3[3];
- ushort volatile Status; /* command status */
- ushort Service; /* service(for async.events) */
- ulong32 Info[2]; /* additional info */
+ u8 S_Cmd_Indx; /* special command */
+ u8 volatile S_Status; /* status special command */
+ u16 reserved1;
+ u32 S_Info[4]; /* add. info special command */
+ u8 volatile Sema0; /* command semaphore */
+ u8 reserved2[3];
+ u8 Cmd_Index; /* command number */
+ u8 reserved3[3];
+ u16 volatile Status; /* command status */
+ u16 Service; /* service(for async.events) */
+ u32 Info[2]; /* additional info */
struct {
- ushort offset; /* command offs. in the DPRAM*/
- ushort serv_id; /* service */
- } PACKED comm_queue[MAXOFFSETS]; /* command queue */
- ulong32 bios_reserved[2];
- unchar gdt_dpr_cmd[1]; /* commands */
-} PACKED gdt_dpr_if;
+ u16 offset; /* command offs. in the DPRAM*/
+ u16 serv_id; /* service */
+ } __attribute__((packed)) comm_queue[MAXOFFSETS]; /* command queue */
+ u32 bios_reserved[2];
+ u8 gdt_dpr_cmd[1]; /* commands */
+} __attribute__((packed)) gdt_dpr_if;
/* SRAM structure PCI controllers */
typedef struct {
- ulong32 magic; /* controller ID from BIOS */
- ushort need_deinit; /* switch betw. BIOS/driver */
- unchar switch_support; /* see need_deinit */
- unchar padding[9];
- unchar os_used[16]; /* OS code per service */
- unchar unused[28];
- unchar fw_magic; /* contr. ID from firmware */
-} PACKED gdt_pci_sram;
+ u32 magic; /* controller ID from BIOS */
+ u16 need_deinit; /* switch betw. BIOS/driver */
+ u8 switch_support; /* see need_deinit */
+ u8 padding[9];
+ u8 os_used[16]; /* OS code per service */
+ u8 unused[28];
+ u8 fw_magic; /* contr. ID from firmware */
+} __attribute__((packed)) gdt_pci_sram;
/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
typedef struct {
- unchar os_used[16]; /* OS code per service */
- ushort need_deinit; /* switch betw. BIOS/driver */
- unchar switch_support; /* see need_deinit */
- unchar padding;
-} PACKED gdt_eisa_sram;
+ u8 os_used[16]; /* OS code per service */
+ u16 need_deinit; /* switch betw. BIOS/driver */
+ u8 switch_support; /* see need_deinit */
+ u8 padding;
+} __attribute__((packed)) gdt_eisa_sram;
/* DPRAM ISA controllers */
typedef struct {
union {
struct {
- unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
- ulong32 magic; /* controller (EISA) ID */
- ushort need_deinit; /* switch betw. BIOS/driver */
- unchar switch_support; /* see need_deinit */
- unchar padding[9];
- unchar os_used[16]; /* OS code per service */
- } PACKED dp_sram;
- unchar bios_area[0x4000]; /* 16KB reserved for BIOS */
+ u8 bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
+ u32 magic; /* controller (EISA) ID */
+ u16 need_deinit; /* switch betw. BIOS/driver */
+ u8 switch_support; /* see need_deinit */
+ u8 padding[9];
+ u8 os_used[16]; /* OS code per service */
+ } __attribute__((packed)) dp_sram;
+ u8 bios_area[0x4000]; /* 16KB reserved for BIOS */
} bu;
union {
gdt_dpr_if ic; /* interface area */
- unchar if_area[0x3000]; /* 12KB for interface */
+ u8 if_area[0x3000]; /* 12KB for interface */
} u;
struct {
- unchar memlock; /* write protection DPRAM */
- unchar event; /* release event */
- unchar irqen; /* board interrupts enable */
- unchar irqdel; /* acknowledge board int. */
- unchar volatile Sema1; /* status semaphore */
- unchar rq; /* IRQ/DRQ configuration */
- } PACKED io;
-} PACKED gdt2_dpram_str;
+ u8 memlock; /* write protection DPRAM */
+ u8 event; /* release event */
+ u8 irqen; /* board interrupts enable */
+ u8 irqdel; /* acknowledge board int. */
+ u8 volatile Sema1; /* status semaphore */
+ u8 rq; /* IRQ/DRQ configuration */
+ } __attribute__((packed)) io;
+} __attribute__((packed)) gdt2_dpram_str;
/* DPRAM PCI controllers */
typedef struct {
union {
gdt_dpr_if ic; /* interface area */
- unchar if_area[0xff0-sizeof(gdt_pci_sram)];
+ u8 if_area[0xff0-sizeof(gdt_pci_sram)];
} u;
gdt_pci_sram gdt6sr; /* SRAM structure */
struct {
- unchar unused0[1];
- unchar volatile Sema1; /* command semaphore */
- unchar unused1[3];
- unchar irqen; /* board interrupts enable */
- unchar unused2[2];
- unchar event; /* release event */
- unchar unused3[3];
- unchar irqdel; /* acknowledge board int. */
- unchar unused4[3];
- } PACKED io;
-} PACKED gdt6_dpram_str;
+ u8 unused0[1];
+ u8 volatile Sema1; /* command semaphore */
+ u8 unused1[3];
+ u8 irqen; /* board interrupts enable */
+ u8 unused2[2];
+ u8 event; /* release event */
+ u8 unused3[3];
+ u8 irqdel; /* acknowledge board int. */
+ u8 unused4[3];
+ } __attribute__((packed)) io;
+} __attribute__((packed)) gdt6_dpram_str;
/* PLX register structure (new PCI controllers) */
typedef struct {
- unchar cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
- unchar unused1[0x3f];
- unchar volatile sema0_reg; /* command semaphore */
- unchar volatile sema1_reg; /* status semaphore */
- unchar unused2[2];
- ushort volatile status; /* command status */
- ushort service; /* service */
- ulong32 info[2]; /* additional info */
- unchar unused3[0x10];
- unchar ldoor_reg; /* PCI to local doorbell */
- unchar unused4[3];
- unchar volatile edoor_reg; /* local to PCI doorbell */
- unchar unused5[3];
- unchar control0; /* control0 register(unused) */
- unchar control1; /* board interrupts enable */
- unchar unused6[0x16];
-} PACKED gdt6c_plx_regs;
+ u8 cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
+ u8 unused1[0x3f];
+ u8 volatile sema0_reg; /* command semaphore */
+ u8 volatile sema1_reg; /* status semaphore */
+ u8 unused2[2];
+ u16 volatile status; /* command status */
+ u16 service; /* service */
+ u32 info[2]; /* additional info */
+ u8 unused3[0x10];
+ u8 ldoor_reg; /* PCI to local doorbell */
+ u8 unused4[3];
+ u8 volatile edoor_reg; /* local to PCI doorbell */
+ u8 unused5[3];
+ u8 control0; /* control0 register(unused) */
+ u8 control1; /* board interrupts enable */
+ u8 unused6[0x16];
+} __attribute__((packed)) gdt6c_plx_regs;
/* DPRAM new PCI controllers */
typedef struct {
union {
gdt_dpr_if ic; /* interface area */
- unchar if_area[0x4000-sizeof(gdt_pci_sram)];
+ u8 if_area[0x4000-sizeof(gdt_pci_sram)];
} u;
gdt_pci_sram gdt6sr; /* SRAM structure */
-} PACKED gdt6c_dpram_str;
+} __attribute__((packed)) gdt6c_dpram_str;
/* i960 register structure (PCI MPR controllers) */
typedef struct {
- unchar unused1[16];
- unchar volatile sema0_reg; /* command semaphore */
- unchar unused2;
- unchar volatile sema1_reg; /* status semaphore */
- unchar unused3;
- ushort volatile status; /* command status */
- ushort service; /* service */
- ulong32 info[2]; /* additional info */
- unchar ldoor_reg; /* PCI to local doorbell */
- unchar unused4[11];
- unchar volatile edoor_reg; /* local to PCI doorbell */
- unchar unused5[7];
- unchar edoor_en_reg; /* board interrupts enable */
- unchar unused6[27];
- ulong32 unused7[939];
- ulong32 severity;
+ u8 unused1[16];
+ u8 volatile sema0_reg; /* command semaphore */
+ u8 unused2;
+ u8 volatile sema1_reg; /* status semaphore */
+ u8 unused3;
+ u16 volatile status; /* command status */
+ u16 service; /* service */
+ u32 info[2]; /* additional info */
+ u8 ldoor_reg; /* PCI to local doorbell */
+ u8 unused4[11];
+ u8 volatile edoor_reg; /* local to PCI doorbell */
+ u8 unused5[7];
+ u8 edoor_en_reg; /* board interrupts enable */
+ u8 unused6[27];
+ u32 unused7[939];
+ u32 severity;
char evt_str[256]; /* event string */
-} PACKED gdt6m_i960_regs;
+} __attribute__((packed)) gdt6m_i960_regs;
/* DPRAM PCI MPR controllers */
typedef struct {
gdt6m_i960_regs i960r; /* 4KB i960 registers */
union {
gdt_dpr_if ic; /* interface area */
- unchar if_area[0x3000-sizeof(gdt_pci_sram)];
+ u8 if_area[0x3000-sizeof(gdt_pci_sram)];
} u;
gdt_pci_sram gdt6sr; /* SRAM structure */
-} PACKED gdt6m_dpram_str;
+} __attribute__((packed)) gdt6m_dpram_str;
/* PCI resources */
typedef struct {
struct pci_dev *pdev;
- ulong dpmem; /* DPRAM address */
- ulong io; /* IO address */
+ unsigned long dpmem; /* DPRAM address */
+ unsigned long io; /* IO address */
} gdth_pci_str;
@@ -846,93 +846,93 @@ typedef struct {
typedef struct {
struct Scsi_Host *shost;
struct list_head list;
- ushort hanum;
- ushort oem_id; /* OEM */
- ushort type; /* controller class */
- ulong32 stype; /* subtype (PCI: device ID) */
- ushort fw_vers; /* firmware version */
- ushort cache_feat; /* feat. cache serv. (s/g,..)*/
- ushort raw_feat; /* feat. raw service (s/g,..)*/
- ushort screen_feat; /* feat. raw service (s/g,..)*/
- ushort bmic; /* BMIC address (EISA) */
+ u16 hanum;
+ u16 oem_id; /* OEM */
+ u16 type; /* controller class */
+ u32 stype; /* subtype (PCI: device ID) */
+ u16 fw_vers; /* firmware version */
+ u16 cache_feat; /* feat. cache serv. (s/g,..)*/
+ u16 raw_feat; /* feat. raw service (s/g,..)*/
+ u16 screen_feat; /* feat. raw service (s/g,..)*/
+ u16 bmic; /* BMIC address (EISA) */
void __iomem *brd; /* DPRAM address */
- ulong32 brd_phys; /* slot number/BIOS address */
+ u32 brd_phys; /* slot number/BIOS address */
gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
gdth_cmd_str cmdext;
gdth_cmd_str *pccb; /* address command structure */
- ulong32 ccb_phys; /* phys. address */
+ u32 ccb_phys; /* phys. address */
#ifdef INT_COAL
gdth_coal_status *coal_stat; /* buffer for coalescing int.*/
- ulong64 coal_stat_phys; /* phys. address */
+ u64 coal_stat_phys; /* phys. address */
#endif
char *pscratch; /* scratch (DMA) buffer */
- ulong64 scratch_phys; /* phys. address */
- unchar scratch_busy; /* in use? */
- unchar dma64_support; /* 64-bit DMA supported? */
+ u64 scratch_phys; /* phys. address */
+ u8 scratch_busy; /* in use? */
+ u8 dma64_support; /* 64-bit DMA supported? */
gdth_msg_str *pmsg; /* message buffer */
- ulong64 msg_phys; /* phys. address */
- unchar scan_mode; /* current scan mode */
- unchar irq; /* IRQ */
- unchar drq; /* DRQ (ISA controllers) */
- ushort status; /* command status */
- ushort service; /* service/firmware ver./.. */
- ulong32 info;
- ulong32 info2; /* additional info */
+ u64 msg_phys; /* phys. address */
+ u8 scan_mode; /* current scan mode */
+ u8 irq; /* IRQ */
+ u8 drq; /* DRQ (ISA controllers) */
+ u16 status; /* command status */
+ u16 service; /* service/firmware ver./.. */
+ u32 info;
+ u32 info2; /* additional info */
Scsi_Cmnd *req_first; /* top of request queue */
struct {
- unchar present; /* Flag: host drive present? */
- unchar is_logdrv; /* Flag: log. drive (master)? */
- unchar is_arraydrv; /* Flag: array drive? */
- unchar is_master; /* Flag: array drive master? */
- unchar is_parity; /* Flag: parity drive? */
- unchar is_hotfix; /* Flag: hotfix drive? */
- unchar master_no; /* number of master drive */
- unchar lock; /* drive locked? (hot plug) */
- unchar heads; /* mapping */
- unchar secs;
- ushort devtype; /* further information */
- ulong64 size; /* capacity */
- unchar ldr_no; /* log. drive no. */
- unchar rw_attribs; /* r/w attributes */
- unchar cluster_type; /* cluster properties */
- unchar media_changed; /* Flag:MOUNT/UNMOUNT occured */
- ulong32 start_sec; /* start sector */
+ u8 present; /* Flag: host drive present? */
+ u8 is_logdrv; /* Flag: log. drive (master)? */
+ u8 is_arraydrv; /* Flag: array drive? */
+ u8 is_master; /* Flag: array drive master? */
+ u8 is_parity; /* Flag: parity drive? */
+ u8 is_hotfix; /* Flag: hotfix drive? */
+ u8 master_no; /* number of master drive */
+ u8 lock; /* drive locked? (hot plug) */
+ u8 heads; /* mapping */
+ u8 secs;
+ u16 devtype; /* further information */
+ u64 size; /* capacity */
+ u8 ldr_no; /* log. drive no. */
+ u8 rw_attribs; /* r/w attributes */
+ u8 cluster_type; /* cluster properties */
+ u8 media_changed; /* Flag:MOUNT/UNMOUNT occured */
+ u32 start_sec; /* start sector */
} hdr[MAX_LDRIVES]; /* host drives */
struct {
- unchar lock; /* channel locked? (hot plug) */
- unchar pdev_cnt; /* physical device count */
- unchar local_no; /* local channel number */
- unchar io_cnt[MAXID]; /* current IO count */
- ulong32 address; /* channel address */
- ulong32 id_list[MAXID]; /* IDs of the phys. devices */
+ u8 lock; /* channel locked? (hot plug) */
+ u8 pdev_cnt; /* physical device count */
+ u8 local_no; /* local channel number */
+ u8 io_cnt[MAXID]; /* current IO count */
+ u32 address; /* channel address */
+ u32 id_list[MAXID]; /* IDs of the phys. devices */
} raw[MAXBUS]; /* SCSI channels */
struct {
Scsi_Cmnd *cmnd; /* pending request */
- ushort service; /* service */
+ u16 service; /* service */
} cmd_tab[GDTH_MAXCMDS]; /* table of pend. requests */
struct gdth_cmndinfo { /* per-command private info */
int index;
int internal_command; /* don't call scsi_done */
gdth_cmd_str *internal_cmd_str; /* crier for internal messages*/
dma_addr_t sense_paddr; /* sense dma-addr */
- unchar priority;
+ u8 priority;
int timeout_count; /* # of timeout calls */
volatile int wait_for_completion;
- ushort status;
- ulong32 info;
+ u16 status;
+ u32 info;
enum dma_data_direction dma_dir;
int phase; /* ???? */
int OpCode;
} cmndinfo[GDTH_MAXCMDS]; /* index==0 is free */
- unchar bus_cnt; /* SCSI bus count */
- unchar tid_cnt; /* Target ID count */
- unchar bus_id[MAXBUS]; /* IOP IDs */
- unchar virt_bus; /* number of virtual bus */
- unchar more_proc; /* more /proc info supported */
- ushort cmd_cnt; /* command count in DPRAM */
- ushort cmd_len; /* length of actual command */
- ushort cmd_offs_dpmem; /* actual offset in DPRAM */
- ushort ic_all_size; /* sizeof DPRAM interf. area */
+ u8 bus_cnt; /* SCSI bus count */
+ u8 tid_cnt; /* Target ID count */
+ u8 bus_id[MAXBUS]; /* IOP IDs */
+ u8 virt_bus; /* number of virtual bus */
+ u8 more_proc; /* more /proc info supported */
+ u16 cmd_cnt; /* command count in DPRAM */
+ u16 cmd_len; /* length of actual command */
+ u16 cmd_offs_dpmem; /* actual offset in DPRAM */
+ u16 ic_all_size; /* sizeof DPRAM interf. area */
gdth_cpar_str cpar; /* controller cache par. */
gdth_bfeat_str bfeat; /* controller features */
gdth_binfo_str binfo; /* controller info */
@@ -941,7 +941,7 @@ typedef struct {
struct pci_dev *pdev;
char oem_name[8];
#ifdef GDTH_DMA_STATISTICS
- ulong dma32_cnt, dma64_cnt; /* statistics: DMA buffer */
+ unsigned long dma32_cnt, dma64_cnt; /* statistics: DMA buffer */
#endif
struct scsi_device *sdev;
} gdth_ha_str;
@@ -953,65 +953,65 @@ static inline struct gdth_cmndinfo *gdth_cmnd_priv(struct scsi_cmnd* cmd)
/* INQUIRY data format */
typedef struct {
- unchar type_qual;
- unchar modif_rmb;
- unchar version;
- unchar resp_aenc;
- unchar add_length;
- unchar reserved1;
- unchar reserved2;
- unchar misc;
- unchar vendor[8];
- unchar product[16];
- unchar revision[4];
-} PACKED gdth_inq_data;
+ u8 type_qual;
+ u8 modif_rmb;
+ u8 version;
+ u8 resp_aenc;
+ u8 add_length;
+ u8 reserved1;
+ u8 reserved2;
+ u8 misc;
+ u8 vendor[8];
+ u8 product[16];
+ u8 revision[4];
+} __attribute__((packed)) gdth_inq_data;
/* READ_CAPACITY data format */
typedef struct {
- ulong32 last_block_no;
- ulong32 block_length;
-} PACKED gdth_rdcap_data;
+ u32 last_block_no;
+ u32 block_length;
+} __attribute__((packed)) gdth_rdcap_data;
/* READ_CAPACITY (16) data format */
typedef struct {
- ulong64 last_block_no;
- ulong32 block_length;
-} PACKED gdth_rdcap16_data;
+ u64 last_block_no;
+ u32 block_length;
+} __attribute__((packed)) gdth_rdcap16_data;
/* REQUEST_SENSE data format */
typedef struct {
- unchar errorcode;
- unchar segno;
- unchar key;
- ulong32 info;
- unchar add_length;
- ulong32 cmd_info;
- unchar adsc;
- unchar adsq;
- unchar fruc;
- unchar key_spec[3];
-} PACKED gdth_sense_data;
+ u8 errorcode;
+ u8 segno;
+ u8 key;
+ u32 info;
+ u8 add_length;
+ u32 cmd_info;
+ u8 adsc;
+ u8 adsq;
+ u8 fruc;
+ u8 key_spec[3];
+} __attribute__((packed)) gdth_sense_data;
/* MODE_SENSE data format */
typedef struct {
struct {
- unchar data_length;
- unchar med_type;
- unchar dev_par;
- unchar bd_length;
- } PACKED hd;
+ u8 data_length;
+ u8 med_type;
+ u8 dev_par;
+ u8 bd_length;
+ } __attribute__((packed)) hd;
struct {
- unchar dens_code;
- unchar block_count[3];
- unchar reserved;
- unchar block_length[3];
- } PACKED bd;
-} PACKED gdth_modep_data;
+ u8 dens_code;
+ u8 block_count[3];
+ u8 reserved;
+ u8 block_length[3];
+ } __attribute__((packed)) bd;
+} __attribute__((packed)) gdth_modep_data;
/* stack frame */
typedef struct {
- ulong b[10]; /* 32/64 bit compiler ! */
-} PACKED gdth_stackframe;
+ unsigned long b[10]; /* 32/64 bit compiler ! */
+} __attribute__((packed)) gdth_stackframe;
/* function prototyping */
diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
index 783fae737f1..b004c616588 100644
--- a/drivers/scsi/gdth_ioctl.h
+++ b/drivers/scsi/gdth_ioctl.h
@@ -32,109 +32,101 @@
#define MAX_HDRIVES MAX_LDRIVES /* max. host drive count */
#endif
-/* typedefs */
-#ifdef __KERNEL__
-typedef u32 ulong32;
-typedef u64 ulong64;
-#endif
-
-#define PACKED __attribute__((packed))
-
/* scatter/gather element */
typedef struct {
- ulong32 sg_ptr; /* address */
- ulong32 sg_len; /* length */
-} PACKED gdth_sg_str;
+ u32 sg_ptr; /* address */
+ u32 sg_len; /* length */
+} __attribute__((packed)) gdth_sg_str;
/* scatter/gather element - 64bit addresses */
typedef struct {
- ulong64 sg_ptr; /* address */
- ulong32 sg_len; /* length */
-} PACKED gdth_sg64_str;
+ u64 sg_ptr; /* address */
+ u32 sg_len; /* length */
+} __attribute__((packed)) gdth_sg64_str;
/* command structure */
typedef struct {
- ulong32 BoardNode; /* board node (always 0) */
- ulong32 CommandIndex; /* command number */
- ushort OpCode; /* the command (READ,..) */
+ u32 BoardNode; /* board node (always 0) */
+ u32 CommandIndex; /* command number */
+ u16 OpCode; /* the command (READ,..) */
union {
struct {
- ushort DeviceNo; /* number of cache drive */
- ulong32 BlockNo; /* block number */
- ulong32 BlockCnt; /* block count */
- ulong32 DestAddr; /* dest. addr. (if s/g: -1) */
- ulong32 sg_canz; /* s/g element count */
+ u16 DeviceNo; /* number of cache drive */
+ u32 BlockNo; /* block number */
+ u32 BlockCnt; /* block count */
+ u32 DestAddr; /* dest. addr. (if s/g: -1) */
+ u32 sg_canz; /* s/g element count */
gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } PACKED cache; /* cache service cmd. str. */
+ } __attribute__((packed)) cache; /* cache service cmd. str. */
struct {
- ushort DeviceNo; /* number of cache drive */
- ulong64 BlockNo; /* block number */
- ulong32 BlockCnt; /* block count */
- ulong64 DestAddr; /* dest. addr. (if s/g: -1) */
- ulong32 sg_canz; /* s/g element count */
+ u16 DeviceNo; /* number of cache drive */
+ u64 BlockNo; /* block number */
+ u32 BlockCnt; /* block count */
+ u64 DestAddr; /* dest. addr. (if s/g: -1) */
+ u32 sg_canz; /* s/g element count */
gdth_sg64_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } PACKED cache64; /* cache service cmd. str. */
+ } __attribute__((packed)) cache64; /* cache service cmd. str. */
struct {
- ushort param_size; /* size of p_param buffer */
- ulong32 subfunc; /* IOCTL function */
- ulong32 channel; /* device */
- ulong64 p_param; /* buffer */
- } PACKED ioctl; /* IOCTL command structure */
+ u16 param_size; /* size of p_param buffer */
+ u32 subfunc; /* IOCTL function */
+ u32 channel; /* device */
+ u64 p_param; /* buffer */
+ } __attribute__((packed)) ioctl; /* IOCTL command structure */
struct {
- ushort reserved;
+ u16 reserved;
union {
struct {
- ulong32 msg_handle; /* message handle */
- ulong64 msg_addr; /* message buffer address */
- } PACKED msg;
- unchar data[12]; /* buffer for rtc data, ... */
+ u32 msg_handle; /* message handle */
+ u64 msg_addr; /* message buffer address */
+ } __attribute__((packed)) msg;
+ u8 data[12]; /* buffer for rtc data, ... */
} su;
- } PACKED screen; /* screen service cmd. str. */
+ } __attribute__((packed)) screen; /* screen service cmd. str. */
struct {
- ushort reserved;
- ulong32 direction; /* data direction */
- ulong32 mdisc_time; /* disc. time (0: no timeout)*/
- ulong32 mcon_time; /* connect time(0: no to.) */
- ulong32 sdata; /* dest. addr. (if s/g: -1) */
- ulong32 sdlen; /* data length (bytes) */
- ulong32 clen; /* SCSI cmd. length(6,10,12) */
- unchar cmd[12]; /* SCSI command */
- unchar target; /* target ID */
- unchar lun; /* LUN */
- unchar bus; /* SCSI bus number */
- unchar priority; /* only 0 used */
- ulong32 sense_len; /* sense data length */
- ulong32 sense_data; /* sense data addr. */
- ulong32 link_p; /* linked cmds (not supp.) */
- ulong32 sg_ranz; /* s/g element count */
+ u16 reserved;
+ u32 direction; /* data direction */
+ u32 mdisc_time; /* disc. time (0: no timeout)*/
+ u32 mcon_time; /* connect time(0: no to.) */
+ u32 sdata; /* dest. addr. (if s/g: -1) */
+ u32 sdlen; /* data length (bytes) */
+ u32 clen; /* SCSI cmd. length(6,10,12) */
+ u8 cmd[12]; /* SCSI command */
+ u8 target; /* target ID */
+ u8 lun; /* LUN */
+ u8 bus; /* SCSI bus number */
+ u8 priority; /* only 0 used */
+ u32 sense_len; /* sense data length */
+ u32 sense_data; /* sense data addr. */
+ u32 link_p; /* linked cmds (not supp.) */
+ u32 sg_ranz; /* s/g element count */
gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } PACKED raw; /* raw service cmd. struct. */
+ } __attribute__((packed)) raw; /* raw service cmd. struct. */
struct {
- ushort reserved;
- ulong32 direction; /* data direction */
- ulong32 mdisc_time; /* disc. time (0: no timeout)*/
- ulong32 mcon_time; /* connect time(0: no to.) */
- ulong64 sdata; /* dest. addr. (if s/g: -1) */
- ulong32 sdlen; /* data length (bytes) */
- ulong32 clen; /* SCSI cmd. length(6,..,16) */
- unchar cmd[16]; /* SCSI command */
- unchar target; /* target ID */
- unchar lun; /* LUN */
- unchar bus; /* SCSI bus number */
- unchar priority; /* only 0 used */
- ulong32 sense_len; /* sense data length */
- ulong64 sense_data; /* sense data addr. */
- ulong32 sg_ranz; /* s/g element count */
+ u16 reserved;
+ u32 direction; /* data direction */
+ u32 mdisc_time; /* disc. time (0: no timeout)*/
+ u32 mcon_time; /* connect time(0: no to.) */
+ u64 sdata; /* dest. addr. (if s/g: -1) */
+ u32 sdlen; /* data length (bytes) */
+ u32 clen; /* SCSI cmd. length(6,..,16) */
+ u8 cmd[16]; /* SCSI command */
+ u8 target; /* target ID */
+ u8 lun; /* LUN */
+ u8 bus; /* SCSI bus number */
+ u8 priority; /* only 0 used */
+ u32 sense_len; /* sense data length */
+ u64 sense_data; /* sense data addr. */
+ u32 sg_ranz; /* s/g element count */
gdth_sg64_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } PACKED raw64; /* raw service cmd. struct. */
+ } __attribute__((packed)) raw64; /* raw service cmd. struct. */
} u;
/* additional variables */
- unchar Service; /* controller service */
- unchar reserved;
- ushort Status; /* command result */
- ulong32 Info; /* additional information */
+ u8 Service; /* controller service */
+ u8 reserved;
+ u16 Status; /* command result */
+ u32 Info; /* additional information */
void *RequestBuffer; /* request buffer */
-} PACKED gdth_cmd_str;
+} __attribute__((packed)) gdth_cmd_str;
/* controller event structure */
#define ES_ASYNC 1
@@ -142,129 +134,129 @@ typedef struct {
#define ES_TEST 3
#define ES_SYNC 4
typedef struct {
- ushort size; /* size of structure */
+ u16 size; /* size of structure */
union {
char stream[16];
struct {
- ushort ionode;
- ushort service;
- ulong32 index;
- } PACKED driver;
+ u16 ionode;
+ u16 service;
+ u32 index;
+ } __attribute__((packed)) driver;
struct {
- ushort ionode;
- ushort service;
- ushort status;
- ulong32 info;
- unchar scsi_coord[3];
- } PACKED async;
+ u16 ionode;
+ u16 service;
+ u16 status;
+ u32 info;
+ u8 scsi_coord[3];
+ } __attribute__((packed)) async;
struct {
- ushort ionode;
- ushort service;
- ushort status;
- ulong32 info;
- ushort hostdrive;
- unchar scsi_coord[3];
- unchar sense_key;
- } PACKED sync;
+ u16 ionode;
+ u16 service;
+ u16 status;
+ u32 info;
+ u16 hostdrive;
+ u8 scsi_coord[3];
+ u8 sense_key;
+ } __attribute__((packed)) sync;
struct {
- ulong32 l1, l2, l3, l4;
- } PACKED test;
+ u32 l1, l2, l3, l4;
+ } __attribute__((packed)) test;
} eu;
- ulong32 severity;
- unchar event_string[256];
-} PACKED gdth_evt_data;
+ u32 severity;
+ u8 event_string[256];
+} __attribute__((packed)) gdth_evt_data;
typedef struct {
- ulong32 first_stamp;
- ulong32 last_stamp;
- ushort same_count;
- ushort event_source;
- ushort event_idx;
- unchar application;
- unchar reserved;
+ u32 first_stamp;
+ u32 last_stamp;
+ u16 same_count;
+ u16 event_source;
+ u16 event_idx;
+ u8 application;
+ u8 reserved;
gdth_evt_data event_data;
-} PACKED gdth_evt_str;
+} __attribute__((packed)) gdth_evt_str;
#ifdef GDTH_IOCTL_PROC
/* IOCTL structure (write) */
typedef struct {
- ulong32 magic; /* IOCTL magic */
- ushort ioctl; /* IOCTL */
- ushort ionode; /* controller number */
- ushort service; /* controller service */
- ushort timeout; /* timeout */
+ u32 magic; /* IOCTL magic */
+ u16 ioctl; /* IOCTL */
+ u16 ionode; /* controller number */
+ u16 service; /* controller service */
+ u16 timeout; /* timeout */
union {
struct {
- unchar command[512]; /* controller command */
- unchar data[1]; /* add. data */
+ u8 command[512]; /* controller command */
+ u8 data[1]; /* add. data */
} general;
struct {
- unchar lock; /* lock/unlock */
- unchar drive_cnt; /* drive count */
- ushort drives[MAX_HDRIVES];/* drives */
+ u8 lock; /* lock/unlock */
+ u8 drive_cnt; /* drive count */
+ u16 drives[MAX_HDRIVES];/* drives */
} lockdrv;
struct {
- unchar lock; /* lock/unlock */
- unchar channel; /* channel */
+ u8 lock; /* lock/unlock */
+ u8 channel; /* channel */
} lockchn;
struct {
int erase; /* erase event ? */
int handle;
- unchar evt[EVENT_SIZE]; /* event structure */
+ u8 evt[EVENT_SIZE]; /* event structure */
} event;
struct {
- unchar bus; /* SCSI bus */
- unchar target; /* target ID */
- unchar lun; /* LUN */
- unchar cmd_len; /* command length */
- unchar cmd[12]; /* SCSI command */
+ u8 bus; /* SCSI bus */
+ u8 target; /* target ID */
+ u8 lun; /* LUN */
+ u8 cmd_len; /* command length */
+ u8 cmd[12]; /* SCSI command */
} scsi;
struct {
- ushort hdr_no; /* host drive number */
- unchar flag; /* old meth./add/remove */
+ u16 hdr_no; /* host drive number */
+ u8 flag; /* old meth./add/remove */
} rescan;
} iu;
} gdth_iowr_str;
/* IOCTL structure (read) */
typedef struct {
- ulong32 size; /* buffer size */
- ulong32 status; /* IOCTL error code */
+ u32 size; /* buffer size */
+ u32 status; /* IOCTL error code */
union {
struct {
- unchar data[1]; /* data */
+ u8 data[1]; /* data */
} general;
struct {
- ushort version; /* driver version */
+ u16 version; /* driver version */
} drvers;
struct {
- unchar type; /* controller type */
- ushort info; /* slot etc. */
- ushort oem_id; /* OEM ID */
- ushort bios_ver; /* not used */
- ushort access; /* not used */
- ushort ext_type; /* extended type */
- ushort device_id; /* device ID */
- ushort sub_device_id; /* sub device ID */
+ u8 type; /* controller type */
+ u16 info; /* slot etc. */
+ u16 oem_id; /* OEM ID */
+ u16 bios_ver; /* not used */
+ u16 access; /* not used */
+ u16 ext_type; /* extended type */
+ u16 device_id; /* device ID */
+ u16 sub_device_id; /* sub device ID */
} ctrtype;
struct {
- unchar version; /* OS version */
- unchar subversion; /* OS subversion */
- ushort revision; /* revision */
+ u8 version; /* OS version */
+ u8 subversion; /* OS subversion */
+ u16 revision; /* revision */
} osvers;
struct {
- ushort count; /* controller count */
+ u16 count; /* controller count */
} ctrcnt;
struct {
int handle;
- unchar evt[EVENT_SIZE]; /* event structure */
+ u8 evt[EVENT_SIZE]; /* event structure */
} event;
struct {
- unchar bus; /* SCSI bus, 0xff: invalid */
- unchar target; /* target ID */
- unchar lun; /* LUN */
- unchar cluster_type; /* cluster properties */
+ u8 bus; /* SCSI bus, 0xff: invalid */
+ u8 target; /* target ID */
+ u8 lun; /* LUN */
+ u8 cluster_type; /* cluster properties */
} hdr_list[MAX_HDRIVES]; /* index is host drive number */
} iu;
} gdth_iord_str;
@@ -272,53 +264,53 @@ typedef struct {
/* GDTIOCTL_GENERAL */
typedef struct {
- ushort ionode; /* controller number */
- ushort timeout; /* timeout */
- ulong32 info; /* error info */
- ushort status; /* status */
- ulong data_len; /* data buffer size */
- ulong sense_len; /* sense buffer size */
+ u16 ionode; /* controller number */
+ u16 timeout; /* timeout */
+ u32 info; /* error info */
+ u16 status; /* status */
+ unsigned long data_len; /* data buffer size */
+ unsigned long sense_len; /* sense buffer size */
gdth_cmd_str command; /* command */
} gdth_ioctl_general;
/* GDTIOCTL_LOCKDRV */
typedef struct {
- ushort ionode; /* controller number */
- unchar lock; /* lock/unlock */
- unchar drive_cnt; /* drive count */
- ushort drives[MAX_HDRIVES]; /* drives */
+ u16 ionode; /* controller number */
+ u8 lock; /* lock/unlock */
+ u8 drive_cnt; /* drive count */
+ u16 drives[MAX_HDRIVES]; /* drives */
} gdth_ioctl_lockdrv;
/* GDTIOCTL_LOCKCHN */
typedef struct {
- ushort ionode; /* controller number */
- unchar lock; /* lock/unlock */
- unchar channel; /* channel */
+ u16 ionode; /* controller number */
+ u8 lock; /* lock/unlock */
+ u8 channel; /* channel */
} gdth_ioctl_lockchn;
/* GDTIOCTL_OSVERS */
typedef struct {
- unchar version; /* OS version */
- unchar subversion; /* OS subversion */
- ushort revision; /* revision */
+ u8 version; /* OS version */
+ u8 subversion; /* OS subversion */
+ u16 revision; /* revision */
} gdth_ioctl_osvers;
/* GDTIOCTL_CTRTYPE */
typedef struct {
- ushort ionode; /* controller number */
- unchar type; /* controller type */
- ushort info; /* slot etc. */
- ushort oem_id; /* OEM ID */
- ushort bios_ver; /* not used */
- ushort access; /* not used */
- ushort ext_type; /* extended type */
- ushort device_id; /* device ID */
- ushort sub_device_id; /* sub device ID */
+ u16 ionode; /* controller number */
+ u8 type; /* controller type */
+ u16 info; /* slot etc. */
+ u16 oem_id; /* OEM ID */
+ u16 bios_ver; /* not used */
+ u16 access; /* not used */
+ u16 ext_type; /* extended type */
+ u16 device_id; /* device ID */
+ u16 sub_device_id; /* sub device ID */
} gdth_ioctl_ctrtype;
/* GDTIOCTL_EVENT */
typedef struct {
- ushort ionode;
+ u16 ionode;
int erase; /* erase event? */
int handle; /* event handle */
gdth_evt_str event;
@@ -326,22 +318,22 @@ typedef struct {
/* GDTIOCTL_RESCAN/GDTIOCTL_HDRLIST */
typedef struct {
- ushort ionode; /* controller number */
- unchar flag; /* add/remove */
- ushort hdr_no; /* drive no. */
+ u16 ionode; /* controller number */
+ u8 flag; /* add/remove */
+ u16 hdr_no; /* drive no. */
struct {
- unchar bus; /* SCSI bus */
- unchar target; /* target ID */
- unchar lun; /* LUN */
- unchar cluster_type; /* cluster properties */
+ u8 bus; /* SCSI bus */
+ u8 target; /* target ID */
+ u8 lun; /* LUN */
+ u8 cluster_type; /* cluster properties */
} hdr_list[MAX_HDRIVES]; /* index is host drive number */
} gdth_ioctl_rescan;
/* GDTIOCTL_RESET_BUS/GDTIOCTL_RESET_DRV */
typedef struct {
- ushort ionode; /* controller number */
- ushort number; /* bus/host drive number */
- ushort status; /* status */
+ u16 ionode; /* controller number */
+ u16 number; /* bus/host drive number */
+ u16 status; /* status */
} gdth_ioctl_reset;
#endif
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
index 1258da34fbc..ffb2b21992b 100644
--- a/drivers/scsi/gdth_proc.c
+++ b/drivers/scsi/gdth_proc.c
@@ -43,7 +43,7 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
int i, found;
gdth_cmd_str gdtcmd;
gdth_cpar_str *pcpar;
- ulong64 paddr;
+ u64 paddr;
char cmnd[MAX_COMMAND_SIZE];
memset(cmnd, 0xff, 12);
@@ -156,8 +156,8 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
off_t begin = 0,pos = 0;
int id, i, j, k, sec, flag;
int no_mdrv = 0, drv_no, is_mirr;
- ulong32 cnt;
- ulong64 paddr;
+ u32 cnt;
+ u64 paddr;
int rc = -ENOMEM;
gdth_cmd_str *gdtcmd;
@@ -220,14 +220,14 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
if (ha->more_proc)
sprintf(hrec, "%d.%02d.%02d-%c%03X",
- (unchar)(ha->binfo.upd_fw_ver>>24),
- (unchar)(ha->binfo.upd_fw_ver>>16),
- (unchar)(ha->binfo.upd_fw_ver),
+ (u8)(ha->binfo.upd_fw_ver>>24),
+ (u8)(ha->binfo.upd_fw_ver>>16),
+ (u8)(ha->binfo.upd_fw_ver),
ha->bfeat.raid ? 'R':'N',
ha->binfo.upd_revision);
else
- sprintf(hrec, "%d.%02d", (unchar)(ha->cpar.version>>8),
- (unchar)(ha->cpar.version));
+ sprintf(hrec, "%d.%02d", (u8)(ha->cpar.version>>8),
+ (u8)(ha->cpar.version));
size = sprintf(buffer+len,
" Driver Ver.: \t%-10s\tFirmware Ver.: \t%s\n",
@@ -281,7 +281,7 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
pds->bid = ha->raw[i].local_no;
pds->first = 0;
pds->entries = ha->raw[i].pdev_cnt;
- cnt = (3*GDTH_SCRATCH/4 - 5 * sizeof(ulong32)) /
+ cnt = (3*GDTH_SCRATCH/4 - 5 * sizeof(u32)) /
sizeof(pds->list[0]);
if (pds->entries > cnt)
pds->entries = cnt;
@@ -604,7 +604,7 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
size = sprintf(buffer+len,
" Capacity [MB]:\t%-6d \tStart Sector: \t%d\n",
- (ulong32)(ha->hdr[i].size/2048), ha->hdr[i].start_sec);
+ (u32)(ha->hdr[i].size/2048), ha->hdr[i].start_sec);
len += size; pos = begin + len;
if (pos < offset) {
len = 0;
@@ -664,9 +664,9 @@ free_fail:
}
static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
- ulong64 *paddr)
+ u64 *paddr)
{
- ulong flags;
+ unsigned long flags;
char *ret_val;
if (size == 0)
@@ -691,9 +691,9 @@ static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
return ret_val;
}
-static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr)
+static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr)
{
- ulong flags;
+ unsigned long flags;
if (buf == ha->pscratch) {
spin_lock_irqsave(&ha->smp_lock, flags);
@@ -705,16 +705,16 @@ static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr)
}
#ifdef GDTH_IOCTL_PROC
-static int gdth_ioctl_check_bin(gdth_ha_str *ha, ushort size)
+static int gdth_ioctl_check_bin(gdth_ha_str *ha, u16 size)
{
- ulong flags;
+ unsigned long flags;
int ret_val;
spin_lock_irqsave(&ha->smp_lock, flags);
ret_val = FALSE;
if (ha->scratch_busy) {
- if (((gdth_iord_str *)ha->pscratch)->size == (ulong32)size)
+ if (((gdth_iord_str *)ha->pscratch)->size == (u32)size)
ret_val = TRUE;
}
spin_unlock_irqrestore(&ha->smp_lock, flags);
@@ -724,11 +724,11 @@ static int gdth_ioctl_check_bin(gdth_ha_str *ha, ushort size)
static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id)
{
- ulong flags;
+ unsigned long flags;
int i;
Scsi_Cmnd *scp;
struct gdth_cmndinfo *cmndinfo;
- unchar b, t;
+ u8 b, t;
spin_lock_irqsave(&ha->smp_lock, flags);
@@ -738,8 +738,8 @@ static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id)
b = scp->device->channel;
t = scp->device->id;
- if (!SPECIAL_SCP(scp) && t == (unchar)id &&
- b == (unchar)busnum) {
+ if (!SPECIAL_SCP(scp) && t == (u8)id &&
+ b == (u8)busnum) {
cmndinfo->wait_for_completion = 0;
spin_unlock_irqrestore(&ha->smp_lock, flags);
while (!cmndinfo->wait_for_completion)
diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
index 9b900cc9ebe..dab15f59f2c 100644
--- a/drivers/scsi/gdth_proc.h
+++ b/drivers/scsi/gdth_proc.h
@@ -17,8 +17,8 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
int length, gdth_ha_str *ha);
static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
- ulong64 *paddr);
-static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr);
+ u64 *paddr);
+static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr);
static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id);
#endif
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 554626e1806..09dbcb847b7 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -215,6 +215,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
shost->shost_gendev.parent = dev ? dev : &platform_bus;
shost->dma_dev = dma_dev;
+ device_enable_async_suspend(&shost->shost_gendev);
+
error = device_add(&shost->shost_gendev);
if (error)
goto out;
@@ -222,6 +224,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
scsi_host_set_state(shost, SHOST_RUNNING);
get_device(shost->shost_gendev.parent);
+ device_enable_async_suspend(&shost->shost_dev);
+
error = device_add(&shost->shost_dev);
if (error)
goto out_del_gendev;
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index bb96fdd58e2..03697ba9425 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -52,7 +52,7 @@
#include "hpsa.h"
/* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' */
-#define HPSA_DRIVER_VERSION "1.0.0"
+#define HPSA_DRIVER_VERSION "2.0.1-3"
#define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
/* How long to wait (in milliseconds) for board to go into simple mode */
@@ -77,9 +77,6 @@ MODULE_PARM_DESC(hpsa_allow_any,
/* define the PCI info for the cards we can control */
static const struct pci_device_id hpsa_pci_device_id[] = {
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x3223},
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x3234},
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x323D},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3241},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3243},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3245},
@@ -87,6 +84,9 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324a},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324b},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3233},
+#define PCI_DEVICE_ID_HP_CISSF 0x333f
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x333F},
{PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
{0,}
@@ -99,9 +99,6 @@ MODULE_DEVICE_TABLE(pci, hpsa_pci_device_id);
* access = Address of the struct of function pointers
*/
static struct board_type products[] = {
- {0x3223103C, "Smart Array P800", &SA5_access},
- {0x3234103C, "Smart Array P400", &SA5_access},
- {0x323d103c, "Smart Array P700M", &SA5_access},
{0x3241103C, "Smart Array P212", &SA5_access},
{0x3243103C, "Smart Array P410", &SA5_access},
{0x3245103C, "Smart Array P410i", &SA5_access},
@@ -109,6 +106,8 @@ static struct board_type products[] = {
{0x3249103C, "Smart Array P812", &SA5_access},
{0x324a103C, "Smart Array P712m", &SA5_access},
{0x324b103C, "Smart Array P711m", &SA5_access},
+ {0x3233103C, "StorageWorks P1210m", &SA5_access},
+ {0x333F103C, "StorageWorks P1210m", &SA5_access},
{0xFFFF103C, "Unknown Smart Array", &SA5_access},
};
@@ -126,12 +125,15 @@ static void cmd_free(struct ctlr_info *h, struct CommandList *c);
static void cmd_special_free(struct ctlr_info *h, struct CommandList *c);
static struct CommandList *cmd_alloc(struct ctlr_info *h);
static struct CommandList *cmd_special_alloc(struct ctlr_info *h);
-static void fill_cmd(struct CommandList *c, __u8 cmd, struct ctlr_info *h,
- void *buff, size_t size, __u8 page_code, unsigned char *scsi3addr,
+static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
+ void *buff, size_t size, u8 page_code, unsigned char *scsi3addr,
int cmd_type);
static int hpsa_scsi_queue_command(struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *));
+static void hpsa_scan_start(struct Scsi_Host *);
+static int hpsa_scan_finished(struct Scsi_Host *sh,
+ unsigned long elapsed_time);
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
static int hpsa_slave_alloc(struct scsi_device *sdev);
@@ -150,6 +152,11 @@ static int check_for_unit_attention(struct ctlr_info *h,
struct CommandList *c);
static void check_ioctl_unit_attention(struct ctlr_info *h,
struct CommandList *c);
+/* performant mode helper functions */
+static void calc_bucket_map(int *bucket, int num_buckets,
+ int nsgs, int *bucket_map);
+static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
+static inline u32 next_command(struct ctlr_info *h);
static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);
static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
@@ -173,10 +180,10 @@ static struct scsi_host_template hpsa_driver_template = {
.name = "hpsa",
.proc_name = "hpsa",
.queuecommand = hpsa_scsi_queue_command,
- .can_queue = 512,
+ .scan_start = hpsa_scan_start,
+ .scan_finished = hpsa_scan_finished,
.this_id = -1,
.sg_tablesize = MAXSGENTRIES,
- .cmd_per_lun = 512,
.use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = hpsa_eh_device_reset_handler,
.ioctl = hpsa_ioctl,
@@ -195,6 +202,12 @@ static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
return (struct ctlr_info *) *priv;
}
+static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh)
+{
+ unsigned long *priv = shost_priv(sh);
+ return (struct ctlr_info *) *priv;
+}
+
static struct task_struct *hpsa_scan_thread;
static DEFINE_MUTEX(hpsa_scan_mutex);
static LIST_HEAD(hpsa_scan_q);
@@ -312,7 +325,7 @@ static int hpsa_scan_func(__attribute__((unused)) void *data)
h->busy_scanning = 1;
mutex_unlock(&hpsa_scan_mutex);
host_no = h->scsi_host ? h->scsi_host->host_no : -1;
- hpsa_update_scsi_devices(h, host_no);
+ hpsa_scan_start(h->scsi_host);
complete_all(&h->scan_wait);
mutex_lock(&hpsa_scan_mutex);
h->busy_scanning = 0;
@@ -379,8 +392,7 @@ static ssize_t host_store_rescan(struct device *dev,
{
struct ctlr_info *h;
struct Scsi_Host *shost = class_to_shost(dev);
- unsigned long *priv = shost_priv(shost);
- h = (struct ctlr_info *) *priv;
+ h = shost_to_hba(shost);
if (add_to_scan_list(h)) {
wake_up_process(hpsa_scan_thread);
wait_for_completion_interruptible(&h->scan_wait);
@@ -394,10 +406,44 @@ static inline void addQ(struct hlist_head *list, struct CommandList *c)
hlist_add_head(&c->list, list);
}
+static inline u32 next_command(struct ctlr_info *h)
+{
+ u32 a;
+
+ if (unlikely(h->transMethod != CFGTBL_Trans_Performant))
+ return h->access.command_completed(h);
+
+ if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
+ a = *(h->reply_pool_head); /* Next cmd in ring buffer */
+ (h->reply_pool_head)++;
+ h->commands_outstanding--;
+ } else {
+ a = FIFO_EMPTY;
+ }
+ /* Check for wraparound */
+ if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
+ h->reply_pool_head = h->reply_pool;
+ h->reply_pool_wraparound ^= 1;
+ }
+ return a;
+}
+
+/* set_performant_mode: Modify the tag for cciss performant
+ * set bit 0 for pull model, bits 3-1 for block fetch
+ * register number
+ */
+static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
+{
+ if (likely(h->transMethod == CFGTBL_Trans_Performant))
+ c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
+}
+
static void enqueue_cmd_and_start_io(struct ctlr_info *h,
struct CommandList *c)
{
unsigned long flags;
+
+ set_performant_mode(h, c);
spin_lock_irqsave(&h->lock, flags);
addQ(&h->reqQ, c);
h->Qdepth++;
@@ -422,6 +468,15 @@ static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[])
return (scsi3addr[3] & 0xC0) == 0x40;
}
+static inline int is_scsi_rev_5(struct ctlr_info *h)
+{
+ if (!h->hba_inquiry_data)
+ return 0;
+ if ((h->hba_inquiry_data[2] & 0x07) == 5)
+ return 1;
+ return 0;
+}
+
static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
"UNKNOWN"
};
@@ -431,7 +486,7 @@ static ssize_t raid_level_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t l = 0;
- int rlevel;
+ unsigned char rlevel;
struct ctlr_info *h;
struct scsi_device *sdev;
struct hpsa_scsi_dev_t *hdev;
@@ -455,7 +510,7 @@ static ssize_t raid_level_show(struct device *dev,
rlevel = hdev->raid_level;
spin_unlock_irqrestore(&h->lock, flags);
- if (rlevel < 0 || rlevel > RAID_UNKNOWN)
+ if (rlevel > RAID_UNKNOWN)
rlevel = RAID_UNKNOWN;
l = snprintf(buf, PAGE_SIZE, "RAID %s\n", raid_label[rlevel]);
return l;
@@ -620,6 +675,24 @@ lun_assigned:
return 0;
}
+/* Replace an entry from h->dev[] array. */
+static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
+ int entry, struct hpsa_scsi_dev_t *new_entry,
+ struct hpsa_scsi_dev_t *added[], int *nadded,
+ struct hpsa_scsi_dev_t *removed[], int *nremoved)
+{
+ /* assumes h->devlock is held */
+ BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA);
+ removed[*nremoved] = h->dev[entry];
+ (*nremoved)++;
+ h->dev[entry] = new_entry;
+ added[*nadded] = new_entry;
+ (*nadded)++;
+ dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d changed.\n",
+ scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
+ new_entry->target, new_entry->lun);
+}
+
/* Remove an entry from h->dev[] array. */
static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
struct hpsa_scsi_dev_t *removed[], int *nremoved)
@@ -628,8 +701,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
int i;
struct hpsa_scsi_dev_t *sd;
- if (entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA)
- BUG();
+ BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA);
sd = h->dev[entry];
removed[*nremoved] = h->dev[entry];
@@ -722,6 +794,8 @@ static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
#define DEVICE_CHANGED 1
#define DEVICE_SAME 2
for (i = 0; i < haystack_size; i++) {
+ if (haystack[i] == NULL) /* previously removed. */
+ continue;
if (SCSI3ADDR_EQ(needle->scsi3addr, haystack[i]->scsi3addr)) {
*index = i;
if (device_is_the_same(needle, haystack[i]))
@@ -734,7 +808,7 @@ static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
return DEVICE_NOT_FOUND;
}
-static int adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
+static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
struct hpsa_scsi_dev_t *sd[], int nsds)
{
/* sd contains scsi3 addresses and devtypes, and inquiry
@@ -779,12 +853,12 @@ static int adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
continue; /* remove ^^^, hence i not incremented */
} else if (device_change == DEVICE_CHANGED) {
changes++;
- hpsa_scsi_remove_entry(h, hostno, i,
- removed, &nremoved);
- (void) hpsa_scsi_add_entry(h, hostno, sd[entry],
- added, &nadded);
- /* add can't fail, we just removed one. */
- sd[entry] = NULL; /* prevent it from being freed */
+ hpsa_scsi_replace_entry(h, hostno, i, sd[entry],
+ added, &nadded, removed, &nremoved);
+ /* Set it to NULL to prevent it from being freed
+ * at the bottom of hpsa_update_scsi_devices()
+ */
+ sd[entry] = NULL;
}
i++;
}
@@ -860,7 +934,6 @@ static int adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
free_and_out:
kfree(added);
kfree(removed);
- return 0;
}
/*
@@ -900,7 +973,7 @@ static int hpsa_slave_alloc(struct scsi_device *sdev)
static void hpsa_slave_destroy(struct scsi_device *sdev)
{
- return; /* nothing to do. */
+ /* nothing to do. */
}
static void hpsa_scsi_setup(struct ctlr_info *h)
@@ -908,11 +981,10 @@ static void hpsa_scsi_setup(struct ctlr_info *h)
h->ndevices = 0;
h->scsi_host = NULL;
spin_lock_init(&h->devlock);
- return;
}
static void complete_scsi_command(struct CommandList *cp,
- int timeout, __u32 tag)
+ int timeout, u32 tag)
{
struct scsi_cmnd *cmd;
struct ctlr_info *h;
@@ -987,7 +1059,6 @@ static void complete_scsi_command(struct CommandList *cp,
* required
*/
if ((asc == 0x04) && (ascq == 0x03)) {
- cmd->result = DID_NO_CONNECT << 16;
dev_warn(&h->pdev->dev, "cp %p "
"has check condition: unit "
"not ready, manual "
@@ -995,14 +1066,22 @@ static void complete_scsi_command(struct CommandList *cp,
break;
}
}
-
-
+ if (sense_key == ABORTED_COMMAND) {
+ /* Aborted command is retryable */
+ dev_warn(&h->pdev->dev, "cp %p "
+ "has check condition: aborted command: "
+ "ASC: 0x%x, ASCQ: 0x%x\n",
+ cp, asc, ascq);
+ cmd->result = DID_SOFT_ERROR << 16;
+ break;
+ }
/* Must be some other type of check condition */
dev_warn(&h->pdev->dev, "cp %p has check condition: "
"unknown type: "
"Sense: 0x%x, ASC: 0x%x, ASCQ: 0x%x, "
"Returning result: 0x%x, "
"cmd=[%02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x]\n",
cp, sense_key, asc, ascq,
cmd->result,
@@ -1010,7 +1089,10 @@ static void complete_scsi_command(struct CommandList *cp,
cmd->cmnd[2], cmd->cmnd[3],
cmd->cmnd[4], cmd->cmnd[5],
cmd->cmnd[6], cmd->cmnd[7],
- cmd->cmnd[8], cmd->cmnd[9]);
+ cmd->cmnd[8], cmd->cmnd[9],
+ cmd->cmnd[10], cmd->cmnd[11],
+ cmd->cmnd[12], cmd->cmnd[13],
+ cmd->cmnd[14], cmd->cmnd[15]);
break;
}
@@ -1086,7 +1168,7 @@ static void complete_scsi_command(struct CommandList *cp,
dev_warn(&h->pdev->dev, "cp %p reports abort failed\n", cp);
break;
case CMD_UNSOLICITED_ABORT:
- cmd->result = DID_ABORT << 16;
+ cmd->result = DID_RESET << 16;
dev_warn(&h->pdev->dev, "cp %p aborted do to an unsolicited "
"abort\n", cp);
break;
@@ -1119,9 +1201,11 @@ static int hpsa_scsi_detect(struct ctlr_info *h)
sh->max_cmd_len = MAX_COMMAND_SIZE;
sh->max_lun = HPSA_MAX_LUN;
sh->max_id = HPSA_MAX_LUN;
+ sh->can_queue = h->nr_cmds;
+ sh->cmd_per_lun = h->nr_cmds;
h->scsi_host = sh;
sh->hostdata[0] = (unsigned long) h;
- sh->irq = h->intr[SIMPLE_MODE_INT];
+ sh->irq = h->intr[PERF_MODE_INT];
sh->unique_id = sh->irq;
error = scsi_add_host(sh, &h->pdev->dev);
if (error)
@@ -1133,11 +1217,11 @@ static int hpsa_scsi_detect(struct ctlr_info *h)
dev_err(&h->pdev->dev, "hpsa_scsi_detect: scsi_add_host"
" failed for controller %d\n", h->ctlr);
scsi_host_put(sh);
- return -1;
+ return error;
fail:
dev_err(&h->pdev->dev, "hpsa_scsi_detect: scsi_host_alloc"
" failed for controller %d\n", h->ctlr);
- return -1;
+ return -ENOMEM;
}
static void hpsa_pci_unmap(struct pci_dev *pdev,
@@ -1160,7 +1244,7 @@ static void hpsa_map_one(struct pci_dev *pdev,
size_t buflen,
int data_direction)
{
- __u64 addr64;
+ u64 addr64;
if (buflen == 0 || data_direction == PCI_DMA_NONE) {
cp->Header.SGList = 0;
@@ -1168,14 +1252,14 @@ static void hpsa_map_one(struct pci_dev *pdev,
return;
}
- addr64 = (__u64) pci_map_single(pdev, buf, buflen, data_direction);
+ addr64 = (u64) pci_map_single(pdev, buf, buflen, data_direction);
cp->SG[0].Addr.lower =
- (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF);
+ (u32) (addr64 & (u64) 0x00000000FFFFFFFF);
cp->SG[0].Addr.upper =
- (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF);
+ (u32) ((addr64 >> 32) & (u64) 0x00000000FFFFFFFF);
cp->SG[0].Len = buflen;
- cp->Header.SGList = (__u8) 1; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (__u16) 1; /* total sgs in this cmd list */
+ cp->Header.SGList = (u8) 1; /* no. SGs contig in this cmd */
+ cp->Header.SGTotal = (u16) 1; /* total sgs in this cmd list */
}
static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
@@ -1274,7 +1358,7 @@ static int hpsa_scsi_do_inquiry(struct ctlr_info *h, unsigned char *scsi3addr,
if (c == NULL) { /* trouble... */
dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
- return -1;
+ return -ENOMEM;
}
fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize, page, scsi3addr, TYPE_CMD);
@@ -1366,9 +1450,8 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
dev_err(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
return -1;
}
-
- memset(&scsi3addr[0], 0, 8); /* address the controller */
-
+ /* address the controller */
+ memset(scsi3addr, 0, sizeof(scsi3addr));
fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h,
buf, bufsize, 0, scsi3addr, TYPE_CMD);
if (extended_response)
@@ -1409,13 +1492,12 @@ static int hpsa_update_device_info(struct ctlr_info *h,
unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device)
{
#define OBDR_TAPE_INQ_SIZE 49
- unsigned char *inq_buff = NULL;
+ unsigned char *inq_buff;
- inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
+ inq_buff = kzalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
if (!inq_buff)
goto bail_out;
- memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE);
/* Do an inquiry to the device to see what it is. */
if (hpsa_scsi_do_inquiry(h, scsi3addr, 0, inq_buff,
(unsigned char) OBDR_TAPE_INQ_SIZE) != 0) {
@@ -1485,32 +1567,51 @@ static int is_msa2xxx(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
* in hpsa_find_target_lun, called by hpsa_scsi_add_entry.)
*/
static void figure_bus_target_lun(struct ctlr_info *h,
- __u8 *lunaddrbytes, int *bus, int *target, int *lun,
+ u8 *lunaddrbytes, int *bus, int *target, int *lun,
struct hpsa_scsi_dev_t *device)
{
-
- __u32 lunid;
+ u32 lunid;
if (is_logical_dev_addr_mode(lunaddrbytes)) {
/* logical device */
- memcpy(&lunid, lunaddrbytes, sizeof(lunid));
- lunid = le32_to_cpu(lunid);
-
- if (is_msa2xxx(h, device)) {
- *bus = 1;
- *target = (lunid >> 16) & 0x3fff;
- *lun = lunid & 0x00ff;
- } else {
+ if (unlikely(is_scsi_rev_5(h))) {
+ /* p1210m, logical drives lun assignments
+ * match SCSI REPORT LUNS data.
+ */
+ lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
*bus = 0;
- *lun = 0;
- *target = lunid & 0x3fff;
+ *target = 0;
+ *lun = (lunid & 0x3fff) + 1;
+ } else {
+ /* not p1210m... */
+ lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
+ if (is_msa2xxx(h, device)) {
+ /* msa2xxx way, put logicals on bus 1
+ * and match target/lun numbers box
+ * reports.
+ */
+ *bus = 1;
+ *target = (lunid >> 16) & 0x3fff;
+ *lun = lunid & 0x00ff;
+ } else {
+ /* Traditional smart array way. */
+ *bus = 0;
+ *lun = 0;
+ *target = lunid & 0x3fff;
+ }
}
} else {
/* physical device */
if (is_hba_lunid(lunaddrbytes))
- *bus = 3;
+ if (unlikely(is_scsi_rev_5(h))) {
+ *bus = 0; /* put p1210m ctlr at 0,0,0 */
+ *target = 0;
+ *lun = 0;
+ return;
+ } else
+ *bus = 3; /* traditional smartarray */
else
- *bus = 2;
+ *bus = 2; /* physical disk */
*target = -1;
*lun = -1; /* we will fill these in later. */
}
@@ -1529,7 +1630,7 @@ static void figure_bus_target_lun(struct ctlr_info *h,
*/
static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
struct hpsa_scsi_dev_t *tmpdevice,
- struct hpsa_scsi_dev_t *this_device, __u8 *lunaddrbytes,
+ struct hpsa_scsi_dev_t *this_device, u8 *lunaddrbytes,
int bus, int target, int lun, unsigned long lunzerobits[],
int *nmsa2xxx_enclosures)
{
@@ -1550,6 +1651,9 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
if (is_hba_lunid(scsi3addr))
return 0; /* Don't add the RAID controller here. */
+ if (is_scsi_rev_5(h))
+ return 0; /* p1210m doesn't need to do this. */
+
#define MAX_MSA2XXX_ENCLOSURES 32
if (*nmsa2xxx_enclosures >= MAX_MSA2XXX_ENCLOSURES) {
dev_warn(&h->pdev->dev, "Maximum number of MSA2XXX "
@@ -1576,18 +1680,14 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
*/
static int hpsa_gather_lun_info(struct ctlr_info *h,
int reportlunsize,
- struct ReportLUNdata *physdev, __u32 *nphysicals,
- struct ReportLUNdata *logdev, __u32 *nlogicals)
+ struct ReportLUNdata *physdev, u32 *nphysicals,
+ struct ReportLUNdata *logdev, u32 *nlogicals)
{
if (hpsa_scsi_do_report_phys_luns(h, physdev, reportlunsize, 0)) {
dev_err(&h->pdev->dev, "report physical LUNs failed.\n");
return -1;
}
- memcpy(nphysicals, &physdev->LUNListLength[0], sizeof(*nphysicals));
- *nphysicals = be32_to_cpu(*nphysicals) / 8;
-#ifdef DEBUG
- dev_info(&h->pdev->dev, "number of physical luns is %d\n", *nphysicals);
-#endif
+ *nphysicals = be32_to_cpu(*((__be32 *)physdev->LUNListLength)) / 8;
if (*nphysicals > HPSA_MAX_PHYS_LUN) {
dev_warn(&h->pdev->dev, "maximum physical LUNs (%d) exceeded."
" %d LUNs ignored.\n", HPSA_MAX_PHYS_LUN,
@@ -1598,11 +1698,7 @@ static int hpsa_gather_lun_info(struct ctlr_info *h,
dev_err(&h->pdev->dev, "report logical LUNs failed.\n");
return -1;
}
- memcpy(nlogicals, &logdev->LUNListLength[0], sizeof(*nlogicals));
- *nlogicals = be32_to_cpu(*nlogicals) / 8;
-#ifdef DEBUG
- dev_info(&h->pdev->dev, "number of logical luns is %d\n", *nlogicals);
-#endif
+ *nlogicals = be32_to_cpu(*((__be32 *) logdev->LUNListLength)) / 8;
/* Reject Logicals in excess of our max capability. */
if (*nlogicals > HPSA_MAX_LUN) {
dev_warn(&h->pdev->dev,
@@ -1621,6 +1717,31 @@ static int hpsa_gather_lun_info(struct ctlr_info *h,
return 0;
}
+u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i,
+ int nphysicals, int nlogicals, struct ReportLUNdata *physdev_list,
+ struct ReportLUNdata *logdev_list)
+{
+ /* Helper function, figure out where the LUN ID info is coming from
+ * given index i, lists of physical and logical devices, where in
+ * the list the raid controller is supposed to appear (first or last)
+ */
+
+ int logicals_start = nphysicals + (raid_ctlr_position == 0);
+ int last_device = nphysicals + nlogicals + (raid_ctlr_position == 0);
+
+ if (i == raid_ctlr_position)
+ return RAID_CTLR_LUNID;
+
+ if (i < logicals_start)
+ return &physdev_list->LUN[i - (raid_ctlr_position == 0)][0];
+
+ if (i < last_device)
+ return &logdev_list->LUN[i - nphysicals -
+ (raid_ctlr_position == 0)][0];
+ BUG();
+ return NULL;
+}
+
static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
{
/* the idea here is we could get notified
@@ -1636,14 +1757,15 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
struct ReportLUNdata *physdev_list = NULL;
struct ReportLUNdata *logdev_list = NULL;
unsigned char *inq_buff = NULL;
- __u32 nphysicals = 0;
- __u32 nlogicals = 0;
- __u32 ndev_allocated = 0;
+ u32 nphysicals = 0;
+ u32 nlogicals = 0;
+ u32 ndev_allocated = 0;
struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice;
int ncurrent = 0;
int reportlunsize = sizeof(*physdev_list) + HPSA_MAX_PHYS_LUN * 8;
int i, nmsa2xxx_enclosures, ndevs_to_allocate;
int bus, target, lun;
+ int raid_ctlr_position;
DECLARE_BITMAP(lunzerobits, HPSA_MAX_TARGETS_PER_CTLR);
currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_SCSI_DEVS_PER_HBA,
@@ -1681,23 +1803,22 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
ndev_allocated++;
}
+ if (unlikely(is_scsi_rev_5(h)))
+ raid_ctlr_position = 0;
+ else
+ raid_ctlr_position = nphysicals + nlogicals;
+
/* adjust our table of devices */
nmsa2xxx_enclosures = 0;
for (i = 0; i < nphysicals + nlogicals + 1; i++) {
- __u8 *lunaddrbytes;
+ u8 *lunaddrbytes;
/* Figure out where the LUN ID info is coming from */
- if (i < nphysicals)
- lunaddrbytes = &physdev_list->LUN[i][0];
- else
- if (i < nphysicals + nlogicals)
- lunaddrbytes =
- &logdev_list->LUN[i-nphysicals][0];
- else /* jam in the RAID controller at the end */
- lunaddrbytes = RAID_CTLR_LUNID;
-
+ lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
+ i, nphysicals, nlogicals, physdev_list, logdev_list);
/* skip masked physical devices. */
- if (lunaddrbytes[3] & 0xC0 && i < nphysicals)
+ if (lunaddrbytes[3] & 0xC0 &&
+ i < nphysicals + (raid_ctlr_position == 0))
continue;
/* Get device type, vendor, model, device id */
@@ -1777,7 +1898,6 @@ out:
kfree(inq_buff);
kfree(physdev_list);
kfree(logdev_list);
- return;
}
/* hpsa_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
@@ -1790,7 +1910,7 @@ static int hpsa_scatter_gather(struct pci_dev *pdev,
{
unsigned int len;
struct scatterlist *sg;
- __u64 addr64;
+ u64 addr64;
int use_sg, i;
BUG_ON(scsi_sg_count(cmd) > MAXSGENTRIES);
@@ -1803,20 +1923,20 @@ static int hpsa_scatter_gather(struct pci_dev *pdev,
goto sglist_finished;
scsi_for_each_sg(cmd, sg, use_sg, i) {
- addr64 = (__u64) sg_dma_address(sg);
+ addr64 = (u64) sg_dma_address(sg);
len = sg_dma_len(sg);
cp->SG[i].Addr.lower =
- (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF);
+ (u32) (addr64 & (u64) 0x00000000FFFFFFFF);
cp->SG[i].Addr.upper =
- (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF);
+ (u32) ((addr64 >> 32) & (u64) 0x00000000FFFFFFFF);
cp->SG[i].Len = len;
cp->SG[i].Ext = 0; /* we are not chaining */
}
sglist_finished:
- cp->Header.SGList = (__u8) use_sg; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (__u16) use_sg; /* total sgs in this cmd list */
+ cp->Header.SGList = (u8) use_sg; /* no. SGs contig in this cmd */
+ cp->Header.SGTotal = (u16) use_sg; /* total sgs in this cmd list */
return 0;
}
@@ -1860,7 +1980,8 @@ static int hpsa_scsi_queue_command(struct scsi_cmnd *cmd,
c->scsi_cmd = cmd;
c->Header.ReplyQueue = 0; /* unused in simple mode */
memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
- c->Header.Tag.lower = c->busaddr; /* Use k. address of cmd as tag */
+ c->Header.Tag.lower = (c->cmdindex << DIRECT_LOOKUP_SHIFT);
+ c->Header.Tag.lower |= DIRECT_LOOKUP_BIT;
/* Fill in the request block... */
@@ -1914,6 +2035,48 @@ static int hpsa_scsi_queue_command(struct scsi_cmnd *cmd,
return 0;
}
+static void hpsa_scan_start(struct Scsi_Host *sh)
+{
+ struct ctlr_info *h = shost_to_hba(sh);
+ unsigned long flags;
+
+ /* wait until any scan already in progress is finished. */
+ while (1) {
+ spin_lock_irqsave(&h->scan_lock, flags);
+ if (h->scan_finished)
+ break;
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+ wait_event(h->scan_wait_queue, h->scan_finished);
+ /* Note: We don't need to worry about a race between this
+ * thread and driver unload because the midlayer will
+ * have incremented the reference count, so unload won't
+ * happen if we're in here.
+ */
+ }
+ h->scan_finished = 0; /* mark scan as in progress */
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+
+ hpsa_update_scsi_devices(h, h->scsi_host->host_no);
+
+ spin_lock_irqsave(&h->scan_lock, flags);
+ h->scan_finished = 1; /* mark scan as finished. */
+ wake_up_all(&h->scan_wait_queue);
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+}
+
+static int hpsa_scan_finished(struct Scsi_Host *sh,
+ unsigned long elapsed_time)
+{
+ struct ctlr_info *h = shost_to_hba(sh);
+ unsigned long flags;
+ int finished;
+
+ spin_lock_irqsave(&h->scan_lock, flags);
+ finished = h->scan_finished;
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+ return finished;
+}
+
static void hpsa_unregister_scsi(struct ctlr_info *h)
{
/* we are being forcibly unloaded, and may not refuse. */
@@ -1926,7 +2089,6 @@ static int hpsa_register_scsi(struct ctlr_info *h)
{
int rc;
- hpsa_update_scsi_devices(h, -1);
rc = hpsa_scsi_detect(h);
if (rc != 0)
dev_err(&h->pdev->dev, "hpsa_register_scsi: failed"
@@ -2003,14 +2165,14 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
h = sdev_to_hba(scsicmd->device);
if (h == NULL) /* paranoia */
return FAILED;
- dev_warn(&h->pdev->dev, "resetting drive\n");
-
dev = scsicmd->device->hostdata;
if (!dev) {
dev_err(&h->pdev->dev, "hpsa_eh_device_reset_handler: "
"device lookup failed.\n");
return FAILED;
}
+ dev_warn(&h->pdev->dev, "resetting device %d:%d:%d:%d\n",
+ h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
/* send a reset to the SCSI LUN which the command was sent to */
rc = hpsa_send_reset(h, dev->scsi3addr);
if (rc == 0 && wait_for_device_to_become_ready(h, dev->scsi3addr) == 0)
@@ -2053,8 +2215,8 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
c->cmdindex = i;
INIT_HLIST_NODE(&c->list);
- c->busaddr = (__u32) cmd_dma_handle;
- temp64.val = (__u64) err_dma_handle;
+ c->busaddr = (u32) cmd_dma_handle;
+ temp64.val = (u64) err_dma_handle;
c->ErrDesc.Addr.lower = temp64.val32.lower;
c->ErrDesc.Addr.upper = temp64.val32.upper;
c->ErrDesc.Len = sizeof(*c->err_info);
@@ -2091,8 +2253,8 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h)
memset(c->err_info, 0, sizeof(*c->err_info));
INIT_HLIST_NODE(&c->list);
- c->busaddr = (__u32) cmd_dma_handle;
- temp64.val = (__u64) err_dma_handle;
+ c->busaddr = (u32) cmd_dma_handle;
+ temp64.val = (u64) err_dma_handle;
c->ErrDesc.Addr.lower = temp64.val32.lower;
c->ErrDesc.Addr.upper = temp64.val32.upper;
c->ErrDesc.Len = sizeof(*c->err_info);
@@ -2125,50 +2287,6 @@ static void cmd_special_free(struct ctlr_info *h, struct CommandList *c)
#ifdef CONFIG_COMPAT
-static int do_ioctl(struct scsi_device *dev, int cmd, void *arg)
-{
- int ret;
-
- lock_kernel();
- ret = hpsa_ioctl(dev, cmd, arg);
- unlock_kernel();
- return ret;
-}
-
-static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg);
-static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
- int cmd, void *arg);
-
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg)
-{
- switch (cmd) {
- case CCISS_GETPCIINFO:
- case CCISS_GETINTINFO:
- case CCISS_SETINTINFO:
- case CCISS_GETNODENAME:
- case CCISS_SETNODENAME:
- case CCISS_GETHEARTBEAT:
- case CCISS_GETBUSTYPES:
- case CCISS_GETFIRMVER:
- case CCISS_GETDRIVVER:
- case CCISS_REVALIDVOLS:
- case CCISS_DEREGDISK:
- case CCISS_REGNEWDISK:
- case CCISS_REGNEWD:
- case CCISS_RESCANDISK:
- case CCISS_GETLUNINFO:
- return do_ioctl(dev, cmd, arg);
-
- case CCISS_PASSTHRU32:
- return hpsa_ioctl32_passthru(dev, cmd, arg);
- case CCISS_BIG_PASSTHRU32:
- return hpsa_ioctl32_big_passthru(dev, cmd, arg);
-
- default:
- return -ENOIOCTLCMD;
- }
-}
-
static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg)
{
IOCTL32_Command_struct __user *arg32 =
@@ -2193,7 +2311,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg)
if (err)
return -EFAULT;
- err = do_ioctl(dev, CCISS_PASSTHRU, (void *)p);
+ err = hpsa_ioctl(dev, CCISS_PASSTHRU, (void *)p);
if (err)
return err;
err |= copy_in_user(&arg32->error_info, &p->error_info,
@@ -2230,7 +2348,7 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
if (err)
return -EFAULT;
- err = do_ioctl(dev, CCISS_BIG_PASSTHRU, (void *)p);
+ err = hpsa_ioctl(dev, CCISS_BIG_PASSTHRU, (void *)p);
if (err)
return err;
err |= copy_in_user(&arg32->error_info, &p->error_info,
@@ -2239,6 +2357,36 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
return -EFAULT;
return err;
}
+
+static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg)
+{
+ switch (cmd) {
+ case CCISS_GETPCIINFO:
+ case CCISS_GETINTINFO:
+ case CCISS_SETINTINFO:
+ case CCISS_GETNODENAME:
+ case CCISS_SETNODENAME:
+ case CCISS_GETHEARTBEAT:
+ case CCISS_GETBUSTYPES:
+ case CCISS_GETFIRMVER:
+ case CCISS_GETDRIVVER:
+ case CCISS_REVALIDVOLS:
+ case CCISS_DEREGDISK:
+ case CCISS_REGNEWDISK:
+ case CCISS_REGNEWD:
+ case CCISS_RESCANDISK:
+ case CCISS_GETLUNINFO:
+ return hpsa_ioctl(dev, cmd, arg);
+
+ case CCISS_PASSTHRU32:
+ return hpsa_ioctl32_passthru(dev, cmd, arg);
+ case CCISS_BIG_PASSTHRU32:
+ return hpsa_ioctl32_big_passthru(dev, cmd, arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
#endif
static int hpsa_getpciinfo_ioctl(struct ctlr_info *h, void __user *argp)
@@ -2378,8 +2526,8 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
BYTE sg_used = 0;
int status = 0;
int i;
- __u32 left;
- __u32 sz;
+ u32 left;
+ u32 sz;
BYTE __user *data_ptr;
if (!argp)
@@ -2527,7 +2675,7 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg)
case CCISS_DEREGDISK:
case CCISS_REGNEWDISK:
case CCISS_REGNEWD:
- hpsa_update_scsi_devices(h, dev->host->host_no);
+ hpsa_scan_start(h->scsi_host);
return 0;
case CCISS_GETPCIINFO:
return hpsa_getpciinfo_ioctl(h, argp);
@@ -2542,8 +2690,8 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg)
}
}
-static void fill_cmd(struct CommandList *c, __u8 cmd, struct ctlr_info *h,
- void *buff, size_t size, __u8 page_code, unsigned char *scsi3addr,
+static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
+ void *buff, size_t size, u8 page_code, unsigned char *scsi3addr,
int cmd_type)
{
int pci_dir = XFER_NONE;
@@ -2710,19 +2858,20 @@ static inline unsigned long get_next_completion(struct ctlr_info *h)
return h->access.command_completed(h);
}
-static inline int interrupt_pending(struct ctlr_info *h)
+static inline bool interrupt_pending(struct ctlr_info *h)
{
return h->access.intr_pending(h);
}
static inline long interrupt_not_for_us(struct ctlr_info *h)
{
- return ((h->access.intr_pending(h) == 0) ||
- (h->interrupts_enabled == 0));
+ return !(h->msi_vector || h->msix_vector) &&
+ ((h->access.intr_pending(h) == 0) ||
+ (h->interrupts_enabled == 0));
}
-static inline int bad_tag(struct ctlr_info *h, __u32 tag_index,
- __u32 raw_tag)
+static inline int bad_tag(struct ctlr_info *h, u32 tag_index,
+ u32 raw_tag)
{
if (unlikely(tag_index >= h->nr_cmds)) {
dev_warn(&h->pdev->dev, "bad tag 0x%08x ignored.\n", raw_tag);
@@ -2731,7 +2880,7 @@ static inline int bad_tag(struct ctlr_info *h, __u32 tag_index,
return 0;
}
-static inline void finish_cmd(struct CommandList *c, __u32 raw_tag)
+static inline void finish_cmd(struct CommandList *c, u32 raw_tag)
{
removeQ(c);
if (likely(c->cmd_type == CMD_SCSI))
@@ -2740,42 +2889,79 @@ static inline void finish_cmd(struct CommandList *c, __u32 raw_tag)
complete(c->waiting);
}
+static inline u32 hpsa_tag_contains_index(u32 tag)
+{
+#define DIRECT_LOOKUP_BIT 0x10
+ return tag & DIRECT_LOOKUP_BIT;
+}
+
+static inline u32 hpsa_tag_to_index(u32 tag)
+{
+#define DIRECT_LOOKUP_SHIFT 5
+ return tag >> DIRECT_LOOKUP_SHIFT;
+}
+
+static inline u32 hpsa_tag_discard_error_bits(u32 tag)
+{
+#define HPSA_ERROR_BITS 0x03
+ return tag & ~HPSA_ERROR_BITS;
+}
+
+/* process completion of an indexed ("direct lookup") command */
+static inline u32 process_indexed_cmd(struct ctlr_info *h,
+ u32 raw_tag)
+{
+ u32 tag_index;
+ struct CommandList *c;
+
+ tag_index = hpsa_tag_to_index(raw_tag);
+ if (bad_tag(h, tag_index, raw_tag))
+ return next_command(h);
+ c = h->cmd_pool + tag_index;
+ finish_cmd(c, raw_tag);
+ return next_command(h);
+}
+
+/* process completion of a non-indexed command */
+static inline u32 process_nonindexed_cmd(struct ctlr_info *h,
+ u32 raw_tag)
+{
+ u32 tag;
+ struct CommandList *c = NULL;
+ struct hlist_node *tmp;
+
+ tag = hpsa_tag_discard_error_bits(raw_tag);
+ hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+ if ((c->busaddr & 0xFFFFFFE0) == (tag & 0xFFFFFFE0)) {
+ finish_cmd(c, raw_tag);
+ return next_command(h);
+ }
+ }
+ bad_tag(h, h->nr_cmds + 1, raw_tag);
+ return next_command(h);
+}
+
static irqreturn_t do_hpsa_intr(int irq, void *dev_id)
{
struct ctlr_info *h = dev_id;
- struct CommandList *c;
unsigned long flags;
- __u32 raw_tag, tag, tag_index;
- struct hlist_node *tmp;
+ u32 raw_tag;
if (interrupt_not_for_us(h))
return IRQ_NONE;
spin_lock_irqsave(&h->lock, flags);
- while (interrupt_pending(h)) {
- while ((raw_tag = get_next_completion(h)) != FIFO_EMPTY) {
- if (likely(HPSA_TAG_CONTAINS_INDEX(raw_tag))) {
- tag_index = HPSA_TAG_TO_INDEX(raw_tag);
- if (bad_tag(h, tag_index, raw_tag))
- return IRQ_HANDLED;
- c = h->cmd_pool + tag_index;
- finish_cmd(c, raw_tag);
- continue;
- }
- tag = HPSA_TAG_DISCARD_ERROR_BITS(raw_tag);
- c = NULL;
- hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
- if (c->busaddr == tag) {
- finish_cmd(c, raw_tag);
- break;
- }
- }
- }
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY) {
+ if (hpsa_tag_contains_index(raw_tag))
+ raw_tag = process_indexed_cmd(h, raw_tag);
+ else
+ raw_tag = process_nonindexed_cmd(h, raw_tag);
}
spin_unlock_irqrestore(&h->lock, flags);
return IRQ_HANDLED;
}
-/* Send a message CDB to the firmware. */
+/* Send a message CDB to the firmwart. */
static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
unsigned char type)
{
@@ -2841,7 +3027,7 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
for (i = 0; i < HPSA_MSG_SEND_RETRY_LIMIT; i++) {
tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
- if (HPSA_TAG_DISCARD_ERROR_BITS(tag) == paddr32)
+ if (hpsa_tag_discard_error_bits(tag) == paddr32)
break;
msleep(HPSA_MSG_SEND_RETRY_INTERVAL_MSECS);
}
@@ -3063,7 +3249,7 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
*/
static void __devinit hpsa_interrupt_mode(struct ctlr_info *h,
- struct pci_dev *pdev, __u32 board_id)
+ struct pci_dev *pdev, u32 board_id)
{
#ifdef CONFIG_PCI_MSI
int err;
@@ -3107,22 +3293,22 @@ static void __devinit hpsa_interrupt_mode(struct ctlr_info *h,
default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
- h->intr[SIMPLE_MODE_INT] = pdev->irq;
- return;
+ h->intr[PERF_MODE_INT] = pdev->irq;
}
static int hpsa_pci_init(struct ctlr_info *h, struct pci_dev *pdev)
{
ushort subsystem_vendor_id, subsystem_device_id, command;
- __u32 board_id, scratchpad = 0;
- __u64 cfg_offset;
- __u32 cfg_base_addr;
- __u64 cfg_base_addr_index;
+ u32 board_id, scratchpad = 0;
+ u64 cfg_offset;
+ u32 cfg_base_addr;
+ u64 cfg_base_addr_index;
+ u32 trans_offset;
int i, prod_index, err;
subsystem_vendor_id = pdev->subsystem_vendor;
subsystem_device_id = pdev->subsystem_device;
- board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) |
+ board_id = (((u32) (subsystem_device_id << 16) & 0xffff0000) |
subsystem_vendor_id);
for (i = 0; i < ARRAY_SIZE(products); i++)
@@ -3199,7 +3385,7 @@ static int hpsa_pci_init(struct ctlr_info *h, struct pci_dev *pdev)
/* get the address index number */
cfg_base_addr = readl(h->vaddr + SA5_CTCFG_OFFSET);
- cfg_base_addr &= (__u32) 0x0000ffff;
+ cfg_base_addr &= (u32) 0x0000ffff;
cfg_base_addr_index = find_PCI_BAR_index(pdev, cfg_base_addr);
if (cfg_base_addr_index == -1) {
dev_warn(&pdev->dev, "cannot find cfg_base_addr_index\n");
@@ -3211,11 +3397,14 @@ static int hpsa_pci_init(struct ctlr_info *h, struct pci_dev *pdev)
h->cfgtable = remap_pci_mem(pci_resource_start(pdev,
cfg_base_addr_index) + cfg_offset,
sizeof(h->cfgtable));
- h->board_id = board_id;
-
- /* Query controller for max supported commands: */
- h->max_commands = readl(&(h->cfgtable->CmdsOutMax));
+ /* Find performant mode table. */
+ trans_offset = readl(&(h->cfgtable->TransMethodOffset));
+ h->transtable = remap_pci_mem(pci_resource_start(pdev,
+ cfg_base_addr_index)+cfg_offset+trans_offset,
+ sizeof(*h->transtable));
+ h->board_id = board_id;
+ h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
h->product_name = products[prod_index].product_name;
h->access = *(products[prod_index].access);
/* Allow room for some ioctls */
@@ -3232,7 +3421,7 @@ static int hpsa_pci_init(struct ctlr_info *h, struct pci_dev *pdev)
#ifdef CONFIG_X86
{
/* Need to enable prefetch in the SCSI core for 6400 in x86 */
- __u32 prefetch;
+ u32 prefetch;
prefetch = readl(&(h->cfgtable->SCSI_Prefetch));
prefetch |= 0x100;
writel(prefetch, &(h->cfgtable->SCSI_Prefetch));
@@ -3244,7 +3433,7 @@ static int hpsa_pci_init(struct ctlr_info *h, struct pci_dev *pdev)
* physical memory.
*/
if (board_id == 0x3225103C) {
- __u32 dma_prefetch;
+ u32 dma_prefetch;
dma_prefetch = readl(h->vaddr + I2O_DMA1_CFG);
dma_prefetch |= 0x8000;
writel(dma_prefetch, h->vaddr + I2O_DMA1_CFG);
@@ -3286,10 +3475,26 @@ err_out_free_res:
return err;
}
+static void __devinit hpsa_hba_inquiry(struct ctlr_info *h)
+{
+ int rc;
+
+#define HBA_INQUIRY_BYTE_COUNT 64
+ h->hba_inquiry_data = kmalloc(HBA_INQUIRY_BYTE_COUNT, GFP_KERNEL);
+ if (!h->hba_inquiry_data)
+ return;
+ rc = hpsa_scsi_do_inquiry(h, RAID_CTLR_LUNID, 0,
+ h->hba_inquiry_data, HBA_INQUIRY_BYTE_COUNT);
+ if (rc != 0) {
+ kfree(h->hba_inquiry_data);
+ h->hba_inquiry_data = NULL;
+ }
+}
+
static int __devinit hpsa_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- int i;
+ int i, rc;
int dac;
struct ctlr_info *h;
@@ -3314,17 +3519,23 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
}
}
- BUILD_BUG_ON(sizeof(struct CommandList) % 8);
+ /* Command structures must be aligned on a 32-byte boundary because
+ * the 5 lower bits of the address are used by the hardware. and by
+ * the driver. See comments in hpsa.h for more info.
+ */
+#define COMMANDLIST_ALIGNMENT 32
+ BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT);
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
- return -1;
+ return -ENOMEM;
h->busy_initializing = 1;
INIT_HLIST_HEAD(&h->cmpQ);
INIT_HLIST_HEAD(&h->reqQ);
mutex_init(&h->busy_shutting_down);
init_completion(&h->scan_wait);
- if (hpsa_pci_init(h, pdev) != 0)
+ rc = hpsa_pci_init(h, pdev);
+ if (rc != 0)
goto clean1;
sprintf(h->devname, "hpsa%d", number_of_controllers);
@@ -3333,27 +3544,32 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
h->pdev = pdev;
/* configure PCI DMA stuff */
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (rc == 0) {
dac = 1;
- else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))
- dac = 0;
- else {
- dev_err(&pdev->dev, "no suitable DMA available\n");
- goto clean1;
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc == 0) {
+ dac = 0;
+ } else {
+ dev_err(&pdev->dev, "no suitable DMA available\n");
+ goto clean1;
+ }
}
/* make sure the board interrupts are off */
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- if (request_irq(h->intr[SIMPLE_MODE_INT], do_hpsa_intr,
- IRQF_DISABLED | IRQF_SHARED, h->devname, h)) {
+ rc = request_irq(h->intr[PERF_MODE_INT], do_hpsa_intr,
+ IRQF_DISABLED, h->devname, h);
+ if (rc) {
dev_err(&pdev->dev, "unable to get irq %d for %s\n",
- h->intr[SIMPLE_MODE_INT], h->devname);
+ h->intr[PERF_MODE_INT], h->devname);
goto clean2;
}
- dev_info(&pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
- h->devname, pdev->device, pci_name(pdev),
- h->intr[SIMPLE_MODE_INT], dac ? "" : " not");
+ dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
+ h->devname, pdev->device,
+ h->intr[PERF_MODE_INT], dac ? "" : " not");
h->cmd_pool_bits =
kmalloc(((h->nr_cmds + BITS_PER_LONG -
@@ -3368,9 +3584,13 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
|| (h->cmd_pool == NULL)
|| (h->errinfo_pool == NULL)) {
dev_err(&pdev->dev, "out of memory");
+ rc = -ENOMEM;
goto clean4;
}
spin_lock_init(&h->lock);
+ spin_lock_init(&h->scan_lock);
+ init_waitqueue_head(&h->scan_wait_queue);
+ h->scan_finished = 1; /* no scan currently in progress */
pci_set_drvdata(pdev, h);
memset(h->cmd_pool_bits, 0,
@@ -3382,6 +3602,8 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
/* Turn the interrupts on so we can service requests */
h->access.set_intr_mask(h, HPSA_INTR_ON);
+ hpsa_put_ctlr_into_performant_mode(h);
+ hpsa_hba_inquiry(h);
hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */
h->busy_initializing = 0;
return 1;
@@ -3397,12 +3619,12 @@ clean4:
h->nr_cmds * sizeof(struct ErrorInfo),
h->errinfo_pool,
h->errinfo_pool_dhandle);
- free_irq(h->intr[SIMPLE_MODE_INT], h);
+ free_irq(h->intr[PERF_MODE_INT], h);
clean2:
clean1:
h->busy_initializing = 0;
kfree(h);
- return -1;
+ return rc;
}
static void hpsa_flush_cache(struct ctlr_info *h)
@@ -3441,7 +3663,7 @@ static void hpsa_shutdown(struct pci_dev *pdev)
*/
hpsa_flush_cache(h);
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- free_irq(h->intr[2], h);
+ free_irq(h->intr[PERF_MODE_INT], h);
#ifdef CONFIG_PCI_MSI
if (h->msix_vector)
pci_disable_msix(h->pdev);
@@ -3470,7 +3692,11 @@ static void __devexit hpsa_remove_one(struct pci_dev *pdev)
pci_free_consistent(h->pdev,
h->nr_cmds * sizeof(struct ErrorInfo),
h->errinfo_pool, h->errinfo_pool_dhandle);
+ pci_free_consistent(h->pdev, h->reply_pool_size,
+ h->reply_pool, h->reply_pool_dhandle);
kfree(h->cmd_pool_bits);
+ kfree(h->blockFetchTable);
+ kfree(h->hba_inquiry_data);
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
@@ -3502,6 +3728,129 @@ static struct pci_driver hpsa_pci_driver = {
.resume = hpsa_resume,
};
+/* Fill in bucket_map[], given nsgs (the max number of
+ * scatter gather elements supported) and bucket[],
+ * which is an array of 8 integers. The bucket[] array
+ * contains 8 different DMA transfer sizes (in 16
+ * byte increments) which the controller uses to fetch
+ * commands. This function fills in bucket_map[], which
+ * maps a given number of scatter gather elements to one of
+ * the 8 DMA transfer sizes. The point of it is to allow the
+ * controller to only do as much DMA as needed to fetch the
+ * command, with the DMA transfer size encoded in the lower
+ * bits of the command address.
+ */
+static void calc_bucket_map(int bucket[], int num_buckets,
+ int nsgs, int *bucket_map)
+{
+ int i, j, b, size;
+
+ /* even a command with 0 SGs requires 4 blocks */
+#define MINIMUM_TRANSFER_BLOCKS 4
+#define NUM_BUCKETS 8
+ /* Note, bucket_map must have nsgs+1 entries. */
+ for (i = 0; i <= nsgs; i++) {
+ /* Compute size of a command with i SG entries */
+ size = i + MINIMUM_TRANSFER_BLOCKS;
+ b = num_buckets; /* Assume the biggest bucket */
+ /* Find the bucket that is just big enough */
+ for (j = 0; j < 8; j++) {
+ if (bucket[j] >= size) {
+ b = j;
+ break;
+ }
+ }
+ /* for a command with i SG entries, use bucket b. */
+ bucket_map[i] = b;
+ }
+}
+
+static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
+{
+ u32 trans_support;
+ u64 trans_offset;
+ /* 5 = 1 s/g entry or 4k
+ * 6 = 2 s/g entry or 8k
+ * 8 = 4 s/g entry or 16k
+ * 10 = 6 s/g entry or 24k
+ */
+ int bft[8] = {5, 6, 8, 10, 12, 20, 28, 35}; /* for scatter/gathers */
+ int i = 0;
+ int l = 0;
+ unsigned long register_value;
+
+ trans_support = readl(&(h->cfgtable->TransportSupport));
+ if (!(trans_support & PERFORMANT_MODE))
+ return;
+
+ h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+ h->max_sg_entries = 32;
+ /* Performant mode ring buffer and supporting data structures */
+ h->reply_pool_size = h->max_commands * sizeof(u64);
+ h->reply_pool = pci_alloc_consistent(h->pdev, h->reply_pool_size,
+ &(h->reply_pool_dhandle));
+
+ /* Need a block fetch table for performant mode */
+ h->blockFetchTable = kmalloc(((h->max_sg_entries+1) *
+ sizeof(u32)), GFP_KERNEL);
+
+ if ((h->reply_pool == NULL)
+ || (h->blockFetchTable == NULL))
+ goto clean_up;
+
+ h->reply_pool_wraparound = 1; /* spec: init to 1 */
+
+ /* Controller spec: zero out this buffer. */
+ memset(h->reply_pool, 0, h->reply_pool_size);
+ h->reply_pool_head = h->reply_pool;
+
+ trans_offset = readl(&(h->cfgtable->TransMethodOffset));
+ bft[7] = h->max_sg_entries + 4;
+ calc_bucket_map(bft, ARRAY_SIZE(bft), 32, h->blockFetchTable);
+ for (i = 0; i < 8; i++)
+ writel(bft[i], &h->transtable->BlockFetch[i]);
+
+ /* size of controller ring buffer */
+ writel(h->max_commands, &h->transtable->RepQSize);
+ writel(1, &h->transtable->RepQCount);
+ writel(0, &h->transtable->RepQCtrAddrLow32);
+ writel(0, &h->transtable->RepQCtrAddrHigh32);
+ writel(h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32);
+ writel(0, &h->transtable->RepQAddr0High32);
+ writel(CFGTBL_Trans_Performant,
+ &(h->cfgtable->HostWrite.TransportRequest));
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
+ /* under certain very rare conditions, this can take awhile.
+ * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
+ * as we enter this code.) */
+ for (l = 0; l < MAX_CONFIG_WAIT; l++) {
+ register_value = readl(h->vaddr + SA5_DOORBELL);
+ if (!(register_value & CFGTBL_ChangeReq))
+ break;
+ /* delay and try again */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(10);
+ }
+ register_value = readl(&(h->cfgtable->TransportActive));
+ if (!(register_value & CFGTBL_Trans_Performant)) {
+ dev_warn(&h->pdev->dev, "unable to get board into"
+ " performant mode\n");
+ return;
+ }
+
+ /* Change the access methods to the performant access methods */
+ h->access = SA5_performant_access;
+ h->transMethod = CFGTBL_Trans_Performant;
+
+ return;
+
+clean_up:
+ if (h->reply_pool)
+ pci_free_consistent(h->pdev, h->reply_pool_size,
+ h->reply_pool, h->reply_pool_dhandle);
+ kfree(h->blockFetchTable);
+}
+
/*
* This is it. Register the PCI driver information for the cards we control
* the OS will call our registered routines when it finds one of our cards.
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 6bd1949144b..a0502b3ac17 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -33,7 +33,7 @@ struct access_method {
struct CommandList *c);
void (*set_intr_mask)(struct ctlr_info *h, unsigned long val);
unsigned long (*fifo_full)(struct ctlr_info *h);
- unsigned long (*intr_pending)(struct ctlr_info *h);
+ bool (*intr_pending)(struct ctlr_info *h);
unsigned long (*command_completed)(struct ctlr_info *h);
};
@@ -55,19 +55,20 @@ struct ctlr_info {
char *product_name;
char firm_ver[4]; /* Firmware version */
struct pci_dev *pdev;
- __u32 board_id;
+ u32 board_id;
void __iomem *vaddr;
unsigned long paddr;
int nr_cmds; /* Number of commands allowed on this controller */
struct CfgTable __iomem *cfgtable;
+ int max_sg_entries;
int interrupts_enabled;
int major;
int max_commands;
int commands_outstanding;
int max_outstanding; /* Debug */
int usage_count; /* number of opens all all minor devices */
-# define DOORBELL_INT 0
-# define PERF_MODE_INT 1
+# define PERF_MODE_INT 0
+# define DOORBELL_INT 1
# define SIMPLE_MODE_INT 2
# define MEMQ_MODE_INT 3
unsigned int intr[4];
@@ -93,6 +94,9 @@ struct ctlr_info {
int nr_frees;
int busy_initializing;
int busy_scanning;
+ int scan_finished;
+ spinlock_t scan_lock;
+ wait_queue_head_t scan_wait_queue;
struct mutex busy_shutting_down;
struct list_head scan_list;
struct completion scan_wait;
@@ -102,6 +106,24 @@ struct ctlr_info {
int ndevices; /* number of used elements in .dev[] array. */
#define HPSA_MAX_SCSI_DEVS_PER_HBA 256
struct hpsa_scsi_dev_t *dev[HPSA_MAX_SCSI_DEVS_PER_HBA];
+ /*
+ * Performant mode tables.
+ */
+ u32 trans_support;
+ u32 trans_offset;
+ struct TransTable_struct *transtable;
+ unsigned long transMethod;
+
+ /*
+ * Performant mode completion buffer
+ */
+ u64 *reply_pool;
+ dma_addr_t reply_pool_dhandle;
+ u64 *reply_pool_head;
+ size_t reply_pool_size;
+ unsigned char reply_pool_wraparound;
+ u32 *blockFetchTable;
+ unsigned char *hba_inquiry_data;
};
#define HPSA_ABORT_MSG 0
#define HPSA_DEVICE_RESET_MSG 1
@@ -164,9 +186,16 @@ struct ctlr_info {
#define HPSA_FIRMWARE_READY 0xffff0000 /* value in scratchpad register */
#define HPSA_ERROR_BIT 0x02
-#define HPSA_TAG_CONTAINS_INDEX(tag) ((tag) & 0x04)
-#define HPSA_TAG_TO_INDEX(tag) ((tag) >> 3)
-#define HPSA_TAG_DISCARD_ERROR_BITS(tag) ((tag) & ~3)
+
+/* Performant mode flags */
+#define SA5_PERF_INTR_PENDING 0x04
+#define SA5_PERF_INTR_OFF 0x05
+#define SA5_OUTDB_STATUS_PERF_BIT 0x01
+#define SA5_OUTDB_CLEAR_PERF_BIT 0x01
+#define SA5_OUTDB_CLEAR 0xA0
+#define SA5_OUTDB_CLEAR_PERF_BIT 0x01
+#define SA5_OUTDB_STATUS 0x9C
+
#define HPSA_INTR_ON 1
#define HPSA_INTR_OFF 0
@@ -176,10 +205,8 @@ struct ctlr_info {
static void SA5_submit_command(struct ctlr_info *h,
struct CommandList *c)
{
-#ifdef HPSA_DEBUG
- printk(KERN_WARNING "hpsa: Sending %x - down to controller\n",
- c->busaddr);
-#endif /* HPSA_DEBUG */
+ dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
+ c->Header.Tag.lower);
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
h->commands_outstanding++;
if (h->commands_outstanding > h->max_outstanding)
@@ -202,6 +229,52 @@ static void SA5_intr_mask(struct ctlr_info *h, unsigned long val)
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
+
+static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val)
+{
+ if (val) { /* turn on interrupts */
+ h->interrupts_enabled = 1;
+ writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ } else {
+ h->interrupts_enabled = 0;
+ writel(SA5_PERF_INTR_OFF,
+ h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ }
+}
+
+static unsigned long SA5_performant_completed(struct ctlr_info *h)
+{
+ unsigned long register_value = FIFO_EMPTY;
+
+ /* flush the controller write of the reply queue by reading
+ * outbound doorbell status register.
+ */
+ register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ /* msi auto clears the interrupt pending bit. */
+ if (!(h->msi_vector || h->msix_vector)) {
+ writel(SA5_OUTDB_CLEAR_PERF_BIT, h->vaddr + SA5_OUTDB_CLEAR);
+ /* Do a read in order to flush the write to the controller
+ * (as per spec.)
+ */
+ register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ }
+
+ if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
+ register_value = *(h->reply_pool_head);
+ (h->reply_pool_head)++;
+ h->commands_outstanding--;
+ } else {
+ register_value = FIFO_EMPTY;
+ }
+ /* Check for wraparound */
+ if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
+ h->reply_pool_head = h->reply_pool;
+ h->reply_pool_wraparound ^= 1;
+ }
+
+ return register_value;
+}
+
/*
* Returns true if fifo is full.
*
@@ -228,10 +301,10 @@ static unsigned long SA5_completed(struct ctlr_info *h)
#ifdef HPSA_DEBUG
if (register_value != FIFO_EMPTY)
- printk(KERN_INFO "hpsa: Read %lx back from board\n",
+ dev_dbg(&h->pdev->dev, "Read %lx back from board\n",
register_value);
else
- printk(KERN_INFO "hpsa: FIFO Empty read\n");
+ dev_dbg(&h->pdev->dev, "hpsa: FIFO Empty read\n");
#endif
return register_value;
@@ -239,18 +312,28 @@ static unsigned long SA5_completed(struct ctlr_info *h)
/*
* Returns true if an interrupt is pending..
*/
-static unsigned long SA5_intr_pending(struct ctlr_info *h)
+static bool SA5_intr_pending(struct ctlr_info *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
-#ifdef HPSA_DEBUG
- printk(KERN_INFO "hpsa: intr_pending %lx\n", register_value);
-#endif /* HPSA_DEBUG */
- if (register_value & SA5_INTR_PENDING)
- return 1;
- return 0 ;
+ dev_dbg(&h->pdev->dev, "intr_pending %lx\n", register_value);
+ return register_value & SA5_INTR_PENDING;
}
+static bool SA5_performant_intr_pending(struct ctlr_info *h)
+{
+ unsigned long register_value = readl(h->vaddr + SA5_INTR_STATUS);
+
+ if (!register_value)
+ return false;
+
+ if (h->msi_vector || h->msix_vector)
+ return true;
+
+ /* Read outbound doorbell to flush */
+ register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ return register_value & SA5_OUTDB_STATUS_PERF_BIT;
+}
static struct access_method SA5_access = {
SA5_submit_command,
@@ -260,14 +343,19 @@ static struct access_method SA5_access = {
SA5_completed,
};
+static struct access_method SA5_performant_access = {
+ SA5_submit_command,
+ SA5_performant_intr_mask,
+ SA5_fifo_full,
+ SA5_performant_intr_pending,
+ SA5_performant_completed,
+};
+
struct board_type {
- __u32 board_id;
+ u32 board_id;
char *product_name;
struct access_method *access;
};
-
-/* end of old hpsa_scsi.h file */
-
#endif /* HPSA_H */
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 12d71387ed9..3e0abdf7668 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -101,19 +101,20 @@
#define CFGTBL_AccCmds 0x00000001l
#define CFGTBL_Trans_Simple 0x00000002l
+#define CFGTBL_Trans_Performant 0x00000004l
#define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l
#define CFGTBL_BusType_Fibre1G 0x00000100l
#define CFGTBL_BusType_Fibre2G 0x00000200l
struct vals32 {
- __u32 lower;
- __u32 upper;
+ u32 lower;
+ u32 upper;
};
union u64bit {
struct vals32 val32;
- __u64 val;
+ u64 val;
};
/* FIXME this is a per controller value (barf!) */
@@ -126,34 +127,34 @@ union u64bit {
#define HPSA_INQUIRY 0x12
struct InquiryData {
- __u8 data_byte[36];
+ u8 data_byte[36];
};
#define HPSA_REPORT_LOG 0xc2 /* Report Logical LUNs */
#define HPSA_REPORT_PHYS 0xc3 /* Report Physical LUNs */
struct ReportLUNdata {
- __u8 LUNListLength[4];
- __u32 reserved;
- __u8 LUN[HPSA_MAX_LUN][8];
+ u8 LUNListLength[4];
+ u32 reserved;
+ u8 LUN[HPSA_MAX_LUN][8];
};
struct ReportExtendedLUNdata {
- __u8 LUNListLength[4];
- __u8 extended_response_flag;
- __u8 reserved[3];
- __u8 LUN[HPSA_MAX_LUN][24];
+ u8 LUNListLength[4];
+ u8 extended_response_flag;
+ u8 reserved[3];
+ u8 LUN[HPSA_MAX_LUN][24];
};
struct SenseSubsystem_info {
- __u8 reserved[36];
- __u8 portname[8];
- __u8 reserved1[1108];
+ u8 reserved[36];
+ u8 portname[8];
+ u8 reserved1[1108];
};
#define HPSA_READ_CAPACITY 0x25 /* Read Capacity */
struct ReadCapdata {
- __u8 total_size[4]; /* Total size in blocks */
- __u8 block_size[4]; /* Size of blocks in bytes */
+ u8 total_size[4]; /* Total size in blocks */
+ u8 block_size[4]; /* Size of blocks in bytes */
};
#if 0
@@ -174,112 +175,131 @@ struct ReadCapdata {
/* Command List Structure */
union SCSI3Addr {
struct {
- __u8 Dev;
- __u8 Bus:6;
- __u8 Mode:2; /* b00 */
+ u8 Dev;
+ u8 Bus:6;
+ u8 Mode:2; /* b00 */
} PeripDev;
struct {
- __u8 DevLSB;
- __u8 DevMSB:6;
- __u8 Mode:2; /* b01 */
+ u8 DevLSB;
+ u8 DevMSB:6;
+ u8 Mode:2; /* b01 */
} LogDev;
struct {
- __u8 Dev:5;
- __u8 Bus:3;
- __u8 Targ:6;
- __u8 Mode:2; /* b10 */
+ u8 Dev:5;
+ u8 Bus:3;
+ u8 Targ:6;
+ u8 Mode:2; /* b10 */
} LogUnit;
};
struct PhysDevAddr {
- __u32 TargetId:24;
- __u32 Bus:6;
- __u32 Mode:2;
+ u32 TargetId:24;
+ u32 Bus:6;
+ u32 Mode:2;
/* 2 level target device addr */
union SCSI3Addr Target[2];
};
struct LogDevAddr {
- __u32 VolId:30;
- __u32 Mode:2;
- __u8 reserved[4];
+ u32 VolId:30;
+ u32 Mode:2;
+ u8 reserved[4];
};
union LUNAddr {
- __u8 LunAddrBytes[8];
+ u8 LunAddrBytes[8];
union SCSI3Addr SCSI3Lun[4];
struct PhysDevAddr PhysDev;
struct LogDevAddr LogDev;
};
struct CommandListHeader {
- __u8 ReplyQueue;
- __u8 SGList;
- __u16 SGTotal;
+ u8 ReplyQueue;
+ u8 SGList;
+ u16 SGTotal;
struct vals32 Tag;
union LUNAddr LUN;
};
struct RequestBlock {
- __u8 CDBLen;
+ u8 CDBLen;
struct {
- __u8 Type:3;
- __u8 Attribute:3;
- __u8 Direction:2;
+ u8 Type:3;
+ u8 Attribute:3;
+ u8 Direction:2;
} Type;
- __u16 Timeout;
- __u8 CDB[16];
+ u16 Timeout;
+ u8 CDB[16];
};
struct ErrDescriptor {
struct vals32 Addr;
- __u32 Len;
+ u32 Len;
};
struct SGDescriptor {
struct vals32 Addr;
- __u32 Len;
- __u32 Ext;
+ u32 Len;
+ u32 Ext;
};
union MoreErrInfo {
struct {
- __u8 Reserved[3];
- __u8 Type;
- __u32 ErrorInfo;
+ u8 Reserved[3];
+ u8 Type;
+ u32 ErrorInfo;
} Common_Info;
struct {
- __u8 Reserved[2];
- __u8 offense_size; /* size of offending entry */
- __u8 offense_num; /* byte # of offense 0-base */
- __u32 offense_value;
+ u8 Reserved[2];
+ u8 offense_size; /* size of offending entry */
+ u8 offense_num; /* byte # of offense 0-base */
+ u32 offense_value;
} Invalid_Cmd;
};
struct ErrorInfo {
- __u8 ScsiStatus;
- __u8 SenseLen;
- __u16 CommandStatus;
- __u32 ResidualCnt;
+ u8 ScsiStatus;
+ u8 SenseLen;
+ u16 CommandStatus;
+ u32 ResidualCnt;
union MoreErrInfo MoreErrInfo;
- __u8 SenseInfo[SENSEINFOBYTES];
+ u8 SenseInfo[SENSEINFOBYTES];
};
/* Command types */
#define CMD_IOCTL_PEND 0x01
#define CMD_SCSI 0x03
+/* This structure needs to be divisible by 32 for new
+ * indexing method and performant mode.
+ */
+#define PAD32 32
+#define PAD64DIFF 0
+#define USEEXTRA ((sizeof(void *) - 4)/4)
+#define PADSIZE (PAD32 + PAD64DIFF * USEEXTRA)
+
+#define DIRECT_LOOKUP_SHIFT 5
+#define DIRECT_LOOKUP_BIT 0x10
+
+#define HPSA_ERROR_BIT 0x02
struct ctlr_info; /* defined in hpsa.h */
-/* The size of this structure needs to be divisible by 8
- * od on all architectures, because the controller uses 2
- * lower bits of the address, and the driver uses 1 lower
- * bit (3 bits total.)
+/* The size of this structure needs to be divisible by 32
+ * on all architectures because low 5 bits of the addresses
+ * are used as follows:
+ *
+ * bit 0: to device, used to indicate "performant mode" command
+ * from device, indidcates error status.
+ * bit 1-3: to device, indicates block fetch table entry for
+ * reducing DMA in fetching commands from host memory.
+ * bit 4: used to indicate whether tag is "direct lookup" (index),
+ * or a bus address.
*/
+
struct CommandList {
struct CommandListHeader Header;
struct RequestBlock Request;
struct ErrDescriptor ErrDesc;
struct SGDescriptor SG[MAXSGENTRIES];
/* information associated with the command */
- __u32 busaddr; /* physical addr of this record */
+ u32 busaddr; /* physical addr of this record */
struct ErrorInfo *err_info; /* pointer to the allocated mem */
struct ctlr_info *h;
int cmd_type;
@@ -291,35 +311,63 @@ struct CommandList {
struct completion *waiting;
int retry_count;
void *scsi_cmd;
+
+/* on 64 bit architectures, to get this to be 32-byte-aligned
+ * it so happens we need no padding, on 32 bit systems,
+ * we need 8 bytes of padding. This does that.
+ */
+#define COMMANDLIST_PAD ((8 - sizeof(long))/4 * 8)
+ u8 pad[COMMANDLIST_PAD];
+
};
/* Configuration Table Structure */
struct HostWrite {
- __u32 TransportRequest;
- __u32 Reserved;
- __u32 CoalIntDelay;
- __u32 CoalIntCount;
+ u32 TransportRequest;
+ u32 Reserved;
+ u32 CoalIntDelay;
+ u32 CoalIntCount;
};
+#define SIMPLE_MODE 0x02
+#define PERFORMANT_MODE 0x04
+#define MEMQ_MODE 0x08
+
struct CfgTable {
- __u8 Signature[4];
- __u32 SpecValence;
- __u32 TransportSupport;
- __u32 TransportActive;
- struct HostWrite HostWrite;
- __u32 CmdsOutMax;
- __u32 BusTypes;
- __u32 Reserved;
- __u8 ServerName[16];
- __u32 HeartBeat;
- __u32 SCSI_Prefetch;
+ u8 Signature[4];
+ u32 SpecValence;
+ u32 TransportSupport;
+ u32 TransportActive;
+ struct HostWrite HostWrite;
+ u32 CmdsOutMax;
+ u32 BusTypes;
+ u32 TransMethodOffset;
+ u8 ServerName[16];
+ u32 HeartBeat;
+ u32 SCSI_Prefetch;
+ u32 MaxScatterGatherElements;
+ u32 MaxLogicalUnits;
+ u32 MaxPhysicalDevices;
+ u32 MaxPhysicalDrivesPerLogicalUnit;
+ u32 MaxPerformantModeCommands;
+};
+
+#define NUM_BLOCKFETCH_ENTRIES 8
+struct TransTable_struct {
+ u32 BlockFetch[NUM_BLOCKFETCH_ENTRIES];
+ u32 RepQSize;
+ u32 RepQCount;
+ u32 RepQCtrAddrLow32;
+ u32 RepQCtrAddrHigh32;
+ u32 RepQAddr0Low32;
+ u32 RepQAddr0High32;
};
struct hpsa_pci_info {
unsigned char bus;
unsigned char dev_fn;
unsigned short domain;
- __u32 board_id;
+ u32 board_id;
};
#pragma pack()
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 9c1e6a5b5af..9a4b69d4f4e 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -2336,7 +2336,7 @@ static int option_setup(char *str)
char *cur = str;
int i = 1;
- while (cur && isdigit(*cur) && i <= IM_MAX_HOSTS) {
+ while (cur && isdigit(*cur) && i < IM_MAX_HOSTS) {
ints[i++] = simple_strtoul(cur, NULL, 0);
if ((cur = strchr(cur, ',')) != NULL)
cur++;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 87b536a97cb..732f6d35b4a 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -4195,7 +4195,7 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
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);
+ blk_queue_max_segments(rport->rqst_q, 1);
} else
tgt_dbg(tgt, "rport add failed\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4669,7 +4669,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
}
if (shost_to_fc_host(shost)->rqst_q)
- blk_queue_max_hw_segments(shost_to_fc_host(shost)->rqst_q, 1);
+ blk_queue_max_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);
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index e475b7957c2..e3a18e0ef27 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -40,7 +40,7 @@
* (CRQ), which is just a buffer of 16 byte entries in the receiver's
* Senders cannot access the buffer directly, but send messages by
* making a hypervisor call and passing in the 16 bytes. The hypervisor
- * puts the message in the next 16 byte space in round-robbin fashion,
+ * puts the message in the next 16 byte space in round-robin fashion,
* turns on the high order bit of the message (the valid bit), and
* generates an interrupt to the receiver (if interrupts are turned on.)
* The receiver just turns off the valid bit when they have copied out
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 9e52d16c7c3..032f0d0e6cb 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3674,7 +3674,7 @@ static int ipr_slave_configure(struct scsi_device *sdev)
if (ipr_is_vset_device(res)) {
blk_queue_rq_timeout(sdev->request_queue,
IPR_VSET_RW_TIMEOUT);
- blk_queue_max_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
+ blk_queue_max_hw_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
}
if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res))
sdev->allow_restart = 1;
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 517da3fd89d..8a89ba90058 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -584,9 +584,10 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct socket *sock = tcp_sw_conn->sock;
/* userspace may have goofed up and not bound us */
- if (!tcp_sw_conn->sock)
+ if (!sock)
return;
/*
* Make sure our recv side is stopped.
@@ -597,6 +598,11 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
+ if (sock->sk->sk_sleep && waitqueue_active(sock->sk->sk_sleep)) {
+ sock->sk->sk_err = EIO;
+ wake_up_interruptible(sock->sk->sk_sleep);
+ }
+
iscsi_conn_stop(cls_conn, flag);
iscsi_sw_tcp_release_conn(conn);
}
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 19d711cb938..7f4364770e4 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1890,7 +1890,7 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
fc_exch_setup_hdr(ep, fp, ep->f_ctl);
sp->cnt++;
- if (ep->xid <= lport->lro_xid)
+ if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD)
fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
if (unlikely(lport->tt.frame_send(lport, fp)))
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 881d5dfe8c7..6fde2fabfd9 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -298,9 +298,6 @@ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
{
struct fc_lport *lport;
- if (!fsp)
- return;
-
lport = fsp->lp;
if ((fsp->req_flags & FC_SRB_READ) &&
(lport->lro_enabled) && (lport->tt.ddp_setup)) {
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 0b165024a21..7ec8ce75007 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1800,7 +1800,8 @@ int fc_lport_bsg_request(struct fc_bsg_job *job)
u32 did;
job->reply->reply_payload_rcv_len = 0;
- rsp->resid_len = job->reply_payload.payload_len;
+ if (rsp)
+ rsp->resid_len = job->reply_payload.payload_len;
mutex_lock(&lport->lp_mutex);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 02300523b23..97923bb0776 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -623,7 +623,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
tov = ntohl(plp->fl_csp.sp_e_d_tov);
if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
- tov /= 1000;
+ tov /= 1000000;
if (tov > rdata->e_d_tov)
rdata->e_d_tov = tov;
csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index c28a712fd4d..703eb6a8879 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1919,10 +1919,11 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
{
enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED;
- struct iscsi_task *task = NULL;
+ struct iscsi_task *task = NULL, *running_task;
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct iscsi_conn *conn;
+ int i;
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
@@ -1947,8 +1948,15 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
}
task = (struct iscsi_task *)sc->SCp.ptr;
- if (!task)
+ if (!task) {
+ /*
+ * Raced with completion. Just reset timer, and let it
+ * complete normally
+ */
+ rc = BLK_EH_RESET_TIMER;
goto done;
+ }
+
/*
* If we have sent (at least queued to the network layer) a pdu or
* recvd one for the task since the last timeout ask for
@@ -1956,10 +1964,10 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
* we can check if it is the task or connection when we send the
* nop as a ping.
*/
- if (time_after_eq(task->last_xfer, task->last_timeout)) {
+ if (time_after(task->last_xfer, task->last_timeout)) {
ISCSI_DBG_EH(session, "Command making progress. Asking "
"scsi-ml for more time to complete. "
- "Last data recv at %lu. Last timeout was at "
+ "Last data xfer at %lu. Last timeout was at "
"%lu\n.", task->last_xfer, task->last_timeout);
task->have_checked_conn = false;
rc = BLK_EH_RESET_TIMER;
@@ -1977,6 +1985,43 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
goto done;
}
+ for (i = 0; i < conn->session->cmds_max; i++) {
+ running_task = conn->session->cmds[i];
+ if (!running_task->sc || running_task == task ||
+ running_task->state != ISCSI_TASK_RUNNING)
+ continue;
+
+ /*
+ * Only check if cmds started before this one have made
+ * progress, or this could never fail
+ */
+ if (time_after(running_task->sc->jiffies_at_alloc,
+ task->sc->jiffies_at_alloc))
+ continue;
+
+ if (time_after(running_task->last_xfer, task->last_timeout)) {
+ /*
+ * This task has not made progress, but a task
+ * started before us has transferred data since
+ * we started/last-checked. We could be queueing
+ * too many tasks or the LU is bad.
+ *
+ * If the device is bad the cmds ahead of us on
+ * other devs will complete, and this loop will
+ * eventually fail starting the scsi eh.
+ */
+ ISCSI_DBG_EH(session, "Command has not made progress "
+ "but commands ahead of it have. "
+ "Asking scsi-ml for more time to "
+ "complete. Our last xfer vs running task "
+ "last xfer %lu/%lu. Last check %lu.\n",
+ task->last_xfer, running_task->last_xfer,
+ task->last_timeout);
+ rc = BLK_EH_RESET_TIMER;
+ goto done;
+ }
+ }
+
/* Assumes nop timeout is shorter than scsi cmd timeout */
if (task->have_checked_conn)
goto done;
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index db6856c138f..4ad87fd74dd 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -992,12 +992,10 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
if (r2t == NULL) {
if (kfifo_out(&tcp_task->r2tqueue,
(void *)&tcp_task->r2t, sizeof(void *)) !=
- sizeof(void *)) {
- WARN_ONCE(1, "unexpected fifo state");
+ sizeof(void *))
r2t = NULL;
- }
-
- r2t = tcp_task->r2t;
+ else
+ r2t = tcp_task->r2t;
}
spin_unlock_bh(&session->lock);
}
diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
index ab19b3b4be5..22775165bf6 100644
--- a/drivers/scsi/libsrp.c
+++ b/drivers/scsi/libsrp.c
@@ -1,5 +1,5 @@
/*
- * SCSI RDAM Protocol lib functions
+ * SCSI RDMA Protocol lib functions
*
* Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
*
@@ -328,7 +328,7 @@ int srp_transfer_data(struct scsi_cmnd *sc, struct srp_cmd *cmd,
int offset, err = 0;
u8 format;
- offset = cmd->add_cdb_len * 4;
+ offset = cmd->add_cdb_len & ~3;
dir = srp_cmd_direction(cmd);
if (dir == DMA_FROM_DEVICE)
@@ -366,7 +366,7 @@ static int vscsis_data_length(struct srp_cmd *cmd, enum dma_data_direction dir)
{
struct srp_direct_buf *md;
struct srp_indirect_buf *id;
- int len = 0, offset = cmd->add_cdb_len * 4;
+ int len = 0, offset = cmd->add_cdb_len & ~3;
u8 fmt;
if (dir == DMA_TO_DEVICE)
@@ -440,6 +440,6 @@ int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info,
}
EXPORT_SYMBOL_GPL(srp_cmd_queue);
-MODULE_DESCRIPTION("SCSI RDAM Protocol lib functions");
+MODULE_DESCRIPTION("SCSI RDMA Protocol lib functions");
MODULE_AUTHOR("FUJITA Tomonori");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 1cc23a69db5..84b696463a5 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -315,6 +315,9 @@ struct lpfc_vport {
#define FC_VPORT_NEEDS_REG_VPI 0x80000 /* Needs to have its vpi registered */
#define FC_RSCN_DEFERRED 0x100000 /* A deferred RSCN being processed */
#define FC_VPORT_NEEDS_INIT_VPI 0x200000 /* Need to INIT_VPI before FDISC */
+#define FC_VPORT_CVL_RCVD 0x400000 /* VLink failed due to CVL */
+#define FC_VFI_REGISTERED 0x800000 /* VFI is registered */
+#define FC_FDISC_COMPLETED 0x1000000/* FDISC completed */
uint32_t ct_flags;
#define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */
@@ -448,6 +451,8 @@ struct unsol_rcv_ct_ctx {
uint32_t ctxt_id;
uint32_t SID;
uint32_t oxid;
+ uint32_t flags;
+#define UNSOL_VALID 0x00000001
};
struct lpfc_hba {
@@ -499,6 +504,10 @@ struct lpfc_hba {
(struct lpfc_hba *);
void (*lpfc_stop_port)
(struct lpfc_hba *);
+ int (*lpfc_hba_init_link)
+ (struct lpfc_hba *);
+ int (*lpfc_hba_down_link)
+ (struct lpfc_hba *);
/* SLI4 specific HBA data structure */
@@ -613,6 +622,7 @@ struct lpfc_hba {
uint32_t cfg_enable_bg;
uint32_t cfg_log_verbose;
uint32_t cfg_aer_support;
+ uint32_t cfg_suppress_link_up;
lpfc_vpd_t vpd; /* vital product data */
@@ -790,7 +800,7 @@ struct lpfc_hba {
uint16_t vlan_id;
struct list_head fcf_conn_rec_list;
- struct mutex ct_event_mutex; /* synchronize access to ct_ev_waiters */
+ spinlock_t ct_ev_lock; /* synchronize access to ct_ev_waiters */
struct list_head ct_ev_waiters;
struct unsol_rcv_ct_ctx ct_ctx[64];
uint32_t ctx_idx;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 91542f786ed..c992e8328f9 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -482,6 +482,41 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr,
}
/**
+ * lpfc_link_state_store - Transition the link_state on an HBA port
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: one or more lpfc_polling_flags values.
+ * @count: not used.
+ *
+ * Returns:
+ * -EINVAL if the buffer is not "up" or "down"
+ * return from link state change function if non-zero
+ * length of the buf on success
+ **/
+static ssize_t
+lpfc_link_state_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 status = -EINVAL;
+
+ if ((strncmp(buf, "up", sizeof("up") - 1) == 0) &&
+ (phba->link_state == LPFC_LINK_DOWN))
+ status = phba->lpfc_hba_init_link(phba);
+ else if ((strncmp(buf, "down", sizeof("down") - 1) == 0) &&
+ (phba->link_state >= LPFC_LINK_UP))
+ status = phba->lpfc_hba_down_link(phba);
+
+ if (status == 0)
+ return strlen(buf);
+ else
+ return status;
+}
+
+/**
* lpfc_num_discovered_ports_show - Return sum of mapped and unmapped vports
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
@@ -1219,7 +1254,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
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;\
+ uint val = 0;\
val = phba->cfg_##attr;\
return snprintf(buf, PAGE_SIZE, "%d\n",\
phba->cfg_##attr);\
@@ -1247,7 +1282,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
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;\
+ uint val = 0;\
val = phba->cfg_##attr;\
return snprintf(buf, PAGE_SIZE, "%#x\n",\
phba->cfg_##attr);\
@@ -1274,7 +1309,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
**/
#define lpfc_param_init(attr, default, minval, maxval) \
static int \
-lpfc_##attr##_init(struct lpfc_hba *phba, int val) \
+lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \
{ \
if (val >= minval && val <= maxval) {\
phba->cfg_##attr = val;\
@@ -1309,7 +1344,7 @@ lpfc_##attr##_init(struct lpfc_hba *phba, int val) \
**/
#define lpfc_param_set(attr, default, minval, maxval) \
static int \
-lpfc_##attr##_set(struct lpfc_hba *phba, int val) \
+lpfc_##attr##_set(struct lpfc_hba *phba, uint val) \
{ \
if (val >= minval && val <= maxval) {\
phba->cfg_##attr = val;\
@@ -1350,7 +1385,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
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;\
+ uint val = 0;\
if (!isdigit(buf[0]))\
return -EINVAL;\
if (sscanf(buf, "%i", &val) != 1)\
@@ -1382,7 +1417,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
{ \
struct Scsi_Host *shost = class_to_shost(dev);\
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\
- int val = 0;\
+ uint val = 0;\
val = vport->cfg_##attr;\
return snprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_##attr);\
}
@@ -1409,7 +1444,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
{ \
struct Scsi_Host *shost = class_to_shost(dev);\
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\
- int val = 0;\
+ uint val = 0;\
val = vport->cfg_##attr;\
return snprintf(buf, PAGE_SIZE, "%#x\n", vport->cfg_##attr);\
}
@@ -1434,7 +1469,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
**/
#define lpfc_vport_param_init(attr, default, minval, maxval) \
static int \
-lpfc_##attr##_init(struct lpfc_vport *vport, int val) \
+lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \
{ \
if (val >= minval && val <= maxval) {\
vport->cfg_##attr = val;\
@@ -1466,7 +1501,7 @@ lpfc_##attr##_init(struct lpfc_vport *vport, int val) \
**/
#define lpfc_vport_param_set(attr, default, minval, maxval) \
static int \
-lpfc_##attr##_set(struct lpfc_vport *vport, int val) \
+lpfc_##attr##_set(struct lpfc_vport *vport, uint val) \
{ \
if (val >= minval && val <= maxval) {\
vport->cfg_##attr = val;\
@@ -1502,7 +1537,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
{ \
struct Scsi_Host *shost = class_to_shost(dev);\
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\
- int val=0;\
+ uint val = 0;\
if (!isdigit(buf[0]))\
return -EINVAL;\
if (sscanf(buf, "%i", &val) != 1)\
@@ -1515,22 +1550,22 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
#define LPFC_ATTR(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_init(name, defval, minval, maxval)
#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
@@ -1540,16 +1575,16 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
lpfc_##name##_show, lpfc_##name##_store)
#define LPFC_ATTR_HEX_R(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_hex_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_hex_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
@@ -1559,22 +1594,22 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
lpfc_##name##_show, lpfc_##name##_store)
#define LPFC_VPORT_ATTR(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_init(name, defval, minval, maxval)
#define LPFC_VPORT_ATTR_R(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
@@ -1584,16 +1619,16 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
lpfc_##name##_show, lpfc_##name##_store)
#define LPFC_VPORT_ATTR_HEX_R(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_hex_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_VPORT_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
-static int lpfc_##name = defval;\
-module_param(lpfc_##name, int, 0);\
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_hex_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
@@ -1614,7 +1649,8 @@ static DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL);
static DEVICE_ATTR(portnum, S_IRUGO, lpfc_vportnum_show, NULL);
static DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL);
static DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL);
-static DEVICE_ATTR(link_state, S_IRUGO, lpfc_link_state_show, NULL);
+static DEVICE_ATTR(link_state, S_IRUGO | S_IWUSR, lpfc_link_state_show,
+ lpfc_link_state_store);
static DEVICE_ATTR(option_rom_version, S_IRUGO,
lpfc_option_rom_version_show, NULL);
static DEVICE_ATTR(num_discovered_ports, S_IRUGO,
@@ -1897,6 +1933,15 @@ static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO,
lpfc_enable_npiv_show, NULL);
/*
+# lpfc_suppress_link_up: Bring link up at initialization
+# 0x0 = bring link up (issue MBX_INIT_LINK)
+# 0x1 = do NOT bring link up at initialization(MBX_INIT_LINK)
+# 0x2 = never bring up link
+# Default value is 0.
+*/
+LPFC_ATTR_R(suppress_link_up, 0, 0, 2, "Suppress Link Up at initialization");
+
+/*
# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear
# until the timer expires. Value range is [0,255]. Default value is 30.
*/
@@ -3114,12 +3159,12 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255,
/*
# lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that
# support this feature
-# 0 = MSI disabled (default)
+# 0 = MSI disabled
# 1 = MSI enabled
-# 2 = MSI-X enabled
-# Value range is [0,2]. Default value is 0.
+# 2 = MSI-X enabled (default)
+# Value range is [0,2]. Default value is 2.
*/
-LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or "
+LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or "
"MSI-X (2), if possible");
/*
@@ -3278,6 +3323,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_prot_sg_seg_cnt,
&dev_attr_lpfc_aer_support,
&dev_attr_lpfc_aer_state_cleanup,
+ &dev_attr_lpfc_suppress_link_up,
NULL,
};
@@ -4456,7 +4502,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
lpfc_aer_support_init(phba, lpfc_aer_support);
-
+ lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index a5d9048235d..f3f1bf1a0a7 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2009 Emulex. All rights reserved. *
+ * Copyright (C) 2009-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/mempool.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
@@ -33,6 +34,7 @@
#include "lpfc_sli.h"
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
+#include "lpfc_bsg.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
#include "lpfc.h"
@@ -41,14 +43,183 @@
#include "lpfc_vport.h"
#include "lpfc_version.h"
+struct lpfc_bsg_event {
+ struct list_head node;
+ struct kref kref;
+ wait_queue_head_t wq;
+
+ /* Event type and waiter identifiers */
+ uint32_t type_mask;
+ uint32_t req_id;
+ uint32_t reg_id;
+
+ /* next two flags are here for the auto-delete logic */
+ unsigned long wait_time_stamp;
+ int waiting;
+
+ /* seen and not seen events */
+ struct list_head events_to_get;
+ struct list_head events_to_see;
+
+ /* job waiting for this event to finish */
+ struct fc_bsg_job *set_job;
+};
+
+struct lpfc_bsg_iocb {
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_iocbq *rspiocbq;
+ struct lpfc_dmabuf *bmp;
+ struct lpfc_nodelist *ndlp;
+
+ /* job waiting for this iocb to finish */
+ struct fc_bsg_job *set_job;
+};
+
+struct lpfc_bsg_mbox {
+ LPFC_MBOXQ_t *pmboxq;
+ MAILBOX_t *mb;
+
+ /* job waiting for this mbox command to finish */
+ struct fc_bsg_job *set_job;
+};
+
+#define TYPE_EVT 1
+#define TYPE_IOCB 2
+#define TYPE_MBOX 3
+struct bsg_job_data {
+ uint32_t type;
+ union {
+ struct lpfc_bsg_event *evt;
+ struct lpfc_bsg_iocb iocb;
+ struct lpfc_bsg_mbox mbox;
+ } context_un;
+};
+
+struct event_data {
+ struct list_head node;
+ uint32_t type;
+ uint32_t immed_dat;
+ void *data;
+ uint32_t len;
+};
+
+#define BUF_SZ_4K 4096
+#define SLI_CT_ELX_LOOPBACK 0x10
+
+enum ELX_LOOPBACK_CMD {
+ ELX_LOOPBACK_XRI_SETUP,
+ ELX_LOOPBACK_DATA,
+};
+
+#define ELX_LOOPBACK_HEADER_SZ \
+ (size_t)(&((struct lpfc_sli_ct_request *)NULL)->un)
+
+struct lpfc_dmabufext {
+ struct lpfc_dmabuf dma;
+ uint32_t size;
+ uint32_t flag;
+};
+
+/**
+ * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler
+ * @phba: Pointer to HBA context object.
+ * @cmdiocbq: Pointer to command iocb.
+ * @rspiocbq: Pointer to response iocb.
+ *
+ * This function is the completion handler for iocbs issued using
+ * lpfc_bsg_send_mgmt_cmd function. This function is called by the
+ * ring event handler function without any lock held. This function
+ * can be called from both worker thread context and interrupt
+ * context. This function also can be called from another thread which
+ * cleans up the SLI layer objects.
+ * This function copies the contents of the response iocb to the
+ * response iocb memory object provided by the caller of
+ * lpfc_sli_issue_iocb_wait and then wakes up the thread which
+ * sleeps for the iocb completion.
+ **/
+static void
+lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_iocbq *rspiocbq)
+{
+ unsigned long iflags;
+ struct bsg_job_data *dd_data;
+ struct fc_bsg_job *job;
+ IOCB_t *rsp;
+ struct lpfc_dmabuf *bmp;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_bsg_iocb *iocb;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ dd_data = cmdiocbq->context1;
+ if (!dd_data) {
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
+ }
+
+ iocb = &dd_data->context_un.iocb;
+ job = iocb->set_job;
+ job->dd_data = NULL; /* so timeout handler does not reply */
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
+ if (cmdiocbq->context2 && rspiocbq)
+ memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
+ &rspiocbq->iocb, sizeof(IOCB_t));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ bmp = iocb->bmp;
+ rspiocbq = iocb->rspiocbq;
+ rsp = &rspiocbq->iocb;
+ ndlp = iocb->ndlp;
+
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ if (rsp->ulpStatus) {
+ if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
+ switch (rsp->un.ulpWord[4] & 0xff) {
+ case IOERR_SEQUENCE_TIMEOUT:
+ rc = -ETIMEDOUT;
+ break;
+ case IOERR_INVALID_RPI:
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EACCES;
+ break;
+ }
+ } else
+ rc = -EACCES;
+ } else
+ job->reply->reply_payload_rcv_len =
+ rsp->un.genreq64.bdl.bdeSize;
+
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ lpfc_nlp_put(ndlp);
+ kfree(bmp);
+ kfree(dd_data);
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace */
+ job->job_done(job);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
+}
+
/**
- * lpfc_bsg_rport_ct - send a CT command from a bsg request
+ * lpfc_bsg_send_mgmt_cmd - send a CT command from a bsg request
* @job: fc_bsg_job to handle
- */
+ **/
static int
-lpfc_bsg_rport_ct(struct fc_bsg_job *job)
+lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
{
- struct Scsi_Host *shost = job->shost;
struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct lpfc_rport_data *rdata = job->rport->dd_data;
@@ -65,57 +236,60 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job)
struct scatterlist *sgel = NULL;
int numbde;
dma_addr_t busaddr;
+ struct bsg_job_data *dd_data;
+ uint32_t creg_val;
int rc = 0;
/* in case no data is transferred */
job->reply->reply_payload_rcv_len = 0;
+ /* allocate our bsg tracking structure */
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2733 Failed allocation of dd_data\n");
+ rc = -ENOMEM;
+ goto no_dd_data;
+ }
+
if (!lpfc_nlp_get(ndlp)) {
- job->reply->result = -ENODEV;
- return 0;
+ rc = -ENODEV;
+ goto no_ndlp;
+ }
+
+ bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!bmp) {
+ rc = -ENOMEM;
+ goto free_ndlp;
}
if (ndlp->nlp_flag & NLP_ELS_SND_MASK) {
rc = -ENODEV;
- goto free_ndlp_exit;
+ goto free_bmp;
}
- spin_lock_irq(shost->host_lock);
cmdiocbq = lpfc_sli_get_iocbq(phba);
if (!cmdiocbq) {
rc = -ENOMEM;
- spin_unlock_irq(shost->host_lock);
- goto free_ndlp_exit;
+ goto free_bmp;
}
- cmd = &cmdiocbq->iocb;
+ cmd = &cmdiocbq->iocb;
rspiocbq = lpfc_sli_get_iocbq(phba);
if (!rspiocbq) {
rc = -ENOMEM;
goto free_cmdiocbq;
}
- spin_unlock_irq(shost->host_lock);
rsp = &rspiocbq->iocb;
-
- bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!bmp) {
- rc = -ENOMEM;
- spin_lock_irq(shost->host_lock);
- goto free_rspiocbq;
- }
-
- spin_lock_irq(shost->host_lock);
bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
if (!bmp->virt) {
rc = -ENOMEM;
- goto free_bmp;
+ goto free_rspiocbq;
}
- spin_unlock_irq(shost->host_lock);
INIT_LIST_HEAD(&bmp->list);
bpl = (struct ulp_bde64 *) bmp->virt;
-
request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
@@ -157,78 +331,152 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job)
cmd->ulpContext = ndlp->nlp_rpi;
cmd->ulpOwner = OWN_CHIP;
cmdiocbq->vport = phba->pport;
- cmdiocbq->context1 = NULL;
- cmdiocbq->context2 = NULL;
+ cmdiocbq->context3 = bmp;
cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
-
timeout = phba->fc_ratov * 2;
- job->dd_data = cmdiocbq;
-
- rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq,
- timeout + LPFC_DRVR_TIMEOUT);
-
- if (rc != IOCB_TIMEDOUT) {
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ cmd->ulpTimeout = timeout;
+
+ cmdiocbq->iocb_cmpl = lpfc_bsg_send_mgmt_cmd_cmp;
+ cmdiocbq->context1 = dd_data;
+ cmdiocbq->context2 = rspiocbq;
+ dd_data->type = TYPE_IOCB;
+ dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
+ dd_data->context_un.iocb.rspiocbq = rspiocbq;
+ dd_data->context_un.iocb.set_job = job;
+ dd_data->context_un.iocb.bmp = bmp;
+ dd_data->context_un.iocb.ndlp = ndlp;
+
+ if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
+ creg_val = readl(phba->HCregaddr);
+ creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
+ writel(creg_val, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
}
- if (rc == IOCB_TIMEDOUT) {
- lpfc_sli_release_iocbq(phba, rspiocbq);
- rc = -EACCES;
- goto free_ndlp_exit;
- }
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
- if (rc != IOCB_SUCCESS) {
- rc = -EACCES;
- goto free_outdmp;
- }
+ if (rc == IOCB_SUCCESS)
+ return 0; /* done for now */
- if (rsp->ulpStatus) {
- if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- switch (rsp->un.ulpWord[4] & 0xff) {
- case IOERR_SEQUENCE_TIMEOUT:
- rc = -ETIMEDOUT;
- break;
- case IOERR_INVALID_RPI:
- rc = -EFAULT;
- break;
- default:
- rc = -EACCES;
- break;
- }
- goto free_outdmp;
- }
- } else
- job->reply->reply_payload_rcv_len =
- rsp->un.genreq64.bdl.bdeSize;
+ /* iocb failed so cleanup */
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
-free_outdmp:
- spin_lock_irq(shost->host_lock);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
-free_bmp:
- kfree(bmp);
+
free_rspiocbq:
lpfc_sli_release_iocbq(phba, rspiocbq);
free_cmdiocbq:
lpfc_sli_release_iocbq(phba, cmdiocbq);
- spin_unlock_irq(shost->host_lock);
-free_ndlp_exit:
+free_bmp:
+ kfree(bmp);
+free_ndlp:
lpfc_nlp_put(ndlp);
+no_ndlp:
+ kfree(dd_data);
+no_dd_data:
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ job->dd_data = NULL;
+ return rc;
+}
+
+/**
+ * lpfc_bsg_rport_els_cmp - lpfc_bsg_rport_els's completion handler
+ * @phba: Pointer to HBA context object.
+ * @cmdiocbq: Pointer to command iocb.
+ * @rspiocbq: Pointer to response iocb.
+ *
+ * This function is the completion handler for iocbs issued using
+ * lpfc_bsg_rport_els_cmp function. This function is called by the
+ * ring event handler function without any lock held. This function
+ * can be called from both worker thread context and interrupt
+ * context. This function also can be called from other thread which
+ * cleans up the SLI layer objects.
+ * This function copies the contents of the response iocb to the
+ * response iocb memory object provided by the caller of
+ * lpfc_sli_issue_iocb_wait and then wakes up the thread which
+ * sleeps for the iocb completion.
+ **/
+static void
+lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_iocbq *rspiocbq)
+{
+ struct bsg_job_data *dd_data;
+ struct fc_bsg_job *job;
+ IOCB_t *rsp;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_dmabuf *pbuflist = NULL;
+ struct fc_bsg_ctels_reply *els_reply;
+ uint8_t *rjt_data;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ dd_data = cmdiocbq->context1;
+ /* normal completion and timeout crossed paths, already done */
+ if (!dd_data) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ return;
+ }
+
+ cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
+ if (cmdiocbq->context2 && rspiocbq)
+ memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
+ &rspiocbq->iocb, sizeof(IOCB_t));
+
+ job = dd_data->context_un.iocb.set_job;
+ cmdiocbq = dd_data->context_un.iocb.cmdiocbq;
+ rspiocbq = dd_data->context_un.iocb.rspiocbq;
+ rsp = &rspiocbq->iocb;
+ ndlp = dd_data->context_un.iocb.ndlp;
+
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (job->reply->result == -EAGAIN)
+ rc = -EAGAIN;
+ else if (rsp->ulpStatus == IOSTAT_SUCCESS)
+ job->reply->reply_payload_rcv_len =
+ rsp->un.elsreq64.bdl.bdeSize;
+ else if (rsp->ulpStatus == IOSTAT_LS_RJT) {
+ job->reply->reply_payload_rcv_len =
+ sizeof(struct fc_bsg_ctels_reply);
+ /* LS_RJT data returned in word 4 */
+ rjt_data = (uint8_t *)&rsp->un.ulpWord[4];
+ els_reply = &job->reply->reply_data.ctels_reply;
+ els_reply->status = FC_CTELS_STATUS_REJECT;
+ els_reply->rjt_data.action = rjt_data[3];
+ els_reply->rjt_data.reason_code = rjt_data[2];
+ els_reply->rjt_data.reason_explanation = rjt_data[1];
+ els_reply->rjt_data.vendor_unique = rjt_data[0];
+ } else
+ rc = -EIO;
+
+ pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3;
+ lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys);
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ lpfc_nlp_put(ndlp);
+ kfree(dd_data);
/* make error code available to userspace */
job->reply->result = rc;
+ job->dd_data = NULL;
/* complete the job back to userspace */
job->job_done(job);
-
- return 0;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
}
/**
* lpfc_bsg_rport_els - send an ELS command from a bsg request
* @job: fc_bsg_job to handle
- */
+ **/
static int
lpfc_bsg_rport_els(struct fc_bsg_job *job)
{
@@ -236,7 +484,6 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
struct lpfc_hba *phba = vport->phba;
struct lpfc_rport_data *rdata = job->rport->dd_data;
struct lpfc_nodelist *ndlp = rdata->pnode;
-
uint32_t elscmd;
uint32_t cmdsize;
uint32_t rspsize;
@@ -248,20 +495,30 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
struct lpfc_dmabuf *prsp;
struct lpfc_dmabuf *pbuflist = NULL;
struct ulp_bde64 *bpl;
- int iocb_status;
int request_nseg;
int reply_nseg;
struct scatterlist *sgel = NULL;
int numbde;
dma_addr_t busaddr;
+ struct bsg_job_data *dd_data;
+ uint32_t creg_val;
int rc = 0;
/* in case no data is transferred */
job->reply->reply_payload_rcv_len = 0;
+ /* allocate our bsg tracking structure */
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2735 Failed allocation of dd_data\n");
+ rc = -ENOMEM;
+ goto no_dd_data;
+ }
+
if (!lpfc_nlp_get(ndlp)) {
rc = -ENODEV;
- goto out;
+ goto free_dd_data;
}
elscmd = job->request->rqst_data.r_els.els_code;
@@ -271,24 +528,24 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
if (!rspiocbq) {
lpfc_nlp_put(ndlp);
rc = -ENOMEM;
- goto out;
+ goto free_dd_data;
}
rsp = &rspiocbq->iocb;
rpi = ndlp->nlp_rpi;
- cmdiocbq = lpfc_prep_els_iocb(phba->pport, 1, cmdsize, 0, ndlp,
+ cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
ndlp->nlp_DID, elscmd);
-
if (!cmdiocbq) {
- lpfc_sli_release_iocbq(phba, rspiocbq);
- return -EIO;
+ rc = -EIO;
+ goto free_rspiocbq;
}
- job->dd_data = cmdiocbq;
+ /* prep els iocb set context1 to the ndlp, context2 to the command
+ * dmabuf, context3 holds the data dmabuf
+ */
pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2;
prsp = (struct lpfc_dmabuf *) pcmd->list.next;
-
lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
kfree(pcmd);
lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
@@ -300,7 +557,6 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
-
for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
busaddr = sg_dma_address(sgel);
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
@@ -322,7 +578,6 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
}
-
cmdiocbq->iocb.un.elsreq64.bdl.bdeSize =
(request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
cmdiocbq->iocb.ulpContext = rpi;
@@ -330,102 +585,62 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
cmdiocbq->context1 = NULL;
cmdiocbq->context2 = NULL;
- iocb_status = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
- rspiocbq, (phba->fc_ratov * 2)
- + LPFC_DRVR_TIMEOUT);
-
- /* release the new ndlp once the iocb completes */
- lpfc_nlp_put(ndlp);
- if (iocb_status != IOCB_TIMEDOUT) {
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ cmdiocbq->iocb_cmpl = lpfc_bsg_rport_els_cmp;
+ cmdiocbq->context1 = dd_data;
+ cmdiocbq->context2 = rspiocbq;
+ dd_data->type = TYPE_IOCB;
+ dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
+ dd_data->context_un.iocb.rspiocbq = rspiocbq;
+ dd_data->context_un.iocb.set_job = job;
+ dd_data->context_un.iocb.bmp = NULL;;
+ dd_data->context_un.iocb.ndlp = ndlp;
+
+ if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
+ creg_val = readl(phba->HCregaddr);
+ creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
+ writel(creg_val, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
}
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
+ lpfc_nlp_put(ndlp);
+ if (rc == IOCB_SUCCESS)
+ return 0; /* done for now */
- if (iocb_status == IOCB_SUCCESS) {
- if (rsp->ulpStatus == IOSTAT_SUCCESS) {
- job->reply->reply_payload_rcv_len =
- rsp->un.elsreq64.bdl.bdeSize;
- rc = 0;
- } else if (rsp->ulpStatus == IOSTAT_LS_RJT) {
- struct fc_bsg_ctels_reply *els_reply;
- /* LS_RJT data returned in word 4 */
- uint8_t *rjt_data = (uint8_t *)&rsp->un.ulpWord[4];
-
- els_reply = &job->reply->reply_data.ctels_reply;
- job->reply->result = 0;
- els_reply->status = FC_CTELS_STATUS_REJECT;
- els_reply->rjt_data.action = rjt_data[0];
- els_reply->rjt_data.reason_code = rjt_data[1];
- els_reply->rjt_data.reason_explanation = rjt_data[2];
- els_reply->rjt_data.vendor_unique = rjt_data[3];
- } else
- rc = -EIO;
- } else
- rc = -EIO;
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys);
- if (iocb_status != IOCB_TIMEDOUT)
- lpfc_els_free_iocb(phba, cmdiocbq);
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+free_rspiocbq:
lpfc_sli_release_iocbq(phba, rspiocbq);
-out:
+free_dd_data:
+ kfree(dd_data);
+
+no_dd_data:
/* make error code available to userspace */
job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
-
- return 0;
-}
-
-struct lpfc_ct_event {
- struct list_head node;
- int ref;
- wait_queue_head_t wq;
-
- /* Event type and waiter identifiers */
- uint32_t type_mask;
- uint32_t req_id;
- uint32_t reg_id;
-
- /* next two flags are here for the auto-delete logic */
- unsigned long wait_time_stamp;
- int waiting;
-
- /* seen and not seen events */
- struct list_head events_to_get;
- struct list_head events_to_see;
-};
-
-struct event_data {
- struct list_head node;
- uint32_t type;
- uint32_t immed_dat;
- void *data;
- uint32_t len;
-};
-
-static struct lpfc_ct_event *
-lpfc_ct_event_new(int ev_reg_id, uint32_t ev_req_id)
-{
- struct lpfc_ct_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL);
- if (!evt)
- return NULL;
-
- INIT_LIST_HEAD(&evt->events_to_get);
- INIT_LIST_HEAD(&evt->events_to_see);
- evt->req_id = ev_req_id;
- evt->reg_id = ev_reg_id;
- evt->wait_time_stamp = jiffies;
- init_waitqueue_head(&evt->wq);
-
- return evt;
+ job->dd_data = NULL;
+ return rc;
}
+/**
+ * lpfc_bsg_event_free - frees an allocated event structure
+ * @kref: Pointer to a kref.
+ *
+ * Called from kref_put. Back cast the kref into an event structure address.
+ * Free any events to get, delete associated nodes, free any events to see,
+ * free any data then free the event itself.
+ **/
static void
-lpfc_ct_event_free(struct lpfc_ct_event *evt)
+lpfc_bsg_event_free(struct kref *kref)
{
+ struct lpfc_bsg_event *evt = container_of(kref, struct lpfc_bsg_event,
+ kref);
struct event_data *ed;
list_del(&evt->node);
@@ -447,25 +662,82 @@ lpfc_ct_event_free(struct lpfc_ct_event *evt)
kfree(evt);
}
+/**
+ * lpfc_bsg_event_ref - increments the kref for an event
+ * @evt: Pointer to an event structure.
+ **/
static inline void
-lpfc_ct_event_ref(struct lpfc_ct_event *evt)
+lpfc_bsg_event_ref(struct lpfc_bsg_event *evt)
{
- evt->ref++;
+ kref_get(&evt->kref);
}
+/**
+ * lpfc_bsg_event_unref - Uses kref_put to free an event structure
+ * @evt: Pointer to an event structure.
+ **/
static inline void
-lpfc_ct_event_unref(struct lpfc_ct_event *evt)
+lpfc_bsg_event_unref(struct lpfc_bsg_event *evt)
{
- if (--evt->ref < 0)
- lpfc_ct_event_free(evt);
+ kref_put(&evt->kref, lpfc_bsg_event_free);
}
-#define SLI_CT_ELX_LOOPBACK 0x10
+/**
+ * lpfc_bsg_event_new - allocate and initialize a event structure
+ * @ev_mask: Mask of events.
+ * @ev_reg_id: Event reg id.
+ * @ev_req_id: Event request id.
+ **/
+static struct lpfc_bsg_event *
+lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id)
+{
+ struct lpfc_bsg_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL);
-enum ELX_LOOPBACK_CMD {
- ELX_LOOPBACK_XRI_SETUP,
- ELX_LOOPBACK_DATA,
-};
+ if (!evt)
+ return NULL;
+
+ INIT_LIST_HEAD(&evt->events_to_get);
+ INIT_LIST_HEAD(&evt->events_to_see);
+ evt->type_mask = ev_mask;
+ evt->req_id = ev_req_id;
+ evt->reg_id = ev_reg_id;
+ evt->wait_time_stamp = jiffies;
+ init_waitqueue_head(&evt->wq);
+ kref_init(&evt->kref);
+ return evt;
+}
+
+/**
+ * diag_cmd_data_free - Frees an lpfc dma buffer extension
+ * @phba: Pointer to HBA context object.
+ * @mlist: Pointer to an lpfc dma buffer extension.
+ **/
+static int
+diag_cmd_data_free(struct lpfc_hba *phba, struct lpfc_dmabufext *mlist)
+{
+ struct lpfc_dmabufext *mlast;
+ struct pci_dev *pcidev;
+ struct list_head head, *curr, *next;
+
+ if ((!mlist) || (!lpfc_is_link_up(phba) &&
+ (phba->link_flag & LS_LOOPBACK_MODE))) {
+ return 0;
+ }
+
+ pcidev = phba->pcidev;
+ list_add_tail(&head, &mlist->dma.list);
+
+ list_for_each_safe(curr, next, &head) {
+ mlast = list_entry(curr, struct lpfc_dmabufext , dma.list);
+ if (mlast->dma.virt)
+ dma_free_coherent(&pcidev->dev,
+ mlast->size,
+ mlast->dma.virt,
+ mlast->dma.phys);
+ kfree(mlast);
+ }
+ return 0;
+}
/**
* lpfc_bsg_ct_unsol_event - process an unsolicited CT command
@@ -474,9 +746,9 @@ enum ELX_LOOPBACK_CMD {
* @piocbq:
*
* This function is called when an unsolicited CT command is received. It
- * forwards the event to any processes registerd to receive CT events.
- */
-void
+ * forwards the event to any processes registered to receive CT events.
+ **/
+int
lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *piocbq)
{
@@ -484,7 +756,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
uint32_t cmd;
uint32_t len;
struct lpfc_dmabuf *dmabuf = NULL;
- struct lpfc_ct_event *evt;
+ struct lpfc_bsg_event *evt;
struct event_data *evt_dat = NULL;
struct lpfc_iocbq *iocbq;
size_t offset = 0;
@@ -496,6 +768,9 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_dmabuf *bdeBuf2 = piocbq->context3;
struct lpfc_hbq_entry *hbqe;
struct lpfc_sli_ct_request *ct_req;
+ struct fc_bsg_job *job = NULL;
+ unsigned long flags;
+ int size = 0;
INIT_LIST_HEAD(&head);
list_add_tail(&head, &piocbq->list);
@@ -504,6 +779,10 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0)
goto error_ct_unsol_exit;
+ if (phba->link_state == LPFC_HBA_ERROR ||
+ (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)))
+ goto error_ct_unsol_exit;
+
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
dmabuf = bdeBuf1;
else {
@@ -511,7 +790,8 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
piocbq->iocb.un.cont64[0].addrLow);
dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr);
}
-
+ if (dmabuf == NULL)
+ goto error_ct_unsol_exit;
ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt;
evt_req_id = ct_req->FsType;
cmd = ct_req->CommandResponse.bits.CmdRsp;
@@ -519,24 +799,24 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
lpfc_sli_ringpostbuf_put(phba, pring, dmabuf);
- mutex_lock(&phba->ct_event_mutex);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
- if (evt->req_id != evt_req_id)
+ if (!(evt->type_mask & FC_REG_CT_EVENT) ||
+ evt->req_id != evt_req_id)
continue;
- lpfc_ct_event_ref(evt);
-
+ lpfc_bsg_event_ref(evt);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL);
- if (!evt_dat) {
- lpfc_ct_event_unref(evt);
+ if (evt_dat == NULL) {
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ lpfc_bsg_event_unref(evt);
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2614 Memory allocation failed for "
"CT event\n");
break;
}
- mutex_unlock(&phba->ct_event_mutex);
-
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
/* take accumulated byte count from the last iocbq */
iocbq = list_entry(head.prev, typeof(*iocbq), list);
@@ -550,25 +830,25 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL);
- if (!evt_dat->data) {
+ if (evt_dat->data == NULL) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2615 Memory allocation failed for "
"CT event data, size %d\n",
evt_dat->len);
kfree(evt_dat);
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt);
- mutex_unlock(&phba->ct_event_mutex);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ lpfc_bsg_event_unref(evt);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
goto error_ct_unsol_exit;
}
list_for_each_entry(iocbq, &head, list) {
+ size = 0;
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
bdeBuf1 = iocbq->context2;
bdeBuf2 = iocbq->context3;
}
for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) {
- int size = 0;
if (phba->sli3_options &
LPFC_SLI3_HBQ_ENABLED) {
if (i == 0) {
@@ -601,9 +881,11 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
iocbq);
kfree(evt_dat->data);
kfree(evt_dat);
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt);
- mutex_unlock(&phba->ct_event_mutex);
+ spin_lock_irqsave(&phba->ct_ev_lock,
+ flags);
+ lpfc_bsg_event_unref(evt);
+ spin_unlock_irqrestore(
+ &phba->ct_ev_lock, flags);
goto error_ct_unsol_exit;
}
memcpy((char *)(evt_dat->data) + offset,
@@ -616,15 +898,24 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
dmabuf);
} else {
switch (cmd) {
+ case ELX_LOOPBACK_DATA:
+ diag_cmd_data_free(phba,
+ (struct lpfc_dmabufext *)
+ dmabuf);
+ break;
case ELX_LOOPBACK_XRI_SETUP:
- if (!(phba->sli3_options &
- LPFC_SLI3_HBQ_ENABLED))
+ if ((phba->sli_rev ==
+ LPFC_SLI_REV2) ||
+ (phba->sli3_options &
+ LPFC_SLI3_HBQ_ENABLED
+ )) {
+ lpfc_in_buf_free(phba,
+ dmabuf);
+ } else {
lpfc_post_buffer(phba,
pring,
1);
- else
- lpfc_in_buf_free(phba,
- dmabuf);
+ }
break;
default:
if (!(phba->sli3_options &
@@ -638,7 +929,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
}
- mutex_lock(&phba->ct_event_mutex);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
if (phba->sli_rev == LPFC_SLI_REV4) {
evt_dat->immed_dat = phba->ctx_idx;
phba->ctx_idx = (phba->ctx_idx + 1) % 64;
@@ -651,122 +942,144 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
evt_dat->type = FC_REG_CT_EVENT;
list_add(&evt_dat->node, &evt->events_to_see);
- wake_up_interruptible(&evt->wq);
- lpfc_ct_event_unref(evt);
- if (evt_req_id == SLI_CT_ELX_LOOPBACK)
+ if (evt_req_id == SLI_CT_ELX_LOOPBACK) {
+ wake_up_interruptible(&evt->wq);
+ lpfc_bsg_event_unref(evt);
break;
+ }
+
+ list_move(evt->events_to_see.prev, &evt->events_to_get);
+ lpfc_bsg_event_unref(evt);
+
+ job = evt->set_job;
+ evt->set_job = NULL;
+ if (job) {
+ job->reply->reply_payload_rcv_len = size;
+ /* make error code available to userspace */
+ job->reply->result = 0;
+ job->dd_data = NULL;
+ /* complete the job back to userspace */
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ job->job_done(job);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ }
}
- mutex_unlock(&phba->ct_event_mutex);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
error_ct_unsol_exit:
if (!list_empty(&head))
list_del(&head);
-
- return;
+ if (evt_req_id == SLI_CT_ELX_LOOPBACK)
+ return 0;
+ return 1;
}
/**
- * lpfc_bsg_set_event - process a SET_EVENT bsg vendor command
+ * lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command
* @job: SET_EVENT fc_bsg_job
- */
+ **/
static int
-lpfc_bsg_set_event(struct fc_bsg_job *job)
+lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
{
struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct set_ct_event *event_req;
- struct lpfc_ct_event *evt;
+ struct lpfc_bsg_event *evt;
int rc = 0;
+ struct bsg_job_data *dd_data = NULL;
+ uint32_t ev_mask;
+ unsigned long flags;
if (job->request_len <
sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2612 Received SET_CT_EVENT below minimum "
"size\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (dd_data == NULL) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2734 Failed allocation of dd_data\n");
+ rc = -ENOMEM;
+ goto job_error;
}
event_req = (struct set_ct_event *)
job->request->rqst_data.h_vendor.vendor_cmd;
-
- mutex_lock(&phba->ct_event_mutex);
+ ev_mask = ((uint32_t)(unsigned long)event_req->type_mask &
+ FC_REG_EVENT_MASK);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
if (evt->reg_id == event_req->ev_reg_id) {
- lpfc_ct_event_ref(evt);
+ lpfc_bsg_event_ref(evt);
evt->wait_time_stamp = jiffies;
break;
}
}
- mutex_unlock(&phba->ct_event_mutex);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
if (&evt->node == &phba->ct_ev_waiters) {
/* no event waiting struct yet - first call */
- evt = lpfc_ct_event_new(event_req->ev_reg_id,
+ evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id,
event_req->ev_req_id);
if (!evt) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2617 Failed allocation of event "
"waiter\n");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto job_error;
}
- mutex_lock(&phba->ct_event_mutex);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_add(&evt->node, &phba->ct_ev_waiters);
- lpfc_ct_event_ref(evt);
- mutex_unlock(&phba->ct_event_mutex);
+ lpfc_bsg_event_ref(evt);
+ evt->wait_time_stamp = jiffies;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
}
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
evt->waiting = 1;
- if (wait_event_interruptible(evt->wq,
- !list_empty(&evt->events_to_see))) {
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt); /* release ref */
- lpfc_ct_event_unref(evt); /* delete */
- mutex_unlock(&phba->ct_event_mutex);
- rc = -EINTR;
- goto set_event_out;
- }
-
- evt->wait_time_stamp = jiffies;
- evt->waiting = 0;
-
- mutex_lock(&phba->ct_event_mutex);
- list_move(evt->events_to_see.prev, &evt->events_to_get);
- lpfc_ct_event_unref(evt); /* release ref */
- mutex_unlock(&phba->ct_event_mutex);
-
-set_event_out:
- /* set_event carries no reply payload */
- job->reply->reply_payload_rcv_len = 0;
- /* make error code available to userspace */
- job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
-
- return 0;
+ dd_data->type = TYPE_EVT;
+ dd_data->context_un.evt = evt;
+ evt->set_job = job; /* for unsolicited command */
+ job->dd_data = dd_data; /* for fc transport timeout callback*/
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return 0; /* call job done later */
+
+job_error:
+ if (dd_data != NULL)
+ kfree(dd_data);
+
+ job->dd_data = NULL;
+ return rc;
}
/**
- * lpfc_bsg_get_event - process a GET_EVENT bsg vendor command
+ * lpfc_bsg_hba_get_event - process a GET_EVENT bsg vendor command
* @job: GET_EVENT fc_bsg_job
- */
+ **/
static int
-lpfc_bsg_get_event(struct fc_bsg_job *job)
+lpfc_bsg_hba_get_event(struct fc_bsg_job *job)
{
struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct get_ct_event *event_req;
struct get_ct_event_reply *event_reply;
- struct lpfc_ct_event *evt;
+ struct lpfc_bsg_event *evt;
struct event_data *evt_dat = NULL;
- int rc = 0;
+ unsigned long flags;
+ uint32_t rc = 0;
if (job->request_len <
sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2613 Received GET_CT_EVENT request below "
"minimum size\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto job_error;
}
event_req = (struct get_ct_event *)
@@ -774,13 +1087,12 @@ lpfc_bsg_get_event(struct fc_bsg_job *job)
event_reply = (struct get_ct_event_reply *)
job->reply->reply_data.vendor_reply.vendor_rsp;
-
- mutex_lock(&phba->ct_event_mutex);
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
if (evt->reg_id == event_req->ev_reg_id) {
if (list_empty(&evt->events_to_get))
break;
- lpfc_ct_event_ref(evt);
+ lpfc_bsg_event_ref(evt);
evt->wait_time_stamp = jiffies;
evt_dat = list_entry(evt->events_to_get.prev,
struct event_data, node);
@@ -788,45 +1100,1539 @@ lpfc_bsg_get_event(struct fc_bsg_job *job)
break;
}
}
- mutex_unlock(&phba->ct_event_mutex);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- if (!evt_dat) {
+ /* The app may continue to ask for event data until it gets
+ * an error indicating that there isn't anymore
+ */
+ if (evt_dat == NULL) {
job->reply->reply_payload_rcv_len = 0;
rc = -ENOENT;
- goto error_get_event_exit;
+ goto job_error;
}
- if (evt_dat->len > job->reply_payload.payload_len) {
- evt_dat->len = job->reply_payload.payload_len;
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2618 Truncated event data at %d "
- "bytes\n",
- job->reply_payload.payload_len);
+ if (evt_dat->len > job->request_payload.payload_len) {
+ evt_dat->len = job->request_payload.payload_len;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2618 Truncated event data at %d "
+ "bytes\n",
+ job->request_payload.payload_len);
}
+ event_reply->type = evt_dat->type;
event_reply->immed_data = evt_dat->immed_dat;
-
if (evt_dat->len > 0)
job->reply->reply_payload_rcv_len =
- sg_copy_from_buffer(job->reply_payload.sg_list,
- job->reply_payload.sg_cnt,
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
evt_dat->data, evt_dat->len);
else
job->reply->reply_payload_rcv_len = 0;
- rc = 0;
- if (evt_dat)
+ if (evt_dat) {
kfree(evt_dat->data);
- kfree(evt_dat);
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt);
- mutex_unlock(&phba->ct_event_mutex);
+ kfree(evt_dat);
+ }
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ lpfc_bsg_event_unref(evt);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ job->dd_data = NULL;
+ job->reply->result = 0;
+ job->job_done(job);
+ return 0;
+
+job_error:
+ job->dd_data = NULL;
+ job->reply->result = rc;
+ return rc;
+}
+
+/**
+ * lpfc_issue_ct_rsp_cmp - lpfc_issue_ct_rsp's completion handler
+ * @phba: Pointer to HBA context object.
+ * @cmdiocbq: Pointer to command iocb.
+ * @rspiocbq: Pointer to response iocb.
+ *
+ * This function is the completion handler for iocbs issued using
+ * lpfc_issue_ct_rsp_cmp function. This function is called by the
+ * ring event handler function without any lock held. This function
+ * can be called from both worker thread context and interrupt
+ * context. This function also can be called from other thread which
+ * cleans up the SLI layer objects.
+ * This function copy the contents of the response iocb to the
+ * response iocb memory object provided by the caller of
+ * lpfc_sli_issue_iocb_wait and then wakes up the thread which
+ * sleeps for the iocb completion.
+ **/
+static void
+lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_iocbq *rspiocbq)
+{
+ struct bsg_job_data *dd_data;
+ struct fc_bsg_job *job;
+ IOCB_t *rsp;
+ struct lpfc_dmabuf *bmp;
+ struct lpfc_nodelist *ndlp;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ dd_data = cmdiocbq->context1;
+ /* normal completion and timeout crossed paths, already done */
+ if (!dd_data) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ return;
+ }
-error_get_event_exit:
+ job = dd_data->context_un.iocb.set_job;
+ bmp = dd_data->context_un.iocb.bmp;
+ rsp = &rspiocbq->iocb;
+ ndlp = dd_data->context_un.iocb.ndlp;
+
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ if (rsp->ulpStatus) {
+ if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
+ switch (rsp->un.ulpWord[4] & 0xff) {
+ case IOERR_SEQUENCE_TIMEOUT:
+ rc = -ETIMEDOUT;
+ break;
+ case IOERR_INVALID_RPI:
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EACCES;
+ break;
+ }
+ } else
+ rc = -EACCES;
+ } else
+ job->reply->reply_payload_rcv_len =
+ rsp->un.genreq64.bdl.bdeSize;
+
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ lpfc_nlp_put(ndlp);
+ kfree(bmp);
+ kfree(dd_data);
/* make error code available to userspace */
job->reply->result = rc;
+ job->dd_data = NULL;
/* complete the job back to userspace */
job->job_done(job);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
+}
+
+/**
+ * lpfc_issue_ct_rsp - issue a ct response
+ * @phba: Pointer to HBA context object.
+ * @job: Pointer to the job object.
+ * @tag: tag index value into the ports context exchange array.
+ * @bmp: Pointer to a dma buffer descriptor.
+ * @num_entry: Number of enties in the bde.
+ **/
+static int
+lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
+ struct lpfc_dmabuf *bmp, int num_entry)
+{
+ IOCB_t *icmd;
+ struct lpfc_iocbq *ctiocb = NULL;
+ int rc = 0;
+ struct lpfc_nodelist *ndlp = NULL;
+ struct bsg_job_data *dd_data;
+ uint32_t creg_val;
+
+ /* allocate our bsg tracking structure */
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2736 Failed allocation of dd_data\n");
+ rc = -ENOMEM;
+ goto no_dd_data;
+ }
+
+ /* Allocate buffer for command iocb */
+ ctiocb = lpfc_sli_get_iocbq(phba);
+ if (!ctiocb) {
+ rc = ENOMEM;
+ goto no_ctiocb;
+ }
+
+ icmd = &ctiocb->iocb;
+ icmd->un.xseq64.bdl.ulpIoTag32 = 0;
+ icmd->un.xseq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ icmd->un.xseq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ icmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ icmd->un.xseq64.bdl.bdeSize = (num_entry * sizeof(struct ulp_bde64));
+ icmd->un.xseq64.w5.hcsw.Fctl = (LS | LA);
+ icmd->un.xseq64.w5.hcsw.Dfctl = 0;
+ icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_SOL_CTL;
+ icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
+
+ /* Fill in rest of iocb */
+ icmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX;
+ icmd->ulpBdeCount = 1;
+ icmd->ulpLe = 1;
+ icmd->ulpClass = CLASS3;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ /* Do not issue unsol response if oxid not marked as valid */
+ if (!(phba->ct_ctx[tag].flags & UNSOL_VALID)) {
+ rc = IOCB_ERROR;
+ goto issue_ct_rsp_exit;
+ }
+ icmd->ulpContext = phba->ct_ctx[tag].oxid;
+ ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID);
+ if (!ndlp) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+ "2721 ndlp null for oxid %x SID %x\n",
+ icmd->ulpContext,
+ phba->ct_ctx[tag].SID);
+ rc = IOCB_ERROR;
+ goto issue_ct_rsp_exit;
+ }
+ icmd->un.ulpWord[3] = ndlp->nlp_rpi;
+ /* The exchange is done, mark the entry as invalid */
+ phba->ct_ctx[tag].flags &= ~UNSOL_VALID;
+ } else
+ icmd->ulpContext = (ushort) tag;
+
+ icmd->ulpTimeout = phba->fc_ratov * 2;
+
+ /* Xmit CT response on exchange <xid> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "2722 Xmit CT response on exchange x%x Data: x%x x%x\n",
+ icmd->ulpContext, icmd->ulpIoTag, phba->link_state);
+
+ ctiocb->iocb_cmpl = NULL;
+ ctiocb->iocb_flag |= LPFC_IO_LIBDFC;
+ ctiocb->vport = phba->pport;
+ ctiocb->context3 = bmp;
+
+ ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp;
+ ctiocb->context1 = dd_data;
+ ctiocb->context2 = NULL;
+ dd_data->type = TYPE_IOCB;
+ dd_data->context_un.iocb.cmdiocbq = ctiocb;
+ dd_data->context_un.iocb.rspiocbq = NULL;
+ dd_data->context_un.iocb.set_job = job;
+ dd_data->context_un.iocb.bmp = bmp;
+ dd_data->context_un.iocb.ndlp = ndlp;
+
+ if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
+ creg_val = readl(phba->HCregaddr);
+ creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
+ writel(creg_val, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
+
+ if (rc == IOCB_SUCCESS)
+ return 0; /* done for now */
+
+issue_ct_rsp_exit:
+ lpfc_sli_release_iocbq(phba, ctiocb);
+no_ctiocb:
+ kfree(dd_data);
+no_dd_data:
+ return rc;
+}
+
+/**
+ * lpfc_bsg_send_mgmt_rsp - process a SEND_MGMT_RESP bsg vendor command
+ * @job: SEND_MGMT_RESP fc_bsg_job
+ **/
+static int
+lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+ struct ulp_bde64 *bpl;
+ struct lpfc_dmabuf *bmp = NULL;
+ struct scatterlist *sgel = NULL;
+ int request_nseg;
+ int numbde;
+ dma_addr_t busaddr;
+ uint32_t tag = mgmt_resp->tag;
+ unsigned long reqbfrcnt =
+ (unsigned long)job->request_payload.payload_len;
+ int rc = 0;
+
+ /* in case no data is transferred */
+ job->reply->reply_payload_rcv_len = 0;
+
+ if (!reqbfrcnt || (reqbfrcnt > (80 * BUF_SZ_4K))) {
+ rc = -ERANGE;
+ goto send_mgmt_rsp_exit;
+ }
+
+ bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!bmp) {
+ rc = -ENOMEM;
+ goto send_mgmt_rsp_exit;
+ }
+
+ bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
+ if (!bmp->virt) {
+ rc = -ENOMEM;
+ goto send_mgmt_rsp_free_bmp;
+ }
+
+ INIT_LIST_HEAD(&bmp->list);
+ bpl = (struct ulp_bde64 *) bmp->virt;
+ request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
+ busaddr = sg_dma_address(sgel);
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bpl->tus.f.bdeSize = sg_dma_len(sgel);
+ bpl->tus.w = cpu_to_le32(bpl->tus.w);
+ bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
+ bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
+ bpl++;
+ }
+
+ rc = lpfc_issue_ct_rsp(phba, job, tag, bmp, request_nseg);
+
+ if (rc == IOCB_SUCCESS)
+ return 0; /* done for now */
+
+ /* TBD need to handle a timeout */
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ rc = -EACCES;
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+
+send_mgmt_rsp_free_bmp:
+ kfree(bmp);
+send_mgmt_rsp_exit:
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ job->dd_data = NULL;
+ return rc;
+}
+
+/**
+ * lpfc_bsg_diag_mode - process a LPFC_BSG_VENDOR_DIAG_MODE bsg vendor command
+ * @job: LPFC_BSG_VENDOR_DIAG_MODE
+ *
+ * This function is responsible for placing a port into diagnostic loopback
+ * mode in order to perform a diagnostic loopback test.
+ * All new scsi requests are blocked, a small delay is used to allow the
+ * scsi requests to complete then the link is brought down. If the link is
+ * is placed in loopback mode then scsi requests are again allowed
+ * so the scsi mid-layer doesn't give up on the port.
+ * All of this is done in-line.
+ */
+static int
+lpfc_bsg_diag_mode(struct fc_bsg_job *job)
+{
+ struct Scsi_Host *shost = job->shost;
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct diag_mode_set *loopback_mode;
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING];
+ uint32_t link_flags;
+ uint32_t timeout;
+ struct lpfc_vport **vports;
+ LPFC_MBOXQ_t *pmboxq;
+ int mbxstatus;
+ int i = 0;
+ int rc = 0;
+
+ /* no data to return just the return code */
+ job->reply->reply_payload_rcv_len = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_set)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2738 Received DIAG MODE request below minimum "
+ "size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ loopback_mode = (struct diag_mode_set *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+ link_flags = loopback_mode->type;
+ timeout = loopback_mode->timeout;
+
+ if ((phba->link_state == LPFC_HBA_ERROR) ||
+ (psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE))) {
+ rc = -EACCES;
+ goto job_error;
+ }
+
+ pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmboxq) {
+ rc = -ENOMEM;
+ goto job_error;
+ }
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports) {
+ for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ scsi_block_requests(shost);
+ }
+
+ lpfc_destroy_vport_work_array(phba, vports);
+ } else {
+ shost = lpfc_shost_from_vport(phba->pport);
+ scsi_block_requests(shost);
+ }
+
+ while (pring->txcmplq_cnt) {
+ if (i++ > 500) /* wait up to 5 seconds */
+ break;
+
+ msleep(10);
+ }
+
+ memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t));
+ pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK;
+ pmboxq->u.mb.mbxOwner = OWN_HOST;
+
+ mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
+
+ if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) {
+ /* wait for link down before proceeding */
+ i = 0;
+ while (phba->link_state != LPFC_LINK_DOWN) {
+ if (i++ > timeout) {
+ rc = -ETIMEDOUT;
+ goto loopback_mode_exit;
+ }
+
+ msleep(10);
+ }
+
+ memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t));
+ if (link_flags == INTERNAL_LOOP_BACK)
+ pmboxq->u.mb.un.varInitLnk.link_flags = FLAGS_LOCAL_LB;
+ else
+ pmboxq->u.mb.un.varInitLnk.link_flags =
+ FLAGS_TOPOLOGY_MODE_LOOP;
+
+ pmboxq->u.mb.mbxCommand = MBX_INIT_LINK;
+ pmboxq->u.mb.mbxOwner = OWN_HOST;
+
+ mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq,
+ LPFC_MBOX_TMO);
+
+ if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus))
+ rc = -ENODEV;
+ else {
+ phba->link_flag |= LS_LOOPBACK_MODE;
+ /* wait for the link attention interrupt */
+ msleep(100);
+
+ i = 0;
+ while (phba->link_state != LPFC_HBA_READY) {
+ if (i++ > timeout) {
+ rc = -ETIMEDOUT;
+ break;
+ }
+
+ msleep(10);
+ }
+ }
+
+ } else
+ rc = -ENODEV;
+
+loopback_mode_exit:
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports) {
+ for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ scsi_unblock_requests(shost);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+ } else {
+ shost = lpfc_shost_from_vport(phba->pport);
+ scsi_unblock_requests(shost);
+ }
+
+ /*
+ * Let SLI layer release mboxq if mbox command completed after timeout.
+ */
+ if (mbxstatus != MBX_TIMEOUT)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+
+job_error:
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace if no error */
+ if (rc == 0)
+ job->job_done(job);
+ return rc;
+}
+
+/**
+ * lpfcdiag_loop_self_reg - obtains a remote port login id
+ * @phba: Pointer to HBA context object
+ * @rpi: Pointer to a remote port login id
+ *
+ * This function obtains a remote port login id so the diag loopback test
+ * can send and receive its own unsolicited CT command.
+ **/
+static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t * rpi)
+{
+ LPFC_MBOXQ_t *mbox;
+ struct lpfc_dmabuf *dmabuff;
+ int status;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return ENOMEM;
+
+ status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID,
+ (uint8_t *)&phba->pport->fc_sparam, mbox, 0);
+ if (status) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return ENOMEM;
+ }
+
+ dmabuff = (struct lpfc_dmabuf *) mbox->context1;
+ mbox->context1 = NULL;
+ status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
+
+ if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) {
+ lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys);
+ kfree(dmabuff);
+ if (status != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return ENODEV;
+ }
+
+ *rpi = mbox->u.mb.un.varWords[0];
+
+ lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys);
+ kfree(dmabuff);
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 0;
+}
+
+/**
+ * lpfcdiag_loop_self_unreg - unregs from the rpi
+ * @phba: Pointer to HBA context object
+ * @rpi: Remote port login id
+ *
+ * This function unregisters the rpi obtained in lpfcdiag_loop_self_reg
+ **/
+static int lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi)
+{
+ LPFC_MBOXQ_t *mbox;
+ int status;
+
+ /* Allocate mboxq structure */
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (mbox == NULL)
+ return ENOMEM;
+
+ lpfc_unreg_login(phba, 0, rpi, mbox);
+ status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
+
+ if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) {
+ if (status != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return EIO;
+ }
+
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 0;
+}
+
+/**
+ * lpfcdiag_loop_get_xri - obtains the transmit and receive ids
+ * @phba: Pointer to HBA context object
+ * @rpi: Remote port login id
+ * @txxri: Pointer to transmit exchange id
+ * @rxxri: Pointer to response exchabge id
+ *
+ * This function obtains the transmit and receive ids required to send
+ * an unsolicited ct command with a payload. A special lpfc FsType and CmdRsp
+ * flags are used to the unsolicted response handler is able to process
+ * the ct command sent on the same port.
+ **/
+static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
+ uint16_t *txxri, uint16_t * rxxri)
+{
+ struct lpfc_bsg_event *evt;
+ struct lpfc_iocbq *cmdiocbq, *rspiocbq;
+ IOCB_t *cmd, *rsp;
+ struct lpfc_dmabuf *dmabuf;
+ struct ulp_bde64 *bpl = NULL;
+ struct lpfc_sli_ct_request *ctreq = NULL;
+ int ret_val = 0;
+ unsigned long flags;
+
+ *txxri = 0;
+ *rxxri = 0;
+ evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid,
+ SLI_CT_ELX_LOOPBACK);
+ if (!evt)
+ return ENOMEM;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ list_add(&evt->node, &phba->ct_ev_waiters);
+ lpfc_bsg_event_ref(evt);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ rspiocbq = lpfc_sli_get_iocbq(phba);
+
+ dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (dmabuf) {
+ dmabuf->virt = lpfc_mbuf_alloc(phba, 0, &dmabuf->phys);
+ INIT_LIST_HEAD(&dmabuf->list);
+ bpl = (struct ulp_bde64 *) dmabuf->virt;
+ memset(bpl, 0, sizeof(*bpl));
+ ctreq = (struct lpfc_sli_ct_request *)(bpl + 1);
+ bpl->addrHigh =
+ le32_to_cpu(putPaddrHigh(dmabuf->phys + sizeof(*bpl)));
+ bpl->addrLow =
+ le32_to_cpu(putPaddrLow(dmabuf->phys + sizeof(*bpl)));
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ }
+
+ if (cmdiocbq == NULL || rspiocbq == NULL ||
+ dmabuf == NULL || bpl == NULL || ctreq == NULL) {
+ ret_val = ENOMEM;
+ goto err_get_xri_exit;
+ }
+
+ cmd = &cmdiocbq->iocb;
+ rsp = &rspiocbq->iocb;
+
+ memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ);
+
+ ctreq->RevisionId.bits.Revision = SLI_CT_REVISION;
+ ctreq->RevisionId.bits.InId = 0;
+ ctreq->FsType = SLI_CT_ELX_LOOPBACK;
+ ctreq->FsSubType = 0;
+ ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_XRI_SETUP;
+ ctreq->CommandResponse.bits.Size = 0;
+
+
+ cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(dmabuf->phys);
+ cmd->un.xseq64.bdl.addrLow = putPaddrLow(dmabuf->phys);
+ cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ cmd->un.xseq64.bdl.bdeSize = sizeof(*bpl);
+
+ cmd->un.xseq64.w5.hcsw.Fctl = LA;
+ cmd->un.xseq64.w5.hcsw.Dfctl = 0;
+ cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
+
+ cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CR;
+ cmd->ulpBdeCount = 1;
+ cmd->ulpLe = 1;
+ cmd->ulpClass = CLASS3;
+ cmd->ulpContext = rpi;
+
+ cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->vport = phba->pport;
+
+ ret_val = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
+ rspiocbq,
+ (phba->fc_ratov * 2)
+ + LPFC_DRVR_TIMEOUT);
+ if (ret_val)
+ goto err_get_xri_exit;
+
+ *txxri = rsp->ulpContext;
+
+ evt->waiting = 1;
+ evt->wait_time_stamp = jiffies;
+ ret_val = wait_event_interruptible_timeout(
+ evt->wq, !list_empty(&evt->events_to_see),
+ ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ);
+ if (list_empty(&evt->events_to_see))
+ ret_val = (ret_val) ? EINTR : ETIMEDOUT;
+ else {
+ ret_val = IOCB_SUCCESS;
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ list_move(evt->events_to_see.prev, &evt->events_to_get);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ *rxxri = (list_entry(evt->events_to_get.prev,
+ typeof(struct event_data),
+ node))->immed_dat;
+ }
+ evt->waiting = 0;
+
+err_get_xri_exit:
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ lpfc_bsg_event_unref(evt); /* release ref */
+ lpfc_bsg_event_unref(evt); /* delete */
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ if (dmabuf) {
+ if (dmabuf->virt)
+ lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ }
+
+ if (cmdiocbq && (ret_val != IOCB_TIMEDOUT))
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ if (rspiocbq)
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ return ret_val;
+}
+
+/**
+ * diag_cmd_data_alloc - fills in a bde struct with dma buffers
+ * @phba: Pointer to HBA context object
+ * @bpl: Pointer to 64 bit bde structure
+ * @size: Number of bytes to process
+ * @nocopydata: Flag to copy user data into the allocated buffer
+ *
+ * This function allocates page size buffers and populates an lpfc_dmabufext.
+ * If allowed the user data pointed to with indataptr is copied into the kernel
+ * memory. The chained list of page size buffers is returned.
+ **/
+static struct lpfc_dmabufext *
+diag_cmd_data_alloc(struct lpfc_hba *phba,
+ struct ulp_bde64 *bpl, uint32_t size,
+ int nocopydata)
+{
+ struct lpfc_dmabufext *mlist = NULL;
+ struct lpfc_dmabufext *dmp;
+ int cnt, offset = 0, i = 0;
+ struct pci_dev *pcidev;
+
+ pcidev = phba->pcidev;
+
+ while (size) {
+ /* We get chunks of 4K */
+ if (size > BUF_SZ_4K)
+ cnt = BUF_SZ_4K;
+ else
+ cnt = size;
+
+ /* allocate struct lpfc_dmabufext buffer header */
+ dmp = kmalloc(sizeof(struct lpfc_dmabufext), GFP_KERNEL);
+ if (!dmp)
+ goto out;
+
+ INIT_LIST_HEAD(&dmp->dma.list);
+
+ /* Queue it to a linked list */
+ if (mlist)
+ list_add_tail(&dmp->dma.list, &mlist->dma.list);
+ else
+ mlist = dmp;
+
+ /* allocate buffer */
+ dmp->dma.virt = dma_alloc_coherent(&pcidev->dev,
+ cnt,
+ &(dmp->dma.phys),
+ GFP_KERNEL);
+
+ if (!dmp->dma.virt)
+ goto out;
+
+ dmp->size = cnt;
+
+ if (nocopydata) {
+ bpl->tus.f.bdeFlags = 0;
+ pci_dma_sync_single_for_device(phba->pcidev,
+ dmp->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+ } else {
+ memset((uint8_t *)dmp->dma.virt, 0, cnt);
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ }
+
+ /* build buffer ptr list for IOCB */
+ bpl->addrLow = le32_to_cpu(putPaddrLow(dmp->dma.phys));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(dmp->dma.phys));
+ bpl->tus.f.bdeSize = (ushort) cnt;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+
+ i++;
+ offset += cnt;
+ size -= cnt;
+ }
+
+ mlist->flag = i;
+ return mlist;
+out:
+ diag_cmd_data_free(phba, mlist);
+ return NULL;
+}
+
+/**
+ * lpfcdiag_loop_post_rxbufs - post the receive buffers for an unsol CT cmd
+ * @phba: Pointer to HBA context object
+ * @rxxri: Receive exchange id
+ * @len: Number of data bytes
+ *
+ * This function allocates and posts a data buffer of sufficient size to recieve
+ * an unsolicted CT command.
+ **/
+static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
+ size_t len)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
+ struct lpfc_iocbq *cmdiocbq;
+ IOCB_t *cmd = NULL;
+ struct list_head head, *curr, *next;
+ struct lpfc_dmabuf *rxbmp;
+ struct lpfc_dmabuf *dmp;
+ struct lpfc_dmabuf *mp[2] = {NULL, NULL};
+ struct ulp_bde64 *rxbpl = NULL;
+ uint32_t num_bde;
+ struct lpfc_dmabufext *rxbuffer = NULL;
+ int ret_val = 0;
+ int i = 0;
+
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (rxbmp != NULL) {
+ rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
+ INIT_LIST_HEAD(&rxbmp->list);
+ rxbpl = (struct ulp_bde64 *) rxbmp->virt;
+ rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0);
+ }
+
+ if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer) {
+ ret_val = ENOMEM;
+ goto err_post_rxbufs_exit;
+ }
+
+ /* Queue buffers for the receive exchange */
+ num_bde = (uint32_t)rxbuffer->flag;
+ dmp = &rxbuffer->dma;
+
+ cmd = &cmdiocbq->iocb;
+ i = 0;
+
+ INIT_LIST_HEAD(&head);
+ list_add_tail(&head, &dmp->list);
+ list_for_each_safe(curr, next, &head) {
+ mp[i] = list_entry(curr, struct lpfc_dmabuf, list);
+ list_del(curr);
+
+ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+ mp[i]->buffer_tag = lpfc_sli_get_buffer_tag(phba);
+ cmd->un.quexri64cx.buff.bde.addrHigh =
+ putPaddrHigh(mp[i]->phys);
+ cmd->un.quexri64cx.buff.bde.addrLow =
+ putPaddrLow(mp[i]->phys);
+ cmd->un.quexri64cx.buff.bde.tus.f.bdeSize =
+ ((struct lpfc_dmabufext *)mp[i])->size;
+ cmd->un.quexri64cx.buff.buffer_tag = mp[i]->buffer_tag;
+ cmd->ulpCommand = CMD_QUE_XRI64_CX;
+ cmd->ulpPU = 0;
+ cmd->ulpLe = 1;
+ cmd->ulpBdeCount = 1;
+ cmd->unsli3.que_xri64cx_ext_words.ebde_count = 0;
+
+ } else {
+ cmd->un.cont64[i].addrHigh = putPaddrHigh(mp[i]->phys);
+ cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys);
+ cmd->un.cont64[i].tus.f.bdeSize =
+ ((struct lpfc_dmabufext *)mp[i])->size;
+ cmd->ulpBdeCount = ++i;
+
+ if ((--num_bde > 0) && (i < 2))
+ continue;
+
+ cmd->ulpCommand = CMD_QUE_XRI_BUF64_CX;
+ cmd->ulpLe = 1;
+ }
+
+ cmd->ulpClass = CLASS3;
+ cmd->ulpContext = rxxri;
+
+ ret_val = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
+
+ if (ret_val == IOCB_ERROR) {
+ diag_cmd_data_free(phba,
+ (struct lpfc_dmabufext *)mp[0]);
+ if (mp[1])
+ diag_cmd_data_free(phba,
+ (struct lpfc_dmabufext *)mp[1]);
+ dmp = list_entry(next, struct lpfc_dmabuf, list);
+ ret_val = EIO;
+ goto err_post_rxbufs_exit;
+ }
+
+ lpfc_sli_ringpostbuf_put(phba, pring, mp[0]);
+ if (mp[1]) {
+ lpfc_sli_ringpostbuf_put(phba, pring, mp[1]);
+ mp[1] = NULL;
+ }
+
+ /* The iocb was freed by lpfc_sli_issue_iocb */
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ if (!cmdiocbq) {
+ dmp = list_entry(next, struct lpfc_dmabuf, list);
+ ret_val = EIO;
+ goto err_post_rxbufs_exit;
+ }
+
+ cmd = &cmdiocbq->iocb;
+ i = 0;
+ }
+ list_del(&head);
+
+err_post_rxbufs_exit:
+
+ if (rxbmp) {
+ if (rxbmp->virt)
+ lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys);
+ kfree(rxbmp);
+ }
+
+ if (cmdiocbq)
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ return ret_val;
+}
+
+/**
+ * lpfc_bsg_diag_test - with a port in loopback issues a Ct cmd to itself
+ * @job: LPFC_BSG_VENDOR_DIAG_TEST fc_bsg_job
+ *
+ * This function receives a user data buffer to be transmitted and received on
+ * the same port, the link must be up and in loopback mode prior
+ * to being called.
+ * 1. A kernel buffer is allocated to copy the user data into.
+ * 2. The port registers with "itself".
+ * 3. The transmit and receive exchange ids are obtained.
+ * 4. The receive exchange id is posted.
+ * 5. A new els loopback event is created.
+ * 6. The command and response iocbs are allocated.
+ * 7. The cmd iocb FsType is set to elx loopback and the CmdRsp to looppback.
+ *
+ * This function is meant to be called n times while the port is in loopback
+ * so it is the apps responsibility to issue a reset to take the port out
+ * of loopback mode.
+ **/
+static int
+lpfc_bsg_diag_test(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct diag_mode_test *diag_mode;
+ struct lpfc_bsg_event *evt;
+ struct event_data *evdat;
+ struct lpfc_sli *psli = &phba->sli;
+ uint32_t size;
+ uint32_t full_size;
+ size_t segment_len = 0, segment_offset = 0, current_offset = 0;
+ uint16_t rpi;
+ struct lpfc_iocbq *cmdiocbq, *rspiocbq;
+ IOCB_t *cmd, *rsp;
+ struct lpfc_sli_ct_request *ctreq;
+ struct lpfc_dmabuf *txbmp;
+ struct ulp_bde64 *txbpl = NULL;
+ struct lpfc_dmabufext *txbuffer = NULL;
+ struct list_head head;
+ struct lpfc_dmabuf *curr;
+ uint16_t txxri, rxxri;
+ uint32_t num_bde;
+ uint8_t *ptr = NULL, *rx_databuf = NULL;
+ int rc = 0;
+ unsigned long flags;
+ void *dataout = NULL;
+ uint32_t total_mem;
+
+ /* in case no data is returned return just the return code */
+ job->reply->reply_payload_rcv_len = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_test)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2739 Received DIAG TEST request below minimum "
+ "size\n");
+ rc = -EINVAL;
+ goto loopback_test_exit;
+ }
+
+ if (job->request_payload.payload_len !=
+ job->reply_payload.payload_len) {
+ rc = -EINVAL;
+ goto loopback_test_exit;
+ }
+
+ diag_mode = (struct diag_mode_test *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+
+ if ((phba->link_state == LPFC_HBA_ERROR) ||
+ (psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
+ (!(psli->sli_flag & LPFC_SLI_ACTIVE))) {
+ rc = -EACCES;
+ goto loopback_test_exit;
+ }
+
+ if (!lpfc_is_link_up(phba) || !(phba->link_flag & LS_LOOPBACK_MODE)) {
+ rc = -EACCES;
+ goto loopback_test_exit;
+ }
+
+ size = job->request_payload.payload_len;
+ full_size = size + ELX_LOOPBACK_HEADER_SZ; /* plus the header */
+
+ if ((size == 0) || (size > 80 * BUF_SZ_4K)) {
+ rc = -ERANGE;
+ goto loopback_test_exit;
+ }
+
+ if (size >= BUF_SZ_4K) {
+ /*
+ * Allocate memory for ioctl data. If buffer is bigger than 64k,
+ * then we allocate 64k and re-use that buffer over and over to
+ * xfer the whole block. This is because Linux kernel has a
+ * problem allocating more than 120k of kernel space memory. Saw
+ * problem with GET_FCPTARGETMAPPING...
+ */
+ if (size <= (64 * 1024))
+ total_mem = size;
+ else
+ total_mem = 64 * 1024;
+ } else
+ /* Allocate memory for ioctl data */
+ total_mem = BUF_SZ_4K;
+
+ dataout = kmalloc(total_mem, GFP_KERNEL);
+ if (dataout == NULL) {
+ rc = -ENOMEM;
+ goto loopback_test_exit;
+ }
+
+ ptr = dataout;
+ ptr += ELX_LOOPBACK_HEADER_SZ;
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ ptr, size);
+
+ rc = lpfcdiag_loop_self_reg(phba, &rpi);
+ if (rc) {
+ rc = -ENOMEM;
+ goto loopback_test_exit;
+ }
+
+ rc = lpfcdiag_loop_get_xri(phba, rpi, &txxri, &rxxri);
+ if (rc) {
+ lpfcdiag_loop_self_unreg(phba, rpi);
+ rc = -ENOMEM;
+ goto loopback_test_exit;
+ }
+
+ rc = lpfcdiag_loop_post_rxbufs(phba, rxxri, full_size);
+ if (rc) {
+ lpfcdiag_loop_self_unreg(phba, rpi);
+ rc = -ENOMEM;
+ goto loopback_test_exit;
+ }
+
+ evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid,
+ SLI_CT_ELX_LOOPBACK);
+ if (!evt) {
+ lpfcdiag_loop_self_unreg(phba, rpi);
+ rc = -ENOMEM;
+ goto loopback_test_exit;
+ }
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ list_add(&evt->node, &phba->ct_ev_waiters);
+ lpfc_bsg_event_ref(evt);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ rspiocbq = lpfc_sli_get_iocbq(phba);
+ txbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+
+ if (txbmp) {
+ txbmp->virt = lpfc_mbuf_alloc(phba, 0, &txbmp->phys);
+ INIT_LIST_HEAD(&txbmp->list);
+ txbpl = (struct ulp_bde64 *) txbmp->virt;
+ if (txbpl)
+ txbuffer = diag_cmd_data_alloc(phba,
+ txbpl, full_size, 0);
+ }
+
+ if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer) {
+ rc = -ENOMEM;
+ goto err_loopback_test_exit;
+ }
+
+ cmd = &cmdiocbq->iocb;
+ rsp = &rspiocbq->iocb;
+
+ INIT_LIST_HEAD(&head);
+ list_add_tail(&head, &txbuffer->dma.list);
+ list_for_each_entry(curr, &head, list) {
+ segment_len = ((struct lpfc_dmabufext *)curr)->size;
+ if (current_offset == 0) {
+ ctreq = curr->virt;
+ memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ);
+ ctreq->RevisionId.bits.Revision = SLI_CT_REVISION;
+ ctreq->RevisionId.bits.InId = 0;
+ ctreq->FsType = SLI_CT_ELX_LOOPBACK;
+ ctreq->FsSubType = 0;
+ ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_DATA;
+ ctreq->CommandResponse.bits.Size = size;
+ segment_offset = ELX_LOOPBACK_HEADER_SZ;
+ } else
+ segment_offset = 0;
+
+ BUG_ON(segment_offset >= segment_len);
+ memcpy(curr->virt + segment_offset,
+ ptr + current_offset,
+ segment_len - segment_offset);
+
+ current_offset += segment_len - segment_offset;
+ BUG_ON(current_offset > size);
+ }
+ list_del(&head);
+
+ /* Build the XMIT_SEQUENCE iocb */
+
+ num_bde = (uint32_t)txbuffer->flag;
+
+ cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(txbmp->phys);
+ cmd->un.xseq64.bdl.addrLow = putPaddrLow(txbmp->phys);
+ cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ cmd->un.xseq64.bdl.bdeSize = (num_bde * sizeof(struct ulp_bde64));
+
+ cmd->un.xseq64.w5.hcsw.Fctl = (LS | LA);
+ cmd->un.xseq64.w5.hcsw.Dfctl = 0;
+ cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
+
+ cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX;
+ cmd->ulpBdeCount = 1;
+ cmd->ulpLe = 1;
+ cmd->ulpClass = CLASS3;
+ cmd->ulpContext = txxri;
+
+ cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->vport = phba->pport;
+
+ rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq,
+ (phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT);
+
+ if ((rc != IOCB_SUCCESS) || (rsp->ulpStatus != IOCB_SUCCESS)) {
+ rc = -EIO;
+ goto err_loopback_test_exit;
+ }
+
+ evt->waiting = 1;
+ rc = wait_event_interruptible_timeout(
+ evt->wq, !list_empty(&evt->events_to_see),
+ ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ);
+ evt->waiting = 0;
+ if (list_empty(&evt->events_to_see))
+ rc = (rc) ? -EINTR : -ETIMEDOUT;
+ else {
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ list_move(evt->events_to_see.prev, &evt->events_to_get);
+ evdat = list_entry(evt->events_to_get.prev,
+ typeof(*evdat), node);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ rx_databuf = evdat->data;
+ if (evdat->len != full_size) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "1603 Loopback test did not receive expected "
+ "data length. actual length 0x%x expected "
+ "length 0x%x\n",
+ evdat->len, full_size);
+ rc = -EIO;
+ } else if (rx_databuf == NULL)
+ rc = -EIO;
+ else {
+ rc = IOCB_SUCCESS;
+ /* skip over elx loopback header */
+ rx_databuf += ELX_LOOPBACK_HEADER_SZ;
+ job->reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ rx_databuf, size);
+ job->reply->reply_payload_rcv_len = size;
+ }
+ }
+
+err_loopback_test_exit:
+ lpfcdiag_loop_self_unreg(phba, rpi);
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ lpfc_bsg_event_unref(evt); /* release ref */
+ lpfc_bsg_event_unref(evt); /* delete */
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ if (cmdiocbq != NULL)
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+
+ if (rspiocbq != NULL)
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+
+ if (txbmp != NULL) {
+ if (txbpl != NULL) {
+ if (txbuffer != NULL)
+ diag_cmd_data_free(phba, txbuffer);
+ lpfc_mbuf_free(phba, txbmp->virt, txbmp->phys);
+ }
+ kfree(txbmp);
+ }
+
+loopback_test_exit:
+ kfree(dataout);
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ job->dd_data = NULL;
+ /* complete the job back to userspace if no error */
+ if (rc == 0)
+ job->job_done(job);
+ return rc;
+}
+
+/**
+ * lpfc_bsg_get_dfc_rev - process a GET_DFC_REV bsg vendor command
+ * @job: GET_DFC_REV fc_bsg_job
+ **/
+static int
+lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct get_mgmt_rev *event_req;
+ struct get_mgmt_rev_reply *event_reply;
+ int rc = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2740 Received GET_DFC_REV request below "
+ "minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ event_req = (struct get_mgmt_rev *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+
+ event_reply = (struct get_mgmt_rev_reply *)
+ job->reply->reply_data.vendor_reply.vendor_rsp;
+
+ if (job->reply_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev_reply)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2741 Received GET_DFC_REV reply below "
+ "minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ event_reply->info.a_Major = MANAGEMENT_MAJOR_REV;
+ event_reply->info.a_Minor = MANAGEMENT_MINOR_REV;
+job_error:
+ job->reply->result = rc;
+ if (rc == 0)
+ job->job_done(job);
+ return rc;
+}
+
+/**
+ * lpfc_bsg_wake_mbox_wait - lpfc_bsg_issue_mbox mbox completion handler
+ * @phba: Pointer to HBA context object.
+ * @pmboxq: Pointer to mailbox command.
+ *
+ * This is completion handler function for mailbox commands issued from
+ * lpfc_bsg_issue_mbox function. This function is called by the
+ * mailbox event handler function with no lock held. This function
+ * will wake up thread waiting on the wait queue pointed by context1
+ * of the mailbox.
+ **/
+void
+lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+{
+ struct bsg_job_data *dd_data;
+ MAILBOX_t *pmb;
+ MAILBOX_t *mb;
+ struct fc_bsg_job *job;
+ uint32_t size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ dd_data = pmboxq->context1;
+ if (!dd_data) {
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
+ }
+
+ pmb = &dd_data->context_un.mbox.pmboxq->u.mb;
+ mb = dd_data->context_un.mbox.mb;
+ job = dd_data->context_un.mbox.set_job;
+ memcpy(mb, pmb, sizeof(*pmb));
+ size = job->request_payload.payload_len;
+ job->reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ mb, size);
+ job->reply->result = 0;
+ dd_data->context_un.mbox.set_job = NULL;
+ job->dd_data = NULL;
+ job->job_done(job);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
+ kfree(mb);
+ kfree(dd_data);
+ return;
+}
+
+/**
+ * lpfc_bsg_check_cmd_access - test for a supported mailbox command
+ * @phba: Pointer to HBA context object.
+ * @mb: Pointer to a mailbox object.
+ * @vport: Pointer to a vport object.
+ *
+ * Some commands require the port to be offline, some may not be called from
+ * the application.
+ **/
+static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
+ MAILBOX_t *mb, struct lpfc_vport *vport)
+{
+ /* return negative error values for bsg job */
+ switch (mb->mbxCommand) {
+ /* Offline only */
+ case MBX_INIT_LINK:
+ case MBX_DOWN_LINK:
+ case MBX_CONFIG_LINK:
+ case MBX_CONFIG_RING:
+ case MBX_RESET_RING:
+ case MBX_UNREG_LOGIN:
+ case MBX_CLEAR_LA:
+ case MBX_DUMP_CONTEXT:
+ case MBX_RUN_DIAGS:
+ case MBX_RESTART:
+ case MBX_SET_MASK:
+ if (!(vport->fc_flag & FC_OFFLINE_MODE)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2743 Command 0x%x is illegal in on-line "
+ "state\n",
+ mb->mbxCommand);
+ return -EPERM;
+ }
+ case MBX_WRITE_NV:
+ case MBX_WRITE_VPARMS:
+ case MBX_LOAD_SM:
+ case MBX_READ_NV:
+ case MBX_READ_CONFIG:
+ case MBX_READ_RCONFIG:
+ case MBX_READ_STATUS:
+ case MBX_READ_XRI:
+ case MBX_READ_REV:
+ case MBX_READ_LNK_STAT:
+ case MBX_DUMP_MEMORY:
+ case MBX_DOWN_LOAD:
+ case MBX_UPDATE_CFG:
+ case MBX_KILL_BOARD:
+ case MBX_LOAD_AREA:
+ case MBX_LOAD_EXP_ROM:
+ case MBX_BEACON:
+ case MBX_DEL_LD_ENTRY:
+ case MBX_SET_DEBUG:
+ case MBX_WRITE_WWN:
+ case MBX_SLI4_CONFIG:
+ case MBX_READ_EVENT_LOG_STATUS:
+ case MBX_WRITE_EVENT_LOG:
+ case MBX_PORT_CAPABILITIES:
+ case MBX_PORT_IOV_CONTROL:
+ break;
+ case MBX_SET_VARIABLE:
+ case MBX_RUN_BIU_DIAG64:
+ case MBX_READ_EVENT_LOG:
+ case MBX_READ_SPARM64:
+ case MBX_READ_LA:
+ case MBX_READ_LA64:
+ case MBX_REG_LOGIN:
+ case MBX_REG_LOGIN64:
+ case MBX_CONFIG_PORT:
+ case MBX_RUN_BIU_DIAG:
+ default:
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2742 Unknown Command 0x%x\n",
+ mb->mbxCommand);
+ return -EPERM;
+ }
+
+ return 0; /* ok */
+}
+
+/**
+ * lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app
+ * @phba: Pointer to HBA context object.
+ * @mb: Pointer to a mailbox object.
+ * @vport: Pointer to a vport object.
+ *
+ * Allocate a tracking object, mailbox command memory, get a mailbox
+ * from the mailbox pool, copy the caller mailbox command.
+ *
+ * If offline and the sli is active we need to poll for the command (port is
+ * being reset) and com-plete the job, otherwise issue the mailbox command and
+ * let our completion handler finish the command.
+ **/
+static uint32_t
+lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
+ struct lpfc_vport *vport)
+{
+ LPFC_MBOXQ_t *pmboxq;
+ MAILBOX_t *pmb;
+ MAILBOX_t *mb;
+ struct bsg_job_data *dd_data;
+ uint32_t size;
+ int rc = 0;
+
+ /* allocate our bsg tracking structure */
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2727 Failed allocation of dd_data\n");
+ return -ENOMEM;
+ }
+
+ mb = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!mb) {
+ kfree(dd_data);
+ return -ENOMEM;
+ }
+
+ pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmboxq) {
+ kfree(dd_data);
+ kfree(mb);
+ return -ENOMEM;
+ }
+
+ size = job->request_payload.payload_len;
+ job->reply->reply_payload_rcv_len =
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ mb, size);
+
+ rc = lpfc_bsg_check_cmd_access(phba, mb, vport);
+ if (rc != 0) {
+ kfree(dd_data);
+ kfree(mb);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return rc; /* must be negative */
+ }
+
+ memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
+ pmb = &pmboxq->u.mb;
+ memcpy(pmb, mb, sizeof(*pmb));
+ pmb->mbxOwner = OWN_HOST;
+ pmboxq->context1 = NULL;
+ pmboxq->vport = vport;
+
+ if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+ (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
+ rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ if (rc != MBX_TIMEOUT) {
+ kfree(dd_data);
+ kfree(mb);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ }
+ return (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;
+ }
+
+ memcpy(mb, pmb, sizeof(*pmb));
+ job->reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ mb, size);
+ kfree(dd_data);
+ kfree(mb);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ /* not waiting mbox already done */
+ return 0;
+ }
+
+ /* setup wake call as IOCB callback */
+ pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait;
+ /* setup context field to pass wait_queue pointer to wake function */
+ pmboxq->context1 = dd_data;
+ dd_data->type = TYPE_MBOX;
+ dd_data->context_un.mbox.pmboxq = pmboxq;
+ dd_data->context_un.mbox.mb = mb;
+ dd_data->context_un.mbox.set_job = job;
+ job->dd_data = dd_data;
+ rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
+ if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
+ kfree(dd_data);
+ kfree(mb);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return -EIO;
+ }
+
+ return 1;
+}
+
+/**
+ * lpfc_bsg_mbox_cmd - process an fc bsg LPFC_BSG_VENDOR_MBOX command
+ * @job: MBOX fc_bsg_job for LPFC_BSG_VENDOR_MBOX.
+ **/
+static int
+lpfc_bsg_mbox_cmd(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int rc = 0;
+
+ /* in case no data is transferred */
+ job->reply->reply_payload_rcv_len = 0;
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct dfc_mbox_req)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2737 Received MBOX_REQ request below "
+ "minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ if (job->request_payload.payload_len != PAGE_SIZE) {
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
+ rc = -EAGAIN;
+ goto job_error;
+ }
+
+ rc = lpfc_bsg_issue_mbox(phba, job, vport);
+
+job_error:
+ if (rc == 0) {
+ /* job done */
+ job->reply->result = 0;
+ job->dd_data = NULL;
+ job->job_done(job);
+ } else if (rc == 1)
+ /* job submitted, will complete later*/
+ rc = 0; /* return zero, no error */
+ else {
+ /* some error occurred */
+ job->reply->result = rc;
+ job->dd_data = NULL;
+ }
return rc;
}
@@ -834,38 +2640,57 @@ error_get_event_exit:
/**
* lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
* @job: fc_bsg_job to handle
- */
+ **/
static int
lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
{
int command = job->request->rqst_data.h_vendor.vendor_cmd[0];
+ int rc;
switch (command) {
case LPFC_BSG_VENDOR_SET_CT_EVENT:
- return lpfc_bsg_set_event(job);
+ rc = lpfc_bsg_hba_set_event(job);
break;
-
case LPFC_BSG_VENDOR_GET_CT_EVENT:
- return lpfc_bsg_get_event(job);
+ rc = lpfc_bsg_hba_get_event(job);
+ break;
+ case LPFC_BSG_VENDOR_SEND_MGMT_RESP:
+ rc = lpfc_bsg_send_mgmt_rsp(job);
+ break;
+ case LPFC_BSG_VENDOR_DIAG_MODE:
+ rc = lpfc_bsg_diag_mode(job);
+ break;
+ case LPFC_BSG_VENDOR_DIAG_TEST:
+ rc = lpfc_bsg_diag_test(job);
+ break;
+ case LPFC_BSG_VENDOR_GET_MGMT_REV:
+ rc = lpfc_bsg_get_dfc_rev(job);
+ break;
+ case LPFC_BSG_VENDOR_MBOX:
+ rc = lpfc_bsg_mbox_cmd(job);
break;
-
default:
- return -EINVAL;
+ rc = -EINVAL;
+ job->reply->reply_payload_rcv_len = 0;
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ break;
}
+
+ return rc;
}
/**
* lpfc_bsg_request - handle a bsg request from the FC transport
* @job: fc_bsg_job to handle
- */
+ **/
int
lpfc_bsg_request(struct fc_bsg_job *job)
{
uint32_t msgcode;
- int rc = -EINVAL;
+ int rc;
msgcode = job->request->msgcode;
-
switch (msgcode) {
case FC_BSG_HST_VENDOR:
rc = lpfc_bsg_hst_vendor(job);
@@ -874,9 +2699,13 @@ lpfc_bsg_request(struct fc_bsg_job *job)
rc = lpfc_bsg_rport_els(job);
break;
case FC_BSG_RPT_CT:
- rc = lpfc_bsg_rport_ct(job);
+ rc = lpfc_bsg_send_mgmt_cmd(job);
break;
default:
+ rc = -EINVAL;
+ job->reply->reply_payload_rcv_len = 0;
+ /* make error code available to userspace */
+ job->reply->result = rc;
break;
}
@@ -889,17 +2718,71 @@ lpfc_bsg_request(struct fc_bsg_job *job)
*
* This function just aborts the job's IOCB. The aborted IOCB will return to
* the waiting function which will handle passing the error back to userspace
- */
+ **/
int
lpfc_bsg_timeout(struct fc_bsg_job *job)
{
struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)job->dd_data;
+ struct lpfc_iocbq *cmdiocb;
+ struct lpfc_bsg_event *evt;
+ struct lpfc_bsg_iocb *iocb;
+ struct lpfc_bsg_mbox *mbox;
struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct bsg_job_data *dd_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ dd_data = (struct bsg_job_data *)job->dd_data;
+ /* timeout and completion crossed paths if no dd_data */
+ if (!dd_data) {
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return 0;
+ }
- if (cmdiocb)
+ switch (dd_data->type) {
+ case TYPE_IOCB:
+ iocb = &dd_data->context_un.iocb;
+ cmdiocb = iocb->cmdiocbq;
+ /* hint to completion handler that the job timed out */
+ job->reply->result = -EAGAIN;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ /* this will call our completion handler */
+ spin_lock_irq(&phba->hbalock);
lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
+ spin_unlock_irq(&phba->hbalock);
+ break;
+ case TYPE_EVT:
+ evt = dd_data->context_un.evt;
+ /* this event has no job anymore */
+ evt->set_job = NULL;
+ job->dd_data = NULL;
+ job->reply->reply_payload_rcv_len = 0;
+ /* Return -EAGAIN which is our way of signallying the
+ * app to retry.
+ */
+ job->reply->result = -EAGAIN;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ job->job_done(job);
+ break;
+ case TYPE_MBOX:
+ mbox = &dd_data->context_un.mbox;
+ /* this mbox has no job anymore */
+ mbox->set_job = NULL;
+ job->dd_data = NULL;
+ job->reply->reply_payload_rcv_len = 0;
+ job->reply->result = -EAGAIN;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ job->job_done(job);
+ break;
+ default:
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ break;
+ }
+ /* scsi transport fc fc_bsg_job_timeout expects a zero return code,
+ * otherwise an error message will be displayed on the console
+ * so always return success (zero)
+ */
return 0;
}
diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h
new file mode 100644
index 00000000000..6c8f87e39b9
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_bsg.h
@@ -0,0 +1,98 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2010 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.emulex.com *
+ * *
+ * 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. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+/* bsg definitions
+ * No pointers to user data are allowed, all application buffers and sizes will
+ * derived through the bsg interface.
+ *
+ * These are the vendor unique structures passed in using the bsg
+ * FC_BSG_HST_VENDOR message code type.
+ */
+#define LPFC_BSG_VENDOR_SET_CT_EVENT 1
+#define LPFC_BSG_VENDOR_GET_CT_EVENT 2
+#define LPFC_BSG_VENDOR_SEND_MGMT_RESP 3
+#define LPFC_BSG_VENDOR_DIAG_MODE 4
+#define LPFC_BSG_VENDOR_DIAG_TEST 5
+#define LPFC_BSG_VENDOR_GET_MGMT_REV 6
+#define LPFC_BSG_VENDOR_MBOX 7
+
+struct set_ct_event {
+ uint32_t command;
+ uint32_t type_mask;
+ uint32_t ev_req_id;
+ uint32_t ev_reg_id;
+};
+
+struct get_ct_event {
+ uint32_t command;
+ uint32_t ev_reg_id;
+ uint32_t ev_req_id;
+};
+
+struct get_ct_event_reply {
+ uint32_t immed_data;
+ uint32_t type;
+};
+
+struct send_mgmt_resp {
+ uint32_t command;
+ uint32_t tag;
+};
+
+
+#define INTERNAL_LOOP_BACK 0x1 /* adapter short cuts the loop internally */
+#define EXTERNAL_LOOP_BACK 0x2 /* requires an external loopback plug */
+
+struct diag_mode_set {
+ uint32_t command;
+ uint32_t type;
+ uint32_t timeout;
+};
+
+struct diag_mode_test {
+ uint32_t command;
+};
+
+#define LPFC_WWNN_TYPE 0
+#define LPFC_WWPN_TYPE 1
+
+struct get_mgmt_rev {
+ uint32_t command;
+};
+
+#define MANAGEMENT_MAJOR_REV 1
+#define MANAGEMENT_MINOR_REV 0
+
+/* the MgmtRevInfo structure */
+struct MgmtRevInfo {
+ uint32_t a_Major;
+ uint32_t a_Minor;
+};
+
+struct get_mgmt_rev_reply {
+ struct MgmtRevInfo info;
+};
+
+struct dfc_mbox_req {
+ uint32_t command;
+ uint32_t inExtWLen;
+ uint32_t outExtWLen;
+ uint8_t mbOffset;
+};
+
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 650494d622c..6f0fb51eb46 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -44,18 +44,26 @@ int lpfc_reg_rpi(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *,
void lpfc_unreg_login(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
void lpfc_unreg_did(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
void lpfc_reg_vpi(struct lpfc_vport *, LPFC_MBOXQ_t *);
+void lpfc_register_new_vport(struct lpfc_hba *, struct lpfc_vport *,
+ struct lpfc_nodelist *);
void lpfc_unreg_vpi(struct lpfc_hba *, uint16_t, LPFC_MBOXQ_t *);
void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
+void lpfc_supported_pages(struct lpfcMboxq *);
+void lpfc_sli4_params(struct lpfcMboxq *);
+int lpfc_pc_sli4_params_get(struct lpfc_hba *, LPFC_MBOXQ_t *);
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);
+void lpfc_cleanup_pending_mbox(struct lpfc_vport *);
int lpfc_linkdown(struct lpfc_hba *);
void lpfc_linkdown_port(struct lpfc_vport *);
void lpfc_port_link_failure(struct lpfc_vport *);
void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_init_vpi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_retry_pport_discovery(struct lpfc_hba *);
void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *);
@@ -73,6 +81,7 @@ void lpfc_set_disctmo(struct lpfc_vport *);
int lpfc_can_disctmo(struct lpfc_vport *);
int lpfc_unreg_rpi(struct lpfc_vport *, struct lpfc_nodelist *);
void lpfc_unreg_all_rpis(struct lpfc_vport *);
+void lpfc_unreg_hba_rpis(struct lpfc_hba *);
void lpfc_unreg_default_rpis(struct lpfc_vport *);
void lpfc_issue_reg_vpi(struct lpfc_hba *, struct lpfc_vport *);
@@ -99,7 +108,7 @@ int lpfc_disc_state_machine(struct lpfc_vport *, struct lpfc_nodelist *, void *,
void lpfc_do_scr_ns_plogi(struct lpfc_hba *, struct lpfc_vport *);
int lpfc_check_sparm(struct lpfc_vport *, struct lpfc_nodelist *,
- struct serv_parm *, uint32_t);
+ struct serv_parm *, uint32_t, int);
int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist *);
void lpfc_more_plogi(struct lpfc_vport *);
void lpfc_more_adisc(struct lpfc_vport *);
@@ -197,6 +206,7 @@ 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 *);
int lpfc_check_pending_fcoe_event(struct lpfc_hba *, uint8_t);
+void lpfc_issue_init_vpi(struct lpfc_vport *);
void lpfc_config_hbq(struct lpfc_hba *, uint32_t, struct lpfc_hbq_init *,
uint32_t , LPFC_MBOXQ_t *);
@@ -206,7 +216,11 @@ struct hbq_dmabuf *lpfc_sli4_rb_alloc(struct lpfc_hba *);
void lpfc_sli4_rb_free(struct lpfc_hba *, struct hbq_dmabuf *);
void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
uint16_t);
+void lpfc_unregister_fcf(struct lpfc_hba *);
+void lpfc_unregister_fcf_rescan(struct lpfc_hba *);
void lpfc_unregister_unused_fcf(struct lpfc_hba *);
+int lpfc_sli4_redisc_fcf_table(struct lpfc_hba *);
+void lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *);
int lpfc_mem_alloc(struct lpfc_hba *, int align);
void lpfc_mem_free(struct lpfc_hba *);
@@ -365,6 +379,8 @@ void lpfc_free_fast_evt(struct lpfc_hba *, struct lpfc_fast_path_event *);
void lpfc_create_static_vport(struct lpfc_hba *);
void lpfc_stop_hba_timers(struct lpfc_hba *);
void lpfc_stop_port(struct lpfc_hba *);
+void __lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *);
+void lpfc_sli4_stop_fcf_redisc_wait_timer(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);
@@ -378,5 +394,5 @@ struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t);
/* functions to support SGIOv4/bsg interface */
int lpfc_bsg_request(struct fc_bsg_job *);
int lpfc_bsg_timeout(struct fc_bsg_job *);
-void lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+int lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 0ebcd9baca7..c7e921973f6 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -97,7 +97,8 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct list_head head;
struct lpfc_dmabuf *bdeBuf;
- lpfc_bsg_ct_unsol_event(phba, pring, piocbq);
+ if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0)
+ return;
if (unlikely(icmd->ulpStatus == IOSTAT_NEED_BUFFER)) {
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
@@ -181,7 +182,8 @@ lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba,
uint32_t size;
/* Forward abort event to any process registered to receive ct event */
- lpfc_bsg_ct_unsol_event(phba, pring, piocbq);
+ if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0)
+ return;
/* If there is no BDE associated with IOCB, there is nothing to do */
if (icmd->ulpBdeCount == 0)
@@ -1843,12 +1845,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
c = (rev & 0x0000ff00) >> 8;
b4 = (rev & 0x000000ff);
- if (flag)
- sprintf(fwrevision, "%d.%d%d%c%d ", b1,
- b2, b3, c, b4);
- else
- sprintf(fwrevision, "%d.%d%d%c%d ", b1,
- b2, b3, c, b4);
+ sprintf(fwrevision, "%d.%d%d%c%d", b1, b2, b3, c, b4);
}
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 2cc39684ce9..08b6634cb99 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -50,9 +50,6 @@ static int lpfc_issue_els_fdisc(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp, uint8_t retry);
static int lpfc_issue_fabric_iocb(struct lpfc_hba *phba,
struct lpfc_iocbq *iocb);
-static void lpfc_register_new_vport(struct lpfc_hba *phba,
- struct lpfc_vport *vport,
- struct lpfc_nodelist *ndlp);
static int lpfc_max_els_tries = 3;
@@ -592,6 +589,15 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
spin_unlock_irq(shost->host_lock);
}
+ /*
+ * If VPI is unreged, driver need to do INIT_VPI
+ * before re-registering
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ spin_unlock_irq(shost->host_lock);
+ }
}
if (phba->sli_rev < LPFC_SLI_REV4) {
@@ -604,10 +610,13 @@ 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->vpi_state & LPFC_VPI_REGISTERED) {
+ if ((!(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) &&
+ (vport->vpi_state & LPFC_VPI_REGISTERED)) {
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
- } else
+ } else if (vport->fc_flag & FC_VFI_REGISTERED)
+ lpfc_issue_init_vpi(vport);
+ else
lpfc_issue_reg_vfi(vport);
}
return 0;
@@ -804,6 +813,9 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpTimeout);
goto flogifail;
}
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_VPORT_CVL_RCVD;
+ spin_unlock_irq(shost->host_lock);
/*
* The FLogI succeeded. Sync the data for the CPU before
@@ -2720,7 +2732,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (did == FDMI_DID)
retry = 1;
- if ((cmd == ELS_CMD_FLOGI) &&
+ if (((cmd == ELS_CMD_FLOGI) || (cmd == ELS_CMD_FDISC)) &&
(phba->fc_topology != TOPOLOGY_LOOP) &&
!lpfc_error_lost_link(irsp)) {
/* FLOGI retry policy */
@@ -4385,7 +4397,7 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
did = Fabric_DID;
- if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3))) {
+ if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1))) {
/* For a FLOGI we accept, then if our portname is greater
* then the remote portname we initiate Nport login.
*/
@@ -5915,6 +5927,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
MAILBOX_t *mb = &pmb->u.mb;
+ int rc;
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
@@ -5936,6 +5949,26 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
break;
+ /* If reg_vpi fail with invalid VPI status, re-init VPI */
+ case 0x20:
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ spin_unlock_irq(shost->host_lock);
+ lpfc_init_vpi(phba, pmb, vport->vpi);
+ pmb->vport = vport;
+ pmb->mbox_cmpl = lpfc_init_vpi_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, pmb,
+ MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_vlog(vport,
+ KERN_ERR, LOG_MBOX,
+ "2732 Failed to issue INIT_VPI"
+ " mailbox command\n");
+ } else {
+ lpfc_nlp_put(ndlp);
+ return;
+ }
+
default:
/* Try to recover from this error */
lpfc_mbx_unreg_vpi(vport);
@@ -5949,13 +5982,17 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
break;
}
} else {
+ spin_lock_irq(shost->host_lock);
vport->vpi_state |= LPFC_VPI_REGISTERED;
- if (vport == phba->pport)
+ spin_unlock_irq(shost->host_lock);
+ if (vport == phba->pport) {
if (phba->sli_rev < LPFC_SLI_REV4)
lpfc_issue_fabric_reglogin(vport);
- else
- lpfc_issue_reg_vfi(vport);
- else
+ else {
+ lpfc_start_fdiscs(phba);
+ lpfc_do_scr_ns_plogi(phba, vport);
+ }
+ } else
lpfc_do_scr_ns_plogi(phba, vport);
}
@@ -5977,7 +6014,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
* This routine registers the @vport as a new virtual port with a HBA.
* It is done through a registering vpi mailbox command.
**/
-static void
+void
lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp)
{
@@ -6018,6 +6055,78 @@ mbox_err_exit:
}
/**
+ * lpfc_retry_pport_discovery - Start timer to retry FLOGI.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine abort all pending discovery commands and
+ * start a timer to retry FLOGI for the physical port
+ * discovery.
+ **/
+void
+lpfc_retry_pport_discovery(struct lpfc_hba *phba)
+{
+ struct lpfc_vport **vports;
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost;
+ int i;
+ uint32_t link_state;
+
+ /* Treat this failure as linkdown for all vports */
+ link_state = phba->link_state;
+ lpfc_linkdown(phba);
+ phba->link_state = link_state;
+
+ vports = lpfc_create_vport_work_array(phba);
+
+ if (vports) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ ndlp = lpfc_findnode_did(vports[i], Fabric_DID);
+ if (ndlp)
+ lpfc_cancel_retry_delay_tmo(vports[i], ndlp);
+ lpfc_els_flush_cmd(vports[i]);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+ }
+
+ /* If fabric require FLOGI, then re-instantiate physical login */
+ ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
+ if (!ndlp)
+ return;
+
+
+ shost = lpfc_shost_from_vport(phba->pport);
+ 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;
+ phba->pport->port_state = LPFC_FLOGI;
+ return;
+}
+
+/**
+ * lpfc_fabric_login_reqd - Check if FLOGI required.
+ * @phba: pointer to lpfc hba data structure.
+ * @cmdiocb: pointer to FDISC command iocb.
+ * @rspiocb: pointer to FDISC response iocb.
+ *
+ * This routine checks if a FLOGI is reguired for FDISC
+ * to succeed.
+ **/
+static int
+lpfc_fabric_login_reqd(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+
+ if ((rspiocb->iocb.ulpStatus != IOSTAT_FABRIC_RJT) ||
+ (rspiocb->iocb.un.ulpWord[4] != RJT_LOGIN_REQUIRED))
+ return 0;
+ else
+ return 1;
+}
+
+/**
* lpfc_cmpl_els_fdisc - Completion function for fdisc iocb command
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
@@ -6066,6 +6175,12 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_prevDID);
if (irsp->ulpStatus) {
+
+ if (lpfc_fabric_login_reqd(phba, cmdiocb, rspiocb)) {
+ lpfc_retry_pport_discovery(phba);
+ goto out;
+ }
+
/* Check for retry */
if (lpfc_els_retry(phba, cmdiocb, rspiocb))
goto out;
@@ -6076,6 +6191,7 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
goto fdisc_failed;
}
spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_VPORT_CVL_RCVD;
vport->fc_flag |= FC_FABRIC;
if (vport->phba->fc_topology == TOPOLOGY_LOOP)
vport->fc_flag |= FC_PUBLIC_LOOP;
@@ -6103,10 +6219,13 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_mbx_unreg_vpi(vport);
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
spin_unlock_irq(shost->host_lock);
}
- if (vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
+ if (vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI)
+ lpfc_issue_init_vpi(vport);
+ else if (vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
lpfc_register_new_vport(phba, vport, ndlp);
else
lpfc_do_scr_ns_plogi(phba, vport);
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 2445e399fd6..2359d0bfb73 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -525,6 +525,8 @@ lpfc_work_done(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
}
+ if (phba->fcf.fcf_flag & FCF_REDISC_EVT)
+ lpfc_sli4_fcf_redisc_event_proc(phba);
}
vports = lpfc_create_vport_work_array(phba);
@@ -706,6 +708,8 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
void
lpfc_port_link_failure(struct lpfc_vport *vport)
{
+ lpfc_vport_set_state(vport, FC_VPORT_LINKDOWN);
+
/* Cleanup any outstanding received buffers */
lpfc_cleanup_rcv_buffers(vport);
@@ -752,12 +756,14 @@ lpfc_linkdown(struct lpfc_hba *phba)
lpfc_scsi_dev_block(phba);
spin_lock_irq(&phba->hbalock);
- phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_DISCOVERED);
+ phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_SCAN_DONE);
+ spin_unlock_irq(&phba->hbalock);
if (phba->link_state > LPFC_LINK_DOWN) {
phba->link_state = LPFC_LINK_DOWN;
+ spin_lock_irq(shost->host_lock);
phba->pport->fc_flag &= ~FC_LBIT;
+ spin_unlock_irq(shost->host_lock);
}
- spin_unlock_irq(&phba->hbalock);
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
@@ -1023,7 +1029,7 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
return;
}
spin_lock_irqsave(&phba->hbalock, flags);
- phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ phba->fcf.fcf_flag |= (FCF_SCAN_DONE | FCF_IN_USE);
phba->hba_flag &= ~FCF_DISC_INPROGRESS;
spin_unlock_irqrestore(&phba->hbalock, flags);
if (vport->port_state != LPFC_FLOGI)
@@ -1045,25 +1051,23 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
static uint32_t
lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record)
{
- if ((fab_name[0] ==
- bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record)) &&
- (fab_name[1] ==
- bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record)) &&
- (fab_name[2] ==
- bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record)) &&
- (fab_name[3] ==
- bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record)) &&
- (fab_name[4] ==
- bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record)) &&
- (fab_name[5] ==
- bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record)) &&
- (fab_name[6] ==
- bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record)) &&
- (fab_name[7] ==
- bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record)))
- return 1;
- else
+ if (fab_name[0] != bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record))
+ return 0;
+ if (fab_name[1] != bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record))
+ return 0;
+ if (fab_name[2] != bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record))
return 0;
+ if (fab_name[3] != bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record))
+ return 0;
+ if (fab_name[4] != bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record))
+ return 0;
+ if (fab_name[5] != bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record))
+ return 0;
+ if (fab_name[6] != bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record))
+ return 0;
+ if (fab_name[7] != bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record))
+ return 0;
+ return 1;
}
/**
@@ -1078,30 +1082,28 @@ lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record)
static uint32_t
lpfc_sw_name_match(uint8_t *sw_name, struct fcf_record *new_fcf_record)
{
- if ((sw_name[0] ==
- bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record)) &&
- (sw_name[1] ==
- bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record)) &&
- (sw_name[2] ==
- bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record)) &&
- (sw_name[3] ==
- bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record)) &&
- (sw_name[4] ==
- bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record)) &&
- (sw_name[5] ==
- bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record)) &&
- (sw_name[6] ==
- bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record)) &&
- (sw_name[7] ==
- bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record)))
- return 1;
- else
+ if (sw_name[0] != bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record))
return 0;
+ if (sw_name[1] != bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record))
+ return 0;
+ if (sw_name[2] != bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record))
+ return 0;
+ if (sw_name[3] != bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record))
+ return 0;
+ if (sw_name[4] != bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record))
+ return 0;
+ if (sw_name[5] != bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record))
+ return 0;
+ if (sw_name[6] != bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record))
+ return 0;
+ if (sw_name[7] != bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record))
+ return 0;
+ return 1;
}
/**
* lpfc_mac_addr_match - Check if the fcf mac address match.
- * @phba: pointer to lpfc hba data structure.
+ * @mac_addr: pointer to mac address.
* @new_fcf_record: pointer to fcf record.
*
* This routine compare the fcf record's mac address with HBA's
@@ -1109,85 +1111,115 @@ lpfc_sw_name_match(uint8_t *sw_name, struct fcf_record *new_fcf_record)
* returns 1 else return 0.
**/
static uint32_t
-lpfc_mac_addr_match(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
+lpfc_mac_addr_match(uint8_t *mac_addr, struct fcf_record *new_fcf_record)
{
- if ((phba->fcf.mac_addr[0] ==
- bf_get(lpfc_fcf_record_mac_0, new_fcf_record)) &&
- (phba->fcf.mac_addr[1] ==
- bf_get(lpfc_fcf_record_mac_1, new_fcf_record)) &&
- (phba->fcf.mac_addr[2] ==
- bf_get(lpfc_fcf_record_mac_2, new_fcf_record)) &&
- (phba->fcf.mac_addr[3] ==
- bf_get(lpfc_fcf_record_mac_3, new_fcf_record)) &&
- (phba->fcf.mac_addr[4] ==
- bf_get(lpfc_fcf_record_mac_4, new_fcf_record)) &&
- (phba->fcf.mac_addr[5] ==
- bf_get(lpfc_fcf_record_mac_5, new_fcf_record)))
- return 1;
- else
+ if (mac_addr[0] != bf_get(lpfc_fcf_record_mac_0, new_fcf_record))
+ return 0;
+ if (mac_addr[1] != bf_get(lpfc_fcf_record_mac_1, new_fcf_record))
+ return 0;
+ if (mac_addr[2] != bf_get(lpfc_fcf_record_mac_2, new_fcf_record))
return 0;
+ if (mac_addr[3] != bf_get(lpfc_fcf_record_mac_3, new_fcf_record))
+ return 0;
+ if (mac_addr[4] != bf_get(lpfc_fcf_record_mac_4, new_fcf_record))
+ return 0;
+ if (mac_addr[5] != bf_get(lpfc_fcf_record_mac_5, new_fcf_record))
+ return 0;
+ return 1;
+}
+
+static bool
+lpfc_vlan_id_match(uint16_t curr_vlan_id, uint16_t new_vlan_id)
+{
+ return (curr_vlan_id == new_vlan_id);
}
/**
* lpfc_copy_fcf_record - Copy fcf information to lpfc_hba.
- * @phba: pointer to lpfc hba data structure.
+ * @fcf: pointer to driver fcf record.
* @new_fcf_record: pointer to fcf record.
*
* This routine copies the FCF information from the FCF
* record to lpfc_hba data structure.
**/
static void
-lpfc_copy_fcf_record(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
+lpfc_copy_fcf_record(struct lpfc_fcf_rec *fcf_rec,
+ struct fcf_record *new_fcf_record)
{
- phba->fcf.fabric_name[0] =
+ /* Fabric name */
+ fcf_rec->fabric_name[0] =
bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record);
- phba->fcf.fabric_name[1] =
+ fcf_rec->fabric_name[1] =
bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record);
- phba->fcf.fabric_name[2] =
+ fcf_rec->fabric_name[2] =
bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record);
- phba->fcf.fabric_name[3] =
+ fcf_rec->fabric_name[3] =
bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record);
- phba->fcf.fabric_name[4] =
+ fcf_rec->fabric_name[4] =
bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record);
- phba->fcf.fabric_name[5] =
+ fcf_rec->fabric_name[5] =
bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record);
- phba->fcf.fabric_name[6] =
+ fcf_rec->fabric_name[6] =
bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record);
- phba->fcf.fabric_name[7] =
+ fcf_rec->fabric_name[7] =
bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record);
- phba->fcf.mac_addr[0] =
- bf_get(lpfc_fcf_record_mac_0, new_fcf_record);
- phba->fcf.mac_addr[1] =
- bf_get(lpfc_fcf_record_mac_1, new_fcf_record);
- phba->fcf.mac_addr[2] =
- bf_get(lpfc_fcf_record_mac_2, new_fcf_record);
- phba->fcf.mac_addr[3] =
- bf_get(lpfc_fcf_record_mac_3, new_fcf_record);
- phba->fcf.mac_addr[4] =
- bf_get(lpfc_fcf_record_mac_4, new_fcf_record);
- phba->fcf.mac_addr[5] =
- bf_get(lpfc_fcf_record_mac_5, new_fcf_record);
- phba->fcf.fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
- phba->fcf.priority = new_fcf_record->fip_priority;
- phba->fcf.switch_name[0] =
+ /* Mac address */
+ fcf_rec->mac_addr[0] = bf_get(lpfc_fcf_record_mac_0, new_fcf_record);
+ fcf_rec->mac_addr[1] = bf_get(lpfc_fcf_record_mac_1, new_fcf_record);
+ fcf_rec->mac_addr[2] = bf_get(lpfc_fcf_record_mac_2, new_fcf_record);
+ fcf_rec->mac_addr[3] = bf_get(lpfc_fcf_record_mac_3, new_fcf_record);
+ fcf_rec->mac_addr[4] = bf_get(lpfc_fcf_record_mac_4, new_fcf_record);
+ fcf_rec->mac_addr[5] = bf_get(lpfc_fcf_record_mac_5, new_fcf_record);
+ /* FCF record index */
+ fcf_rec->fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
+ /* FCF record priority */
+ fcf_rec->priority = new_fcf_record->fip_priority;
+ /* Switch name */
+ fcf_rec->switch_name[0] =
bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record);
- phba->fcf.switch_name[1] =
+ fcf_rec->switch_name[1] =
bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record);
- phba->fcf.switch_name[2] =
+ fcf_rec->switch_name[2] =
bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record);
- phba->fcf.switch_name[3] =
+ fcf_rec->switch_name[3] =
bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record);
- phba->fcf.switch_name[4] =
+ fcf_rec->switch_name[4] =
bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record);
- phba->fcf.switch_name[5] =
+ fcf_rec->switch_name[5] =
bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record);
- phba->fcf.switch_name[6] =
+ fcf_rec->switch_name[6] =
bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record);
- phba->fcf.switch_name[7] =
+ fcf_rec->switch_name[7] =
bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record);
}
/**
+ * lpfc_update_fcf_record - Update driver fcf record
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_rec: pointer to driver fcf record.
+ * @new_fcf_record: pointer to hba fcf record.
+ * @addr_mode: address mode to be set to the driver fcf record.
+ * @vlan_id: vlan tag to be set to the driver fcf record.
+ * @flag: flag bits to be set to the driver fcf record.
+ *
+ * This routine updates the driver FCF record from the new HBA FCF record
+ * together with the address mode, vlan_id, and other informations. This
+ * routine is called with the host lock held.
+ **/
+static void
+__lpfc_update_fcf_record(struct lpfc_hba *phba, struct lpfc_fcf_rec *fcf_rec,
+ struct fcf_record *new_fcf_record, uint32_t addr_mode,
+ uint16_t vlan_id, uint32_t flag)
+{
+ /* Copy the fields from the HBA's FCF record */
+ lpfc_copy_fcf_record(fcf_rec, new_fcf_record);
+ /* Update other fields of driver FCF record */
+ fcf_rec->addr_mode = addr_mode;
+ fcf_rec->vlan_id = vlan_id;
+ fcf_rec->flag |= (flag | RECORD_VALID);
+}
+
+/**
* lpfc_register_fcf - Register the FCF with hba.
* @phba: pointer to lpfc hba data structure.
*
@@ -1212,7 +1244,7 @@ lpfc_register_fcf(struct lpfc_hba *phba)
/* The FCF is already registered, start discovery */
if (phba->fcf.fcf_flag & FCF_REGISTERED) {
- phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ phba->fcf.fcf_flag |= (FCF_SCAN_DONE | FCF_IN_USE);
phba->hba_flag &= ~FCF_DISC_INPROGRESS;
spin_unlock_irqrestore(&phba->hbalock, flags);
if (phba->pport->port_state != LPFC_FLOGI)
@@ -1250,6 +1282,7 @@ lpfc_register_fcf(struct lpfc_hba *phba)
* @new_fcf_record: pointer to fcf record.
* @boot_flag: Indicates if this record used by boot bios.
* @addr_mode: The address mode to be used by this FCF
+ * @vlan_id: The vlan id to be used as vlan tagging by this FCF.
*
* This routine compare the fcf record with connect list obtained from the
* config region to decide if this FCF can be used for SAN discovery. It returns
@@ -1323,7 +1356,8 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
return 1;
}
- list_for_each_entry(conn_entry, &phba->fcf_conn_rec_list, list) {
+ list_for_each_entry(conn_entry,
+ &phba->fcf_conn_rec_list, list) {
if (!(conn_entry->conn_rec.flags & FCFCNCT_VALID))
continue;
@@ -1470,6 +1504,7 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
*/
spin_lock_irq(&phba->hbalock);
phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
spin_unlock_irq(&phba->hbalock);
}
@@ -1524,11 +1559,12 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
struct fcf_record *new_fcf_record;
- int rc;
uint32_t boot_flag, addr_mode;
uint32_t next_fcf_index;
- unsigned long flags;
+ struct lpfc_fcf_rec *fcf_rec = NULL;
+ unsigned long iflags;
uint16_t vlan_id;
+ int rc;
/* If there is pending FCoE event restart FCF table scan */
if (lpfc_check_pending_fcoe_event(phba, 0)) {
@@ -1583,9 +1619,8 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
sizeof(struct fcf_record));
bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
- rc = lpfc_match_fcf_conn_list(phba, new_fcf_record,
- &boot_flag, &addr_mode,
- &vlan_id);
+ rc = lpfc_match_fcf_conn_list(phba, new_fcf_record, &boot_flag,
+ &addr_mode, &vlan_id);
/*
* If the fcf record does not match with connect list entries
* read the next entry.
@@ -1594,90 +1629,159 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
goto read_next_fcf;
/*
* If this is not the first FCF discovery of the HBA, use last
- * FCF record for the discovery.
+ * FCF record for the discovery. The condition that a rescan
+ * matches the in-use FCF record: fabric name, switch name, mac
+ * address, and vlan_id.
*/
- spin_lock_irqsave(&phba->hbalock, flags);
+ spin_lock_irqsave(&phba->hbalock, iflags);
if (phba->fcf.fcf_flag & FCF_IN_USE) {
- if (lpfc_fab_name_match(phba->fcf.fabric_name,
+ if (lpfc_fab_name_match(phba->fcf.current_rec.fabric_name,
new_fcf_record) &&
- lpfc_sw_name_match(phba->fcf.switch_name,
+ lpfc_sw_name_match(phba->fcf.current_rec.switch_name,
new_fcf_record) &&
- lpfc_mac_addr_match(phba, new_fcf_record)) {
+ lpfc_mac_addr_match(phba->fcf.current_rec.mac_addr,
+ new_fcf_record) &&
+ lpfc_vlan_id_match(phba->fcf.current_rec.vlan_id,
+ vlan_id)) {
phba->fcf.fcf_flag |= FCF_AVAILABLE;
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (phba->fcf.fcf_flag & FCF_REDISC_PEND)
+ /* Stop FCF redisc wait timer if pending */
+ __lpfc_sli4_stop_fcf_redisc_wait_timer(phba);
+ else if (phba->fcf.fcf_flag & FCF_REDISC_FOV)
+ /* If in fast failover, mark it's completed */
+ phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
goto out;
}
- spin_unlock_irqrestore(&phba->hbalock, flags);
- goto read_next_fcf;
+ /*
+ * Read next FCF record from HBA searching for the matching
+ * with in-use record only if not during the fast failover
+ * period. In case of fast failover period, it shall try to
+ * determine whether the FCF record just read should be the
+ * next candidate.
+ */
+ if (!(phba->fcf.fcf_flag & FCF_REDISC_FOV)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ goto read_next_fcf;
+ }
}
+ /*
+ * Update on failover FCF record only if it's in FCF fast-failover
+ * period; otherwise, update on current FCF record.
+ */
+ if (phba->fcf.fcf_flag & FCF_REDISC_FOV) {
+ /* Fast FCF failover only to the same fabric name */
+ if (lpfc_fab_name_match(phba->fcf.current_rec.fabric_name,
+ new_fcf_record))
+ fcf_rec = &phba->fcf.failover_rec;
+ else
+ goto read_next_fcf;
+ } else
+ fcf_rec = &phba->fcf.current_rec;
+
if (phba->fcf.fcf_flag & FCF_AVAILABLE) {
/*
- * If the current FCF record does not have boot flag
- * set and new fcf record has boot flag set, use the
- * new fcf record.
+ * If the driver FCF record does not have boot flag
+ * set and new hba fcf record has boot flag set, use
+ * the new hba fcf record.
*/
- if (boot_flag && !(phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) {
- /* Use this FCF record */
- lpfc_copy_fcf_record(phba, new_fcf_record);
- phba->fcf.addr_mode = addr_mode;
- phba->fcf.fcf_flag |= FCF_BOOT_ENABLE;
- if (vlan_id != 0xFFFF) {
- phba->fcf.fcf_flag |= FCF_VALID_VLAN;
- phba->fcf.vlan_id = vlan_id;
- }
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (boot_flag && !(fcf_rec->flag & BOOT_ENABLE)) {
+ /* Choose this FCF record */
+ __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record,
+ addr_mode, vlan_id, BOOT_ENABLE);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
goto read_next_fcf;
}
/*
- * If the current FCF record has boot flag set and the
- * new FCF record does not have boot flag, read the next
- * FCF record.
+ * If the driver FCF record has boot flag set and the
+ * new hba FCF record does not have boot flag, read
+ * the next FCF record.
*/
- if (!boot_flag && (phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (!boot_flag && (fcf_rec->flag & BOOT_ENABLE)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
goto read_next_fcf;
}
/*
- * If there is a record with lower priority value for
- * the current FCF, use that record.
+ * If the new hba FCF record has lower priority value
+ * than the driver FCF record, use the new record.
*/
- if (lpfc_fab_name_match(phba->fcf.fabric_name,
- new_fcf_record) &&
- (new_fcf_record->fip_priority < phba->fcf.priority)) {
- /* Use this FCF record */
- lpfc_copy_fcf_record(phba, new_fcf_record);
- phba->fcf.addr_mode = addr_mode;
- if (vlan_id != 0xFFFF) {
- phba->fcf.fcf_flag |= FCF_VALID_VLAN;
- phba->fcf.vlan_id = vlan_id;
- }
- spin_unlock_irqrestore(&phba->hbalock, flags);
- goto read_next_fcf;
+ if (lpfc_fab_name_match(fcf_rec->fabric_name, new_fcf_record) &&
+ (new_fcf_record->fip_priority < fcf_rec->priority)) {
+ /* Choose this FCF record */
+ __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record,
+ addr_mode, vlan_id, 0);
}
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
goto read_next_fcf;
}
/*
- * This is the first available FCF record, use this
- * record.
+ * This is the first suitable FCF record, choose this record for
+ * initial best-fit FCF.
*/
- lpfc_copy_fcf_record(phba, new_fcf_record);
- phba->fcf.addr_mode = addr_mode;
- if (boot_flag)
- phba->fcf.fcf_flag |= FCF_BOOT_ENABLE;
- phba->fcf.fcf_flag |= FCF_AVAILABLE;
- if (vlan_id != 0xFFFF) {
- phba->fcf.fcf_flag |= FCF_VALID_VLAN;
- phba->fcf.vlan_id = vlan_id;
+ if (fcf_rec) {
+ __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record,
+ addr_mode, vlan_id, (boot_flag ?
+ BOOT_ENABLE : 0));
+ phba->fcf.fcf_flag |= FCF_AVAILABLE;
}
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
goto read_next_fcf;
read_next_fcf:
lpfc_sli4_mbox_cmd_free(phba, mboxq);
- if (next_fcf_index == LPFC_FCOE_FCF_NEXT_NONE || next_fcf_index == 0)
- lpfc_register_fcf(phba);
- else
+ if (next_fcf_index == LPFC_FCOE_FCF_NEXT_NONE || next_fcf_index == 0) {
+ if (phba->fcf.fcf_flag & FCF_REDISC_FOV) {
+ /*
+ * Case of FCF fast failover scan
+ */
+
+ /*
+ * It has not found any suitable FCF record, cancel
+ * FCF scan inprogress, and do nothing
+ */
+ if (!(phba->fcf.failover_rec.flag & RECORD_VALID)) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return;
+ }
+ /*
+ * It has found a suitable FCF record that is not
+ * the same as in-use FCF record, unregister the
+ * in-use FCF record, replace the in-use FCF record
+ * with the new FCF record, mark FCF fast failover
+ * completed, and then start register the new FCF
+ * record.
+ */
+
+ /* unregister the current in-use FCF record */
+ lpfc_unregister_fcf(phba);
+ /* replace in-use record with the new record */
+ memcpy(&phba->fcf.current_rec,
+ &phba->fcf.failover_rec,
+ sizeof(struct lpfc_fcf_rec));
+ /* mark the FCF fast failover completed */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ /* Register to the new FCF record */
+ lpfc_register_fcf(phba);
+ } else {
+ /*
+ * In case of transaction period to fast FCF failover,
+ * do nothing when search to the end of the FCF table.
+ */
+ if ((phba->fcf.fcf_flag & FCF_REDISC_EVT) ||
+ (phba->fcf.fcf_flag & FCF_REDISC_PEND))
+ return;
+ /*
+ * Otherwise, initial scan or post linkdown rescan,
+ * register with the best fit FCF record found so
+ * far through the scanning process.
+ */
+ lpfc_register_fcf(phba);
+ }
+ } else
lpfc_sli4_read_fcf_record(phba, next_fcf_index);
return;
@@ -1695,10 +1799,13 @@ out:
*
* This function handles completion of init vpi mailbox command.
*/
-static void
+void
lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_vport *vport = mboxq->vport;
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+
if (mboxq->u.mb.mbxStatus) {
lpfc_printf_vlog(vport, KERN_ERR,
LOG_MBOX,
@@ -1708,9 +1815,23 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
return;
}
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_VPORT_NEEDS_INIT_VPI;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(shost->host_lock);
+
+ /* If this port is physical port or FDISC is done, do reg_vpi */
+ if ((phba->pport == vport) || (vport->port_state == LPFC_FDISC)) {
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp)
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_DISCOVERY,
+ "2731 Cannot find fabric "
+ "controller node\n");
+ else
+ lpfc_register_new_vport(phba, vport, ndlp);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return;
+ }
if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
lpfc_initial_fdisc(vport);
@@ -1719,10 +1840,42 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"2606 No NPIV Fabric support\n");
}
+ mempool_free(mboxq, phba->mbox_mem_pool);
return;
}
/**
+ * lpfc_issue_init_vpi - Issue init_vpi mailbox command.
+ * @vport: pointer to lpfc_vport data structure.
+ *
+ * This function issue a init_vpi mailbox command to initialize
+ * VPI for the vport.
+ */
+void
+lpfc_issue_init_vpi(struct lpfc_vport *vport)
+{
+ LPFC_MBOXQ_t *mboxq;
+ int rc;
+
+ mboxq = mempool_alloc(vport->phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_MBOX, "2607 Failed to allocate "
+ "init_vpi mailbox\n");
+ return;
+ }
+ lpfc_init_vpi(vport->phba, mboxq, vport->vpi);
+ mboxq->vport = vport;
+ mboxq->mbox_cmpl = lpfc_init_vpi_cmpl;
+ rc = lpfc_sli_issue_mbox(vport->phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_MBOX, "2608 Failed to issue init_vpi mailbox\n");
+ mempool_free(mboxq, vport->phba->mbox_mem_pool);
+ }
+}
+
+/**
* lpfc_start_fdiscs - send fdiscs for each vports on this port.
* @phba: pointer to lpfc hba data structure.
*
@@ -1734,8 +1887,6 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
int i;
- LPFC_MBOXQ_t *mboxq;
- int rc;
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL) {
@@ -1754,26 +1905,7 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
continue;
}
if (vports[i]->fc_flag & FC_VPORT_NEEDS_INIT_VPI) {
- mboxq = mempool_alloc(phba->mbox_mem_pool,
- GFP_KERNEL);
- if (!mboxq) {
- lpfc_printf_vlog(vports[i], KERN_ERR,
- LOG_MBOX, "2607 Failed to allocate "
- "init_vpi mailbox\n");
- continue;
- }
- lpfc_init_vpi(phba, mboxq, vports[i]->vpi);
- mboxq->vport = vports[i];
- mboxq->mbox_cmpl = lpfc_init_vpi_cmpl;
- rc = lpfc_sli_issue_mbox(phba, mboxq,
- MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_vlog(vports[i], KERN_ERR,
- LOG_MBOX, "2608 Failed to issue "
- "init_vpi mailbox\n");
- mempool_free(mboxq,
- phba->mbox_mem_pool);
- }
+ lpfc_issue_init_vpi(vports[i]);
continue;
}
if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
@@ -1796,6 +1928,7 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_dmabuf *dmabuf = mboxq->context1;
struct lpfc_vport *vport = mboxq->vport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if (mboxq->u.mb.mbxStatus) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
@@ -1813,7 +1946,11 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
goto fail_free_mem;
}
/* The VPI is implicitly registered when the VFI is registered */
+ spin_lock_irq(shost->host_lock);
vport->vpi_state |= LPFC_VPI_REGISTERED;
+ vport->fc_flag |= FC_VFI_REGISTERED;
+ vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
+ spin_unlock_irq(shost->host_lock);
if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
lpfc_start_fdiscs(phba);
@@ -2050,8 +2187,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
return;
}
spin_unlock_irq(&phba->hbalock);
- 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)
goto out;
}
@@ -2139,10 +2275,12 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
phba->fc_eventTag = la->eventTag;
+ spin_lock_irq(&phba->hbalock);
if (la->mm)
phba->sli.sli_flag |= LPFC_MENLO_MAINT;
else
phba->sli.sli_flag &= ~LPFC_MENLO_MAINT;
+ spin_unlock_irq(&phba->hbalock);
phba->link_events++;
if (la->attType == AT_LINK_UP && (!la->mm)) {
@@ -2271,10 +2409,10 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->mbxStatus);
break;
}
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(shost->host_lock);
vport->vpi_state &= ~LPFC_VPI_REGISTERED;
vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(shost->host_lock);
vport->unreg_vpi_cmpl = VPORT_OK;
mempool_free(pmb, phba->mbox_mem_pool);
/*
@@ -2332,7 +2470,10 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
goto out;
}
+ spin_lock_irq(shost->host_lock);
vport->vpi_state |= LPFC_VPI_REGISTERED;
+ vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
+ spin_unlock_irq(shost->host_lock);
vport->num_disc_nodes = 0;
/* go thru NPR list and issue ELS PLOGIs */
if (vport->fc_npr_cnt)
@@ -3218,6 +3359,34 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
return 0;
}
+/**
+ * lpfc_unreg_hba_rpis - Unregister rpis registered to the hba.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unregister all the currently registered RPIs
+ * to the HBA.
+ **/
+void
+lpfc_unreg_hba_rpis(struct lpfc_hba *phba)
+{
+ struct lpfc_vport **vports;
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost;
+ int i;
+
+ vports = lpfc_create_vport_work_array(phba);
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ spin_lock_irq(shost->host_lock);
+ list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) {
+ if (ndlp->nlp_flag & NLP_RPI_VALID)
+ lpfc_unreg_rpi(vports[i], ndlp);
+ }
+ spin_unlock_irq(shost->host_lock);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
void
lpfc_unreg_all_rpis(struct lpfc_vport *vport)
{
@@ -4448,63 +4617,56 @@ lpfc_unregister_fcfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
}
/**
- * lpfc_unregister_unused_fcf - Unregister FCF if all devices are disconnected.
+ * lpfc_unregister_fcf_prep - Unregister fcf record preparation
* @phba: Pointer to hba context object.
*
- * This function check if there are any connected remote port for the FCF and
- * if all the devices are disconnected, this function unregister FCFI.
- * This function also tries to use another FCF for discovery.
+ * This function prepare the HBA for unregistering the currently registered
+ * FCF from the HBA. It performs unregistering, in order, RPIs, VPIs, and
+ * VFIs.
*/
-void
-lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
+int
+lpfc_unregister_fcf_prep(struct lpfc_hba *phba)
{
LPFC_MBOXQ_t *mbox;
- int rc;
struct lpfc_vport **vports;
- int i;
-
- spin_lock_irq(&phba->hbalock);
- /*
- * If HBA is not running in FIP mode or
- * If HBA does not support FCoE or
- * If FCF is not registered.
- * do nothing.
- */
- if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
- !(phba->fcf.fcf_flag & FCF_REGISTERED) ||
- (!(phba->hba_flag & HBA_FIP_SUPPORT))) {
- spin_unlock_irq(&phba->hbalock);
- return;
- }
- spin_unlock_irq(&phba->hbalock);
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost;
+ int i, rc;
+ /* Unregister RPIs */
if (lpfc_fcf_inuse(phba))
- return;
+ lpfc_unreg_hba_rpis(phba);
/* At this point, all discovery is aborted */
phba->pport->port_state = LPFC_VPORT_UNKNOWN;
/* Unregister VPIs */
vports = lpfc_create_vport_work_array(phba);
- if (vports &&
- (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))
+ if (vports && (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ /* Stop FLOGI/FDISC retries */
+ ndlp = lpfc_findnode_did(vports[i], Fabric_DID);
+ if (ndlp)
+ lpfc_cancel_retry_delay_tmo(vports[i], ndlp);
lpfc_mbx_unreg_vpi(vports[i]);
- spin_lock_irq(&phba->hbalock);
+ shost = lpfc_shost_from_vport(vports[i]);
+ spin_lock_irq(shost->host_lock);
vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(shost->host_lock);
}
lpfc_destroy_vport_work_array(phba, vports);
+ /* Cleanup any outstanding ELS commands */
+ lpfc_els_flush_all_cmd(phba);
+
/* Unregister VFI */
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2556 UNREG_VFI mbox allocation failed"
- "HBA state x%x\n",
- phba->pport->port_state);
- return;
+ "2556 UNREG_VFI mbox allocation failed"
+ "HBA state x%x\n", phba->pport->port_state);
+ return -ENOMEM;
}
lpfc_unreg_vfi(mbox, phba->pport);
@@ -4514,58 +4676,163 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2557 UNREG_VFI issue mbox failed rc x%x "
- "HBA state x%x\n",
- rc, phba->pport->port_state);
+ "2557 UNREG_VFI issue mbox failed rc x%x "
+ "HBA state x%x\n",
+ rc, phba->pport->port_state);
mempool_free(mbox, phba->mbox_mem_pool);
- return;
+ return -EIO;
}
- /* Unregister FCF */
+ shost = lpfc_shost_from_vport(phba->pport);
+ spin_lock_irq(shost->host_lock);
+ phba->pport->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+
+ return 0;
+}
+
+/**
+ * lpfc_sli4_unregister_fcf - Unregister currently registered FCF record
+ * @phba: Pointer to hba context object.
+ *
+ * This function issues synchronous unregister FCF mailbox command to HBA to
+ * unregister the currently registered FCF record. The driver does not reset
+ * the driver FCF usage state flags.
+ *
+ * Return 0 if successfully issued, none-zero otherwise.
+ */
+int
+lpfc_sli4_unregister_fcf(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2551 UNREG_FCFI mbox allocation failed"
- "HBA state x%x\n",
- phba->pport->port_state);
- return;
+ "2551 UNREG_FCFI mbox allocation failed"
+ "HBA state x%x\n", phba->pport->port_state);
+ return -ENOMEM;
}
-
lpfc_unreg_fcfi(mbox, phba->fcf.fcfi);
mbox->vport = phba->pport;
mbox->mbox_cmpl = lpfc_unregister_fcfi_cmpl;
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2552 UNREG_FCFI issue mbox failed rc x%x "
- "HBA state x%x\n",
- rc, phba->pport->port_state);
- mempool_free(mbox, phba->mbox_mem_pool);
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2552 Unregister FCFI command failed rc x%x "
+ "HBA state x%x\n",
+ rc, phba->pport->port_state);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * lpfc_unregister_fcf_rescan - Unregister currently registered fcf and rescan
+ * @phba: Pointer to hba context object.
+ *
+ * This function unregisters the currently reigstered FCF. This function
+ * also tries to find another FCF for discovery by rescan the HBA FCF table.
+ */
+void
+lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
+{
+ int rc;
+
+ /* Preparation for unregistering fcf */
+ rc = lpfc_unregister_fcf_prep(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2748 Failed to prepare for unregistering "
+ "HBA's FCF record: rc=%d\n", rc);
return;
}
- spin_lock_irq(&phba->hbalock);
- phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_REGISTERED |
- FCF_DISCOVERED | FCF_BOOT_ENABLE | FCF_IN_USE |
- FCF_VALID_VLAN);
- spin_unlock_irq(&phba->hbalock);
+ /* Now, unregister FCF record and reset HBA FCF state */
+ rc = lpfc_sli4_unregister_fcf(phba);
+ if (rc)
+ return;
+ /* Reset HBA FCF states after successful unregister FCF */
+ phba->fcf.fcf_flag = 0;
/*
* If driver is not unloading, check if there is any other
* FCF record that can be used for discovery.
*/
if ((phba->pport->load_flag & FC_UNLOADING) ||
- (phba->link_state < LPFC_LINK_UP))
+ (phba->link_state < LPFC_LINK_UP))
return;
rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
if (rc)
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2553 lpfc_unregister_unused_fcf failed to read FCF"
- " record HBA state x%x\n",
- phba->pport->port_state);
+ "2553 lpfc_unregister_unused_fcf failed "
+ "to read FCF record HBA state x%x\n",
+ phba->pport->port_state);
+}
+
+/**
+ * lpfc_unregister_fcf - Unregister the currently registered fcf record
+ * @phba: Pointer to hba context object.
+ *
+ * This function just unregisters the currently reigstered FCF. It does not
+ * try to find another FCF for discovery.
+ */
+void
+lpfc_unregister_fcf(struct lpfc_hba *phba)
+{
+ int rc;
+
+ /* Preparation for unregistering fcf */
+ rc = lpfc_unregister_fcf_prep(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2749 Failed to prepare for unregistering "
+ "HBA's FCF record: rc=%d\n", rc);
+ return;
+ }
+
+ /* Now, unregister FCF record and reset HBA FCF state */
+ rc = lpfc_sli4_unregister_fcf(phba);
+ if (rc)
+ return;
+ /* Set proper HBA FCF states after successful unregister FCF */
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~FCF_REGISTERED;
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_unregister_unused_fcf - Unregister FCF if all devices are disconnected.
+ * @phba: Pointer to hba context object.
+ *
+ * This function check if there are any connected remote port for the FCF and
+ * if all the devices are disconnected, this function unregister FCFI.
+ * This function also tries to use another FCF for discovery.
+ */
+void
+lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
+{
+ /*
+ * If HBA is not running in FIP mode or if HBA does not support
+ * FCoE or if FCF is not registered, do nothing.
+ */
+ spin_lock_irq(&phba->hbalock);
+ if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
+ !(phba->fcf.fcf_flag & FCF_REGISTERED) ||
+ !(phba->hba_flag & HBA_FIP_SUPPORT)) {
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ if (lpfc_fcf_inuse(phba))
+ return;
+
+ lpfc_unregister_fcf_rescan(phba);
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index c9faa1d8c3c..89ff7c09e29 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -1346,6 +1346,9 @@ typedef struct { /* FireFly BIU registers */
#define MBX_HEARTBEAT 0x31
#define MBX_WRITE_VPARMS 0x32
#define MBX_ASYNCEVT_ENABLE 0x33
+#define MBX_READ_EVENT_LOG_STATUS 0x37
+#define MBX_READ_EVENT_LOG 0x38
+#define MBX_WRITE_EVENT_LOG 0x39
#define MBX_PORT_CAPABILITIES 0x3B
#define MBX_PORT_IOV_CONTROL 0x3C
@@ -1465,17 +1468,13 @@ typedef struct { /* FireFly BIU registers */
#define CMD_IOCB_LOGENTRY_CN 0x94
#define CMD_IOCB_LOGENTRY_ASYNC_CN 0x96
-/* Unhandled Data Security SLI Commands */
-#define DSSCMD_IWRITE64_CR 0xD8
-#define DSSCMD_IWRITE64_CX 0xD9
-#define DSSCMD_IREAD64_CR 0xDA
-#define DSSCMD_IREAD64_CX 0xDB
-#define DSSCMD_INVALIDATE_DEK 0xDC
-#define DSSCMD_SET_KEK 0xDD
-#define DSSCMD_GET_KEK_ID 0xDE
-#define DSSCMD_GEN_XFER 0xDF
-
-#define CMD_MAX_IOCB_CMD 0xE6
+/* Data Security SLI Commands */
+#define DSSCMD_IWRITE64_CR 0xF8
+#define DSSCMD_IWRITE64_CX 0xF9
+#define DSSCMD_IREAD64_CR 0xFA
+#define DSSCMD_IREAD64_CX 0xFB
+
+#define CMD_MAX_IOCB_CMD 0xFB
#define CMD_IOCB_MASK 0xff
#define MAX_MSG_DATA 28 /* max msg data in CMD_ADAPTER_MSG
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 8a2a1c5935c..820015fbc4d 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -52,35 +52,37 @@ struct dma_address {
uint32_t addr_hi;
};
-#define LPFC_SLIREV_CONF_WORD 0x58
struct lpfc_sli_intf {
uint32_t word0;
-#define lpfc_sli_intf_iftype_MASK 0x00000007
-#define lpfc_sli_intf_iftype_SHIFT 0
-#define lpfc_sli_intf_iftype_WORD word0
-#define lpfc_sli_intf_rev_MASK 0x0000000f
-#define lpfc_sli_intf_rev_SHIFT 4
-#define lpfc_sli_intf_rev_WORD word0
-#define LPFC_SLIREV_CONF_SLI4 4
-#define lpfc_sli_intf_family_MASK 0x000000ff
-#define lpfc_sli_intf_family_SHIFT 8
-#define lpfc_sli_intf_family_WORD word0
-#define lpfc_sli_intf_feat1_MASK 0x000000ff
-#define lpfc_sli_intf_feat1_SHIFT 16
-#define lpfc_sli_intf_feat1_WORD word0
-#define lpfc_sli_intf_feat2_MASK 0x0000001f
-#define lpfc_sli_intf_feat2_SHIFT 24
-#define lpfc_sli_intf_feat2_WORD word0
-#define lpfc_sli_intf_valid_MASK 0x00000007
-#define lpfc_sli_intf_valid_SHIFT 29
-#define lpfc_sli_intf_valid_WORD word0
+#define lpfc_sli_intf_valid_SHIFT 29
+#define lpfc_sli_intf_valid_MASK 0x00000007
+#define lpfc_sli_intf_valid_WORD word0
#define LPFC_SLI_INTF_VALID 6
+#define lpfc_sli_intf_featurelevel2_SHIFT 24
+#define lpfc_sli_intf_featurelevel2_MASK 0x0000001F
+#define lpfc_sli_intf_featurelevel2_WORD word0
+#define lpfc_sli_intf_featurelevel1_SHIFT 16
+#define lpfc_sli_intf_featurelevel1_MASK 0x000000FF
+#define lpfc_sli_intf_featurelevel1_WORD word0
+#define LPFC_SLI_INTF_FEATURELEVEL1_1 1
+#define LPFC_SLI_INTF_FEATURELEVEL1_2 2
+#define lpfc_sli_intf_sli_family_SHIFT 8
+#define lpfc_sli_intf_sli_family_MASK 0x000000FF
+#define lpfc_sli_intf_sli_family_WORD word0
+#define LPFC_SLI_INTF_FAMILY_BE2 0
+#define LPFC_SLI_INTF_FAMILY_BE3 1
+#define lpfc_sli_intf_slirev_SHIFT 4
+#define lpfc_sli_intf_slirev_MASK 0x0000000F
+#define lpfc_sli_intf_slirev_WORD word0
+#define LPFC_SLI_INTF_REV_SLI3 3
+#define LPFC_SLI_INTF_REV_SLI4 4
+#define lpfc_sli_intf_if_type_SHIFT 0
+#define lpfc_sli_intf_if_type_MASK 0x00000007
+#define lpfc_sli_intf_if_type_WORD word0
+#define LPFC_SLI_INTF_IF_TYPE_0 0
+#define LPFC_SLI_INTF_IF_TYPE_1 1
};
-#define LPFC_SLI4_BAR0 1
-#define LPFC_SLI4_BAR1 2
-#define LPFC_SLI4_BAR2 4
-
#define LPFC_SLI4_MBX_EMBED true
#define LPFC_SLI4_MBX_NEMBED false
@@ -161,6 +163,9 @@ struct lpfc_sli_intf {
#define LPFC_FP_DEF_IMAX 10000
#define LPFC_SP_DEF_IMAX 10000
+/* PORT_CAPABILITIES constants. */
+#define LPFC_MAX_SUPPORTED_PAGES 8
+
struct ulp_bde64 {
union ULP_BDE_TUS {
uint32_t w;
@@ -516,7 +521,7 @@ struct lpfc_register {
#define LPFC_UERR_STATUS_LO 0x00A0
#define LPFC_UE_MASK_HI 0x00AC
#define LPFC_UE_MASK_LO 0x00A8
-#define LPFC_SCRATCHPAD 0x0058
+#define LPFC_SLI_INTF 0x0058
/* BAR0 Registers */
#define LPFC_HST_STATE 0x00AC
@@ -576,19 +581,6 @@ struct lpfc_register {
#define LPFC_POST_STAGE_ARMFW_READY 0xC000
#define LPFC_POST_STAGE_ARMFW_UE 0xF000
-#define lpfc_scratchpad_slirev_SHIFT 4
-#define lpfc_scratchpad_slirev_MASK 0xF
-#define lpfc_scratchpad_slirev_WORD word0
-#define lpfc_scratchpad_chiptype_SHIFT 8
-#define lpfc_scratchpad_chiptype_MASK 0xFF
-#define lpfc_scratchpad_chiptype_WORD word0
-#define lpfc_scratchpad_featurelevel1_SHIFT 16
-#define lpfc_scratchpad_featurelevel1_MASK 0xFF
-#define lpfc_scratchpad_featurelevel1_WORD word0
-#define lpfc_scratchpad_featurelevel2_SHIFT 24
-#define lpfc_scratchpad_featurelevel2_MASK 0xFF
-#define lpfc_scratchpad_featurelevel2_WORD word0
-
/* BAR1 Registers */
#define LPFC_IMR_MASK_ALL 0xFFFFFFFF
#define LPFC_ISCR_CLEAR_ALL 0xFFFFFFFF
@@ -801,6 +793,7 @@ struct mbox_header {
#define LPFC_MBOX_OPCODE_FCOE_ADD_FCF 0x09
#define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF 0x0A
#define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE 0x0B
+#define LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF 0x10
/* Mailbox command structures */
struct eq_context {
@@ -1149,10 +1142,7 @@ struct sli4_sge { /* SLI-4 */
this flag !! */
#define lpfc_sli4_sge_last_MASK 0x00000001
#define lpfc_sli4_sge_last_WORD word2
- uint32_t word3;
-#define lpfc_sli4_sge_len_SHIFT 0
-#define lpfc_sli4_sge_len_MASK 0x0001FFFF
-#define lpfc_sli4_sge_len_WORD word3
+ uint32_t sge_len;
};
struct fcf_record {
@@ -1301,6 +1291,19 @@ struct lpfc_mbx_del_fcf_tbl_entry {
#define lpfc_mbx_del_fcf_tbl_index_WORD word10
};
+struct lpfc_mbx_redisc_fcf_tbl {
+ struct mbox_header header;
+ uint32_t word10;
+#define lpfc_mbx_redisc_fcf_count_SHIFT 0
+#define lpfc_mbx_redisc_fcf_count_MASK 0x0000FFFF
+#define lpfc_mbx_redisc_fcf_count_WORD word10
+ uint32_t resvd;
+ uint32_t word12;
+#define lpfc_mbx_redisc_fcf_index_SHIFT 0
+#define lpfc_mbx_redisc_fcf_index_MASK 0x0000FFFF
+#define lpfc_mbx_redisc_fcf_index_WORD word12
+};
+
struct lpfc_mbx_query_fw_cfg {
struct mbox_header header;
uint32_t config_number;
@@ -1834,6 +1837,177 @@ struct lpfc_mbx_request_features {
#define lpfc_mbx_rq_ftr_rsp_ifip_WORD word3
};
+struct lpfc_mbx_supp_pages {
+ uint32_t word1;
+#define qs_SHIFT 0
+#define qs_MASK 0x00000001
+#define qs_WORD word1
+#define wr_SHIFT 1
+#define wr_MASK 0x00000001
+#define wr_WORD word1
+#define pf_SHIFT 8
+#define pf_MASK 0x000000ff
+#define pf_WORD word1
+#define cpn_SHIFT 16
+#define cpn_MASK 0x000000ff
+#define cpn_WORD word1
+ uint32_t word2;
+#define list_offset_SHIFT 0
+#define list_offset_MASK 0x000000ff
+#define list_offset_WORD word2
+#define next_offset_SHIFT 8
+#define next_offset_MASK 0x000000ff
+#define next_offset_WORD word2
+#define elem_cnt_SHIFT 16
+#define elem_cnt_MASK 0x000000ff
+#define elem_cnt_WORD word2
+ uint32_t word3;
+#define pn_0_SHIFT 24
+#define pn_0_MASK 0x000000ff
+#define pn_0_WORD word3
+#define pn_1_SHIFT 16
+#define pn_1_MASK 0x000000ff
+#define pn_1_WORD word3
+#define pn_2_SHIFT 8
+#define pn_2_MASK 0x000000ff
+#define pn_2_WORD word3
+#define pn_3_SHIFT 0
+#define pn_3_MASK 0x000000ff
+#define pn_3_WORD word3
+ uint32_t word4;
+#define pn_4_SHIFT 24
+#define pn_4_MASK 0x000000ff
+#define pn_4_WORD word4
+#define pn_5_SHIFT 16
+#define pn_5_MASK 0x000000ff
+#define pn_5_WORD word4
+#define pn_6_SHIFT 8
+#define pn_6_MASK 0x000000ff
+#define pn_6_WORD word4
+#define pn_7_SHIFT 0
+#define pn_7_MASK 0x000000ff
+#define pn_7_WORD word4
+ uint32_t rsvd[27];
+#define LPFC_SUPP_PAGES 0
+#define LPFC_BLOCK_GUARD_PROFILES 1
+#define LPFC_SLI4_PARAMETERS 2
+};
+
+struct lpfc_mbx_sli4_params {
+ uint32_t word1;
+#define qs_SHIFT 0
+#define qs_MASK 0x00000001
+#define qs_WORD word1
+#define wr_SHIFT 1
+#define wr_MASK 0x00000001
+#define wr_WORD word1
+#define pf_SHIFT 8
+#define pf_MASK 0x000000ff
+#define pf_WORD word1
+#define cpn_SHIFT 16
+#define cpn_MASK 0x000000ff
+#define cpn_WORD word1
+ uint32_t word2;
+#define if_type_SHIFT 0
+#define if_type_MASK 0x00000007
+#define if_type_WORD word2
+#define sli_rev_SHIFT 4
+#define sli_rev_MASK 0x0000000f
+#define sli_rev_WORD word2
+#define sli_family_SHIFT 8
+#define sli_family_MASK 0x000000ff
+#define sli_family_WORD word2
+#define featurelevel_1_SHIFT 16
+#define featurelevel_1_MASK 0x000000ff
+#define featurelevel_1_WORD word2
+#define featurelevel_2_SHIFT 24
+#define featurelevel_2_MASK 0x0000001f
+#define featurelevel_2_WORD word2
+ uint32_t word3;
+#define fcoe_SHIFT 0
+#define fcoe_MASK 0x00000001
+#define fcoe_WORD word3
+#define fc_SHIFT 1
+#define fc_MASK 0x00000001
+#define fc_WORD word3
+#define nic_SHIFT 2
+#define nic_MASK 0x00000001
+#define nic_WORD word3
+#define iscsi_SHIFT 3
+#define iscsi_MASK 0x00000001
+#define iscsi_WORD word3
+#define rdma_SHIFT 4
+#define rdma_MASK 0x00000001
+#define rdma_WORD word3
+ uint32_t sge_supp_len;
+ uint32_t word5;
+#define if_page_sz_SHIFT 0
+#define if_page_sz_MASK 0x0000ffff
+#define if_page_sz_WORD word5
+#define loopbk_scope_SHIFT 24
+#define loopbk_scope_MASK 0x0000000f
+#define loopbk_scope_WORD word5
+#define rq_db_window_SHIFT 28
+#define rq_db_window_MASK 0x0000000f
+#define rq_db_window_WORD word5
+ uint32_t word6;
+#define eq_pages_SHIFT 0
+#define eq_pages_MASK 0x0000000f
+#define eq_pages_WORD word6
+#define eqe_size_SHIFT 8
+#define eqe_size_MASK 0x000000ff
+#define eqe_size_WORD word6
+ uint32_t word7;
+#define cq_pages_SHIFT 0
+#define cq_pages_MASK 0x0000000f
+#define cq_pages_WORD word7
+#define cqe_size_SHIFT 8
+#define cqe_size_MASK 0x000000ff
+#define cqe_size_WORD word7
+ uint32_t word8;
+#define mq_pages_SHIFT 0
+#define mq_pages_MASK 0x0000000f
+#define mq_pages_WORD word8
+#define mqe_size_SHIFT 8
+#define mqe_size_MASK 0x000000ff
+#define mqe_size_WORD word8
+#define mq_elem_cnt_SHIFT 16
+#define mq_elem_cnt_MASK 0x000000ff
+#define mq_elem_cnt_WORD word8
+ uint32_t word9;
+#define wq_pages_SHIFT 0
+#define wq_pages_MASK 0x0000ffff
+#define wq_pages_WORD word9
+#define wqe_size_SHIFT 8
+#define wqe_size_MASK 0x000000ff
+#define wqe_size_WORD word9
+ uint32_t word10;
+#define rq_pages_SHIFT 0
+#define rq_pages_MASK 0x0000ffff
+#define rq_pages_WORD word10
+#define rqe_size_SHIFT 8
+#define rqe_size_MASK 0x000000ff
+#define rqe_size_WORD word10
+ uint32_t word11;
+#define hdr_pages_SHIFT 0
+#define hdr_pages_MASK 0x0000000f
+#define hdr_pages_WORD word11
+#define hdr_size_SHIFT 8
+#define hdr_size_MASK 0x0000000f
+#define hdr_size_WORD word11
+#define hdr_pp_align_SHIFT 16
+#define hdr_pp_align_MASK 0x0000ffff
+#define hdr_pp_align_WORD word11
+ uint32_t word12;
+#define sgl_pages_SHIFT 0
+#define sgl_pages_MASK 0x0000000f
+#define sgl_pages_WORD word12
+#define sgl_pp_align_SHIFT 16
+#define sgl_pp_align_MASK 0x0000ffff
+#define sgl_pp_align_WORD word12
+ uint32_t rsvd_13_63[51];
+};
+
/* Mailbox Completion Queue Error Messages */
#define MB_CQE_STATUS_SUCCESS 0x0
#define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES 0x1
@@ -1863,6 +2037,7 @@ struct lpfc_mqe {
struct lpfc_mbx_read_fcf_tbl read_fcf_tbl;
struct lpfc_mbx_add_fcf_tbl_entry add_fcf_entry;
struct lpfc_mbx_del_fcf_tbl_entry del_fcf_entry;
+ struct lpfc_mbx_redisc_fcf_tbl redisc_fcf_tbl;
struct lpfc_mbx_reg_fcfi reg_fcfi;
struct lpfc_mbx_unreg_fcfi unreg_fcfi;
struct lpfc_mbx_mq_create mq_create;
@@ -1883,6 +2058,8 @@ struct lpfc_mqe {
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_supp_pages supp_pages;
+ struct lpfc_mbx_sli4_params sli4_params;
struct lpfc_mbx_nop nop;
} un;
};
@@ -1959,6 +2136,9 @@ struct lpfc_acqe_link {
#define LPFC_ASYNC_LINK_FAULT_NONE 0x0
#define LPFC_ASYNC_LINK_FAULT_LOCAL 0x1
#define LPFC_ASYNC_LINK_FAULT_REMOTE 0x2
+#define lpfc_acqe_qos_link_speed_SHIFT 16
+#define lpfc_acqe_qos_link_speed_MASK 0x0000FFFF
+#define lpfc_acqe_qos_link_speed_WORD word1
uint32_t event_tag;
uint32_t trailer;
};
@@ -1976,6 +2156,7 @@ struct lpfc_acqe_fcoe {
#define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL 0x2
#define LPFC_FCOE_EVENT_TYPE_FCF_DEAD 0x3
#define LPFC_FCOE_EVENT_TYPE_CVL 0x4
+#define LPFC_FCOE_EVENT_TYPE_FCF_PARAM_MOD 0x5
uint32_t event_tag;
uint32_t trailer;
};
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index b8eb1b6e5e7..d29ac7c317d 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -544,7 +544,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
mempool_free(pmb, phba->mbox_mem_pool);
return -EIO;
}
- } else {
+ } else if (phba->cfg_suppress_link_up == 0) {
lpfc_init_link(phba, pmb, phba->cfg_topology,
phba->cfg_link_speed);
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
@@ -603,6 +603,102 @@ lpfc_config_port_post(struct lpfc_hba *phba)
}
/**
+ * lpfc_hba_init_link - Initialize the FC link
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine will issue the INIT_LINK mailbox command call.
+ * It is available to other drivers through the lpfc_hba data
+ * structure for use as a delayed link up mechanism with the
+ * module parameter lpfc_suppress_link_up.
+ *
+ * Return code
+ * 0 - success
+ * Any other value - error
+ **/
+int
+lpfc_hba_init_link(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport = phba->pport;
+ LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+ int rc;
+
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ phba->link_state = LPFC_HBA_ERROR;
+ return -ENOMEM;
+ }
+ mb = &pmb->u.mb;
+ pmb->vport = vport;
+
+ lpfc_init_link(phba, pmb, phba->cfg_topology,
+ phba->cfg_link_speed);
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ lpfc_set_loopback_flag(phba);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0498 Adapter failed to init, mbxCmd x%x "
+ "INIT_LINK, mbxStatus x%x\n",
+ mb->mbxCommand, mb->mbxStatus);
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+ phba->link_state = LPFC_HBA_ERROR;
+ if (rc != MBX_BUSY)
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ phba->cfg_suppress_link_up = 0;
+
+ return 0;
+}
+
+/**
+ * lpfc_hba_down_link - this routine downs the FC link
+ *
+ * This routine will issue the DOWN_LINK mailbox command call.
+ * It is available to other drivers through the lpfc_hba data
+ * structure for use to stop the link.
+ *
+ * Return code
+ * 0 - success
+ * Any other value - error
+ **/
+int
+lpfc_hba_down_link(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *pmb;
+ int rc;
+
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ phba->link_state = LPFC_HBA_ERROR;
+ return -ENOMEM;
+ }
+
+ lpfc_printf_log(phba,
+ KERN_ERR, LOG_INIT,
+ "0491 Adapter Link is disabled.\n");
+ lpfc_down_link(phba, pmb);
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
+ lpfc_printf_log(phba,
+ KERN_ERR, LOG_INIT,
+ "2522 Adapter failed to issue DOWN_LINK"
+ " mbox command rc 0x%x\n", rc);
+
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
* lpfc_hba_down_prep - Perform lpfc uninitialization prior to HBA reset
* @phba: pointer to lpfc HBA data structure.
*
@@ -2073,6 +2169,44 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport)
}
/**
+ * __lpfc_sli4_stop_fcf_redisc_wait_timer - Stop FCF rediscovery wait timer
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine stops the SLI4 FCF rediscover wait timer if it's on. The
+ * caller of this routine should already hold the host lock.
+ **/
+void
+__lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
+{
+ /* Clear pending FCF rediscovery wait timer */
+ phba->fcf.fcf_flag &= ~FCF_REDISC_PEND;
+ /* Now, try to stop the timer */
+ del_timer(&phba->fcf.redisc_wait);
+}
+
+/**
+ * lpfc_sli4_stop_fcf_redisc_wait_timer - Stop FCF rediscovery wait timer
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine stops the SLI4 FCF rediscover wait timer if it's on. It
+ * checks whether the FCF rediscovery wait timer is pending with the host
+ * lock held before proceeding with disabling the timer and clearing the
+ * wait timer pendig flag.
+ **/
+void
+lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
+{
+ spin_lock_irq(&phba->hbalock);
+ if (!(phba->fcf.fcf_flag & FCF_REDISC_PEND)) {
+ /* FCF rediscovery timer already fired or stopped */
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+ __lpfc_sli4_stop_fcf_redisc_wait_timer(phba);
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
* lpfc_stop_hba_timers - Stop all the timers associated with an HBA
* @phba: pointer to lpfc hba data structure.
*
@@ -2096,6 +2230,7 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba)
break;
case LPFC_PCI_DEV_OC:
/* Stop any OneConnect device sepcific driver timers */
+ lpfc_sli4_stop_fcf_redisc_wait_timer(phba);
break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -2228,6 +2363,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
struct lpfc_vport *vport = phba->pport;
struct lpfc_nodelist *ndlp, *next_ndlp;
struct lpfc_vport **vports;
+ struct Scsi_Host *shost;
int i;
if (vport->fc_flag & FC_OFFLINE_MODE)
@@ -2241,11 +2377,15 @@ lpfc_offline_prep(struct lpfc_hba * phba)
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- struct Scsi_Host *shost;
-
if (vports[i]->load_flag & FC_UNLOADING)
continue;
+ shost = lpfc_shost_from_vport(vports[i]);
+ spin_lock_irq(shost->host_lock);
vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
+ vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ vports[i]->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+
shost = lpfc_shost_from_vport(vports[i]);
list_for_each_entry_safe(ndlp, next_ndlp,
&vports[i]->fc_nodes,
@@ -2401,7 +2541,8 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
shost->this_id = -1;
shost->max_cmd_len = 16;
if (phba->sli_rev == LPFC_SLI_REV4) {
- shost->dma_boundary = LPFC_SLI4_MAX_SEGMENT_SIZE;
+ shost->dma_boundary =
+ phba->sli4_hba.pc_sli4_params.sge_supp_len;
shost->sg_tablesize = phba->cfg_sg_seg_cnt;
}
@@ -2650,8 +2791,6 @@ lpfc_stop_port_s4(struct lpfc_hba *phba)
lpfc_stop_hba_timers(phba);
phba->pport->work_port_events = 0;
phba->sli4_hba.intr_enable = 0;
- /* Hard clear it for now, shall have more graceful way to wait later */
- phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
}
/**
@@ -2703,7 +2842,7 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
del_fcf_record = &mboxq->u.mqe.un.del_fcf_entry;
bf_set(lpfc_mbx_del_fcf_tbl_count, del_fcf_record, 1);
bf_set(lpfc_mbx_del_fcf_tbl_index, del_fcf_record,
- phba->fcf.fcf_indx);
+ phba->fcf.current_rec.fcf_indx);
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
@@ -2727,6 +2866,57 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
}
/**
+ * lpfc_fcf_redisc_wait_start_timer - Start fcf rediscover wait timer
+ * @phba: Pointer to hba for which this call is being executed.
+ *
+ * This routine starts the timer waiting for the FCF rediscovery to complete.
+ **/
+void
+lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *phba)
+{
+ unsigned long fcf_redisc_wait_tmo =
+ (jiffies + msecs_to_jiffies(LPFC_FCF_REDISCOVER_WAIT_TMO));
+ /* Start fcf rediscovery wait period timer */
+ mod_timer(&phba->fcf.redisc_wait, fcf_redisc_wait_tmo);
+ spin_lock_irq(&phba->hbalock);
+ /* Allow action to new fcf asynchronous event */
+ phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_SCAN_DONE);
+ /* Mark the FCF rediscovery pending state */
+ phba->fcf.fcf_flag |= FCF_REDISC_PEND;
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_fcf_redisc_wait_tmo - FCF table rediscover wait timeout
+ * @ptr: Map to lpfc_hba data structure pointer.
+ *
+ * This routine is invoked when waiting for FCF table rediscover has been
+ * timed out. If new FCF record(s) has (have) been discovered during the
+ * wait period, a new FCF event shall be added to the FCOE async event
+ * list, and then worker thread shall be waked up for processing from the
+ * worker thread context.
+ **/
+void
+lpfc_sli4_fcf_redisc_wait_tmo(unsigned long ptr)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)ptr;
+
+ /* Don't send FCF rediscovery event if timer cancelled */
+ spin_lock_irq(&phba->hbalock);
+ if (!(phba->fcf.fcf_flag & FCF_REDISC_PEND)) {
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+ /* Clear FCF rediscovery timer pending flag */
+ phba->fcf.fcf_flag &= ~FCF_REDISC_PEND;
+ /* FCF rediscovery event to worker thread */
+ phba->fcf.fcf_flag |= FCF_REDISC_EVT;
+ spin_unlock_irq(&phba->hbalock);
+ /* wake up worker thread */
+ lpfc_worker_wake_up(phba);
+}
+
+/**
* lpfc_sli4_fw_cfg_check - Read the firmware config and verify FCoE support
* @phba: pointer to lpfc hba data structure.
*
@@ -2978,6 +3168,8 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
bf_get(lpfc_acqe_link_physical, acqe_link);
phba->sli4_hba.link_state.fault =
bf_get(lpfc_acqe_link_fault, acqe_link);
+ phba->sli4_hba.link_state.logical_speed =
+ bf_get(lpfc_acqe_qos_link_speed, acqe_link);
/* Invoke the lpfc_handle_latt mailbox command callback function */
lpfc_mbx_cmpl_read_la(phba, pmb);
@@ -3007,22 +3199,34 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
struct lpfc_nodelist *ndlp;
struct Scsi_Host *shost;
uint32_t link_state;
+ int active_vlink_present;
+ struct lpfc_vport **vports;
+ int i;
phba->fc_eventTag = acqe_fcoe->event_tag;
phba->fcoe_eventtag = acqe_fcoe->event_tag;
switch (event_type) {
case LPFC_FCOE_EVENT_TYPE_NEW_FCF:
+ case LPFC_FCOE_EVENT_TYPE_FCF_PARAM_MOD:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
"2546 New FCF found index 0x%x tag 0x%x\n",
acqe_fcoe->index,
acqe_fcoe->event_tag);
- /*
- * If the current FCF is in discovered state, or
- * FCF discovery is in progress do nothing.
- */
spin_lock_irq(&phba->hbalock);
- if ((phba->fcf.fcf_flag & FCF_DISCOVERED) ||
- (phba->hba_flag & FCF_DISC_INPROGRESS)) {
+ if ((phba->fcf.fcf_flag & FCF_SCAN_DONE) ||
+ (phba->hba_flag & FCF_DISC_INPROGRESS)) {
+ /*
+ * If the current FCF is in discovered state or
+ * FCF discovery is in progress, do nothing.
+ */
+ spin_unlock_irq(&phba->hbalock);
+ break;
+ }
+ if (phba->fcf.fcf_flag & FCF_REDISC_EVT) {
+ /*
+ * If fast FCF failover rescan event is pending,
+ * do nothing.
+ */
spin_unlock_irq(&phba->hbalock);
break;
}
@@ -3049,7 +3253,7 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
" 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->index)
+ if (phba->fcf.current_rec.fcf_indx != acqe_fcoe->index)
break;
/*
* Currently, driver support only one FCF - so treat this as
@@ -3074,14 +3278,58 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
if (!ndlp)
break;
shost = lpfc_shost_from_vport(vport);
+ if (phba->pport->port_state <= LPFC_FLOGI)
+ break;
+ /* If virtual link is not yet instantiated ignore CVL */
+ if (vport->port_state <= LPFC_FDISC)
+ break;
+
lpfc_linkdown_port(vport);
- if (vport->port_type != LPFC_NPIV_PORT) {
+ lpfc_cleanup_pending_mbox(vport);
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag |= FC_VPORT_CVL_RCVD;
+ spin_unlock_irq(shost->host_lock);
+ active_vlink_present = 0;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL;
+ i++) {
+ if ((!(vports[i]->fc_flag &
+ FC_VPORT_CVL_RCVD)) &&
+ (vports[i]->port_state > LPFC_FDISC)) {
+ active_vlink_present = 1;
+ break;
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+ }
+
+ if (active_vlink_present) {
+ /*
+ * If there are other active VLinks present,
+ * re-instantiate the Vlink using FDISC.
+ */
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;
+ ndlp->nlp_last_elscmd = ELS_CMD_FDISC;
+ vport->port_state = LPFC_FDISC;
+ } else {
+ /*
+ * Otherwise, we request port to rediscover
+ * the entire FCF table for a fast recovery
+ * from possible case that the current FCF
+ * is no longer valid.
+ */
+ rc = lpfc_sli4_redisc_fcf_table(phba);
+ if (rc)
+ /*
+ * Last resort will be re-try on the
+ * the current registered FCF entry.
+ */
+ lpfc_retry_pport_discovery(phba);
}
break;
default:
@@ -3158,6 +3406,34 @@ void lpfc_sli4_async_event_proc(struct lpfc_hba *phba)
}
/**
+ * lpfc_sli4_fcf_redisc_event_proc - Process fcf table rediscovery event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process FCF table
+ * rediscovery pending completion event.
+ **/
+void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *phba)
+{
+ int rc;
+
+ spin_lock_irq(&phba->hbalock);
+ /* Clear FCF rediscovery timeout event */
+ phba->fcf.fcf_flag &= ~FCF_REDISC_EVT;
+ /* Clear driver fast failover FCF record flag */
+ phba->fcf.failover_rec.flag = 0;
+ /* Set state for FCF fast failover */
+ phba->fcf.fcf_flag |= FCF_REDISC_FOV;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Scan FCF table from the first entry to re-discover SAN */
+ rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+ if (rc)
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2747 Post FCF rediscovery read FCF record "
+ "failed 0x%x\n", rc);
+}
+
+/**
* lpfc_api_table_setup - Set up per hba pci-device group func api jump table
* @phba: pointer to lpfc hba data structure.
* @dev_grp: The HBA PCI-Device group number.
@@ -3442,8 +3718,10 @@ static int
lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
{
struct lpfc_sli *psli;
- int rc;
- int i, hbq_count;
+ LPFC_MBOXQ_t *mboxq;
+ int rc, i, hbq_count, buf_size, dma_buf_size, max_buf_size;
+ uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0};
+ struct lpfc_mqe *mqe;
/* Before proceed, wait for POST done and device ready */
rc = lpfc_sli4_post_status_check(phba);
@@ -3472,6 +3750,11 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
init_timer(&phba->eratt_poll);
phba->eratt_poll.function = lpfc_poll_eratt;
phba->eratt_poll.data = (unsigned long) phba;
+ /* FCF rediscover timer */
+ init_timer(&phba->fcf.redisc_wait);
+ phba->fcf.redisc_wait.function = lpfc_sli4_fcf_redisc_wait_tmo;
+ phba->fcf.redisc_wait.data = (unsigned long)phba;
+
/*
* We need to do a READ_CONFIG mailbox command here before
* calling lpfc_get_cfgparam. For VFs this will report the
@@ -3496,31 +3779,26 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
* used to create the sg_dma_buf_pool must be dynamically calculated.
* 2 segments are added since the IOCB needs a command and response bde.
* To insure that the scsi sgl does not cross a 4k page boundary only
- * sgl sizes of 1k, 2k, 4k, and 8k are supported.
- * Table of sgl sizes and seg_cnt:
- * sgl size, sg_seg_cnt total seg
- * 1k 50 52
- * 2k 114 116
- * 4k 242 244
- * 8k 498 500
- * cmd(32) + rsp(160) + (52 * sizeof(sli4_sge)) = 1024
- * cmd(32) + rsp(160) + (116 * sizeof(sli4_sge)) = 2048
- * cmd(32) + rsp(160) + (244 * sizeof(sli4_sge)) = 4096
- * cmd(32) + rsp(160) + (500 * sizeof(sli4_sge)) = 8192
+ * sgl sizes of must be a power of 2.
*/
- if (phba->cfg_sg_seg_cnt <= LPFC_DEFAULT_SG_SEG_CNT)
- phba->cfg_sg_seg_cnt = 50;
- else if (phba->cfg_sg_seg_cnt <= 114)
- phba->cfg_sg_seg_cnt = 114;
- else if (phba->cfg_sg_seg_cnt <= 242)
- phba->cfg_sg_seg_cnt = 242;
+ buf_size = (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) +
+ ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge)));
+ /* Feature Level 1 hardware is limited to 2 pages */
+ if ((bf_get(lpfc_sli_intf_featurelevel1, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_FEATURELEVEL1_1))
+ max_buf_size = LPFC_SLI4_FL1_MAX_BUF_SIZE;
else
- phba->cfg_sg_seg_cnt = 498;
-
- phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd)
- + sizeof(struct fcp_rsp);
- phba->cfg_sg_dma_buf_size +=
- ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge));
+ max_buf_size = LPFC_SLI4_MAX_BUF_SIZE;
+ for (dma_buf_size = LPFC_SLI4_MIN_BUF_SIZE;
+ dma_buf_size < max_buf_size && buf_size > dma_buf_size;
+ dma_buf_size = dma_buf_size << 1)
+ ;
+ if (dma_buf_size == max_buf_size)
+ phba->cfg_sg_seg_cnt = (dma_buf_size -
+ sizeof(struct fcp_cmnd) - sizeof(struct fcp_rsp) -
+ (2 * sizeof(struct sli4_sge))) /
+ sizeof(struct sli4_sge);
+ phba->cfg_sg_dma_buf_size = dma_buf_size;
/* Initialize buffer queue management fields */
hbq_count = lpfc_sli_hbq_count();
@@ -3638,6 +3916,43 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_fcp_eq_hdl;
}
+ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!mboxq) {
+ rc = -ENOMEM;
+ goto out_free_fcp_eq_hdl;
+ }
+
+ /* Get the Supported Pages. It is always available. */
+ lpfc_supported_pages(mboxq);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (unlikely(rc)) {
+ rc = -EIO;
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ goto out_free_fcp_eq_hdl;
+ }
+
+ mqe = &mboxq->u.mqe;
+ memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3),
+ LPFC_MAX_SUPPORTED_PAGES);
+ for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) {
+ switch (pn_page[i]) {
+ case LPFC_SLI4_PARAMETERS:
+ phba->sli4_hba.pc_sli4_params.supported = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Read the port's SLI4 Parameters capabilities if supported. */
+ if (phba->sli4_hba.pc_sli4_params.supported)
+ rc = lpfc_pc_sli4_params_get(phba, mboxq);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (rc) {
+ rc = -EIO;
+ goto out_free_fcp_eq_hdl;
+ }
return rc;
out_free_fcp_eq_hdl:
@@ -3733,6 +4048,8 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
int
lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
{
+ phba->lpfc_hba_init_link = lpfc_hba_init_link;
+ phba->lpfc_hba_down_link = lpfc_hba_down_link;
switch (dev_grp) {
case LPFC_PCI_DEV_LP:
phba->lpfc_hba_down_post = lpfc_hba_down_post_s3;
@@ -4291,7 +4608,7 @@ lpfc_hba_alloc(struct pci_dev *pdev)
return NULL;
}
- mutex_init(&phba->ct_event_mutex);
+ spin_lock_init(&phba->ct_ev_lock);
INIT_LIST_HEAD(&phba->ct_ev_waiters);
return phba;
@@ -4641,7 +4958,7 @@ lpfc_sli_pci_mem_unset(struct lpfc_hba *phba)
int
lpfc_sli4_post_status_check(struct lpfc_hba *phba)
{
- struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad;
+ struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg;
int i, port_error = -ENODEV;
if (!phba->sli4_hba.STAregaddr)
@@ -4677,14 +4994,21 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
bf_get(lpfc_hst_state_port_status, &sta_reg));
/* Log device information */
- scratchpad.word0 = readl(phba->sli4_hba.SCRATCHPADregaddr);
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2534 Device Info: ChipType=0x%x, SliRev=0x%x, "
- "FeatureL1=0x%x, FeatureL2=0x%x\n",
- bf_get(lpfc_scratchpad_chiptype, &scratchpad),
- bf_get(lpfc_scratchpad_slirev, &scratchpad),
- bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
- bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
+ phba->sli4_hba.sli_intf.word0 = readl(phba->sli4_hba.SLIINTFregaddr);
+ if (bf_get(lpfc_sli_intf_valid,
+ &phba->sli4_hba.sli_intf) == LPFC_SLI_INTF_VALID) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2534 Device Info: ChipType=0x%x, SliRev=0x%x, "
+ "FeatureL1=0x%x, FeatureL2=0x%x\n",
+ bf_get(lpfc_sli_intf_sli_family,
+ &phba->sli4_hba.sli_intf),
+ bf_get(lpfc_sli_intf_slirev,
+ &phba->sli4_hba.sli_intf),
+ bf_get(lpfc_sli_intf_featurelevel1,
+ &phba->sli4_hba.sli_intf),
+ bf_get(lpfc_sli_intf_featurelevel2,
+ &phba->sli4_hba.sli_intf));
+ }
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 */
@@ -4723,8 +5047,8 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba)
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;
+ phba->sli4_hba.SLIINTFregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_SLI_INTF;
}
/**
@@ -5999,7 +6323,7 @@ lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi)
spin_lock_irqsave(&phba->hbalock, flags);
/* Mark the FCFI is no longer registered */
phba->fcf.fcf_flag &=
- ~(FCF_AVAILABLE | FCF_REGISTERED | FCF_DISCOVERED);
+ ~(FCF_AVAILABLE | FCF_REGISTERED | FCF_SCAN_DONE);
spin_unlock_irqrestore(&phba->hbalock, flags);
}
}
@@ -6039,16 +6363,20 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
/* Get the bus address of SLI4 device Bar0, Bar1, and Bar2 and the
* number of bytes required by each mapping. They are actually
- * mapping to the PCI BAR regions 1, 2, and 4 by the SLI4 device.
+ * mapping to the PCI BAR regions 0 or 1, 2, and 4 by the SLI4 device.
*/
- phba->pci_bar0_map = pci_resource_start(pdev, LPFC_SLI4_BAR0);
- bar0map_len = pci_resource_len(pdev, LPFC_SLI4_BAR0);
-
- phba->pci_bar1_map = pci_resource_start(pdev, LPFC_SLI4_BAR1);
- bar1map_len = pci_resource_len(pdev, LPFC_SLI4_BAR1);
+ if (pci_resource_start(pdev, 0)) {
+ phba->pci_bar0_map = pci_resource_start(pdev, 0);
+ bar0map_len = pci_resource_len(pdev, 0);
+ } else {
+ phba->pci_bar0_map = pci_resource_start(pdev, 1);
+ bar0map_len = pci_resource_len(pdev, 1);
+ }
+ phba->pci_bar1_map = pci_resource_start(pdev, 2);
+ bar1map_len = pci_resource_len(pdev, 2);
- phba->pci_bar2_map = pci_resource_start(pdev, LPFC_SLI4_BAR2);
- bar2map_len = pci_resource_len(pdev, LPFC_SLI4_BAR2);
+ phba->pci_bar2_map = pci_resource_start(pdev, 4);
+ bar2map_len = pci_resource_len(pdev, 4);
/* Map SLI4 PCI Config Space Register base to a kernel virtual addr */
phba->sli4_hba.conf_regs_memmap_p =
@@ -6793,6 +7121,73 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba)
phba->pport->work_port_events = 0;
}
+ /**
+ * lpfc_pc_sli4_params_get - Get the SLI4_PARAMS port capabilities.
+ * @phba: Pointer to HBA context object.
+ * @mboxq: Pointer to the mailboxq memory for the mailbox command response.
+ *
+ * This function is called in the SLI4 code path to read the port's
+ * sli4 capabilities.
+ *
+ * This function may be be called from any context that can block-wait
+ * for the completion. The expectation is that this routine is called
+ * typically from probe_one or from the online routine.
+ **/
+int
+lpfc_pc_sli4_params_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ int rc;
+ struct lpfc_mqe *mqe;
+ struct lpfc_pc_sli4_params *sli4_params;
+ uint32_t mbox_tmo;
+
+ rc = 0;
+ mqe = &mboxq->u.mqe;
+
+ /* Read the port's SLI4 Parameters port capabilities */
+ lpfc_sli4_params(mboxq);
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_PORT_CAPABILITIES);
+ rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ }
+
+ if (unlikely(rc))
+ return 1;
+
+ sli4_params = &phba->sli4_hba.pc_sli4_params;
+ sli4_params->if_type = bf_get(if_type, &mqe->un.sli4_params);
+ sli4_params->sli_rev = bf_get(sli_rev, &mqe->un.sli4_params);
+ sli4_params->sli_family = bf_get(sli_family, &mqe->un.sli4_params);
+ sli4_params->featurelevel_1 = bf_get(featurelevel_1,
+ &mqe->un.sli4_params);
+ sli4_params->featurelevel_2 = bf_get(featurelevel_2,
+ &mqe->un.sli4_params);
+ sli4_params->proto_types = mqe->un.sli4_params.word3;
+ sli4_params->sge_supp_len = mqe->un.sli4_params.sge_supp_len;
+ sli4_params->if_page_sz = bf_get(if_page_sz, &mqe->un.sli4_params);
+ sli4_params->rq_db_window = bf_get(rq_db_window, &mqe->un.sli4_params);
+ sli4_params->loopbk_scope = bf_get(loopbk_scope, &mqe->un.sli4_params);
+ sli4_params->eq_pages_max = bf_get(eq_pages, &mqe->un.sli4_params);
+ sli4_params->eqe_size = bf_get(eqe_size, &mqe->un.sli4_params);
+ sli4_params->cq_pages_max = bf_get(cq_pages, &mqe->un.sli4_params);
+ sli4_params->cqe_size = bf_get(cqe_size, &mqe->un.sli4_params);
+ sli4_params->mq_pages_max = bf_get(mq_pages, &mqe->un.sli4_params);
+ sli4_params->mqe_size = bf_get(mqe_size, &mqe->un.sli4_params);
+ sli4_params->mq_elem_cnt = bf_get(mq_elem_cnt, &mqe->un.sli4_params);
+ sli4_params->wq_pages_max = bf_get(wq_pages, &mqe->un.sli4_params);
+ sli4_params->wqe_size = bf_get(wqe_size, &mqe->un.sli4_params);
+ sli4_params->rq_pages_max = bf_get(rq_pages, &mqe->un.sli4_params);
+ sli4_params->rqe_size = bf_get(rqe_size, &mqe->un.sli4_params);
+ sli4_params->hdr_pages_max = bf_get(hdr_pages, &mqe->un.sli4_params);
+ sli4_params->hdr_size = bf_get(hdr_size, &mqe->un.sli4_params);
+ sli4_params->hdr_pp_align = bf_get(hdr_pp_align, &mqe->un.sli4_params);
+ sli4_params->sgl_pages_max = bf_get(sgl_pages, &mqe->un.sli4_params);
+ sli4_params->sgl_pp_align = bf_get(sgl_pp_align, &mqe->un.sli4_params);
+ return rc;
+}
+
/**
* lpfc_pci_probe_one_s3 - PCI probe func to reg SLI-3 device to PCI subsystem.
* @pdev: pointer to PCI device
@@ -7134,6 +7529,12 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
+ /*
+ * As the new kernel behavior of pci_restore_state() API call clears
+ * device saved_state flag, need to save the restored state again.
+ */
+ pci_save_state(pdev);
+
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -7317,6 +7718,13 @@ lpfc_io_slot_reset_s3(struct pci_dev *pdev)
}
pci_restore_state(pdev);
+
+ /*
+ * As the new kernel behavior of pci_restore_state() API call clears
+ * device saved_state flag, need to save the restored state again.
+ */
+ pci_save_state(pdev);
+
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -7726,6 +8134,13 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
/* Restore device state from PCI config space */
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
+
+ /*
+ * As the new kernel behavior of pci_restore_state() API call clears
+ * device saved_state flag, need to save the restored state again.
+ */
+ pci_save_state(pdev);
+
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -7845,11 +8260,11 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
int rc;
struct lpfc_sli_intf intf;
- if (pci_read_config_dword(pdev, LPFC_SLIREV_CONF_WORD, &intf.word0))
+ if (pci_read_config_dword(pdev, LPFC_SLI_INTF, &intf.word0))
return -ENODEV;
if ((bf_get(lpfc_sli_intf_valid, &intf) == LPFC_SLI_INTF_VALID) &&
- (bf_get(lpfc_sli_intf_rev, &intf) == LPFC_SLIREV_CONF_SLI4))
+ (bf_get(lpfc_sli_intf_slirev, &intf) == LPFC_SLI_INTF_REV_SLI4))
rc = lpfc_pci_probe_one_s4(pdev, pid);
else
rc = lpfc_pci_probe_one_s3(pdev, pid);
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index a9afd8b94b6..6c4dce1a30c 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -1707,7 +1707,8 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
alloc_len - sizeof(union lpfc_sli4_cfg_shdr);
}
/* The sub-header is in DMA memory, which needs endian converstion */
- lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr,
+ if (cfg_shdr)
+ lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr,
sizeof(union lpfc_sli4_cfg_shdr));
return alloc_len;
@@ -1747,6 +1748,65 @@ lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
}
/**
+ * lpfc_sli4_mbx_read_fcf_record - Allocate and construct read fcf mbox cmd
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: index to fcf table.
+ *
+ * This routine routine allocates and constructs non-embedded mailbox command
+ * for reading a FCF table entry refered by @fcf_index.
+ *
+ * Return: pointer to the mailbox command constructed if successful, otherwise
+ * NULL.
+ **/
+int
+lpfc_sli4_mbx_read_fcf_record(struct lpfc_hba *phba,
+ struct lpfcMboxq *mboxq,
+ uint16_t fcf_index)
+{
+ void *virt_addr;
+ dma_addr_t phys_addr;
+ uint8_t *bytep;
+ struct lpfc_mbx_sge sge;
+ uint32_t alloc_len, req_len;
+ struct lpfc_mbx_read_fcf_tbl *read_fcf;
+
+ if (!mboxq)
+ return -ENOMEM;
+
+ req_len = sizeof(struct fcf_record) +
+ sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t);
+
+ /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */
+ alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len,
+ LPFC_SLI4_MBX_NEMBED);
+
+ if (alloc_len < req_len) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "0291 Allocated DMA memory size (x%x) is "
+ "less than the requested DMA memory "
+ "size (x%x)\n", alloc_len, req_len);
+ return -ENOMEM;
+ }
+
+ /* Get the first SGE entry from the non-embedded DMA memory. This
+ * routine only uses a single SGE.
+ */
+ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+ phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+ virt_addr = mboxq->sge_array->addr[0];
+ read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
+
+ /* Set up command fields */
+ bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index);
+ /* Perform necessary endian conversion */
+ bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+ lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t));
+
+ return 0;
+}
+
+/**
* lpfc_request_features: Configure SLI4 REQUEST_FEATURES mailbox
* @mboxq: pointer to lpfc mbox command.
*
@@ -1946,13 +2006,14 @@ lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID);
bf_set(lpfc_reg_fcfi_rq_id2, reg_fcfi, REG_FCF_INVALID_QID);
bf_set(lpfc_reg_fcfi_rq_id3, reg_fcfi, REG_FCF_INVALID_QID);
- bf_set(lpfc_reg_fcfi_info_index, reg_fcfi, phba->fcf.fcf_indx);
+ bf_set(lpfc_reg_fcfi_info_index, reg_fcfi,
+ phba->fcf.current_rec.fcf_indx);
/* reg_fcf addr mode is bit wise inverted value of fcf addr_mode */
- bf_set(lpfc_reg_fcfi_mam, reg_fcfi,
- (~phba->fcf.addr_mode) & 0x3);
- if (phba->fcf.fcf_flag & FCF_VALID_VLAN) {
+ bf_set(lpfc_reg_fcfi_mam, reg_fcfi, (~phba->fcf.addr_mode) & 0x3);
+ if (phba->fcf.current_rec.vlan_id != 0xFFFF) {
bf_set(lpfc_reg_fcfi_vv, reg_fcfi, 1);
- bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi, phba->fcf.vlan_id);
+ bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi,
+ phba->fcf.current_rec.vlan_id);
}
}
@@ -1992,3 +2053,41 @@ lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp)
bf_set(lpfc_resume_rpi_ii, resume_rpi, RESUME_INDEX_RPI);
resume_rpi->event_tag = ndlp->phba->fc_eventTag;
}
+
+/**
+ * lpfc_supported_pages - Initialize the PORT_CAPABILITIES supported pages
+ * mailbox command.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * The PORT_CAPABILITIES supported pages mailbox command is issued to
+ * retrieve the particular feature pages supported by the port.
+ **/
+void
+lpfc_supported_pages(struct lpfcMboxq *mbox)
+{
+ struct lpfc_mbx_supp_pages *supp_pages;
+
+ memset(mbox, 0, sizeof(*mbox));
+ supp_pages = &mbox->u.mqe.un.supp_pages;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_PORT_CAPABILITIES);
+ bf_set(cpn, supp_pages, LPFC_SUPP_PAGES);
+}
+
+/**
+ * lpfc_sli4_params - Initialize the PORT_CAPABILITIES SLI4 Params
+ * mailbox command.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * The PORT_CAPABILITIES SLI4 parameters mailbox command is issued to
+ * retrieve the particular SLI4 features supported by the port.
+ **/
+void
+lpfc_sli4_params(struct lpfcMboxq *mbox)
+{
+ struct lpfc_mbx_sli4_params *sli4_params;
+
+ memset(mbox, 0, sizeof(*mbox));
+ sli4_params = &mbox->u.mqe.un.sli4_params;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_PORT_CAPABILITIES);
+ bf_set(cpn, sli4_params, LPFC_SLI4_PARAMETERS);
+}
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
index d655ed3eebe..f3cfbe2ce98 100644
--- a/drivers/scsi/lpfc/lpfc_nl.h
+++ b/drivers/scsi/lpfc/lpfc_nl.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2008 Emulex. All rights reserved. *
+ * Copyright (C) 2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -177,23 +177,3 @@ struct temp_event {
uint32_t data;
};
-/* bsg definitions */
-#define LPFC_BSG_VENDOR_SET_CT_EVENT 1
-#define LPFC_BSG_VENDOR_GET_CT_EVENT 2
-
-struct set_ct_event {
- uint32_t command;
- uint32_t ev_req_id;
- uint32_t ev_reg_id;
-};
-
-struct get_ct_event {
- uint32_t command;
- uint32_t ev_reg_id;
- uint32_t ev_req_id;
-};
-
-struct get_ct_event_reply {
- uint32_t immed_data;
- uint32_t type;
-};
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 2ed6af19493..d20ae6b3b3c 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -62,7 +62,7 @@ lpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int
lpfc_check_sparm(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
- struct serv_parm * sp, uint32_t class)
+ struct serv_parm *sp, uint32_t class, int flogi)
{
volatile struct serv_parm *hsp = &vport->fc_sparam;
uint16_t hsp_value, ssp_value = 0;
@@ -75,49 +75,56 @@ lpfc_check_sparm(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* correcting the byte values.
*/
if (sp->cls1.classValid) {
- hsp_value = (hsp->cls1.rcvDataSizeMsb << 8) |
- hsp->cls1.rcvDataSizeLsb;
- ssp_value = (sp->cls1.rcvDataSizeMsb << 8) |
- sp->cls1.rcvDataSizeLsb;
- if (!ssp_value)
- goto bad_service_param;
- if (ssp_value > hsp_value) {
- sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb;
- sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb;
+ if (!flogi) {
+ hsp_value = ((hsp->cls1.rcvDataSizeMsb << 8) |
+ hsp->cls1.rcvDataSizeLsb);
+ ssp_value = ((sp->cls1.rcvDataSizeMsb << 8) |
+ sp->cls1.rcvDataSizeLsb);
+ if (!ssp_value)
+ goto bad_service_param;
+ if (ssp_value > hsp_value) {
+ sp->cls1.rcvDataSizeLsb =
+ hsp->cls1.rcvDataSizeLsb;
+ sp->cls1.rcvDataSizeMsb =
+ hsp->cls1.rcvDataSizeMsb;
+ }
}
- } else if (class == CLASS1) {
+ } else if (class == CLASS1)
goto bad_service_param;
- }
-
if (sp->cls2.classValid) {
- hsp_value = (hsp->cls2.rcvDataSizeMsb << 8) |
- hsp->cls2.rcvDataSizeLsb;
- ssp_value = (sp->cls2.rcvDataSizeMsb << 8) |
- sp->cls2.rcvDataSizeLsb;
- if (!ssp_value)
- goto bad_service_param;
- if (ssp_value > hsp_value) {
- sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb;
- sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb;
+ if (!flogi) {
+ hsp_value = ((hsp->cls2.rcvDataSizeMsb << 8) |
+ hsp->cls2.rcvDataSizeLsb);
+ ssp_value = ((sp->cls2.rcvDataSizeMsb << 8) |
+ sp->cls2.rcvDataSizeLsb);
+ if (!ssp_value)
+ goto bad_service_param;
+ if (ssp_value > hsp_value) {
+ sp->cls2.rcvDataSizeLsb =
+ hsp->cls2.rcvDataSizeLsb;
+ sp->cls2.rcvDataSizeMsb =
+ hsp->cls2.rcvDataSizeMsb;
+ }
}
- } else if (class == CLASS2) {
+ } else if (class == CLASS2)
goto bad_service_param;
- }
-
if (sp->cls3.classValid) {
- hsp_value = (hsp->cls3.rcvDataSizeMsb << 8) |
- hsp->cls3.rcvDataSizeLsb;
- ssp_value = (sp->cls3.rcvDataSizeMsb << 8) |
- sp->cls3.rcvDataSizeLsb;
- if (!ssp_value)
- goto bad_service_param;
- if (ssp_value > hsp_value) {
- sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb;
- sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb;
+ if (!flogi) {
+ hsp_value = ((hsp->cls3.rcvDataSizeMsb << 8) |
+ hsp->cls3.rcvDataSizeLsb);
+ ssp_value = ((sp->cls3.rcvDataSizeMsb << 8) |
+ sp->cls3.rcvDataSizeLsb);
+ if (!ssp_value)
+ goto bad_service_param;
+ if (ssp_value > hsp_value) {
+ sp->cls3.rcvDataSizeLsb =
+ hsp->cls3.rcvDataSizeLsb;
+ sp->cls3.rcvDataSizeMsb =
+ hsp->cls3.rcvDataSizeMsb;
+ }
}
- } else if (class == CLASS3) {
+ } else if (class == CLASS3)
goto bad_service_param;
- }
/*
* Preserve the upper four bits of the MSB from the PLOGI response.
@@ -247,7 +254,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int rc;
memset(&stat, 0, sizeof (struct ls_rjt));
- if (vport->port_state <= LPFC_FLOGI) {
+ if (vport->port_state <= LPFC_FDISC) {
/* Before responding to PLOGI, check for pt2pt mode.
* If we are pt2pt, with an outstanding FLOGI, abort
* the FLOGI and resend it first.
@@ -295,7 +302,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
NULL);
return 0;
}
- if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3) == 0)) {
+ if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 0) == 0)) {
/* Reject this request because invalid parameters */
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
@@ -831,7 +838,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
"0142 PLOGI RSP: Invalid WWN.\n");
goto out;
}
- if (!lpfc_check_sparm(vport, ndlp, sp, CLASS3))
+ if (!lpfc_check_sparm(vport, ndlp, sp, CLASS3, 0))
goto out;
/* PLOGI chkparm OK */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index a246410ce9d..7f21b47db79 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -626,6 +626,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
&phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
if (psb->cur_iocbq.sli4_xritag == xri) {
list_del(&psb->list);
+ psb->exch_busy = 0;
psb->status = IOSTAT_SUCCESS;
spin_unlock_irqrestore(
&phba->sli4_hba.abts_scsi_buf_list_lock,
@@ -688,11 +689,12 @@ lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba)
list);
if (status) {
/* Put this back on the abort scsi list */
- psb->status = IOSTAT_LOCAL_REJECT;
- psb->result = IOERR_ABORT_REQUESTED;
+ psb->exch_busy = 1;
rc++;
- } else
+ } else {
+ psb->exch_busy = 0;
psb->status = IOSTAT_SUCCESS;
+ }
/* Put it back into the SCSI buffer list */
lpfc_release_scsi_buf_s4(phba, psb);
}
@@ -796,19 +798,17 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
*/
sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
- bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_cmnd));
bf_set(lpfc_sli4_sge_last, sgl, 0);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd));
sgl++;
/* Setup the physical region for the FCP RSP */
sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
- bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_rsp));
bf_set(lpfc_sli4_sge_last, sgl, 1);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp));
/*
* Since the IOCB for the FCP I/O is built into this
@@ -839,11 +839,12 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
psb->cur_iocbq.sli4_xritag);
if (status) {
/* Put this back on the abort scsi list */
- psb->status = IOSTAT_LOCAL_REJECT;
- psb->result = IOERR_ABORT_REQUESTED;
+ psb->exch_busy = 1;
rc++;
- } else
+ } else {
+ psb->exch_busy = 0;
psb->status = IOSTAT_SUCCESS;
+ }
/* Put it back into the SCSI buffer list */
lpfc_release_scsi_buf_s4(phba, psb);
break;
@@ -857,11 +858,12 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
list);
if (status) {
/* Put this back on the abort scsi list */
- psb->status = IOSTAT_LOCAL_REJECT;
- psb->result = IOERR_ABORT_REQUESTED;
+ psb->exch_busy = 1;
rc++;
- } else
+ } else {
+ psb->exch_busy = 0;
psb->status = IOSTAT_SUCCESS;
+ }
/* Put it back into the SCSI buffer list */
lpfc_release_scsi_buf_s4(phba, psb);
}
@@ -951,8 +953,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
unsigned long iflag = 0;
- if (psb->status == IOSTAT_LOCAL_REJECT
- && psb->result == IOERR_ABORT_REQUESTED) {
+ if (psb->exch_busy) {
spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
iflag);
psb->pCmd = NULL;
@@ -1869,7 +1870,6 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
physaddr = sg_dma_address(sgel);
dma_len = sg_dma_len(sgel);
- bf_set(lpfc_sli4_sge_len, sgl, sg_dma_len(sgel));
sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
if ((num_bde + 1) == nseg)
@@ -1878,7 +1878,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
bf_set(lpfc_sli4_sge_last, sgl, 0);
bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ sgl->sge_len = cpu_to_le32(dma_len);
dma_offset += dma_len;
sgl++;
}
@@ -2221,6 +2221,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
+ /* pick up SLI4 exhange busy status from HBA */
+ lpfc_cmd->exch_busy = pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY;
+
if (pnode && NLP_CHK_NODE_ACT(pnode))
atomic_dec(&pnode->cmd_pending);
@@ -2637,6 +2640,7 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
}
phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf;
phba->lpfc_rampdown_queue_depth = lpfc_rampdown_queue_depth;
+ phba->lpfc_scsi_cmd_iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
return 0;
}
@@ -2695,6 +2699,13 @@ lpfc_info(struct Scsi_Host *host)
" port %s",
phba->Port);
}
+ len = strlen(lpfcinfobuf);
+ if (phba->sli4_hba.link_state.logical_speed) {
+ snprintf(lpfcinfobuf + len,
+ 384-len,
+ " Logical Link Speed: %d Mbps",
+ phba->sli4_hba.link_state.logical_speed * 10);
+ }
}
return lpfcinfobuf;
}
@@ -2990,6 +3001,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
/* ABTS WQE must go to the same WQ as the WQE to be aborted */
abtsiocb->fcp_wqidx = iocb->fcp_wqidx;
+ abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX;
if (lpfc_is_link_up(phba))
icmd->ulpCommand = CMD_ABORT_XRI_CN;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 65dfc8bd5b4..5932273870a 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -118,6 +118,7 @@ struct lpfc_scsi_buf {
uint32_t timeout;
+ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */
uint16_t status; /* From IOCB Word 7- ulpStatus */
uint32_t result; /* From IOCB Word 4. */
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 589549b2bf0..35e3b96d4e0 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -580,10 +580,7 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
else
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.un.ulpWord[4]
- == IOERR_ABORT_REQUESTED))) {
+ if (iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) {
spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
iflag);
list_add(&sglq->list,
@@ -764,10 +761,6 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
case DSSCMD_IWRITE64_CX:
case DSSCMD_IREAD64_CR:
case DSSCMD_IREAD64_CX:
- case DSSCMD_INVALIDATE_DEK:
- case DSSCMD_SET_KEK:
- case DSSCMD_GET_KEK_ID:
- case DSSCMD_GEN_XFER:
type = LPFC_SOL_IOCB;
break;
case CMD_ABORT_XRI_CN:
@@ -1717,6 +1710,7 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
struct lpfc_dmabuf *mp;
uint16_t rpi, vpi;
int rc;
+ struct lpfc_vport *vport = pmb->vport;
mp = (struct lpfc_dmabuf *) (pmb->context1);
@@ -1745,6 +1739,18 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
return;
}
+ /* Unreg VPI, if the REG_VPI succeed after VLink failure */
+ if ((pmb->u.mb.mbxCommand == MBX_REG_VPI) &&
+ !(phba->pport->load_flag & FC_UNLOADING) &&
+ !pmb->u.mb.mbxStatus) {
+ lpfc_unreg_vpi(phba, pmb->u.mb.un.varRegVpi.vpi, pmb);
+ pmb->vport = vport;
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc != MBX_NOT_FINISHED)
+ return;
+ }
+
if (bf_get(lpfc_mqe_command, &pmb->u.mqe) == MBX_SLI4_CONFIG)
lpfc_sli4_mbox_cmd_free(phba, pmb);
else
@@ -2228,9 +2234,15 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* All other are passed to the completion callback.
*/
if (pring->ringno == LPFC_ELS_RING) {
- if (cmdiocbp->iocb_flag & LPFC_DRIVER_ABORTED) {
+ if ((phba->sli_rev < LPFC_SLI_REV4) &&
+ (cmdiocbp->iocb_flag &
+ LPFC_DRIVER_ABORTED)) {
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
cmdiocbp->iocb_flag &=
~LPFC_DRIVER_ABORTED;
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
saveq->iocb.ulpStatus =
IOSTAT_LOCAL_REJECT;
saveq->iocb.un.ulpWord[4] =
@@ -2240,7 +2252,47 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* of DMAing payload, so don't free data
* buffer till after a hbeat.
*/
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
saveq->iocb_flag |= LPFC_DELAY_MEM_FREE;
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
+ }
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (saveq->iocb_flag & LPFC_EXCHANGE_BUSY)) {
+ /* Set cmdiocb flag for the exchange
+ * busy so sgl (xri) will not be
+ * released until the abort xri is
+ * received from hba, clear the
+ * LPFC_DRIVER_ABORTED bit in case
+ * it was driver initiated abort.
+ */
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
+ cmdiocbp->iocb_flag &=
+ ~LPFC_DRIVER_ABORTED;
+ cmdiocbp->iocb_flag |=
+ LPFC_EXCHANGE_BUSY;
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
+ cmdiocbp->iocb.ulpStatus =
+ IOSTAT_LOCAL_REJECT;
+ cmdiocbp->iocb.un.ulpWord[4] =
+ IOERR_ABORT_REQUESTED;
+ /*
+ * For SLI4, irsiocb contains NO_XRI
+ * in sli_xritag, it shall not affect
+ * releasing sgl (xri) process.
+ */
+ saveq->iocb.ulpStatus =
+ IOSTAT_LOCAL_REJECT;
+ saveq->iocb.un.ulpWord[4] =
+ IOERR_SLI_ABORTED;
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
+ saveq->iocb_flag |= LPFC_DELAY_MEM_FREE;
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
}
}
(cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
@@ -5687,19 +5739,19 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
for (i = 0; i < numBdes; i++) {
/* Should already be byte swapped. */
- sgl->addr_hi = bpl->addrHigh;
- sgl->addr_lo = bpl->addrLow;
- /* swap the size field back to the cpu so we
- * can assign it to the sgl.
- */
- bde.tus.w = le32_to_cpu(bpl->tus.w);
- bf_set(lpfc_sli4_sge_len, sgl, bde.tus.f.bdeSize);
+ sgl->addr_hi = bpl->addrHigh;
+ sgl->addr_lo = bpl->addrLow;
+
if ((i+1) == numBdes)
bf_set(lpfc_sli4_sge_last, sgl, 1);
else
bf_set(lpfc_sli4_sge_last, sgl, 0);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ /* swap the size field back to the cpu so we
+ * can assign it to the sgl.
+ */
+ bde.tus.w = le32_to_cpu(bpl->tus.w);
+ sgl->sge_len = cpu_to_le32(bde.tus.f.bdeSize);
bpl++;
sgl++;
}
@@ -5712,11 +5764,10 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
cpu_to_le32(icmd->un.genreq64.bdl.addrHigh);
sgl->addr_lo =
cpu_to_le32(icmd->un.genreq64.bdl.addrLow);
- bf_set(lpfc_sli4_sge_len, sgl,
- icmd->un.genreq64.bdl.bdeSize);
bf_set(lpfc_sli4_sge_last, sgl, 1);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ sgl->sge_len =
+ cpu_to_le32(icmd->un.genreq64.bdl.bdeSize);
}
return sglq->sli4_xritag;
}
@@ -5987,12 +6038,10 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
else
bf_set(abort_cmd_ia, &wqe->abort_cmd, 0);
bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG);
- abort_tag = iocbq->iocb.un.acxri.abortIoTag;
wqe->words[5] = 0;
bf_set(lpfc_wqe_gen_ct, &wqe->generic,
((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
abort_tag = iocbq->iocb.un.acxri.abortIoTag;
- wqe->generic.abort_tag = abort_tag;
/*
* The abort handler will send us CMD_ABORT_XRI_CN or
* CMD_CLOSE_XRI_CN and the fw only accepts CMD_ABORT_XRI_CX
@@ -6121,15 +6170,15 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe))
return IOCB_ERROR;
- if (piocb->iocb_flag & LPFC_IO_FCP) {
+ if ((piocb->iocb_flag & LPFC_IO_FCP) ||
+ (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
/*
* 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))
+ if (piocb->iocb_flag & LPFC_IO_FCP)
piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
&wqe))
@@ -7004,7 +7053,14 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
abort_iocb->iocb.ulpContext != abort_context ||
(abort_iocb->iocb_flag & LPFC_DRIVER_ABORTED) == 0)
spin_unlock_irq(&phba->hbalock);
- else {
+ else if (phba->sli_rev < LPFC_SLI_REV4) {
+ /*
+ * leave the SLI4 aborted command on the txcmplq
+ * list and the command complete WCQE's XB bit
+ * will tell whether the SGL (XRI) can be released
+ * immediately or to the aborted SGL list for the
+ * following abort XRI from the HBA.
+ */
list_del_init(&abort_iocb->list);
pring->txcmplq_cnt--;
spin_unlock_irq(&phba->hbalock);
@@ -7013,11 +7069,13 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* payload, so don't free data buffer till after
* a hbeat.
*/
+ spin_lock_irq(&phba->hbalock);
abort_iocb->iocb_flag |= LPFC_DELAY_MEM_FREE;
-
abort_iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED;
+ spin_unlock_irq(&phba->hbalock);
+
abort_iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
- abort_iocb->iocb.un.ulpWord[4] = IOERR_SLI_ABORTED;
+ abort_iocb->iocb.un.ulpWord[4] = IOERR_ABORT_REQUESTED;
(abort_iocb->iocb_cmpl)(phba, abort_iocb, abort_iocb);
}
}
@@ -7106,7 +7164,7 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
return 0;
/* This signals the response to set the correct status
- * before calling the completion handler.
+ * before calling the completion handler
*/
cmdiocb->iocb_flag |= LPFC_DRIVER_ABORTED;
@@ -7124,6 +7182,8 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* ABTS WQE must go to the same WQ as the WQE to be aborted */
abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx;
+ if (cmdiocb->iocb_flag & LPFC_IO_FCP)
+ abtsiocbp->iocb_flag |= LPFC_USE_FCPWQIDX;
if (phba->link_state >= LPFC_LINK_UP)
iabt->ulpCommand = CMD_ABORT_XRI_CN;
@@ -7330,6 +7390,8 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
/* ABTS WQE must go to the same WQ as the WQE to be aborted */
abtsiocb->fcp_wqidx = iocbq->fcp_wqidx;
+ if (iocbq->iocb_flag & LPFC_IO_FCP)
+ abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX;
if (lpfc_is_link_up(phba))
abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
@@ -8359,11 +8421,24 @@ void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *phba)
}
}
+/**
+ * lpfc_sli4_iocb_param_transfer - Transfer pIocbOut and cmpl status to pIocbIn
+ * @phba: pointer to lpfc hba data structure
+ * @pIocbIn: pointer to the rspiocbq
+ * @pIocbOut: pointer to the cmdiocbq
+ * @wcqe: pointer to the complete wcqe
+ *
+ * This routine transfers the fields of a command iocbq to a response iocbq
+ * by copying all the IOCB fields from command iocbq and transferring the
+ * completion status information from the complete wcqe.
+ **/
static void
-lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
+lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
+ struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut,
struct lpfc_wcqe_complete *wcqe)
{
+ unsigned long iflags;
size_t offset = offsetof(struct lpfc_iocbq, iocb);
memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
@@ -8377,8 +8452,17 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
wcqe->total_data_placed;
else
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
- else
+ else {
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
+ pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed;
+ }
+
+ /* Pick up HBA exchange busy condition */
+ if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pIocbIn->iocb_flag |= LPFC_EXCHANGE_BUSY;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
}
/**
@@ -8419,7 +8503,7 @@ lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba,
}
/* Fake the irspiocbq and copy necessary response information */
- lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
+ lpfc_sli4_iocb_param_transfer(phba, irspiocbq, cmdiocbq, wcqe);
return irspiocbq;
}
@@ -8849,8 +8933,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
int ecount = 0;
uint16_t cqid;
- if (bf_get(lpfc_eqe_major_code, eqe) != 0 ||
- bf_get(lpfc_eqe_minor_code, eqe) != 0) {
+ if (bf_get(lpfc_eqe_major_code, eqe) != 0) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0359 Not a valid slow-path completion "
"event: majorcode=x%x, minorcode=x%x\n",
@@ -8976,7 +9059,7 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,
}
/* Fake the irspiocb and copy necessary response information */
- lpfc_sli4_iocb_param_transfer(&irspiocbq, cmdiocbq, wcqe);
+ lpfc_sli4_iocb_param_transfer(phba, &irspiocbq, cmdiocbq, wcqe);
/* Pass the cmd_iocb and the rsp state to the upper layer */
(cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq);
@@ -9082,8 +9165,7 @@ lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
uint16_t cqid;
int ecount = 0;
- if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0) ||
- unlikely(bf_get(lpfc_eqe_minor_code, eqe) != 0)) {
+ if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0)) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0366 Not a valid fast-path completion "
"event: majorcode=x%x, minorcode=x%x\n",
@@ -11871,12 +11953,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
{
int rc = 0, error;
LPFC_MBOXQ_t *mboxq;
- void *virt_addr;
- dma_addr_t phys_addr;
- uint8_t *bytep;
- struct lpfc_mbx_sge sge;
- uint32_t alloc_len, req_len;
- struct lpfc_mbx_read_fcf_tbl *read_fcf;
phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag;
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
@@ -11887,43 +11963,19 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
error = -ENOMEM;
goto fail_fcfscan;
}
-
- req_len = sizeof(struct fcf_record) +
- sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t);
-
- /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */
- alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len,
- LPFC_SLI4_MBX_NEMBED);
-
- if (alloc_len < req_len) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0291 Allocated DMA memory size (x%x) is "
- "less than the requested DMA memory "
- "size (x%x)\n", alloc_len, req_len);
- error = -ENOMEM;
+ /* Construct the read FCF record mailbox command */
+ rc = lpfc_sli4_mbx_read_fcf_record(phba, mboxq, fcf_index);
+ if (rc) {
+ error = -EINVAL;
goto fail_fcfscan;
}
-
- /* Get the first SGE entry from the non-embedded DMA memory. This
- * routine only uses a single SGE.
- */
- lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
- phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
- virt_addr = mboxq->sge_array->addr[0];
- read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
-
- /* Set up command fields */
- bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index);
- /* Perform necessary endian conversion */
- bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
- lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t));
+ /* Issue the mailbox command asynchronously */
mboxq->vport = phba->pport;
mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
+ if (rc == MBX_NOT_FINISHED)
error = -EIO;
- } else {
+ else {
spin_lock_irq(&phba->hbalock);
phba->hba_flag |= FCF_DISC_INPROGRESS;
spin_unlock_irq(&phba->hbalock);
@@ -11942,6 +11994,90 @@ fail_fcfscan:
}
/**
+ * lpfc_mbx_cmpl_redisc_fcf_table - completion routine for rediscover FCF table
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is the completion routine for the rediscover FCF table mailbox
+ * command. If the mailbox command returned failure, it will try to stop the
+ * FCF rediscover wait timer.
+ **/
+void
+lpfc_mbx_cmpl_redisc_fcf_table(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf;
+ uint32_t shdr_status, shdr_add_status;
+
+ redisc_fcf = &mbox->u.mqe.un.redisc_fcf_tbl;
+
+ shdr_status = bf_get(lpfc_mbox_hdr_status,
+ &redisc_fcf->header.cfg_shdr.response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &redisc_fcf->header.cfg_shdr.response);
+ if (shdr_status || shdr_add_status) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2746 Requesting for FCF rediscovery failed "
+ "status x%x add_status x%x\n",
+ shdr_status, shdr_add_status);
+ /*
+ * Request failed, last resort to re-try current
+ * registered FCF entry
+ */
+ lpfc_retry_pport_discovery(phba);
+ } else
+ /*
+ * Start FCF rediscovery wait timer for pending FCF
+ * before rescan FCF record table.
+ */
+ lpfc_fcf_redisc_wait_start_timer(phba);
+
+ mempool_free(mbox, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_redisc_all_fcf - Request to rediscover entire FCF table by port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to request for rediscovery of the entire FCF table
+ * by the port.
+ **/
+int
+lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mbox;
+ struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf;
+ int rc, length;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2745 Failed to allocate mbox for "
+ "requesting FCF rediscover.\n");
+ return -ENOMEM;
+ }
+
+ length = (sizeof(struct lpfc_mbx_redisc_fcf_tbl) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF,
+ length, LPFC_SLI4_MBX_EMBED);
+
+ redisc_fcf = &mbox->u.mqe.un.redisc_fcf_tbl;
+ /* Set count to 0 for invalidating the entire FCF database */
+ bf_set(lpfc_mbx_redisc_fcf_count, redisc_fcf, 0);
+
+ /* Issue the mailbox command asynchronously */
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_redisc_fcf_table;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
* lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled.
* @phba: pointer to lpfc hba data structure.
*
@@ -12069,3 +12205,48 @@ out:
kfree(rgn23_data);
return;
}
+
+/**
+ * lpfc_cleanup_pending_mbox - Free up vport discovery mailbox commands.
+ * @vport: pointer to vport data structure.
+ *
+ * This function iterate through the mailboxq and clean up all REG_LOGIN
+ * and REG_VPI mailbox commands associated with the vport. This function
+ * is called when driver want to restart discovery of the vport due to
+ * a Clear Virtual Link event.
+ **/
+void
+lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ LPFC_MBOXQ_t *mb, *nextmb;
+ struct lpfc_dmabuf *mp;
+
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
+ if (mb->vport != vport)
+ continue;
+
+ if ((mb->u.mb.mbxCommand != MBX_REG_LOGIN64) &&
+ (mb->u.mb.mbxCommand != MBX_REG_VPI))
+ continue;
+
+ if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) {
+ mp = (struct lpfc_dmabuf *) (mb->context1);
+ if (mp) {
+ __lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ }
+ list_del(&mb->list);
+ mempool_free(mb, phba->mbox_mem_pool);
+ }
+ mb = phba->sli.mbox_active;
+ if (mb && (mb->vport == vport)) {
+ if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) ||
+ (mb->u.mb.mbxCommand == MBX_REG_VPI))
+ mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ }
+ spin_unlock_irq(&phba->hbalock);
+}
+
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index ba38de3c28f..dfcf5437d1f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -53,17 +53,19 @@ struct lpfc_iocbq {
IOCB_t iocb; /* IOCB cmd */
uint8_t retry; /* retry counter for IOCB cmd - if needed */
- uint8_t iocb_flag;
+ uint16_t iocb_flag;
#define LPFC_IO_LIBDFC 1 /* libdfc iocb */
#define LPFC_IO_WAKE 2 /* High Priority Queue signal flag */
#define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */
#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_ID_MASK 0xc0 /* ELS_ID range 0-3 */
-#define LPFC_FIP_ELS_ID_SHIFT 6
+#define LPFC_EXCHANGE_BUSY 0x40 /* SLI4 hba reported XB in response */
+#define LPFC_USE_FCPWQIDX 0x80 /* Submit to specified FCPWQ index */
+
+#define LPFC_FIP_ELS_ID_MASK 0xc000 /* ELS_ID range 0-3, non-shifted mask */
+#define LPFC_FIP_ELS_ID_SHIFT 14
- uint8_t abort_count;
uint8_t rsvd2;
uint32_t drvrTimeout; /* driver timeout in seconds */
uint32_t fcp_wqidx; /* index to FCP work queue */
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 44e5f574236..86308836600 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -22,6 +22,10 @@
#define LPFC_RELEASE_NOTIFICATION_INTERVAL 32
#define LPFC_GET_QE_REL_INT 32
#define LPFC_RPI_LOW_WATER_MARK 10
+
+/* Amount of time in seconds for waiting FCF rediscovery to complete */
+#define LPFC_FCF_REDISCOVER_WAIT_TMO 2000 /* msec */
+
/* Number of SGL entries can be posted in a 4KB nonembedded mbox command */
#define LPFC_NEMBED_MBOX_SGL_CNT 254
@@ -126,24 +130,36 @@ struct lpfc_sli4_link {
uint8_t status;
uint8_t physical;
uint8_t fault;
+ uint16_t logical_speed;
};
-struct lpfc_fcf {
- uint8_t fabric_name[8];
- uint8_t switch_name[8];
+struct lpfc_fcf_rec {
+ uint8_t fabric_name[8];
+ uint8_t switch_name[8];
uint8_t mac_addr[6];
uint16_t fcf_indx;
+ uint32_t priority;
+ uint16_t vlan_id;
+ uint32_t addr_mode;
+ uint32_t flag;
+#define BOOT_ENABLE 0x01
+#define RECORD_VALID 0x02
+};
+
+struct lpfc_fcf {
uint16_t fcfi;
uint32_t fcf_flag;
#define FCF_AVAILABLE 0x01 /* FCF available for discovery */
#define FCF_REGISTERED 0x02 /* FCF registered with FW */
-#define FCF_DISCOVERED 0x04 /* FCF discovery started */
-#define FCF_BOOT_ENABLE 0x08 /* Boot bios use this FCF */
-#define FCF_IN_USE 0x10 /* Atleast one discovery completed */
-#define FCF_VALID_VLAN 0x20 /* Use the vlan id specified */
- uint32_t priority;
+#define FCF_SCAN_DONE 0x04 /* FCF table scan done */
+#define FCF_IN_USE 0x08 /* Atleast one discovery completed */
+#define FCF_REDISC_PEND 0x10 /* FCF rediscovery pending */
+#define FCF_REDISC_EVT 0x20 /* FCF rediscovery event to worker thread */
+#define FCF_REDISC_FOV 0x40 /* Post FCF rediscovery fast failover */
uint32_t addr_mode;
- uint16_t vlan_id;
+ struct lpfc_fcf_rec current_rec;
+ struct lpfc_fcf_rec failover_rec;
+ struct timer_list redisc_wait;
};
#define LPFC_REGION23_SIGNATURE "RG23"
@@ -248,7 +264,10 @@ struct lpfc_bmbx {
#define SLI4_CT_VFI 2
#define SLI4_CT_FCFI 3
-#define LPFC_SLI4_MAX_SEGMENT_SIZE 0x10000
+#define LPFC_SLI4_FL1_MAX_SEGMENT_SIZE 0x10000
+#define LPFC_SLI4_FL1_MAX_BUF_SIZE 0X2000
+#define LPFC_SLI4_MIN_BUF_SIZE 0x400
+#define LPFC_SLI4_MAX_BUF_SIZE 0x20000
/*
* SLI4 specific data structures
@@ -282,6 +301,42 @@ struct lpfc_fcp_eq_hdl {
struct lpfc_hba *phba;
};
+/* Port Capabilities for SLI4 Parameters */
+struct lpfc_pc_sli4_params {
+ uint32_t supported;
+ uint32_t if_type;
+ uint32_t sli_rev;
+ uint32_t sli_family;
+ uint32_t featurelevel_1;
+ uint32_t featurelevel_2;
+ uint32_t proto_types;
+#define LPFC_SLI4_PROTO_FCOE 0x0000001
+#define LPFC_SLI4_PROTO_FC 0x0000002
+#define LPFC_SLI4_PROTO_NIC 0x0000004
+#define LPFC_SLI4_PROTO_ISCSI 0x0000008
+#define LPFC_SLI4_PROTO_RDMA 0x0000010
+ uint32_t sge_supp_len;
+ uint32_t if_page_sz;
+ uint32_t rq_db_window;
+ uint32_t loopbk_scope;
+ uint32_t eq_pages_max;
+ uint32_t eqe_size;
+ uint32_t cq_pages_max;
+ uint32_t cqe_size;
+ uint32_t mq_pages_max;
+ uint32_t mqe_size;
+ uint32_t mq_elem_cnt;
+ uint32_t wq_pages_max;
+ uint32_t wqe_size;
+ uint32_t rq_pages_max;
+ uint32_t rqe_size;
+ uint32_t hdr_pages_max;
+ uint32_t hdr_size;
+ uint32_t hdr_pp_align;
+ uint32_t sgl_pages_max;
+ uint32_t sgl_pp_align;
+};
+
/* SLI4 HBA data structure entries */
struct lpfc_sli4_hba {
void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
@@ -295,7 +350,7 @@ struct lpfc_sli4_hba {
void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */
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 */
+ void __iomem *SLIINTFregaddr; /* Address to SLI_INTF register */
/* BAR1 FCoE function CSR register memory map */
void __iomem *STAregaddr; /* Address to HST_STATE register */
void __iomem *ISRregaddr; /* Address to HST_ISR register */
@@ -310,6 +365,8 @@ struct lpfc_sli4_hba {
uint32_t ue_mask_lo;
uint32_t ue_mask_hi;
+ struct lpfc_register sli_intf;
+ struct lpfc_pc_sli4_params pc_sli4_params;
struct msix_entry *msix_entries;
uint32_t cfg_eqn;
struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
@@ -406,6 +463,8 @@ void lpfc_sli4_mbox_cmd_free(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_sli4_mbx_sge_set(struct lpfcMboxq *, uint32_t, dma_addr_t, uint32_t);
void lpfc_sli4_mbx_sge_get(struct lpfcMboxq *, uint32_t,
struct lpfc_mbx_sge *);
+int lpfc_sli4_mbx_read_fcf_record(struct lpfc_hba *, struct lpfcMboxq *,
+ uint16_t);
void lpfc_sli4_hba_reset(struct lpfc_hba *);
struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t,
@@ -448,6 +507,7 @@ int lpfc_sli4_alloc_rpi(struct lpfc_hba *);
void lpfc_sli4_free_rpi(struct lpfc_hba *, int);
void lpfc_sli4_remove_rpis(struct lpfc_hba *);
void lpfc_sli4_async_event_proc(struct lpfc_hba *);
+void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *);
int lpfc_sli4_resume_rpi(struct lpfc_nodelist *);
void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *);
void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *);
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 792f72263f1..ac276aa46fb 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.7"
+#define LPFC_DRIVER_VERSION "8.3.9"
#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 e3c7fa64230..dc86e873102 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->vpi_state & LPFC_VPI_REGISTERED)) {
+ (pport->fc_flag & FC_VFI_REGISTERED)) {
rc = lpfc_sli4_init_vpi(phba, vpi);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
@@ -505,6 +505,7 @@ enable_vport(struct fc_vport *fc_vport)
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp = NULL;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if ((phba->link_state < LPFC_LINK_UP) ||
(phba->fc_topology == TOPOLOGY_LOOP)) {
@@ -512,10 +513,10 @@ enable_vport(struct fc_vport *fc_vport)
return VPORT_OK;
}
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(shost->host_lock);
vport->load_flag |= FC_LOADING;
vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(shost->host_lock);
/* Use the Physical nodes Fabric NDLP to determine if the link is
* up and ready to FDISC.
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
index c24e86f0780..4a90eaf7cb6 100644
--- a/drivers/scsi/mac_esp.c
+++ b/drivers/scsi/mac_esp.c
@@ -22,7 +22,6 @@
#include <asm/irq.h>
#include <asm/dma.h>
-
#include <asm/macints.h>
#include <asm/macintosh.h>
@@ -53,7 +52,6 @@ struct mac_esp_priv {
void __iomem *pdma_io;
int error;
};
-static struct platform_device *internal_pdev, *external_pdev;
static struct esp *esp_chips[2];
#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
@@ -279,24 +277,27 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
* Programmed IO routines follow.
*/
-static inline int mac_esp_wait_for_fifo(struct esp *esp)
+static inline unsigned int mac_esp_wait_for_fifo(struct esp *esp)
{
int i = 500000;
do {
- if (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES)
- return 0;
+ unsigned int fbytes = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+ if (fbytes)
+ return fbytes;
udelay(2);
} while (--i);
printk(KERN_ERR PFX "FIFO is empty (sreg %02x)\n",
esp_read8(ESP_STATUS));
- return 1;
+ return 0;
}
static inline int mac_esp_wait_for_intr(struct esp *esp)
{
+ struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
int i = 500000;
do {
@@ -308,6 +309,7 @@ static inline int mac_esp_wait_for_intr(struct esp *esp)
} while (--i);
printk(KERN_ERR PFX "IRQ timeout (sreg %02x)\n", esp->sreg);
+ mep->error = 1;
return 1;
}
@@ -347,11 +349,10 @@ static inline int mac_esp_wait_for_intr(struct esp *esp)
static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
u32 dma_count, int write, u8 cmd)
{
- unsigned long flags;
struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
u8 *fifo = esp->regs + ESP_FDATA * 16;
- local_irq_save(flags);
+ disable_irq(esp->host->irq);
cmd &= ~ESP_CMD_DMA;
mep->error = 0;
@@ -359,11 +360,35 @@ static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
if (write) {
scsi_esp_cmd(esp, cmd);
- if (!mac_esp_wait_for_intr(esp)) {
- if (mac_esp_wait_for_fifo(esp))
- esp_count = 0;
- } else {
- esp_count = 0;
+ while (1) {
+ unsigned int n;
+
+ n = mac_esp_wait_for_fifo(esp);
+ if (!n)
+ break;
+
+ if (n > esp_count)
+ n = esp_count;
+ esp_count -= n;
+
+ MAC_ESP_PIO_LOOP("%2@,%0@+", n);
+
+ if (!esp_count)
+ break;
+
+ if (mac_esp_wait_for_intr(esp))
+ break;
+
+ if (((esp->sreg & ESP_STAT_PMASK) != ESP_DIP) &&
+ ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP))
+ break;
+
+ esp->ireg = esp_read8(ESP_INTRPT);
+ if ((esp->ireg & (ESP_INTR_DC | ESP_INTR_BSERV)) !=
+ ESP_INTR_BSERV)
+ break;
+
+ scsi_esp_cmd(esp, ESP_CMD_TI);
}
} else {
scsi_esp_cmd(esp, ESP_CMD_FLUSH);
@@ -374,47 +399,24 @@ static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
MAC_ESP_PIO_LOOP("%0@+,%2@", esp_count);
scsi_esp_cmd(esp, cmd);
- }
-
- while (esp_count) {
- unsigned int n;
- if (mac_esp_wait_for_intr(esp)) {
- mep->error = 1;
- break;
- }
-
- if (esp->sreg & ESP_STAT_SPAM) {
- printk(KERN_ERR PFX "gross error\n");
- mep->error = 1;
- break;
- }
-
- n = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
-
- if (write) {
- if (n > esp_count)
- n = esp_count;
- esp_count -= n;
-
- MAC_ESP_PIO_LOOP("%2@,%0@+", n);
+ while (esp_count) {
+ unsigned int n;
- if ((esp->sreg & ESP_STAT_PMASK) == ESP_STATP)
+ if (mac_esp_wait_for_intr(esp))
break;
- if (esp_count) {
- esp->ireg = esp_read8(ESP_INTRPT);
- if (esp->ireg & ESP_INTR_DC)
- break;
+ if (((esp->sreg & ESP_STAT_PMASK) != ESP_DOP) &&
+ ((esp->sreg & ESP_STAT_PMASK) != ESP_MOP))
+ break;
- scsi_esp_cmd(esp, ESP_CMD_TI);
- }
- } else {
esp->ireg = esp_read8(ESP_INTRPT);
- if (esp->ireg & ESP_INTR_DC)
+ if ((esp->ireg & (ESP_INTR_DC | ESP_INTR_BSERV)) !=
+ ESP_INTR_BSERV)
break;
- n = MAC_ESP_FIFO_SIZE - n;
+ n = MAC_ESP_FIFO_SIZE -
+ (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES);
if (n > esp_count)
n = esp_count;
@@ -429,7 +431,7 @@ static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
}
}
- local_irq_restore(flags);
+ enable_irq(esp->host->irq);
}
static int mac_esp_irq_pending(struct esp *esp)
@@ -492,29 +494,12 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
struct Scsi_Host *host;
struct esp *esp;
int err;
- int chips_present;
struct mac_esp_priv *mep;
if (!MACH_IS_MAC)
return -ENODEV;
- switch (macintosh_config->scsi_type) {
- case MAC_SCSI_QUADRA:
- case MAC_SCSI_QUADRA3:
- chips_present = 1;
- break;
- case MAC_SCSI_QUADRA2:
- if ((macintosh_config->ident == MAC_MODEL_Q900) ||
- (macintosh_config->ident == MAC_MODEL_Q950))
- chips_present = 2;
- else
- chips_present = 1;
- break;
- default:
- chips_present = 0;
- }
-
- if (dev->id + 1 > chips_present)
+ if (dev->id > 1)
return -ENODEV;
host = scsi_host_alloc(tpnt, sizeof(struct esp));
@@ -639,55 +624,26 @@ static struct platform_driver esp_mac_driver = {
.probe = esp_mac_probe,
.remove = __devexit_p(esp_mac_remove),
.driver = {
- .name = DRV_MODULE_NAME,
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
},
};
static int __init mac_esp_init(void)
{
- int err;
-
- err = platform_driver_register(&esp_mac_driver);
- if (err)
- return err;
-
- internal_pdev = platform_device_alloc(DRV_MODULE_NAME, 0);
- if (internal_pdev && platform_device_add(internal_pdev)) {
- platform_device_put(internal_pdev);
- internal_pdev = NULL;
- }
- external_pdev = platform_device_alloc(DRV_MODULE_NAME, 1);
- if (external_pdev && platform_device_add(external_pdev)) {
- platform_device_put(external_pdev);
- external_pdev = NULL;
- }
-
- if (internal_pdev || external_pdev) {
- return 0;
- } else {
- platform_driver_unregister(&esp_mac_driver);
- return -ENOMEM;
- }
+ return platform_driver_register(&esp_mac_driver);
}
static void __exit mac_esp_exit(void)
{
platform_driver_unregister(&esp_mac_driver);
-
- if (internal_pdev) {
- platform_device_unregister(internal_pdev);
- internal_pdev = NULL;
- }
- if (external_pdev) {
- platform_device_unregister(external_pdev);
- external_pdev = NULL;
- }
}
MODULE_DESCRIPTION("Mac ESP SCSI driver");
MODULE_AUTHOR("Finn Thain <fthain@telegraphics.com.au>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
module_init(mac_esp_init);
module_exit(mac_esp_exit);
diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c
index 708ea3157b6..409648f5845 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.12-rc1
+ * Version : v00.00.04.17.1-rc1
*
* Authors:
* (email-id : megaraidlinux@lsi.com)
@@ -843,6 +843,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
pthru->lun = scp->device->lun;
pthru->cdb_len = scp->cmd_len;
pthru->timeout = 0;
+ pthru->pad_0 = 0;
pthru->flags = flags;
pthru->data_xfer_len = scsi_bufflen(scp);
@@ -874,6 +875,12 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
pthru->sge_count = megasas_make_sgl32(instance, scp,
&pthru->sgl);
+ if (pthru->sge_count > instance->max_num_sge) {
+ printk(KERN_ERR "megasas: DCDB two many SGE NUM=%x\n",
+ pthru->sge_count);
+ return 0;
+ }
+
/*
* Sense info specific
*/
@@ -1000,6 +1007,12 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
} else
ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl);
+ if (ldio->sge_count > instance->max_num_sge) {
+ printk(KERN_ERR "megasas: build_ld_io: sge_count = %x\n",
+ ldio->sge_count);
+ return 0;
+ }
+
/*
* Sense info specific
*/
@@ -2250,6 +2263,7 @@ megasas_get_pd_list(struct megasas_instance *instance)
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
+ dcmd->pad_0 = 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;
@@ -2294,6 +2308,86 @@ megasas_get_pd_list(struct megasas_instance *instance)
return ret;
}
+/*
+ * megasas_get_ld_list_info - Returns FW's ld_list structure
+ * @instance: Adapter soft state
+ * @ld_list: ld_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_ld_list(struct megasas_instance *instance)
+{
+ int ret = 0, ld_index = 0, ids = 0;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_LD_LIST *ci;
+ dma_addr_t ci_h = 0;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ printk(KERN_DEBUG "megasas_get_ld_list: Failed to get cmd\n");
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ ci = pci_alloc_consistent(instance->pdev,
+ sizeof(struct MR_LD_LIST),
+ &ci_h);
+
+ if (!ci) {
+ printk(KERN_DEBUG "Failed to alloc mem in get_ld_list\n");
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ 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 = sizeof(struct MR_LD_LIST);
+ dcmd->opcode = MR_DCMD_LD_GET_LIST;
+ dcmd->sgl.sge32[0].phys_addr = ci_h;
+ dcmd->sgl.sge32[0].length = sizeof(struct MR_LD_LIST);
+ dcmd->pad_0 = 0;
+
+ if (!megasas_issue_polled(instance, cmd)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ /* the following function will get the instance PD LIST */
+
+ if ((ret == 0) && (ci->ldCount < MAX_LOGICAL_DRIVES)) {
+ memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
+
+ for (ld_index = 0; ld_index < ci->ldCount; ld_index++) {
+ if (ci->ldList[ld_index].state != 0) {
+ ids = ci->ldList[ld_index].ref.targetId;
+ instance->ld_ids[ids] =
+ ci->ldList[ld_index].ref.targetId;
+ }
+ }
+ }
+
+ pci_free_consistent(instance->pdev,
+ sizeof(struct MR_LD_LIST),
+ ci,
+ ci_h);
+
+ megasas_return_cmd(instance, cmd);
+ return ret;
+}
+
/**
* megasas_get_controller_info - Returns FW's controller structure
* @instance: Adapter soft state
@@ -2339,6 +2433,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance,
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
dcmd->data_xfer_len = sizeof(struct megasas_ctrl_info);
dcmd->opcode = MR_DCMD_CTRL_GET_INFO;
dcmd->sgl.sge32[0].phys_addr = ci_h;
@@ -2590,6 +2685,9 @@ static int megasas_init_mfi(struct megasas_instance *instance)
(MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
megasas_get_pd_list(instance);
+ memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
+ megasas_get_ld_list(instance);
+
ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
/*
@@ -2714,6 +2812,7 @@ megasas_get_seq_num(struct megasas_instance *instance,
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
dcmd->data_xfer_len = sizeof(struct megasas_evt_log_info);
dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO;
dcmd->sgl.sge32[0].phys_addr = el_info_h;
@@ -2828,6 +2927,7 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
dcmd->data_xfer_len = sizeof(struct megasas_evt_detail);
dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT;
dcmd->mbox.w[0] = seq_num;
@@ -3166,6 +3266,7 @@ static void megasas_flush_cache(struct megasas_instance *instance)
dcmd->sge_count = 0;
dcmd->flags = MFI_FRAME_DIR_NONE;
dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
dcmd->data_xfer_len = 0;
dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH;
dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE;
@@ -3205,6 +3306,7 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
dcmd->sge_count = 0;
dcmd->flags = MFI_FRAME_DIR_NONE;
dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
dcmd->data_xfer_len = 0;
dcmd->opcode = opcode;
@@ -3781,6 +3883,7 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg)
compat_alloc_user_space(sizeof(struct megasas_iocpacket));
int i;
int error = 0;
+ compat_uptr_t ptr;
if (clear_user(ioc, sizeof(*ioc)))
return -EFAULT;
@@ -3793,9 +3896,22 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg)
copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32)))
return -EFAULT;
- for (i = 0; i < MAX_IOCTL_SGE; i++) {
- compat_uptr_t ptr;
+ /*
+ * The sense_ptr is used in megasas_mgmt_fw_ioctl only when
+ * sense_len is not null, so prepare the 64bit value under
+ * the same condition.
+ */
+ if (ioc->sense_len) {
+ void __user **sense_ioc_ptr =
+ (void __user **)(ioc->frame.raw + ioc->sense_off);
+ compat_uptr_t *sense_cioc_ptr =
+ (compat_uptr_t *)(cioc->frame.raw + cioc->sense_off);
+ if (get_user(ptr, sense_cioc_ptr) ||
+ put_user(compat_ptr(ptr), sense_ioc_ptr))
+ return -EFAULT;
+ }
+ for (i = 0; i < MAX_IOCTL_SGE; i++) {
if (get_user(ptr, &cioc->sgl[i].iov_base) ||
put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) ||
copy_in_user(&ioc->sgl[i].iov_len,
@@ -3970,6 +4086,7 @@ megasas_aen_polling(struct work_struct *work)
struct Scsi_Host *host;
struct scsi_device *sdev1;
u16 pd_index = 0;
+ u16 ld_index = 0;
int i, j, doscan = 0;
u32 seq_num;
int error;
@@ -3985,8 +4102,124 @@ megasas_aen_polling(struct work_struct *work)
switch (instance->evt_detail->code) {
case MR_EVT_PD_INSERTED:
+ if (megasas_get_pd_list(instance) == 0) {
+ 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);
+ }
+ }
+ }
+ }
+ doscan = 0;
+ break;
+
case MR_EVT_PD_REMOVED:
+ if (megasas_get_pd_list(instance) == 0) {
+ 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_device_put(sdev1);
+ }
+ } else {
+ if (sdev1) {
+ scsi_remove_device(sdev1);
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+ }
+ }
+ doscan = 0;
+ break;
+
+ case MR_EVT_LD_OFFLINE:
+ case MR_EVT_LD_DELETED:
+ megasas_get_ld_list(instance);
+ for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+ for (j = 0;
+ j < MEGASAS_MAX_DEV_PER_CHANNEL;
+ j++) {
+
+ ld_index =
+ (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+
+ sdev1 = scsi_device_lookup(host,
+ i + MEGASAS_MAX_LD_CHANNELS,
+ j,
+ 0);
+
+ if (instance->ld_ids[ld_index] != 0xff) {
+ if (sdev1) {
+ scsi_device_put(sdev1);
+ }
+ } else {
+ if (sdev1) {
+ scsi_remove_device(sdev1);
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+ }
+ doscan = 0;
+ break;
+ case MR_EVT_LD_CREATED:
+ megasas_get_ld_list(instance);
+ for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+ for (j = 0;
+ j < MEGASAS_MAX_DEV_PER_CHANNEL;
+ j++) {
+ ld_index =
+ (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+
+ sdev1 = scsi_device_lookup(host,
+ i+MEGASAS_MAX_LD_CHANNELS,
+ j, 0);
+
+ if (instance->ld_ids[ld_index] !=
+ 0xff) {
+ if (!sdev1) {
+ scsi_add_device(host,
+ i + 2,
+ j, 0);
+ }
+ }
+ if (sdev1) {
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+ doscan = 0;
+ break;
case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
+ case MR_EVT_FOREIGN_CFG_IMPORTED:
doscan = 1;
break;
default:
@@ -4021,6 +4254,31 @@ megasas_aen_polling(struct work_struct *work)
}
}
}
+
+ megasas_get_ld_list(instance);
+ for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ ld_index =
+ (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+
+ sdev1 = scsi_device_lookup(host,
+ i+MEGASAS_MAX_LD_CHANNELS, j, 0);
+ if (instance->ld_ids[ld_index] != 0xff) {
+ if (!sdev1) {
+ scsi_add_device(host,
+ i+2,
+ j, 0);
+ } else {
+ scsi_device_put(sdev1);
+ }
+ } else {
+ if (sdev1) {
+ scsi_remove_device(sdev1);
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+ }
}
if ( instance->aen_cmd != NULL ) {
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 72b28e436e3..9d8b6bf605a 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.12-rc1"
-#define MEGASAS_RELDATE "Sep. 17, 2009"
-#define MEGASAS_EXT_VERSION "Thu Sep. 17 11:41:51 PST 2009"
+#define MEGASAS_VERSION "00.00.04.17.1-rc1"
+#define MEGASAS_RELDATE "Oct. 29, 2009"
+#define MEGASAS_EXT_VERSION "Thu. Oct. 29, 11:41:51 PST 2009"
/*
* Device IDs
@@ -117,6 +117,7 @@
#define MFI_CMD_STP 0x08
#define MR_DCMD_CTRL_GET_INFO 0x01010000
+#define MR_DCMD_LD_GET_LIST 0x03010000
#define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000
#define MR_FLUSH_CTRL_CACHE 0x01
@@ -349,6 +350,32 @@ struct megasas_pd_list {
u8 driveState;
} __packed;
+ /*
+ * defines the logical drive reference structure
+ */
+union MR_LD_REF {
+ struct {
+ u8 targetId;
+ u8 reserved;
+ u16 seqNum;
+ };
+ u32 ref;
+} __packed;
+
+/*
+ * defines the logical drive list structure
+ */
+struct MR_LD_LIST {
+ u32 ldCount;
+ u32 reserved;
+ struct {
+ union MR_LD_REF ref;
+ u8 state;
+ u8 reserved[3];
+ u64 size;
+ } ldList[MAX_LOGICAL_DRIVES];
+} __packed;
+
/*
* SAS controller properties
*/
@@ -637,6 +664,8 @@ struct megasas_ctrl_info {
#define MEGASAS_MAX_LD 64
#define MEGASAS_MAX_PD (MEGASAS_MAX_PD_CHANNELS * \
MEGASAS_MAX_DEV_PER_CHANNEL)
+#define MEGASAS_MAX_LD_IDS (MEGASAS_MAX_LD_CHANNELS * \
+ MEGASAS_MAX_DEV_PER_CHANNEL)
#define MEGASAS_DBG_LVL 1
@@ -1187,6 +1216,7 @@ struct megasas_instance {
struct megasas_register_set __iomem *reg_set;
struct megasas_pd_list pd_list[MEGASAS_MAX_PD];
+ u8 ld_ids[MEGASAS_MAX_LD_IDS];
s8 init_id;
u16 max_num_sge;
diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig
index 70c4c2467dd..ba8e128de23 100644
--- a/drivers/scsi/mpt2sas/Kconfig
+++ b/drivers/scsi/mpt2sas/Kconfig
@@ -44,6 +44,7 @@ config SCSI_MPT2SAS
tristate "LSI MPT Fusion SAS 2.0 Device Driver"
depends on PCI && SCSI
select SCSI_SAS_ATTRS
+ select RAID_ATTRS
---help---
This driver supports PCI-Express SAS 6Gb/s Host Adapters.
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h
index 91416810529..9958d847a88 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.13
+ * mpi2.h Version: 02.00.14
*
* Version History
* ---------------
@@ -53,6 +53,10 @@
* bytes reserved.
* Added RAID Accelerator functionality.
* 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MSI-x index mask and shift for Reply Post Host
+ * Index register.
+ * Added function code for Host Based Discovery Action.
* --------------------------------------------------------------------------
*/
@@ -78,7 +82,7 @@
#define MPI2_VERSION_02_00 (0x0200)
/* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT (0x0D)
+#define MPI2_HEADER_VERSION_UNIT (0x0E)
#define MPI2_HEADER_VERSION_DEV (0x00)
#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
@@ -232,9 +236,12 @@ typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS
#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048)
/*
- * Offset for the Reply Descriptor Post Queue
+ * Defines for the Reply Descriptor Post Queue
*/
#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C)
+#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF)
+#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000)
+#define MPI2_RPHI_MSIX_INDEX_SHIFT (24)
/*
* Defines for the HCBSize and address
@@ -497,12 +504,13 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION
#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */
#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */
#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator*/
+/* Host Based Discovery Action */
+#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F)
/* Doorbell functions */
#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40)
-/* #define MPI2_FUNCTION_IO_UNIT_RESET (0x41) */
#define MPI2_FUNCTION_HANDSHAKE (0x42)
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
index 1611c57a6fd..cf0ac9f40c9 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.12
+ * mpi2_cnfg.h Version: 02.00.13
*
* Version History
* ---------------
@@ -107,6 +107,8 @@
* to SAS Device Page 0 Flags field.
* Added PhyInfo defines for power condition.
* Added Ethernet configuration pages.
+ * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
+ * Added SAS PHY Page 4 structure and defines.
* --------------------------------------------------------------------------
*/
@@ -712,6 +714,7 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1
#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04)
/* IO Unit Page 1 Flags defines */
+#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800)
#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600)
#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000)
#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200)
@@ -2291,6 +2294,26 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 {
#define MPI2_SASPHY3_PAGEVERSION (0x00)
+/* SAS PHY Page 4 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 Reserved1; /* 0x08 */
+ U8 Reserved2; /* 0x0A */
+ U8 Flags; /* 0x0B */
+ U8 InitialFrame[28]; /* 0x0C */
+} MPI2_CONFIG_PAGE_SAS_PHY_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_4,
+ Mpi2SasPhyPage4_t, MPI2_POINTER pMpi2SasPhyPage4_t;
+
+#define MPI2_SASPHY4_PAGEVERSION (0x00)
+
+/* values for the Flags field */
+#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02)
+#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01)
+
+
+
+
/****************************************************************************
* SAS Port Config Pages
****************************************************************************/
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt
index 65fcaa31cb3..c4adf76b49d 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt
@@ -5,23 +5,24 @@
Copyright (c) 2000-2009 LSI Corporation.
---------------------------------------
- Header Set Release Version: 02.00.12
- Header Set Release Date: 05-06-09
+ Header Set Release Version: 02.00.14
+ Header Set Release Date: 10-28-09
---------------------------------------
Filename Current version Prior version
---------- --------------- -------------
- mpi2.h 02.00.12 02.00.11
- mpi2_cnfg.h 02.00.11 02.00.10
- mpi2_init.h 02.00.07 02.00.06
- mpi2_ioc.h 02.00.11 02.00.10
- mpi2_raid.h 02.00.03 02.00.03
- mpi2_sas.h 02.00.02 02.00.02
+ mpi2.h 02.00.14 02.00.13
+ mpi2_cnfg.h 02.00.13 02.00.12
+ mpi2_init.h 02.00.08 02.00.07
+ mpi2_ioc.h 02.00.13 02.00.12
+ mpi2_raid.h 02.00.04 02.00.04
+ mpi2_sas.h 02.00.03 02.00.02
mpi2_targ.h 02.00.03 02.00.03
- mpi2_tool.h 02.00.03 02.00.02
+ mpi2_tool.h 02.00.04 02.00.04
mpi2_type.h 02.00.00 02.00.00
- mpi2_ra.h 02.00.00
- mpi2_history.txt 02.00.11 02.00.12
+ mpi2_ra.h 02.00.00 02.00.00
+ mpi2_hbd.h 02.00.00
+ mpi2_history.txt 02.00.14 02.00.13
* Date Version Description
@@ -65,6 +66,11 @@ mpi2.h
* MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those
* bytes reserved.
* Added RAID Accelerator functionality.
+ * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MSI-x index mask and shift for Reply Post Host
+ * Index register.
+ * Added function code for Host Based Discovery Action.
* --------------------------------------------------------------------------
mpi2_cnfg.h
@@ -155,6 +161,15 @@ mpi2_cnfg.h
* Added expander reduced functionality data to SAS
* Expander Page 0.
* Added SAS PHY Page 2 and SAS PHY Page 3.
+ * 07-30-09 02.00.12 Added IO Unit Page 7.
+ * Added new device ids.
+ * Added SAS IO Unit Page 5.
+ * Added partial and slumber power management capable flags
+ * to SAS Device Page 0 Flags field.
+ * Added PhyInfo defines for power condition.
+ * Added Ethernet configuration pages.
+ * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
+ * Added SAS PHY Page 4 structure and defines.
* --------------------------------------------------------------------------
mpi2_init.h
@@ -172,6 +187,10 @@ mpi2_init.h
* Query Asynchronous Event.
* Defined two new bits in the SlotStatus field of the SCSI
* Enclosure Processor Request and Reply.
+ * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for
+ * both SCSI IO Error Reply and SCSI Task Management Reply.
+ * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
+ * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
* --------------------------------------------------------------------------
mpi2_ioc.h
@@ -246,6 +265,20 @@ mpi2_ioc.h
* Added two new reason codes for SAS Device Status Change
* Event.
* Added new event: SAS PHY Counter.
+ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure.
+ * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Added new product id family for 2208.
+ * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST.
+ * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY.
+ * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY.
+ * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY.
+ * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define.
+ * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define.
+ * Added Host Based Discovery Phy Event data.
+ * Added defines for ProductID Product field
+ * (MPI2_FW_HEADER_PID_).
+ * Modified values for SAS ProductID Family
+ * (MPI2_FW_HEADER_PID_FAMILY_).
* --------------------------------------------------------------------------
mpi2_raid.h
@@ -256,6 +289,8 @@ mpi2_raid.h
* 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.
* --------------------------------------------------------------------------
mpi2_sas.h
@@ -264,6 +299,8 @@ mpi2_sas.h
* Control Request.
* 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control
* Request.
+ * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
+ * to MPI2_SGE_IO_UNION since it supports chained SGLs.
* --------------------------------------------------------------------------
mpi2_targ.h
@@ -283,6 +320,10 @@ mpi2_tool.h
* 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.
* --------------------------------------------------------------------------
mpi2_type.h
@@ -293,20 +334,26 @@ mpi2_ra.h
* 05-06-09 02.00.00 Initial version.
* --------------------------------------------------------------------------
+mpi2_hbd.h
+ * 10-28-09 02.00.00 Initial version.
+ * --------------------------------------------------------------------------
+
+
mpi2_history.txt Parts list history
-Filename 02.00.12
----------- --------
-mpi2.h 02.00.12
-mpi2_cnfg.h 02.00.11
-mpi2_init.h 02.00.07
-mpi2_ioc.h 02.00.11
-mpi2_raid.h 02.00.03
-mpi2_sas.h 02.00.02
-mpi2_targ.h 02.00.03
-mpi2_tool.h 02.00.03
-mpi2_type.h 02.00.00
-mpi2_ra.h 02.00.00
+Filename 02.00.14 02.00.13 02.00.12
+---------- -------- -------- --------
+mpi2.h 02.00.14 02.00.13 02.00.12
+mpi2_cnfg.h 02.00.13 02.00.12 02.00.11
+mpi2_init.h 02.00.08 02.00.07 02.00.07
+mpi2_ioc.h 02.00.13 02.00.12 02.00.11
+mpi2_raid.h 02.00.04 02.00.04 02.00.03
+mpi2_sas.h 02.00.03 02.00.02 02.00.02
+mpi2_targ.h 02.00.03 02.00.03 02.00.03
+mpi2_tool.h 02.00.04 02.00.04 02.00.03
+mpi2_type.h 02.00.00 02.00.00 02.00.00
+mpi2_ra.h 02.00.00 02.00.00 02.00.00
+mpi2_hbd.h 02.00.00
Filename 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06
---------- -------- -------- -------- -------- -------- --------
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h
index 563e56d2e94..6541945e97c 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h
@@ -6,7 +6,7 @@
* Title: MPI SCSI initiator mode messages and structures
* Creation Date: June 23, 2006
*
- * mpi2_init.h Version: 02.00.07
+ * mpi2_init.h Version: 02.00.08
*
* Version History
* ---------------
@@ -27,6 +27,10 @@
* Query Asynchronous Event.
* Defined two new bits in the SlotStatus field of the SCSI
* Enclosure Processor Request and Reply.
+ * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for
+ * both SCSI IO Error Reply and SCSI Task Management Reply.
+ * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
+ * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
* --------------------------------------------------------------------------
*/
@@ -254,6 +258,11 @@ typedef struct _MPI2_SCSI_IO_REPLY
#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02)
#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01)
+/* masks and shifts for the ResponseInfo field */
+
+#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF)
+#define MPI2_SCSI_RI_SHIFT_REASONCODE (0)
+
#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF)
@@ -327,6 +336,7 @@ typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY
U16 IOCStatus; /* 0x0E */
U32 IOCLogInfo; /* 0x10 */
U32 TerminationCount; /* 0x14 */
+ U32 ResponseInfo; /* 0x18 */
} MPI2_SCSI_TASK_MANAGE_REPLY,
MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REPLY,
Mpi2SCSITaskManagementReply_t, MPI2_POINTER pMpi2SCSIManagementReply_t;
@@ -339,8 +349,20 @@ typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY
#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05)
#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08)
#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09)
+#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A)
#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80)
+/* masks and shifts for the ResponseInfo field */
+
+#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24)
+
/****************************************************************************
* SCSI Enclosure Processor messages
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
index ea51ce86869..754938422f6 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.12
+ * mpi2_ioc.h Version: 02.00.13
*
* Version History
* ---------------
@@ -87,6 +87,17 @@
* 07-30-09 02.00.12 Added GPIO Interrupt event define and structure.
* Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
* Added new product id family for 2208.
+ * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST.
+ * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY.
+ * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY.
+ * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY.
+ * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define.
+ * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define.
+ * Added Host Based Discovery Phy Event data.
+ * Added defines for ProductID Product field
+ * (MPI2_FW_HEADER_PID_).
+ * Modified values for SAS ProductID Family
+ * (MPI2_FW_HEADER_PID_FAMILY_).
* --------------------------------------------------------------------------
*/
@@ -119,8 +130,10 @@ typedef struct _MPI2_IOC_INIT_REQUEST
U16 MsgVersion; /* 0x0C */
U16 HeaderVersion; /* 0x0E */
U32 Reserved5; /* 0x10 */
- U32 Reserved6; /* 0x14 */
- U16 Reserved7; /* 0x18 */
+ U16 Reserved6; /* 0x14 */
+ U8 Reserved7; /* 0x16 */
+ U8 HostMSIxVectors; /* 0x17 */
+ U16 Reserved8; /* 0x18 */
U16 SystemRequestFrameSize; /* 0x1A */
U16 ReplyDescriptorPostQueueDepth; /* 0x1C */
U16 ReplyFreeQueueDepth; /* 0x1E */
@@ -215,7 +228,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY
U8 MaxChainDepth; /* 0x14 */
U8 WhoInit; /* 0x15 */
U8 NumberOfPorts; /* 0x16 */
- U8 Reserved2; /* 0x17 */
+ U8 MaxMSIxVectors; /* 0x17 */
U16 RequestCredit; /* 0x18 */
U16 ProductID; /* 0x1A */
U32 IOCCapabilities; /* 0x1C */
@@ -233,7 +246,8 @@ typedef struct _MPI2_IOC_FACTS_REPLY
U8 MaxVolumes; /* 0x37 */
U16 MaxDevHandle; /* 0x38 */
U16 MaxPersistentEntries; /* 0x3A */
- U32 Reserved4; /* 0x3C */
+ U16 MinDevHandle; /* 0x3C */
+ U16 Reserved4; /* 0x3E */
} MPI2_IOC_FACTS_REPLY, MPI2_POINTER PTR_MPI2_IOC_FACTS_REPLY,
Mpi2IOCFactsReply_t, MPI2_POINTER pMpi2IOCFactsReply_t;
@@ -269,6 +283,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY
/* ProductID field uses MPI2_FW_HEADER_PID_ */
/* IOCCapabilities */
+#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000)
#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000)
#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000)
#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000)
@@ -453,6 +468,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021)
#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022)
#define MPI2_EVENT_GPIO_INTERRUPT (0x0023)
+#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024)
/* Log Entry Added Event data */
@@ -793,6 +809,7 @@ typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST
MPI2_POINTER pMpi2EventDataSasTopologyChangeList_t;
/* values for the ExpStatus field */
+#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00)
#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01)
#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
@@ -878,6 +895,44 @@ typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER {
* */
+/* Host Based Discovery Phy Event data */
+
+typedef struct _MPI2_EVENT_HBD_PHY_SAS {
+ U8 Flags; /* 0x00 */
+ U8 NegotiatedLinkRate; /* 0x01 */
+ U8 PhyNum; /* 0x02 */
+ U8 PhysicalPort; /* 0x03 */
+ U32 Reserved1; /* 0x04 */
+ U8 InitialFrame[28]; /* 0x08 */
+} MPI2_EVENT_HBD_PHY_SAS, MPI2_POINTER PTR_MPI2_EVENT_HBD_PHY_SAS,
+ Mpi2EventHbdPhySas_t, MPI2_POINTER pMpi2EventHbdPhySas_t;
+
+/* values for the Flags field */
+#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02)
+#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01)
+
+/* use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h for
+ * the NegotiatedLinkRate field */
+
+typedef union _MPI2_EVENT_HBD_DESCRIPTOR {
+ MPI2_EVENT_HBD_PHY_SAS Sas;
+} MPI2_EVENT_HBD_DESCRIPTOR, MPI2_POINTER PTR_MPI2_EVENT_HBD_DESCRIPTOR,
+ Mpi2EventHbdDescriptor_t, MPI2_POINTER pMpi2EventHbdDescriptor_t;
+
+typedef struct _MPI2_EVENT_DATA_HBD_PHY {
+ U8 DescriptorType; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+ U32 Reserved3; /* 0x04 */
+ MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /* 0x08 */
+} MPI2_EVENT_DATA_HBD_PHY, MPI2_POINTER PTR_MPI2_EVENT_DATA_HBD_PHY,
+ Mpi2EventDataHbdPhy_t, MPI2_POINTER pMpi2EventDataMpi2EventDataHbdPhy_t;
+
+/* values for the DescriptorType field */
+#define MPI2_EVENT_HBD_DT_SAS (0x01)
+
+
+
/****************************************************************************
* EventAck message
****************************************************************************/
@@ -1126,13 +1181,17 @@ typedef struct _MPI2_FW_IMAGE_HEADER
#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000)
#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000)
-#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
-#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
+#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
+#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
+#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
+#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200)
+#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700)
+
#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
/* SAS */
-#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010)
-#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0011)
+#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013)
+#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014)
/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
index 8a42b136cf5..2d8aeed5139 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
@@ -6,7 +6,7 @@
* Title: MPI Serial Attached SCSI structures and definitions
* Creation Date: February 9, 2007
*
- * mpi2.h Version: 02.00.02
+ * mpi2.h Version: 02.00.03
*
* Version History
* ---------------
@@ -18,6 +18,8 @@
* Control Request.
* 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control
* Request.
+ * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
+ * to MPI2_SGE_IO_UNION since it supports chained SGLs.
* --------------------------------------------------------------------------
*/
@@ -160,7 +162,7 @@ typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST
U32 Reserved4; /* 0x14 */
U32 DataLength; /* 0x18 */
U8 CommandFIS[20]; /* 0x1C */
- MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */
+ MPI2_SGE_IO_UNION SGL; /* 0x20 */
} MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t;
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 89d02401b9e..88e6eebc315 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -107,8 +107,7 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
if (ret)
return ret;
- printk(KERN_INFO "setting logging_level(0x%08x)\n",
- mpt2sas_fwfault_debug);
+ printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug);
list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
ioc->fwfault_debug = mpt2sas_fwfault_debug;
return 0;
@@ -1222,6 +1221,8 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
u32 memap_sz;
u32 pio_sz;
int i, r = 0;
+ u64 pio_chip = 0;
+ u64 chip_phys = 0;
dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n",
ioc->name, __func__));
@@ -1255,12 +1256,13 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) {
if (pio_sz)
continue;
- ioc->pio_chip = pci_resource_start(pdev, i);
+ pio_chip = (u64)pci_resource_start(pdev, i);
pio_sz = pci_resource_len(pdev, i);
} else {
if (memap_sz)
continue;
ioc->chip_phys = pci_resource_start(pdev, i);
+ chip_phys = (u64)ioc->chip_phys;
memap_sz = pci_resource_len(pdev, i);
ioc->chip = ioremap(ioc->chip_phys, memap_sz);
if (ioc->chip == NULL) {
@@ -1280,10 +1282,10 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n",
ioc->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
"IO-APIC enabled"), ioc->pci_irq);
- printk(MPT2SAS_INFO_FMT "iomem(0x%lx), mapped(0x%p), size(%d)\n",
- ioc->name, ioc->chip_phys, ioc->chip, memap_sz);
- printk(MPT2SAS_INFO_FMT "ioport(0x%lx), size(%d)\n",
- ioc->name, ioc->pio_chip, pio_sz);
+ printk(MPT2SAS_INFO_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
+ ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz);
+ printk(MPT2SAS_INFO_FMT "ioport(0x%016llx), size(%d)\n",
+ ioc->name, (unsigned long long)pio_chip, pio_sz);
return 0;
@@ -3573,6 +3575,8 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
init_waitqueue_head(&ioc->reset_wq);
+ ioc->fwfault_debug = mpt2sas_fwfault_debug;
+
/* base internal command bits */
mutex_init(&ioc->base_cmds.mutex);
ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index bb4f14656af..e18b0544c38 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -69,10 +69,10 @@
#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 "03.100.03.00"
-#define MPT2SAS_MAJOR_VERSION 03
+#define MPT2SAS_DRIVER_VERSION "04.100.01.00"
+#define MPT2SAS_MAJOR_VERSION 04
#define MPT2SAS_MINOR_VERSION 100
-#define MPT2SAS_BUILD_VERSION 03
+#define MPT2SAS_BUILD_VERSION 01
#define MPT2SAS_RELEASE_VERSION 00
/*
@@ -323,6 +323,7 @@ struct _sas_device {
* @device_info: bitfield provides detailed info about the hidden components
* @num_pds: number of hidden raid components
* @responding: used in _scsih_raid_device_mark_responding
+ * @percent_complete: resync percent complete
*/
struct _raid_device {
struct list_head list;
@@ -336,6 +337,7 @@ struct _raid_device {
u32 device_info;
u8 num_pds;
u8 responding;
+ u8 percent_complete;
};
/**
@@ -464,7 +466,6 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @pdev: pci pdev object
* @chip: memory mapped register space
* @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
@@ -587,8 +588,7 @@ struct MPT2SAS_ADAPTER {
char tmp_string[MPT_STRING_LENGTH];
struct pci_dev *pdev;
Mpi2SystemInterfaceRegs_t __iomem *chip;
- unsigned long chip_phys;
- unsigned long pio_chip;
+ resource_size_t chip_phys;
int logging_level;
int fwfault_debug;
u8 ir_firmware;
@@ -853,6 +853,8 @@ int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
*mpi_reply, Mpi2IOUnitPage1_t *config_page);
int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
*mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
+int mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
*mpi_reply, Mpi2IOCPage8_t *config_page);
int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c
index 594a389c652..411c27d7f78 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_config.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_config.c
@@ -324,7 +324,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
if (r != 0)
goto out;
if (mpi_request->Action ==
- MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT) {
+ MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
+ mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz,
mem.page_dma);
@@ -882,7 +884,7 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
}
/**
- * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 0
+ * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1
* @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
* @config_page: contents of the config page
@@ -907,7 +909,7 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
@@ -922,6 +924,49 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
}
/**
+ * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
* mpt2sas_config_get_expander_pg0 - obtain expander page 0
* @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 84a124f8e21..fa9bf83819d 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -891,6 +891,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
issue_host_reset:
if (issue_reset) {
+ ret = -ENODATA;
if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
mpi_request->Function ==
MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
@@ -2202,14 +2203,10 @@ _ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg)
karg.data_out_size = karg32.data_out_size;
karg.max_sense_bytes = karg32.max_sense_bytes;
karg.data_sge_offset = karg32.data_sge_offset;
- memcpy(&karg.reply_frame_buf_ptr, &karg32.reply_frame_buf_ptr,
- sizeof(uint32_t));
- memcpy(&karg.data_in_buf_ptr, &karg32.data_in_buf_ptr,
- sizeof(uint32_t));
- memcpy(&karg.data_out_buf_ptr, &karg32.data_out_buf_ptr,
- sizeof(uint32_t));
- memcpy(&karg.sense_data_ptr, &karg32.sense_data_ptr,
- sizeof(uint32_t));
+ karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr);
+ karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr);
+ karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr);
+ karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr);
state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
return _ctl_do_mpt_command(ioc, karg, &uarg->mf, state);
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index efabea1a3ce..c7ec3f17478 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -52,6 +52,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/raid_class.h>
#include "mpt2sas_base.h"
@@ -133,6 +134,9 @@ struct fw_event_work {
void *event_data;
};
+/* raid transport support */
+static struct raid_template *mpt2sas_raid_template;
+
/**
* struct _scsi_io_transfer - scsi io transfer
* @handle: sas device handle (assigned by firmware)
@@ -1305,7 +1309,6 @@ _scsih_slave_alloc(struct scsi_device *sdev)
struct MPT2SAS_DEVICE *sas_device_priv_data;
struct scsi_target *starget;
struct _raid_device *raid_device;
- struct _sas_device *sas_device;
unsigned long flags;
sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL);
@@ -1332,21 +1335,8 @@ _scsih_slave_alloc(struct scsi_device *sdev)
if (raid_device)
raid_device->sdev = sdev; /* raid is single lun */
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- } else {
- /* set TLR bit for SSP devices */
- if (!(ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_TLR))
- goto out;
- 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_SSP_TARGET)
- sas_device_priv_data->flags |= MPT_DEVICE_TLR_ON;
}
- out:
return 0;
}
@@ -1419,6 +1409,140 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
}
/**
+ * _scsih_is_raid - return boolean indicating device is raid volume
+ * @dev the device struct object
+ */
+static int
+_scsih_is_raid(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return (sdev->channel == RAID_CHANNEL) ? 1 : 0;
+}
+
+/**
+ * _scsih_get_resync - get raid volume resync percent complete
+ * @dev the device struct object
+ */
+static void
+_scsih_get_resync(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ Mpi2RaidVolPage0_t vol_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 volume_status_flags;
+ u8 percent_complete = 0;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
+ sdev->channel);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!raid_device)
+ goto out;
+
+ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle,
+ sizeof(Mpi2RaidVolPage0_t))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags);
+ if (volume_status_flags & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS)
+ percent_complete = raid_device->percent_complete;
+ out:
+ raid_set_resync(mpt2sas_raid_template, dev, percent_complete);
+}
+
+/**
+ * _scsih_get_state - get raid volume level
+ * @dev the device struct object
+ */
+static void
+_scsih_get_state(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ Mpi2RaidVolPage0_t vol_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 volstate;
+ enum raid_state state = RAID_STATE_UNKNOWN;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
+ sdev->channel);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!raid_device)
+ goto out;
+
+ if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle,
+ sizeof(Mpi2RaidVolPage0_t))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags);
+ if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) {
+ state = RAID_STATE_RESYNCING;
+ goto out;
+ }
+
+ switch (vol_pg0.VolumeState) {
+ case MPI2_RAID_VOL_STATE_OPTIMAL:
+ case MPI2_RAID_VOL_STATE_ONLINE:
+ state = RAID_STATE_ACTIVE;
+ break;
+ case MPI2_RAID_VOL_STATE_DEGRADED:
+ state = RAID_STATE_DEGRADED;
+ break;
+ case MPI2_RAID_VOL_STATE_FAILED:
+ case MPI2_RAID_VOL_STATE_MISSING:
+ state = RAID_STATE_OFFLINE;
+ break;
+ }
+ out:
+ raid_set_state(mpt2sas_raid_template, dev, state);
+}
+
+/**
+ * _scsih_set_level - set raid level
+ * @sdev: scsi device struct
+ * @raid_device: raid_device object
+ */
+static void
+_scsih_set_level(struct scsi_device *sdev, struct _raid_device *raid_device)
+{
+ enum raid_level level = RAID_LEVEL_UNKNOWN;
+
+ switch (raid_device->volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ level = RAID_LEVEL_0;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ level = RAID_LEVEL_10;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ level = RAID_LEVEL_1E;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ level = RAID_LEVEL_1;
+ break;
+ }
+
+ raid_set_level(mpt2sas_raid_template, &sdev->sdev_gendev, level);
+}
+
+/**
* _scsih_get_volume_capabilities - volume capabilities
* @ioc: per adapter object
* @sas_device: the raid_device object
@@ -1479,6 +1603,32 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
}
/**
+ * _scsih_enable_tlr - setting TLR flags
+ * @ioc: per adapter object
+ * @sdev: scsi device struct
+ *
+ * Enabling Transaction Layer Retries for tape devices when
+ * vpd page 0x90 is present
+ *
+ */
+static void
+_scsih_enable_tlr(struct MPT2SAS_ADAPTER *ioc, struct scsi_device *sdev)
+{
+ /* only for TAPE */
+ if (sdev->type != TYPE_TAPE)
+ return;
+
+ if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR))
+ return;
+
+ sas_enable_tlr(sdev);
+ sdev_printk(KERN_INFO, sdev, "TLR %s\n",
+ sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled");
+ return;
+
+}
+
+/**
* _scsih_slave_configure - device configure routine.
* @sdev: scsi device struct
*
@@ -1574,6 +1724,8 @@ _scsih_slave_configure(struct scsi_device *sdev)
(unsigned long long)raid_device->wwid,
raid_device->num_pds, ds);
_scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+ /* raid transport support */
+ _scsih_set_level(sdev, raid_device);
return 0;
}
@@ -1621,8 +1773,10 @@ _scsih_slave_configure(struct scsi_device *sdev)
_scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
- if (ssp_target)
+ if (ssp_target) {
sas_read_port_mode_page(sdev);
+ _scsih_enable_tlr(ioc, sdev);
+ }
return 0;
}
@@ -2908,8 +3062,9 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
} else
mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
-
- if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON))
+ /* Make sure Device is not raid volume */
+ if (!_scsih_is_raid(&scmd->device->sdev_gendev) &&
+ sas_is_tlr_enabled(scmd->device))
mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd);
@@ -3298,10 +3453,12 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
if (!sas_device_priv_data->tlr_snoop_check) {
sas_device_priv_data->tlr_snoop_check++;
- if ((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;
+ if (!_scsih_is_raid(&scmd->device->sdev_gendev) &&
+ sas_is_tlr_enabled(scmd->device) &&
+ response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) {
+ sas_disable_tlr(scmd->device);
+ sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n");
+ }
}
xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
@@ -5170,11 +5327,33 @@ static void
_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
+ Mpi2EventDataIrOperationStatus_t *event_data = fw_event->event_data;
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ u16 handle;
+
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_sas_ir_operation_status_event_debug(ioc,
- fw_event->event_data);
+ event_data);
#endif
+
+ /* code added for raid transport support */
+ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) {
+
+ handle = le16_to_cpu(event_data->VolDevHandle);
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!raid_device)
+ return;
+
+ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC)
+ raid_device->percent_complete =
+ event_data->PercentComplete;
+ }
}
/**
@@ -5998,6 +6177,8 @@ _scsih_remove(struct pci_dev *pdev)
struct _sas_port *mpt2sas_port;
struct _sas_device *sas_device;
struct _sas_node *expander_sibling;
+ struct _raid_device *raid_device, *next;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
struct workqueue_struct *wq;
unsigned long flags;
@@ -6011,6 +6192,21 @@ _scsih_remove(struct pci_dev *pdev)
if (wq)
destroy_workqueue(wq);
+ /* release all the volumes */
+ list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list,
+ list) {
+ if (raid_device->starget) {
+ sas_target_priv_data =
+ raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ scsi_remove_target(&raid_device->starget->dev);
+ }
+ printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid"
+ "(0x%016llx)\n", ioc->name, raid_device->handle,
+ (unsigned long long) raid_device->wwid);
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+
/* free ports attached to the sas_host */
retry_again:
list_for_each_entry(mpt2sas_port,
@@ -6373,6 +6569,13 @@ static struct pci_driver scsih_driver = {
#endif
};
+/* raid transport support */
+static struct raid_function_template mpt2sas_raid_functions = {
+ .cookie = &scsih_driver_template,
+ .is_raid = _scsih_is_raid,
+ .get_resync = _scsih_get_resync,
+ .get_state = _scsih_get_state,
+};
/**
* _scsih_init - main entry point for this driver.
@@ -6392,6 +6595,12 @@ _scsih_init(void)
sas_attach_transport(&mpt2sas_transport_functions);
if (!mpt2sas_transport_template)
return -ENODEV;
+ /* raid transport support */
+ mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions);
+ if (!mpt2sas_raid_template) {
+ sas_release_transport(mpt2sas_transport_template);
+ return -ENODEV;
+ }
mpt2sas_base_initialize_callback_handler();
@@ -6426,8 +6635,11 @@ _scsih_init(void)
mpt2sas_ctl_init();
error = pci_register_driver(&scsih_driver);
- if (error)
+ if (error) {
+ /* raid transport support */
+ raid_class_release(mpt2sas_raid_template);
sas_release_transport(mpt2sas_transport_template);
+ }
return error;
}
@@ -6445,7 +6657,8 @@ _scsih_exit(void)
pci_unregister_driver(&scsih_driver);
- sas_release_transport(mpt2sas_transport_template);
+ mpt2sas_ctl_exit();
+
mpt2sas_base_release_callback_handler(scsi_io_cb_idx);
mpt2sas_base_release_callback_handler(tm_cb_idx);
mpt2sas_base_release_callback_handler(base_cb_idx);
@@ -6457,7 +6670,10 @@ _scsih_exit(void)
mpt2sas_base_release_callback_handler(tm_tr_cb_idx);
mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx);
- mpt2sas_ctl_exit();
+ /* raid transport support */
+ raid_class_release(mpt2sas_raid_template);
+ sas_release_transport(mpt2sas_transport_template);
+
}
module_init(_scsih_init);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
index 3a82872bad4..789f9ee7f00 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -855,6 +855,17 @@ rphy_to_ioc(struct sas_rphy *rphy)
return shost_priv(shost);
}
+static struct _sas_phy *
+_transport_find_local_phy(struct MPT2SAS_ADAPTER *ioc, struct sas_phy *phy)
+{
+ int i;
+
+ for (i = 0; i < ioc->sas_hba.num_phys; i++)
+ if (ioc->sas_hba.phy[i].phy == phy)
+ return(&ioc->sas_hba.phy[i]);
+ return NULL;
+}
+
/**
* _transport_get_linkerrors -
* @phy: The sas phy object
@@ -870,14 +881,8 @@ _transport_get_linkerrors(struct sas_phy *phy)
struct _sas_phy *mpt2sas_phy;
Mpi2ConfigReply_t mpi_reply;
Mpi2SasPhyPage1_t phy_pg1;
- int i;
- for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys &&
- !mpt2sas_phy; i++) {
- if (ioc->sas_hba.phy[i].phy != phy)
- continue;
- mpt2sas_phy = &ioc->sas_hba.phy[i];
- }
+ mpt2sas_phy = _transport_find_local_phy(ioc, phy);
if (!mpt2sas_phy) /* this phy not on sas_host */
return -EINVAL;
@@ -971,14 +976,8 @@ _transport_phy_reset(struct sas_phy *phy, int hard_reset)
struct _sas_phy *mpt2sas_phy;
Mpi2SasIoUnitControlReply_t mpi_reply;
Mpi2SasIoUnitControlRequest_t mpi_request;
- int i;
- for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys &&
- !mpt2sas_phy; i++) {
- if (ioc->sas_hba.phy[i].phy != phy)
- continue;
- mpt2sas_phy = &ioc->sas_hba.phy[i];
- }
+ mpt2sas_phy = _transport_find_local_phy(ioc, phy);
if (!mpt2sas_phy) /* this phy not on sas_host */
return -EINVAL;
@@ -1006,6 +1005,173 @@ _transport_phy_reset(struct sas_phy *phy, int hard_reset)
}
/**
+ * _transport_phy_enable - enable/disable phys
+ * @phy: The sas phy object
+ * @enable: enable phy when true
+ *
+ * Only support sas_host direct attached phys.
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_enable(struct sas_phy *phy, int enable)
+{
+ struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ struct _sas_phy *mpt2sas_phy;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 sz;
+ int rc = 0;
+
+ mpt2sas_phy = _transport_find_local_phy(ioc, phy);
+
+ if (!mpt2sas_phy) /* this phy not on sas_host */
+ return -EINVAL;
+
+ /* sas_iounit page 1 */
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ if (enable)
+ sas_iounit_pg1->PhyData[mpt2sas_phy->phy_id].PhyFlags
+ &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+ else
+ sas_iounit_pg1->PhyData[mpt2sas_phy->phy_id].PhyFlags
+ |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+
+ mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
+
+ out:
+ kfree(sas_iounit_pg1);
+ return rc;
+}
+
+/**
+ * _transport_phy_speed - set phy min/max link rates
+ * @phy: The sas phy object
+ * @rates: rates defined in sas_phy_linkrates
+ *
+ * Only support sas_host direct attached phys.
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
+{
+ struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ struct _sas_phy *mpt2sas_phy;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasPhyPage0_t phy_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 sz;
+ int i;
+ int rc = 0;
+
+ mpt2sas_phy = _transport_find_local_phy(ioc, phy);
+
+ if (!mpt2sas_phy) /* this phy not on sas_host */
+ return -EINVAL;
+
+ if (!rates->minimum_linkrate)
+ rates->minimum_linkrate = phy->minimum_linkrate;
+ else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
+ rates->minimum_linkrate = phy->minimum_linkrate_hw;
+
+ if (!rates->maximum_linkrate)
+ rates->maximum_linkrate = phy->maximum_linkrate;
+ else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
+ rates->maximum_linkrate = phy->maximum_linkrate_hw;
+
+ /* sas_iounit page 1 */
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i = 0; i < ioc->sas_hba.num_phys; i++) {
+ if (mpt2sas_phy->phy_id != i) {
+ sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
+ (ioc->sas_hba.phy[i].phy->minimum_linkrate +
+ (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
+ } else {
+ sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
+ (rates->minimum_linkrate +
+ (rates->maximum_linkrate << 4));
+ }
+ }
+
+ if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
+ sz)) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* link reset */
+ _transport_phy_reset(phy, 0);
+
+ /* read phy page 0, then update the rates in the sas transport phy */
+ if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ mpt2sas_phy->phy_id)) {
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate >> 4);
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.NegotiatedLinkRate &
+ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ }
+
+ out:
+ kfree(sas_iounit_pg1);
+ return rc;
+}
+
+
+/**
* _transport_smp_handler - transport portal for smp passthru
* @shost: shost object
* @rphy: sas transport rphy object
@@ -1207,6 +1373,8 @@ struct sas_function_template mpt2sas_transport_functions = {
.get_enclosure_identifier = _transport_get_enclosure_identifier,
.get_bay_identifier = _transport_get_bay_identifier,
.phy_reset = _transport_phy_reset,
+ .phy_enable = _transport_phy_enable,
+ .set_phy_speed = _transport_phy_speed,
.smp_handler = _transport_smp_handler,
};
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index c2f1032496c..f80c1da8f6c 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -654,7 +654,7 @@ static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
}
chip = &pm8001_chips[ent->driver_data];
SHOST_TO_SAS_HA(shost) =
- kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
+ kzalloc(sizeof(struct sas_ha_struct), GFP_KERNEL);
if (!SHOST_TO_SAS_HA(shost)) {
rc = -ENOMEM;
goto err_out_free_host;
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index b6f1ef954af..9b1c1433c26 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -235,7 +235,7 @@ static int pmcraid_slave_configure(struct scsi_device *scsi_dev)
scsi_dev->allow_restart = 1;
blk_queue_rq_timeout(scsi_dev->request_queue,
PMCRAID_VSET_IO_TIMEOUT);
- blk_queue_max_sectors(scsi_dev->request_queue,
+ blk_queue_max_hw_sectors(scsi_dev->request_queue,
PMCRAID_VSET_MAX_SECTORS);
}
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index 8371d917a9a..49ac4148493 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -1640,8 +1640,10 @@ qla1280_load_firmware_pio(struct scsi_qla_host *ha)
uint16_t mb[MAILBOX_REGISTER_COUNT], i;
int err;
+ spin_unlock_irq(ha->host->host_lock);
err = request_firmware(&fw, ql1280_board_tbl[ha->devnum].fwname,
&ha->pdev->dev);
+ spin_lock_irq(ha->host->host_lock);
if (err) {
printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
ql1280_board_tbl[ha->devnum].fwname, err);
@@ -1699,8 +1701,10 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha)
return -ENOMEM;
#endif
+ spin_unlock_irq(ha->host->host_lock);
err = request_firmware(&fw, ql1280_board_tbl[ha->devnum].fwname,
&ha->pdev->dev);
+ spin_lock_irq(ha->host->host_lock);
if (err) {
printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
ql1280_board_tbl[ha->devnum].fwname, err);
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 3a9f5b288ae..90d1e062ec4 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -11,7 +11,9 @@
#include <linux/delay.h>
static int qla24xx_vport_disable(struct fc_vport *, bool);
-
+static int qla84xx_reset(scsi_qla_host_t *, struct msg_echo_lb *, struct fc_bsg_job *);
+int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t, uint16_t *);
+static int qla84xx_mgmt_cmd(scsi_qla_host_t *, struct msg_echo_lb *, struct fc_bsg_job *);
/* SYSFS attributes --------------------------------------------------------- */
static ssize_t
@@ -1168,6 +1170,28 @@ qla2x00_total_isp_aborts_show(struct device *dev,
}
static ssize_t
+qla24xx_84xx_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rval = QLA_SUCCESS;
+ uint16_t status[2] = {0, 0};
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA84XX(ha) && ha->cs84xx) {
+ if (ha->cs84xx->op_fw_version == 0) {
+ rval = qla84xx_verify_chip(vha, status);
+ }
+
+ if ((rval == QLA_SUCCESS) && (status[0] == 0))
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ (uint32_t)ha->cs84xx->op_fw_version);
+ }
+
+ return snprintf(buf, PAGE_SIZE, "\n");
+}
+
+static ssize_t
qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -1281,6 +1305,8 @@ static DEVICE_ATTR(optrom_fcode_version, S_IRUGO,
qla2x00_optrom_fcode_version_show, NULL);
static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show,
NULL);
+static DEVICE_ATTR(84xx_fw_version, S_IRUGO, qla24xx_84xx_fw_version_show,
+ NULL);
static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show,
NULL);
static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL);
@@ -1310,6 +1336,7 @@ struct device_attribute *qla2x00_host_attrs[] = {
&dev_attr_optrom_efi_version,
&dev_attr_optrom_fcode_version,
&dev_attr_optrom_fw_version,
+ &dev_attr_84xx_fw_version,
&dev_attr_total_isp_aborts,
&dev_attr_mpi_version,
&dev_attr_phy_version,
@@ -1504,8 +1531,6 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa);
-
- qla2x00_abort_fcport_cmds(fcport);
}
static int
@@ -1795,6 +1820,581 @@ qla24xx_vport_disable(struct fc_vport *fc_vport, bool disable)
return 0;
}
+/* BSG support for ELS/CT pass through */
+inline srb_t *
+qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+ struct srb_bsg_ctx *ctx;
+
+ sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL);
+ if (!sp)
+ goto done;
+ ctx = kzalloc(size, GFP_KERNEL);
+ if (!ctx) {
+ mempool_free(sp, ha->srb_mempool);
+ goto done;
+ }
+
+ memset(sp, 0, sizeof(*sp));
+ sp->fcport = fcport;
+ sp->ctx = ctx;
+done:
+ return sp;
+}
+
+static int
+qla2x00_process_els(struct fc_bsg_job *bsg_job)
+{
+ struct fc_rport *rport;
+ fc_port_t *fcport;
+ struct Scsi_Host *host;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ srb_t *sp;
+ const char *type;
+ int req_sg_cnt, rsp_sg_cnt;
+ int rval = (DRIVER_ERROR << 16);
+ uint16_t nextlid = 0;
+ struct srb_bsg *els;
+
+ /* Multiple SG's are not supported for ELS requests */
+ if (bsg_job->request_payload.sg_cnt > 1 ||
+ bsg_job->reply_payload.sg_cnt > 1) {
+ DEBUG2(printk(KERN_INFO
+ "multiple SG's are not supported for ELS requests"
+ " [request_sg_cnt: %x reply_sg_cnt: %x]\n",
+ bsg_job->request_payload.sg_cnt,
+ bsg_job->reply_payload.sg_cnt));
+ rval = -EPERM;
+ goto done;
+ }
+
+ /* ELS request for rport */
+ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
+ rport = bsg_job->rport;
+ fcport = *(fc_port_t **) rport->dd_data;
+ host = rport_to_shost(rport);
+ vha = shost_priv(host);
+ ha = vha->hw;
+ type = "FC_BSG_RPT_ELS";
+
+ /* make sure the rport is logged in,
+ * if not perform fabric login
+ */
+ if (qla2x00_fabric_login(vha, fcport, &nextlid)) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "failed to login port %06X for ELS passthru\n",
+ fcport->d_id.b24));
+ rval = -EIO;
+ goto done;
+ }
+ } else {
+ host = bsg_job->shost;
+ vha = shost_priv(host);
+ ha = vha->hw;
+ type = "FC_BSG_HST_ELS_NOLOGIN";
+
+ /* Allocate a dummy fcport structure, since functions
+ * preparing the IOCB and mailbox command retrieves port
+ * specific information from fcport structure. For Host based
+ * ELS commands there will be no fcport structure allocated
+ */
+ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (!fcport) {
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize all required fields of fcport */
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ fcport->d_id.b.al_pa =
+ bsg_job->request->rqst_data.h_els.port_id[0];
+ fcport->d_id.b.area =
+ bsg_job->request->rqst_data.h_els.port_id[1];
+ fcport->d_id.b.domain =
+ bsg_job->request->rqst_data.h_els.port_id[2];
+ fcport->loop_id =
+ (fcport->d_id.b.al_pa == 0xFD) ?
+ NPH_FABRIC_CONTROLLER : NPH_F_PORT;
+ }
+
+ if (!vha->flags.online) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "host not online\n"));
+ rval = -EIO;
+ goto done;
+ }
+
+ req_sg_cnt =
+ dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!req_sg_cnt) {
+ rval = -ENOMEM;
+ goto done_free_fcport;
+ }
+ rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (!rsp_sg_cnt) {
+ rval = -ENOMEM;
+ goto done_free_fcport;
+ }
+
+ if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
+ (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt))
+ {
+ DEBUG2(printk(KERN_INFO
+ "dma mapping resulted in different sg counts \
+ [request_sg_cnt: %x dma_request_sg_cnt: %x\
+ reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n",
+ bsg_job->request_payload.sg_cnt, req_sg_cnt,
+ bsg_job->reply_payload.sg_cnt, rsp_sg_cnt));
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ /* Alloc SRB structure */
+ sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_bsg));
+ if (!sp) {
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ els = sp->ctx;
+ els->ctx.type =
+ (bsg_job->request->msgcode == FC_BSG_RPT_ELS ?
+ SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST);
+ els->bsg_job = bsg_job;
+
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x "
+ "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type,
+ bsg_job->request->rqst_data.h_els.command_code,
+ fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS) {
+ kfree(sp->ctx);
+ mempool_free(sp, ha->srb_mempool);
+ rval = -EIO;
+ goto done_unmap_sg;
+ }
+ return rval;
+
+done_unmap_sg:
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ goto done_free_fcport;
+
+done_free_fcport:
+ if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN)
+ kfree(fcport);
+done:
+ return rval;
+}
+
+static int
+qla2x00_process_ct(struct fc_bsg_job *bsg_job)
+{
+ srb_t *sp;
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = (DRIVER_ERROR << 16);
+ int req_sg_cnt, rsp_sg_cnt;
+ uint16_t loop_id;
+ struct fc_port *fcport;
+ char *type = "FC_BSG_HST_CT";
+ struct srb_bsg *ct;
+
+ /* pass through is supported only for ISP 4Gb or higher */
+ if (!IS_FWI2_CAPABLE(ha)) {
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld):Firmware is not capable to support FC "
+ "CT pass thru\n", vha->host_no));
+ rval = -EPERM;
+ goto done;
+ }
+
+ req_sg_cnt =
+ dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!req_sg_cnt) {
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (!rsp_sg_cnt) {
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
+ (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt))
+ {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "dma mapping resulted in different sg counts \
+ [request_sg_cnt: %x dma_request_sg_cnt: %x\
+ reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n",
+ bsg_job->request_payload.sg_cnt, req_sg_cnt,
+ bsg_job->reply_payload.sg_cnt, rsp_sg_cnt));
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ if (!vha->flags.online) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "host not online\n"));
+ rval = -EIO;
+ goto done_unmap_sg;
+ }
+
+ loop_id =
+ (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000)
+ >> 24;
+ switch (loop_id) {
+ case 0xFC:
+ loop_id = cpu_to_le16(NPH_SNS);
+ break;
+ case 0xFA:
+ loop_id = vha->mgmt_svr_loop_id;
+ break;
+ default:
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "Unknown loop id: %x\n", loop_id));
+ rval = -EINVAL;
+ goto done_unmap_sg;
+ }
+
+ /* Allocate a dummy fcport structure, since functions preparing the
+ * IOCB and mailbox command retrieves port specific information
+ * from fcport structure. For Host based ELS commands there will be
+ * no fcport structure allocated
+ */
+ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (!fcport)
+ {
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ /* Initialize all required fields of fcport */
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0];
+ fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1];
+ fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2];
+ fcport->loop_id = loop_id;
+
+ /* Alloc SRB structure */
+ sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_bsg));
+ if (!sp) {
+ rval = -ENOMEM;
+ goto done_free_fcport;
+ }
+
+ ct = sp->ctx;
+ ct->ctx.type = SRB_CT_CMD;
+ ct->bsg_job = bsg_job;
+
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x "
+ "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type,
+ (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16),
+ fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS) {
+ kfree(sp->ctx);
+ mempool_free(sp, ha->srb_mempool);
+ rval = -EIO;
+ goto done_free_fcport;
+ }
+ return rval;
+
+done_free_fcport:
+ kfree(fcport);
+done_unmap_sg:
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+done:
+ return rval;
+}
+
+static int
+qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+ uint8_t command_sent;
+ uint32_t vendor_cmd;
+ char *type;
+ struct msg_echo_lb elreq;
+ uint16_t response[MAILBOX_REGISTER_COUNT];
+ uint8_t* fw_sts_ptr;
+ uint8_t *req_data;
+ dma_addr_t req_data_dma;
+ uint32_t req_data_len;
+ uint8_t *rsp_data;
+ dma_addr_t rsp_data_dma;
+ uint32_t rsp_data_len;
+
+ if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
+ test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+ rval = -EBUSY;
+ goto done;
+ }
+
+ if (!vha->flags.online) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "host not online\n"));
+ rval = -EIO;
+ goto done;
+ }
+
+ elreq.req_sg_cnt =
+ dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!elreq.req_sg_cnt) {
+ rval = -ENOMEM;
+ goto done;
+ }
+ elreq.rsp_sg_cnt =
+ dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (!elreq.rsp_sg_cnt) {
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
+ (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt))
+ {
+ DEBUG2(printk(KERN_INFO
+ "dma mapping resulted in different sg counts \
+ [request_sg_cnt: %x dma_request_sg_cnt: %x\
+ reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n",
+ bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt,
+ bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt));
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+ req_data_len = rsp_data_len = bsg_job->request_payload.payload_len;
+ req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len,
+ &req_data_dma, GFP_KERNEL);
+
+ rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len,
+ &rsp_data_dma, GFP_KERNEL);
+
+ /* Copy the request buffer in req_data now */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, req_data,
+ req_data_len);
+
+ elreq.send_dma = req_data_dma;
+ elreq.rcv_dma = rsp_data_dma;
+ elreq.transfer_size = req_data_len;
+
+ /* Vendor cmd : loopback or ECHO diagnostic
+ * Options:
+ * Loopback : Either internal or external loopback
+ * ECHO: ECHO ELS or Vendor specific FC4 link data
+ */
+ vendor_cmd = bsg_job->request->rqst_data.h_vendor.vendor_cmd[0];
+ elreq.options =
+ *(((uint32_t *)bsg_job->request->rqst_data.h_vendor.vendor_cmd)
+ + 1);
+
+ switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) {
+ case QL_VND_LOOPBACK:
+ if (ha->current_topology != ISP_CFG_F) {
+ type = "FC_BSG_HST_VENDOR_LOOPBACK";
+
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld) bsg rqst type: %s vendor rqst type: %x options: %x.\n",
+ vha->host_no, type, vendor_cmd, elreq.options));
+
+ command_sent = INT_DEF_LB_LOOPBACK_CMD;
+ rval = qla2x00_loopback_test(vha, &elreq, response);
+ if (IS_QLA81XX(ha)) {
+ if (response[0] == MBS_COMMAND_ERROR && response[1] == MBS_LB_RESET) {
+ DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing "
+ "ISP\n", __func__, vha->host_no));
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ }
+ }
+ } else {
+ type = "FC_BSG_HST_VENDOR_ECHO_DIAG";
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld) bsg rqst type: %s vendor rqst type: %x options: %x.\n",
+ vha->host_no, type, vendor_cmd, elreq.options));
+
+ command_sent = INT_DEF_LB_ECHO_CMD;
+ rval = qla2x00_echo_test(vha, &elreq, response);
+ }
+ break;
+ case QLA84_RESET:
+ if (!IS_QLA84XX(vha->hw)) {
+ rval = -EINVAL;
+ DEBUG16(printk(
+ "%s(%ld): 8xxx exiting.\n",
+ __func__, vha->host_no));
+ return rval;
+ }
+ rval = qla84xx_reset(vha, &elreq, bsg_job);
+ break;
+ case QLA84_MGMT_CMD:
+ if (!IS_QLA84XX(vha->hw)) {
+ rval = -EINVAL;
+ DEBUG16(printk(
+ "%s(%ld): 8xxx exiting.\n",
+ __func__, vha->host_no));
+ return rval;
+ }
+ rval = qla84xx_mgmt_cmd(vha, &elreq, bsg_job);
+ break;
+ default:
+ rval = -ENOSYS;
+ }
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "scsi(%ld) Vendor request %s failed\n", vha->host_no, type));
+ rval = 0;
+ bsg_job->reply->result = (DID_ERROR << 16);
+ bsg_job->reply->reply_payload_rcv_len = 0;
+ fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
+ memcpy( fw_sts_ptr, response, sizeof(response));
+ fw_sts_ptr += sizeof(response);
+ *fw_sts_ptr = command_sent;
+ } else {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "scsi(%ld) Vendor request %s completed\n", vha->host_no, type));
+ rval = bsg_job->reply->result = 0;
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(response) + sizeof(uint8_t);
+ bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
+ fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
+ memcpy(fw_sts_ptr, response, sizeof(response));
+ fw_sts_ptr += sizeof(response);
+ *fw_sts_ptr = command_sent;
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, rsp_data,
+ rsp_data_len);
+ }
+ bsg_job->job_done(bsg_job);
+
+done_unmap_sg:
+
+ if(req_data)
+ dma_free_coherent(&ha->pdev->dev, req_data_len,
+ req_data, req_data_dma);
+ dma_unmap_sg(&ha->pdev->dev,
+ bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(&ha->pdev->dev,
+ bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+done:
+ return rval;
+}
+
+static int
+qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
+{
+ int ret = -EINVAL;
+
+ switch (bsg_job->request->msgcode) {
+ case FC_BSG_RPT_ELS:
+ case FC_BSG_HST_ELS_NOLOGIN:
+ ret = qla2x00_process_els(bsg_job);
+ break;
+ case FC_BSG_HST_CT:
+ ret = qla2x00_process_ct(bsg_job);
+ break;
+ case FC_BSG_HST_VENDOR:
+ ret = qla2x00_process_vendor_specific(bsg_job);
+ break;
+ case FC_BSG_HST_ADD_RPORT:
+ case FC_BSG_HST_DEL_RPORT:
+ case FC_BSG_RPT_CT:
+ default:
+ DEBUG2(printk("qla2xxx: unsupported BSG request\n"));
+ break;
+ }
+ return ret;
+}
+
+static int
+qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job)
+{
+ scsi_qla_host_t *vha = shost_priv(bsg_job->shost);
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp;
+ int cnt, que;
+ unsigned long flags;
+ struct req_que *req;
+ struct srb_bsg *sp_bsg;
+
+ /* find the bsg job from the active list of commands */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (que = 0; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ continue;
+
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++ ) {
+ sp = req->outstanding_cmds[cnt];
+
+ if (sp) {
+ sp_bsg = (struct srb_bsg*)sp->ctx;
+
+ if (((sp_bsg->ctx.type == SRB_CT_CMD) ||
+ (sp_bsg->ctx.type == SRB_ELS_CMD_RPT)
+ || ( sp_bsg->ctx.type == SRB_ELS_CMD_HST)) &&
+ (sp_bsg->bsg_job == bsg_job)) {
+ if (ha->isp_ops->abort_command(sp)) {
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld): mbx abort_command failed\n", vha->host_no));
+ bsg_job->req->errors = bsg_job->reply->result = -EIO;
+ } else {
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld): mbx abort_command success\n", vha->host_no));
+ bsg_job->req->errors = bsg_job->reply->result = 0;
+ }
+ goto done;
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "scsi(%ld) SRB not found to abort\n", vha->host_no));
+ bsg_job->req->errors = bsg_job->reply->result = -ENXIO;
+ return 0;
+
+done:
+ if (bsg_job->request->msgcode == FC_BSG_HST_CT)
+ kfree(sp->fcport);
+ kfree(sp->ctx);
+ mempool_free(sp, ha->srb_mempool);
+ return 0;
+}
+
struct fc_function_template qla2xxx_transport_functions = {
.show_host_node_name = 1,
@@ -1838,6 +2438,8 @@ struct fc_function_template qla2xxx_transport_functions = {
.vport_create = qla24xx_vport_create,
.vport_disable = qla24xx_vport_disable,
.vport_delete = qla24xx_vport_delete,
+ .bsg_request = qla24xx_bsg_request,
+ .bsg_timeout = qla24xx_bsg_timeout,
};
struct fc_function_template qla2xxx_transport_vport_functions = {
@@ -1878,6 +2480,8 @@ struct fc_function_template qla2xxx_transport_vport_functions = {
.dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
.terminate_rport_io = qla2x00_terminate_rport_io,
.get_fc_host_stats = qla2x00_get_fc_host_stats,
+ .bsg_request = qla24xx_bsg_request,
+ .bsg_timeout = qla24xx_bsg_timeout,
};
void
@@ -1906,3 +2510,125 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha)
speed = FC_PORTSPEED_1GBIT;
fc_host_supported_speeds(vha->host) = speed;
}
+static int
+qla84xx_reset(scsi_qla_host_t *ha, struct msg_echo_lb *mreq, struct fc_bsg_job *bsg_job)
+{
+ int ret = 0;
+ int cmd;
+ uint16_t cmd_status;
+
+ DEBUG16(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+ cmd = (*((bsg_job->request->rqst_data.h_vendor.vendor_cmd) + 2))
+ == A84_RESET_FLAG_ENABLE_DIAG_FW ?
+ A84_ISSUE_RESET_DIAG_FW : A84_ISSUE_RESET_OP_FW;
+ ret = qla84xx_reset_chip(ha, cmd == A84_ISSUE_RESET_DIAG_FW,
+ &cmd_status);
+ return ret;
+}
+
+static int
+qla84xx_mgmt_cmd(scsi_qla_host_t *ha, struct msg_echo_lb *mreq, struct fc_bsg_job *bsg_job)
+{
+ struct access_chip_84xx *mn;
+ dma_addr_t mn_dma, mgmt_dma;
+ void *mgmt_b = NULL;
+ int ret = 0;
+ int rsp_hdr_len, len = 0;
+ struct qla84_msg_mgmt *ql84_mgmt;
+
+ ql84_mgmt = (struct qla84_msg_mgmt *) vmalloc(sizeof(struct qla84_msg_mgmt));
+ ql84_mgmt->cmd =
+ *((uint16_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 2));
+ ql84_mgmt->mgmtp.u.mem.start_addr =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 3));
+ ql84_mgmt->len =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 4));
+ ql84_mgmt->mgmtp.u.config.id =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 5));
+ ql84_mgmt->mgmtp.u.config.param0 =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 6));
+ ql84_mgmt->mgmtp.u.config.param1 =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 7));
+ ql84_mgmt->mgmtp.u.info.type =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 8));
+ ql84_mgmt->mgmtp.u.info.context =
+ *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 9));
+
+ rsp_hdr_len = bsg_job->request_payload.payload_len;
+
+ mn = dma_pool_alloc(ha->hw->s_dma_pool, GFP_KERNEL, &mn_dma);
+ if (mn == NULL) {
+ DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer "
+ "failed%lu\n", __func__, ha->host_no));
+ return -ENOMEM;
+ }
+
+ memset(mn, 0, sizeof (struct access_chip_84xx));
+
+ mn->entry_type = ACCESS_CHIP_IOCB_TYPE;
+ mn->entry_count = 1;
+
+ switch (ql84_mgmt->cmd) {
+ case QLA84_MGMT_READ_MEM:
+ mn->options = cpu_to_le16(ACO_DUMP_MEMORY);
+ mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.mem.start_addr);
+ break;
+ case QLA84_MGMT_WRITE_MEM:
+ mn->options = cpu_to_le16(ACO_LOAD_MEMORY);
+ mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.mem.start_addr);
+ break;
+ case QLA84_MGMT_CHNG_CONFIG:
+ mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM);
+ mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.id);
+ mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.param0);
+ mn->parameter3 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.param1);
+ break;
+ case QLA84_MGMT_GET_INFO:
+ mn->options = cpu_to_le16(ACO_REQUEST_INFO);
+ mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.info.type);
+ mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmtp.u.info.context);
+ break;
+ default:
+ ret = -EIO;
+ goto exit_mgmt0;
+ }
+
+ if ((len == ql84_mgmt->len) &&
+ ql84_mgmt->cmd != QLA84_MGMT_CHNG_CONFIG) {
+ mgmt_b = dma_alloc_coherent(&ha->hw->pdev->dev, len,
+ &mgmt_dma, GFP_KERNEL);
+ if (mgmt_b == NULL) {
+ DEBUG2(printk(KERN_ERR "%s: dma alloc mgmt_b "
+ "failed%lu\n", __func__, ha->host_no));
+ ret = -ENOMEM;
+ goto exit_mgmt0;
+ }
+ mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->len);
+ mn->dseg_count = cpu_to_le16(1);
+ mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma));
+ mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma));
+ mn->dseg_length = cpu_to_le32(len);
+
+ if (ql84_mgmt->cmd == QLA84_MGMT_WRITE_MEM) {
+ memcpy(mgmt_b, ql84_mgmt->payload, len);
+ }
+ }
+
+ ret = qla2x00_issue_iocb(ha, mn, mn_dma, 0);
+ if ((ret != QLA_SUCCESS) || (ql84_mgmt->cmd == QLA84_MGMT_WRITE_MEM)
+ || (ql84_mgmt->cmd == QLA84_MGMT_CHNG_CONFIG)) {
+ if (ret != QLA_SUCCESS)
+ DEBUG2(printk(KERN_ERR "%s(%lu): failed\n",
+ __func__, ha->host_no));
+ } else if ((ql84_mgmt->cmd == QLA84_MGMT_READ_MEM) ||
+ (ql84_mgmt->cmd == QLA84_MGMT_GET_INFO)) {
+ }
+
+ if (mgmt_b)
+ dma_free_coherent(&ha->hw->pdev->dev, len, mgmt_b, mgmt_dma);
+
+exit_mgmt0:
+ dma_pool_free(ha->hw->s_dma_pool, mn, mn_dma);
+ return ret;
+}
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 1263d9796e8..afa95614aaf 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -31,6 +31,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
#define QLA2XXX_DRIVER_NAME "qla2xxx"
@@ -228,6 +229,27 @@ struct srb_logio {
uint16_t flags;
};
+struct srb_bsg_ctx {
+#define SRB_ELS_CMD_RPT 3
+#define SRB_ELS_CMD_HST 4
+#define SRB_CT_CMD 5
+ uint16_t type;
+};
+
+struct srb_bsg {
+ struct srb_bsg_ctx ctx;
+ struct fc_bsg_job *bsg_job;
+};
+
+struct msg_echo_lb {
+ dma_addr_t send_dma;
+ dma_addr_t rcv_dma;
+ uint16_t req_sg_cnt;
+ uint16_t rsp_sg_cnt;
+ uint16_t options;
+ uint32_t transfer_size;
+};
+
/*
* ISP I/O Register Set structure definitions.
*/
@@ -522,6 +544,8 @@ typedef struct {
#define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */
#define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */
+/* ISP mailbox loopback echo diagnostic error code */
+#define MBS_LB_RESET 0x17
/*
* Firmware options 1, 2, 3.
*/
@@ -2230,6 +2254,13 @@ struct req_que {
int max_q_depth;
};
+/* Place holder for FW buffer parameters */
+struct qlfc_fw {
+ void *fw_buf;
+ dma_addr_t fw_dma;
+ uint32_t len;
+};
+
/*
* Qlogic host adapter specific data structure.
*/
@@ -2594,6 +2625,7 @@ struct qla_hw_data {
struct qla_statistics qla_stats;
struct isp_operations *isp_ops;
struct workqueue_struct *wq;
+ struct qlfc_fw fw_buf;
};
/*
@@ -2766,4 +2798,127 @@ typedef struct scsi_qla_host {
#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
+/*
+ * BSG Vendor specific commands
+ */
+
+#define QL_VND_LOOPBACK 0x01
+#define QLA84_RESET 0x02
+#define QLA84_UPDATE_FW 0x03
+#define QLA84_MGMT_CMD 0x04
+
+/* BSG definations for interpreting CommandSent field */
+#define INT_DEF_LB_LOOPBACK_CMD 0
+#define INT_DEF_LB_ECHO_CMD 1
+
+/* BSG Vendor specific definations */
+typedef struct _A84_RESET {
+ uint16_t Flags;
+ uint16_t Reserved;
+#define A84_RESET_FLAG_ENABLE_DIAG_FW 1
+} __attribute__((packed)) A84_RESET, *PA84_RESET;
+
+#define A84_ISSUE_WRITE_TYPE_CMD 0
+#define A84_ISSUE_READ_TYPE_CMD 1
+#define A84_CLEANUP_CMD 2
+#define A84_ISSUE_RESET_OP_FW 3
+#define A84_ISSUE_RESET_DIAG_FW 4
+#define A84_ISSUE_UPDATE_OPFW_CMD 5
+#define A84_ISSUE_UPDATE_DIAGFW_CMD 6
+
+struct qla84_mgmt_param {
+ union {
+ struct {
+ uint32_t start_addr;
+ } mem; /* for QLA84_MGMT_READ/WRITE_MEM */
+ struct {
+ uint32_t id;
+#define QLA84_MGMT_CONFIG_ID_UIF 1
+#define QLA84_MGMT_CONFIG_ID_FCOE_COS 2
+#define QLA84_MGMT_CONFIG_ID_PAUSE 3
+#define QLA84_MGMT_CONFIG_ID_TIMEOUTS 4
+
+ uint32_t param0;
+ uint32_t param1;
+ } config; /* for QLA84_MGMT_CHNG_CONFIG */
+
+ struct {
+ uint32_t type;
+#define QLA84_MGMT_INFO_CONFIG_LOG_DATA 1 /* Get Config Log Data */
+#define QLA84_MGMT_INFO_LOG_DATA 2 /* Get Log Data */
+#define QLA84_MGMT_INFO_PORT_STAT 3 /* Get Port Statistics */
+#define QLA84_MGMT_INFO_LIF_STAT 4 /* Get LIF Statistics */
+#define QLA84_MGMT_INFO_ASIC_STAT 5 /* Get ASIC Statistics */
+#define QLA84_MGMT_INFO_CONFIG_PARAMS 6 /* Get Config Parameters */
+#define QLA84_MGMT_INFO_PANIC_LOG 7 /* Get Panic Log */
+
+ uint32_t context;
+/*
+* context definitions for QLA84_MGMT_INFO_CONFIG_LOG_DATA
+*/
+#define IC_LOG_DATA_LOG_ID_DEBUG_LOG 0
+#define IC_LOG_DATA_LOG_ID_LEARN_LOG 1
+#define IC_LOG_DATA_LOG_ID_FC_ACL_INGRESS_LOG 2
+#define IC_LOG_DATA_LOG_ID_FC_ACL_EGRESS_LOG 3
+#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_INGRESS_LOG 4
+#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_EGRESS_LOG 5
+#define IC_LOG_DATA_LOG_ID_MESSAGE_TRANSMIT_LOG 6
+#define IC_LOG_DATA_LOG_ID_MESSAGE_RECEIVE_LOG 7
+#define IC_LOG_DATA_LOG_ID_LINK_EVENT_LOG 8
+#define IC_LOG_DATA_LOG_ID_DCX_LOG 9
+
+/*
+* context definitions for QLA84_MGMT_INFO_PORT_STAT
+*/
+#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT0 0
+#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT1 1
+#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT0 2
+#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT1 3
+#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT0 4
+#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT1 5
+
+
+/*
+* context definitions for QLA84_MGMT_INFO_LIF_STAT
+*/
+#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT0 0
+#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT1 1
+#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT0 2
+#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT1 3
+#define IC_LIF_STATISTICS_LIF_NUMBER_CPU 6
+
+ } info; /* for QLA84_MGMT_GET_INFO */
+ } u;
+};
+
+struct qla84_msg_mgmt {
+ uint16_t cmd;
+#define QLA84_MGMT_READ_MEM 0x00
+#define QLA84_MGMT_WRITE_MEM 0x01
+#define QLA84_MGMT_CHNG_CONFIG 0x02
+#define QLA84_MGMT_GET_INFO 0x03
+ uint16_t rsrvd;
+ struct qla84_mgmt_param mgmtp;/* parameters for cmd */
+ uint32_t len; /* bytes in payload following this struct */
+ uint8_t payload[0]; /* payload for cmd */
+};
+
+struct msg_update_fw {
+ /*
+ * diag_fw = 0 operational fw
+ * otherwise diagnostic fw
+ * offset, len, fw_len are present to overcome the current limitation
+ * of 128Kb xfer size. The fw is sent in smaller chunks. Each chunk
+ * specifies the byte "offset" where it fits in the fw buffer. The
+ * number of bytes in each chunk is specified in "len". "fw_len"
+ * is the total size of fw. The first chunk should start at offset = 0.
+ * When offset+len == fw_len, the fw is written to the HBA.
+ */
+ uint32_t diag_fw;
+ uint32_t offset;/* start offset */
+ uint32_t len; /* num bytes in cur xfer */
+ uint32_t fw_len; /* size of fw in bytes */
+ uint8_t fw_bytes[0];
+};
+
#endif
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index 66a8da5d7d0..cebf4f1bb7d 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -627,6 +627,39 @@ struct els_entry_24xx {
uint32_t rx_len; /* Data segment 1 length. */
};
+struct els_sts_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t comp_status;
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t reserved_1;
+
+ uint8_t vp_index;
+ uint8_t sof_type;
+
+ uint32_t rx_xchg_address; /* Receive exchange address. */
+ uint16_t reserved_2;
+
+ uint8_t opcode;
+ uint8_t reserved_3;
+
+ uint8_t port_id[3];
+ uint8_t reserved_4;
+
+ uint16_t reserved_5;
+
+ uint16_t control_flags; /* Control flags. */
+ uint32_t total_byte_count;
+ uint32_t error_subcode_1;
+ uint32_t error_subcode_2;
+};
/*
* ISP queue - Mailbox Command entry structure definition.
*/
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 8bc6f53691e..3a89bc514e2 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -60,6 +60,8 @@ extern int qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *,
extern int qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
+extern fc_port_t *
+qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t );
/*
* Global Data in qla_os.c source file.
*/
@@ -76,6 +78,7 @@ extern int ql2xiidmaenable;
extern int ql2xmaxqueues;
extern int ql2xmultique_tag;
extern int ql2xfwloadbin;
+extern int ql2xetsenable;
extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -94,7 +97,6 @@ extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
-extern void qla2x00_abort_fcport_cmds(fc_port_t *);
extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *,
struct qla_hw_data *);
extern void qla2x00_free_host(struct scsi_qla_host *);
@@ -154,6 +156,7 @@ int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
int __qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
uint16_t, uint16_t, uint8_t);
extern int qla2x00_start_sp(srb_t *);
+extern void qla2x00_ctx_sp_free(srb_t *);
/*
* Global Function Prototypes in qla_mbx.c source file.
@@ -426,6 +429,8 @@ extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
extern void qla2x00_init_host_attr(scsi_qla_host_t *);
extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
+extern int qla2x00_loopback_test(scsi_qla_host_t *, struct msg_echo_lb *, uint16_t *);
+extern int qla2x00_echo_test(scsi_qla_host_t *, struct msg_echo_lb *, uint16_t *);
/*
* Global Function Prototypes in qla_dfs.c source file.
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 3f8e8495b74..a67b2bafb88 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -62,7 +62,7 @@ qla2x00_ctx_sp_timeout(unsigned long __data)
ctx->free(sp);
}
-static void
+void
qla2x00_ctx_sp_free(srb_t *sp)
{
struct srb_ctx *ctx = sp->ctx;
@@ -338,6 +338,16 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
rval = qla2x00_init_rings(vha);
ha->flags.chip_reset_done = 1;
+ if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) {
+ /* Issue verify 84xx FW IOCB to complete 84xx initialization */
+ rval = qla84xx_init_chip(vha);
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_ERR, ha,
+ "Unable to initialize ISP84XX.\n");
+ qla84xx_put_chip(vha);
+ }
+ }
+
return (rval);
}
@@ -2216,7 +2226,7 @@ qla2x00_rport_del(void *data)
*
* Returns a pointer to the allocated fcport, or NULL, if none available.
*/
-static fc_port_t *
+fc_port_t *
qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
{
fc_port_t *fcport;
@@ -2900,8 +2910,13 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
if (qla2x00_is_reserved_id(vha, loop_id))
continue;
- if (atomic_read(&vha->loop_down_timer) || LOOP_TRANSITION(vha))
+ if (atomic_read(&vha->loop_down_timer) ||
+ LOOP_TRANSITION(vha)) {
+ atomic_set(&vha->loop_down_timer, 0);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
break;
+ }
if (swl != NULL) {
if (last_dev) {
@@ -4877,6 +4892,15 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
}
void
-qla81xx_update_fw_options(scsi_qla_host_t *ha)
+qla81xx_update_fw_options(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!ql2xetsenable)
+ return;
+
+ /* Enable ETS Burst. */
+ memset(ha->fw_options, 0, sizeof(ha->fw_options));
+ ha->fw_options[2] |= BIT_9;
+ qla2x00_set_fw_options(vha, ha->fw_options);
}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index c5ccac0bef7..8299a9891bf 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -1025,6 +1025,119 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
/* Implicit: mbx->mbx10 = 0. */
}
+static void
+qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
+{
+ struct fc_bsg_job *bsg_job = ((struct srb_bsg*)sp->ctx)->bsg_job;
+
+ els_iocb->entry_type = ELS_IOCB_TYPE;
+ els_iocb->entry_count = 1;
+ els_iocb->sys_define = 0;
+ els_iocb->entry_status = 0;
+ els_iocb->handle = sp->handle;
+ els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ els_iocb->tx_dsd_count = __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
+ els_iocb->vp_index = sp->fcport->vp_idx;
+ els_iocb->sof_type = EST_SOFI3;
+ els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
+
+ els_iocb->opcode =(((struct srb_bsg*)sp->ctx)->ctx.type == SRB_ELS_CMD_RPT) ?
+ bsg_job->request->rqst_data.r_els.els_code : bsg_job->request->rqst_data.h_els.command_code;
+ els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
+ els_iocb->port_id[1] = sp->fcport->d_id.b.area;
+ els_iocb->port_id[2] = sp->fcport->d_id.b.domain;
+ els_iocb->control_flags = 0;
+ els_iocb->rx_byte_count =
+ cpu_to_le32(bsg_job->reply_payload.payload_len);
+ els_iocb->tx_byte_count =
+ cpu_to_le32(bsg_job->request_payload.payload_len);
+
+ els_iocb->tx_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ els_iocb->tx_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ els_iocb->tx_len = cpu_to_le32(sg_dma_len
+ (bsg_job->request_payload.sg_list));
+
+ els_iocb->rx_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->reply_payload.sg_list)));
+ els_iocb->rx_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->reply_payload.sg_list)));
+ els_iocb->rx_len = cpu_to_le32(sg_dma_len
+ (bsg_job->reply_payload.sg_list));
+}
+
+static void
+qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ struct scatterlist *sg;
+ int index;
+ uint16_t tot_dsds;
+ scsi_qla_host_t *vha = sp->fcport->vha;
+ struct fc_bsg_job *bsg_job = ((struct srb_bsg*)sp->ctx)->bsg_job;
+ int loop_iterartion = 0;
+ int cont_iocb_prsnt = 0;
+ int entry_count = 1;
+
+ ct_iocb->entry_type = CT_IOCB_TYPE;
+ ct_iocb->entry_status = 0;
+ ct_iocb->sys_define = 0;
+ ct_iocb->handle = sp->handle;
+
+ ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ ct_iocb->vp_index = sp->fcport->vp_idx;
+ ct_iocb->comp_status = __constant_cpu_to_le16(0);
+
+ ct_iocb->cmd_dsd_count =
+ __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
+ ct_iocb->timeout = 0;
+ ct_iocb->rsp_dsd_count =
+ __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
+ ct_iocb->rsp_byte_count =
+ cpu_to_le32(bsg_job->reply_payload.payload_len);
+ ct_iocb->cmd_byte_count =
+ cpu_to_le32(bsg_job->request_payload.payload_len);
+ ct_iocb->dseg_0_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ ct_iocb->dseg_0_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ ct_iocb->dseg_0_len = cpu_to_le32(sg_dma_len
+ (bsg_job->request_payload.sg_list));
+
+ avail_dsds = 1;
+ cur_dsd = (uint32_t *)ct_iocb->dseg_1_address;
+ index = 0;
+ tot_dsds = bsg_job->reply_payload.sg_cnt;
+
+ for_each_sg(bsg_job->reply_payload.sg_list, sg, tot_dsds, index) {
+ dma_addr_t sle_dma;
+ cont_a64_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Five DSDs are available in the Cont.
+ * Type 1 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha);
+ cur_dsd = (uint32_t *) cont_pkt->dseg_0_address;
+ avail_dsds = 5;
+ cont_iocb_prsnt = 1;
+ entry_count++;
+ }
+
+ sle_dma = sg_dma_address(sg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ loop_iterartion++;
+ avail_dsds--;
+ }
+ ct_iocb->entry_count = entry_count;
+}
+
int
qla2x00_start_sp(srb_t *sp)
{
@@ -1052,6 +1165,13 @@ qla2x00_start_sp(srb_t *sp)
qla24xx_logout_iocb(sp, pkt):
qla2x00_logout_iocb(sp, pkt);
break;
+ case SRB_ELS_CMD_RPT:
+ case SRB_ELS_CMD_HST:
+ qla24xx_els_iocb(sp, pkt);
+ break;
+ case SRB_CT_CMD:
+ qla24xx_ct_iocb(sp, pkt);
+ break;
default:
break;
}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 6fc63b98818..ab90329ff2e 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_bsg_fc.h>
static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
static void qla2x00_process_completed_request(struct scsi_qla_host *,
@@ -881,7 +882,9 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
index);
return NULL;
}
+
req->outstanding_cmds[index] = NULL;
+
done:
return sp;
}
@@ -982,6 +985,100 @@ done_post_logio_done_work:
}
static void
+qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct sts_entry_24xx *pkt, int iocb_type)
+{
+ const char func[] = "ELS_CT_IOCB";
+ const char *type;
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp;
+ struct srb_bsg *sp_bsg;
+ struct fc_bsg_job *bsg_job;
+ uint16_t comp_status;
+ uint32_t fw_status[3];
+ uint8_t* fw_sts_ptr;
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
+ if (!sp)
+ return;
+ sp_bsg = (struct srb_bsg*)sp->ctx;
+ bsg_job = sp_bsg->bsg_job;
+
+ type = NULL;
+ switch (sp_bsg->ctx.type) {
+ case SRB_ELS_CMD_RPT:
+ case SRB_ELS_CMD_HST:
+ type = "els";
+ break;
+ case SRB_CT_CMD:
+ type = "ct pass-through";
+ break;
+ default:
+ qla_printk(KERN_WARNING, ha,
+ "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp,
+ sp_bsg->ctx.type);
+ return;
+ }
+
+ comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
+ fw_status[1] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1);
+ fw_status[2] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2);
+
+ /* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
+ * fc payload to the caller
+ */
+ bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(fw_status);
+
+ if (comp_status != CS_COMPLETE) {
+ if (comp_status == CS_DATA_UNDERRUN) {
+ bsg_job->reply->result = DID_OK << 16;
+ bsg_job->reply->reply_payload_rcv_len =
+ le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count);
+
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "scsi(%ld:0x%x): ELS-CT pass-through-%s error comp_status-status=0x%x "
+ "error subcode 1=0x%x error subcode 2=0x%x total_byte = 0x%x.\n",
+ vha->host_no, sp->handle, type, comp_status, fw_status[1], fw_status[2],
+ le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count)));
+ fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
+ memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
+ }
+ else {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "scsi(%ld:0x%x): ELS-CT pass-through-%s error comp_status-status=0x%x "
+ "error subcode 1=0x%x error subcode 2=0x%x.\n",
+ vha->host_no, sp->handle, type, comp_status,
+ le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1),
+ le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2)));
+ bsg_job->reply->result = DID_ERROR << 16;
+ bsg_job->reply->reply_payload_rcv_len = 0;
+ fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
+ memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
+ }
+ DEBUG2(qla2x00_dump_buffer((uint8_t *)pkt, sizeof(*pkt)));
+ }
+ else {
+ bsg_job->reply->result = DID_OK << 16;;
+ bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
+ bsg_job->reply_len = 0;
+ }
+
+ dma_unmap_sg(&ha->pdev->dev,
+ bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(&ha->pdev->dev,
+ bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if ((sp_bsg->ctx.type == SRB_ELS_CMD_HST) ||
+ (sp_bsg->ctx.type == SRB_CT_CMD))
+ kfree(sp->fcport);
+ kfree(sp->ctx);
+ mempool_free(sp, ha->srb_mempool);
+ bsg_job->job_done(bsg_job);
+}
+
+static void
qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
struct logio_entry_24xx *logio)
{
@@ -1749,6 +1846,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla24xx_logio_entry(vha, rsp->req,
(struct logio_entry_24xx *)pkt);
break;
+ case CT_IOCB_TYPE:
+ qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE);
+ clear_bit(MBX_INTERRUPT, &vha->hw->mbx_cmd_flags);
+ break;
+ case ELS_IOCB_TYPE:
+ qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE);
+ break;
default:
/* Type Not Supported. */
DEBUG4(printk(KERN_WARNING
@@ -2049,7 +2153,6 @@ qla24xx_msix_default(int irq, void *dev_id)
set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
complete(&ha->mbx_intr_comp);
}
-
return IRQ_HANDLED;
}
@@ -2255,10 +2358,11 @@ qla2x00_free_irqs(scsi_qla_host_t *vha)
if (ha->flags.msix_enabled)
qla24xx_disable_msix(ha);
- else if (ha->flags.inta_enabled) {
+ else if (ha->flags.msi_enabled) {
free_irq(ha->pdev->irq, rsp);
pci_disable_msi(ha->pdev);
- }
+ } else
+ free_irq(ha->pdev->irq, rsp);
}
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 056e4d4505f..6e53bdbb1da 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3636,6 +3636,157 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
}
int
+qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mresp)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint32_t iter_cnt = 0x1;
+
+ DEBUG11(printk("scsi(%ld): entered.\n", vha->host_no));
+
+ memset(mcp->mb, 0 , sizeof(mcp->mb));
+ mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK;
+ mcp->mb[1] = mreq->options | BIT_6; // BIT_6 specifies 64 bit addressing
+
+ /* transfer count */
+ mcp->mb[10] = LSW(mreq->transfer_size);
+ mcp->mb[11] = MSW(mreq->transfer_size);
+
+ /* send data address */
+ mcp->mb[14] = LSW(mreq->send_dma);
+ mcp->mb[15] = MSW(mreq->send_dma);
+ mcp->mb[20] = LSW(MSD(mreq->send_dma));
+ mcp->mb[21] = MSW(MSD(mreq->send_dma));
+
+ /* recieve data address */
+ mcp->mb[16] = LSW(mreq->rcv_dma);
+ mcp->mb[17] = MSW(mreq->rcv_dma);
+ mcp->mb[6] = LSW(MSD(mreq->rcv_dma));
+ mcp->mb[7] = MSW(MSD(mreq->rcv_dma));
+
+ /* Iteration count */
+ mcp->mb[18] = LSW(iter_cnt);
+ mcp->mb[19] = MSW(iter_cnt);
+
+ mcp->out_mb = MBX_21|MBX_20|MBX_19|MBX_18|MBX_17|MBX_16|MBX_15|
+ MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw))
+ mcp->out_mb |= MBX_2;
+ mcp->in_mb = MBX_19|MBX_18|MBX_3|MBX_2|MBX_1|MBX_0;
+
+ mcp->buf_size = mreq->transfer_size;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(printk(KERN_WARNING
+ "(%ld): failed=%x mb[0]=0x%x "
+ "mb[1]=0x%x mb[2]=0x%x mb[3]=0x%x mb[18]=0x%x mb[19]=0x%x. \n", vha->host_no, rval,
+ mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[18], mcp->mb[19]));
+ } else {
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld): done.\n", vha->host_no));
+ }
+
+ /* Copy mailbox information */
+ memcpy( mresp, mcp->mb, 64);
+ mresp[3] = mcp->mb[18];
+ mresp[4] = mcp->mb[19];
+ return rval;
+}
+
+int
+qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mresp)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ DEBUG11(printk("scsi(%ld): entered.\n", vha->host_no));
+
+ memset(mcp->mb, 0 , sizeof(mcp->mb));
+ mcp->mb[0] = MBC_DIAGNOSTIC_ECHO;
+ mcp->mb[1] = mreq->options | BIT_6; /* BIT_6 specifies 64bit address */
+ if (IS_QLA81XX(ha))
+ mcp->mb[1] |= BIT_15;
+ mcp->mb[2] = IS_QLA81XX(ha) ? vha->fcoe_fcf_idx : 0;
+ mcp->mb[16] = LSW(mreq->rcv_dma);
+ mcp->mb[17] = MSW(mreq->rcv_dma);
+ mcp->mb[6] = LSW(MSD(mreq->rcv_dma));
+ mcp->mb[7] = MSW(MSD(mreq->rcv_dma));
+
+ mcp->mb[10] = LSW(mreq->transfer_size);
+
+ mcp->mb[14] = LSW(mreq->send_dma);
+ mcp->mb[15] = MSW(mreq->send_dma);
+ mcp->mb[20] = LSW(MSD(mreq->send_dma));
+ mcp->mb[21] = MSW(MSD(mreq->send_dma));
+
+ mcp->out_mb = MBX_21|MBX_20|MBX_17|MBX_16|MBX_15|
+ MBX_14|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0;
+ if (IS_QLA81XX(ha))
+ mcp->out_mb |= MBX_2;
+
+ mcp->in_mb = MBX_0;
+ if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha))
+ mcp->in_mb |= MBX_1;
+ if (IS_QLA81XX(ha))
+ mcp->in_mb |= MBX_3;
+
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+ mcp->buf_size = mreq->transfer_size;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(printk(KERN_WARNING
+ "(%ld): failed=%x mb[0]=0x%x mb[1]=0x%x.\n",
+ vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ } else {
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld): done.\n", vha->host_no));
+ }
+
+ /* Copy mailbox information */
+ memcpy( mresp, mcp->mb, 32);
+ return rval;
+}
+int
+qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic,
+ uint16_t *cmd_status)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG16(printk("%s(%ld): enable_diag=%d entered.\n", __func__,
+ ha->host_no, enable_diagnostic));
+
+ mcp->mb[0] = MBC_ISP84XX_RESET;
+ mcp->mb[1] = enable_diagnostic;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Return mailbox statuses. */
+ *cmd_status = mcp->mb[0];
+ if (rval != QLA_SUCCESS)
+ DEBUG16(printk("%s(%ld): failed=%x.\n", __func__, ha->host_no,
+ rval));
+ else
+ DEBUG16(printk("%s(%ld): done.\n", __func__, ha->host_no));
+
+ return rval;
+}
+
+int
qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
{
int rval;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 8529eb1f3cd..46720b23028 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -107,6 +107,12 @@ MODULE_PARM_DESC(ql2xfwloadbin,
" 1 -- load firmware from flash.\n"
" 0 -- use default semantics.\n");
+int ql2xetsenable;
+module_param(ql2xetsenable, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xetsenable,
+ "Enables firmware ETS burst."
+ "Default is 0 - skip ETS enablement.");
+
/*
* SCSI host template entry points
*/
@@ -682,44 +688,6 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha)
return (return_status);
}
-void
-qla2x00_abort_fcport_cmds(fc_port_t *fcport)
-{
- int cnt;
- unsigned long flags;
- srb_t *sp;
- scsi_qla_host_t *vha = fcport->vha;
- struct qla_hw_data *ha = vha->hw;
- struct req_que *req;
-
- spin_lock_irqsave(&ha->hardware_lock, flags);
- req = vha->req;
- for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
- sp = req->outstanding_cmds[cnt];
- if (!sp)
- continue;
- if (sp->fcport != fcport)
- continue;
- if (sp->ctx)
- continue;
-
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(sp)) {
- DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed -- %lx\n",
- sp->cmd->serial_number));
- } else {
- if (qla2x00_eh_wait_on_command(sp->cmd) !=
- QLA_SUCCESS)
- DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed while waiting -- %lx\n",
- sp->cmd->serial_number));
- }
- spin_lock_irqsave(&ha->hardware_lock, flags);
- }
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
-}
-
/**************************************************************************
* qla2xxx_eh_abort
*
@@ -1095,6 +1063,20 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
struct fc_port *fcport;
struct qla_hw_data *ha = vha->hw;
+ if (ha->flags.enable_target_reset) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ ret = ha->isp_ops->target_reset(fcport, 0, 0);
+ if (ret != QLA_SUCCESS) {
+ DEBUG2_3(printk("%s(%ld): bus_reset failed: "
+ "target_reset=%d d_id=%x.\n", __func__,
+ vha->host_no, ret, fcport->d_id.b24));
+ }
+ }
+ }
+
if (ha->flags.enable_lip_full_login && !IS_QLA81XX(ha)) {
ret = qla2x00_full_login_lip(vha);
if (ret != QLA_SUCCESS) {
@@ -1117,19 +1099,6 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
qla2x00_wait_for_loop_ready(vha);
}
- if (ha->flags.enable_target_reset) {
- list_for_each_entry(fcport, &vha->vp_fcports, list) {
- if (fcport->port_type != FCT_TARGET)
- continue;
-
- ret = ha->isp_ops->target_reset(fcport, 0, 0);
- if (ret != QLA_SUCCESS) {
- DEBUG2_3(printk("%s(%ld): bus_reset failed: "
- "target_reset=%d d_id=%x.\n", __func__,
- vha->host_no, ret, fcport->d_id.b24));
- }
- }
- }
/* Issue marker command only when we are going to start the I/O */
vha->marker_needed = 1;
@@ -1160,8 +1129,19 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
qla2x00_sp_compl(ha, sp);
} else {
ctx = sp->ctx;
- del_timer_sync(&ctx->timer);
- ctx->free(sp);
+ if (ctx->type == SRB_LOGIN_CMD || ctx->type == SRB_LOGOUT_CMD) {
+ del_timer_sync(&ctx->timer);
+ ctx->free(sp);
+ } else {
+ struct srb_bsg* sp_bsg = (struct srb_bsg*)sp->ctx;
+ if (sp_bsg->bsg_job->request->msgcode == FC_BSG_HST_CT)
+ kfree(sp->fcport);
+ sp_bsg->bsg_job->req->errors = 0;
+ sp_bsg->bsg_job->reply->result = res;
+ sp_bsg->bsg_job->job_done(sp_bsg->bsg_job);
+ kfree(sp->ctx);
+ mempool_free(sp, ha->srb_mempool);
+ }
}
}
}
@@ -1258,7 +1238,7 @@ qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
qla2x00_adjust_sdev_qdepth_up(sdev, qdepth);
break;
default:
- return EOPNOTSUPP;
+ return -EOPNOTSUPP;
}
return sdev->queue_depth;
@@ -1818,7 +1798,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
/* Set EEH reset type to fundamental if required by hba */
if ( IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha)) {
pdev->needs_freset = 1;
- pci_save_state(pdev);
}
/* Configure PCI I/O space */
@@ -1970,11 +1949,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
host->max_channel = MAX_BUSES - 1;
host->max_lun = MAX_LUNS;
host->transportt = qla2xxx_transport_template;
+ sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC);
/* Set up the irqs */
ret = qla2x00_request_irqs(ha, rsp);
if (ret)
goto probe_init_failed;
+
+ pci_save_state(pdev);
+
/* Alloc arrays of request and response ring ptrs */
que_init:
if (!qla2x00_alloc_queues(ha)) {
@@ -2176,6 +2159,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
kfree(ha);
ha = NULL;
+ pci_disable_pcie_error_reporting(pdev);
+
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
@@ -3310,6 +3295,7 @@ qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
return PCI_ERS_RESULT_CAN_RECOVER;
case pci_channel_io_frozen:
ha->flags.eeh_busy = 1;
+ qla2x00_free_irqs(vha);
pci_disable_device(pdev);
return PCI_ERS_RESULT_NEED_RESET;
case pci_channel_io_perm_failure:
@@ -3363,10 +3349,24 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
struct qla_hw_data *ha = base_vha->hw;
- int rc;
+ struct rsp_que *rsp;
+ int rc, retries = 10;
DEBUG17(qla_printk(KERN_WARNING, ha, "slot_reset\n"));
+ /* Workaround: qla2xxx driver which access hardware earlier
+ * needs error state to be pci_channel_io_online.
+ * Otherwise mailbox command timesout.
+ */
+ pdev->error_state = pci_channel_io_normal;
+
+ pci_restore_state(pdev);
+
+ /* pci_restore_state() clears the saved_state flag of the device
+ * save restored state which resets saved_state flag
+ */
+ pci_save_state(pdev);
+
if (ha->mem_only)
rc = pci_enable_device_mem(pdev);
else
@@ -3378,27 +3378,23 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
return ret;
}
+ rsp = ha->rsp_q_map[0];
+ if (qla2x00_request_irqs(ha, rsp))
+ return ret;
+
if (ha->isp_ops->pci_config(base_vha))
return ret;
-#ifdef QL_DEBUG_LEVEL_17
- {
- uint8_t b;
- uint32_t i;
+ while (ha->flags.mbox_busy && retries--)
+ msleep(1000);
- printk("slot_reset_1: ");
- for (i = 0; i < 256; i++) {
- pci_read_config_byte(ha->pdev, i, &b);
- printk("%s%02x", (i%16) ? " " : "\n", b);
- }
- printk("\n");
- }
-#endif
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
if (qla2x00_abort_isp(base_vha) == QLA_SUCCESS)
ret = PCI_ERS_RESULT_RECOVERED;
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+
DEBUG17(qla_printk(KERN_WARNING, ha,
"slot_reset-return:ret=%x\n", ret));
@@ -3422,8 +3418,6 @@ qla2xxx_pci_resume(struct pci_dev *pdev)
}
ha->flags.eeh_busy = 0;
-
- pci_cleanup_aer_uncorrect_error_status(pdev);
}
static struct pci_error_handlers qla2xxx_err_handler = {
@@ -3536,4 +3530,3 @@ MODULE_FIRMWARE(FW_FILE_ISP2300);
MODULE_FIRMWARE(FW_FILE_ISP2322);
MODULE_FIRMWARE(FW_FILE_ISP24XX);
MODULE_FIRMWARE(FW_FILE_ISP25XX);
-MODULE_FIRMWARE(FW_FILE_ISP81XX);
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index ed36279a33c..8d2fc2fa7a6 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,9 +7,9 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.03.01-k10"
+#define QLA2XXX_VERSION "8.03.02-k1"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
-#define QLA_DRIVER_PATCH_VER 1
-#define QLA_DRIVER_BETA_VER 0
+#define QLA_DRIVER_PATCH_VER 2
+#define QLA_DRIVER_BETA_VER 1
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c
index af8c3233e8a..92329a461c6 100644
--- a/drivers/scsi/qla4xxx/ql4_init.c
+++ b/drivers/scsi/qla4xxx/ql4_init.c
@@ -844,10 +844,10 @@ static int qla4xxx_config_nvram(struct scsi_qla_host *ha)
DEBUG2(printk("scsi%ld: %s: Get EEProm parameters \n", ha->host_no,
__func__));
if (ql4xxx_lock_flash(ha) != QLA_SUCCESS)
- return (QLA_ERROR);
+ return QLA_ERROR;
if (ql4xxx_lock_nvram(ha) != QLA_SUCCESS) {
ql4xxx_unlock_flash(ha);
- return (QLA_ERROR);
+ return QLA_ERROR;
}
/* Get EEPRom Parameters from NVRAM and validate */
@@ -858,20 +858,18 @@ static int qla4xxx_config_nvram(struct scsi_qla_host *ha)
rd_nvram_word(ha, eeprom_ext_hw_conf_offset(ha));
spin_unlock_irqrestore(&ha->hardware_lock, flags);
} else {
- /*
- * QLogic adapters should always have a valid NVRAM.
- * If not valid, do not load.
- */
dev_warn(&ha->pdev->dev,
"scsi%ld: %s: EEProm checksum invalid. "
"Please update your EEPROM\n", ha->host_no,
__func__);
- /* set defaults */
+ /* Attempt to set defaults */
if (is_qla4010(ha))
extHwConfig.Asuint32_t = 0x1912;
else if (is_qla4022(ha) | is_qla4032(ha))
extHwConfig.Asuint32_t = 0x0023;
+ else
+ return QLA_ERROR;
}
DEBUG(printk("scsi%ld: %s: Setting extHwConfig to 0xFFFF%04x\n",
ha->host_no, __func__, extHwConfig.Asuint32_t));
@@ -884,7 +882,7 @@ static int qla4xxx_config_nvram(struct scsi_qla_host *ha)
ql4xxx_unlock_nvram(ha);
ql4xxx_unlock_flash(ha);
- return (QLA_SUCCESS);
+ return QLA_SUCCESS;
}
static void qla4x00_pci_config(struct scsi_qla_host *ha)
diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c
index 8e5c169b03f..bd88349b852 100644
--- a/drivers/scsi/raid_class.c
+++ b/drivers/scsi/raid_class.c
@@ -149,6 +149,7 @@ static struct {
{ RAID_LEVEL_0, "raid0" },
{ RAID_LEVEL_1, "raid1" },
{ RAID_LEVEL_10, "raid10" },
+ { RAID_LEVEL_1E, "raid1e" },
{ RAID_LEVEL_3, "raid3" },
{ RAID_LEVEL_4, "raid4" },
{ RAID_LEVEL_5, "raid5" },
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a60da555557..1c08f616465 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -1018,6 +1018,8 @@ static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer,
* scsi_get_vpd_page - Get Vital Product Data from a SCSI device
* @sdev: The device to ask
* @page: Which Vital Product Data to return
+ * @buf: where to store the VPD
+ * @buf_len: number of bytes in the VPD buffer area
*
* SCSI devices may optionally supply Vital Product Data. Each 'page'
* of VPD is defined in the appropriate SCSI document (eg SPC, SBC).
@@ -1026,55 +1028,39 @@ static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer,
* responsible for calling kfree() on this pointer when it is no longer
* needed. If we cannot retrieve the VPD page this routine returns %NULL.
*/
-unsigned char *scsi_get_vpd_page(struct scsi_device *sdev, u8 page)
+int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf,
+ int buf_len)
{
int i, result;
- unsigned int len;
- const unsigned int init_vpd_len = 255;
- unsigned char *buf = kmalloc(init_vpd_len, GFP_KERNEL);
-
- if (!buf)
- return NULL;
/* Ask for all the pages supported by this device */
- result = scsi_vpd_inquiry(sdev, buf, 0, init_vpd_len);
+ result = scsi_vpd_inquiry(sdev, buf, 0, buf_len);
if (result)
goto fail;
/* If the user actually wanted this page, we can skip the rest */
if (page == 0)
- return buf;
+ return -EINVAL;
- for (i = 0; i < buf[3]; i++)
+ for (i = 0; i < min((int)buf[3], buf_len - 4); i++)
if (buf[i + 4] == page)
goto found;
+
+ if (i < buf[3] && i > buf_len)
+ /* ran off the end of the buffer, give us benefit of doubt */
+ goto found;
/* The device claims it doesn't support the requested page */
goto fail;
found:
- result = scsi_vpd_inquiry(sdev, buf, page, 255);
+ result = scsi_vpd_inquiry(sdev, buf, page, buf_len);
if (result)
goto fail;
- /*
- * Some pages are longer than 255 bytes. The actual length of
- * the page is returned in the header.
- */
- len = ((buf[2] << 8) | buf[3]) + 4;
- if (len <= init_vpd_len)
- return buf;
-
- kfree(buf);
- buf = kmalloc(len, GFP_KERNEL);
- result = scsi_vpd_inquiry(sdev, buf, page, len);
- if (result)
- goto fail;
-
- return buf;
+ return 0;
fail:
- kfree(buf);
- return NULL;
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(scsi_get_vpd_page);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index c6642423cc6..1646fe7cbd4 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -773,8 +773,14 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* we already took a copy of the original into rq->errors which
* is what gets returned to the user
*/
- if (sense_valid && sshdr.sense_key == RECOVERED_ERROR) {
- if (!(req->cmd_flags & REQ_QUIET))
+ if (sense_valid && (sshdr.sense_key == RECOVERED_ERROR)) {
+ /* if ATA PASS-THROUGH INFORMATION AVAILABLE skip
+ * print since caller wants ATA registers. Only occurs on
+ * SCSI ATA PASS_THROUGH commands when CK_COND=1
+ */
+ if ((sshdr.asc == 0x0) && (sshdr.ascq == 0x1d))
+ ;
+ else if (!(req->cmd_flags & REQ_QUIET))
scsi_print_sense("", cmd);
result = 0;
/* BLOCK_PC may have set error */
@@ -1624,10 +1630,10 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,
/*
* this limit is imposed by hardware restrictions
*/
- blk_queue_max_hw_segments(q, shost->sg_tablesize);
- blk_queue_max_phys_segments(q, SCSI_MAX_SG_CHAIN_SEGMENTS);
+ blk_queue_max_segments(q, min_t(unsigned short, shost->sg_tablesize,
+ SCSI_MAX_SG_CHAIN_SEGMENTS));
- blk_queue_max_sectors(q, shost->max_sectors);
+ blk_queue_max_hw_sectors(q, shost->max_sectors);
blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));
blk_queue_segment_boundary(q, shost->dma_boundary);
dma_set_seg_boundary(dev, shost->dma_boundary);
diff --git a/drivers/scsi/scsi_sas_internal.h b/drivers/scsi/scsi_sas_internal.h
index 998cb5be683..6266a5d73d0 100644
--- a/drivers/scsi/scsi_sas_internal.h
+++ b/drivers/scsi/scsi_sas_internal.h
@@ -5,7 +5,7 @@
#define SAS_PHY_ATTRS 17
#define SAS_PORT_ATTRS 1
#define SAS_RPORT_ATTRS 7
-#define SAS_END_DEV_ATTRS 3
+#define SAS_END_DEV_ATTRS 5
#define SAS_EXPANDER_ATTRS 7
struct sas_internal {
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 012f73a9688..4bc8b77a2ef 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -879,7 +879,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
* broken RA4x00 Compaq Disk Array
*/
if (*bflags & BLIST_MAX_512)
- blk_queue_max_sectors(sdev->request_queue, 512);
+ blk_queue_max_hw_sectors(sdev->request_queue, 512);
/*
* Some devices may not want to have a start command automatically
@@ -1339,8 +1339,10 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
sdev = scsi_alloc_sdev(starget, 0, NULL);
if (!sdev)
return 0;
- if (scsi_device_get(sdev))
+ if (scsi_device_get(sdev)) {
+ __scsi_remove_device(sdev);
return 0;
+ }
}
sprintf(devname, "host %d channel %d id %d",
@@ -1907,10 +1909,9 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
goto out;
sdev = scsi_alloc_sdev(starget, 0, NULL);
- if (sdev) {
- sdev->sdev_gendev.parent = get_device(&starget->dev);
+ if (sdev)
sdev->borken = 0;
- } else
+ else
scsi_target_reap(starget);
put_device(&starget->dev);
out:
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 5a065055e68..19ec9e2d3f3 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -847,6 +847,8 @@ static int scsi_target_add(struct scsi_target *starget)
if (starget->state != STARGET_CREATED)
return 0;
+ device_enable_async_suspend(&starget->dev);
+
error = device_add(&starget->dev);
if (error) {
dev_err(&starget->dev, "target device_add failed, error %d\n", error);
@@ -878,7 +880,8 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
struct request_queue *rq = sdev->request_queue;
struct scsi_target *starget = sdev->sdev_target;
- if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
+ error = scsi_device_set_state(sdev, SDEV_RUNNING);
+ if (error)
return error;
error = scsi_target_add(starget);
@@ -886,16 +889,18 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
return error;
transport_configure_device(&starget->dev);
+ device_enable_async_suspend(&sdev->sdev_gendev);
error = device_add(&sdev->sdev_gendev);
if (error) {
printk(KERN_INFO "error 1\n");
- goto out_remove;
+ return error;
}
+ device_enable_async_suspend(&sdev->sdev_dev);
error = device_add(&sdev->sdev_dev);
if (error) {
printk(KERN_INFO "error 2\n");
device_del(&sdev->sdev_gendev);
- goto out_remove;
+ return error;
}
transport_add_device(&sdev->sdev_gendev);
sdev->is_visible = 1;
@@ -910,14 +915,14 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
else
error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth);
if (error)
- goto out_remove;
+ return error;
if (sdev->host->hostt->change_queue_type)
error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_type_rw);
else
error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_type);
if (error)
- goto out_remove;
+ return error;
error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);
@@ -933,16 +938,11 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
error = device_create_file(&sdev->sdev_gendev,
sdev->host->hostt->sdev_attrs[i]);
if (error)
- goto out_remove;
+ return error;
}
}
- return 0;
-
- out_remove:
- __scsi_remove_device(sdev);
return error;
-
}
void __scsi_remove_device(struct scsi_device *sdev)
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 653f22a8deb..79660ee3e21 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -475,7 +475,8 @@ MODULE_PARM_DESC(dev_loss_tmo,
"Maximum number of seconds that the FC transport should"
" insulate the loss of a remote port. Once this value is"
" exceeded, the scsi target is removed. Value should be"
- " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT.");
+ " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if"
+ " fast_io_fail_tmo is not set.");
/*
* Netlink Infrastructure
@@ -842,9 +843,17 @@ store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
(rport->port_state == FC_PORTSTATE_NOTPRESENT))
return -EBUSY;
val = simple_strtoul(buf, &cp, 0);
- if ((*cp && (*cp != '\n')) ||
- (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+ if ((*cp && (*cp != '\n')) || (val < 0))
return -EINVAL;
+
+ /*
+ * If fast_io_fail is off we have to cap
+ * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT
+ */
+ if (rport->fast_io_fail_tmo == -1 &&
+ val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
i->f->set_rport_dev_loss_tmo(rport, val);
return count;
}
@@ -925,9 +934,16 @@ store_fc_rport_fast_io_fail_tmo(struct device *dev,
rport->fast_io_fail_tmo = -1;
else {
val = simple_strtoul(buf, &cp, 0);
- if ((*cp && (*cp != '\n')) ||
- (val < 0) || (val >= rport->dev_loss_tmo))
+ if ((*cp && (*cp != '\n')) || (val < 0))
return -EINVAL;
+ /*
+ * Cap fast_io_fail by dev_loss_tmo or
+ * SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
+ */
+ if ((val >= rport->dev_loss_tmo) ||
+ (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+ return -EINVAL;
+
rport->fast_io_fail_tmo = val;
}
return count;
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index f27e52d963d..927e99cb722 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -155,6 +155,17 @@ static struct {
sas_bitfield_name_search(linkspeed, sas_linkspeed_names)
sas_bitfield_name_set(linkspeed, sas_linkspeed_names)
+static struct sas_end_device *sas_sdev_to_rdev(struct scsi_device *sdev)
+{
+ struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target);
+ struct sas_end_device *rdev;
+
+ BUG_ON(rphy->identify.device_type != SAS_END_DEVICE);
+
+ rdev = rphy_to_end_device(rphy);
+ return rdev;
+}
+
static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost,
struct sas_rphy *rphy)
{
@@ -358,6 +369,85 @@ void sas_remove_host(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(sas_remove_host);
+/**
+ * sas_tlr_supported - checking TLR bit in vpd 0x90
+ * @sdev: scsi device struct
+ *
+ * Check Transport Layer Retries are supported or not.
+ * If vpd page 0x90 is present, TRL is supported.
+ *
+ */
+unsigned int
+sas_tlr_supported(struct scsi_device *sdev)
+{
+ const int vpd_len = 32;
+ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
+ char *buffer = kzalloc(vpd_len, GFP_KERNEL);
+ int ret = 0;
+
+ if (scsi_get_vpd_page(sdev, 0x90, buffer, vpd_len))
+ goto out;
+
+ /*
+ * Magic numbers: the VPD Protocol page (0x90)
+ * has a 4 byte header and then one entry per device port
+ * the TLR bit is at offset 8 on each port entry
+ * if we take the first port, that's at total offset 12
+ */
+ ret = buffer[12] & 0x01;
+
+ out:
+ kfree(buffer);
+ rdev->tlr_supported = ret;
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(sas_tlr_supported);
+
+/**
+ * sas_disable_tlr - setting TLR flags
+ * @sdev: scsi device struct
+ *
+ * Seting tlr_enabled flag to 0.
+ *
+ */
+void
+sas_disable_tlr(struct scsi_device *sdev)
+{
+ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
+
+ rdev->tlr_enabled = 0;
+}
+EXPORT_SYMBOL_GPL(sas_disable_tlr);
+
+/**
+ * sas_enable_tlr - setting TLR flags
+ * @sdev: scsi device struct
+ *
+ * Seting tlr_enabled flag 1.
+ *
+ */
+void sas_enable_tlr(struct scsi_device *sdev)
+{
+ unsigned int tlr_supported = 0;
+ tlr_supported = sas_tlr_supported(sdev);
+
+ if (tlr_supported) {
+ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
+
+ rdev->tlr_enabled = 1;
+ }
+
+ return;
+}
+EXPORT_SYMBOL_GPL(sas_enable_tlr);
+
+unsigned int sas_is_tlr_enabled(struct scsi_device *sdev)
+{
+ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
+ return rdev->tlr_enabled;
+}
+EXPORT_SYMBOL_GPL(sas_is_tlr_enabled);
/*
* SAS Phy attributes
@@ -1146,15 +1236,10 @@ sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8);
int sas_read_port_mode_page(struct scsi_device *sdev)
{
char *buffer = kzalloc(BUF_SIZE, GFP_KERNEL), *msdata;
- struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target);
- struct sas_end_device *rdev;
+ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
struct scsi_mode_data mode_data;
int res, error;
- BUG_ON(rphy->identify.device_type != SAS_END_DEVICE);
-
- rdev = rphy_to_end_device(rphy);
-
if (!buffer)
return -ENOMEM;
@@ -1207,6 +1292,10 @@ sas_end_dev_simple_attr(I_T_nexus_loss_timeout, I_T_nexus_loss_timeout,
"%d\n", int);
sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout,
"%d\n", int);
+sas_end_dev_simple_attr(tlr_supported, tlr_supported,
+ "%d\n", int);
+sas_end_dev_simple_attr(tlr_enabled, tlr_enabled,
+ "%d\n", int);
static DECLARE_TRANSPORT_CLASS(sas_expander_class,
"sas_expander", NULL, NULL, NULL);
@@ -1733,6 +1822,8 @@ sas_attach_transport(struct sas_function_template *ft)
SETUP_END_DEV_ATTRIBUTE(end_dev_ready_led_meaning);
SETUP_END_DEV_ATTRIBUTE(end_dev_I_T_nexus_loss_timeout);
SETUP_END_DEV_ATTRIBUTE(end_dev_initiator_response_timeout);
+ SETUP_END_DEV_ATTRIBUTE(end_dev_tlr_supported);
+ SETUP_END_DEV_ATTRIBUTE(end_dev_tlr_enabled);
i->end_dev_attrs[count] = NULL;
count = 0;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 255da53e5a0..1dd4d840769 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1196,19 +1196,10 @@ static int sd_done(struct scsi_cmnd *SCpnt)
SCpnt->result = 0;
memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
break;
- case ABORTED_COMMAND:
- if (sshdr.asc == 0x10) { /* DIF: Disk detected corruption */
- scsi_print_result(SCpnt);
- scsi_print_sense("sd", SCpnt);
+ case ABORTED_COMMAND: /* DIF: Target detected corruption */
+ case ILLEGAL_REQUEST: /* DIX: Host detected corruption */
+ if (sshdr.asc == 0x10)
good_bytes = sd_completed_bytes(SCpnt);
- }
- break;
- case ILLEGAL_REQUEST:
- if (sshdr.asc == 0x10) { /* DIX: HBA detected corruption */
- scsi_print_result(SCpnt);
- scsi_print_sense("sd", SCpnt);
- good_bytes = sd_completed_bytes(SCpnt);
- }
break;
default:
break;
@@ -1218,8 +1209,19 @@ static int sd_done(struct scsi_cmnd *SCpnt)
sd_dif_complete(SCpnt, good_bytes);
if (scsi_host_dif_capable(sdkp->device->host, sdkp->protection_type)
- == SD_DIF_TYPE2_PROTECTION && SCpnt->cmnd != SCpnt->request->cmd)
+ == SD_DIF_TYPE2_PROTECTION && SCpnt->cmnd != SCpnt->request->cmd) {
+
+ /* We have to print a failed command here as the
+ * extended CDB gets freed before scsi_io_completion()
+ * is called.
+ */
+ if (result)
+ scsi_print_command(SCpnt);
+
mempool_free(SCpnt->cmnd, sd_cdb_pool);
+ SCpnt->cmnd = NULL;
+ SCpnt->cmd_len = 0;
+ }
return good_bytes;
}
@@ -1946,13 +1948,13 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
{
struct request_queue *q = sdkp->disk->queue;
unsigned int sector_sz = sdkp->device->sector_size;
- char *buffer;
+ const int vpd_len = 32;
+ unsigned char *buffer = kmalloc(vpd_len, GFP_KERNEL);
- /* Block Limits VPD */
- buffer = scsi_get_vpd_page(sdkp->device, 0xb0);
-
- if (buffer == NULL)
- return;
+ if (!buffer ||
+ /* Block Limits VPD */
+ scsi_get_vpd_page(sdkp->device, 0xb0, buffer, vpd_len))
+ goto out;
blk_queue_io_min(sdkp->disk->queue,
get_unaligned_be16(&buffer[6]) * sector_sz);
@@ -1984,6 +1986,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
get_unaligned_be32(&buffer[32]) & ~(1 << 31);
}
+ out:
kfree(buffer);
}
@@ -1993,20 +1996,23 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
*/
static void sd_read_block_characteristics(struct scsi_disk *sdkp)
{
- char *buffer;
+ unsigned char *buffer;
u16 rot;
+ const int vpd_len = 32;
- /* Block Device Characteristics VPD */
- buffer = scsi_get_vpd_page(sdkp->device, 0xb1);
+ buffer = kmalloc(vpd_len, GFP_KERNEL);
- if (buffer == NULL)
- return;
+ if (!buffer ||
+ /* Block Device Characteristics VPD */
+ scsi_get_vpd_page(sdkp->device, 0xb1, buffer, vpd_len))
+ goto out;
rot = get_unaligned_be16(&buffer[4]);
if (rot == 1)
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue);
+ out:
kfree(buffer);
}
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 55b034b7270..1d7a8780e00 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -448,13 +448,17 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
.addr = 0,
};
- buf = scsi_get_vpd_page(sdev, 0x83);
- if (!buf)
- return;
+ buf = kmalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
+ if (!buf || scsi_get_vpd_page(sdev, 0x83, buf, INIT_ALLOC_SIZE))
+ goto free;
ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
vpd_len = ((buf[2] << 8) | buf[3]) + 4;
+ kfree(buf);
+ buf = kmalloc(vpd_len, GFP_KERNEL);
+ if (!buf ||scsi_get_vpd_page(sdev, 0x83, buf, vpd_len))
+ goto free;
desc = buf + 4;
while (desc < buf + vpd_len) {
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 040f751809e..c996d98636f 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -287,8 +287,7 @@ sg_open(struct inode *inode, struct file *filp)
if (list_empty(&sdp->sfds)) { /* no existing opens on this device */
sdp->sgdebug = 0;
q = sdp->device->request_queue;
- sdp->sg_tablesize = min(queue_max_hw_segments(q),
- queue_max_phys_segments(q));
+ sdp->sg_tablesize = queue_max_segments(q);
}
if ((sfp = sg_add_sfp(sdp, dev)))
filp->private_data = sfp;
@@ -1376,8 +1375,7 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
sdp->device = scsidp;
INIT_LIST_HEAD(&sdp->sfds);
init_waitqueue_head(&sdp->o_excl_wait);
- sdp->sg_tablesize = min(queue_max_hw_segments(q),
- queue_max_phys_segments(q));
+ sdp->sg_tablesize = queue_max_segments(q);
sdp->index = k;
kref_init(&sdp->d_ref);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index d04ea9a6f67..f67d1a159aa 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -3983,8 +3983,7 @@ static int st_probe(struct device *dev)
return -ENODEV;
}
- i = min(queue_max_hw_segments(SDp->request_queue),
- queue_max_phys_segments(SDp->request_queue));
+ i = queue_max_segments(SDp->request_queue);
if (st_max_sg_segs < i)
i = st_max_sg_segs;
buffer = new_tape_buffer((SDp->host)->unchecked_isa_dma, i);
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index 54023d41fd1..26e8e0e6b8d 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1070,7 +1070,7 @@ static int option_setup(char *str) {
char *cur = str;
int i = 1;
- while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+ while (cur && isdigit(*cur) && i < MAX_INT_PARAM) {
ints[i++] = simple_strtoul(cur, NULL, 0);
if ((cur = strchr(cur, ',')) != NULL) cur++;
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
index d2604c813a2..e4ac5829b63 100644
--- a/drivers/scsi/vmw_pvscsi.c
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -1069,7 +1069,8 @@ static void pvscsi_free_sgls(const struct pvscsi_adapter *adapter)
free_pages((unsigned long)ctx->sgl, get_order(SGL_SIZE));
}
-static int pvscsi_setup_msix(const struct pvscsi_adapter *adapter, int *irq)
+static int pvscsi_setup_msix(const struct pvscsi_adapter *adapter,
+ unsigned int *irq)
{
struct msix_entry entry = { 0, PVSCSI_VECTOR_COMPLETION };
int ret;
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index c3e37c8e7e2..a81ff7bc5fa 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -83,6 +83,9 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */
#define PASS_LIMIT 256
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+
/*
* We default to IRQ0 for the "no irq" hack. Some
* machine types want others as well - they're free
@@ -1214,12 +1217,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
}
#endif
-#ifdef CONFIG_SERIAL_8250_AU1X00
- /* if access method is AU, it is a 16550 with a quirk */
- if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU)
- up->bugs |= UART_BUG_NOMSR;
-#endif
-
serial_outp(up, UART_LCR, save_lcr);
if (up->capabilities != uart_config[up->port.type].flags) {
@@ -1792,7 +1789,7 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
spin_unlock_irqrestore(&up->port.lock, flags);
- return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+ return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
}
static unsigned int serial8250_get_mctrl(struct uart_port *port)
@@ -1850,8 +1847,6 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&up->port.lock, flags);
}
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
/*
* Wait for transmitter & holding register to empty
*/
@@ -2427,7 +2422,7 @@ serial8250_pm(struct uart_port *port, unsigned int state,
static unsigned int serial8250_port_size(struct uart_8250_port *pt)
{
if (pt->port.iotype == UPIO_AU)
- return 0x100000;
+ return 0x1000;
#ifdef CONFIG_ARCH_OMAP
if (is_omap_port(pt))
return 0x16 << pt->port.regshift;
@@ -2584,6 +2579,13 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (flags & UART_CONFIG_TYPE)
autoconfig(up, probeflags);
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+ /* if access method is AU, it is a 16550 with a quirk */
+ if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU)
+ up->bugs |= UART_BUG_NOMSR;
+#endif
+
if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(up);
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 9ff47db0b2c..888a0ce91c4 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1086,12 +1086,12 @@ config SERIAL_68360
default y
config SERIAL_PMACZILOG
- tristate "PowerMac z85c30 ESCC support"
- depends on PPC_OF && PPC_PMAC
+ tristate "Mac or PowerMac z85c30 ESCC support"
+ depends on (M68K && MAC) || (PPC_OF && PPC_PMAC)
select SERIAL_CORE
help
This driver supports the Zilog z85C30 serial ports found on
- PowerMac machines.
+ (Power)Mac machines.
Say Y or M if you want to be able to these serial ports.
config SERIAL_PMACZILOG_TTYS
@@ -1116,16 +1116,16 @@ config SERIAL_PMACZILOG_TTYS
unable to use the 8250 module for PCMCIA or other 16C550-style
UARTs.
- Say N unless you need the z85c30 ports on your powermac
+ Say N unless you need the z85c30 ports on your (Power)Mac
to appear as /dev/ttySn.
config SERIAL_PMACZILOG_CONSOLE
- bool "Console on PowerMac z85c30 serial port"
+ bool "Console on Mac or PowerMac z85c30 serial port"
depends on SERIAL_PMACZILOG=y
select SERIAL_CORE_CONSOLE
help
If you would like to be able to use the z85c30 serial port
- on your PowerMac as the console, you can do so by answering
+ on your (Power)Mac as the console, you can do so by answering
Y to this option.
config SERIAL_LH7A40X
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index ef7adc8135d..ce6c35333ff 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -71,6 +71,7 @@ struct uart_amba_port {
unsigned int im; /* interrupt mask */
unsigned int old_status;
unsigned int ifls; /* vendor-specific */
+ bool autorts;
};
/* There is by now at least one vendor with differing details, so handle it */
@@ -308,6 +309,11 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1);
TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2);
TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE);
+
+ if (uap->autorts) {
+ /* We need to disable auto-RTS if we want to turn RTS off */
+ TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN);
+ }
#undef TIOCMBIT
writew(cr, uap->port.membase + UART011_CR);
@@ -437,6 +443,7 @@ static void pl011_shutdown(struct uart_port *port)
/*
* disable the port
*/
+ uap->autorts = false;
writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR);
/*
@@ -456,6 +463,7 @@ static void
pl011_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int lcr_h, old_cr;
unsigned long flags;
unsigned int baud, quot;
@@ -532,6 +540,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
old_cr = readw(port->membase + UART011_CR);
writew(0, port->membase + UART011_CR);
+ if (termios->c_cflag & CRTSCTS) {
+ if (old_cr & UART011_CR_RTS)
+ old_cr |= UART011_CR_RTSEN;
+
+ old_cr |= UART011_CR_CTSEN;
+ uap->autorts = true;
+ } else {
+ old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN);
+ uap->autorts = false;
+ }
+
/* Set baud rate */
writew(quot & 0x3f, port->membase + UART011_FBRD);
writew(quot >> 6, port->membase + UART011_IBRD);
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 7ce9e9f567a..3119fddaedb 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -74,6 +74,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/clk.h>
#include <asm/mpc52xx.h>
#include <asm/mpc52xx_psc.h>
@@ -113,6 +114,7 @@ static void mpc52xx_uart_of_enumerate(void);
/* Forward declaration of the interruption handling routine */
static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id);
+static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port);
/* Simple macro to test if a port is console or not. This one is taken
@@ -145,6 +147,11 @@ struct psc_ops {
void (*cw_disable_ints)(struct uart_port *port);
void (*cw_restore_ints)(struct uart_port *port);
unsigned long (*getuartclk)(void *p);
+ int (*clock)(struct uart_port *port, int enable);
+ int (*fifoc_init)(void);
+ void (*fifoc_uninit)(void);
+ void (*get_irq)(struct uart_port *, struct device_node *);
+ irqreturn_t (*handle_irq)(struct uart_port *port);
};
#ifdef CONFIG_PPC_MPC52xx
@@ -256,6 +263,18 @@ static unsigned long mpc52xx_getuartclk(void *p)
return mpc5xxx_get_bus_frequency(p) / 2;
}
+static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np)
+{
+ port->irqflags = IRQF_DISABLED;
+ port->irq = irq_of_parse_and_map(np, 0);
+}
+
+/* 52xx specific interrupt handler. The caller holds the port lock */
+static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port)
+{
+ return mpc5xxx_uart_process_int(port);
+}
+
static struct psc_ops mpc52xx_psc_ops = {
.fifo_init = mpc52xx_psc_fifo_init,
.raw_rx_rdy = mpc52xx_psc_raw_rx_rdy,
@@ -273,14 +292,32 @@ static struct psc_ops mpc52xx_psc_ops = {
.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
.getuartclk = mpc52xx_getuartclk,
+ .get_irq = mpc52xx_psc_get_irq,
+ .handle_irq = mpc52xx_psc_handle_irq,
};
#endif /* CONFIG_MPC52xx */
#ifdef CONFIG_PPC_MPC512x
#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1))
+
+/* PSC FIFO Controller for mpc512x */
+struct psc_fifoc {
+ u32 fifoc_cmd;
+ u32 fifoc_int;
+ u32 fifoc_dma;
+ u32 fifoc_axe;
+ u32 fifoc_debug;
+};
+
+static struct psc_fifoc __iomem *psc_fifoc;
+static unsigned int psc_fifoc_irq;
+
static void mpc512x_psc_fifo_init(struct uart_port *port)
{
+ /* /32 prescaler */
+ out_be16(&PSC(port)->mpc52xx_psc_clock_select, 0xdd00);
+
out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE);
out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
out_be32(&FIFO_512x(port)->txalarm, 1);
@@ -393,6 +430,160 @@ static unsigned long mpc512x_getuartclk(void *p)
return mpc5xxx_get_bus_frequency(p);
}
+#define DEFAULT_FIFO_SIZE 16
+
+static unsigned int __init get_fifo_size(struct device_node *np,
+ char *fifo_name)
+{
+ const unsigned int *fp;
+
+ fp = of_get_property(np, fifo_name, NULL);
+ if (fp)
+ return *fp;
+
+ pr_warning("no %s property in %s node, defaulting to %d\n",
+ fifo_name, np->full_name, DEFAULT_FIFO_SIZE);
+
+ return DEFAULT_FIFO_SIZE;
+}
+
+#define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \
+ ((u32)(_base) + sizeof(struct mpc52xx_psc)))
+
+/* Init PSC FIFO Controller */
+static int __init mpc512x_psc_fifoc_init(void)
+{
+ struct device_node *np;
+ void __iomem *psc;
+ unsigned int tx_fifo_size;
+ unsigned int rx_fifo_size;
+ int fifobase = 0; /* current fifo address in 32 bit words */
+
+ np = of_find_compatible_node(NULL, NULL,
+ "fsl,mpc5121-psc-fifo");
+ if (!np) {
+ pr_err("%s: Can't find FIFOC node\n", __func__);
+ return -ENODEV;
+ }
+
+ psc_fifoc = of_iomap(np, 0);
+ if (!psc_fifoc) {
+ pr_err("%s: Can't map FIFOC\n", __func__);
+ return -ENODEV;
+ }
+
+ psc_fifoc_irq = irq_of_parse_and_map(np, 0);
+ of_node_put(np);
+ if (psc_fifoc_irq == NO_IRQ) {
+ pr_err("%s: Can't get FIFOC irq\n", __func__);
+ iounmap(psc_fifoc);
+ return -ENODEV;
+ }
+
+ for_each_compatible_node(np, NULL, "fsl,mpc5121-psc-uart") {
+ tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size");
+ rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size");
+
+ /* size in register is in 4 byte units */
+ tx_fifo_size /= 4;
+ rx_fifo_size /= 4;
+ if (!tx_fifo_size)
+ tx_fifo_size = 1;
+ if (!rx_fifo_size)
+ rx_fifo_size = 1;
+
+ psc = of_iomap(np, 0);
+ if (!psc) {
+ pr_err("%s: Can't map %s device\n",
+ __func__, np->full_name);
+ continue;
+ }
+
+ /* FIFO space is 4KiB, check if requested size is available */
+ if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) {
+ pr_err("%s: no fifo space available for %s\n",
+ __func__, np->full_name);
+ iounmap(psc);
+ /*
+ * chances are that another device requests less
+ * fifo space, so we continue.
+ */
+ continue;
+ }
+ /* set tx and rx fifo size registers */
+ out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size);
+ fifobase += tx_fifo_size;
+ out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size);
+ fifobase += rx_fifo_size;
+
+ /* reset and enable the slices */
+ out_be32(&FIFOC(psc)->txcmd, 0x80);
+ out_be32(&FIFOC(psc)->txcmd, 0x01);
+ out_be32(&FIFOC(psc)->rxcmd, 0x80);
+ out_be32(&FIFOC(psc)->rxcmd, 0x01);
+
+ iounmap(psc);
+ }
+
+ return 0;
+}
+
+static void __exit mpc512x_psc_fifoc_uninit(void)
+{
+ iounmap(psc_fifoc);
+}
+
+/* 512x specific interrupt handler. The caller holds the port lock */
+static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port)
+{
+ unsigned long fifoc_int;
+ int psc_num;
+
+ /* Read pending PSC FIFOC interrupts */
+ fifoc_int = in_be32(&psc_fifoc->fifoc_int);
+
+ /* Check if it is an interrupt for this port */
+ psc_num = (port->mapbase & 0xf00) >> 8;
+ if (test_bit(psc_num, &fifoc_int) ||
+ test_bit(psc_num + 16, &fifoc_int))
+ return mpc5xxx_uart_process_int(port);
+
+ return IRQ_NONE;
+}
+
+static int mpc512x_psc_clock(struct uart_port *port, int enable)
+{
+ struct clk *psc_clk;
+ int psc_num;
+ char clk_name[10];
+
+ if (uart_console(port))
+ return 0;
+
+ psc_num = (port->mapbase & 0xf00) >> 8;
+ snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num);
+ psc_clk = clk_get(port->dev, clk_name);
+ if (IS_ERR(psc_clk)) {
+ dev_err(port->dev, "Failed to get PSC clock entry!\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis");
+
+ if (enable)
+ clk_enable(psc_clk);
+ else
+ clk_disable(psc_clk);
+
+ return 0;
+}
+
+static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np)
+{
+ port->irqflags = IRQF_SHARED;
+ port->irq = psc_fifoc_irq;
+}
+
static struct psc_ops mpc512x_psc_ops = {
.fifo_init = mpc512x_psc_fifo_init,
.raw_rx_rdy = mpc512x_psc_raw_rx_rdy,
@@ -410,6 +601,11 @@ static struct psc_ops mpc512x_psc_ops = {
.cw_disable_ints = mpc512x_psc_cw_disable_ints,
.cw_restore_ints = mpc512x_psc_cw_restore_ints,
.getuartclk = mpc512x_getuartclk,
+ .clock = mpc512x_psc_clock,
+ .fifoc_init = mpc512x_psc_fifoc_init,
+ .fifoc_uninit = mpc512x_psc_fifoc_uninit,
+ .get_irq = mpc512x_psc_get_irq,
+ .handle_irq = mpc512x_psc_handle_irq,
};
#endif
@@ -519,10 +715,15 @@ mpc52xx_uart_startup(struct uart_port *port)
struct mpc52xx_psc __iomem *psc = PSC(port);
int ret;
+ if (psc_ops->clock) {
+ ret = psc_ops->clock(port, 1);
+ if (ret)
+ return ret;
+ }
+
/* Request IRQ */
ret = request_irq(port->irq, mpc52xx_uart_int,
- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
- "mpc52xx_psc_uart", port);
+ port->irqflags, "mpc52xx_psc_uart", port);
if (ret)
return ret;
@@ -553,6 +754,9 @@ mpc52xx_uart_shutdown(struct uart_port *port)
port->read_status_mask = 0;
out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+ if (psc_ops->clock)
+ psc_ops->clock(port, 0);
+
/* Release interrupt */
free_irq(port->irq, port);
}
@@ -851,15 +1055,12 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port)
}
static irqreturn_t
-mpc52xx_uart_int(int irq, void *dev_id)
+mpc5xxx_uart_process_int(struct uart_port *port)
{
- struct uart_port *port = dev_id;
unsigned long pass = ISR_PASS_LIMIT;
unsigned int keepgoing;
u8 status;
- spin_lock(&port->lock);
-
/* While we have stuff to do, we continue */
do {
/* If we don't find anything to do, we stop */
@@ -886,11 +1087,23 @@ mpc52xx_uart_int(int irq, void *dev_id)
} while (keepgoing);
- spin_unlock(&port->lock);
-
return IRQ_HANDLED;
}
+static irqreturn_t
+mpc52xx_uart_int(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ irqreturn_t ret;
+
+ spin_lock(&port->lock);
+
+ ret = psc_ops->handle_irq(port);
+
+ spin_unlock(&port->lock);
+
+ return ret;
+}
/* ======================================================================== */
/* Console ( if applicable ) */
@@ -1152,7 +1365,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
return -EINVAL;
}
- port->irq = irq_of_parse_and_map(op->node, 0);
+ psc_ops->get_irq(port, op->node);
if (port->irq == NO_IRQ) {
dev_dbg(&op->dev, "Could not get irq\n");
return -EINVAL;
@@ -1163,10 +1376,8 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
/* Add the port to the uart sub-system */
ret = uart_add_one_port(&mpc52xx_uart_driver, port);
- if (ret) {
- irq_dispose_mapping(port->irq);
+ if (ret)
return ret;
- }
dev_set_drvdata(&op->dev, (void *)port);
return 0;
@@ -1178,10 +1389,8 @@ mpc52xx_uart_of_remove(struct of_device *op)
struct uart_port *port = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
- if (port) {
+ if (port)
uart_remove_one_port(&mpc52xx_uart_driver, port);
- irq_dispose_mapping(port->irq);
- }
return 0;
}
@@ -1288,6 +1497,15 @@ mpc52xx_uart_init(void)
mpc52xx_uart_of_enumerate();
+ /*
+ * Map the PSC FIFO Controller and init if on MPC512x.
+ */
+ if (psc_ops->fifoc_init) {
+ ret = psc_ops->fifoc_init();
+ if (ret)
+ return ret;
+ }
+
ret = of_register_platform_driver(&mpc52xx_uart_of_driver);
if (ret) {
printk(KERN_ERR "%s: of_register_platform_driver failed (%i)\n",
@@ -1302,6 +1520,9 @@ mpc52xx_uart_init(void)
static void __exit
mpc52xx_uart_exit(void)
{
+ if (psc_ops->fifoc_uninit)
+ psc_ops->fifoc_uninit();
+
of_unregister_platform_driver(&mpc52xx_uart_of_driver);
uart_unregister_driver(&mpc52xx_uart_driver);
}
diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c
index 683e66f18e8..f020de1cdd5 100644
--- a/drivers/serial/pmac_zilog.c
+++ b/drivers/serial/pmac_zilog.c
@@ -63,11 +63,17 @@
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/irq.h>
+
+#ifdef CONFIG_PPC_PMAC
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/dbdma.h>
#include <asm/macio.h>
+#else
+#include <linux/platform_device.h>
+#define of_machine_is_compatible(x) (0)
+#endif
#if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
@@ -83,11 +89,9 @@
static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
-MODULE_DESCRIPTION("Driver for the PowerMac serial ports.");
+MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports.");
MODULE_LICENSE("GPL");
-#define PWRDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg)
-
#ifdef CONFIG_SERIAL_PMACZILOG_TTYS
#define PMACZILOG_MAJOR TTY_MAJOR
#define PMACZILOG_MINOR 64
@@ -153,8 +157,8 @@ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs)
write_zsreg(uap, R10, regs[R10]);
/* Set TX/RX controls sans the enable bits. */
- write_zsreg(uap, R3, regs[R3] & ~RxENABLE);
- write_zsreg(uap, R5, regs[R5] & ~TxENABLE);
+ write_zsreg(uap, R3, regs[R3] & ~RxENABLE);
+ write_zsreg(uap, R5, regs[R5] & ~TxENABLE);
/* now set R7 "prime" on ESCC */
write_zsreg(uap, R15, regs[R15] | EN85C30);
@@ -205,7 +209,7 @@ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs)
*/
static void pmz_maybe_update_regs(struct uart_pmac_port *uap)
{
- if (!ZS_REGS_HELD(uap)) {
+ if (!ZS_REGS_HELD(uap)) {
if (ZS_TX_ACTIVE(uap)) {
uap->flags |= PMACZILOG_FLAG_REGS_HELD;
} else {
@@ -281,7 +285,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
spin_lock(&uap->port.lock);
if (swallow)
goto next_char;
- }
+ }
#endif /* CONFIG_MAGIC_SYSRQ && CONFIG_SERIAL_CORE_CONSOLE */
/* A real serial line, record the character and status. */
@@ -317,7 +321,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
if (uap->port.ignore_status_mask == 0xff ||
(r1 & uap->port.ignore_status_mask) == 0) {
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(tty, ch, flag);
}
if (r1 & Rx_OVR)
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
@@ -341,7 +345,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
write_zsreg(uap, R1, uap->curregs[R1]);
zssync(uap);
- dev_err(&uap->dev->ofdev.dev, "pmz: rx irq flood !\n");
+ pmz_error("pmz: rx irq flood !\n");
return tty;
}
@@ -470,47 +474,47 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
uap_a = pmz_get_port_A(uap);
uap_b = uap_a->mate;
-
- spin_lock(&uap_a->port.lock);
+
+ spin_lock(&uap_a->port.lock);
r3 = read_zsreg(uap_a, R3);
#ifdef DEBUG_HARD
pmz_debug("irq, r3: %x\n", r3);
#endif
- /* Channel A */
+ /* Channel A */
tty = NULL;
- if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+ if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
write_zsreg(uap_a, R0, RES_H_IUS);
zssync(uap_a);
- if (r3 & CHAEXT)
- pmz_status_handle(uap_a);
+ if (r3 & CHAEXT)
+ pmz_status_handle(uap_a);
if (r3 & CHARxIP)
tty = pmz_receive_chars(uap_a);
- if (r3 & CHATxIP)
- pmz_transmit_chars(uap_a);
- rc = IRQ_HANDLED;
- }
- spin_unlock(&uap_a->port.lock);
+ if (r3 & CHATxIP)
+ pmz_transmit_chars(uap_a);
+ rc = IRQ_HANDLED;
+ }
+ spin_unlock(&uap_a->port.lock);
if (tty != NULL)
tty_flip_buffer_push(tty);
if (uap_b->node == NULL)
goto out;
- spin_lock(&uap_b->port.lock);
+ spin_lock(&uap_b->port.lock);
tty = NULL;
if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
write_zsreg(uap_b, R0, RES_H_IUS);
zssync(uap_b);
- if (r3 & CHBEXT)
- pmz_status_handle(uap_b);
- if (r3 & CHBRxIP)
- tty = pmz_receive_chars(uap_b);
- if (r3 & CHBTxIP)
- pmz_transmit_chars(uap_b);
- rc = IRQ_HANDLED;
- }
- spin_unlock(&uap_b->port.lock);
+ if (r3 & CHBEXT)
+ pmz_status_handle(uap_b);
+ if (r3 & CHBRxIP)
+ tty = pmz_receive_chars(uap_b);
+ if (r3 & CHBTxIP)
+ pmz_transmit_chars(uap_b);
+ rc = IRQ_HANDLED;
+ }
+ spin_unlock(&uap_b->port.lock);
if (tty != NULL)
tty_flip_buffer_push(tty);
@@ -718,7 +722,7 @@ static void pmz_enable_ms(struct uart_port *port)
if (ZS_IS_ASLEEP(uap))
return;
- /* NOTE: Not subject to 'transmitter active' rule. */
+ /* NOTE: Not subject to 'transmitter active' rule. */
write_zsreg(uap, R15, uap->curregs[R15]);
}
}
@@ -748,7 +752,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state)
if (new_reg != uap->curregs[R5]) {
uap->curregs[R5] = new_reg;
- /* NOTE: Not subject to 'transmitter active' rule. */
+ /* NOTE: Not subject to 'transmitter active' rule. */
if (ZS_IS_ASLEEP(uap))
return;
write_zsreg(uap, R5, uap->curregs[R5]);
@@ -757,6 +761,8 @@ static void pmz_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&port->lock, flags);
}
+#ifdef CONFIG_PPC_PMAC
+
/*
* Turn power on or off to the SCC and associated stuff
* (port drivers, modem, IR port, etc.)
@@ -792,6 +798,15 @@ static int pmz_set_scc_power(struct uart_pmac_port *uap, int state)
return delay;
}
+#else
+
+static int pmz_set_scc_power(struct uart_pmac_port *uap, int state)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_PPC_PMAC */
+
/*
* FixZeroBug....Works around a bug in the SCC receving channel.
* Inspired from Darwin code, 15 Sept. 2000 -DanM
@@ -908,7 +923,6 @@ static int __pmz_startup(struct uart_pmac_port *uap)
/* Remember status for DCD/CTS changes */
uap->prev_status = read_zsreg(uap, R0);
-
return pwr_delay;
}
@@ -955,9 +969,9 @@ static int pmz_startup(struct uart_port *port)
}
pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON;
- if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, "PowerMac Zilog", uap)) {
- dev_err(&uap->dev->ofdev.dev,
- "Unable to register zs interrupt handler.\n");
+ if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED,
+ "SCC", uap)) {
+ pmz_error("Unable to register zs interrupt handler.\n");
pmz_set_scc_power(uap, 0);
mutex_unlock(&pmz_irq_mutex);
return -ENXIO;
@@ -983,7 +997,7 @@ static int pmz_startup(struct uart_port *port)
if (!ZS_IS_EXTCLK(uap))
uap->curregs[R1] |= EXT_INT_ENAB;
write_zsreg(uap, R1, uap->curregs[R1]);
- spin_unlock_irqrestore(&port->lock, flags);
+ spin_unlock_irqrestore(&port->lock, flags);
pmz_debug("pmz: startup() done.\n");
@@ -1003,7 +1017,7 @@ static void pmz_shutdown(struct uart_port *port)
mutex_lock(&pmz_irq_mutex);
/* Release interrupt handler */
- free_irq(uap->port.irq, uap);
+ free_irq(uap->port.irq, uap);
spin_lock_irqsave(&port->lock, flags);
@@ -1051,7 +1065,6 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag,
{
int brg;
-
/* Switch to external clocking for IrDA high clock rates. That
* code could be re-used for Midi interfaces with different
* multipliers
@@ -1198,7 +1211,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud)
while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0
|| (read_zsreg(uap, R1) & ALL_SNT) == 0) {
if (--t <= 0) {
- dev_err(&uap->dev->ofdev.dev, "transmitter didn't drain\n");
+ pmz_error("transmitter didn't drain\n");
return;
}
udelay(10);
@@ -1214,7 +1227,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud)
read_zsdata(uap);
mdelay(10);
if (--t <= 0) {
- dev_err(&uap->dev->ofdev.dev, "receiver didn't drain\n");
+ pmz_error("receiver didn't drain\n");
return;
}
}
@@ -1223,20 +1236,19 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud)
uap->curregs[R5] |= DTR;
write_zsreg(uap, R5, uap->curregs[R5]);
zssync(uap);
- mdelay(1);
+ mdelay(1);
/* Switch SCC to 19200 */
pmz_convert_to_zs(uap, CS8, 0, 19200);
pmz_load_zsregs(uap, uap->curregs);
- mdelay(1);
+ mdelay(1);
/* Write get_version command byte */
write_zsdata(uap, 1);
t = 5000;
while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) {
if (--t <= 0) {
- dev_err(&uap->dev->ofdev.dev,
- "irda_setup timed out on get_version byte\n");
+ pmz_error("irda_setup timed out on get_version byte\n");
goto out;
}
udelay(10);
@@ -1244,8 +1256,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud)
version = read_zsdata(uap);
if (version < 4) {
- dev_info(&uap->dev->ofdev.dev, "IrDA: dongle version %d not supported\n",
- version);
+ pmz_info("IrDA: dongle version %d not supported\n", version);
goto out;
}
@@ -1254,18 +1265,16 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud)
t = 5000;
while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) {
if (--t <= 0) {
- dev_err(&uap->dev->ofdev.dev,
- "irda_setup timed out on speed mode byte\n");
+ pmz_error("irda_setup timed out on speed mode byte\n");
goto out;
}
udelay(10);
}
t = read_zsdata(uap);
if (t != cmdbyte)
- dev_err(&uap->dev->ofdev.dev,
- "irda_setup speed mode byte = %x (%x)\n", t, cmdbyte);
+ pmz_error("irda_setup speed mode byte = %x (%x)\n", t, cmdbyte);
- dev_info(&uap->dev->ofdev.dev, "IrDA setup for %ld bps, dongle version: %d\n",
+ pmz_info("IrDA setup for %ld bps, dongle version: %d\n",
*baud, version);
(void)read_zsdata(uap);
@@ -1415,7 +1424,7 @@ static void pmz_poll_put_char(struct uart_port *port, unsigned char c)
write_zsdata(uap, c);
}
-#endif
+#endif /* CONFIG_CONSOLE_POLL */
static struct uart_ops pmz_pops = {
.tx_empty = pmz_tx_empty,
@@ -1440,6 +1449,8 @@ static struct uart_ops pmz_pops = {
#endif
};
+#ifdef CONFIG_PPC_PMAC
+
/*
* Setup one port structure after probing, HW is down at this point,
* Unlike sunzilog, we don't need to pre-init the spinlock as we don't
@@ -1463,7 +1474,7 @@ static int __init pmz_init_port(struct uart_pmac_port *uap)
return -ENODEV;
uap->port.mapbase = r_ports.start;
uap->port.membase = ioremap(uap->port.mapbase, 0x1000);
-
+
uap->control_reg = uap->port.membase;
uap->data_reg = uap->control_reg + 0x10;
@@ -1590,7 +1601,7 @@ static void pmz_dispose_port(struct uart_pmac_port *uap)
}
/*
- * Called upon match with an escc node in the devive-tree.
+ * Called upon match with an escc node in the device-tree.
*/
static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match)
{
@@ -1812,7 +1823,7 @@ static int __init pmz_probe(void)
pmz_ports[count].node = node_a;
pmz_ports[count+1].node = node_b;
pmz_ports[count].port.line = count;
- pmz_ports[count+1].port.line = count+1;
+ pmz_ports[count+1].port.line = count+1;
/*
* Setup the ports for real
@@ -1836,6 +1847,88 @@ next:
return 0;
}
+#else
+
+extern struct platform_device scc_a_pdev, scc_b_pdev;
+
+static int __init pmz_init_port(struct uart_pmac_port *uap)
+{
+ struct resource *r_ports;
+ int irq;
+
+ r_ports = platform_get_resource(uap->node, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(uap->node, 0);
+ if (!r_ports || !irq)
+ return -ENODEV;
+
+ uap->port.mapbase = r_ports->start;
+ uap->port.membase = (unsigned char __iomem *) r_ports->start;
+ uap->port.iotype = UPIO_MEM;
+ uap->port.irq = irq;
+ uap->port.uartclk = ZS_CLOCK;
+ uap->port.fifosize = 1;
+ uap->port.ops = &pmz_pops;
+ uap->port.type = PORT_PMAC_ZILOG;
+ uap->port.flags = 0;
+
+ uap->control_reg = uap->port.membase;
+ uap->data_reg = uap->control_reg + 4;
+ uap->port_type = 0;
+
+ pmz_convert_to_zs(uap, CS8, 0, 9600);
+
+ return 0;
+}
+
+static int __init pmz_probe(void)
+{
+ int err;
+
+ pmz_ports_count = 0;
+
+ pmz_ports[0].mate = &pmz_ports[1];
+ pmz_ports[0].port.line = 0;
+ pmz_ports[0].flags = PMACZILOG_FLAG_IS_CHANNEL_A;
+ pmz_ports[0].node = &scc_a_pdev;
+ err = pmz_init_port(&pmz_ports[0]);
+ if (err)
+ return err;
+ pmz_ports_count++;
+
+ pmz_ports[1].mate = &pmz_ports[0];
+ pmz_ports[1].port.line = 1;
+ pmz_ports[1].flags = 0;
+ pmz_ports[1].node = &scc_b_pdev;
+ err = pmz_init_port(&pmz_ports[1]);
+ if (err)
+ return err;
+ pmz_ports_count++;
+
+ return 0;
+}
+
+static void pmz_dispose_port(struct uart_pmac_port *uap)
+{
+ memset(uap, 0, sizeof(struct uart_pmac_port));
+}
+
+static int __init pmz_attach(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < pmz_ports_count; i++)
+ if (pmz_ports[i].node == pdev)
+ return 0;
+ return -ENODEV;
+}
+
+static int __exit pmz_detach(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_PPC_PMAC */
+
#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
static void pmz_console_write(struct console *con, const char *s, unsigned int count);
@@ -1896,28 +1989,41 @@ err_out:
return rc;
}
+#ifdef CONFIG_PPC_PMAC
+
static struct of_device_id pmz_match[] =
{
{
- .name = "ch-a",
+ .name = "ch-a",
},
{
- .name = "ch-b",
+ .name = "ch-b",
},
{},
};
MODULE_DEVICE_TABLE (of, pmz_match);
-static struct macio_driver pmz_driver =
-{
+static struct macio_driver pmz_driver = {
.name = "pmac_zilog",
.match_table = pmz_match,
.probe = pmz_attach,
.remove = pmz_detach,
.suspend = pmz_suspend,
- .resume = pmz_resume,
+ .resume = pmz_resume,
};
+#else
+
+static struct platform_driver pmz_driver = {
+ .remove = __exit_p(pmz_detach),
+ .driver = {
+ .name = "scc",
+ .owner = THIS_MODULE,
+ },
+};
+
+#endif /* !CONFIG_PPC_PMAC */
+
static int __init init_pmz(void)
{
int rc, i;
@@ -1952,19 +2058,27 @@ static int __init init_pmz(void)
pmz_dispose_port(&pmz_ports[i]);
return rc;
}
-
+
/*
* Then we register the macio driver itself
*/
+#ifdef CONFIG_PPC_PMAC
return macio_register_driver(&pmz_driver);
+#else
+ return platform_driver_probe(&pmz_driver, pmz_attach);
+#endif
}
static void __exit exit_pmz(void)
{
int i;
+#ifdef CONFIG_PPC_PMAC
/* Get rid of macio-driver (detach from macio) */
macio_unregister_driver(&pmz_driver);
+#else
+ platform_driver_unregister(&pmz_driver);
+#endif
for (i = 0; i < pmz_ports_count; i++) {
struct uart_pmac_port *uport = &pmz_ports[i];
@@ -2031,10 +2145,10 @@ static int __init pmz_console_setup(struct console *co, char *options)
/*
* XServe's default to 57600 bps
*/
- if (machine_is_compatible("RackMac1,1")
- || machine_is_compatible("RackMac1,2")
- || machine_is_compatible("MacRISC4"))
- baud = 57600;
+ if (of_machine_is_compatible("RackMac1,1")
+ || of_machine_is_compatible("RackMac1,2")
+ || of_machine_is_compatible("MacRISC4"))
+ baud = 57600;
/*
* Check whether an invalid uart number has been specified, and
diff --git a/drivers/serial/pmac_zilog.h b/drivers/serial/pmac_zilog.h
index f6e77f12acd..cbc34fbb1b2 100644
--- a/drivers/serial/pmac_zilog.h
+++ b/drivers/serial/pmac_zilog.h
@@ -1,7 +1,15 @@
#ifndef __PMAC_ZILOG_H__
#define __PMAC_ZILOG_H__
-#define pmz_debug(fmt,arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg)
+#ifdef CONFIG_PPC_PMAC
+#define pmz_debug(fmt, arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg)
+#define pmz_error(fmt, arg...) dev_err(&uap->dev->ofdev.dev, fmt, ## arg)
+#define pmz_info(fmt, arg...) dev_info(&uap->dev->ofdev.dev, fmt, ## arg)
+#else
+#define pmz_debug(fmt, arg...) dev_dbg(&uap->node->dev, fmt, ## arg)
+#define pmz_error(fmt, arg...) dev_err(&uap->node->dev, fmt, ## arg)
+#define pmz_info(fmt, arg...) dev_info(&uap->node->dev, fmt, ## arg)
+#endif
/*
* At most 2 ESCCs with 2 ports each
@@ -17,6 +25,7 @@ struct uart_pmac_port {
struct uart_port port;
struct uart_pmac_port *mate;
+#ifdef CONFIG_PPC_PMAC
/* macio_dev for the escc holding this port (maybe be null on
* early inited port)
*/
@@ -25,6 +34,9 @@ struct uart_pmac_port {
* of "escc" node (ie. ch-a or ch-b)
*/
struct device_node *node;
+#else
+ struct platform_device *node;
+#endif
/* Port type as obtained from device tree (IRDA, modem, ...) */
int port_type;
@@ -55,10 +67,12 @@ struct uart_pmac_port {
volatile u8 __iomem *control_reg;
volatile u8 __iomem *data_reg;
+#ifdef CONFIG_PPC_PMAC
unsigned int tx_dma_irq;
unsigned int rx_dma_irq;
volatile struct dbdma_regs __iomem *tx_dma_regs;
volatile struct dbdma_regs __iomem *rx_dma_regs;
+#endif
struct ktermios termios_cache;
};
@@ -113,7 +127,7 @@ static inline void zssync(struct uart_pmac_port *port)
#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */
+#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */
/* The Zilog register set */
@@ -171,7 +185,7 @@ static inline void zssync(struct uart_pmac_port *port)
/* Write Register 3 */
-#define RxENABLE 0x1 /* Rx Enable */
+#define RxENABLE 0x1 /* Rx Enable */
#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
@@ -185,7 +199,7 @@ static inline void zssync(struct uart_pmac_port *port)
/* Write Register 4 */
-#define PAR_ENAB 0x1 /* Parity Enable */
+#define PAR_ENAB 0x1 /* Parity Enable */
#define PAR_EVEN 0x2 /* Parity Even/Odd* */
#define SYNC_ENAB 0 /* Sync Modes Enable */
@@ -210,7 +224,7 @@ static inline void zssync(struct uart_pmac_port *port)
#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
#define RTS 0x2 /* RTS */
#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
-#define TxENABLE 0x8 /* Tx Enable */
+#define TxENABLE 0x8 /* Tx Enable */
#define SND_BRK 0x10 /* Send Break */
#define Tx5 0x0 /* Tx 5 bits (or less)/character */
#define Tx7 0x20 /* Tx 7 bits/character */
@@ -372,11 +386,11 @@ static inline void zssync(struct uart_pmac_port *port)
#define ZS_TX_ACTIVE(UP) ((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE)
#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS)
#define ZS_IS_IRDA(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA)
-#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM)
+#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM)
#define ZS_HAS_DMA(UP) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA)
-#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP)
-#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN)
-#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON)
-#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK)
+#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP)
+#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN)
+#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON)
+#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK)
#endif /* __PMAC_ZILOG_H__ */
diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c
index 95421fa3b30..e91db4b3801 100644
--- a/drivers/serial/serial_cs.c
+++ b/drivers/serial/serial_cs.c
@@ -696,11 +696,11 @@ static int serial_config(struct pcmcia_device * link)
info->multi = info->quirk->multi;
if (info->multi > 1)
- multi_config(link);
+ i = multi_config(link);
else
- simple_config(link);
+ i = simple_config(link);
- if (info->ndev == 0)
+ if (i || info->ndev == 0)
goto failed;
/*
@@ -715,6 +715,7 @@ static int serial_config(struct pcmcia_device * link)
return 0;
failed:
+ dev_warn(&link->dev, "serial_cs: failed to initialize\n");
serial_remove(link);
return -ENODEV;
}
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 0efcded59ae..f7d2589926d 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -518,34 +518,6 @@ static inline int sci_rxd_in(struct uart_port *port)
{
if (port->mapbase == 0xfffffe80)
return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */
- if (port->mapbase == 0xa4000150)
- return __raw_readb(SCPDR)&0x10 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xa4000140)
- return __raw_readb(SCPDR)&0x04 ? 1 : 0; /* IRDA */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == SCIF0)
- return __raw_readb(SCPDR)&0x04 ? 1 : 0; /* IRDA */
- if (port->mapbase == SCIF2)
- return __raw_readb(SCPDR)&0x10 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- return sci_in(port,SCxSR)&0x0010 ? 1 : 0;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xa4430000)
- return sci_in(port, SCxSR) & 0x0003 ? 1 : 0;
- else if (port->mapbase == 0xa4438000)
- return sci_in(port, SCxSR) & 0x0003 ? 1 : 0;
return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \
@@ -558,207 +530,17 @@ static inline int sci_rxd_in(struct uart_port *port)
{
if (port->mapbase == 0xffe00000)
return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */
- if (port->mapbase == 0xffe80000)
- return __raw_readw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe80000)
- return __raw_readw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
return 1;
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7757)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xfe4b0000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0;
- if (port->mapbase == 0xfe4c0000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0;
- if (port->mapbase == 0xfe4d0000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xfe600000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfe610000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfe620000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffe10000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffe20000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffe30000)
- return __raw_readw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7366)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readb(SCPDR0) & 0x0001 ? 1 : 0; /* SCIF0 */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readb(PSDR) & 0x02 ? 1 : 0; /* SCIF0 */
- if (port->mapbase == 0xffe10000)
- return __raw_readb(PADR) & 0x40 ? 1 : 0; /* SCIF1 */
- if (port->mapbase == 0xffe20000)
- return __raw_readb(PWDR) & 0x04 ? 1 : 0; /* SCIF2 */
-
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readb(SCSPTR0) & 0x0008 ? 1 : 0; /* SCIF0 */
- if (port->mapbase == 0xffe10000)
- return __raw_readb(SCSPTR1) & 0x0020 ? 1 : 0; /* SCIF1 */
- if (port->mapbase == 0xffe20000)
- return __raw_readb(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF2 */
- if (port->mapbase == 0xa4e30000)
- return __raw_readb(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF3 */
- if (port->mapbase == 0xa4e40000)
- return __raw_readb(SCSPTR4) & 0x0001 ? 1 : 0; /* SCIF4 */
- if (port->mapbase == 0xa4e50000)
- return __raw_readb(SCSPTR5) & 0x0008 ? 1 : 0; /* SCIF5 */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
-# define SCFSR 0x0010
-# define SCASSR 0x0014
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->type == PORT_SCIF)
- return __raw_readw((port->mapbase + SCFSR)) & SCIF_BRK ? 1 : 0;
- if (port->type == PORT_SCIFA)
- return __raw_readw((port->mapbase + SCASSR)) & SCIF_BRK ? 1 : 0;
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- return sci_in(port, SCSPTR)&0x0001 ? 1 : 0; /* SCIF */
-}
#elif defined(__H8300H__) || defined(__H8300S__)
static inline int sci_rxd_in(struct uart_port *port)
{
int ch = (port->mapbase - SMR0) >> 3;
return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0;
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffe08000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffe10000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF/IRDA */
-
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xff923000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xff924000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xff925000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffe10000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffea0000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffeb0000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffec0000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffed0000)
- return __raw_readw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffee0000)
- return __raw_readw(SCSPTR4) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffef0000)
- return __raw_readw(SCSPTR5) & 0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \
- defined(CONFIG_CPU_SUBTYPE_SH7203) || \
- defined(CONFIG_CPU_SUBTYPE_SH7206) || \
- defined(CONFIG_CPU_SUBTYPE_SH7263)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xfffe8000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfffe8800)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfffe9000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfffe9800)
- return __raw_readw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
-#if defined(CONFIG_CPU_SUBTYPE_SH7201)
- if (port->mapbase == 0xfffeA000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfffeA800)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfffeB000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xfffeB800)
- return __raw_readw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
-#endif
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xf8400000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xf8410000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xf8420000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SHX3)
+#else /* default case for non-SCI processors */
static inline int sci_rxd_in(struct uart_port *port)
{
- if (port->mapbase == 0xffc30000)
- return __raw_readw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffc40000)
- return __raw_readw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffc50000)
- return __raw_readw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
- if (port->mapbase == 0xffc60000)
- return __raw_readw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
return 1;
}
#endif
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index d5d7f23c19a..3a5a17db947 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -259,6 +259,43 @@ static void intc_disable(unsigned int irq)
}
}
+static void (*intc_enable_noprio_fns[])(unsigned long addr,
+ unsigned long handle,
+ void (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq) = {
+ [MODE_ENABLE_REG] = intc_mode_field,
+ [MODE_MASK_REG] = intc_mode_zero,
+ [MODE_DUAL_REG] = intc_mode_field,
+ [MODE_PRIO_REG] = intc_mode_field,
+ [MODE_PCLR_REG] = intc_mode_field,
+};
+
+static void intc_enable_disable(struct intc_desc_int *d,
+ unsigned long handle, int do_enable)
+{
+ unsigned long addr;
+ unsigned int cpu;
+ void (*fn)(unsigned long, unsigned long,
+ void (*)(unsigned long, unsigned long, unsigned long),
+ unsigned int);
+
+ if (do_enable) {
+ for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
+ addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
+ fn = intc_enable_noprio_fns[_INTC_MODE(handle)];
+ fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
+ }
+ } else {
+ for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
+ addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
+ fn = intc_disable_fns[_INTC_MODE(handle)];
+ fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
+ }
+ }
+}
+
static int intc_set_wake(unsigned int irq, unsigned int on)
{
return 0; /* allow wakeup, but setup hardware in intc_suspend() */
@@ -400,11 +437,11 @@ static unsigned int __init intc_get_reg(struct intc_desc_int *d,
static intc_enum __init intc_grp_id(struct intc_desc *desc,
intc_enum enum_id)
{
- struct intc_group *g = desc->groups;
+ struct intc_group *g = desc->hw.groups;
unsigned int i, j;
- for (i = 0; g && enum_id && i < desc->nr_groups; i++) {
- g = desc->groups + i;
+ for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) {
+ g = desc->hw.groups + i;
for (j = 0; g->enum_ids[j]; j++) {
if (g->enum_ids[j] != enum_id)
@@ -417,19 +454,21 @@ static intc_enum __init intc_grp_id(struct intc_desc *desc,
return 0;
}
-static unsigned int __init intc_mask_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id, int do_grps)
+static unsigned int __init _intc_mask_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id,
+ unsigned int *reg_idx,
+ unsigned int *fld_idx)
{
- struct intc_mask_reg *mr = desc->mask_regs;
- unsigned int i, j, fn, mode;
+ struct intc_mask_reg *mr = desc->hw.mask_regs;
+ unsigned int fn, mode;
unsigned long reg_e, reg_d;
- for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) {
- mr = desc->mask_regs + i;
+ while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) {
+ mr = desc->hw.mask_regs + *reg_idx;
- for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
- if (mr->enum_ids[j] != enum_id)
+ for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) {
+ if (mr->enum_ids[*fld_idx] != enum_id)
continue;
if (mr->set_reg && mr->clr_reg) {
@@ -455,29 +494,49 @@ static unsigned int __init intc_mask_data(struct intc_desc *desc,
intc_get_reg(d, reg_e),
intc_get_reg(d, reg_d),
1,
- (mr->reg_width - 1) - j);
+ (mr->reg_width - 1) - *fld_idx);
}
+
+ *fld_idx = 0;
+ (*reg_idx)++;
}
+ return 0;
+}
+
+static unsigned int __init intc_mask_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id, int do_grps)
+{
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int ret;
+
+ ret = _intc_mask_data(desc, d, enum_id, &i, &j);
+ if (ret)
+ return ret;
+
if (do_grps)
return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0);
return 0;
}
-static unsigned int __init intc_prio_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id, int do_grps)
+static unsigned int __init _intc_prio_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id,
+ unsigned int *reg_idx,
+ unsigned int *fld_idx)
{
- struct intc_prio_reg *pr = desc->prio_regs;
- unsigned int i, j, fn, mode, bit;
+ struct intc_prio_reg *pr = desc->hw.prio_regs;
+ unsigned int fn, n, mode, bit;
unsigned long reg_e, reg_d;
- for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) {
- pr = desc->prio_regs + i;
+ while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) {
+ pr = desc->hw.prio_regs + *reg_idx;
- for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) {
- if (pr->enum_ids[j] != enum_id)
+ for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) {
+ if (pr->enum_ids[*fld_idx] != enum_id)
continue;
if (pr->set_reg && pr->clr_reg) {
@@ -495,34 +554,79 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc,
}
fn += (pr->reg_width >> 3) - 1;
+ n = *fld_idx + 1;
- BUG_ON((j + 1) * pr->field_width > pr->reg_width);
+ BUG_ON(n * pr->field_width > pr->reg_width);
- bit = pr->reg_width - ((j + 1) * pr->field_width);
+ bit = pr->reg_width - (n * pr->field_width);
return _INTC_MK(fn, mode,
intc_get_reg(d, reg_e),
intc_get_reg(d, reg_d),
pr->field_width, bit);
}
+
+ *fld_idx = 0;
+ (*reg_idx)++;
}
+ return 0;
+}
+
+static unsigned int __init intc_prio_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id, int do_grps)
+{
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int ret;
+
+ ret = _intc_prio_data(desc, d, enum_id, &i, &j);
+ if (ret)
+ return ret;
+
if (do_grps)
return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0);
return 0;
}
+static void __init intc_enable_disable_enum(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id, int enable)
+{
+ unsigned int i, j, data;
+
+ /* go through and enable/disable all mask bits */
+ i = j = 0;
+ do {
+ data = _intc_mask_data(desc, d, enum_id, &i, &j);
+ if (data)
+ intc_enable_disable(d, data, enable);
+ j++;
+ } while (data);
+
+ /* go through and enable/disable all priority fields */
+ i = j = 0;
+ do {
+ data = _intc_prio_data(desc, d, enum_id, &i, &j);
+ if (data)
+ intc_enable_disable(d, data, enable);
+
+ j++;
+ } while (data);
+}
+
static unsigned int __init intc_ack_data(struct intc_desc *desc,
struct intc_desc_int *d,
intc_enum enum_id)
{
- struct intc_mask_reg *mr = desc->ack_regs;
+ struct intc_mask_reg *mr = desc->hw.ack_regs;
unsigned int i, j, fn, mode;
unsigned long reg_e, reg_d;
- for (i = 0; mr && enum_id && i < desc->nr_ack_regs; i++) {
- mr = desc->ack_regs + i;
+ for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) {
+ mr = desc->hw.ack_regs + i;
for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
if (mr->enum_ids[j] != enum_id)
@@ -549,11 +653,11 @@ static unsigned int __init intc_sense_data(struct intc_desc *desc,
struct intc_desc_int *d,
intc_enum enum_id)
{
- struct intc_sense_reg *sr = desc->sense_regs;
+ struct intc_sense_reg *sr = desc->hw.sense_regs;
unsigned int i, j, fn, bit;
- for (i = 0; sr && enum_id && i < desc->nr_sense_regs; i++) {
- sr = desc->sense_regs + i;
+ for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) {
+ sr = desc->hw.sense_regs + i;
for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
if (sr->enum_ids[j] != enum_id)
@@ -656,7 +760,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
/* irq should be disabled by default */
d->chip.mask(irq);
- if (desc->ack_regs)
+ if (desc->hw.ack_regs)
ack_handle[irq] = intc_ack_data(desc, d, enum_id);
}
@@ -684,6 +788,7 @@ static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
void __init register_intc_controller(struct intc_desc *desc)
{
unsigned int i, k, smp;
+ struct intc_hw_desc *hw = &desc->hw;
struct intc_desc_int *d;
d = kzalloc(sizeof(*d), GFP_NOWAIT);
@@ -691,10 +796,10 @@ void __init register_intc_controller(struct intc_desc *desc)
INIT_LIST_HEAD(&d->list);
list_add(&d->list, &intc_list);
- 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;
- d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
+ d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
+ d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
+ d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
+ d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
#ifdef CONFIG_SMP
@@ -702,30 +807,31 @@ void __init register_intc_controller(struct intc_desc *desc)
#endif
k = 0;
- if (desc->mask_regs) {
- for (i = 0; i < desc->nr_mask_regs; i++) {
- smp = IS_SMP(desc->mask_regs[i]);
- k += save_reg(d, k, desc->mask_regs[i].set_reg, smp);
- k += save_reg(d, k, desc->mask_regs[i].clr_reg, smp);
+ if (hw->mask_regs) {
+ for (i = 0; i < hw->nr_mask_regs; i++) {
+ smp = IS_SMP(hw->mask_regs[i]);
+ k += save_reg(d, k, hw->mask_regs[i].set_reg, smp);
+ k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp);
}
}
- if (desc->prio_regs) {
- d->prio = kzalloc(desc->nr_vectors * sizeof(*d->prio), GFP_NOWAIT);
+ if (hw->prio_regs) {
+ d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio),
+ GFP_NOWAIT);
- for (i = 0; i < desc->nr_prio_regs; i++) {
- smp = IS_SMP(desc->prio_regs[i]);
- k += save_reg(d, k, desc->prio_regs[i].set_reg, smp);
- k += save_reg(d, k, desc->prio_regs[i].clr_reg, smp);
+ for (i = 0; i < hw->nr_prio_regs; i++) {
+ smp = IS_SMP(hw->prio_regs[i]);
+ k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
+ k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
}
}
- if (desc->sense_regs) {
- d->sense = kzalloc(desc->nr_vectors * sizeof(*d->sense), GFP_NOWAIT);
+ if (hw->sense_regs) {
+ d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense),
+ GFP_NOWAIT);
- for (i = 0; i < desc->nr_sense_regs; i++) {
- k += save_reg(d, k, desc->sense_regs[i].reg, 0);
- }
+ for (i = 0; i < hw->nr_sense_regs; i++)
+ k += save_reg(d, k, hw->sense_regs[i].reg, 0);
}
d->chip.name = desc->name;
@@ -738,18 +844,26 @@ void __init register_intc_controller(struct intc_desc *desc)
d->chip.set_type = intc_set_sense;
d->chip.set_wake = intc_set_wake;
- 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);
+ if (hw->ack_regs) {
+ for (i = 0; i < hw->nr_ack_regs; i++)
+ k += save_reg(d, k, hw->ack_regs[i].set_reg, 0);
d->chip.mask_ack = intc_mask_ack;
}
+ /* disable bits matching force_disable before registering irqs */
+ if (desc->force_disable)
+ intc_enable_disable_enum(desc, d, desc->force_disable, 0);
+
+ /* disable bits matching force_enable before registering irqs */
+ if (desc->force_enable)
+ intc_enable_disable_enum(desc, d, desc->force_enable, 0);
+
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
/* register the vectors one by one */
- for (i = 0; i < desc->nr_vectors; i++) {
- struct intc_vect *vect = desc->vectors + i;
+ for (i = 0; i < hw->nr_vectors; i++) {
+ struct intc_vect *vect = hw->vectors + i;
unsigned int irq = evt2irq(vect->vect);
struct irq_desc *irq_desc;
@@ -764,8 +878,8 @@ void __init register_intc_controller(struct intc_desc *desc)
intc_register_irq(desc, d, vect->enum_id, irq);
- for (k = i + 1; k < desc->nr_vectors; k++) {
- struct intc_vect *vect2 = desc->vectors + k;
+ for (k = i + 1; k < hw->nr_vectors; k++) {
+ struct intc_vect *vect2 = hw->vectors + k;
unsigned int irq2 = evt2irq(vect2->vect);
if (vect->enum_id != vect2->enum_id)
@@ -785,11 +899,15 @@ void __init register_intc_controller(struct intc_desc *desc)
vect2->enum_id = 0;
/* redirect this interrupts to the first one */
- set_irq_chip_and_handler_name(irq2, &d->chip,
- intc_redirect_irq, "redirect");
+ set_irq_chip(irq2, &dummy_irq_chip);
+ set_irq_chained_handler(irq2, intc_redirect_irq);
set_irq_data(irq2, (void *)irq);
}
}
+
+ /* enable bits matching force_enable after registering irqs */
+ if (desc->force_enable)
+ intc_enable_disable_enum(desc, d, desc->force_enable, 1);
}
static int intc_suspend(struct sys_device *dev, pm_message_t state)
@@ -872,7 +990,7 @@ 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 create_irq_nr(unsigned int irq_want, int node)
{
unsigned int irq = 0, new;
unsigned long flags;
@@ -881,24 +999,28 @@ static unsigned int create_irq_on_node(unsigned int irq_want, int node)
spin_lock_irqsave(&vector_lock, flags);
/*
- * First try the wanted IRQ, then scan.
+ * First try the wanted IRQ
*/
- if (test_and_set_bit(irq_want, intc_irq_map)) {
+ if (test_and_set_bit(irq_want, intc_irq_map) == 0) {
+ new = irq_want;
+ } else {
+ /* .. then fall back to scanning. */
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;
}
+ 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);
+ irq = new;
+
out_unlock:
spin_unlock_irqrestore(&vector_lock, flags);
@@ -913,7 +1035,7 @@ int create_irq(void)
int nid = cpu_to_node(smp_processor_id());
int irq;
- irq = create_irq_on_node(NR_IRQS_LEGACY, nid);
+ irq = create_irq_nr(NR_IRQS_LEGACY, nid);
if (irq == 0)
irq = -1;
diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c
index 082604edc4c..cf0303acab8 100644
--- a/drivers/sh/pfc.c
+++ b/drivers/sh/pfc.c
@@ -337,12 +337,39 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
if (!enum_id)
break;
+ /* first check if this is a function enum */
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) {
+ /* not a function enum */
+ if (range) {
+ /*
+ * other range exists, so this pin is
+ * a regular GPIO pin that now is being
+ * bound to a specific direction.
+ *
+ * for this case we only allow function enums
+ * and the enums that match the other range.
+ */
+ in_range = enum_in_range(enum_id, range);
+
+ /*
+ * special case pass through for fixed
+ * input-only or output-only pins without
+ * function enum register association.
+ */
+ if (in_range && enum_id == range->force)
+ continue;
+ } else {
+ /*
+ * no other range exists, so this pin
+ * must then be of the function type.
+ *
+ * allow function type pins to select
+ * any combination of function/in/out
+ * in their MARK lists.
+ */
+ in_range = 1;
+ }
}
if (!in_range)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 66c35d74a76..a191fa2be7c 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -100,6 +100,23 @@ config SPI_BUTTERFLY
inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware.
+config SPI_COLDFIRE_QSPI
+ tristate "Freescale Coldfire QSPI controller"
+ depends on (M520x || M523x || M5249 || M527x || M528x || M532x)
+ help
+ This enables support for the Coldfire QSPI controller in master
+ mode.
+
+ This driver can also be built as a module. If so, the module
+ will be called coldfire_qspi.
+
+config SPI_DAVINCI
+ tristate "SPI controller driver for DaVinci/DA8xx SoC's"
+ depends on SPI_MASTER && ARCH_DAVINCI
+ select SPI_BITBANG
+ help
+ SPI master controller for DaVinci and DA8xx SPI modules.
+
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
depends on GENERIC_GPIO
@@ -308,7 +325,7 @@ config SPI_NUC900
#
config SPI_DESIGNWARE
- bool "DesignWare SPI controller core support"
+ tristate "DesignWare SPI controller core support"
depends on SPI_MASTER
help
general driver for SPI controller core from DesignWare
@@ -317,6 +334,10 @@ config SPI_DW_PCI
tristate "PCI interface driver for DW SPI core"
depends on SPI_DESIGNWARE && PCI
+config SPI_DW_MMIO
+ tristate "Memory-mapped io interface driver for DW SPI core"
+ depends on SPI_DESIGNWARE && HAVE_CLK
+
#
# There are lots of SPI device types, with sensors and memory
# being probably the most widely used ones.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index f3d2810ba11..d7d0f89b797 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -16,8 +16,11 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
+obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o
+obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o
obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o
obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o
+obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o
obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index ff5bbb9c43c..9aeb6811310 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -363,6 +363,7 @@ struct pl022 {
void *rx_end;
enum ssp_reading read;
enum ssp_writing write;
+ u32 exp_fifo_level;
};
/**
@@ -501,6 +502,9 @@ static int flush(struct pl022 *pl022)
while (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
readw(SSP_DR(pl022->virtbase));
} while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_BSY) && limit--);
+
+ pl022->exp_fifo_level = 0;
+
return limit;
}
@@ -583,10 +587,9 @@ static void readwriter(struct pl022 *pl022)
* errons in 8bit wide transfers on ARM variants (just 8 words
* FIFO, means only 8x8 = 64 bits in FIFO) at least.
*
- * FIXME: currently we have no logic to account for this.
- * perhaps there is even something broken in HW regarding
- * 8bit transfers (it doesn't fail on 16bit) so this needs
- * more investigation...
+ * To prevent this issue, the TX FIFO is only filled to the
+ * unused RX FIFO fill length, regardless of what the TX
+ * FIFO status flag indicates.
*/
dev_dbg(&pl022->adev->dev,
"%s, rx: %p, rxend: %p, tx: %p, txend: %p\n",
@@ -613,11 +616,12 @@ static void readwriter(struct pl022 *pl022)
break;
}
pl022->rx += (pl022->cur_chip->n_bytes);
+ pl022->exp_fifo_level--;
}
/*
- * Write as much as you can, while keeping an eye on the RX FIFO!
+ * Write as much as possible up to the RX FIFO size
*/
- while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF)
+ while ((pl022->exp_fifo_level < pl022->vendor->fifodepth)
&& (pl022->tx < pl022->tx_end)) {
switch (pl022->write) {
case WRITING_NULL:
@@ -634,6 +638,7 @@ static void readwriter(struct pl022 *pl022)
break;
}
pl022->tx += (pl022->cur_chip->n_bytes);
+ pl022->exp_fifo_level++;
/*
* This inner reader takes care of things appearing in the RX
* FIFO as we're transmitting. This will happen a lot since the
@@ -660,6 +665,7 @@ static void readwriter(struct pl022 *pl022)
break;
}
pl022->rx += (pl022->cur_chip->n_bytes);
+ pl022->exp_fifo_level--;
}
}
/*
diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c
index cfd5ff9508f..ba8ac4f599d 100644
--- a/drivers/spi/au1550_spi.c
+++ b/drivers/spi/au1550_spi.c
@@ -412,11 +412,13 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t)
}
/* put buffers on the ring */
- res = au1xxx_dbdma_put_dest(hw->dma_rx_ch, hw->rx, t->len);
+ res = au1xxx_dbdma_put_dest(hw->dma_rx_ch, virt_to_phys(hw->rx),
+ t->len, DDMA_FLAGS_IE);
if (!res)
dev_err(hw->dev, "rx dma put dest error\n");
- res = au1xxx_dbdma_put_source(hw->dma_tx_ch, (void *)hw->tx, t->len);
+ res = au1xxx_dbdma_put_source(hw->dma_tx_ch, virt_to_phys(hw->tx),
+ t->len, DDMA_FLAGS_IE);
if (!res)
dev_err(hw->dev, "tx dma put source error\n");
diff --git a/drivers/spi/coldfire_qspi.c b/drivers/spi/coldfire_qspi.c
new file mode 100644
index 00000000000..59be3efe063
--- /dev/null
+++ b/drivers/spi/coldfire_qspi.c
@@ -0,0 +1,640 @@
+/*
+ * Freescale/Motorola Coldfire Queued SPI driver
+ *
+ * Copyright 2010 Steven King <sfking@fdwdc.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfqspi.h>
+
+#define DRIVER_NAME "mcfqspi"
+
+#define MCFQSPI_BUSCLK (MCF_BUSCLK / 2)
+
+#define MCFQSPI_QMR 0x00
+#define MCFQSPI_QMR_MSTR 0x8000
+#define MCFQSPI_QMR_CPOL 0x0200
+#define MCFQSPI_QMR_CPHA 0x0100
+#define MCFQSPI_QDLYR 0x04
+#define MCFQSPI_QDLYR_SPE 0x8000
+#define MCFQSPI_QWR 0x08
+#define MCFQSPI_QWR_HALT 0x8000
+#define MCFQSPI_QWR_WREN 0x4000
+#define MCFQSPI_QWR_CSIV 0x1000
+#define MCFQSPI_QIR 0x0C
+#define MCFQSPI_QIR_WCEFB 0x8000
+#define MCFQSPI_QIR_ABRTB 0x4000
+#define MCFQSPI_QIR_ABRTL 0x1000
+#define MCFQSPI_QIR_WCEFE 0x0800
+#define MCFQSPI_QIR_ABRTE 0x0400
+#define MCFQSPI_QIR_SPIFE 0x0100
+#define MCFQSPI_QIR_WCEF 0x0008
+#define MCFQSPI_QIR_ABRT 0x0004
+#define MCFQSPI_QIR_SPIF 0x0001
+#define MCFQSPI_QAR 0x010
+#define MCFQSPI_QAR_TXBUF 0x00
+#define MCFQSPI_QAR_RXBUF 0x10
+#define MCFQSPI_QAR_CMDBUF 0x20
+#define MCFQSPI_QDR 0x014
+#define MCFQSPI_QCR 0x014
+#define MCFQSPI_QCR_CONT 0x8000
+#define MCFQSPI_QCR_BITSE 0x4000
+#define MCFQSPI_QCR_DT 0x2000
+
+struct mcfqspi {
+ void __iomem *iobase;
+ int irq;
+ struct clk *clk;
+ struct mcfqspi_cs_control *cs_control;
+
+ wait_queue_head_t waitq;
+
+ struct work_struct work;
+ struct workqueue_struct *workq;
+ spinlock_t lock;
+ struct list_head msgq;
+};
+
+static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val)
+{
+ writew(val, mcfqspi->iobase + MCFQSPI_QMR);
+}
+
+static void mcfqspi_wr_qdlyr(struct mcfqspi *mcfqspi, u16 val)
+{
+ writew(val, mcfqspi->iobase + MCFQSPI_QDLYR);
+}
+
+static u16 mcfqspi_rd_qdlyr(struct mcfqspi *mcfqspi)
+{
+ return readw(mcfqspi->iobase + MCFQSPI_QDLYR);
+}
+
+static void mcfqspi_wr_qwr(struct mcfqspi *mcfqspi, u16 val)
+{
+ writew(val, mcfqspi->iobase + MCFQSPI_QWR);
+}
+
+static void mcfqspi_wr_qir(struct mcfqspi *mcfqspi, u16 val)
+{
+ writew(val, mcfqspi->iobase + MCFQSPI_QIR);
+}
+
+static void mcfqspi_wr_qar(struct mcfqspi *mcfqspi, u16 val)
+{
+ writew(val, mcfqspi->iobase + MCFQSPI_QAR);
+}
+
+static void mcfqspi_wr_qdr(struct mcfqspi *mcfqspi, u16 val)
+{
+ writew(val, mcfqspi->iobase + MCFQSPI_QDR);
+}
+
+static u16 mcfqspi_rd_qdr(struct mcfqspi *mcfqspi)
+{
+ return readw(mcfqspi->iobase + MCFQSPI_QDR);
+}
+
+static void mcfqspi_cs_select(struct mcfqspi *mcfqspi, u8 chip_select,
+ bool cs_high)
+{
+ mcfqspi->cs_control->select(mcfqspi->cs_control, chip_select, cs_high);
+}
+
+static void mcfqspi_cs_deselect(struct mcfqspi *mcfqspi, u8 chip_select,
+ bool cs_high)
+{
+ mcfqspi->cs_control->deselect(mcfqspi->cs_control, chip_select, cs_high);
+}
+
+static int mcfqspi_cs_setup(struct mcfqspi *mcfqspi)
+{
+ return (mcfqspi->cs_control && mcfqspi->cs_control->setup) ?
+ mcfqspi->cs_control->setup(mcfqspi->cs_control) : 0;
+}
+
+static void mcfqspi_cs_teardown(struct mcfqspi *mcfqspi)
+{
+ if (mcfqspi->cs_control && mcfqspi->cs_control->teardown)
+ mcfqspi->cs_control->teardown(mcfqspi->cs_control);
+}
+
+static u8 mcfqspi_qmr_baud(u32 speed_hz)
+{
+ return clamp((MCFQSPI_BUSCLK + speed_hz - 1) / speed_hz, 2u, 255u);
+}
+
+static bool mcfqspi_qdlyr_spe(struct mcfqspi *mcfqspi)
+{
+ return mcfqspi_rd_qdlyr(mcfqspi) & MCFQSPI_QDLYR_SPE;
+}
+
+static irqreturn_t mcfqspi_irq_handler(int this_irq, void *dev_id)
+{
+ struct mcfqspi *mcfqspi = dev_id;
+
+ /* clear interrupt */
+ mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE | MCFQSPI_QIR_SPIF);
+ wake_up(&mcfqspi->waitq);
+
+ return IRQ_HANDLED;
+}
+
+static void mcfqspi_transfer_msg8(struct mcfqspi *mcfqspi, unsigned count,
+ const u8 *txbuf, u8 *rxbuf)
+{
+ unsigned i, n, offset = 0;
+
+ n = min(count, 16u);
+
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_CMDBUF);
+ for (i = 0; i < n; ++i)
+ mcfqspi_wr_qdr(mcfqspi, MCFQSPI_QCR_BITSE);
+
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_TXBUF);
+ if (txbuf)
+ for (i = 0; i < n; ++i)
+ mcfqspi_wr_qdr(mcfqspi, *txbuf++);
+ else
+ for (i = 0; i < count; ++i)
+ mcfqspi_wr_qdr(mcfqspi, 0);
+
+ count -= n;
+ if (count) {
+ u16 qwr = 0xf08;
+ mcfqspi_wr_qwr(mcfqspi, 0x700);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+
+ do {
+ wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi));
+ mcfqspi_wr_qwr(mcfqspi, qwr);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+ if (rxbuf) {
+ mcfqspi_wr_qar(mcfqspi,
+ MCFQSPI_QAR_RXBUF + offset);
+ for (i = 0; i < 8; ++i)
+ *rxbuf++ = mcfqspi_rd_qdr(mcfqspi);
+ }
+ n = min(count, 8u);
+ if (txbuf) {
+ mcfqspi_wr_qar(mcfqspi,
+ MCFQSPI_QAR_TXBUF + offset);
+ for (i = 0; i < n; ++i)
+ mcfqspi_wr_qdr(mcfqspi, *txbuf++);
+ }
+ qwr = (offset ? 0x808 : 0) + ((n - 1) << 8);
+ offset ^= 8;
+ count -= n;
+ } while (count);
+ wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi));
+ mcfqspi_wr_qwr(mcfqspi, qwr);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+ if (rxbuf) {
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset);
+ for (i = 0; i < 8; ++i)
+ *rxbuf++ = mcfqspi_rd_qdr(mcfqspi);
+ offset ^= 8;
+ }
+ } else {
+ mcfqspi_wr_qwr(mcfqspi, (n - 1) << 8);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+ }
+ wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi));
+ if (rxbuf) {
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset);
+ for (i = 0; i < n; ++i)
+ *rxbuf++ = mcfqspi_rd_qdr(mcfqspi);
+ }
+}
+
+static void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count,
+ const u16 *txbuf, u16 *rxbuf)
+{
+ unsigned i, n, offset = 0;
+
+ n = min(count, 16u);
+
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_CMDBUF);
+ for (i = 0; i < n; ++i)
+ mcfqspi_wr_qdr(mcfqspi, MCFQSPI_QCR_BITSE);
+
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_TXBUF);
+ if (txbuf)
+ for (i = 0; i < n; ++i)
+ mcfqspi_wr_qdr(mcfqspi, *txbuf++);
+ else
+ for (i = 0; i < count; ++i)
+ mcfqspi_wr_qdr(mcfqspi, 0);
+
+ count -= n;
+ if (count) {
+ u16 qwr = 0xf08;
+ mcfqspi_wr_qwr(mcfqspi, 0x700);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+
+ do {
+ wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi));
+ mcfqspi_wr_qwr(mcfqspi, qwr);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+ if (rxbuf) {
+ mcfqspi_wr_qar(mcfqspi,
+ MCFQSPI_QAR_RXBUF + offset);
+ for (i = 0; i < 8; ++i)
+ *rxbuf++ = mcfqspi_rd_qdr(mcfqspi);
+ }
+ n = min(count, 8u);
+ if (txbuf) {
+ mcfqspi_wr_qar(mcfqspi,
+ MCFQSPI_QAR_TXBUF + offset);
+ for (i = 0; i < n; ++i)
+ mcfqspi_wr_qdr(mcfqspi, *txbuf++);
+ }
+ qwr = (offset ? 0x808 : 0x000) + ((n - 1) << 8);
+ offset ^= 8;
+ count -= n;
+ } while (count);
+ wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi));
+ mcfqspi_wr_qwr(mcfqspi, qwr);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+ if (rxbuf) {
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset);
+ for (i = 0; i < 8; ++i)
+ *rxbuf++ = mcfqspi_rd_qdr(mcfqspi);
+ offset ^= 8;
+ }
+ } else {
+ mcfqspi_wr_qwr(mcfqspi, (n - 1) << 8);
+ mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE);
+ }
+ wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi));
+ if (rxbuf) {
+ mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset);
+ for (i = 0; i < n; ++i)
+ *rxbuf++ = mcfqspi_rd_qdr(mcfqspi);
+ }
+}
+
+static void mcfqspi_work(struct work_struct *work)
+{
+ struct mcfqspi *mcfqspi = container_of(work, struct mcfqspi, work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mcfqspi->lock, flags);
+ while (!list_empty(&mcfqspi->msgq)) {
+ struct spi_message *msg;
+ struct spi_device *spi;
+ struct spi_transfer *xfer;
+ int status = 0;
+
+ msg = container_of(mcfqspi->msgq.next, struct spi_message,
+ queue);
+
+ list_del_init(&mcfqspi->msgq);
+ spin_unlock_irqrestore(&mcfqspi->lock, flags);
+
+ spi = msg->spi;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ bool cs_high = spi->mode & SPI_CS_HIGH;
+ u16 qmr = MCFQSPI_QMR_MSTR;
+
+ if (xfer->bits_per_word)
+ qmr |= xfer->bits_per_word << 10;
+ else
+ qmr |= spi->bits_per_word << 10;
+ if (spi->mode & SPI_CPHA)
+ qmr |= MCFQSPI_QMR_CPHA;
+ if (spi->mode & SPI_CPOL)
+ qmr |= MCFQSPI_QMR_CPOL;
+ if (xfer->speed_hz)
+ qmr |= mcfqspi_qmr_baud(xfer->speed_hz);
+ else
+ qmr |= mcfqspi_qmr_baud(spi->max_speed_hz);
+ mcfqspi_wr_qmr(mcfqspi, qmr);
+
+ mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high);
+
+ mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE);
+ if ((xfer->bits_per_word ? xfer->bits_per_word :
+ spi->bits_per_word) == 8)
+ mcfqspi_transfer_msg8(mcfqspi, xfer->len,
+ xfer->tx_buf,
+ xfer->rx_buf);
+ else
+ mcfqspi_transfer_msg16(mcfqspi, xfer->len / 2,
+ xfer->tx_buf,
+ xfer->rx_buf);
+ mcfqspi_wr_qir(mcfqspi, 0);
+
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+ if (xfer->cs_change) {
+ if (!list_is_last(&xfer->transfer_list,
+ &msg->transfers))
+ mcfqspi_cs_deselect(mcfqspi,
+ spi->chip_select,
+ cs_high);
+ } else {
+ if (list_is_last(&xfer->transfer_list,
+ &msg->transfers))
+ mcfqspi_cs_deselect(mcfqspi,
+ spi->chip_select,
+ cs_high);
+ }
+ msg->actual_length += xfer->len;
+ }
+ msg->status = status;
+ msg->complete(msg->context);
+
+ spin_lock_irqsave(&mcfqspi->lock, flags);
+ }
+ spin_unlock_irqrestore(&mcfqspi->lock, flags);
+}
+
+static int mcfqspi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct mcfqspi *mcfqspi;
+ struct spi_transfer *xfer;
+ unsigned long flags;
+
+ mcfqspi = spi_master_get_devdata(spi->master);
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (xfer->bits_per_word && ((xfer->bits_per_word < 8)
+ || (xfer->bits_per_word > 16))) {
+ dev_dbg(&spi->dev,
+ "%d bits per word is not supported\n",
+ xfer->bits_per_word);
+ goto fail;
+ }
+ if (xfer->speed_hz) {
+ u32 real_speed = MCFQSPI_BUSCLK /
+ mcfqspi_qmr_baud(xfer->speed_hz);
+ if (real_speed != xfer->speed_hz)
+ dev_dbg(&spi->dev,
+ "using speed %d instead of %d\n",
+ real_speed, xfer->speed_hz);
+ }
+ }
+ msg->status = -EINPROGRESS;
+ msg->actual_length = 0;
+
+ spin_lock_irqsave(&mcfqspi->lock, flags);
+ list_add_tail(&msg->queue, &mcfqspi->msgq);
+ queue_work(mcfqspi->workq, &mcfqspi->work);
+ spin_unlock_irqrestore(&mcfqspi->lock, flags);
+
+ return 0;
+fail:
+ msg->status = -EINVAL;
+ return -EINVAL;
+}
+
+static int mcfqspi_setup(struct spi_device *spi)
+{
+ if ((spi->bits_per_word < 8) || (spi->bits_per_word > 16)) {
+ dev_dbg(&spi->dev, "%d bits per word is not supported\n",
+ spi->bits_per_word);
+ return -EINVAL;
+ }
+ if (spi->chip_select >= spi->master->num_chipselect) {
+ dev_dbg(&spi->dev, "%d chip select is out of range\n",
+ spi->chip_select);
+ return -EINVAL;
+ }
+
+ mcfqspi_cs_deselect(spi_master_get_devdata(spi->master),
+ spi->chip_select, spi->mode & SPI_CS_HIGH);
+
+ dev_dbg(&spi->dev,
+ "bits per word %d, chip select %d, speed %d KHz\n",
+ spi->bits_per_word, spi->chip_select,
+ (MCFQSPI_BUSCLK / mcfqspi_qmr_baud(spi->max_speed_hz))
+ / 1000);
+
+ return 0;
+}
+
+static int __devinit mcfqspi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct mcfqspi *mcfqspi;
+ struct resource *res;
+ struct mcfqspi_platform_data *pdata;
+ int status;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi));
+ if (master == NULL) {
+ dev_dbg(&pdev->dev, "spi_alloc_master failed\n");
+ return -ENOMEM;
+ }
+
+ mcfqspi = spi_master_get_devdata(master);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_dbg(&pdev->dev, "platform_get_resource failed\n");
+ status = -ENXIO;
+ goto fail0;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ status = -EBUSY;
+ goto fail0;
+ }
+
+ mcfqspi->iobase = ioremap(res->start, resource_size(res));
+ if (!mcfqspi->iobase) {
+ dev_dbg(&pdev->dev, "ioremap failed\n");
+ status = -ENOMEM;
+ goto fail1;
+ }
+
+ mcfqspi->irq = platform_get_irq(pdev, 0);
+ if (mcfqspi->irq < 0) {
+ dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+ status = -ENXIO;
+ goto fail2;
+ }
+
+ status = request_irq(mcfqspi->irq, mcfqspi_irq_handler, IRQF_DISABLED,
+ pdev->name, mcfqspi);
+ if (status) {
+ dev_dbg(&pdev->dev, "request_irq failed\n");
+ goto fail2;
+ }
+
+ mcfqspi->clk = clk_get(&pdev->dev, "qspi_clk");
+ if (IS_ERR(mcfqspi->clk)) {
+ dev_dbg(&pdev->dev, "clk_get failed\n");
+ status = PTR_ERR(mcfqspi->clk);
+ goto fail3;
+ }
+ clk_enable(mcfqspi->clk);
+
+ mcfqspi->workq = create_singlethread_workqueue(dev_name(master->dev.parent));
+ if (!mcfqspi->workq) {
+ dev_dbg(&pdev->dev, "create_workqueue failed\n");
+ status = -ENOMEM;
+ goto fail4;
+ }
+ INIT_WORK(&mcfqspi->work, mcfqspi_work);
+ spin_lock_init(&mcfqspi->lock);
+ INIT_LIST_HEAD(&mcfqspi->msgq);
+ init_waitqueue_head(&mcfqspi->waitq);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "platform data is missing\n");
+ goto fail5;
+ }
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->num_chipselect;
+
+ mcfqspi->cs_control = pdata->cs_control;
+ status = mcfqspi_cs_setup(mcfqspi);
+ if (status) {
+ dev_dbg(&pdev->dev, "error initializing cs_control\n");
+ goto fail5;
+ }
+
+ master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA;
+ master->setup = mcfqspi_setup;
+ master->transfer = mcfqspi_transfer;
+
+ platform_set_drvdata(pdev, master);
+
+ status = spi_register_master(master);
+ if (status) {
+ dev_dbg(&pdev->dev, "spi_register_master failed\n");
+ goto fail6;
+ }
+ dev_info(&pdev->dev, "Coldfire QSPI bus driver\n");
+
+ return 0;
+
+fail6:
+ mcfqspi_cs_teardown(mcfqspi);
+fail5:
+ destroy_workqueue(mcfqspi->workq);
+fail4:
+ clk_disable(mcfqspi->clk);
+ clk_put(mcfqspi->clk);
+fail3:
+ free_irq(mcfqspi->irq, mcfqspi);
+fail2:
+ iounmap(mcfqspi->iobase);
+fail1:
+ release_mem_region(res->start, resource_size(res));
+fail0:
+ spi_master_put(master);
+
+ dev_dbg(&pdev->dev, "Coldfire QSPI probe failed\n");
+
+ return status;
+}
+
+static int __devexit mcfqspi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* disable the hardware (set the baud rate to 0) */
+ mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR);
+
+ platform_set_drvdata(pdev, NULL);
+ mcfqspi_cs_teardown(mcfqspi);
+ destroy_workqueue(mcfqspi->workq);
+ clk_disable(mcfqspi->clk);
+ clk_put(mcfqspi->clk);
+ free_irq(mcfqspi->irq, mcfqspi);
+ iounmap(mcfqspi->iobase);
+ release_mem_region(res->start, resource_size(res));
+ spi_unregister_master(master);
+ spi_master_put(master);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int mcfqspi_suspend(struct device *dev)
+{
+ struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+
+ clk_disable(mcfqspi->clk);
+
+ return 0;
+}
+
+static int mcfqspi_resume(struct device *dev)
+{
+ struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+
+ clk_enable(mcfqspi->clk);
+
+ return 0;
+}
+
+static struct dev_pm_ops mcfqspi_dev_pm_ops = {
+ .suspend = mcfqspi_suspend,
+ .resume = mcfqspi_resume,
+};
+
+#define MCFQSPI_DEV_PM_OPS (&mcfqspi_dev_pm_ops)
+#else
+#define MCFQSPI_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver mcfqspi_driver = {
+ .driver.name = DRIVER_NAME,
+ .driver.owner = THIS_MODULE,
+ .driver.pm = MCFQSPI_DEV_PM_OPS,
+ .remove = __devexit_p(mcfqspi_remove),
+};
+
+static int __init mcfqspi_init(void)
+{
+ return platform_driver_probe(&mcfqspi_driver, mcfqspi_probe);
+}
+module_init(mcfqspi_init);
+
+static void __exit mcfqspi_exit(void)
+{
+ platform_driver_unregister(&mcfqspi_driver);
+}
+module_exit(mcfqspi_exit);
+
+MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
+MODULE_DESCRIPTION("Coldfire QSPI Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c
new file mode 100644
index 00000000000..225ab60b02c
--- /dev/null
+++ b/drivers/spi/davinci_spi.c
@@ -0,0 +1,1255 @@
+/*
+ * Copyright (C) 2009 Texas Instruments.
+ *
+ * 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/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <mach/spi.h>
+#include <mach/edma.h>
+
+#define SPI_NO_RESOURCE ((resource_size_t)-1)
+
+#define SPI_MAX_CHIPSELECT 2
+
+#define CS_DEFAULT 0xFF
+
+#define SPI_BUFSIZ (SMP_CACHE_BYTES + 1)
+#define DAVINCI_DMA_DATA_TYPE_S8 0x01
+#define DAVINCI_DMA_DATA_TYPE_S16 0x02
+#define DAVINCI_DMA_DATA_TYPE_S32 0x04
+
+#define SPIFMT_PHASE_MASK BIT(16)
+#define SPIFMT_POLARITY_MASK BIT(17)
+#define SPIFMT_DISTIMER_MASK BIT(18)
+#define SPIFMT_SHIFTDIR_MASK BIT(20)
+#define SPIFMT_WAITENA_MASK BIT(21)
+#define SPIFMT_PARITYENA_MASK BIT(22)
+#define SPIFMT_ODD_PARITY_MASK BIT(23)
+#define SPIFMT_WDELAY_MASK 0x3f000000u
+#define SPIFMT_WDELAY_SHIFT 24
+#define SPIFMT_CHARLEN_MASK 0x0000001Fu
+
+/* SPIGCR1 */
+#define SPIGCR1_SPIENA_MASK 0x01000000u
+
+/* SPIPC0 */
+#define SPIPC0_DIFUN_MASK BIT(11) /* MISO */
+#define SPIPC0_DOFUN_MASK BIT(10) /* MOSI */
+#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */
+#define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */
+#define SPIPC0_EN1FUN_MASK BIT(1)
+#define SPIPC0_EN0FUN_MASK BIT(0)
+
+#define SPIINT_MASKALL 0x0101035F
+#define SPI_INTLVL_1 0x000001FFu
+#define SPI_INTLVL_0 0x00000000u
+
+/* SPIDAT1 */
+#define SPIDAT1_CSHOLD_SHIFT 28
+#define SPIDAT1_CSNR_SHIFT 16
+#define SPIGCR1_CLKMOD_MASK BIT(1)
+#define SPIGCR1_MASTER_MASK BIT(0)
+#define SPIGCR1_LOOPBACK_MASK BIT(16)
+
+/* SPIBUF */
+#define SPIBUF_TXFULL_MASK BIT(29)
+#define SPIBUF_RXEMPTY_MASK BIT(31)
+
+/* Error Masks */
+#define SPIFLG_DLEN_ERR_MASK BIT(0)
+#define SPIFLG_TIMEOUT_MASK BIT(1)
+#define SPIFLG_PARERR_MASK BIT(2)
+#define SPIFLG_DESYNC_MASK BIT(3)
+#define SPIFLG_BITERR_MASK BIT(4)
+#define SPIFLG_OVRRUN_MASK BIT(6)
+#define SPIFLG_RX_INTR_MASK BIT(8)
+#define SPIFLG_TX_INTR_MASK BIT(9)
+#define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24)
+#define SPIFLG_MASK (SPIFLG_DLEN_ERR_MASK \
+ | SPIFLG_TIMEOUT_MASK | SPIFLG_PARERR_MASK \
+ | SPIFLG_DESYNC_MASK | SPIFLG_BITERR_MASK \
+ | SPIFLG_OVRRUN_MASK | SPIFLG_RX_INTR_MASK \
+ | SPIFLG_TX_INTR_MASK \
+ | SPIFLG_BUF_INIT_ACTIVE_MASK)
+
+#define SPIINT_DLEN_ERR_INTR BIT(0)
+#define SPIINT_TIMEOUT_INTR BIT(1)
+#define SPIINT_PARERR_INTR BIT(2)
+#define SPIINT_DESYNC_INTR BIT(3)
+#define SPIINT_BITERR_INTR BIT(4)
+#define SPIINT_OVRRUN_INTR BIT(6)
+#define SPIINT_RX_INTR BIT(8)
+#define SPIINT_TX_INTR BIT(9)
+#define SPIINT_DMA_REQ_EN BIT(16)
+#define SPIINT_ENABLE_HIGHZ BIT(24)
+
+#define SPI_T2CDELAY_SHIFT 16
+#define SPI_C2TDELAY_SHIFT 24
+
+/* SPI Controller registers */
+#define SPIGCR0 0x00
+#define SPIGCR1 0x04
+#define SPIINT 0x08
+#define SPILVL 0x0c
+#define SPIFLG 0x10
+#define SPIPC0 0x14
+#define SPIPC1 0x18
+#define SPIPC2 0x1c
+#define SPIPC3 0x20
+#define SPIPC4 0x24
+#define SPIPC5 0x28
+#define SPIPC6 0x2c
+#define SPIPC7 0x30
+#define SPIPC8 0x34
+#define SPIDAT0 0x38
+#define SPIDAT1 0x3c
+#define SPIBUF 0x40
+#define SPIEMU 0x44
+#define SPIDELAY 0x48
+#define SPIDEF 0x4c
+#define SPIFMT0 0x50
+#define SPIFMT1 0x54
+#define SPIFMT2 0x58
+#define SPIFMT3 0x5c
+#define TGINTVEC0 0x60
+#define TGINTVEC1 0x64
+
+struct davinci_spi_slave {
+ u32 cmd_to_write;
+ u32 clk_ctrl_to_write;
+ u32 bytes_per_word;
+ u8 active_cs;
+};
+
+/* We have 2 DMA channels per CS, one for RX and one for TX */
+struct davinci_spi_dma {
+ int dma_tx_channel;
+ int dma_rx_channel;
+ int dma_tx_sync_dev;
+ int dma_rx_sync_dev;
+ enum dma_event_q eventq;
+
+ struct completion dma_tx_completion;
+ struct completion dma_rx_completion;
+};
+
+/* SPI Controller driver's private data. */
+struct davinci_spi {
+ struct spi_bitbang bitbang;
+ struct clk *clk;
+
+ u8 version;
+ resource_size_t pbase;
+ void __iomem *base;
+ size_t region_size;
+ u32 irq;
+ struct completion done;
+
+ const void *tx;
+ void *rx;
+ u8 *tmp_buf;
+ int count;
+ struct davinci_spi_dma *dma_channels;
+ struct davinci_spi_platform_data *pdata;
+
+ void (*get_rx)(u32 rx_data, struct davinci_spi *);
+ u32 (*get_tx)(struct davinci_spi *);
+
+ struct davinci_spi_slave slave[SPI_MAX_CHIPSELECT];
+};
+
+static unsigned use_dma;
+
+static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi)
+{
+ u8 *rx = davinci_spi->rx;
+
+ *rx++ = (u8)data;
+ davinci_spi->rx = rx;
+}
+
+static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *davinci_spi)
+{
+ u16 *rx = davinci_spi->rx;
+
+ *rx++ = (u16)data;
+ davinci_spi->rx = rx;
+}
+
+static u32 davinci_spi_tx_buf_u8(struct davinci_spi *davinci_spi)
+{
+ u32 data;
+ const u8 *tx = davinci_spi->tx;
+
+ data = *tx++;
+ davinci_spi->tx = tx;
+ return data;
+}
+
+static u32 davinci_spi_tx_buf_u16(struct davinci_spi *davinci_spi)
+{
+ u32 data;
+ const u16 *tx = davinci_spi->tx;
+
+ data = *tx++;
+ davinci_spi->tx = tx;
+ return data;
+}
+
+static inline void set_io_bits(void __iomem *addr, u32 bits)
+{
+ u32 v = ioread32(addr);
+
+ v |= bits;
+ iowrite32(v, addr);
+}
+
+static inline void clear_io_bits(void __iomem *addr, u32 bits)
+{
+ u32 v = ioread32(addr);
+
+ v &= ~bits;
+ iowrite32(v, addr);
+}
+
+static inline void set_fmt_bits(void __iomem *addr, u32 bits, int cs_num)
+{
+ set_io_bits(addr + SPIFMT0 + (0x4 * cs_num), bits);
+}
+
+static inline void clear_fmt_bits(void __iomem *addr, u32 bits, int cs_num)
+{
+ clear_io_bits(addr + SPIFMT0 + (0x4 * cs_num), bits);
+}
+
+static void davinci_spi_set_dma_req(const struct spi_device *spi, int enable)
+{
+ struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master);
+
+ if (enable)
+ set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN);
+ else
+ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN);
+}
+
+/*
+ * Interface to control the chip select signal
+ */
+static void davinci_spi_chipselect(struct spi_device *spi, int value)
+{
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_platform_data *pdata;
+ u32 data1_reg_val = 0;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ pdata = davinci_spi->pdata;
+
+ /*
+ * Board specific chip select logic decides the polarity and cs
+ * line for the controller
+ */
+ if (value == BITBANG_CS_INACTIVE) {
+ set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT);
+
+ data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT;
+ iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);
+
+ while ((ioread32(davinci_spi->base + SPIBUF)
+ & SPIBUF_RXEMPTY_MASK) == 0)
+ cpu_relax();
+ }
+}
+
+/**
+ * davinci_spi_setup_transfer - This functions will determine transfer method
+ * @spi: spi device on which data transfer to be done
+ * @t: spi transfer in which transfer info is filled
+ *
+ * This function determines data transfer method (8/16/32 bit transfer).
+ * It will also set the SPI Clock Control register according to
+ * SPI slave device freq.
+ */
+static int davinci_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_platform_data *pdata;
+ u8 bits_per_word = 0;
+ u32 hz = 0, prescale;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ pdata = davinci_spi->pdata;
+
+ if (t) {
+ bits_per_word = t->bits_per_word;
+ hz = t->speed_hz;
+ }
+
+ /* if bits_per_word is not set then set it default */
+ if (!bits_per_word)
+ bits_per_word = spi->bits_per_word;
+
+ /*
+ * Assign function pointer to appropriate transfer method
+ * 8bit, 16bit or 32bit transfer
+ */
+ if (bits_per_word <= 8 && bits_per_word >= 2) {
+ davinci_spi->get_rx = davinci_spi_rx_buf_u8;
+ davinci_spi->get_tx = davinci_spi_tx_buf_u8;
+ davinci_spi->slave[spi->chip_select].bytes_per_word = 1;
+ } else if (bits_per_word <= 16 && bits_per_word >= 2) {
+ davinci_spi->get_rx = davinci_spi_rx_buf_u16;
+ davinci_spi->get_tx = davinci_spi_tx_buf_u16;
+ davinci_spi->slave[spi->chip_select].bytes_per_word = 2;
+ } else
+ return -EINVAL;
+
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ clear_fmt_bits(davinci_spi->base, SPIFMT_CHARLEN_MASK,
+ spi->chip_select);
+ set_fmt_bits(davinci_spi->base, bits_per_word & 0x1f,
+ spi->chip_select);
+
+ prescale = ((clk_get_rate(davinci_spi->clk) / hz) - 1) & 0xff;
+
+ clear_fmt_bits(davinci_spi->base, 0x0000ff00, spi->chip_select);
+ set_fmt_bits(davinci_spi->base, prescale << 8, spi->chip_select);
+
+ return 0;
+}
+
+static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data)
+{
+ struct spi_device *spi = (struct spi_device *)data;
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_dma *davinci_spi_dma;
+ struct davinci_spi_platform_data *pdata;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
+ pdata = davinci_spi->pdata;
+
+ if (ch_status == DMA_COMPLETE)
+ edma_stop(davinci_spi_dma->dma_rx_channel);
+ else
+ edma_clean_channel(davinci_spi_dma->dma_rx_channel);
+
+ complete(&davinci_spi_dma->dma_rx_completion);
+ /* We must disable the DMA RX request */
+ davinci_spi_set_dma_req(spi, 0);
+}
+
+static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data)
+{
+ struct spi_device *spi = (struct spi_device *)data;
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_dma *davinci_spi_dma;
+ struct davinci_spi_platform_data *pdata;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
+ pdata = davinci_spi->pdata;
+
+ if (ch_status == DMA_COMPLETE)
+ edma_stop(davinci_spi_dma->dma_tx_channel);
+ else
+ edma_clean_channel(davinci_spi_dma->dma_tx_channel);
+
+ complete(&davinci_spi_dma->dma_tx_completion);
+ /* We must disable the DMA TX request */
+ davinci_spi_set_dma_req(spi, 0);
+}
+
+static int davinci_spi_request_dma(struct spi_device *spi)
+{
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_dma *davinci_spi_dma;
+ struct davinci_spi_platform_data *pdata;
+ struct device *sdev;
+ int r;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
+ pdata = davinci_spi->pdata;
+ sdev = davinci_spi->bitbang.master->dev.parent;
+
+ r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev,
+ davinci_spi_dma_rx_callback, spi,
+ davinci_spi_dma->eventq);
+ if (r < 0) {
+ dev_dbg(sdev, "Unable to request DMA channel for SPI RX\n");
+ return -EAGAIN;
+ }
+ davinci_spi_dma->dma_rx_channel = r;
+ r = edma_alloc_channel(davinci_spi_dma->dma_tx_sync_dev,
+ davinci_spi_dma_tx_callback, spi,
+ davinci_spi_dma->eventq);
+ if (r < 0) {
+ edma_free_channel(davinci_spi_dma->dma_rx_channel);
+ davinci_spi_dma->dma_rx_channel = -1;
+ dev_dbg(sdev, "Unable to request DMA channel for SPI TX\n");
+ return -EAGAIN;
+ }
+ davinci_spi_dma->dma_tx_channel = r;
+
+ return 0;
+}
+
+/**
+ * davinci_spi_setup - This functions will set default transfer method
+ * @spi: spi device on which data transfer to be done
+ *
+ * This functions sets the default transfer method.
+ */
+
+static int davinci_spi_setup(struct spi_device *spi)
+{
+ int retval;
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_dma *davinci_spi_dma;
+ struct device *sdev;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ sdev = davinci_spi->bitbang.master->dev.parent;
+
+ /* if bits per word length is zero then set it default 8 */
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ davinci_spi->slave[spi->chip_select].cmd_to_write = 0;
+
+ if (use_dma && davinci_spi->dma_channels) {
+ davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
+
+ if ((davinci_spi_dma->dma_rx_channel == -1)
+ || (davinci_spi_dma->dma_tx_channel == -1)) {
+ retval = davinci_spi_request_dma(spi);
+ if (retval < 0)
+ return retval;
+ }
+ }
+
+ /*
+ * SPI in DaVinci and DA8xx operate between
+ * 600 KHz and 50 MHz
+ */
+ if (spi->max_speed_hz < 600000 || spi->max_speed_hz > 50000000) {
+ dev_dbg(sdev, "Operating frequency is not in acceptable "
+ "range\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Set up SPIFMTn register, unique to this chipselect.
+ *
+ * NOTE: we could do all of these with one write. Also, some
+ * of the "version 2" features are found in chips that don't
+ * support all of them...
+ */
+ if (spi->mode & SPI_LSB_FIRST)
+ set_fmt_bits(davinci_spi->base, SPIFMT_SHIFTDIR_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base, SPIFMT_SHIFTDIR_MASK,
+ spi->chip_select);
+
+ if (spi->mode & SPI_CPOL)
+ set_fmt_bits(davinci_spi->base, SPIFMT_POLARITY_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base, SPIFMT_POLARITY_MASK,
+ spi->chip_select);
+
+ if (!(spi->mode & SPI_CPHA))
+ set_fmt_bits(davinci_spi->base, SPIFMT_PHASE_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base, SPIFMT_PHASE_MASK,
+ spi->chip_select);
+
+ /*
+ * Version 1 hardware supports two basic SPI modes:
+ * - Standard SPI mode uses 4 pins, with chipselect
+ * - 3 pin SPI is a 4 pin variant without CS (SPI_NO_CS)
+ * (distinct from SPI_3WIRE, with just one data wire;
+ * or similar variants without MOSI or without MISO)
+ *
+ * Version 2 hardware supports an optional handshaking signal,
+ * so it can support two more modes:
+ * - 5 pin SPI variant is standard SPI plus SPI_READY
+ * - 4 pin with enable is (SPI_READY | SPI_NO_CS)
+ */
+
+ if (davinci_spi->version == SPI_VERSION_2) {
+ clear_fmt_bits(davinci_spi->base, SPIFMT_WDELAY_MASK,
+ spi->chip_select);
+ set_fmt_bits(davinci_spi->base,
+ (davinci_spi->pdata->wdelay
+ << SPIFMT_WDELAY_SHIFT)
+ & SPIFMT_WDELAY_MASK,
+ spi->chip_select);
+
+ if (davinci_spi->pdata->odd_parity)
+ set_fmt_bits(davinci_spi->base,
+ SPIFMT_ODD_PARITY_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base,
+ SPIFMT_ODD_PARITY_MASK,
+ spi->chip_select);
+
+ if (davinci_spi->pdata->parity_enable)
+ set_fmt_bits(davinci_spi->base,
+ SPIFMT_PARITYENA_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base,
+ SPIFMT_PARITYENA_MASK,
+ spi->chip_select);
+
+ if (davinci_spi->pdata->wait_enable)
+ set_fmt_bits(davinci_spi->base,
+ SPIFMT_WAITENA_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base,
+ SPIFMT_WAITENA_MASK,
+ spi->chip_select);
+
+ if (davinci_spi->pdata->timer_disable)
+ set_fmt_bits(davinci_spi->base,
+ SPIFMT_DISTIMER_MASK,
+ spi->chip_select);
+ else
+ clear_fmt_bits(davinci_spi->base,
+ SPIFMT_DISTIMER_MASK,
+ spi->chip_select);
+ }
+
+ retval = davinci_spi_setup_transfer(spi, NULL);
+
+ return retval;
+}
+
+static void davinci_spi_cleanup(struct spi_device *spi)
+{
+ struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master);
+ struct davinci_spi_dma *davinci_spi_dma;
+
+ davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
+
+ if (use_dma && davinci_spi->dma_channels) {
+ davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
+
+ if ((davinci_spi_dma->dma_rx_channel != -1)
+ && (davinci_spi_dma->dma_tx_channel != -1)) {
+ edma_free_channel(davinci_spi_dma->dma_tx_channel);
+ edma_free_channel(davinci_spi_dma->dma_rx_channel);
+ }
+ }
+}
+
+static int davinci_spi_bufs_prep(struct spi_device *spi,
+ struct davinci_spi *davinci_spi)
+{
+ int op_mode = 0;
+
+ /*
+ * REVISIT unless devices disagree about SPI_LOOP or
+ * SPI_READY (SPI_NO_CS only allows one device!), this
+ * should not need to be done before each message...
+ * optimize for both flags staying cleared.
+ */
+
+ op_mode = SPIPC0_DIFUN_MASK
+ | SPIPC0_DOFUN_MASK
+ | SPIPC0_CLKFUN_MASK;
+ if (!(spi->mode & SPI_NO_CS))
+ op_mode |= 1 << spi->chip_select;
+ if (spi->mode & SPI_READY)
+ op_mode |= SPIPC0_SPIENA_MASK;
+
+ iowrite32(op_mode, davinci_spi->base + SPIPC0);
+
+ if (spi->mode & SPI_LOOP)
+ set_io_bits(davinci_spi->base + SPIGCR1,
+ SPIGCR1_LOOPBACK_MASK);
+ else
+ clear_io_bits(davinci_spi->base + SPIGCR1,
+ SPIGCR1_LOOPBACK_MASK);
+
+ return 0;
+}
+
+static int davinci_spi_check_error(struct davinci_spi *davinci_spi,
+ int int_status)
+{
+ struct device *sdev = davinci_spi->bitbang.master->dev.parent;
+
+ if (int_status & SPIFLG_TIMEOUT_MASK) {
+ dev_dbg(sdev, "SPI Time-out Error\n");
+ return -ETIMEDOUT;
+ }
+ if (int_status & SPIFLG_DESYNC_MASK) {
+ dev_dbg(sdev, "SPI Desynchronization Error\n");
+ return -EIO;
+ }
+ if (int_status & SPIFLG_BITERR_MASK) {
+ dev_dbg(sdev, "SPI Bit error\n");
+ return -EIO;
+ }
+
+ if (davinci_spi->version == SPI_VERSION_2) {
+ if (int_status & SPIFLG_DLEN_ERR_MASK) {
+ dev_dbg(sdev, "SPI Data Length Error\n");
+ return -EIO;
+ }
+ if (int_status & SPIFLG_PARERR_MASK) {
+ dev_dbg(sdev, "SPI Parity Error\n");
+ return -EIO;
+ }
+ if (int_status & SPIFLG_OVRRUN_MASK) {
+ dev_dbg(sdev, "SPI Data Overrun error\n");
+ return -EIO;
+ }
+ if (int_status & SPIFLG_TX_INTR_MASK) {
+ dev_dbg(sdev, "SPI TX intr bit set\n");
+ return -EIO;
+ }
+ if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) {
+ dev_dbg(sdev, "SPI Buffer Init Active\n");
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * davinci_spi_bufs - functions which will handle transfer data
+ * @spi: spi device on which data transfer to be done
+ * @t: spi transfer in which transfer info is filled
+ *
+ * This function will put data to be transferred into data register
+ * of SPI controller and then wait until the completion will be marked
+ * by the IRQ Handler.
+ */
+static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct davinci_spi *davinci_spi;
+ int int_status, count, ret;
+ u8 conv, tmp;
+ u32 tx_data, data1_reg_val;
+ u32 buf_val, flg_val;
+ struct davinci_spi_platform_data *pdata;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ pdata = davinci_spi->pdata;
+
+ davinci_spi->tx = t->tx_buf;
+ davinci_spi->rx = t->rx_buf;
+
+ /* convert len to words based on bits_per_word */
+ conv = davinci_spi->slave[spi->chip_select].bytes_per_word;
+ davinci_spi->count = t->len / conv;
+
+ INIT_COMPLETION(davinci_spi->done);
+
+ ret = davinci_spi_bufs_prep(spi, davinci_spi);
+ if (ret)
+ return ret;
+
+ /* Enable SPI */
+ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK);
+
+ iowrite32(0 | (pdata->c2tdelay << SPI_C2TDELAY_SHIFT) |
+ (pdata->t2cdelay << SPI_T2CDELAY_SHIFT),
+ davinci_spi->base + SPIDELAY);
+
+ count = davinci_spi->count;
+ data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT;
+ tmp = ~(0x1 << spi->chip_select);
+
+ clear_io_bits(davinci_spi->base + SPIDEF, ~tmp);
+
+ data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT;
+
+ while ((ioread32(davinci_spi->base + SPIBUF)
+ & SPIBUF_RXEMPTY_MASK) == 0)
+ cpu_relax();
+
+ /* Determine the command to execute READ or WRITE */
+ if (t->tx_buf) {
+ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL);
+
+ while (1) {
+ tx_data = davinci_spi->get_tx(davinci_spi);
+
+ data1_reg_val &= ~(0xFFFF);
+ data1_reg_val |= (0xFFFF & tx_data);
+
+ buf_val = ioread32(davinci_spi->base + SPIBUF);
+ if ((buf_val & SPIBUF_TXFULL_MASK) == 0) {
+ iowrite32(data1_reg_val,
+ davinci_spi->base + SPIDAT1);
+
+ count--;
+ }
+ while (ioread32(davinci_spi->base + SPIBUF)
+ & SPIBUF_RXEMPTY_MASK)
+ cpu_relax();
+
+ /* getting the returned byte */
+ if (t->rx_buf) {
+ buf_val = ioread32(davinci_spi->base + SPIBUF);
+ davinci_spi->get_rx(buf_val, davinci_spi);
+ }
+ if (count <= 0)
+ break;
+ }
+ } else {
+ if (pdata->poll_mode) {
+ while (1) {
+ /* keeps the serial clock going */
+ if ((ioread32(davinci_spi->base + SPIBUF)
+ & SPIBUF_TXFULL_MASK) == 0)
+ iowrite32(data1_reg_val,
+ davinci_spi->base + SPIDAT1);
+
+ while (ioread32(davinci_spi->base + SPIBUF) &
+ SPIBUF_RXEMPTY_MASK)
+ cpu_relax();
+
+ flg_val = ioread32(davinci_spi->base + SPIFLG);
+ buf_val = ioread32(davinci_spi->base + SPIBUF);
+
+ davinci_spi->get_rx(buf_val, davinci_spi);
+
+ count--;
+ if (count <= 0)
+ break;
+ }
+ } else { /* Receive in Interrupt mode */
+ int i;
+
+ for (i = 0; i < davinci_spi->count; i++) {
+ set_io_bits(davinci_spi->base + SPIINT,
+ SPIINT_BITERR_INTR
+ | SPIINT_OVRRUN_INTR
+ | SPIINT_RX_INTR);
+
+ iowrite32(data1_reg_val,
+ davinci_spi->base + SPIDAT1);
+
+ while (ioread32(davinci_spi->base + SPIINT) &
+ SPIINT_RX_INTR)
+ cpu_relax();
+ }
+ iowrite32((data1_reg_val & 0x0ffcffff),
+ davinci_spi->base + SPIDAT1);
+ }
+ }
+
+ /*
+ * Check for bit error, desync error,parity error,timeout error and
+ * receive overflow errors
+ */
+ int_status = ioread32(davinci_spi->base + SPIFLG);
+
+ ret = davinci_spi_check_error(davinci_spi, int_status);
+ if (ret != 0)
+ return ret;
+
+ /* SPI Framework maintains the count only in bytes so convert back */
+ davinci_spi->count *= conv;
+
+ return t->len;
+}
+
+#define DAVINCI_DMA_DATA_TYPE_S8 0x01
+#define DAVINCI_DMA_DATA_TYPE_S16 0x02
+#define DAVINCI_DMA_DATA_TYPE_S32 0x04
+
+static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct davinci_spi *davinci_spi;
+ int int_status = 0;
+ int count, temp_count;
+ u8 conv = 1;
+ u8 tmp;
+ u32 data1_reg_val;
+ struct davinci_spi_dma *davinci_spi_dma;
+ int word_len, data_type, ret;
+ unsigned long tx_reg, rx_reg;
+ struct davinci_spi_platform_data *pdata;
+ struct device *sdev;
+
+ davinci_spi = spi_master_get_devdata(spi->master);
+ pdata = davinci_spi->pdata;
+ sdev = davinci_spi->bitbang.master->dev.parent;
+
+ davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
+
+ tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1;
+ rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF;
+
+ davinci_spi->tx = t->tx_buf;
+ davinci_spi->rx = t->rx_buf;
+
+ /* convert len to words based on bits_per_word */
+ conv = davinci_spi->slave[spi->chip_select].bytes_per_word;
+ davinci_spi->count = t->len / conv;
+
+ INIT_COMPLETION(davinci_spi->done);
+
+ init_completion(&davinci_spi_dma->dma_rx_completion);
+ init_completion(&davinci_spi_dma->dma_tx_completion);
+
+ word_len = conv * 8;
+
+ if (word_len <= 8)
+ data_type = DAVINCI_DMA_DATA_TYPE_S8;
+ else if (word_len <= 16)
+ data_type = DAVINCI_DMA_DATA_TYPE_S16;
+ else if (word_len <= 32)
+ data_type = DAVINCI_DMA_DATA_TYPE_S32;
+ else
+ return -EINVAL;
+
+ ret = davinci_spi_bufs_prep(spi, davinci_spi);
+ if (ret)
+ return ret;
+
+ /* Put delay val if required */
+ iowrite32(0 | (pdata->c2tdelay << SPI_C2TDELAY_SHIFT) |
+ (pdata->t2cdelay << SPI_T2CDELAY_SHIFT),
+ davinci_spi->base + SPIDELAY);
+
+ count = davinci_spi->count; /* the number of elements */
+ data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT;
+
+ /* CS default = 0xFF */
+ tmp = ~(0x1 << spi->chip_select);
+
+ clear_io_bits(davinci_spi->base + SPIDEF, ~tmp);
+
+ data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT;
+
+ /* disable all interrupts for dma transfers */
+ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL);
+ /* Disable SPI to write configuration bits in SPIDAT */
+ clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK);
+ iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);
+ /* Enable SPI */
+ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK);
+
+ while ((ioread32(davinci_spi->base + SPIBUF)
+ & SPIBUF_RXEMPTY_MASK) == 0)
+ cpu_relax();
+
+
+ if (t->tx_buf) {
+ t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&spi->dev, t->tx_dma)) {
+ dev_dbg(sdev, "Unable to DMA map a %d bytes"
+ " TX buffer\n", count);
+ return -ENOMEM;
+ }
+ temp_count = count;
+ } else {
+ /* We need TX clocking for RX transaction */
+ t->tx_dma = dma_map_single(&spi->dev,
+ (void *)davinci_spi->tmp_buf, count + 1,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&spi->dev, t->tx_dma)) {
+ dev_dbg(sdev, "Unable to DMA map a %d bytes"
+ " TX tmp buffer\n", count);
+ return -ENOMEM;
+ }
+ temp_count = count + 1;
+ }
+
+ edma_set_transfer_params(davinci_spi_dma->dma_tx_channel,
+ data_type, temp_count, 1, 0, ASYNC);
+ edma_set_dest(davinci_spi_dma->dma_tx_channel, tx_reg, INCR, W8BIT);
+ edma_set_src(davinci_spi_dma->dma_tx_channel, t->tx_dma, INCR, W8BIT);
+ edma_set_src_index(davinci_spi_dma->dma_tx_channel, data_type, 0);
+ edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0);
+
+ if (t->rx_buf) {
+ /* initiate transaction */
+ iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);
+
+ t->rx_dma = dma_map_single(&spi->dev, (void *)t->rx_buf, count,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&spi->dev, t->rx_dma)) {
+ dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n",
+ count);
+ if (t->tx_buf != NULL)
+ dma_unmap_single(NULL, t->tx_dma,
+ count, DMA_TO_DEVICE);
+ return -ENOMEM;
+ }
+ edma_set_transfer_params(davinci_spi_dma->dma_rx_channel,
+ data_type, count, 1, 0, ASYNC);
+ edma_set_src(davinci_spi_dma->dma_rx_channel,
+ rx_reg, INCR, W8BIT);
+ edma_set_dest(davinci_spi_dma->dma_rx_channel,
+ t->rx_dma, INCR, W8BIT);
+ edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0);
+ edma_set_dest_index(davinci_spi_dma->dma_rx_channel,
+ data_type, 0);
+ }
+
+ if ((t->tx_buf) || (t->rx_buf))
+ edma_start(davinci_spi_dma->dma_tx_channel);
+
+ if (t->rx_buf)
+ edma_start(davinci_spi_dma->dma_rx_channel);
+
+ if ((t->rx_buf) || (t->tx_buf))
+ davinci_spi_set_dma_req(spi, 1);
+
+ if (t->tx_buf)
+ wait_for_completion_interruptible(
+ &davinci_spi_dma->dma_tx_completion);
+
+ if (t->rx_buf)
+ wait_for_completion_interruptible(
+ &davinci_spi_dma->dma_rx_completion);
+
+ dma_unmap_single(NULL, t->tx_dma, temp_count, DMA_TO_DEVICE);
+
+ if (t->rx_buf)
+ dma_unmap_single(NULL, t->rx_dma, count, DMA_FROM_DEVICE);
+
+ /*
+ * Check for bit error, desync error,parity error,timeout error and
+ * receive overflow errors
+ */
+ int_status = ioread32(davinci_spi->base + SPIFLG);
+
+ ret = davinci_spi_check_error(davinci_spi, int_status);
+ if (ret != 0)
+ return ret;
+
+ /* SPI Framework maintains the count only in bytes so convert back */
+ davinci_spi->count *= conv;
+
+ return t->len;
+}
+
+/**
+ * davinci_spi_irq - IRQ handler for DaVinci SPI
+ * @irq: IRQ number for this SPI Master
+ * @context_data: structure for SPI Master controller davinci_spi
+ */
+static irqreturn_t davinci_spi_irq(s32 irq, void *context_data)
+{
+ struct davinci_spi *davinci_spi = context_data;
+ u32 int_status, rx_data = 0;
+ irqreturn_t ret = IRQ_NONE;
+
+ int_status = ioread32(davinci_spi->base + SPIFLG);
+
+ while ((int_status & SPIFLG_RX_INTR_MASK)) {
+ if (likely(int_status & SPIFLG_RX_INTR_MASK)) {
+ ret = IRQ_HANDLED;
+
+ rx_data = ioread32(davinci_spi->base + SPIBUF);
+ davinci_spi->get_rx(rx_data, davinci_spi);
+
+ /* Disable Receive Interrupt */
+ iowrite32(~(SPIINT_RX_INTR | SPIINT_TX_INTR),
+ davinci_spi->base + SPIINT);
+ } else
+ (void)davinci_spi_check_error(davinci_spi, int_status);
+
+ int_status = ioread32(davinci_spi->base + SPIFLG);
+ }
+
+ return ret;
+}
+
+/**
+ * davinci_spi_probe - probe function for SPI Master Controller
+ * @pdev: platform_device structure which contains plateform specific data
+ */
+static int davinci_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct davinci_spi *davinci_spi;
+ struct davinci_spi_platform_data *pdata;
+ struct resource *r, *mem;
+ resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
+ resource_size_t dma_tx_chan = SPI_NO_RESOURCE;
+ resource_size_t dma_eventq = SPI_NO_RESOURCE;
+ int i = 0, ret = 0;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct davinci_spi));
+ if (master == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_set_drvdata(&pdev->dev, master);
+
+ davinci_spi = spi_master_get_devdata(master);
+ if (davinci_spi == NULL) {
+ ret = -ENOENT;
+ goto free_master;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ ret = -ENOENT;
+ goto free_master;
+ }
+
+ davinci_spi->pbase = r->start;
+ davinci_spi->region_size = resource_size(r);
+ davinci_spi->pdata = pdata;
+
+ mem = request_mem_region(r->start, davinci_spi->region_size,
+ pdev->name);
+ if (mem == NULL) {
+ ret = -EBUSY;
+ goto free_master;
+ }
+
+ davinci_spi->base = (struct davinci_spi_reg __iomem *)
+ ioremap(r->start, davinci_spi->region_size);
+ if (davinci_spi->base == NULL) {
+ ret = -ENOMEM;
+ goto release_region;
+ }
+
+ davinci_spi->irq = platform_get_irq(pdev, 0);
+ if (davinci_spi->irq <= 0) {
+ ret = -EINVAL;
+ goto unmap_io;
+ }
+
+ ret = request_irq(davinci_spi->irq, davinci_spi_irq, IRQF_DISABLED,
+ dev_name(&pdev->dev), davinci_spi);
+ if (ret)
+ goto unmap_io;
+
+ /* Allocate tmp_buf for tx_buf */
+ davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL);
+ if (davinci_spi->tmp_buf == NULL) {
+ ret = -ENOMEM;
+ goto irq_free;
+ }
+
+ davinci_spi->bitbang.master = spi_master_get(master);
+ if (davinci_spi->bitbang.master == NULL) {
+ ret = -ENODEV;
+ goto free_tmp_buf;
+ }
+
+ davinci_spi->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(davinci_spi->clk)) {
+ ret = -ENODEV;
+ goto put_master;
+ }
+ clk_enable(davinci_spi->clk);
+
+
+ master->bus_num = pdev->id;
+ master->num_chipselect = pdata->num_chipselect;
+ master->setup = davinci_spi_setup;
+ master->cleanup = davinci_spi_cleanup;
+
+ davinci_spi->bitbang.chipselect = davinci_spi_chipselect;
+ davinci_spi->bitbang.setup_transfer = davinci_spi_setup_transfer;
+
+ davinci_spi->version = pdata->version;
+ use_dma = pdata->use_dma;
+
+ davinci_spi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP;
+ if (davinci_spi->version == SPI_VERSION_2)
+ davinci_spi->bitbang.flags |= SPI_READY;
+
+ if (use_dma) {
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (r)
+ dma_rx_chan = r->start;
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (r)
+ dma_tx_chan = r->start;
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 2);
+ if (r)
+ dma_eventq = r->start;
+ }
+
+ if (!use_dma ||
+ dma_rx_chan == SPI_NO_RESOURCE ||
+ dma_tx_chan == SPI_NO_RESOURCE ||
+ dma_eventq == SPI_NO_RESOURCE) {
+ davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio;
+ use_dma = 0;
+ } else {
+ davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma;
+ davinci_spi->dma_channels = kzalloc(master->num_chipselect
+ * sizeof(struct davinci_spi_dma), GFP_KERNEL);
+ if (davinci_spi->dma_channels == NULL) {
+ ret = -ENOMEM;
+ goto free_clk;
+ }
+
+ for (i = 0; i < master->num_chipselect; i++) {
+ davinci_spi->dma_channels[i].dma_rx_channel = -1;
+ davinci_spi->dma_channels[i].dma_rx_sync_dev =
+ dma_rx_chan;
+ davinci_spi->dma_channels[i].dma_tx_channel = -1;
+ davinci_spi->dma_channels[i].dma_tx_sync_dev =
+ dma_tx_chan;
+ davinci_spi->dma_channels[i].eventq = dma_eventq;
+ }
+ dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n"
+ "Using RX channel = %d , TX channel = %d and "
+ "event queue = %d", dma_rx_chan, dma_tx_chan,
+ dma_eventq);
+ }
+
+ davinci_spi->get_rx = davinci_spi_rx_buf_u8;
+ davinci_spi->get_tx = davinci_spi_tx_buf_u8;
+
+ init_completion(&davinci_spi->done);
+
+ /* Reset In/OUT SPI module */
+ iowrite32(0, davinci_spi->base + SPIGCR0);
+ udelay(100);
+ iowrite32(1, davinci_spi->base + SPIGCR0);
+
+ /* Clock internal */
+ if (davinci_spi->pdata->clk_internal)
+ set_io_bits(davinci_spi->base + SPIGCR1,
+ SPIGCR1_CLKMOD_MASK);
+ else
+ clear_io_bits(davinci_spi->base + SPIGCR1,
+ SPIGCR1_CLKMOD_MASK);
+
+ /* master mode default */
+ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK);
+
+ if (davinci_spi->pdata->intr_level)
+ iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL);
+ else
+ iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL);
+
+ ret = spi_bitbang_start(&davinci_spi->bitbang);
+ if (ret)
+ goto free_clk;
+
+ dev_info(&pdev->dev, "Controller at 0x%p \n", davinci_spi->base);
+
+ if (!pdata->poll_mode)
+ dev_info(&pdev->dev, "Operating in interrupt mode"
+ " using IRQ %d\n", davinci_spi->irq);
+
+ return ret;
+
+free_clk:
+ clk_disable(davinci_spi->clk);
+ clk_put(davinci_spi->clk);
+put_master:
+ spi_master_put(master);
+free_tmp_buf:
+ kfree(davinci_spi->tmp_buf);
+irq_free:
+ free_irq(davinci_spi->irq, davinci_spi);
+unmap_io:
+ iounmap(davinci_spi->base);
+release_region:
+ release_mem_region(davinci_spi->pbase, davinci_spi->region_size);
+free_master:
+ kfree(master);
+err:
+ return ret;
+}
+
+/**
+ * davinci_spi_remove - remove function for SPI Master Controller
+ * @pdev: platform_device structure which contains plateform specific data
+ *
+ * This function will do the reverse action of davinci_spi_probe function
+ * It will free the IRQ and SPI controller's memory region.
+ * It will also call spi_bitbang_stop to destroy the work queue which was
+ * created by spi_bitbang_start.
+ */
+static int __exit davinci_spi_remove(struct platform_device *pdev)
+{
+ struct davinci_spi *davinci_spi;
+ struct spi_master *master;
+
+ master = dev_get_drvdata(&pdev->dev);
+ davinci_spi = spi_master_get_devdata(master);
+
+ spi_bitbang_stop(&davinci_spi->bitbang);
+
+ clk_disable(davinci_spi->clk);
+ clk_put(davinci_spi->clk);
+ spi_master_put(master);
+ kfree(davinci_spi->tmp_buf);
+ free_irq(davinci_spi->irq, davinci_spi);
+ iounmap(davinci_spi->base);
+ release_mem_region(davinci_spi->pbase, davinci_spi->region_size);
+
+ return 0;
+}
+
+static struct platform_driver davinci_spi_driver = {
+ .driver.name = "spi_davinci",
+ .remove = __exit_p(davinci_spi_remove),
+};
+
+static int __init davinci_spi_init(void)
+{
+ return platform_driver_probe(&davinci_spi_driver, davinci_spi_probe);
+}
+module_init(davinci_spi_init);
+
+static void __exit davinci_spi_exit(void)
+{
+ platform_driver_unregister(&davinci_spi_driver);
+}
+module_exit(davinci_spi_exit);
+
+MODULE_DESCRIPTION("TI DaVinci SPI Master Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c
index 31620fae77b..8ed38f1d6c1 100644
--- a/drivers/spi/dw_spi.c
+++ b/drivers/spi/dw_spi.c
@@ -152,6 +152,7 @@ static void mrst_spi_debugfs_remove(struct dw_spi *dws)
#else
static inline int mrst_spi_debugfs_init(struct dw_spi *dws)
{
+ return 0;
}
static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
@@ -161,14 +162,14 @@ static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
static void wait_till_not_busy(struct dw_spi *dws)
{
- unsigned long end = jiffies + usecs_to_jiffies(1000);
+ unsigned long end = jiffies + 1 + usecs_to_jiffies(1000);
while (time_before(jiffies, end)) {
if (!(dw_readw(dws, sr) & SR_BUSY))
return;
}
dev_err(&dws->master->dev,
- "DW SPI: Stutus keeps busy for 1000us after a read/write!\n");
+ "DW SPI: Status keeps busy for 1000us after a read/write!\n");
}
static void flush(struct dw_spi *dws)
@@ -358,6 +359,8 @@ static void transfer_complete(struct dw_spi *dws)
static irqreturn_t interrupt_transfer(struct dw_spi *dws)
{
u16 irq_status, irq_mask = 0x3f;
+ u32 int_level = dws->fifo_len / 2;
+ u32 left;
irq_status = dw_readw(dws, isr) & irq_mask;
/* Error handling */
@@ -369,22 +372,23 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
return IRQ_HANDLED;
}
- /* INT comes from tx */
- if (dws->tx && (irq_status & SPI_INT_TXEI)) {
- while (dws->tx < dws->tx_end)
+ if (irq_status & SPI_INT_TXEI) {
+ spi_mask_intr(dws, SPI_INT_TXEI);
+
+ left = (dws->tx_end - dws->tx) / dws->n_bytes;
+ left = (left > int_level) ? int_level : left;
+
+ while (left--)
dws->write(dws);
+ dws->read(dws);
- if (dws->tx == dws->tx_end) {
- spi_mask_intr(dws, SPI_INT_TXEI);
+ /* Re-enable the IRQ if there is still data left to tx */
+ if (dws->tx_end > dws->tx)
+ spi_umask_intr(dws, SPI_INT_TXEI);
+ else
transfer_complete(dws);
- }
}
- /* INT comes from rx */
- if (dws->rx && (irq_status & SPI_INT_RXFI)) {
- if (dws->read(dws))
- transfer_complete(dws);
- }
return IRQ_HANDLED;
}
@@ -404,12 +408,9 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
/* Must be called inside pump_transfers() */
static void poll_transfer(struct dw_spi *dws)
{
- if (dws->tx) {
- while (dws->write(dws))
- dws->read(dws);
- }
+ while (dws->write(dws))
+ dws->read(dws);
- dws->read(dws);
transfer_complete(dws);
}
@@ -428,6 +429,7 @@ static void pump_transfers(unsigned long data)
u8 bits = 0;
u8 imask = 0;
u8 cs_change = 0;
+ u16 txint_level = 0;
u16 clk_div = 0;
u32 speed = 0;
u32 cr0 = 0;
@@ -438,6 +440,9 @@ static void pump_transfers(unsigned long data)
chip = dws->cur_chip;
spi = message->spi;
+ if (unlikely(!chip->clk_div))
+ chip->clk_div = dws->max_freq / chip->speed_hz;
+
if (message->state == ERROR_STATE) {
message->status = -EIO;
goto early_exit;
@@ -492,7 +497,7 @@ static void pump_transfers(unsigned long data)
/* clk_div doesn't support odd number */
clk_div = dws->max_freq / speed;
- clk_div = (clk_div >> 1) << 1;
+ clk_div = (clk_div + 1) & 0xfffe;
chip->speed_hz = speed;
chip->clk_div = clk_div;
@@ -532,14 +537,35 @@ static void pump_transfers(unsigned long data)
}
message->state = RUNNING_STATE;
+ /*
+ * Adjust transfer mode if necessary. Requires platform dependent
+ * chipselect mechanism.
+ */
+ if (dws->cs_control) {
+ if (dws->rx && dws->tx)
+ chip->tmode = 0x00;
+ else if (dws->rx)
+ chip->tmode = 0x02;
+ else
+ chip->tmode = 0x01;
+
+ cr0 &= ~(0x3 << SPI_MODE_OFFSET);
+ cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
+ }
+
/* Check if current transfer is a DMA transaction */
dws->dma_mapped = map_dma_buffers(dws);
+ /*
+ * Interrupt mode
+ * we only need set the TXEI IRQ, as TX/RX always happen syncronizely
+ */
if (!dws->dma_mapped && !chip->poll_mode) {
- if (dws->rx)
- imask |= SPI_INT_RXFI;
- if (dws->tx)
- imask |= SPI_INT_TXEI;
+ int templen = dws->len / dws->n_bytes;
+ txint_level = dws->fifo_len / 2;
+ txint_level = (templen > txint_level) ? txint_level : templen;
+
+ imask |= SPI_INT_TXEI;
dws->transfer_handler = interrupt_transfer;
}
@@ -549,21 +575,23 @@ static void pump_transfers(unsigned long data)
* 2. clk_div is changed
* 3. control value changes
*/
- if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) {
+ if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div || imask) {
spi_enable_chip(dws, 0);
if (dw_readw(dws, ctrl0) != cr0)
dw_writew(dws, ctrl0, cr0);
+ spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
+ spi_chip_sel(dws, spi->chip_select);
+
/* Set the interrupt mask, for poll mode just diable all int */
spi_mask_intr(dws, 0xff);
- if (!chip->poll_mode)
+ if (imask)
spi_umask_intr(dws, imask);
+ if (txint_level)
+ dw_writew(dws, txfltr, txint_level);
- spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
- spi_chip_sel(dws, spi->chip_select);
spi_enable_chip(dws, 1);
-
if (cs_change)
dws->prev_chip = chip;
}
@@ -712,11 +740,11 @@ static int dw_spi_setup(struct spi_device *spi)
}
chip->bits_per_word = spi->bits_per_word;
+ if (!spi->max_speed_hz) {
+ dev_err(&spi->dev, "No max speed HZ parameter\n");
+ return -EINVAL;
+ }
chip->speed_hz = spi->max_speed_hz;
- if (chip->speed_hz)
- chip->clk_div = 25000000 / chip->speed_hz;
- else
- chip->clk_div = 8; /* default value */
chip->tmode = 0; /* Tx & Rx */
/* Default SPI mode is SCPOL = 0, SCPH = 0 */
@@ -735,7 +763,7 @@ static void dw_spi_cleanup(struct spi_device *spi)
kfree(chip);
}
-static int __init init_queue(struct dw_spi *dws)
+static int __devinit init_queue(struct dw_spi *dws)
{
INIT_LIST_HEAD(&dws->queue);
spin_lock_init(&dws->lock);
@@ -817,6 +845,22 @@ static void spi_hw_init(struct dw_spi *dws)
spi_mask_intr(dws, 0xff);
spi_enable_chip(dws, 1);
flush(dws);
+
+ /*
+ * Try to detect the FIFO depth if not set by interface driver,
+ * the depth could be from 2 to 256 from HW spec
+ */
+ if (!dws->fifo_len) {
+ u32 fifo;
+ for (fifo = 2; fifo <= 257; fifo++) {
+ dw_writew(dws, txfltr, fifo);
+ if (fifo != dw_readw(dws, txfltr))
+ break;
+ }
+
+ dws->fifo_len = (fifo == 257) ? 0 : fifo;
+ dw_writew(dws, txfltr, 0);
+ }
}
int __devinit dw_spi_add_host(struct dw_spi *dws)
@@ -913,6 +957,7 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws)
/* Disconnect from the SPI framework */
spi_unregister_master(dws->master);
}
+EXPORT_SYMBOL(dw_spi_remove_host);
int dw_spi_suspend_host(struct dw_spi *dws)
{
diff --git a/drivers/spi/dw_spi_mmio.c b/drivers/spi/dw_spi_mmio.c
new file mode 100644
index 00000000000..e35b45ac517
--- /dev/null
+++ b/drivers/spi/dw_spi_mmio.c
@@ -0,0 +1,147 @@
+/*
+ * dw_spi_mmio.c - Memory-mapped interface driver for DW SPI Core
+ *
+ * Copyright (c) 2010, Octasic semiconductor.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/dw_spi.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME "dw_spi_mmio"
+
+struct dw_spi_mmio {
+ struct dw_spi dws;
+ struct clk *clk;
+};
+
+static int __devinit dw_spi_mmio_probe(struct platform_device *pdev)
+{
+ struct dw_spi_mmio *dwsmmio;
+ struct dw_spi *dws;
+ struct resource *mem, *ioarea;
+ int ret;
+
+ dwsmmio = kzalloc(sizeof(struct dw_spi_mmio), GFP_KERNEL);
+ if (!dwsmmio) {
+ ret = -ENOMEM;
+ goto err_end;
+ }
+
+ dws = &dwsmmio->dws;
+
+ /* Get basic io resource and map it */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ ret = -EINVAL;
+ goto err_kfree;
+ }
+
+ ioarea = request_mem_region(mem->start, resource_size(mem),
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "SPI region already claimed\n");
+ ret = -EBUSY;
+ goto err_kfree;
+ }
+
+ dws->regs = ioremap_nocache(mem->start, resource_size(mem));
+ if (!dws->regs) {
+ dev_err(&pdev->dev, "SPI region already mapped\n");
+ ret = -ENOMEM;
+ goto err_release_reg;
+ }
+
+ dws->irq = platform_get_irq(pdev, 0);
+ if (dws->irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ ret = dws->irq; /* -ENXIO */
+ goto err_unmap;
+ }
+
+ dwsmmio->clk = clk_get(&pdev->dev, NULL);
+ if (!dwsmmio->clk) {
+ ret = -ENODEV;
+ goto err_irq;
+ }
+ clk_enable(dwsmmio->clk);
+
+ dws->parent_dev = &pdev->dev;
+ dws->bus_num = 0;
+ dws->num_cs = 4;
+ dws->max_freq = clk_get_rate(dwsmmio->clk);
+
+ ret = dw_spi_add_host(dws);
+ if (ret)
+ goto err_clk;
+
+ platform_set_drvdata(pdev, dwsmmio);
+ return 0;
+
+err_clk:
+ clk_disable(dwsmmio->clk);
+ clk_put(dwsmmio->clk);
+ dwsmmio->clk = NULL;
+err_irq:
+ free_irq(dws->irq, dws);
+err_unmap:
+ iounmap(dws->regs);
+err_release_reg:
+ release_mem_region(mem->start, resource_size(mem));
+err_kfree:
+ kfree(dwsmmio);
+err_end:
+ return ret;
+}
+
+static int __devexit dw_spi_mmio_remove(struct platform_device *pdev)
+{
+ struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+
+ clk_disable(dwsmmio->clk);
+ clk_put(dwsmmio->clk);
+ dwsmmio->clk = NULL;
+
+ free_irq(dwsmmio->dws.irq, &dwsmmio->dws);
+ dw_spi_remove_host(&dwsmmio->dws);
+ iounmap(dwsmmio->dws.regs);
+ kfree(dwsmmio);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+ return 0;
+}
+
+static struct platform_driver dw_spi_mmio_driver = {
+ .remove = __devexit_p(dw_spi_mmio_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init dw_spi_mmio_init(void)
+{
+ return platform_driver_probe(&dw_spi_mmio_driver, dw_spi_mmio_probe);
+}
+module_init(dw_spi_mmio_init);
+
+static void __exit dw_spi_mmio_exit(void)
+{
+ platform_driver_unregister(&dw_spi_mmio_driver);
+}
+module_exit(dw_spi_mmio_exit);
+
+MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>");
+MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c
index 34ba6916173..1f0735f9cc7 100644
--- a/drivers/spi/dw_spi_pci.c
+++ b/drivers/spi/dw_spi_pci.c
@@ -73,6 +73,7 @@ static int __devinit spi_pci_probe(struct pci_dev *pdev,
dws->num_cs = 4;
dws->max_freq = 25000000; /* for Moorestwon */
dws->irq = pdev->irq;
+ dws->fifo_len = 40; /* FIFO has 40 words buffer */
ret = dw_spi_add_host(dws);
if (ret)
@@ -98,6 +99,7 @@ static void __devexit spi_pci_remove(struct pci_dev *pdev)
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
pci_set_drvdata(pdev, NULL);
+ dw_spi_remove_host(&dwpci->dws);
iounmap(dwpci->dws.regs);
pci_release_region(pdev, 0);
kfree(dwpci);
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c
index f50c81df336..04747868d6c 100644
--- a/drivers/spi/mpc52xx_psc_spi.c
+++ b/drivers/spi/mpc52xx_psc_spi.c
@@ -503,7 +503,7 @@ static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op)
return mpc52xx_psc_spi_do_remove(&op->dev);
}
-static struct of_device_id mpc52xx_psc_spi_of_match[] = {
+static const struct of_device_id mpc52xx_psc_spi_of_match[] = {
{ .compatible = "fsl,mpc5200-psc-spi", },
{ .compatible = "mpc5200-psc-spi", }, /* old */
{}
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
index 45bfe645817..6eab46537a0 100644
--- a/drivers/spi/mpc52xx_spi.c
+++ b/drivers/spi/mpc52xx_spi.c
@@ -550,7 +550,7 @@ static int __devexit mpc52xx_spi_remove(struct of_device *op)
return 0;
}
-static struct of_device_id mpc52xx_spi_match[] __devinitdata = {
+static const struct of_device_id mpc52xx_spi_match[] __devinitconst = {
{ .compatible = "fsl,mpc5200-spi", },
{}
};
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c
index 1893f1e96dc..0ddbbe45e83 100644
--- a/drivers/spi/spi_imx.c
+++ b/drivers/spi/spi_imx.c
@@ -469,7 +469,7 @@ static int spi_imx_setup(struct spi_device *spi)
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
int gpio = spi_imx->chipselect[spi->chip_select];
- pr_debug("%s: mode %d, %u bpw, %d hz\n", __func__,
+ dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
spi->mode, spi->bits_per_word, spi->max_speed_hz);
if (gpio >= 0)
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 1fb2a6ea328..4f0cc9d457e 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -365,7 +365,7 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if ((mpc8xxx_spi->spibrg / hz) > 64) {
cs->hw_mode |= SPMODE_DIV16;
- pm = mpc8xxx_spi->spibrg / (hz * 64);
+ pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
"Will use %d Hz instead.\n", dev_name(&spi->dev),
@@ -373,7 +373,7 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if (pm > 16)
pm = 16;
} else
- pm = mpc8xxx_spi->spibrg / (hz * 4);
+ pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
if (pm)
pm--;
@@ -1328,7 +1328,7 @@ static struct of_platform_driver of_mpc8xxx_spi_driver = {
static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
{
struct resource *mem;
- unsigned int irq;
+ int irq;
struct spi_master *master;
if (!pdev->dev.platform_data)
@@ -1339,7 +1339,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
return -EINVAL;
irq = platform_get_irq(pdev, 0);
- if (!irq)
+ if (irq <= 0)
return -EINVAL;
master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
index 140a18d6cf3..6d8d4026a07 100644
--- a/drivers/spi/spi_ppc4xx.c
+++ b/drivers/spi/spi_ppc4xx.c
@@ -578,7 +578,7 @@ static int __exit spi_ppc4xx_of_remove(struct of_device *op)
return 0;
}
-static struct of_device_id spi_ppc4xx_of_match[] = {
+static const struct of_device_id spi_ppc4xx_of_match[] = {
{ .compatible = "ibm,ppc4xx-spi", },
{},
};
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c
index 88a456dba96..97365815a72 100644
--- a/drivers/spi/spi_s3c64xx.c
+++ b/drivers/spi/spi_s3c64xx.c
@@ -28,7 +28,7 @@
#include <linux/spi/spi.h>
#include <mach/dma.h>
-#include <plat/spi.h>
+#include <plat/s3c64xx-spi.h>
/* Registers and bit-fields */
@@ -137,6 +137,7 @@
/**
* struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
* @clk: Pointer to the spi clock.
+ * @src_clk: Pointer to the clock used to generate SPI signals.
* @master: Pointer to the SPI Protocol master.
* @workqueue: Work queue for the SPI xfer requests.
* @cntrlr_info: Platform specific data for the controller this driver manages.
@@ -157,10 +158,11 @@
struct s3c64xx_spi_driver_data {
void __iomem *regs;
struct clk *clk;
+ struct clk *src_clk;
struct platform_device *pdev;
struct spi_master *master;
struct workqueue_struct *workqueue;
- struct s3c64xx_spi_cntrlr_info *cntrlr_info;
+ struct s3c64xx_spi_info *cntrlr_info;
struct spi_device *tgl_spi;
struct work_struct work;
struct list_head queue;
@@ -180,7 +182,7 @@ static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
{
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
unsigned long loops;
u32 val;
@@ -225,7 +227,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode)
{
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 modecfg, chcfg;
@@ -298,19 +300,20 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
/* Deselect the last toggled device */
cs = sdd->tgl_spi->controller_data;
- cs->set_level(spi->mode & SPI_CS_HIGH ? 0 : 1);
+ cs->set_level(cs->line,
+ spi->mode & SPI_CS_HIGH ? 0 : 1);
}
sdd->tgl_spi = NULL;
}
cs = spi->controller_data;
- cs->set_level(spi->mode & SPI_CS_HIGH ? 1 : 0);
+ cs->set_level(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0);
}
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer, int dma_mode)
{
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
unsigned long val;
int ms;
@@ -384,12 +387,11 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
if (sdd->tgl_spi == spi)
sdd->tgl_spi = NULL;
- cs->set_level(spi->mode & SPI_CS_HIGH ? 0 : 1);
+ cs->set_level(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1);
}
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
{
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 val;
@@ -435,7 +437,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
/* Configure Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_PSR_MASK;
- val |= ((clk_get_rate(sci->src_clk) / sdd->cur_speed / 2 - 1)
+ val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
& S3C64XX_SPI_PSR_MASK);
writel(val, regs + S3C64XX_SPI_CLK_CFG);
@@ -558,7 +560,7 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
struct spi_message *msg)
{
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct spi_device *spi = msg->spi;
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct spi_transfer *xfer;
@@ -632,8 +634,8 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
S3C64XX_SPI_DEACT(sdd);
if (status) {
- dev_err(&spi->dev, "I/O Error: \
- rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
+ dev_err(&spi->dev, "I/O Error: "
+ "rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
(sdd->state & RXBUSY) ? 'f' : 'p',
(sdd->state & TXBUSY) ? 'f' : 'p',
@@ -786,7 +788,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
{
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct s3c64xx_spi_driver_data *sdd;
- struct s3c64xx_spi_cntrlr_info *sci;
+ struct s3c64xx_spi_info *sci;
struct spi_message *msg;
u32 psr, speed;
unsigned long flags;
@@ -831,17 +833,17 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}
/* Check if we can provide the requested rate */
- speed = clk_get_rate(sci->src_clk) / 2 / (0 + 1); /* Max possible */
+ speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */
if (spi->max_speed_hz > speed)
spi->max_speed_hz = speed;
- psr = clk_get_rate(sci->src_clk) / 2 / spi->max_speed_hz - 1;
+ psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
psr &= S3C64XX_SPI_PSR_MASK;
if (psr == S3C64XX_SPI_PSR_MASK)
psr--;
- speed = clk_get_rate(sci->src_clk) / 2 / (psr + 1);
+ speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz < speed) {
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
psr++;
@@ -851,7 +853,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}
}
- speed = clk_get_rate(sci->src_clk) / 2 / (psr + 1);
+ speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz >= speed)
spi->max_speed_hz = speed;
else
@@ -867,7 +869,7 @@ setup_exit:
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
{
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
unsigned int val;
@@ -902,7 +904,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{
struct resource *mem_res, *dmatx_res, *dmarx_res;
struct s3c64xx_spi_driver_data *sdd;
- struct s3c64xx_spi_cntrlr_info *sci;
+ struct s3c64xx_spi_info *sci;
struct spi_master *master;
int ret;
@@ -1000,18 +1002,15 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
goto err4;
}
- if (sci->src_clk_nr == S3C64XX_SPI_SRCCLK_PCLK)
- sci->src_clk = sdd->clk;
- else
- sci->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
- if (IS_ERR(sci->src_clk)) {
+ sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
+ if (IS_ERR(sdd->src_clk)) {
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", sci->src_clk_name);
- ret = PTR_ERR(sci->src_clk);
+ ret = PTR_ERR(sdd->src_clk);
goto err5;
}
- if (sci->src_clk != sdd->clk && clk_enable(sci->src_clk)) {
+ if (clk_enable(sdd->src_clk)) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
sci->src_clk_name);
ret = -EBUSY;
@@ -1040,11 +1039,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
goto err8;
}
- dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d \
- with %d Slaves attached\n",
+ dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
+ "with %d Slaves attached\n",
pdev->id, master->num_chipselect);
- dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\
- \tDMA=[Rx-%d, Tx-%d]\n",
+ dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start,
sdd->rx_dmach, sdd->tx_dmach);
@@ -1053,11 +1051,9 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
err8:
destroy_workqueue(sdd->workqueue);
err7:
- if (sci->src_clk != sdd->clk)
- clk_disable(sci->src_clk);
+ clk_disable(sdd->src_clk);
err6:
- if (sci->src_clk != sdd->clk)
- clk_put(sci->src_clk);
+ clk_put(sdd->src_clk);
err5:
clk_disable(sdd->clk);
err4:
@@ -1078,7 +1074,6 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
struct resource *mem_res;
unsigned long flags;
@@ -1093,11 +1088,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
destroy_workqueue(sdd->workqueue);
- if (sci->src_clk != sdd->clk)
- clk_disable(sci->src_clk);
-
- if (sci->src_clk != sdd->clk)
- clk_put(sci->src_clk);
+ clk_disable(sdd->src_clk);
+ clk_put(sdd->src_clk);
clk_disable(sdd->clk);
clk_put(sdd->clk);
@@ -1105,7 +1097,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
iounmap((void *) sdd->regs);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem_res->start, resource_size(mem_res));
+ if (mem_res != NULL)
+ release_mem_region(mem_res->start, resource_size(mem_res));
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
@@ -1118,8 +1111,6 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
- struct s3c64xx_spi_csinfo *cs;
unsigned long flags;
spin_lock_irqsave(&sdd->lock, flags);
@@ -1130,9 +1121,7 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
msleep(10);
/* Disable the clock */
- if (sci->src_clk != sdd->clk)
- clk_disable(sci->src_clk);
-
+ clk_disable(sdd->src_clk);
clk_disable(sdd->clk);
sdd->cur_speed = 0; /* Output Clock is stopped */
@@ -1144,15 +1133,13 @@ static int s3c64xx_spi_resume(struct platform_device *pdev)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
unsigned long flags;
sci->cfg_gpio(pdev);
/* Enable the clock */
- if (sci->src_clk != sdd->clk)
- clk_enable(sci->src_clk);
-
+ clk_enable(sdd->src_clk);
clk_enable(sdd->clk);
s3c64xx_spi_hwinit(sdd, pdev->id);
diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c
index 30973ec16a9..d93b66743ba 100644
--- a/drivers/spi/spi_sh_msiof.c
+++ b/drivers/spi/spi_sh_msiof.c
@@ -20,12 +20,12 @@
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/err.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/sh_msiof.h>
-#include <asm/spi.h>
#include <asm/unaligned.h>
struct sh_msiof_spi_priv {
diff --git a/drivers/spi/spi_stmp.c b/drivers/spi/spi_stmp.c
index 2552bb36400..fadff76eb7e 100644
--- a/drivers/spi/spi_stmp.c
+++ b/drivers/spi/spi_stmp.c
@@ -76,7 +76,7 @@ struct stmp_spi {
break; \
} \
cpu_relax(); \
- } while (time_before(end_jiffies, jiffies)); \
+ } while (time_before(jiffies, end_jiffies)); \
succeeded; \
})
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index 9f386379c16..1b47363cb73 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -93,6 +93,26 @@ struct xilinx_spi {
void (*rx_fn) (struct xilinx_spi *);
};
+static void xspi_write32(u32 val, void __iomem *addr)
+{
+ iowrite32(val, addr);
+}
+
+static unsigned int xspi_read32(void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
+static void xspi_write32_be(u32 val, void __iomem *addr)
+{
+ iowrite32be(val, addr);
+}
+
+static unsigned int xspi_read32_be(void __iomem *addr)
+{
+ return ioread32be(addr);
+}
+
static void xspi_tx8(struct xilinx_spi *xspi)
{
xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET);
@@ -374,11 +394,11 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
xspi->mem = *mem;
xspi->irq = irq;
if (pdata->little_endian) {
- xspi->read_fn = ioread32;
- xspi->write_fn = iowrite32;
+ xspi->read_fn = xspi_read32;
+ xspi->write_fn = xspi_write32;
} else {
- xspi->read_fn = ioread32be;
- xspi->write_fn = iowrite32be;
+ xspi->read_fn = xspi_read32_be;
+ xspi->write_fn = xspi_write32_be;
}
xspi->bits_per_word = pdata->bits_per_word;
if (xspi->bits_per_word == 8) {
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
index 71dc3adc049..ed34a8d419c 100644
--- a/drivers/spi/xilinx_spi_of.c
+++ b/drivers/spi/xilinx_spi_of.c
@@ -99,7 +99,7 @@ static int __exit xilinx_spi_of_remove(struct of_device *op)
return xilinx_spi_remove(op);
}
-static struct of_device_id xilinx_spi_of_match[] = {
+static const struct of_device_id xilinx_spi_of_match[] = {
{ .compatible = "xlnx,xps-spi-2.00.a", },
{ .compatible = "xlnx,xps-spi-2.00.b", },
{}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 5681ebed9c6..03dfd27c4bf 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -494,8 +494,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
#endif
break;
case SSB_BUSTYPE_SDIO:
-#ifdef CONFIG_SSB_SDIO
- sdev->irq = bus->host_sdio->dev.irq;
+#ifdef CONFIG_SSB_SDIOHOST
dev->parent = &bus->host_sdio->dev;
#endif
break;
diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c
index 8cf7f2750b3..c324f6ea002 100644
--- a/drivers/staging/go7007/s2250-board.c
+++ b/drivers/staging/go7007/s2250-board.c
@@ -159,7 +159,7 @@ static int write_reg(struct i2c_client *client, u8 reg, u8 value)
struct go7007 *go = i2c_get_adapdata(client->adapter);
struct go7007_usb *usb;
int rc;
- int dev_addr = client->addr;
+ int dev_addr = client->addr << 1; /* firmware wants 8-bit address */
u8 *buf;
if (go == NULL)
diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c
index 62b282844a5..45d908114d1 100644
--- a/drivers/staging/hv/blkvsc_drv.c
+++ b/drivers/staging/hv/blkvsc_drv.c
@@ -363,10 +363,7 @@ static int blkvsc_probe(struct device *device)
blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock);
blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE);
- blk_queue_max_phys_segments(blkdev->gd->queue,
- MAX_MULTIPAGE_BUFFER_COUNT);
- blk_queue_max_hw_segments(blkdev->gd->queue,
- MAX_MULTIPAGE_BUFFER_COUNT);
+ blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT);
blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1);
blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY);
blk_queue_dma_alignment(blkdev->gd->queue, 511);
diff --git a/drivers/staging/octeon/Makefile b/drivers/staging/octeon/Makefile
index c0a583cc222..87447c102fa 100644
--- a/drivers/staging/octeon/Makefile
+++ b/drivers/staging/octeon/Makefile
@@ -14,7 +14,6 @@ obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o
octeon-ethernet-objs := ethernet.o
octeon-ethernet-objs += ethernet-mdio.o
octeon-ethernet-objs += ethernet-mem.o
-octeon-ethernet-objs += ethernet-proc.o
octeon-ethernet-objs += ethernet-rgmii.o
octeon-ethernet-objs += ethernet-rx.o
octeon-ethernet-objs += ethernet-sgmii.o
diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
index f13131b03c3..6a2cd50a17d 100644
--- a/drivers/staging/octeon/ethernet-defines.h
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -41,17 +41,10 @@
* Tells the driver to populate the packet buffers with kernel skbuffs.
* This allows the driver to receive packets without copying them. It also
* means that 32bit userspace can't access the packet buffers.
- * USE_32BIT_SHARED
- * This define tells the driver to allocate memory for buffers from the
- * 32bit sahred region instead of the kernel memory space.
* USE_HW_TCPUDP_CHECKSUM
* Controls if the Octeon TCP/UDP checksum engine is used for packet
* output. If this is zero, the kernel will perform the checksum in
* software.
- * USE_MULTICORE_RECEIVE
- * Process receive interrupts on multiple cores. This spreads the network
- * load across the first 8 processors. If ths is zero, only one core
- * processes incomming packets.
* USE_ASYNC_IOBDMA
* Use asynchronous IO access to hardware. This uses Octeon's asynchronous
* IOBDMAs to issue IO accesses without stalling. Set this to zero
@@ -75,29 +68,15 @@
#define CONFIG_CAVIUM_RESERVE32 0
#endif
-#if CONFIG_CAVIUM_RESERVE32
-#define USE_32BIT_SHARED 1
-#define USE_SKBUFFS_IN_HW 0
-#define REUSE_SKBUFFS_WITHOUT_FREE 0
-#else
-#define USE_32BIT_SHARED 0
#define USE_SKBUFFS_IN_HW 1
#ifdef CONFIG_NETFILTER
#define REUSE_SKBUFFS_WITHOUT_FREE 0
#else
#define REUSE_SKBUFFS_WITHOUT_FREE 1
#endif
-#endif
-
-/* Max interrupts per second per core */
-#define INTERRUPT_LIMIT 10000
-/* Don't limit the number of interrupts */
-/*#define INTERRUPT_LIMIT 0 */
#define USE_HW_TCPUDP_CHECKSUM 1
-#define USE_MULTICORE_RECEIVE 1
-
/* Enable Random Early Dropping under load */
#define USE_RED 1
#define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0)
@@ -115,21 +94,12 @@
/* Use this to not have FPA frees control L2 */
/*#define DONT_WRITEBACK(x) 0 */
-/* Maximum number of packets to process per interrupt. */
-#define MAX_RX_PACKETS 120
/* Maximum number of SKBs to try to free per xmit packet. */
-#define MAX_SKB_TO_FREE 10
#define MAX_OUT_QUEUE_DEPTH 1000
-#ifndef CONFIG_SMP
-#undef USE_MULTICORE_RECEIVE
-#define USE_MULTICORE_RECEIVE 0
-#endif
-
-#define IP_PROTOCOL_TCP 6
-#define IP_PROTOCOL_UDP 0x11
+#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(uint32_t))
-#define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t))
#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c
index 05a5cc0f43e..7e0be8d00dc 100644
--- a/drivers/staging/octeon/ethernet-mdio.c
+++ b/drivers/staging/octeon/ethernet-mdio.c
@@ -96,11 +96,11 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
};
/**
- * IOCTL support for PHY control
- *
+ * cvm_oct_ioctl - IOCTL support for PHY control
* @dev: Device to change
* @rq: the request
* @cmd: the command
+ *
* Returns Zero on success
*/
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
@@ -153,7 +153,7 @@ static void cvm_oct_adjust_link(struct net_device *dev)
/**
- * Setup the PHY
+ * cvm_oct_phy_setup_device - setup the PHY
*
* @dev: Device to setup
*
diff --git a/drivers/staging/octeon/ethernet-mdio.h b/drivers/staging/octeon/ethernet-mdio.h
index 55d0614a7cd..a417d4fce12 100644
--- a/drivers/staging/octeon/ethernet-mdio.h
+++ b/drivers/staging/octeon/ethernet-mdio.h
@@ -32,7 +32,6 @@
#include <linux/ip.h>
#include <linux/string.h>
#include <linux/ethtool.h>
-#include <linux/mii.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <net/dst.h>
diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c
index b595903e2af..00cc91df6b4 100644
--- a/drivers/staging/octeon/ethernet-mem.c
+++ b/drivers/staging/octeon/ethernet-mem.c
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2007 Cavium Networks
+ * Copyright (c) 2003-2010 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
@@ -26,8 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
-#include <net/dst.h>
#include <asm/octeon/octeon.h>
@@ -36,18 +34,19 @@
#include "cvmx-fpa.h"
/**
- * Fill the supplied hardware pool with skbuffs
- *
+ * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs
* @pool: Pool to allocate an skbuff for
* @size: Size of the buffer needed for the pool
* @elements: Number of buffers to allocate
+ *
+ * Returns the actual number of buffers allocated.
*/
static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements)
{
int freed = elements;
while (freed) {
- struct sk_buff *skb = dev_alloc_skb(size + 128);
+ struct sk_buff *skb = dev_alloc_skb(size + 256);
if (unlikely(skb == NULL)) {
pr_warning
("Failed to allocate skb for hardware pool %d\n",
@@ -55,7 +54,7 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements)
break;
}
- skb_reserve(skb, 128 - (((unsigned long)skb->data) & 0x7f));
+ skb_reserve(skb, 256 - (((unsigned long)skb->data) & 0x7f));
*(struct sk_buff **)(skb->data - sizeof(void *)) = skb;
cvmx_fpa_free(skb->data, pool, DONT_WRITEBACK(size / 128));
freed--;
@@ -64,8 +63,7 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements)
}
/**
- * Free the supplied hardware pool of skbuffs
- *
+ * cvm_oct_free_hw_skbuff- free hardware pool skbuffs
* @pool: Pool to allocate an skbuff for
* @size: Size of the buffer needed for the pool
* @elements: Number of buffers to allocate
@@ -93,96 +91,76 @@ static void cvm_oct_free_hw_skbuff(int pool, int size, int elements)
}
/**
- * This function fills a hardware pool with memory. Depending
- * on the config defines, this memory might come from the
- * kernel or global 32bit memory allocated with
- * cvmx_bootmem_alloc.
- *
+ * cvm_oct_fill_hw_memory - fill a hardware pool with memory.
* @pool: Pool to populate
* @size: Size of each buffer in the pool
* @elements: Number of buffers to allocate
+ *
+ * Returns the actual number of buffers allocated.
*/
static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
{
char *memory;
+ char *fpa;
int freed = elements;
- if (USE_32BIT_SHARED) {
- extern uint64_t octeon_reserve32_memory;
-
- memory =
- cvmx_bootmem_alloc_range(elements * size, 128,
- octeon_reserve32_memory,
- octeon_reserve32_memory +
- (CONFIG_CAVIUM_RESERVE32 << 20) -
- 1);
- if (memory == NULL)
- panic("Unable to allocate %u bytes for FPA pool %d\n",
- elements * size, pool);
-
- pr_notice("Memory range %p - %p reserved for "
- "hardware\n", memory,
- memory + elements * size - 1);
-
- while (freed) {
- cvmx_fpa_free(memory, pool, 0);
- memory += size;
- freed--;
- }
- } else {
- while (freed) {
- /* We need to force alignment to 128 bytes here */
- memory = kmalloc(size + 127, GFP_ATOMIC);
- if (unlikely(memory == NULL)) {
- pr_warning("Unable to allocate %u bytes for "
- "FPA pool %d\n",
- elements * size, pool);
- break;
- }
- memory = (char *)(((unsigned long)memory + 127) & -128);
- cvmx_fpa_free(memory, pool, 0);
- freed--;
+ while (freed) {
+ /*
+ * FPA memory must be 128 byte aligned. Since we are
+ * aligning we need to save the original pointer so we
+ * can feed it to kfree when the memory is returned to
+ * the kernel.
+ *
+ * We allocate an extra 256 bytes to allow for
+ * alignment and space for the original pointer saved
+ * just before the block.
+ */
+ memory = kmalloc(size + 256, GFP_ATOMIC);
+ if (unlikely(memory == NULL)) {
+ pr_warning("Unable to allocate %u bytes for FPA pool %d\n",
+ elements * size, pool);
+ break;
}
+ fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL);
+ *((char **)fpa - 1) = memory;
+ cvmx_fpa_free(fpa, pool, 0);
+ freed--;
}
return elements - freed;
}
/**
- * Free memory previously allocated with cvm_oct_fill_hw_memory
- *
+ * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory
* @pool: FPA pool to free
* @size: Size of each buffer in the pool
* @elements: Number of buffers that should be in the pool
*/
static void cvm_oct_free_hw_memory(int pool, int size, int elements)
{
- if (USE_32BIT_SHARED) {
- pr_warning("Warning: 32 shared memory is not freeable\n");
- } else {
- char *memory;
- do {
- memory = cvmx_fpa_alloc(pool);
- if (memory) {
- elements--;
- kfree(phys_to_virt(cvmx_ptr_to_phys(memory)));
- }
- } while (memory);
+ char *memory;
+ char *fpa;
+ do {
+ fpa = cvmx_fpa_alloc(pool);
+ if (fpa) {
+ elements--;
+ fpa = (char *)phys_to_virt(cvmx_ptr_to_phys(fpa));
+ memory = *((char **)fpa - 1);
+ kfree(memory);
+ }
+ } while (fpa);
- if (elements < 0)
- pr_warning("Freeing of pool %u had too many "
- "buffers (%d)\n",
- pool, elements);
- else if (elements > 0)
- pr_warning("Warning: Freeing of pool %u is "
- "missing %d buffers\n",
- pool, elements);
- }
+ if (elements < 0)
+ pr_warning("Freeing of pool %u had too many buffers (%d)\n",
+ pool, elements);
+ else if (elements > 0)
+ pr_warning("Warning: Freeing of pool %u is missing %d buffers\n",
+ pool, elements);
}
int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
{
int freed;
- if (USE_SKBUFFS_IN_HW)
+ if (USE_SKBUFFS_IN_HW && pool == CVMX_FPA_PACKET_POOL)
freed = cvm_oct_fill_hw_skbuff(pool, size, elements);
else
freed = cvm_oct_fill_hw_memory(pool, size, elements);
@@ -191,7 +169,7 @@ int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
void cvm_oct_mem_empty_fpa(int pool, int size, int elements)
{
- if (USE_SKBUFFS_IN_HW)
+ if (USE_SKBUFFS_IN_HW && pool == CVMX_FPA_PACKET_POOL)
cvm_oct_free_hw_skbuff(pool, size, elements);
else
cvm_oct_free_hw_memory(pool, size, elements);
diff --git a/drivers/staging/octeon/ethernet-proc.c b/drivers/staging/octeon/ethernet-proc.c
deleted file mode 100644
index 16308d484d3..00000000000
--- a/drivers/staging/octeon/ethernet-proc.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/**********************************************************************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2007 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
-**********************************************************************/
-#include <linux/kernel.h>
-#include <linux/seq_file.h>
-#include <linux/proc_fs.h>
-#include <net/dst.h>
-
-#include <asm/octeon/octeon.h>
-
-#include "octeon-ethernet.h"
-#include "ethernet-defines.h"
-
-#include "cvmx-helper.h"
-#include "cvmx-pip.h"
-
-/**
- * User is reading /proc/octeon_ethernet_stats
- *
- * @m:
- * @v:
- * Returns
- */
-static int cvm_oct_stats_show(struct seq_file *m, void *v)
-{
- struct octeon_ethernet *priv;
- int port;
-
- for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
-
- if (cvm_oct_device[port]) {
- priv = netdev_priv(cvm_oct_device[port]);
-
- seq_printf(m, "\nOcteon Port %d (%s)\n", port,
- cvm_oct_device[port]->name);
- seq_printf(m,
- "rx_packets: %12lu\t"
- "tx_packets: %12lu\n",
- priv->stats.rx_packets,
- priv->stats.tx_packets);
- seq_printf(m,
- "rx_bytes: %12lu\t"
- "tx_bytes: %12lu\n",
- priv->stats.rx_bytes, priv->stats.tx_bytes);
- seq_printf(m,
- "rx_errors: %12lu\t"
- "tx_errors: %12lu\n",
- priv->stats.rx_errors,
- priv->stats.tx_errors);
- seq_printf(m,
- "rx_dropped: %12lu\t"
- "tx_dropped: %12lu\n",
- priv->stats.rx_dropped,
- priv->stats.tx_dropped);
- seq_printf(m,
- "rx_length_errors: %12lu\t"
- "tx_aborted_errors: %12lu\n",
- priv->stats.rx_length_errors,
- priv->stats.tx_aborted_errors);
- seq_printf(m,
- "rx_over_errors: %12lu\t"
- "tx_carrier_errors: %12lu\n",
- priv->stats.rx_over_errors,
- priv->stats.tx_carrier_errors);
- seq_printf(m,
- "rx_crc_errors: %12lu\t"
- "tx_fifo_errors: %12lu\n",
- priv->stats.rx_crc_errors,
- priv->stats.tx_fifo_errors);
- seq_printf(m,
- "rx_frame_errors: %12lu\t"
- "tx_heartbeat_errors: %12lu\n",
- priv->stats.rx_frame_errors,
- priv->stats.tx_heartbeat_errors);
- seq_printf(m,
- "rx_fifo_errors: %12lu\t"
- "tx_window_errors: %12lu\n",
- priv->stats.rx_fifo_errors,
- priv->stats.tx_window_errors);
- seq_printf(m,
- "rx_missed_errors: %12lu\t"
- "multicast: %12lu\n",
- priv->stats.rx_missed_errors,
- priv->stats.multicast);
- }
- }
-
- return 0;
-}
-
-/**
- * /proc/octeon_ethernet_stats was openned. Use the single_open iterator
- *
- * @inode:
- * @file:
- * Returns
- */
-static int cvm_oct_stats_open(struct inode *inode, struct file *file)
-{
- return single_open(file, cvm_oct_stats_show, NULL);
-}
-
-static const struct file_operations cvm_oct_stats_operations = {
- .open = cvm_oct_stats_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-void cvm_oct_proc_initialize(void)
-{
- struct proc_dir_entry *entry =
- create_proc_entry("octeon_ethernet_stats", 0, NULL);
- if (entry)
- entry->proc_fops = &cvm_oct_stats_operations;
-}
-
-void cvm_oct_proc_shutdown(void)
-{
- remove_proc_entry("octeon_ethernet_stats", NULL);
-}
diff --git a/drivers/staging/octeon/ethernet-proc.h b/drivers/staging/octeon/ethernet-proc.h
deleted file mode 100644
index 82c7d9f78bc..00000000000
--- a/drivers/staging/octeon/ethernet-proc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*********************************************************************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2007 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
-*********************************************************************/
-
-void cvm_oct_proc_initialize(void);
-void cvm_oct_proc_shutdown(void);
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c
index 3820f1ec11d..a0d4d4b98bd 100644
--- a/drivers/staging/octeon/ethernet-rgmii.c
+++ b/drivers/staging/octeon/ethernet-rgmii.c
@@ -26,7 +26,7 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
+#include <linux/phy.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
@@ -48,14 +48,20 @@ static int number_rgmii_ports;
static void cvm_oct_rgmii_poll(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
- unsigned long flags;
+ unsigned long flags = 0;
cvmx_helper_link_info_t link_info;
+ int use_global_register_lock = (priv->phydev == NULL);
- /*
- * Take the global register lock since we are going to touch
- * registers that affect more than one port.
- */
- spin_lock_irqsave(&global_register_lock, flags);
+ BUG_ON(in_interrupt());
+ if (use_global_register_lock) {
+ /*
+ * Take the global register lock since we are going to
+ * touch registers that affect more than one port.
+ */
+ spin_lock_irqsave(&global_register_lock, flags);
+ } else {
+ mutex_lock(&priv->phydev->bus->mdio_lock);
+ }
link_info = cvmx_helper_link_get(priv->port);
if (link_info.u64 == priv->link_info) {
@@ -115,7 +121,11 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
dev->name);
}
}
- spin_unlock_irqrestore(&global_register_lock, flags);
+
+ if (use_global_register_lock)
+ spin_unlock_irqrestore(&global_register_lock, flags);
+ else
+ mutex_unlock(&priv->phydev->bus->mdio_lock);
return;
}
@@ -151,7 +161,12 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
link_info = cvmx_helper_link_autoconf(priv->port);
priv->link_info = link_info.u64;
}
- spin_unlock_irqrestore(&global_register_lock, flags);
+
+ if (use_global_register_lock)
+ spin_unlock_irqrestore(&global_register_lock, flags);
+ else {
+ mutex_unlock(&priv->phydev->bus->mdio_lock);
+ }
if (priv->phydev == NULL) {
/* Tell core. */
@@ -213,8 +228,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
struct net_device *dev =
cvm_oct_device[cvmx_helper_get_ipd_port
(interface, index)];
- if (dev)
- cvm_oct_rgmii_poll(dev);
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_work(cvm_oct_poll_queue, &priv->port_work);
+
gmx_rx_int_reg.u64 = 0;
gmx_rx_int_reg.s.phy_dupx = 1;
gmx_rx_int_reg.s.phy_link = 1;
@@ -252,8 +270,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
struct net_device *dev =
cvm_oct_device[cvmx_helper_get_ipd_port
(interface, index)];
- if (dev)
- cvm_oct_rgmii_poll(dev);
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_work(cvm_oct_poll_queue, &priv->port_work);
+
gmx_rx_int_reg.u64 = 0;
gmx_rx_int_reg.s.phy_dupx = 1;
gmx_rx_int_reg.s.phy_link = 1;
@@ -302,6 +323,12 @@ int cvm_oct_rgmii_stop(struct net_device *dev)
return 0;
}
+static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
+{
+ struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, port_work);
+ cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
+}
+
int cvm_oct_rgmii_init(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
@@ -309,7 +336,7 @@ int cvm_oct_rgmii_init(struct net_device *dev)
cvm_oct_common_init(dev);
dev->netdev_ops->ndo_stop(dev);
-
+ INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
/*
* Due to GMX errata in CN3XXX series chips, it is necessary
* to take the link down immediately when the PHY changes
@@ -397,4 +424,5 @@ void cvm_oct_rgmii_uninit(struct net_device *dev)
number_rgmii_ports--;
if (number_rgmii_ports == 0)
free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
+ cancel_work_sync(&priv->port_work);
}
diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
index 1b237b7e689..cb38f9eb2cc 100644
--- a/drivers/staging/octeon/ethernet-rx.c
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2007 Cavium Networks
+ * Copyright (c) 2003-2010 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
@@ -27,16 +27,14 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cache.h>
+#include <linux/cpumask.h>
#include <linux/netdevice.h>
#include <linux/init.h>
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/string.h>
#include <linux/prefetch.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/seq_file.h>
-#include <linux/proc_fs.h>
+#include <linux/smp.h>
#include <net/dst.h>
#ifdef CONFIG_XFRM
#include <linux/xfrm.h>
@@ -48,8 +46,9 @@
#include <asm/octeon/octeon.h>
#include "ethernet-defines.h"
-#include "octeon-ethernet.h"
#include "ethernet-mem.h"
+#include "ethernet-rx.h"
+#include "octeon-ethernet.h"
#include "ethernet-util.h"
#include "cvmx-helper.h"
@@ -61,62 +60,88 @@
#include "cvmx-gmxx-defs.h"
-struct cvm_tasklet_wrapper {
- struct tasklet_struct t;
-};
+struct cvm_napi_wrapper {
+ struct napi_struct napi;
+} ____cacheline_aligned_in_smp;
-/*
- * Aligning the tasklet_struct on cachline boundries seems to decrease
- * throughput even though in theory it would reduce contantion on the
- * cache lines containing the locks.
- */
+static struct cvm_napi_wrapper cvm_oct_napi[NR_CPUS] __cacheline_aligned_in_smp;
-static struct cvm_tasklet_wrapper cvm_oct_tasklet[NR_CPUS];
+struct cvm_oct_core_state {
+ int baseline_cores;
+ /*
+ * The number of additional cores that could be processing
+ * input packtes.
+ */
+ atomic_t available_cores;
+ cpumask_t cpu_state;
+} ____cacheline_aligned_in_smp;
-/**
- * Interrupt handler. The interrupt occurs whenever the POW
- * transitions from 0->1 packets in our group.
- *
- * @cpl:
- * @dev_id:
- * @regs:
- * Returns
- */
-irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id)
+static struct cvm_oct_core_state core_state __cacheline_aligned_in_smp;
+
+static void cvm_oct_enable_napi(void *_)
{
- /* Acknowledge the interrupt */
- if (INTERRUPT_LIMIT)
- cvmx_write_csr(CVMX_POW_WQ_INT, 1 << pow_receive_group);
- else
- cvmx_write_csr(CVMX_POW_WQ_INT, 0x10001 << pow_receive_group);
- preempt_disable();
- tasklet_schedule(&cvm_oct_tasklet[smp_processor_id()].t);
- preempt_enable();
- return IRQ_HANDLED;
+ int cpu = smp_processor_id();
+ napi_schedule(&cvm_oct_napi[cpu].napi);
+}
+
+static void cvm_oct_enable_one_cpu(void)
+{
+ int v;
+ int cpu;
+
+ /* Check to see if more CPUs are available for receive processing... */
+ v = atomic_sub_if_positive(1, &core_state.available_cores);
+ if (v < 0)
+ return;
+
+ /* ... if a CPU is available, Turn on NAPI polling for that CPU. */
+ for_each_online_cpu(cpu) {
+ if (!cpu_test_and_set(cpu, core_state.cpu_state)) {
+ v = smp_call_function_single(cpu, cvm_oct_enable_napi,
+ NULL, 0);
+ if (v)
+ panic("Can't enable NAPI.");
+ break;
+ }
+ }
+}
+
+static void cvm_oct_no_more_work(void)
+{
+ int cpu = smp_processor_id();
+
+ /*
+ * CPU zero is special. It always has the irq enabled when
+ * waiting for incoming packets.
+ */
+ if (cpu == 0) {
+ enable_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group);
+ return;
+ }
+
+ cpu_clear(cpu, core_state.cpu_state);
+ atomic_add(1, &core_state.available_cores);
}
-#ifdef CONFIG_NET_POLL_CONTROLLER
/**
- * This is called when the kernel needs to manually poll the
- * device. For Octeon, this is simply calling the interrupt
- * handler. We actually poll all the devices, not just the
- * one supplied.
+ * cvm_oct_do_interrupt - interrupt handler.
+ *
+ * The interrupt occurs whenever the POW has packets in our group.
*
- * @dev: Device to poll. Unused
*/
-void cvm_oct_poll_controller(struct net_device *dev)
+static irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id)
{
- preempt_disable();
- tasklet_schedule(&cvm_oct_tasklet[smp_processor_id()].t);
- preempt_enable();
+ /* Disable the IRQ and start napi_poll. */
+ disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group);
+ cvm_oct_enable_napi(NULL);
+
+ return IRQ_HANDLED;
}
-#endif
/**
- * This is called on receive errors, and determines if the packet
- * can be dropped early-on in cvm_oct_tasklet_rx().
- *
+ * cvm_oct_check_rcv_error - process receive errors
* @work: Work queue entry pointing to the packet.
+ *
* Returns Non-zero if the packet can be dropped, zero otherwise.
*/
static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
@@ -199,19 +224,20 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
}
/**
- * Tasklet function that is scheduled on a core when an interrupt occurs.
+ * cvm_oct_napi_poll - the NAPI poll function.
+ * @napi: The NAPI instance, or null if called from cvm_oct_poll_controller
+ * @budget: Maximum number of packets to receive.
*
- * @unused:
+ * Returns the number of packets processed.
*/
-void cvm_oct_tasklet_rx(unsigned long unused)
+static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
{
- const int coreid = cvmx_get_core_num();
- uint64_t old_group_mask;
- uint64_t old_scratch;
- int rx_count = 0;
- int number_to_free;
- int num_freed;
- int packet_not_copied;
+ const int coreid = cvmx_get_core_num();
+ uint64_t old_group_mask;
+ uint64_t old_scratch;
+ int rx_count = 0;
+ int did_work_request = 0;
+ int packet_not_copied;
/* Prefetch cvm_oct_device since we know we need it soon */
prefetch(cvm_oct_device);
@@ -227,59 +253,63 @@ void cvm_oct_tasklet_rx(unsigned long unused)
cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid),
(old_group_mask & ~0xFFFFull) | 1 << pow_receive_group);
- if (USE_ASYNC_IOBDMA)
+ if (USE_ASYNC_IOBDMA) {
cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+ did_work_request = 1;
+ }
- while (1) {
+ while (rx_count < budget) {
struct sk_buff *skb = NULL;
+ struct sk_buff **pskb = NULL;
int skb_in_hw;
cvmx_wqe_t *work;
- if (USE_ASYNC_IOBDMA) {
+ if (USE_ASYNC_IOBDMA && did_work_request)
work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH);
- } else {
- if ((INTERRUPT_LIMIT == 0)
- || likely(rx_count < MAX_RX_PACKETS))
- work =
- cvmx_pow_work_request_sync
- (CVMX_POW_NO_WAIT);
- else
- work = NULL;
- }
+ else
+ work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT);
+
prefetch(work);
- if (work == NULL)
+ did_work_request = 0;
+ if (work == NULL) {
+ union cvmx_pow_wq_int wq_int;
+ wq_int.u64 = 0;
+ wq_int.s.iq_dis = 1 << pow_receive_group;
+ wq_int.s.wq_int = 1 << pow_receive_group;
+ cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64);
break;
+ }
+ pskb = (struct sk_buff **)(cvm_oct_get_buffer_ptr(work->packet_ptr) - sizeof(void *));
+ prefetch(pskb);
- /*
- * Limit each core to processing MAX_RX_PACKETS
- * packets without a break. This way the RX can't
- * starve the TX task.
- */
- if (USE_ASYNC_IOBDMA) {
-
- if ((INTERRUPT_LIMIT == 0)
- || likely(rx_count < MAX_RX_PACKETS))
- cvmx_pow_work_request_async_nocheck
- (CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
- else {
- cvmx_scratch_write64(CVMX_SCR_SCRATCH,
- 0x8000000000000000ull);
- cvmx_pow_tag_sw_null_nocheck();
- }
+ if (USE_ASYNC_IOBDMA && rx_count < (budget - 1)) {
+ cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+ did_work_request = 1;
+ }
+
+ if (rx_count == 0) {
+ /*
+ * First time through, see if there is enough
+ * work waiting to merit waking another
+ * CPU.
+ */
+ union cvmx_pow_wq_int_cntx counts;
+ int backlog;
+ int cores_in_use = core_state.baseline_cores - atomic_read(&core_state.available_cores);
+ counts.u64 = cvmx_read_csr(CVMX_POW_WQ_INT_CNTX(pow_receive_group));
+ backlog = counts.s.iq_cnt + counts.s.ds_cnt;
+ if (backlog > budget * cores_in_use && napi != NULL)
+ cvm_oct_enable_one_cpu();
}
skb_in_hw = USE_SKBUFFS_IN_HW && work->word2.s.bufs == 1;
if (likely(skb_in_hw)) {
- skb =
- *(struct sk_buff
- **)(cvm_oct_get_buffer_ptr(work->packet_ptr) -
- sizeof(void *));
+ skb = *pskb;
prefetch(&skb->head);
prefetch(&skb->len);
}
prefetch(cvm_oct_device[work->ipprt]);
- rx_count++;
/* Immediately throw away all packets with receive errors */
if (unlikely(work->word2.snoip.rcv_error)) {
if (cvm_oct_check_rcv_error(work))
@@ -292,39 +322,27 @@ void cvm_oct_tasklet_rx(unsigned long unused)
* buffer.
*/
if (likely(skb_in_hw)) {
- /*
- * This calculation was changed in case the
- * skb header is using a different address
- * aliasing type than the buffer. It doesn't
- * make any differnece now, but the new one is
- * more correct.
- */
- skb->data =
- skb->head + work->packet_ptr.s.addr -
- cvmx_ptr_to_phys(skb->head);
+ skb->data = skb->head + work->packet_ptr.s.addr - cvmx_ptr_to_phys(skb->head);
prefetch(skb->data);
skb->len = work->len;
skb_set_tail_pointer(skb, skb->len);
packet_not_copied = 1;
} else {
-
/*
* We have to copy the packet. First allocate
* an skbuff for it.
*/
skb = dev_alloc_skb(work->len);
if (!skb) {
- DEBUGPRINT("Port %d failed to allocate "
- "skbuff, packet dropped\n",
- work->ipprt);
+ DEBUGPRINT("Port %d failed to allocate skbuff, packet dropped\n",
+ work->ipprt);
cvm_oct_free_work(work);
continue;
}
/*
* Check if we've received a packet that was
- * entirely stored in the work entry. This is
- * untested.
+ * entirely stored in the work entry.
*/
if (unlikely(work->word2.s.bufs == 0)) {
uint8_t *ptr = work->packet_data;
@@ -343,15 +361,13 @@ void cvm_oct_tasklet_rx(unsigned long unused)
/* No packet buffers to free */
} else {
int segments = work->word2.s.bufs;
- union cvmx_buf_ptr segment_ptr =
- work->packet_ptr;
+ union cvmx_buf_ptr segment_ptr = work->packet_ptr;
int len = work->len;
while (segments--) {
union cvmx_buf_ptr next_ptr =
- *(union cvmx_buf_ptr *)
- cvmx_phys_to_ptr(segment_ptr.s.
- addr - 8);
+ *(union cvmx_buf_ptr *)cvmx_phys_to_ptr(segment_ptr.s.addr - 8);
+
/*
* Octeon Errata PKI-100: The segment size is
* wrong. Until it is fixed, calculate the
@@ -361,22 +377,18 @@ void cvm_oct_tasklet_rx(unsigned long unused)
* one: int segment_size =
* segment_ptr.s.size;
*/
- int segment_size =
- CVMX_FPA_PACKET_POOL_SIZE -
- (segment_ptr.s.addr -
- (((segment_ptr.s.addr >> 7) -
- segment_ptr.s.back) << 7));
- /* Don't copy more than what is left
- in the packet */
+ int segment_size = CVMX_FPA_PACKET_POOL_SIZE -
+ (segment_ptr.s.addr - (((segment_ptr.s.addr >> 7) - segment_ptr.s.back) << 7));
+ /*
+ * Don't copy more than what
+ * is left in the packet.
+ */
if (segment_size > len)
segment_size = len;
/* Copy the data into the packet */
memcpy(skb_put(skb, segment_size),
- cvmx_phys_to_ptr(segment_ptr.s.
- addr),
+ cvmx_phys_to_ptr(segment_ptr.s.addr),
segment_size);
- /* Reduce the amount of bytes left
- to copy */
len -= segment_size;
segment_ptr = next_ptr;
}
@@ -389,16 +401,15 @@ void cvm_oct_tasklet_rx(unsigned long unused)
struct net_device *dev = cvm_oct_device[work->ipprt];
struct octeon_ethernet *priv = netdev_priv(dev);
- /* Only accept packets for devices
- that are currently up */
+ /*
+ * Only accept packets for devices that are
+ * currently up.
+ */
if (likely(dev->flags & IFF_UP)) {
skb->protocol = eth_type_trans(skb, dev);
skb->dev = dev;
- if (unlikely
- (work->word2.s.not_IP
- || work->word2.s.IP_exc
- || work->word2.s.L4_error))
+ if (unlikely(work->word2.s.not_IP || work->word2.s.IP_exc || work->word2.s.L4_error))
skb->ip_summed = CHECKSUM_NONE;
else
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -414,15 +425,13 @@ void cvm_oct_tasklet_rx(unsigned long unused)
#endif
}
netif_receive_skb(skb);
+ rx_count++;
} else {
+ /* Drop any packet received for a device that isn't up */
/*
- * Drop any packet received for a
- * device that isn't up.
- */
- /*
- DEBUGPRINT("%s: Device not up, packet dropped\n",
- dev->name);
- */
+ DEBUGPRINT("%s: Device not up, packet dropped\n",
+ dev->name);
+ */
#ifdef CONFIG_64BIT
atomic64_add(1, (atomic64_t *)&priv->stats.rx_dropped);
#else
@@ -435,9 +444,8 @@ void cvm_oct_tasklet_rx(unsigned long unused)
* Drop any packet received for a device that
* doesn't exist.
*/
- DEBUGPRINT("Port %d not controlled by Linux, packet "
- "dropped\n",
- work->ipprt);
+ DEBUGPRINT("Port %d not controlled by Linux, packet dropped\n",
+ work->ipprt);
dev_kfree_skb_irq(skb);
}
/*
@@ -459,47 +467,93 @@ void cvm_oct_tasklet_rx(unsigned long unused)
cvm_oct_free_work(work);
}
}
-
/* Restore the original POW group mask */
cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
if (USE_ASYNC_IOBDMA) {
/* Restore the scratch area */
cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
}
+ cvm_oct_rx_refill_pool(0);
- if (USE_SKBUFFS_IN_HW) {
- /* Refill the packet buffer pool */
- number_to_free =
- cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
-
- if (number_to_free > 0) {
- cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
- -number_to_free);
- num_freed =
- cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL,
- CVMX_FPA_PACKET_POOL_SIZE,
- number_to_free);
- if (num_freed != number_to_free) {
- cvmx_fau_atomic_add32
- (FAU_NUM_PACKET_BUFFERS_TO_FREE,
- number_to_free - num_freed);
- }
- }
+ if (rx_count < budget && napi != NULL) {
+ /* No more work */
+ napi_complete(napi);
+ cvm_oct_no_more_work();
}
+ return rx_count;
}
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * cvm_oct_poll_controller - poll for receive packets
+ * device.
+ *
+ * @dev: Device to poll. Unused
+ */
+void cvm_oct_poll_controller(struct net_device *dev)
+{
+ cvm_oct_napi_poll(NULL, 16);
+}
+#endif
+
void cvm_oct_rx_initialize(void)
{
int i;
- /* Initialize all of the tasklets */
- for (i = 0; i < NR_CPUS; i++)
- tasklet_init(&cvm_oct_tasklet[i].t, cvm_oct_tasklet_rx, 0);
+ struct net_device *dev_for_napi = NULL;
+ union cvmx_pow_wq_int_thrx int_thr;
+ union cvmx_pow_wq_int_pc int_pc;
+
+ for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) {
+ if (cvm_oct_device[i]) {
+ dev_for_napi = cvm_oct_device[i];
+ break;
+ }
+ }
+
+ if (NULL == dev_for_napi)
+ panic("No net_devices were allocated.");
+
+ if (max_rx_cpus > 1 && max_rx_cpus < num_online_cpus())
+ atomic_set(&core_state.available_cores, max_rx_cpus);
+ else
+ atomic_set(&core_state.available_cores, num_online_cpus());
+ core_state.baseline_cores = atomic_read(&core_state.available_cores);
+
+ core_state.cpu_state = CPU_MASK_NONE;
+ for_each_possible_cpu(i) {
+ netif_napi_add(dev_for_napi, &cvm_oct_napi[i].napi,
+ cvm_oct_napi_poll, rx_napi_weight);
+ napi_enable(&cvm_oct_napi[i].napi);
+ }
+ /* Register an IRQ hander for to receive POW interrupts */
+ i = request_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group,
+ cvm_oct_do_interrupt, 0, "Ethernet", cvm_oct_device);
+
+ if (i)
+ panic("Could not acquire Ethernet IRQ %d\n",
+ OCTEON_IRQ_WORKQ0 + pow_receive_group);
+
+ disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group);
+
+ int_thr.u64 = 0;
+ int_thr.s.tc_en = 1;
+ int_thr.s.tc_thr = 1;
+ /* Enable POW interrupt when our port has at least one packet */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), int_thr.u64);
+
+ int_pc.u64 = 0;
+ int_pc.s.pc_thr = 5;
+ cvmx_write_csr(CVMX_POW_WQ_INT_PC, int_pc.u64);
+
+
+ /* Scheduld NAPI now. This will indirectly enable interrupts. */
+ cvm_oct_enable_one_cpu();
}
void cvm_oct_rx_shutdown(void)
{
int i;
- /* Shutdown all of the tasklets */
- for (i = 0; i < NR_CPUS; i++)
- tasklet_kill(&cvm_oct_tasklet[i].t);
+ /* Shutdown all of the NAPIs */
+ for_each_possible_cpu(i)
+ netif_napi_del(&cvm_oct_napi[i].napi);
}
diff --git a/drivers/staging/octeon/ethernet-rx.h b/drivers/staging/octeon/ethernet-rx.h
index a9b72b87a7a..a0743b85d54 100644
--- a/drivers/staging/octeon/ethernet-rx.h
+++ b/drivers/staging/octeon/ethernet-rx.h
@@ -24,10 +24,29 @@
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
*********************************************************************/
+#include "cvmx-fau.h"
-irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id);
void cvm_oct_poll_controller(struct net_device *dev);
-void cvm_oct_tasklet_rx(unsigned long unused);
-
void cvm_oct_rx_initialize(void);
void cvm_oct_rx_shutdown(void);
+
+static inline void cvm_oct_rx_refill_pool(int fill_threshold)
+{
+ int number_to_free;
+ int num_freed;
+ /* Refill the packet buffer pool */
+ number_to_free =
+ cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ if (number_to_free > fill_threshold) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ -number_to_free);
+ num_freed = cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL,
+ CVMX_FPA_PACKET_POOL_SIZE,
+ number_to_free);
+ if (num_freed != number_to_free) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ number_to_free - num_freed);
+ }
+ }
+}
diff --git a/drivers/staging/octeon/ethernet-sgmii.c b/drivers/staging/octeon/ethernet-sgmii.c
index 6061d01eca2..2d8589eb461 100644
--- a/drivers/staging/octeon/ethernet-sgmii.c
+++ b/drivers/staging/octeon/ethernet-sgmii.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c
index 00dc0f4bad1..b58b8971f93 100644
--- a/drivers/staging/octeon/ethernet-spi.c
+++ b/drivers/staging/octeon/ethernet-spi.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index 535294105f6..afc2b734d55 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2007 Cavium Networks
+ * Copyright (c) 2003-2010 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
@@ -31,10 +31,6 @@
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/string.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/seq_file.h>
-#include <linux/proc_fs.h>
#include <net/dst.h>
#ifdef CONFIG_XFRM
#include <linux/xfrm.h>
@@ -52,11 +48,14 @@
#include "cvmx-wqe.h"
#include "cvmx-fau.h"
+#include "cvmx-pip.h"
#include "cvmx-pko.h"
#include "cvmx-helper.h"
#include "cvmx-gmxx-defs.h"
+#define CVM_OCT_SKB_CB(skb) ((u64 *)((skb)->cb))
+
/*
* You can define GET_SKBUFF_QOS() to override how the skbuff output
* function determines which output queue is used. The default
@@ -68,12 +67,81 @@
#define GET_SKBUFF_QOS(skb) 0
#endif
+static void cvm_oct_tx_do_cleanup(unsigned long arg);
+static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0);
+
+/* Maximum number of SKBs to try to free per xmit packet. */
+#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2)
+
+static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
+{
+ int32_t undo;
+ undo = skb_to_free > 0 ? MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE;
+ if (undo > 0)
+ cvmx_fau_atomic_add32(fau, -undo);
+ skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? MAX_SKB_TO_FREE : -skb_to_free;
+ return skb_to_free;
+}
+
+static void cvm_oct_kick_tx_poll_watchdog(void)
+{
+ union cvmx_ciu_timx ciu_timx;
+ ciu_timx.u64 = 0;
+ ciu_timx.s.one_shot = 1;
+ ciu_timx.s.len = cvm_oct_tx_poll_interval;
+ cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64);
+}
+
+void cvm_oct_free_tx_skbs(struct net_device *dev)
+{
+ int32_t skb_to_free;
+ int qos, queues_per_port;
+ int total_freed = 0;
+ int total_remaining = 0;
+ unsigned long flags;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ queues_per_port = cvmx_pko_get_num_queues(priv->port);
+ /* Drain any pending packets in the free list */
+ for (qos = 0; qos < queues_per_port; qos++) {
+ if (skb_queue_len(&priv->tx_free_list[qos]) == 0)
+ continue;
+ skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4, MAX_SKB_TO_FREE);
+ skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
+
+
+ total_freed += skb_to_free;
+ if (skb_to_free > 0) {
+ struct sk_buff *to_free_list = NULL;
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ while (skb_to_free > 0) {
+ struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
+ t->next = to_free_list;
+ to_free_list = t;
+ skb_to_free--;
+ }
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+ /* Do the actual freeing outside of the lock. */
+ while (to_free_list) {
+ struct sk_buff *t = to_free_list;
+ to_free_list = to_free_list->next;
+ dev_kfree_skb_any(t);
+ }
+ }
+ total_remaining += skb_queue_len(&priv->tx_free_list[qos]);
+ }
+ if (total_freed >= 0 && netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ if (total_remaining)
+ cvm_oct_kick_tx_poll_watchdog();
+}
+
/**
- * Packet transmit
- *
+ * cvm_oct_xmit - transmit a packet
* @skb: Packet to send
* @dev: Device info structure
- * Returns Always returns zero
+ *
+ * Returns Always returns NETDEV_TX_OK
*/
int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
{
@@ -81,13 +149,15 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
union cvmx_buf_ptr hw_buffer;
uint64_t old_scratch;
uint64_t old_scratch2;
- int dropped;
int qos;
- int queue_it_up;
+ int i;
+ enum {QUEUE_CORE, QUEUE_HW, QUEUE_DROP} queue_type;
struct octeon_ethernet *priv = netdev_priv(dev);
+ struct sk_buff *to_free_list;
int32_t skb_to_free;
- int32_t undo;
int32_t buffers_to_free;
+ u32 total_to_clean;
+ unsigned long flags;
#if REUSE_SKBUFFS_WITHOUT_FREE
unsigned char *fpa_head;
#endif
@@ -98,9 +168,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
*/
prefetch(priv);
- /* Start off assuming no drop */
- dropped = 0;
-
/*
* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to
* completely remove "qos" in the event neither interface
@@ -135,6 +202,28 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
}
/*
+ * We have space for 6 segment pointers, If there will be more
+ * than that, we must linearize.
+ */
+ if (unlikely(skb_shinfo(skb)->nr_frags > 5)) {
+ if (unlikely(__skb_linearize(skb))) {
+ queue_type = QUEUE_DROP;
+ if (USE_ASYNC_IOBDMA) {
+ /* Get the number of skbuffs in use by the hardware */
+ CVMX_SYNCIOBDMA;
+ skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ } else {
+ /* Get the number of skbuffs in use by the hardware */
+ skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
+ MAX_SKB_TO_FREE);
+ }
+ skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau + qos * 4);
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ goto skip_xmit;
+ }
+ }
+
+ /*
* The CN3XXX series of parts has an errata (GMX-401) which
* causes the GMX block to hang if a collision occurs towards
* the end of a <68 byte packet. As a workaround for this, we
@@ -162,13 +251,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
- /* Build the PKO buffer pointer */
- hw_buffer.u64 = 0;
- hw_buffer.s.addr = cvmx_ptr_to_phys(skb->data);
- hw_buffer.s.pool = 0;
- hw_buffer.s.size =
- (unsigned long)skb_end_pointer(skb) - (unsigned long)skb->head;
-
/* Build the PKO command */
pko_command.u64 = 0;
pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
@@ -178,7 +260,31 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
pko_command.s.subone0 = 1;
pko_command.s.dontfree = 1;
- pko_command.s.reg0 = priv->fau + qos * 4;
+
+ /* Build the PKO buffer pointer */
+ hw_buffer.u64 = 0;
+ if (skb_shinfo(skb)->nr_frags == 0) {
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = skb->len;
+ } else {
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = skb_headlen(skb);
+ CVM_OCT_SKB_CB(skb)[0] = hw_buffer.u64;
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ struct skb_frag_struct *fs = skb_shinfo(skb)->frags + i;
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)(page_address(fs->page) + fs->page_offset));
+ hw_buffer.s.size = fs->size;
+ CVM_OCT_SKB_CB(skb)[i + 1] = hw_buffer.u64;
+ }
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)CVM_OCT_SKB_CB(skb));
+ hw_buffer.s.size = skb_shinfo(skb)->nr_frags + 1;
+ pko_command.s.segs = skb_shinfo(skb)->nr_frags + 1;
+ pko_command.s.gather = 1;
+ goto dont_put_skbuff_in_hw;
+ }
+
/*
* See if we can put this skb in the FPA pool. Any strange
* behavior from the Linux networking stack will most likely
@@ -190,7 +296,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
* shown a 25% increase in performance under some loads.
*/
#if REUSE_SKBUFFS_WITHOUT_FREE
- fpa_head = skb->head + 128 - ((unsigned long)skb->head & 0x7f);
+ fpa_head = skb->head + 256 - ((unsigned long)skb->head & 0x7f);
if (unlikely(skb->data < fpa_head)) {
/*
* printk("TX buffer beginning can't meet FPA
@@ -248,10 +354,9 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
* We can use this buffer in the FPA. We don't need the FAU
* update anymore
*/
- pko_command.s.reg0 = 0;
pko_command.s.dontfree = 0;
- hw_buffer.s.back = (skb->data - fpa_head) >> 7;
+ hw_buffer.s.back = ((unsigned long)skb->data >> 7) - ((unsigned long)fpa_head >> 7);
*(struct sk_buff **)(fpa_head - sizeof(void *)) = skb;
/*
@@ -272,16 +377,16 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
skb->tc_verd = 0;
#endif /* CONFIG_NET_CLS_ACT */
#endif /* CONFIG_NET_SCHED */
+#endif /* REUSE_SKBUFFS_WITHOUT_FREE */
dont_put_skbuff_in_hw:
-#endif /* REUSE_SKBUFFS_WITHOUT_FREE */
/* Check if we can use the hardware checksumming */
if (USE_HW_TCPUDP_CHECKSUM && (skb->protocol == htons(ETH_P_IP)) &&
(ip_hdr(skb)->version == 4) && (ip_hdr(skb)->ihl == 5) &&
((ip_hdr(skb)->frag_off == 0) || (ip_hdr(skb)->frag_off == 1 << 14))
- && ((ip_hdr(skb)->protocol == IP_PROTOCOL_TCP)
- || (ip_hdr(skb)->protocol == IP_PROTOCOL_UDP))) {
+ && ((ip_hdr(skb)->protocol == IPPROTO_TCP)
+ || (ip_hdr(skb)->protocol == IPPROTO_UDP))) {
/* Use hardware checksum calc */
pko_command.s.ipoffp1 = sizeof(struct ethhdr) + 1;
}
@@ -299,89 +404,116 @@ dont_put_skbuff_in_hw:
cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
}
- /*
- * We try to claim MAX_SKB_TO_FREE buffers. If there were not
- * that many available, we have to un-claim (undo) any that
- * were in excess. If skb_to_free is positive we will free
- * that many buffers.
- */
- undo = skb_to_free > 0 ?
- MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE;
- if (undo > 0)
- cvmx_fau_atomic_add32(priv->fau+qos*4, -undo);
- skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ?
- MAX_SKB_TO_FREE : -skb_to_free;
+ skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
/*
* If we're sending faster than the receive can free them then
* don't do the HW free.
*/
- if ((buffers_to_free < -100) && !pko_command.s.dontfree) {
+ if ((buffers_to_free < -100) && !pko_command.s.dontfree)
pko_command.s.dontfree = 1;
- pko_command.s.reg0 = priv->fau + qos * 4;
+
+ if (pko_command.s.dontfree) {
+ queue_type = QUEUE_CORE;
+ pko_command.s.reg0 = priv->fau+qos*4;
+ } else {
+ queue_type = QUEUE_HW;
}
+ if (USE_ASYNC_IOBDMA)
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);
- cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos,
- CVMX_PKO_LOCK_CMD_QUEUE);
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
/* Drop this packet if we have too many already queued to the HW */
- if (unlikely
- (skb_queue_len(&priv->tx_free_list[qos]) >= MAX_OUT_QUEUE_DEPTH)) {
- /*
- DEBUGPRINT("%s: Tx dropped. Too many queued\n", dev->name);
- */
- dropped = 1;
+ if (unlikely(skb_queue_len(&priv->tx_free_list[qos]) >= MAX_OUT_QUEUE_DEPTH)) {
+ if (dev->tx_queue_len != 0) {
+ /* Drop the lock when notifying the core. */
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+ netif_stop_queue(dev);
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ } else {
+ /* If not using normal queueing. */
+ queue_type = QUEUE_DROP;
+ goto skip_xmit;
+ }
}
+
+ cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos,
+ CVMX_PKO_LOCK_NONE);
+
/* Send the packet to the output queue */
- else if (unlikely
- (cvmx_pko_send_packet_finish
- (priv->port, priv->queue + qos, pko_command, hw_buffer,
- CVMX_PKO_LOCK_CMD_QUEUE))) {
+ if (unlikely(cvmx_pko_send_packet_finish(priv->port,
+ priv->queue + qos,
+ pko_command, hw_buffer,
+ CVMX_PKO_LOCK_NONE))) {
DEBUGPRINT("%s: Failed to send the packet\n", dev->name);
- dropped = 1;
+ queue_type = QUEUE_DROP;
+ }
+skip_xmit:
+ to_free_list = NULL;
+
+ switch (queue_type) {
+ case QUEUE_DROP:
+ skb->next = to_free_list;
+ to_free_list = skb;
+ priv->stats.tx_dropped++;
+ break;
+ case QUEUE_HW:
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1);
+ break;
+ case QUEUE_CORE:
+ __skb_queue_tail(&priv->tx_free_list[qos], skb);
+ break;
+ default:
+ BUG();
+ }
+
+ while (skb_to_free > 0) {
+ struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
+ t->next = to_free_list;
+ to_free_list = t;
+ skb_to_free--;
+ }
+
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+
+ /* Do the actual freeing outside of the lock. */
+ while (to_free_list) {
+ struct sk_buff *t = to_free_list;
+ to_free_list = to_free_list->next;
+ dev_kfree_skb_any(t);
}
if (USE_ASYNC_IOBDMA) {
+ CVMX_SYNCIOBDMA;
+ total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
/* Restore the scratch area */
cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
- }
-
- queue_it_up = 0;
- if (unlikely(dropped)) {
- dev_kfree_skb_any(skb);
- priv->stats.tx_dropped++;
} else {
- if (USE_SKBUFFS_IN_HW) {
- /* Put this packet on the queue to be freed later */
- if (pko_command.s.dontfree)
- queue_it_up = 1;
- else
- cvmx_fau_atomic_add32
- (FAU_NUM_PACKET_BUFFERS_TO_FREE, -1);
- } else {
- /* Put this packet on the queue to be freed later */
- queue_it_up = 1;
- }
+ total_to_clean = cvmx_fau_fetch_and_add32(FAU_TOTAL_TX_TO_CLEAN, 1);
}
- if (queue_it_up) {
- spin_lock(&priv->tx_free_list[qos].lock);
- __skb_queue_tail(&priv->tx_free_list[qos], skb);
- cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 0);
- spin_unlock(&priv->tx_free_list[qos].lock);
- } else {
- cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1);
+ if (total_to_clean & 0x3ff) {
+ /*
+ * Schedule the cleanup tasklet every 1024 packets for
+ * the pathological case of high traffic on one port
+ * delaying clean up of packets on a different port
+ * that is blocked waiting for the cleanup.
+ */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
}
- return 0;
+ cvm_oct_kick_tx_poll_watchdog();
+
+ return NETDEV_TX_OK;
}
/**
- * Packet transmit to the POW
- *
+ * cvm_oct_xmit_pow - transmit a packet to the POW
* @skb: Packet to send
* @dev: Device info structure
+
* Returns Always returns zero
*/
int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
@@ -459,8 +591,8 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
work->word2.s.dec_ipcomp = 0; /* FIXME */
#endif
work->word2.s.tcp_or_udp =
- (ip_hdr(skb)->protocol == IP_PROTOCOL_TCP)
- || (ip_hdr(skb)->protocol == IP_PROTOCOL_UDP);
+ (ip_hdr(skb)->protocol == IPPROTO_TCP)
+ || (ip_hdr(skb)->protocol == IPPROTO_UDP);
#if 0
/* FIXME */
work->word2.s.dec_ipsec = 0;
@@ -529,116 +661,63 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
}
/**
- * Transmit a work queue entry out of the ethernet port. Both
- * the work queue entry and the packet data can optionally be
- * freed. The work will be freed on error as well.
- *
- * @dev: Device to transmit out.
- * @work_queue_entry:
- * Work queue entry to send
- * @do_free: True if the work queue entry and packet data should be
- * freed. If false, neither will be freed.
- * @qos: Index into the queues for this port to transmit on. This
- * is used to implement QoS if their are multiple queues per
- * port. This parameter must be between 0 and the number of
- * queues per port minus 1. Values outside of this range will
- * be change to zero.
+ * cvm_oct_tx_shutdown_dev - free all skb that are currently queued for TX.
+ * @dev: Device being shutdown
*
- * Returns Zero on success, negative on failure.
*/
-int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
- int do_free, int qos)
+void cvm_oct_tx_shutdown_dev(struct net_device *dev)
{
- unsigned long flags;
- union cvmx_buf_ptr hw_buffer;
- cvmx_pko_command_word0_t pko_command;
- int dropped;
struct octeon_ethernet *priv = netdev_priv(dev);
- cvmx_wqe_t *work = work_queue_entry;
+ unsigned long flags;
+ int qos;
- if (!(dev->flags & IFF_UP)) {
- DEBUGPRINT("%s: Device not up\n", dev->name);
- if (do_free)
- cvm_oct_free_work(work);
- return -1;
+ for (qos = 0; qos < 16; qos++) {
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ while (skb_queue_len(&priv->tx_free_list[qos]))
+ dev_kfree_skb_any(__skb_dequeue
+ (&priv->tx_free_list[qos]));
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
}
+}
- /* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to completely
- remove "qos" in the event neither interface supports
- multiple queues per port */
- if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
- (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
- if (qos <= 0)
- qos = 0;
- else if (qos >= cvmx_pko_get_num_queues(priv->port))
- qos = 0;
- } else
- qos = 0;
-
- /* Start off assuming no drop */
- dropped = 0;
-
- local_irq_save(flags);
- cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos,
- CVMX_PKO_LOCK_CMD_QUEUE);
-
- /* Build the PKO buffer pointer */
- hw_buffer.u64 = 0;
- hw_buffer.s.addr = work->packet_ptr.s.addr;
- hw_buffer.s.pool = CVMX_FPA_PACKET_POOL;
- hw_buffer.s.size = CVMX_FPA_PACKET_POOL_SIZE;
- hw_buffer.s.back = work->packet_ptr.s.back;
+static void cvm_oct_tx_do_cleanup(unsigned long arg)
+{
+ int port;
- /* Build the PKO command */
- pko_command.u64 = 0;
- pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
- pko_command.s.dontfree = !do_free;
- pko_command.s.segs = work->word2.s.bufs;
- pko_command.s.total_bytes = work->len;
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ struct net_device *dev = cvm_oct_device[port];
+ cvm_oct_free_tx_skbs(dev);
+ }
+ }
+}
- /* Check if we can use the hardware checksumming */
- if (unlikely(work->word2.s.not_IP || work->word2.s.IP_exc))
- pko_command.s.ipoffp1 = 0;
- else
- pko_command.s.ipoffp1 = sizeof(struct ethhdr) + 1;
+static irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id)
+{
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Do the work in the tasklet. */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ return IRQ_HANDLED;
+}
- /* Send the packet to the output queue */
- if (unlikely
- (cvmx_pko_send_packet_finish
- (priv->port, priv->queue + qos, pko_command, hw_buffer,
- CVMX_PKO_LOCK_CMD_QUEUE))) {
- DEBUGPRINT("%s: Failed to send the packet\n", dev->name);
- dropped = -1;
- }
- local_irq_restore(flags);
+void cvm_oct_tx_initialize(void)
+{
+ int i;
- if (unlikely(dropped)) {
- if (do_free)
- cvm_oct_free_work(work);
- priv->stats.tx_dropped++;
- } else if (do_free)
- cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Register an IRQ hander for to receive CIU_TIMX(1) interrupts */
+ i = request_irq(OCTEON_IRQ_TIMER1,
+ cvm_oct_tx_cleanup_watchdog, 0,
+ "Ethernet", cvm_oct_device);
- return dropped;
+ if (i)
+ panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1);
}
-EXPORT_SYMBOL(cvm_oct_transmit_qos);
-/**
- * This function frees all skb that are currently queued for TX.
- *
- * @dev: Device being shutdown
- */
-void cvm_oct_tx_shutdown(struct net_device *dev)
+void cvm_oct_tx_shutdown(void)
{
- struct octeon_ethernet *priv = netdev_priv(dev);
- unsigned long flags;
- int qos;
-
- for (qos = 0; qos < 16; qos++) {
- spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
- while (skb_queue_len(&priv->tx_free_list[qos]))
- dev_kfree_skb_any(__skb_dequeue
- (&priv->tx_free_list[qos]));
- spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
- }
+ /* Free the interrupt handler */
+ free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device);
}
diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h
index c0bebf750bc..547680c6c37 100644
--- a/drivers/staging/octeon/ethernet-tx.h
+++ b/drivers/staging/octeon/ethernet-tx.h
@@ -29,29 +29,6 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev);
int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
int do_free, int qos);
-void cvm_oct_tx_shutdown(struct net_device *dev);
-
-/**
- * Free dead transmit skbs.
- *
- * @priv: The driver data
- * @skb_to_free: The number of SKBs to free (free none if negative).
- * @qos: The queue to free from.
- * @take_lock: If true, acquire the skb list lock.
- */
-static inline void cvm_oct_free_tx_skbs(struct octeon_ethernet *priv,
- int skb_to_free,
- int qos, int take_lock)
-{
- /* Free skbuffs not in use by the hardware. */
- if (skb_to_free > 0) {
- if (take_lock)
- spin_lock(&priv->tx_free_list[qos].lock);
- while (skb_to_free > 0) {
- dev_kfree_skb(__skb_dequeue(&priv->tx_free_list[qos]));
- skb_to_free--;
- }
- if (take_lock)
- spin_unlock(&priv->tx_free_list[qos].lock);
- }
-}
+void cvm_oct_tx_initialize(void);
+void cvm_oct_tx_shutdown(void);
+void cvm_oct_tx_shutdown_dev(struct net_device *dev);
diff --git a/drivers/staging/octeon/ethernet-util.h b/drivers/staging/octeon/ethernet-util.h
index 37b66591800..23467563fe5 100644
--- a/drivers/staging/octeon/ethernet-util.h
+++ b/drivers/staging/octeon/ethernet-util.h
@@ -30,10 +30,9 @@
} while (0)
/**
- * Given a packet data address, return a pointer to the
- * beginning of the packet buffer.
- *
+ * cvm_oct_get_buffer_ptr - convert packet data address to pointer
* @packet_ptr: Packet data hardware address
+ *
* Returns Packet buffer pointer
*/
static inline void *cvm_oct_get_buffer_ptr(union cvmx_buf_ptr packet_ptr)
@@ -43,9 +42,7 @@ static inline void *cvm_oct_get_buffer_ptr(union cvmx_buf_ptr packet_ptr)
}
/**
- * Given an IPD/PKO port number, return the logical interface it is
- * on.
- *
+ * INTERFACE - convert IPD port to locgical interface
* @ipd_port: Port to check
*
* Returns Logical interface
@@ -65,9 +62,7 @@ static inline int INTERFACE(int ipd_port)
}
/**
- * Given an IPD/PKO port number, return the port's index on a
- * logical interface.
- *
+ * INDEX - convert IPD/PKO port number to the port's interface index
* @ipd_port: Port to check
*
* Returns Index into interface port list
diff --git a/drivers/staging/octeon/ethernet-xaui.c b/drivers/staging/octeon/ethernet-xaui.c
index ee3dc41b2c5..3fca1cc31ed 100644
--- a/drivers/staging/octeon/ethernet-xaui.c
+++ b/drivers/staging/octeon/ethernet-xaui.c
@@ -26,7 +26,6 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/mii.h>
#include <net/dst.h>
#include <asm/octeon/octeon.h>
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 4cfd4b136b3..02b63678811 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -29,7 +29,6 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/delay.h>
#include <linux/phy.h>
#include <net/dst.h>
@@ -43,8 +42,6 @@
#include "ethernet-tx.h"
#include "ethernet-mdio.h"
#include "ethernet-util.h"
-#include "ethernet-proc.h"
-
#include "cvmx-pip.h"
#include "cvmx-pko.h"
@@ -104,13 +101,15 @@ MODULE_PARM_DESC(pow_send_list, "\n"
"\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
"\tusing the pow_send_group.");
-static int disable_core_queueing = 1;
-module_param(disable_core_queueing, int, 0444);
-MODULE_PARM_DESC(disable_core_queueing, "\n"
- "\tWhen set the networking core's tx_queue_len is set to zero. This\n"
- "\tallows packets to be sent without lock contention in the packet\n"
- "\tscheduler resulting in some cases in improved throughput.\n");
+int max_rx_cpus = -1;
+module_param(max_rx_cpus, int, 0444);
+MODULE_PARM_DESC(max_rx_cpus, "\n"
+ "\t\tThe maximum number of CPUs to use for packet reception.\n"
+ "\t\tUse -1 to use all available CPUs.");
+int rx_napi_weight = 32;
+module_param(rx_napi_weight, int, 0444);
+MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
/*
* The offset from mac_addr_base that should be used for the next port
@@ -122,9 +121,16 @@ MODULE_PARM_DESC(disable_core_queueing, "\n"
static unsigned int cvm_oct_mac_addr_offset;
/**
- * Periodic timer to check auto negotiation
+ * cvm_oct_poll_queue - Workqueue for polling operations.
+ */
+struct workqueue_struct *cvm_oct_poll_queue;
+
+/**
+ * cvm_oct_poll_queue_stopping - flag to indicate polling should stop.
+ *
+ * Set to one right before cvm_oct_poll_queue is destroyed.
*/
-static struct timer_list cvm_oct_poll_timer;
+atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
/**
* Array of every ethernet device owned by this driver indexed by
@@ -132,65 +138,44 @@ static struct timer_list cvm_oct_poll_timer;
*/
struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
-/**
- * Periodic timer tick for slow management operations
- *
- * @arg: Device to check
- */
-static void cvm_do_timer(unsigned long arg)
+u64 cvm_oct_tx_poll_interval;
+
+static void cvm_oct_rx_refill_worker(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
+
+static void cvm_oct_rx_refill_worker(struct work_struct *work)
{
- int32_t skb_to_free, undo;
- int queues_per_port;
- int qos;
- struct octeon_ethernet *priv;
- static int port;
+ /*
+ * FPA 0 may have been drained, try to refill it if we need
+ * more than num_packet_buffers / 2, otherwise normal receive
+ * processing will refill it. If it were drained, no packets
+ * could be received so cvm_oct_napi_poll would never be
+ * invoked to do the refill.
+ */
+ cvm_oct_rx_refill_pool(num_packet_buffers / 2);
- if (port >= CVMX_PIP_NUM_INPUT_PORTS) {
- /*
- * All ports have been polled. Start the next
- * iteration through the ports in one second.
- */
- port = 0;
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
- return;
- }
- if (!cvm_oct_device[port])
- goto out;
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue,
+ &cvm_oct_rx_refill_work, HZ);
+}
+
+static void cvm_oct_periodic_worker(struct work_struct *work)
+{
+ struct octeon_ethernet *priv = container_of(work,
+ struct octeon_ethernet,
+ port_periodic_work.work);
- priv = netdev_priv(cvm_oct_device[port]);
if (priv->poll)
- priv->poll(cvm_oct_device[port]);
-
- queues_per_port = cvmx_pko_get_num_queues(port);
- /* Drain any pending packets in the free list */
- for (qos = 0; qos < queues_per_port; qos++) {
- if (skb_queue_len(&priv->tx_free_list[qos]) == 0)
- continue;
- skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
- MAX_SKB_TO_FREE);
- undo = skb_to_free > 0 ?
- MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE;
- if (undo > 0)
- cvmx_fau_atomic_add32(priv->fau+qos*4, -undo);
- skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ?
- MAX_SKB_TO_FREE : -skb_to_free;
- cvm_oct_free_tx_skbs(priv, skb_to_free, qos, 1);
- }
- cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]);
+ priv->poll(cvm_oct_device[priv->port]);
-out:
- port++;
- /* Poll the next port in a 50th of a second.
- This spreads the polling of ports out a little bit */
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ / 50);
-}
+ cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]);
+
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
+ }
-/**
- * Configure common hardware for all interfaces
- */
static __init void cvm_oct_configure_common_hw(void)
{
- int r;
/* Setup the FPA */
cvmx_fpa_enable();
cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
@@ -205,28 +190,13 @@ static __init void cvm_oct_configure_common_hw(void)
cvmx_helper_setup_red(num_packet_buffers / 4,
num_packet_buffers / 8);
- /* Enable the MII interface */
- if (!octeon_is_simulation())
- cvmx_write_csr(CVMX_SMIX_EN(0), 1);
-
- /* Register an IRQ hander for to receive POW interrupts */
- r = request_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group,
- cvm_oct_do_interrupt, IRQF_SHARED, "Ethernet",
- cvm_oct_device);
-
-#if defined(CONFIG_SMP) && 0
- if (USE_MULTICORE_RECEIVE) {
- irq_set_affinity(OCTEON_IRQ_WORKQ0 + pow_receive_group,
- cpu_online_mask);
- }
-#endif
}
/**
- * Free a work queue entry received in a intercept callback.
+ * cvm_oct_free_work- Free a work queue entry
+ *
+ * @work_queue_entry: Work queue entry to free
*
- * @work_queue_entry:
- * Work queue entry to free
* Returns Zero on success, Negative on failure.
*/
int cvm_oct_free_work(void *work_queue_entry)
@@ -253,9 +223,9 @@ int cvm_oct_free_work(void *work_queue_entry)
EXPORT_SYMBOL(cvm_oct_free_work);
/**
- * Get the low level ethernet statistics
- *
+ * cvm_oct_common_get_stats - get the low level ethernet statistics
* @dev: Device to get the statistics from
+ *
* Returns Pointer to the statistics
*/
static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev)
@@ -299,8 +269,7 @@ static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev)
}
/**
- * Change the link MTU. Unimplemented
- *
+ * cvm_oct_common_change_mtu - change the link MTU
* @dev: Device to change
* @new_mtu: The new MTU
*
@@ -364,8 +333,7 @@ static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu)
}
/**
- * Set the multicast list. Currently unimplemented.
- *
+ * cvm_oct_common_set_multicast_list - set the multicast list
* @dev: Device to work on
*/
static void cvm_oct_common_set_multicast_list(struct net_device *dev)
@@ -420,10 +388,10 @@ static void cvm_oct_common_set_multicast_list(struct net_device *dev)
}
/**
- * Set the hardware MAC address for a device
- *
- * @dev: Device to change the MAC address for
- * @addr: Address structure to change it too. MAC address is addr + 2.
+ * cvm_oct_common_set_mac_address - set the hardware MAC address for a device
+ * @dev: The device in question.
+ * @addr: Address structure to change it too.
+
* Returns Zero on success
*/
static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
@@ -470,9 +438,9 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
}
/**
- * Per network device initialization
- *
+ * cvm_oct_common_init - per network device initialization
* @dev: Device to initialize
+ *
* Returns Zero on success
*/
int cvm_oct_common_init(struct net_device *dev)
@@ -510,8 +478,11 @@ int cvm_oct_common_init(struct net_device *dev)
&& (always_use_pow || strstr(pow_send_list, dev->name)))
priv->queue = -1;
- if (priv->queue != -1 && USE_HW_TCPUDP_CHECKSUM)
- dev->features |= NETIF_F_IP_CSUM;
+ if (priv->queue != -1) {
+ dev->features |= NETIF_F_SG;
+ if (USE_HW_TCPUDP_CHECKSUM)
+ dev->features |= NETIF_F_IP_CSUM;
+ }
/* We do our own locking, Linux doesn't need to */
dev->features |= NETIF_F_LLTX;
@@ -625,12 +596,6 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = {
extern void octeon_mdiobus_force_mod_depencency(void);
-/**
- * Module/ driver initialization. Creates the linux network
- * devices.
- *
- * Returns Zero on success
- */
static int __init cvm_oct_init_module(void)
{
int num_interfaces;
@@ -648,8 +613,12 @@ static int __init cvm_oct_init_module(void)
else
cvm_oct_mac_addr_offset = 0;
- cvm_oct_proc_initialize();
- cvm_oct_rx_initialize();
+ cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
+ if (cvm_oct_poll_queue == NULL) {
+ pr_err("octeon-ethernet: Cannot create workqueue");
+ return -ENOMEM;
+ }
+
cvm_oct_configure_common_hw();
cvmx_helper_initialize_packet_io_global();
@@ -682,6 +651,9 @@ static int __init cvm_oct_init_module(void)
*/
cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ /* Initialize the FAU used for counting tx SKBs that need to be freed */
+ cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0);
+
if ((pow_send_group != -1)) {
struct net_device *dev;
pr_info("\tConfiguring device for POW only access\n");
@@ -689,7 +661,6 @@ static int __init cvm_oct_init_module(void)
if (dev) {
/* Initialize the device private structure. */
struct octeon_ethernet *priv = netdev_priv(dev);
- memset(priv, 0, sizeof(struct octeon_ethernet));
dev->netdev_ops = &cvm_oct_pow_netdev_ops;
priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
@@ -700,19 +671,16 @@ static int __init cvm_oct_init_module(void)
skb_queue_head_init(&priv->tx_free_list[qos]);
if (register_netdev(dev) < 0) {
- pr_err("Failed to register ethernet "
- "device for POW\n");
+ pr_err("Failed to register ethernet device for POW\n");
kfree(dev);
} else {
cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = dev;
- pr_info("%s: POW send group %d, receive "
- "group %d\n",
- dev->name, pow_send_group,
- pow_receive_group);
+ pr_info("%s: POW send group %d, receive group %d\n",
+ dev->name, pow_send_group,
+ pow_receive_group);
}
} else {
- pr_err("Failed to allocate ethernet device "
- "for POW\n");
+ pr_err("Failed to allocate ethernet device for POW\n");
}
}
@@ -730,17 +698,15 @@ static int __init cvm_oct_init_module(void)
struct net_device *dev =
alloc_etherdev(sizeof(struct octeon_ethernet));
if (!dev) {
- pr_err("Failed to allocate ethernet device "
- "for port %d\n", port);
+ pr_err("Failed to allocate ethernet device for port %d\n", port);
continue;
}
- if (disable_core_queueing)
- dev->tx_queue_len = 0;
/* Initialize the device private structure. */
priv = netdev_priv(dev);
- memset(priv, 0, sizeof(struct octeon_ethernet));
+ INIT_DELAYED_WORK(&priv->port_periodic_work,
+ cvm_oct_periodic_worker);
priv->imode = imode;
priv->port = port;
priv->queue = cvmx_pko_get_base_queue(priv->port);
@@ -803,44 +769,25 @@ static int __init cvm_oct_init_module(void)
fau -=
cvmx_pko_get_num_queues(priv->port) *
sizeof(uint32_t);
+ queue_delayed_work(cvm_oct_poll_queue,
+ &priv->port_periodic_work, HZ);
}
}
}
- if (INTERRUPT_LIMIT) {
- /*
- * Set the POW timer rate to give an interrupt at most
- * INTERRUPT_LIMIT times per second.
- */
- cvmx_write_csr(CVMX_POW_WQ_INT_PC,
- octeon_bootinfo->eclock_hz / (INTERRUPT_LIMIT *
- 16 * 256) << 8);
+ cvm_oct_tx_initialize();
+ cvm_oct_rx_initialize();
- /*
- * Enable POW timer interrupt. It will count when
- * there are packets available.
- */
- cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group),
- 0x1ful << 24);
- } else {
- /* Enable POW interrupt when our port has at least one packet */
- cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
- }
+ /*
+ * 150 uS: about 10 1500-byte packtes at 1GE.
+ */
+ cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000);
- /* Enable the poll timer for checking RGMII status */
- init_timer(&cvm_oct_poll_timer);
- cvm_oct_poll_timer.data = 0;
- cvm_oct_poll_timer.function = cvm_do_timer;
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
+ queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ);
return 0;
}
-/**
- * Module / driver shutdown
- *
- * Returns Zero on success
- */
static void __exit cvm_oct_cleanup_module(void)
{
int port;
@@ -853,22 +800,31 @@ static void __exit cvm_oct_cleanup_module(void)
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, cvm_oct_device);
- del_timer(&cvm_oct_poll_timer);
+ atomic_inc_return(&cvm_oct_poll_queue_stopping);
+ cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
+
cvm_oct_rx_shutdown();
+ cvm_oct_tx_shutdown();
+
cvmx_pko_disable();
/* Free the ethernet devices */
for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
if (cvm_oct_device[port]) {
- cvm_oct_tx_shutdown(cvm_oct_device[port]);
- unregister_netdev(cvm_oct_device[port]);
- kfree(cvm_oct_device[port]);
+ struct net_device *dev = cvm_oct_device[port];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cancel_delayed_work_sync(&priv->port_periodic_work);
+
+ cvm_oct_tx_shutdown_dev(dev);
+ unregister_netdev(dev);
+ kfree(dev);
cvm_oct_device[port] = NULL;
}
}
+ destroy_workqueue(cvm_oct_poll_queue);
+
cvmx_pko_shutdown();
- cvm_oct_proc_shutdown();
cvmx_ipd_free_ptr();
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index 402a15b9bb0..d5819256355 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -4,7 +4,7 @@
* Contact: support@caviumnetworks.com
* This file is part of the OCTEON SDK
*
- * Copyright (c) 2003-2007 Cavium Networks
+ * Copyright (c) 2003-2010 Cavium Networks
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 2, as
@@ -57,58 +57,12 @@ struct octeon_ethernet {
uint64_t link_info;
/* Called periodically to check link status */
void (*poll) (struct net_device *dev);
+ struct delayed_work port_periodic_work;
+ struct work_struct port_work; /* may be unused. */
};
-/**
- * Free a work queue entry received in a intercept callback.
- *
- * @work_queue_entry:
- * Work queue entry to free
- * Returns Zero on success, Negative on failure.
- */
int cvm_oct_free_work(void *work_queue_entry);
-/**
- * Transmit a work queue entry out of the ethernet port. Both
- * the work queue entry and the packet data can optionally be
- * freed. The work will be freed on error as well.
- *
- * @dev: Device to transmit out.
- * @work_queue_entry:
- * Work queue entry to send
- * @do_free: True if the work queue entry and packet data should be
- * freed. If false, neither will be freed.
- * @qos: Index into the queues for this port to transmit on. This
- * is used to implement QoS if their are multiple queues per
- * port. This parameter must be between 0 and the number of
- * queues per port minus 1. Values outside of this range will
- * be change to zero.
- *
- * Returns Zero on success, negative on failure.
- */
-int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
- int do_free, int qos);
-
-/**
- * Transmit a work queue entry out of the ethernet port. Both
- * the work queue entry and the packet data can optionally be
- * freed. The work will be freed on error as well. This simply
- * wraps cvmx_oct_transmit_qos() for backwards compatability.
- *
- * @dev: Device to transmit out.
- * @work_queue_entry:
- * Work queue entry to send
- * @do_free: True if the work queue entry and packet data should be
- * freed. If false, neither will be freed.
- *
- * Returns Zero on success, negative on failure.
- */
-static inline int cvm_oct_transmit(struct net_device *dev,
- void *work_queue_entry, int do_free)
-{
- return cvm_oct_transmit_qos(dev, work_queue_entry, do_free, 0);
-}
-
extern int cvm_oct_rgmii_init(struct net_device *dev);
extern void cvm_oct_rgmii_uninit(struct net_device *dev);
extern int cvm_oct_rgmii_open(struct net_device *dev);
@@ -134,5 +88,11 @@ extern int pow_send_group;
extern int pow_receive_group;
extern char pow_send_list[];
extern struct net_device *cvm_oct_device[];
+extern struct workqueue_struct *cvm_oct_poll_queue;
+extern atomic_t cvm_oct_poll_queue_stopping;
+extern u64 cvm_oct_tx_poll_interval;
+
+extern int max_rx_cpus;
+extern int rx_napi_weight;
#endif
diff --git a/drivers/staging/sm7xx/smtc2d.c b/drivers/staging/sm7xx/smtc2d.c
index 133b86c6a67..2fff0a0052d 100644
--- a/drivers/staging/sm7xx/smtc2d.c
+++ b/drivers/staging/sm7xx/smtc2d.c
@@ -5,7 +5,7 @@
* Author: Boyod boyod.yang@siliconmotion.com.cn
*
* Copyright (C) 2009 Lemote, Inc.
- * Author: Wu Zhangjin, wuzj@lemote.com
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* 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
diff --git a/drivers/staging/sm7xx/smtc2d.h b/drivers/staging/sm7xx/smtc2d.h
index 38d0c335322..02b4fa29136 100644
--- a/drivers/staging/sm7xx/smtc2d.h
+++ b/drivers/staging/sm7xx/smtc2d.h
@@ -5,7 +5,7 @@
* Author: Ge Wang, gewang@siliconmotion.com
*
* Copyright (C) 2009 Lemote, Inc.
- * Author: Wu Zhangjin, wuzj@lemote.com
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* 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
diff --git a/drivers/staging/sm7xx/smtcfb.c b/drivers/staging/sm7xx/smtcfb.c
index 161dbc9c139..a4f6f49aef4 100644
--- a/drivers/staging/sm7xx/smtcfb.c
+++ b/drivers/staging/sm7xx/smtcfb.c
@@ -6,7 +6,7 @@
* Boyod boyod.yang@siliconmotion.com.cn
*
* Copyright (C) 2009 Lemote, Inc.
- * Author: Wu Zhangjin, wuzj@lemote.com
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* 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
diff --git a/drivers/staging/sm7xx/smtcfb.h b/drivers/staging/sm7xx/smtcfb.h
index 7f2c3413821..7ee565c2c95 100644
--- a/drivers/staging/sm7xx/smtcfb.h
+++ b/drivers/staging/sm7xx/smtcfb.h
@@ -6,7 +6,7 @@
* Boyod boyod.yang@siliconmotion.com.cn
*
* Copyright (C) 2009 Lemote, Inc.
- * Author: Wu Zhangjin, wuzj@lemote.com
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* 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
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 6e8bcdfd23b..a678186f218 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1312,9 +1312,9 @@ static int processcompl(struct async *as, void __user * __user *arg)
void __user *addr = as->userurb;
unsigned int i;
- if (as->userbuffer)
+ if (as->userbuffer && urb->actual_length)
if (copy_to_user(as->userbuffer, urb->transfer_buffer,
- urb->transfer_buffer_length))
+ urb->actual_length))
goto err_out;
if (put_user(as->status, &userurb->status))
goto err_out;
@@ -1334,14 +1334,11 @@ static int processcompl(struct async *as, void __user * __user *arg)
}
}
- free_async(as);
-
if (put_user(addr, (void __user * __user *)arg))
return -EFAULT;
return 0;
err_out:
- free_async(as);
return -EFAULT;
}
@@ -1371,8 +1368,11 @@ static struct async *reap_as(struct dev_state *ps)
static int proc_reapurb(struct dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
- if (as)
- return processcompl(as, (void __user * __user *)arg);
+ if (as) {
+ int retval = processcompl(as, (void __user * __user *)arg);
+ free_async(as);
+ return retval;
+ }
if (signal_pending(current))
return -EINTR;
return -EIO;
@@ -1380,11 +1380,16 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)
static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
{
+ int retval;
struct async *as;
- if (!(as = async_getcompleted(ps)))
- return -EAGAIN;
- return processcompl(as, (void __user * __user *)arg);
+ as = async_getcompleted(ps);
+ retval = -EAGAIN;
+ if (as) {
+ retval = processcompl(as, (void __user * __user *)arg);
+ free_async(as);
+ }
+ return retval;
}
#ifdef CONFIG_COMPAT
@@ -1475,9 +1480,9 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
void __user *addr = as->userurb;
unsigned int i;
- if (as->userbuffer)
+ if (as->userbuffer && urb->actual_length)
if (copy_to_user(as->userbuffer, urb->transfer_buffer,
- urb->transfer_buffer_length))
+ urb->actual_length))
return -EFAULT;
if (put_user(as->status, &userurb->status))
return -EFAULT;
@@ -1497,7 +1502,6 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
}
}
- free_async(as);
if (put_user(ptr_to_compat(addr), (u32 __user *)arg))
return -EFAULT;
return 0;
@@ -1506,8 +1510,11 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
- if (as)
- return processcompl_compat(as, (void __user * __user *)arg);
+ if (as) {
+ int retval = processcompl_compat(as, (void __user * __user *)arg);
+ free_async(as);
+ return retval;
+ }
if (signal_pending(current))
return -EINTR;
return -EIO;
@@ -1515,11 +1522,16 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
{
+ int retval;
struct async *as;
- if (!(as = async_getcompleted(ps)))
- return -EAGAIN;
- return processcompl_compat(as, (void __user * __user *)arg);
+ retval = -EAGAIN;
+ as = async_getcompleted(ps);
+ if (as) {
+ retval = processcompl_compat(as, (void __user * __user *)arg);
+ free_async(as);
+ }
+ return retval;
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 60a45f1e3a6..f2f055eb683 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
goto done;
}
+ /* Non-root devices on a full/low-speed bus must wait for their
+ * companion high-speed root hub, in case a handoff is needed.
+ */
+ if (!(msg.event & PM_EVENT_AUTO) && udev->parent &&
+ udev->bus->hs_companion)
+ device_pm_wait_for_dev(&udev->dev,
+ &udev->bus->hs_companion->root_hub->dev);
+
if (udev->quirks & USB_QUIRK_RESET_RESUME)
udev->reset_resume = 1;
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index fdfaa788551..d26b9ea981f 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -186,6 +186,7 @@ int usb_create_ep_devs(struct device *parent,
ep_dev->dev.parent = parent;
ep_dev->dev.release = ep_device_release;
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
+ device_enable_async_suspend(&ep_dev->dev);
retval = device_register(&ep_dev->dev);
if (retval)
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 2dcf906df56..15286533c15 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pm_runtime.h>
#include <linux/usb.h>
#include <asm/io.h>
@@ -37,6 +38,122 @@
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
+#ifdef CONFIG_PM_SLEEP
+
+/* Coordinate handoffs between EHCI and companion controllers
+ * during system resume
+ */
+
+static DEFINE_MUTEX(companions_mutex);
+
+#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI
+#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI
+#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI
+
+enum companion_action {
+ SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS
+};
+
+static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
+ enum companion_action action)
+{
+ struct pci_dev *companion;
+ struct usb_hcd *companion_hcd;
+ unsigned int slot = PCI_SLOT(pdev->devfn);
+
+ /* Iterate through other PCI functions in the same slot.
+ * If pdev is OHCI or UHCI then we are looking for EHCI, and
+ * vice versa.
+ */
+ companion = NULL;
+ for (;;) {
+ companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
+ if (!companion)
+ break;
+ if (companion->bus != pdev->bus ||
+ PCI_SLOT(companion->devfn) != slot)
+ continue;
+
+ companion_hcd = pci_get_drvdata(companion);
+ if (!companion_hcd)
+ continue;
+
+ /* For SET_HS_COMPANION, store a pointer to the EHCI bus in
+ * the OHCI/UHCI companion bus structure.
+ * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus
+ * in the OHCI/UHCI companion bus structure.
+ * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI
+ * companion controllers have fully resumed.
+ */
+
+ if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) &&
+ companion->class == CL_EHCI) {
+ /* action must be SET_HS_COMPANION */
+ dev_dbg(&companion->dev, "HS companion for %s\n",
+ dev_name(&pdev->dev));
+ hcd->self.hs_companion = &companion_hcd->self;
+
+ } else if (pdev->class == CL_EHCI &&
+ (companion->class == CL_OHCI ||
+ companion->class == CL_UHCI)) {
+ switch (action) {
+ case SET_HS_COMPANION:
+ dev_dbg(&pdev->dev, "HS companion for %s\n",
+ dev_name(&companion->dev));
+ companion_hcd->self.hs_companion = &hcd->self;
+ break;
+ case CLEAR_HS_COMPANION:
+ companion_hcd->self.hs_companion = NULL;
+ break;
+ case WAIT_FOR_COMPANIONS:
+ device_pm_wait_for_dev(&pdev->dev,
+ &companion->dev);
+ break;
+ }
+ }
+ }
+}
+
+static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
+{
+ mutex_lock(&companions_mutex);
+ dev_set_drvdata(&pdev->dev, hcd);
+ companion_common(pdev, hcd, SET_HS_COMPANION);
+ mutex_unlock(&companions_mutex);
+}
+
+static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
+{
+ mutex_lock(&companions_mutex);
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */
+ if (pdev->class == CL_OHCI || pdev->class == CL_UHCI)
+ hcd->self.hs_companion = NULL;
+
+ /* Otherwise search for companion buses and clear their pointers */
+ else
+ companion_common(pdev, hcd, CLEAR_HS_COMPANION);
+ mutex_unlock(&companions_mutex);
+}
+
+static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd)
+{
+ /* Only EHCI controllers need to wait.
+ * No locking is needed because a controller cannot be resumed
+ * while one of its companions is getting unbound.
+ */
+ if (pdev->class == CL_EHCI)
+ companion_common(pdev, hcd, WAIT_FOR_COMPANIONS);
+}
+
+#else /* !CONFIG_PM_SLEEP */
+
+static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
+static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
+static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
+
+#endif /* !CONFIG_PM_SLEEP */
/*-------------------------------------------------------------------------*/
@@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (region == PCI_ROM_RESOURCE) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
- goto err1;
+ goto err2;
}
}
@@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
goto err4;
+ set_hs_companion(dev, hcd);
return retval;
err4:
@@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
} else
release_region(hcd->rsrc_start, hcd->rsrc_len);
err2:
+ clear_hs_companion(dev, hcd);
usb_put_hcd(hcd);
err1:
pci_disable_device(dev);
@@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
} else {
release_region(hcd->rsrc_start, hcd->rsrc_len);
}
+ clear_hs_companion(dev, hcd);
usb_put_hcd(hcd);
pci_disable_device(dev);
}
@@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated)
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
if (hcd->driver->pci_resume) {
+ /* This call should be made only during system resume,
+ * not during runtime resume.
+ */
+ wait_for_companions(pci_dev, hcd);
+
retval = hcd->driver->pci_resume(hcd, hibernated);
if (retval) {
dev_err(dev, "PCI post-resume error %d!\n", retval);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 35cc8b9ba1f..20ecb4cec8d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1817,6 +1817,7 @@ int usb_new_device(struct usb_device *udev)
/* Tell the world! */
announce_device(udev);
+ device_enable_async_suspend(&udev->dev);
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others).
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 9bc95fec793..df73574a9cc 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1867,6 +1867,7 @@ free_interfaces:
"adding %s (config #%d, interface %d)\n",
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
+ device_enable_async_suspend(&intf->dev);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index df77f6131c7..f1e3aad76c3 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -60,7 +60,7 @@ DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
/* B.3.2 Class-Specific AC Interface Descriptor */
-static struct uac_ac_header_descriptor_2 ac_header_desc = {
+static struct uac_ac_header_descriptor_v1_2 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_LENGTH,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_HEADER,
@@ -124,7 +124,7 @@ static struct usb_audio_control_selector feature_unit = {
};
#define OUTPUT_TERMINAL_ID 3
-static struct uac_output_terminal_descriptor output_terminal_desc = {
+static struct uac_output_terminal_descriptor_v1 output_terminal_desc = {
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
@@ -154,7 +154,7 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = {
};
/* B.4.2 Class-Specific AS Interface Descriptor */
-static struct uac_as_header_descriptor as_header_desc = {
+static struct uac_as_header_descriptor_v1 as_header_desc = {
.bLength = UAC_DT_AS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c
index 0a577d5694f..d4f0db58a8a 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/f_eem.c
@@ -358,7 +358,7 @@ done:
* b15: bmType (0 == data)
*/
len = skb->len;
- put_unaligned_le16((len & 0x3FFF) | BIT(14), skb_push(skb, 2));
+ put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2));
/* add a zero-length EEM packet, if needed */
if (padlen)
@@ -464,7 +464,6 @@ static int eem_unwrap(struct gether *port,
}
/* validate CRC */
- crc = get_unaligned_le32(skb->data + len - ETH_FCS_LEN);
if (header & BIT(14)) {
crc = get_unaligned_le32(skb->data + len
- ETH_FCS_LEN);
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index d0b1e836f0e..5f6a2e0a935 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -237,7 +237,7 @@ static const struct usb_interface_descriptor ac_interface_desc = {
};
/* B.3.2 Class-Specific AC Interface Descriptor */
-static const struct uac_ac_header_descriptor_1 ac_header_desc = {
+static const struct uac_ac_header_descriptor_v1_1 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index 429560100b1..76496f5d272 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -29,7 +29,7 @@
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
#endif
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef CONFIG_USB_G_MULTI_RNDIS
# define USB_ETH_RNDIS y
#endif
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index e220fb8091a..8b45145b913 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -26,6 +26,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/err.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 4b5dbd0127f..5fc80a10415 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2582,6 +2582,7 @@ err:
hsotg->gadget.dev.driver = NULL;
return ret;
}
+EXPORT_SYMBOL(usb_gadget_register_driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index c75d9270c75..19372673bf0 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -196,7 +196,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
if (hostpc_reg) {
u32 t3;
+ spin_unlock_irq(&ehci->lock);
msleep(5);/* 5ms for HCD enter low pwr mode */
+ spin_lock_irq(&ehci->lock);
t3 = ehci_readl(ehci, hostpc_reg);
ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
t3 = ehci_readl(ehci, hostpc_reg);
@@ -904,17 +906,18 @@ static int ehci_hub_control (
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
- ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+
/* After above check the port must be connected.
* Set appropriate bit thus could put phy into low power
* mode if we have hostpc feature
*/
+ temp &= ~PORT_WKCONN_E;
+ temp |= PORT_WKDISC_E | PORT_WKOC_E;
+ ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
if (hostpc_reg) {
- temp &= ~PORT_WKCONN_E;
- temp |= (PORT_WKDISC_E | PORT_WKOC_E);
- ehci_writel(ehci, temp | PORT_SUSPEND,
- status_reg);
+ spin_unlock_irqrestore(&ehci->lock, flags);
msleep(5);/* 5ms for HCD enter low pwr mode */
+ spin_lock_irqsave(&ehci->lock, flags);
temp1 = ehci_readl(ehci, hostpc_reg);
ehci_writel(ehci, temp1 | HOSTPC_PHCD,
hostpc_reg);
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index d224ab467a4..e1232890c78 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -105,7 +105,7 @@ void fhci_ep0_free(struct fhci_usb *usb)
if (ep->td_base)
cpm_muram_free(cpm_muram_offset(ep->td_base));
- if (ep->conf_frame_Q) {
+ if (kfifo_initialized(&ep->conf_frame_Q)) {
size = cq_howmany(&ep->conf_frame_Q);
for (; size; size--) {
struct packet *pkt = cq_get(&ep->conf_frame_Q);
@@ -115,7 +115,7 @@ void fhci_ep0_free(struct fhci_usb *usb)
cq_delete(&ep->conf_frame_Q);
}
- if (ep->empty_frame_Q) {
+ if (kfifo_initialized(&ep->empty_frame_Q)) {
size = cq_howmany(&ep->empty_frame_Q);
for (; size; size--) {
struct packet *pkt = cq_get(&ep->empty_frame_Q);
@@ -125,7 +125,7 @@ void fhci_ep0_free(struct fhci_usb *usb)
cq_delete(&ep->empty_frame_Q);
}
- if (ep->dummy_packets_Q) {
+ if (kfifo_initialized(&ep->dummy_packets_Q)) {
size = cq_howmany(&ep->dummy_packets_Q);
for (; size; size--) {
u8 *buff = cq_get(&ep->dummy_packets_Q);
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 0025847743f..8b37a4b9839 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3245,6 +3245,7 @@ static struct usb_device_id sisusb_table [] = {
{ USB_DEVICE(0x0711, 0x0902) },
{ USB_DEVICE(0x0711, 0x0903) },
{ USB_DEVICE(0x0711, 0x0918) },
+ { USB_DEVICE(0x0711, 0x0920) },
{ USB_DEVICE(0x182d, 0x021c) },
{ USB_DEVICE(0x182d, 0x0269) },
{ }
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index de56b3d743d..3d2d3e549bd 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -44,6 +44,7 @@ config ISP1301_OMAP
config USB_ULPI
bool "Generic ULPI Transceiver Driver"
depends on ARM
+ select USB_OTG_UTILS
help
Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards.
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 216f187582a..7638828e731 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -50,7 +50,7 @@
* Version Information
*/
#define DRIVER_VERSION "v1.5.0"
-#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr"
#define DRIVER_DESC "USB FTDI Serial Converters Driver"
static int debug;
@@ -145,10 +145,15 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
+/*
+ * Device ID not listed? Test via module params product/vendor or
+ * /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
+ */
static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) },
@@ -552,9 +557,16 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
/*
- * Due to many user requests for multiple ELV devices we enable
- * them by default.
+ * ELV devices:
*/
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_WS550_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_EC3000_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_WS888_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_TWS550_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_FEM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) },
@@ -571,11 +583,17 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_UTP8_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_WS444PC_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_UMS100_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) },
{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
@@ -697,6 +715,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
{ USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) },
{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) },
+ { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AD4USB_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) },
{ USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index da92b4952ff..c8951aeed98 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -38,6 +38,8 @@
/* www.candapter.com Ewert Energy Systems CANdapter device */
#define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */
+#define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */
+
/* OOCDlink by Joern Kaipf <joernk@web.de>
* (http://www.joernonline.de/dw/doku.php?id=start&idx=projects:oocdlink) */
#define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */
@@ -161,22 +163,37 @@
/*
* ELV USB devices submitted by Christian Abt of ELV (www.elv.de).
* All of these devices use FTDI's vendor ID (0x0403).
+ * Further IDs taken from ELV Windows .inf file.
*
* The previously included PID for the UO 100 module was incorrect.
* In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58).
*
* Armin Laeuger originally sent the PID for the UM 100 module.
*/
+#define FTDI_ELV_USR_PID 0xE000 /* ELV Universal-Sound-Recorder */
+#define FTDI_ELV_MSM1_PID 0xE001 /* ELV Mini-Sound-Modul */
+#define FTDI_ELV_KL100_PID 0xE002 /* ELV Kfz-Leistungsmesser KL 100 */
+#define FTDI_ELV_WS550_PID 0xE004 /* WS 550 */
+#define FTDI_ELV_EC3000_PID 0xE006 /* ENERGY CONTROL 3000 USB */
+#define FTDI_ELV_WS888_PID 0xE008 /* WS 888 */
+#define FTDI_ELV_TWS550_PID 0xE009 /* Technoline WS 550 */
+#define FTDI_ELV_FEM_PID 0xE00A /* Funk Energie Monitor */
#define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */
#define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */
#define FTDI_ELV_HS485_PID 0xE0EA /* USB to RS-485 adapter */
+#define FTDI_ELV_UMS100_PID 0xE0EB /* ELV USB Master-Slave Schaltsteckdose UMS 100 */
+#define FTDI_ELV_TFD128_PID 0xE0EC /* ELV Temperatur-Feuchte-Datenlogger TFD 128 */
+#define FTDI_ELV_FM3RX_PID 0xE0ED /* ELV Messwertuebertragung FM3 RX */
+#define FTDI_ELV_WS777_PID 0xE0EE /* Conrad WS 777 */
#define FTDI_ELV_EM1010PC_PID 0xE0EF /* Engery monitor EM 1010 PC */
#define FTDI_ELV_CSI8_PID 0xE0F0 /* Computer-Schalt-Interface (CSI 8) */
#define FTDI_ELV_EM1000DL_PID 0xE0F1 /* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */
#define FTDI_ELV_PCK100_PID 0xE0F2 /* PC-Kabeltester (PCK 100) */
#define FTDI_ELV_RFP500_PID 0xE0F3 /* HF-Leistungsmesser (RFP 500) */
#define FTDI_ELV_FS20SIG_PID 0xE0F4 /* Signalgeber (FS 20 SIG) */
+#define FTDI_ELV_UTP8_PID 0xE0F5 /* ELV UTP 8 */
#define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */
+#define FTDI_ELV_WS444PC_PID 0xE0F7 /* Conrad WS 444 PC */
#define FTDI_PHI_FISCO_PID 0xE40B /* PHI Fisco USB to Serial cable */
#define FTDI_ELV_UAD8_PID 0xF068 /* USB-AD-Wandler (UAD 8) */
#define FTDI_ELV_UDA7_PID 0xF069 /* USB-DA-Wandler (UDA 7) */
@@ -968,6 +985,7 @@
#define PAPOUCH_VID 0x5050 /* Vendor ID */
#define PAPOUCH_TMU_PID 0x0400 /* TMU USB Thermometer */
#define PAPOUCH_QUIDO4x4_PID 0x0900 /* Quido 4/4 Module */
+#define PAPOUCH_AD4USB_PID 0x8003 /* AD4USB Measurement Module */
/*
* Marvell SheevaPlug
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ac1b6449fb6..3eb6143bb64 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -298,6 +298,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
},
+ { USB_DEVICE(0x413C, 0x08133) }, /* Dell Computer Corp. Wireless 5720 VZW Mobile Broadband (EVDO Rev-A) Minicard GPS Port */
{ }
};
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index e5e6df39e73..aadc16b5eed 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -134,14 +134,14 @@ static int slave_configure(struct scsi_device *sdev)
if (us->fflags & US_FL_MAX_SECTORS_MIN)
max_sectors = PAGE_CACHE_SIZE >> 9;
if (queue_max_sectors(sdev->request_queue) > max_sectors)
- blk_queue_max_sectors(sdev->request_queue,
+ blk_queue_max_hw_sectors(sdev->request_queue,
max_sectors);
} else if (sdev->type == TYPE_TAPE) {
/* Tapes need much higher max_sector limits, so just
* raise it to the maximum possible (4 GB / 512) and
* let the queue segment size sort out the real limit.
*/
- blk_queue_max_sectors(sdev->request_queue, 0x7FFFFF);
+ blk_queue_max_hw_sectors(sdev->request_queue, 0x7FFFFF);
}
/* Some USB host controllers can't do DMA; they have to use PIO.
@@ -495,7 +495,7 @@ static ssize_t store_max_sectors(struct device *dev, struct device_attribute *at
unsigned short ms;
if (sscanf(buf, "%hu", &ms) > 0 && ms <= SCSI_DEFAULT_MAX_SECTORS) {
- blk_queue_max_sectors(sdev->request_queue, ms);
+ blk_queue_max_hw_sectors(sdev->request_queue, ms);
return strlen(buf);
}
return -EINVAL;
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index c932f905318..49575fba375 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -941,7 +941,7 @@ UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0000, 0x9999,
UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133,
"Microtech",
"USB-SCSI-DB25",
- US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+ US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,
US_FL_SCM_MULT_TARG ),
UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100,
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index e9f995486ec..bbeeb92a213 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -78,7 +78,7 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
-static unsigned int delay_use = 5;
+static unsigned int delay_use = 1;
module_param(delay_use, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index e4e4d433b00..9ee67d6da71 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -1931,22 +1931,22 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
* PowerMac2,2 summer 2000 iMacs
* PowerMac4,1 january 2001 iMacs "flower power"
*/
- if (machine_is_compatible("PowerMac2,1") ||
- machine_is_compatible("PowerMac2,2") ||
- machine_is_compatible("PowerMac4,1"))
+ if (of_machine_is_compatible("PowerMac2,1") ||
+ of_machine_is_compatible("PowerMac2,2") ||
+ of_machine_is_compatible("PowerMac4,1"))
default_vmode = VMODE_1024_768_75;
/* iBook SE */
- if (machine_is_compatible("PowerBook2,2"))
+ if (of_machine_is_compatible("PowerBook2,2"))
default_vmode = VMODE_800_600_60;
/* PowerBook Firewire (Pismo), iBook Dual USB */
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook4,1"))
+ if (of_machine_is_compatible("PowerBook3,1") ||
+ of_machine_is_compatible("PowerBook4,1"))
default_vmode = VMODE_1024_768_60;
/* PowerBook Titanium */
- if (machine_is_compatible("PowerBook3,2"))
+ if (of_machine_is_compatible("PowerBook3,2"))
default_vmode = VMODE_1152_768_60;
if (default_cmode > 16)
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 1ddeb4c3476..e45ab8db2dd 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -2439,7 +2439,7 @@ static int __devinit aty_init(struct fb_info *info)
* The Apple iBook1 uses non-standard memory frequencies.
* We detect it and set the frequency manually.
*/
- if (machine_is_compatible("PowerBook2,1")) {
+ if (of_machine_is_compatible("PowerBook2,1")) {
par->pll_limits.mclk = 70;
par->pll_limits.xclk = 53;
}
@@ -2659,7 +2659,7 @@ static int __devinit aty_init(struct fb_info *info)
FBINFO_HWACCEL_YPAN;
#ifdef CONFIG_PMAC_BACKLIGHT
- if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) {
+ if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) {
/*
* these bits let the 101 powerbook
* wake up from sleep -- paulus
@@ -2690,9 +2690,9 @@ static int __devinit aty_init(struct fb_info *info)
if (M64_HAS(G3_PB_1024x768))
/* G3 PowerBook with 1024x768 LCD */
default_vmode = VMODE_1024_768_60;
- else if (machine_is_compatible("iMac"))
+ else if (of_machine_is_compatible("iMac"))
default_vmode = VMODE_1024_768_75;
- else if (machine_is_compatible("PowerBook2,1"))
+ else if (of_machine_is_compatible("PowerBook2,1"))
/* iBook with 800x600 LCD */
default_vmode = VMODE_800_600_60;
else
@@ -3104,7 +3104,7 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev,
}
dp = pci_device_to_OF_node(pdev);
- if (node == dp->node) {
+ if (node == dp->phandle) {
struct fb_var_screeninfo *var = &default_var;
unsigned int N, P, Q, M, T, R;
u32 v_total, h_total;
diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c
index 1a056adb61c..fa1198c4ccc 100644
--- a/drivers/video/aty/radeon_backlight.c
+++ b/drivers/video/aty/radeon_backlight.c
@@ -175,9 +175,9 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo)
#ifdef CONFIG_PMAC_BACKLIGHT
pdata->negative = pdata->negative ||
- machine_is_compatible("PowerBook4,3") ||
- machine_is_compatible("PowerBook6,3") ||
- machine_is_compatible("PowerBook6,5");
+ of_machine_is_compatible("PowerBook4,3") ||
+ of_machine_is_compatible("PowerBook6,3") ||
+ of_machine_is_compatible("PowerBook6,5");
#endif
rinfo->info->bl_dev = bd;
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index eb12182b205..d25df51bb0d 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -161,8 +161,17 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 0;
}
+static void efifb_destroy(struct fb_info *info)
+{
+ if (info->screen_base)
+ iounmap(info->screen_base);
+ release_mem_region(info->aperture_base, info->aperture_size);
+ framebuffer_release(info);
+}
+
static struct fb_ops efifb_ops = {
.owner = THIS_MODULE,
+ .fb_destroy = efifb_destroy,
.fb_setcolreg = efifb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
@@ -281,7 +290,7 @@ static int __init efifb_probe(struct platform_device *dev)
info->par = NULL;
info->aperture_base = efifb_fix.smem_start;
- info->aperture_size = size_total;
+ info->aperture_size = size_remap;
info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len);
if (!info->screen_base) {
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 72d68b3dc47..4637bcbe03a 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -1633,6 +1633,11 @@ static int __init fsl_diu_setup(char *options)
#endif
static struct of_device_id fsl_diu_match[] = {
+#ifdef CONFIG_PPC_MPC512x
+ {
+ .compatible = "fsl,mpc5121-diu",
+ },
+#endif
{
.compatible = "fsl,diu",
},
diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c
index d66887e8cbb..43207cc6cc1 100644
--- a/drivers/video/macfb.c
+++ b/drivers/video/macfb.c
@@ -1,29 +1,33 @@
-/* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we
- don't know how to set */
-
-/* (c) 1999 David Huggins-Daines <dhd@debian.org>
-
- Primarily based on vesafb.c, by Gerd Knorr
- (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
-
- Also uses information and code from:
-
- The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen
- Mellinger, Mikael Forselius, Michael Schmitz, and others.
-
- valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan
- Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven.
-
- This code is free software. You may copy, modify, and distribute
- it subject to the terms and conditions of the GNU General Public
- License, version 2, or any later version, at your convenience. */
+/*
+ * macfb.c: Generic framebuffer for Macs whose colourmaps/modes we
+ * don't know how to set.
+ *
+ * (c) 1999 David Huggins-Daines <dhd@debian.org>
+ *
+ * Primarily based on vesafb.c, by Gerd Knorr
+ * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+ *
+ * Also uses information and code from:
+ *
+ * The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen
+ * Mellinger, Mikael Forselius, Michael Schmitz, and others.
+ *
+ * valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan
+ * Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven.
+ *
+ * The VideoToolbox "Bugs" web page at
+ * http://rajsky.psych.nyu.edu/Tips/VideoBugs.html
+ *
+ * This code is free software. You may copy, modify, and distribute
+ * it subject to the terms and conditions of the GNU General Public
+ * License, version 2, or any later version, at your convenience.
+ */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
-#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/nubus.h>
#include <linux/init.h>
@@ -31,9 +35,6 @@
#include <asm/setup.h>
#include <asm/bootinfo.h>
-#include <asm/uaccess.h>
-#include <asm/pgtable.h>
-#include <asm/irq.h>
#include <asm/macintosh.h>
#include <asm/io.h>
@@ -44,7 +45,7 @@
#define DAFB_BASE 0xf9800200
/* Address for the built-in Civic framebuffer in Quadra AVs */
-#define CIVIC_BASE 0x50f30800 /* Only tested on 660AV! */
+#define CIVIC_BASE 0x50f30800
/* GSC (Gray Scale Controller) base address */
#define GSC_BASE 0x50F20000
@@ -52,37 +53,9 @@
/* CSC (Color Screen Controller) base address */
#define CSC_BASE 0x50F20000
-static int (*macfb_setpalette) (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info) = NULL;
-static int valkyrie_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info);
-static int dafb_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *fb_info);
-static int rbv_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *fb_info);
-static int mdc_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *fb_info);
-static int toby_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *fb_info);
-static int civic_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *fb_info);
-static int csc_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *fb_info);
-
-static struct {
- unsigned char addr;
- /* Note: word-aligned */
- char pad[3];
- unsigned char lut;
-} __iomem *valkyrie_cmap_regs;
+static int (*macfb_setpalette)(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ struct fb_info *info);
static struct {
unsigned char addr;
@@ -116,15 +89,15 @@ static struct {
} __iomem *civic_cmap_regs;
static struct {
- char pad1[0x40];
- unsigned char clut_waddr; /* 0x40 */
- char pad2;
- unsigned char clut_data; /* 0x42 */
- char pad3[0x3];
- unsigned char clut_raddr; /* 0x46 */
+ char pad1[0x40];
+ unsigned char clut_waddr; /* 0x40 */
+ char pad2;
+ unsigned char clut_data; /* 0x42 */
+ char pad3[0x3];
+ unsigned char clut_raddr; /* 0x46 */
} __iomem *csc_cmap_regs;
-/* We will leave these the way they are for the time being */
+/* The registers in these structs are in NuBus slot space */
struct mdc_cmap_regs {
char pad1[0x200200];
unsigned char addr;
@@ -145,13 +118,10 @@ struct jet_cmap_regs {
unsigned char lut;
};
-#define PIXEL_TO_MM(a) (((a)*10)/28) /* width in mm at 72 dpi */
-
-/* mode */
-static int video_slot = 0;
+#define PIXEL_TO_MM(a) (((a)*10)/28) /* width in mm at 72 dpi */
static struct fb_var_screeninfo macfb_defined = {
- .bits_per_pixel = 8,
+ .bits_per_pixel = 8,
.activate = FB_ACTIVATE_NOW,
.width = -1,
.height = -1,
@@ -167,181 +137,152 @@ static struct fb_fix_screeninfo macfb_fix = {
.accel = FB_ACCEL_NONE,
};
+static void *slot_addr;
static struct fb_info fb_info;
static u32 pseudo_palette[16];
-static int inverse = 0;
-static int vidtest = 0;
+static int inverse;
+static int vidtest;
-static int valkyrie_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info)
-{
- unsigned long flags;
-
- red >>= 8;
- green >>= 8;
- blue >>= 8;
-
- local_irq_save(flags);
-
- /* tell clut which address to fill */
- nubus_writeb(regno, &valkyrie_cmap_regs->addr);
- nop();
-
- /* send one color channel at a time */
- nubus_writeb(red, &valkyrie_cmap_regs->lut);
- nop();
- nubus_writeb(green, &valkyrie_cmap_regs->lut);
- nop();
- nubus_writeb(blue, &valkyrie_cmap_regs->lut);
-
- local_irq_restore(flags);
- return 0;
-}
-
-/* Unlike the Valkyrie, the DAFB cannot set individual colormap
- registers. Therefore, we do what the MacOS driver does (no
- kidding!) and simply set them one by one until we hit the one we
- want. */
-static int dafb_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info)
+/*
+ * Unlike the Valkyrie, the DAFB cannot set individual colormap
+ * registers. Therefore, we do what the MacOS driver does (no
+ * kidding!) and simply set them one by one until we hit the one we
+ * want.
+ */
+static int dafb_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ struct fb_info *info)
{
- /* FIXME: really, really need to use ioremap() here,
- phys_to_virt() doesn't work anymore */
static int lastreg = -1;
unsigned long flags;
-
- red >>= 8;
- green >>= 8;
- blue >>= 8;
local_irq_save(flags);
-
- /* fbdev will set an entire colourmap, but X won't. Hopefully
- this should accommodate both of them */
- if (regno != lastreg+1) {
+
+ /*
+ * fbdev will set an entire colourmap, but X won't. Hopefully
+ * this should accommodate both of them
+ */
+ if (regno != lastreg + 1) {
int i;
-
+
/* Stab in the dark trying to reset the CLUT pointer */
nubus_writel(0, &dafb_cmap_regs->reset);
nop();
-
+
/* Loop until we get to the register we want */
for (i = 0; i < regno; i++) {
- nubus_writeb(info->cmap.red[i] >> 8, &dafb_cmap_regs->lut);
+ nubus_writeb(info->cmap.red[i] >> 8,
+ &dafb_cmap_regs->lut);
nop();
- nubus_writeb(info->cmap.green[i] >> 8, &dafb_cmap_regs->lut);
+ nubus_writeb(info->cmap.green[i] >> 8,
+ &dafb_cmap_regs->lut);
nop();
- nubus_writeb(info->cmap.blue[i] >> 8, &dafb_cmap_regs->lut);
+ nubus_writeb(info->cmap.blue[i] >> 8,
+ &dafb_cmap_regs->lut);
nop();
}
}
-
+
nubus_writeb(red, &dafb_cmap_regs->lut);
nop();
nubus_writeb(green, &dafb_cmap_regs->lut);
nop();
nubus_writeb(blue, &dafb_cmap_regs->lut);
-
+
local_irq_restore(flags);
lastreg = regno;
return 0;
}
/* V8 and Brazil seem to use the same DAC. Sonora does as well. */
-static int v8_brazil_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info)
+static int v8_brazil_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ struct fb_info *info)
{
unsigned int bpp = info->var.bits_per_pixel;
- unsigned char _red =red>>8;
- unsigned char _green=green>>8;
- unsigned char _blue =blue>>8;
- unsigned char _regno;
unsigned long flags;
- if (bpp > 8) return 1; /* failsafe */
+ if (bpp > 8)
+ return 1; /* failsafe */
local_irq_save(flags);
/* On these chips, the CLUT register numbers are spread out
- across the register space. Thus:
-
- In 8bpp, all regnos are valid.
-
- In 4bpp, the regnos are 0x0f, 0x1f, 0x2f, etc, etc
-
- In 2bpp, the regnos are 0x3f, 0x7f, 0xbf, 0xff */
- _regno = (regno << (8 - bpp)) | (0xFF >> bpp);
- nubus_writeb(_regno, &v8_brazil_cmap_regs->addr); nop();
+ * across the register space. Thus:
+ * In 8bpp, all regnos are valid.
+ * In 4bpp, the regnos are 0x0f, 0x1f, 0x2f, etc, etc
+ * In 2bpp, the regnos are 0x3f, 0x7f, 0xbf, 0xff
+ */
+ regno = (regno << (8 - bpp)) | (0xFF >> bpp);
+ nubus_writeb(regno, &v8_brazil_cmap_regs->addr);
+ nop();
/* send one color channel at a time */
- nubus_writeb(_red, &v8_brazil_cmap_regs->lut); nop();
- nubus_writeb(_green, &v8_brazil_cmap_regs->lut); nop();
- nubus_writeb(_blue, &v8_brazil_cmap_regs->lut);
+ nubus_writeb(red, &v8_brazil_cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &v8_brazil_cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &v8_brazil_cmap_regs->lut);
- local_irq_restore(flags);
+ local_irq_restore(flags);
return 0;
}
-static int rbv_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info)
+/* RAM-Based Video */
+static int rbv_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ struct fb_info *info)
{
- /* use MSBs */
- unsigned char _red =red>>8;
- unsigned char _green=green>>8;
- unsigned char _blue =blue>>8;
- unsigned char _regno;
unsigned long flags;
- if (info->var.bits_per_pixel > 8) return 1; /* failsafe */
+ if (info->var.bits_per_pixel > 8)
+ return 1; /* failsafe */
local_irq_save(flags);
-
+
/* From the VideoToolbox driver. Seems to be saying that
* regno #254 and #255 are the important ones for 1-bit color,
* regno #252-255 are the important ones for 2-bit color, etc.
*/
- _regno = regno + (256-(1 << info->var.bits_per_pixel));
+ regno += 256 - (1 << info->var.bits_per_pixel);
/* reset clut? (VideoToolbox sez "not necessary") */
- nubus_writeb(0xFF, &rbv_cmap_regs->cntl); nop();
-
+ nubus_writeb(0xFF, &rbv_cmap_regs->cntl);
+ nop();
+
/* tell clut which address to use. */
- nubus_writeb(_regno, &rbv_cmap_regs->addr); nop();
-
+ nubus_writeb(regno, &rbv_cmap_regs->addr);
+ nop();
+
/* send one color channel at a time. */
- nubus_writeb(_red, &rbv_cmap_regs->lut); nop();
- nubus_writeb(_green, &rbv_cmap_regs->lut); nop();
- nubus_writeb(_blue, &rbv_cmap_regs->lut);
-
- local_irq_restore(flags); /* done. */
+ nubus_writeb(red, &rbv_cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &rbv_cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &rbv_cmap_regs->lut);
+
+ local_irq_restore(flags);
return 0;
}
-/* Macintosh Display Card (8x24) */
+/* Macintosh Display Card (8*24) */
static int mdc_setpalette(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
struct fb_info *info)
{
- volatile struct mdc_cmap_regs *cmap_regs =
- nubus_slot_addr(video_slot);
- /* use MSBs */
- unsigned char _red =red>>8;
- unsigned char _green=green>>8;
- unsigned char _blue =blue>>8;
- unsigned char _regno=regno;
+ struct mdc_cmap_regs *cmap_regs = slot_addr;
unsigned long flags;
local_irq_save(flags);
-
+
/* the nop's are there to order writes. */
- nubus_writeb(_regno, &cmap_regs->addr); nop();
- nubus_writeb(_red, &cmap_regs->lut); nop();
- nubus_writeb(_green, &cmap_regs->lut); nop();
- nubus_writeb(_blue, &cmap_regs->lut);
+ nubus_writeb(regno, &cmap_regs->addr);
+ nop();
+ nubus_writeb(red, &cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &cmap_regs->lut);
local_irq_restore(flags);
return 0;
@@ -350,24 +291,26 @@ static int mdc_setpalette(unsigned int regno, unsigned int red,
/* Toby frame buffer */
static int toby_setpalette(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
- struct fb_info *info)
+ struct fb_info *info)
{
- volatile struct toby_cmap_regs *cmap_regs =
- nubus_slot_addr(video_slot);
+ struct toby_cmap_regs *cmap_regs = slot_addr;
unsigned int bpp = info->var.bits_per_pixel;
- /* use MSBs */
- unsigned char _red =~(red>>8);
- unsigned char _green=~(green>>8);
- unsigned char _blue =~(blue>>8);
- unsigned char _regno = (regno << (8 - bpp)) | (0xFF >> bpp);
unsigned long flags;
+ red = ~red;
+ green = ~green;
+ blue = ~blue;
+ regno = (regno << (8 - bpp)) | (0xFF >> bpp);
+
local_irq_save(flags);
-
- nubus_writeb(_regno, &cmap_regs->addr); nop();
- nubus_writeb(_red, &cmap_regs->lut); nop();
- nubus_writeb(_green, &cmap_regs->lut); nop();
- nubus_writeb(_blue, &cmap_regs->lut);
+
+ nubus_writeb(regno, &cmap_regs->addr);
+ nop();
+ nubus_writeb(red, &cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &cmap_regs->lut);
local_irq_restore(flags);
return 0;
@@ -378,20 +321,18 @@ static int jet_setpalette(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
struct fb_info *info)
{
- volatile struct jet_cmap_regs *cmap_regs =
- nubus_slot_addr(video_slot);
- /* use MSBs */
- unsigned char _red = (red>>8);
- unsigned char _green = (green>>8);
- unsigned char _blue = (blue>>8);
+ struct jet_cmap_regs *cmap_regs = slot_addr;
unsigned long flags;
local_irq_save(flags);
-
- nubus_writeb(regno, &cmap_regs->addr); nop();
- nubus_writeb(_red, &cmap_regs->lut); nop();
- nubus_writeb(_green, &cmap_regs->lut); nop();
- nubus_writeb(_blue, &cmap_regs->lut);
+
+ nubus_writeb(regno, &cmap_regs->addr);
+ nop();
+ nubus_writeb(red, &cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &cmap_regs->lut);
local_irq_restore(flags);
return 0;
@@ -400,53 +341,27 @@ static int jet_setpalette(unsigned int regno, unsigned int red,
/*
* Civic framebuffer -- Quadra AV built-in video. A chip
* called Sebastian holds the actual color palettes, and
- * apparently, there are two different banks of 512K RAM
+ * apparently, there are two different banks of 512K RAM
* which can act as separate framebuffers for doing video
* input and viewing the screen at the same time! The 840AV
- * Can add another 1MB RAM to give the two framebuffers
+ * Can add another 1MB RAM to give the two framebuffers
* 1MB RAM apiece.
- *
- * FIXME: this doesn't seem to work anymore.
*/
-static int civic_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info)
+static int civic_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ struct fb_info *info)
{
- static int lastreg = -1;
unsigned long flags;
int clut_status;
- if (info->var.bits_per_pixel > 8) return 1; /* failsafe */
-
- red >>= 8;
- green >>= 8;
- blue >>= 8;
+ if (info->var.bits_per_pixel > 8)
+ return 1; /* failsafe */
local_irq_save(flags);
-
- /*
- * Set the register address
- */
- nubus_writeb(regno, &civic_cmap_regs->addr); nop();
- /*
- * Wait for VBL interrupt here;
- * They're usually not enabled from Penguin, so we won't check
- */
-#if 0
- {
-#define CIVIC_VBL_OFFSET 0x120
- volatile unsigned long *vbl = nubus_readl(civic_cmap_regs->vbl_addr + CIVIC_VBL_OFFSET);
- /* do interrupt setup stuff here? */
- *vbl = 0L; nop(); /* clear */
- *vbl = 1L; nop(); /* set */
- while (*vbl != 0L) /* wait for next vbl */
- {
- usleep(10); /* needed? */
- }
- /* do interrupt shutdown stuff here? */
- }
-#endif
+ /* Set the register address */
+ nubus_writeb(regno, &civic_cmap_regs->addr);
+ nop();
/*
* Grab a status word and do some checking;
@@ -459,39 +374,52 @@ static int civic_setpalette (unsigned int regno, unsigned int red,
#if 0
if ((clut_status & 0x000D) != 0)
{
- nubus_writeb(0x00, &civic_cmap_regs->lut); nop();
- nubus_writeb(0x00, &civic_cmap_regs->lut); nop();
+ nubus_writeb(0x00, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(0x00, &civic_cmap_regs->lut);
+ nop();
}
#endif
- nubus_writeb( red, &civic_cmap_regs->lut); nop();
- nubus_writeb(green, &civic_cmap_regs->lut); nop();
- nubus_writeb( blue, &civic_cmap_regs->lut); nop();
- nubus_writeb( 0x00, &civic_cmap_regs->lut); nop();
+ nubus_writeb(red, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(0x00, &civic_cmap_regs->lut);
}
else
{
unsigned char junk;
- junk = nubus_readb(&civic_cmap_regs->lut); nop();
- junk = nubus_readb(&civic_cmap_regs->lut); nop();
- junk = nubus_readb(&civic_cmap_regs->lut); nop();
- junk = nubus_readb(&civic_cmap_regs->lut); nop();
+ junk = nubus_readb(&civic_cmap_regs->lut);
+ nop();
+ junk = nubus_readb(&civic_cmap_regs->lut);
+ nop();
+ junk = nubus_readb(&civic_cmap_regs->lut);
+ nop();
+ junk = nubus_readb(&civic_cmap_regs->lut);
+ nop();
if ((clut_status & 0x000D) != 0)
{
- nubus_writeb(0x00, &civic_cmap_regs->lut); nop();
- nubus_writeb(0x00, &civic_cmap_regs->lut); nop();
+ nubus_writeb(0x00, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(0x00, &civic_cmap_regs->lut);
+ nop();
}
- nubus_writeb( red, &civic_cmap_regs->lut); nop();
- nubus_writeb(green, &civic_cmap_regs->lut); nop();
- nubus_writeb( blue, &civic_cmap_regs->lut); nop();
- nubus_writeb( junk, &civic_cmap_regs->lut); nop();
+ nubus_writeb(red, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(green, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(blue, &civic_cmap_regs->lut);
+ nop();
+ nubus_writeb(junk, &civic_cmap_regs->lut);
}
local_irq_restore(flags);
- lastreg = regno;
return 0;
}
@@ -500,16 +428,21 @@ static int civic_setpalette (unsigned int regno, unsigned int red,
* (and the 5300 too, but that's a PowerMac). This function
* brought to you in part by the ECSC driver for MkLinux.
*/
-
-static int csc_setpalette (unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- struct fb_info *info)
+static int csc_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ struct fb_info *info)
{
- mdelay(1);
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ udelay(1); /* mklinux on PB 5300 waits for 260 ns */
nubus_writeb(regno, &csc_cmap_regs->clut_waddr);
- nubus_writeb(red, &csc_cmap_regs->clut_data);
+ nubus_writeb(red, &csc_cmap_regs->clut_data);
nubus_writeb(green, &csc_cmap_regs->clut_data);
- nubus_writeb(blue, &csc_cmap_regs->clut_data);
+ nubus_writeb(blue, &csc_cmap_regs->clut_data);
+
+ local_irq_restore(flags);
return 0;
}
@@ -518,10 +451,10 @@ static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green,
struct fb_info *fb_info)
{
/*
- * Set a single color register. The values supplied are
- * already rounded down to the hardware's capabilities
- * (according to the entries in the `var' structure). Return
- * != 0 for invalid regno.
+ * Set a single color register. The values supplied are
+ * already rounded down to the hardware's capabilities
+ * (according to the entries in the `var' structure).
+ * Return non-zero for invalid regno.
*/
if (regno >= fb_info->cmap.len)
@@ -536,8 +469,8 @@ static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green,
case 4:
case 8:
if (macfb_setpalette)
- macfb_setpalette(regno, red, green, blue,
- fb_info);
+ macfb_setpalette(regno, red >> 8, green >> 8,
+ blue >> 8, fb_info);
else
return 1;
break;
@@ -555,28 +488,22 @@ static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green,
} else {
/* 0:5:6:5 */
((u32*) (fb_info->pseudo_palette))[regno] =
- ((red & 0xf800) ) |
+ ((red & 0xf800) >> 0) |
((green & 0xfc00) >> 5) |
((blue & 0xf800) >> 11);
}
break;
- /* I'm pretty sure that one or the other of these
- doesn't exist on 68k Macs */
+ /*
+ * 24-bit colour almost doesn't exist on 68k Macs --
+ * http://support.apple.com/kb/TA28634 (Old Article: 10992)
+ */
case 24:
- red >>= 8;
- green >>= 8;
- blue >>= 8;
- ((u32 *)(fb_info->pseudo_palette))[regno] =
- (red << fb_info->var.red.offset) |
- (green << fb_info->var.green.offset) |
- (blue << fb_info->var.blue.offset);
- break;
case 32:
red >>= 8;
green >>= 8;
blue >>= 8;
((u32 *)(fb_info->pseudo_palette))[regno] =
- (red << fb_info->var.red.offset) |
+ (red << fb_info->var.red.offset) |
(green << fb_info->var.green.offset) |
(blue << fb_info->var.blue.offset);
break;
@@ -597,25 +524,24 @@ static struct fb_ops macfb_ops = {
static void __init macfb_setup(char *options)
{
char *this_opt;
-
+
if (!options || !*options)
return;
-
+
while ((this_opt = strsep(&options, ",")) != NULL) {
- if (!*this_opt) continue;
-
- if (! strcmp(this_opt, "inverse"))
- inverse=1;
- /* This means "turn on experimental CLUT code" */
- else if (!strcmp(this_opt, "vidtest"))
- vidtest=1;
+ if (!*this_opt)
+ continue;
+
+ if (!strcmp(this_opt, "inverse"))
+ inverse = 1;
+ else
+ if (!strcmp(this_opt, "vidtest"))
+ vidtest = 1; /* enable experimental CLUT code */
}
}
static void __init iounmap_macfb(void)
{
- if (valkyrie_cmap_regs)
- iounmap(valkyrie_cmap_regs);
if (dafb_cmap_regs)
iounmap(dafb_cmap_regs);
if (v8_brazil_cmap_regs)
@@ -642,48 +568,55 @@ static int __init macfb_init(void)
if (!MACH_IS_MAC)
return -ENODEV;
- /* There can only be one internal video controller anyway so
- we're not too worried about this */
+ if (mac_bi_data.id == MAC_MODEL_Q630 ||
+ mac_bi_data.id == MAC_MODEL_P588)
+ return -ENODEV; /* See valkyriefb.c */
+
macfb_defined.xres = mac_bi_data.dimensions & 0xFFFF;
macfb_defined.yres = mac_bi_data.dimensions >> 16;
macfb_defined.bits_per_pixel = mac_bi_data.videodepth;
+
macfb_fix.line_length = mac_bi_data.videorow;
- macfb_fix.smem_len = macfb_fix.line_length * macfb_defined.yres;
+ macfb_fix.smem_len = macfb_fix.line_length * macfb_defined.yres;
/* Note: physical address (since 2.1.127) */
- macfb_fix.smem_start = mac_bi_data.videoaddr;
- /* This is actually redundant with the initial mappings.
- However, there are some non-obvious aspects to the way
- those mappings are set up, so this is in fact the safest
- way to ensure that this driver will work on every possible
- Mac */
- fb_info.screen_base = ioremap(mac_bi_data.videoaddr, macfb_fix.smem_len);
-
- printk("macfb: framebuffer at 0x%08lx, mapped to 0x%p, size %dk\n",
- macfb_fix.smem_start, fb_info.screen_base, macfb_fix.smem_len/1024);
- printk("macfb: mode is %dx%dx%d, linelength=%d\n",
- macfb_defined.xres, macfb_defined.yres, macfb_defined.bits_per_pixel, macfb_fix.line_length);
-
+ macfb_fix.smem_start = mac_bi_data.videoaddr;
+
/*
- * Fill in the available video resolution
+ * This is actually redundant with the initial mappings.
+ * However, there are some non-obvious aspects to the way
+ * those mappings are set up, so this is in fact the safest
+ * way to ensure that this driver will work on every possible Mac
*/
-
- macfb_defined.xres_virtual = macfb_defined.xres;
- macfb_defined.yres_virtual = macfb_defined.yres;
- macfb_defined.height = PIXEL_TO_MM(macfb_defined.yres);
- macfb_defined.width = PIXEL_TO_MM(macfb_defined.xres);
+ fb_info.screen_base = ioremap(mac_bi_data.videoaddr,
+ macfb_fix.smem_len);
+ if (!fb_info.screen_base)
+ return -ENODEV;
- printk("macfb: scrolling: redraw\n");
+ printk("macfb: framebuffer at 0x%08lx, mapped to 0x%p, size %dk\n",
+ macfb_fix.smem_start, fb_info.screen_base,
+ macfb_fix.smem_len / 1024);
+ printk("macfb: mode is %dx%dx%d, linelength=%d\n",
+ macfb_defined.xres, macfb_defined.yres,
+ macfb_defined.bits_per_pixel, macfb_fix.line_length);
+
+ /* Fill in the available video resolution */
+ macfb_defined.xres_virtual = macfb_defined.xres;
macfb_defined.yres_virtual = macfb_defined.yres;
+ macfb_defined.height = PIXEL_TO_MM(macfb_defined.yres);
+ macfb_defined.width = PIXEL_TO_MM(macfb_defined.xres);
- /* some dummy values for timing to make fbset happy */
- macfb_defined.pixclock = 10000000 / macfb_defined.xres * 1000 / macfb_defined.yres;
+ /* Some dummy values for timing to make fbset happy */
+ macfb_defined.pixclock = 10000000 / macfb_defined.xres *
+ 1000 / macfb_defined.yres;
macfb_defined.left_margin = (macfb_defined.xres / 8) & 0xf8;
macfb_defined.hsync_len = (macfb_defined.xres / 8) & 0xf8;
switch (macfb_defined.bits_per_pixel) {
case 1:
- /* XXX: I think this will catch any program that tries
- to do FBIO_PUTCMAP when the visual is monochrome */
+ /*
+ * XXX: I think this will catch any program that tries
+ * to do FBIO_PUTCMAP when the visual is monochrome.
+ */
macfb_defined.red.length = macfb_defined.bits_per_pixel;
macfb_defined.green.length = macfb_defined.bits_per_pixel;
macfb_defined.blue.length = macfb_defined.bits_per_pixel;
@@ -708,53 +641,52 @@ static int __init macfb_init(void)
macfb_defined.green.length = 5;
macfb_defined.blue.offset = 0;
macfb_defined.blue.length = 5;
- printk("macfb: directcolor: "
- "size=1:5:5:5, shift=15:10:5:0\n");
video_cmap_len = 16;
- /* Should actually be FB_VISUAL_DIRECTCOLOR, but this
- works too */
+ /*
+ * Should actually be FB_VISUAL_DIRECTCOLOR, but this
+ * works too
+ */
macfb_fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 24:
case 32:
- /* XXX: have to test these... can any 68k Macs
- actually do this on internal video? */
macfb_defined.red.offset = 16;
macfb_defined.red.length = 8;
macfb_defined.green.offset = 8;
macfb_defined.green.length = 8;
macfb_defined.blue.offset = 0;
macfb_defined.blue.length = 8;
- printk("macfb: truecolor: "
- "size=0:8:8:8, shift=0:16:8:0\n");
video_cmap_len = 16;
macfb_fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
default:
video_cmap_len = 0;
macfb_fix.visual = FB_VISUAL_MONO01;
- printk("macfb: unknown or unsupported bit depth: %d\n", macfb_defined.bits_per_pixel);
+ printk("macfb: unknown or unsupported bit depth: %d\n",
+ macfb_defined.bits_per_pixel);
break;
}
- /* Hardware dependent stuff */
- /* We take a wild guess that if the video physical address is
- * in nubus slot space, that the nubus card is driving video.
- * Penguin really ought to tell us whether we are using internal
- * video or not.
+ /*
+ * We take a wild guess that if the video physical address is
+ * in nubus slot space, that the nubus card is driving video.
+ * Penguin really ought to tell us whether we are using internal
+ * video or not.
+ * Hopefully we only find one of them. Otherwise our NuBus
+ * code is really broken :-)
*/
- /* Hopefully we only find one of them. Otherwise our NuBus
- code is really broken :-) */
- while ((ndev = nubus_find_type(NUBUS_CAT_DISPLAY, NUBUS_TYPE_VIDEO, ndev))
- != NULL)
+ while ((ndev = nubus_find_type(NUBUS_CAT_DISPLAY,
+ NUBUS_TYPE_VIDEO, ndev)))
{
- if (!(mac_bi_data.videoaddr >= ndev->board->slot_addr
- && (mac_bi_data.videoaddr <
- (unsigned long)nubus_slot_addr(ndev->board->slot+1))))
+ unsigned long base = ndev->board->slot_addr;
+
+ if (mac_bi_data.videoaddr < base ||
+ mac_bi_data.videoaddr - base > 0xFFFFFF)
continue;
+
video_is_nubus = 1;
- /* We should probably just use the slot address... */
- video_slot = ndev->board->slot;
+ slot_addr = (unsigned char *)base;
switch(ndev->dr_hw) {
case NUBUS_DRHW_APPLE_MDC:
@@ -771,7 +703,7 @@ static int __init macfb_init(void)
strcpy(macfb_fix.id, "Jet");
macfb_setpalette = jet_setpalette;
macfb_defined.activate = FB_ACTIVATE_NOW;
- break;
+ break;
default:
strcpy(macfb_fix.id, "Generic NuBus");
break;
@@ -779,30 +711,19 @@ static int __init macfb_init(void)
}
/* If it's not a NuBus card, it must be internal video */
- /* FIXME: this function is getting way too big. (this driver
- is too...) */
if (!video_is_nubus)
- switch( mac_bi_data.id )
- {
- /* Valkyrie Quadras */
- case MAC_MODEL_Q630:
- /* I'm not sure about this one */
- case MAC_MODEL_P588:
- strcpy(macfb_fix.id, "Valkyrie");
- macfb_setpalette = valkyrie_setpalette;
- macfb_defined.activate = FB_ACTIVATE_NOW;
- valkyrie_cmap_regs = ioremap(DAC_BASE, 0x1000);
- break;
-
- /* DAFB Quadras */
- /* Note: these first four have the v7 DAFB, which is
- known to be rather unlike the ones used in the
- other models */
+ switch (mac_bi_data.id) {
+ /*
+ * DAFB Quadras
+ * Note: these first four have the v7 DAFB, which is
+ * known to be rather unlike the ones used in the
+ * other models
+ */
case MAC_MODEL_P475:
case MAC_MODEL_P475F:
case MAC_MODEL_P575:
case MAC_MODEL_Q605:
-
+
case MAC_MODEL_Q800:
case MAC_MODEL_Q650:
case MAC_MODEL_Q610:
@@ -817,17 +738,21 @@ static int __init macfb_init(void)
dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000);
break;
- /* LC II uses the V8 framebuffer */
+ /*
+ * LC II uses the V8 framebuffer
+ */
case MAC_MODEL_LCII:
strcpy(macfb_fix.id, "V8");
macfb_setpalette = v8_brazil_setpalette;
macfb_defined.activate = FB_ACTIVATE_NOW;
v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
break;
-
- /* IIvi, IIvx use the "Brazil" framebuffer (which is
- very much like the V8, it seems, and probably uses
- the same DAC) */
+
+ /*
+ * IIvi, IIvx use the "Brazil" framebuffer (which is
+ * very much like the V8, it seems, and probably uses
+ * the same DAC)
+ */
case MAC_MODEL_IIVI:
case MAC_MODEL_IIVX:
case MAC_MODEL_P600:
@@ -836,12 +761,14 @@ static int __init macfb_init(void)
macfb_defined.activate = FB_ACTIVATE_NOW;
v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
break;
-
- /* LC III (and friends) use the Sonora framebuffer */
- /* Incidentally this is also used in the non-AV models
- of the x100 PowerMacs */
- /* These do in fact seem to use the same DAC interface
- as the LC II. */
+
+ /*
+ * LC III (and friends) use the Sonora framebuffer
+ * Incidentally this is also used in the non-AV models
+ * of the x100 PowerMacs
+ * These do in fact seem to use the same DAC interface
+ * as the LC II.
+ */
case MAC_MODEL_LCIII:
case MAC_MODEL_P520:
case MAC_MODEL_P550:
@@ -852,9 +779,11 @@ static int __init macfb_init(void)
v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
break;
- /* IIci and IIsi use the infamous RBV chip
- (the IIsi is just a rebadged and crippled
- IIci in a different case, BTW) */
+ /*
+ * IIci and IIsi use the infamous RBV chip
+ * (the IIsi is just a rebadged and crippled
+ * IIci in a different case, BTW)
+ */
case MAC_MODEL_IICI:
case MAC_MODEL_IISI:
macfb_setpalette = rbv_setpalette;
@@ -863,7 +792,9 @@ static int __init macfb_init(void)
rbv_cmap_regs = ioremap(DAC_BASE, 0x1000);
break;
- /* AVs use the Civic framebuffer */
+ /*
+ * AVs use the Civic framebuffer
+ */
case MAC_MODEL_Q840:
case MAC_MODEL_C660:
macfb_setpalette = civic_setpalette;
@@ -873,15 +804,10 @@ static int __init macfb_init(void)
break;
- /* Write a setpalette function for your machine, then
- you can add something similar here. These are
- grouped by classes of video chipsets. Some of this
- information is from the VideoToolbox "Bugs" web
- page at
- http://rajsky.psych.nyu.edu/Tips/VideoBugs.html */
-
- /* Assorted weirdos */
- /* We think this may be like the LC II */
+ /*
+ * Assorted weirdos
+ * We think this may be like the LC II
+ */
case MAC_MODEL_LC:
if (vidtest) {
macfb_setpalette = v8_brazil_setpalette;
@@ -891,7 +817,10 @@ static int __init macfb_init(void)
}
strcpy(macfb_fix.id, "LC");
break;
- /* We think this may be like the LC II */
+
+ /*
+ * We think this may be like the LC II
+ */
case MAC_MODEL_CCL:
if (vidtest) {
macfb_setpalette = v8_brazil_setpalette;
@@ -902,31 +831,42 @@ static int __init macfb_init(void)
strcpy(macfb_fix.id, "Color Classic");
break;
- /* And we *do* mean "weirdos" */
+ /*
+ * And we *do* mean "weirdos"
+ */
case MAC_MODEL_TV:
strcpy(macfb_fix.id, "Mac TV");
break;
- /* These don't have colour, so no need to worry */
+ /*
+ * These don't have colour, so no need to worry
+ */
case MAC_MODEL_SE30:
case MAC_MODEL_CLII:
strcpy(macfb_fix.id, "Monochrome");
break;
- /* Powerbooks are particularly difficult. Many of
- them have separate framebuffers for external and
- internal video, which is admittedly pretty cool,
- but will be a bit of a headache to support here.
- Also, many of them are grayscale, and we don't
- really support that. */
-
+ /*
+ * Powerbooks are particularly difficult. Many of
+ * them have separate framebuffers for external and
+ * internal video, which is admittedly pretty cool,
+ * but will be a bit of a headache to support here.
+ * Also, many of them are grayscale, and we don't
+ * really support that.
+ */
+
+ /*
+ * Slot 0 ROM says TIM. No external video. B&W.
+ */
case MAC_MODEL_PB140:
case MAC_MODEL_PB145:
case MAC_MODEL_PB170:
strcpy(macfb_fix.id, "DDC");
break;
- /* Internal is GSC, External (if present) is ViSC */
+ /*
+ * Internal is GSC, External (if present) is ViSC
+ */
case MAC_MODEL_PB150: /* no external video */
case MAC_MODEL_PB160:
case MAC_MODEL_PB165:
@@ -936,13 +876,17 @@ static int __init macfb_init(void)
strcpy(macfb_fix.id, "GSC");
break;
- /* Internal is TIM, External is ViSC */
+ /*
+ * Internal is TIM, External is ViSC
+ */
case MAC_MODEL_PB165C:
case MAC_MODEL_PB180C:
strcpy(macfb_fix.id, "TIM");
break;
- /* Internal is CSC, External is Keystone+Ariel. */
+ /*
+ * Internal is CSC, External is Keystone+Ariel.
+ */
case MAC_MODEL_PB190: /* external video is optional */
case MAC_MODEL_PB520:
case MAC_MODEL_PB250:
@@ -954,7 +898,7 @@ static int __init macfb_init(void)
strcpy(macfb_fix.id, "CSC");
csc_cmap_regs = ioremap(CSC_BASE, 0x1000);
break;
-
+
default:
strcpy(macfb_fix.id, "Unknown");
break;
@@ -969,7 +913,7 @@ static int __init macfb_init(void)
err = fb_alloc_cmap(&fb_info.cmap, video_cmap_len, 0);
if (err)
goto fail_unmap;
-
+
err = register_framebuffer(&fb_info);
if (err)
goto fail_dealloc;
diff --git a/drivers/video/macmodes.c b/drivers/video/macmodes.c
index 083f60321ed..af86c081d2b 100644
--- a/drivers/video/macmodes.c
+++ b/drivers/video/macmodes.c
@@ -33,6 +33,10 @@
static const struct fb_videomode mac_modedb[] = {
{
+ /* 512x384, 60Hz, Non-Interlaced (15.67 MHz dot clock) */
+ "mac2", 60, 512, 384, 63828, 80, 16, 19, 1, 32, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
"mac5", 60, 640, 480, 39722, 32, 32, 33, 10, 96, 2,
0, FB_VMODE_NONINTERLACED
@@ -41,6 +45,10 @@ static const struct fb_videomode mac_modedb[] = {
"mac6", 67, 640, 480, 33334, 80, 80, 39, 3, 64, 3,
0, FB_VMODE_NONINTERLACED
}, {
+ /* 640x870, 75Hz (portrait), Non-Interlaced (57.28 MHz dot clock) */
+ "mac7", 75, 640, 870, 17457, 80, 32, 42, 3, 80, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
/* 800x600, 56 Hz, Non-Interlaced (36.00 MHz dotclock) */
"mac9", 56, 800, 600, 27778, 112, 40, 22, 1, 72, 2,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
@@ -105,10 +113,6 @@ static const struct fb_videomode mac_modedb[] = {
"mac1", 60, 512, 384, pixclock, left, right, upper, lower, hslen, vslen,
sync, FB_VMODE_INTERLACED
}, {
- /* VMODE_512_384_60: 512x384, 60Hz, Non-Interlaced */
- "mac2", 60, 512, 384, pixclock, left, right, upper, lower, hslen, vslen,
- sync, FB_VMODE_NONINTERLACED
- }, {
/* VMODE_640_480_50I: 640x480, 50Hz, Interlaced (PAL) */
"mac3", 50, 640, 480, pixclock, left, right, upper, lower, hslen, vslen,
sync, FB_VMODE_INTERLACED
@@ -117,10 +121,6 @@ static const struct fb_videomode mac_modedb[] = {
"mac4", 60, 640, 480, pixclock, left, right, upper, lower, hslen, vslen,
sync, FB_VMODE_INTERLACED
}, {
- /* VMODE_640_870_75P: 640x870, 75Hz (portrait), Non-Interlaced */
- "mac7", 75, 640, 870, pixclock, left, right, upper, lower, hslen, vslen,
- sync, FB_VMODE_NONINTERLACED
- }, {
/* VMODE_768_576_50I: 768x576, 50Hz (PAL full frame), Interlaced */
"mac8", 50, 768, 576, pixclock, left, right, upper, lower, hslen, vslen,
sync, FB_VMODE_INTERLACED
@@ -134,38 +134,42 @@ static const struct fb_videomode mac_modedb[] = {
*
* These MUST be ordered in
* - increasing resolution
- * - decreasing refresh rate
+ * - decreasing pixel clock period
*/
static const struct mode_map {
int vmode;
const struct fb_videomode *mode;
} mac_modes[] = {
+ /* 512x384 */
+ { VMODE_512_384_60, &mac_modedb[0] },
/* 640x480 */
- { VMODE_640_480_67, &mac_modedb[1] },
- { VMODE_640_480_60, &mac_modedb[0] },
+ { VMODE_640_480_60, &mac_modedb[1] },
+ { VMODE_640_480_67, &mac_modedb[2] },
+ /* 640x870 */
+ { VMODE_640_870_75P, &mac_modedb[3] },
/* 800x600 */
- { VMODE_800_600_75, &mac_modedb[5] },
- { VMODE_800_600_72, &mac_modedb[4] },
- { VMODE_800_600_60, &mac_modedb[3] },
- { VMODE_800_600_56, &mac_modedb[2] },
+ { VMODE_800_600_56, &mac_modedb[4] },
+ { VMODE_800_600_60, &mac_modedb[5] },
+ { VMODE_800_600_75, &mac_modedb[7] },
+ { VMODE_800_600_72, &mac_modedb[6] },
/* 832x624 */
- { VMODE_832_624_75, &mac_modedb[6] },
+ { VMODE_832_624_75, &mac_modedb[8] },
/* 1024x768 */
- { VMODE_1024_768_75, &mac_modedb[10] },
- { VMODE_1024_768_75V, &mac_modedb[9] },
- { VMODE_1024_768_70, &mac_modedb[8] },
- { VMODE_1024_768_60, &mac_modedb[7] },
+ { VMODE_1024_768_60, &mac_modedb[9] },
+ { VMODE_1024_768_70, &mac_modedb[10] },
+ { VMODE_1024_768_75V, &mac_modedb[11] },
+ { VMODE_1024_768_75, &mac_modedb[12] },
/* 1152x768 */
- { VMODE_1152_768_60, &mac_modedb[14] },
+ { VMODE_1152_768_60, &mac_modedb[16] },
/* 1152x870 */
- { VMODE_1152_870_75, &mac_modedb[11] },
+ { VMODE_1152_870_75, &mac_modedb[13] },
/* 1280x960 */
- { VMODE_1280_960_75, &mac_modedb[12] },
+ { VMODE_1280_960_75, &mac_modedb[14] },
/* 1280x1024 */
- { VMODE_1280_1024_75, &mac_modedb[13] },
+ { VMODE_1280_1024_75, &mac_modedb[15] },
/* 1600x1024 */
- { VMODE_1600_1024_60, &mac_modedb[15] },
+ { VMODE_1600_1024_60, &mac_modedb[17] },
{ -1, NULL }
};
@@ -299,7 +303,6 @@ EXPORT_SYMBOL(mac_vmode_to_var);
int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode,
int *cmode)
{
- const struct fb_videomode *mode = NULL;
const struct mode_map *map;
if (var->bits_per_pixel <= 8)
@@ -311,8 +314,13 @@ int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode,
else
return -EINVAL;
+ /*
+ * Find the mac_mode with a matching resolution or failing that, the
+ * closest larger resolution. Skip modes with a shorter pixel clock period.
+ */
for (map = mac_modes; map->vmode != -1; map++) {
- mode = map->mode;
+ const struct fb_videomode *mode = map->mode;
+
if (var->xres > mode->xres || var->yres > mode->yres)
continue;
if (var->xres_virtual > mode->xres || var->yres_virtual > mode->yres)
@@ -322,6 +330,24 @@ int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode,
if ((var->vmode & FB_VMODE_MASK) != mode->vmode)
continue;
*vmode = map->vmode;
+
+ /*
+ * Having found a good resolution, find the matching pixel clock
+ * or failing that, the closest longer pixel clock period.
+ */
+ map++;
+ while (map->vmode != -1) {
+ const struct fb_videomode *clk_mode = map->mode;
+
+ if (mode->xres != clk_mode->xres || mode->yres != clk_mode->yres)
+ break;
+ if (var->pixclock > mode->pixclock)
+ break;
+ if (mode->vmode != clk_mode->vmode)
+ continue;
+ *vmode = map->vmode;
+ map++;
+ }
return 0;
}
return -EINVAL;
diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c
index 53f8f1100e8..f9975100d56 100644
--- a/drivers/video/pvr2fb.c
+++ b/drivers/video/pvr2fb.c
@@ -831,7 +831,7 @@ static int __devinit pvr2fb_common_init(void)
printk(KERN_NOTICE "fb%d: registering with SQ API\n", fb_info->node);
pvr2fb_map = sq_remap(fb_info->fix.smem_start, fb_info->fix.smem_len,
- fb_info->fix.id, pgprot_val(PAGE_SHARED));
+ fb_info->fix.id, PAGE_SHARED);
printk(KERN_NOTICE "fb%d: Mapped video memory to SQ addr 0x%lx\n",
fb_info->node, pvr2fb_map);
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index a69830d26f7..8d7653e56df 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -19,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
+#include <linux/ioctl.h>
#include <video/sh_mobile_lcdc.h>
#include <asm/atomic.h>
@@ -106,6 +107,7 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
#define LDRCNTR_SRC 0x00010000
#define LDRCNTR_MRS 0x00000002
#define LDRCNTR_MRC 0x00000001
+#define LDSR_MRS 0x00000100
struct sh_mobile_lcdc_priv;
struct sh_mobile_lcdc_chan {
@@ -122,8 +124,8 @@ struct sh_mobile_lcdc_chan {
struct scatterlist *sglist;
unsigned long frame_end;
unsigned long pan_offset;
- unsigned long new_pan_offset;
wait_queue_head_t frame_end_wait;
+ struct completion vsync_completion;
};
struct sh_mobile_lcdc_priv {
@@ -366,19 +368,8 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
}
/* VSYNC End */
- if (ldintr & LDINTR_VES) {
- unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR);
- /* Set the source address for the next refresh */
- lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle +
- ch->new_pan_offset);
- if (lcdc_chan_is_sublcd(ch))
- lcdc_write(ch->lcdc, _LDRCNTR,
- ldrcntr ^ LDRCNTR_SRS);
- else
- lcdc_write(ch->lcdc, _LDRCNTR,
- ldrcntr ^ LDRCNTR_MRS);
- ch->pan_offset = ch->new_pan_offset;
- }
+ if (ldintr & LDINTR_VES)
+ complete(&ch->vsync_completion);
}
return IRQ_HANDLED;
@@ -767,25 +758,69 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
+ struct sh_mobile_lcdc_priv *priv = ch->lcdc;
+ unsigned long ldrcntr;
+ unsigned long new_pan_offset;
+
+ new_pan_offset = (var->yoffset * info->fix.line_length) +
+ (var->xoffset * (info->var.bits_per_pixel / 8));
- if (info->var.xoffset == var->xoffset &&
- info->var.yoffset == var->yoffset)
+ if (new_pan_offset == ch->pan_offset)
return 0; /* No change, do nothing */
- ch->new_pan_offset = (var->yoffset * info->fix.line_length) +
- (var->xoffset * (info->var.bits_per_pixel / 8));
+ ldrcntr = lcdc_read(priv, _LDRCNTR);
- if (ch->new_pan_offset != ch->pan_offset) {
- unsigned long ldintr;
- ldintr = lcdc_read(ch->lcdc, _LDINTR);
- ldintr |= LDINTR_VEE;
- lcdc_write(ch->lcdc, _LDINTR, ldintr);
- sh_mobile_lcdc_deferred_io_touch(info);
- }
+ /* Set the source address for the next refresh */
+ lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + new_pan_offset);
+ if (lcdc_chan_is_sublcd(ch))
+ lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
+ else
+ lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);
+
+ ch->pan_offset = new_pan_offset;
+
+ sh_mobile_lcdc_deferred_io_touch(info);
+
+ return 0;
+}
+
+static int sh_mobile_wait_for_vsync(struct fb_info *info)
+{
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ unsigned long ldintr;
+ int ret;
+
+ /* Enable VSync End interrupt */
+ ldintr = lcdc_read(ch->lcdc, _LDINTR);
+ ldintr |= LDINTR_VEE;
+ lcdc_write(ch->lcdc, _LDINTR, ldintr);
+
+ ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
+ msecs_to_jiffies(100));
+ if (!ret)
+ return -ETIMEDOUT;
return 0;
}
+static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ int retval;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ retval = sh_mobile_wait_for_vsync(info);
+ break;
+
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+ return retval;
+}
+
+
static struct fb_ops sh_mobile_lcdc_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = sh_mobile_lcdc_setcolreg,
@@ -795,6 +830,7 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_copyarea = sh_mobile_lcdc_copyarea,
.fb_imageblit = sh_mobile_lcdc_imageblit,
.fb_pan_display = sh_mobile_fb_pan_display,
+ .fb_ioctl = sh_mobile_ioctl,
};
static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -962,8 +998,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
init_waitqueue_head(&priv->ch[i].frame_end_wait);
+ init_completion(&priv->ch[i].vsync_completion);
priv->ch[j].pan_offset = 0;
- priv->ch[j].new_pan_offset = 0;
switch (pdata->ch[i].chan) {
case LCDC_CHAN_MAINLCD:
diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c
index 4bb9a0b1895..6b52bf65f0b 100644
--- a/drivers/video/valkyriefb.c
+++ b/drivers/video/valkyriefb.c
@@ -69,7 +69,7 @@
#ifdef CONFIG_MAC
/* We don't yet have functions to read the PRAM... perhaps we can
adapt them from the PPC code? */
-static int default_vmode = VMODE_640_480_67;
+static int default_vmode = VMODE_CHOOSE;
static int default_cmode = CMODE_8;
#else
static int default_vmode = VMODE_NVRAM;
@@ -326,11 +326,11 @@ int __init valkyriefb_init(void)
#ifdef CONFIG_MAC
if (!MACH_IS_MAC)
- return 0;
+ return -ENODEV;
if (!(mac_bi_data.id == MAC_MODEL_Q630
/* I'm not sure about this one */
|| mac_bi_data.id == MAC_MODEL_P588))
- return 0;
+ return -ENODEV;
/* Hardcoded addresses... welcome to 68k Macintosh country :-) */
frame_buffer_phys = 0xf9000000;
diff --git a/drivers/video/valkyriefb.h b/drivers/video/valkyriefb.h
index 97aaf7bb641..d787441e5a4 100644
--- a/drivers/video/valkyriefb.h
+++ b/drivers/video/valkyriefb.h
@@ -134,15 +134,7 @@ static struct valkyrie_regvals valkyrie_reg_init_14 = {
{ 1024, 0 },
1024, 768
};
-
-/* Register values for 800x600, 72Hz mode (11) */
-static struct valkyrie_regvals valkyrie_reg_init_11 = {
- 13,
- { 17, 27, 3 }, /* pixel clock = 49.63MHz for V=71.66Hz */
- { 800, 0 },
- 800, 600
-};
-#endif /* CONFIG_MAC */
+#endif /* !defined CONFIG_MAC */
/* Register values for 832x624, 75Hz mode (13) */
static struct valkyrie_regvals valkyrie_reg_init_13 = {
@@ -152,6 +144,14 @@ static struct valkyrie_regvals valkyrie_reg_init_13 = {
832, 624
};
+/* Register values for 800x600, 72Hz mode (11) */
+static struct valkyrie_regvals valkyrie_reg_init_11 = {
+ 13,
+ { 17, 27, 3 }, /* pixel clock = 49.63MHz for V=71.66Hz */
+ { 800, 0 },
+ 800, 600
+};
+
/* Register values for 800x600, 60Hz mode (10) */
static struct valkyrie_regvals valkyrie_reg_init_10 = {
12,
@@ -188,24 +188,13 @@ static struct valkyrie_regvals *valkyrie_reg_init[VMODE_MAX] = {
NULL,
NULL,
&valkyrie_reg_init_10,
-#ifdef CONFIG_MAC
- NULL,
- NULL,
- &valkyrie_reg_init_13,
- NULL,
- NULL,
- NULL,
- NULL,
-#else
&valkyrie_reg_init_11,
NULL,
&valkyrie_reg_init_13,
+#ifndef CONFIG_MAC
&valkyrie_reg_init_14,
&valkyrie_reg_init_15,
NULL,
&valkyrie_reg_init_17,
#endif
- NULL,
- NULL,
- NULL
};
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 505be88c82a..369f2eebbad 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -28,7 +28,7 @@
struct virtio_balloon
{
struct virtio_device *vdev;
- struct virtqueue *inflate_vq, *deflate_vq;
+ struct virtqueue *inflate_vq, *deflate_vq, *stats_vq;
/* Where the ballooning thread waits for config to change. */
wait_queue_head_t config_change;
@@ -49,6 +49,10 @@ struct virtio_balloon
/* The array of pfns we tell the Host about. */
unsigned int num_pfns;
u32 pfns[256];
+
+ /* Memory statistics */
+ int need_stats_update;
+ struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR];
};
static struct virtio_device_id id_table[] = {
@@ -154,6 +158,72 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num)
}
}
+static inline void update_stat(struct virtio_balloon *vb, int idx,
+ u16 tag, u64 val)
+{
+ BUG_ON(idx >= VIRTIO_BALLOON_S_NR);
+ vb->stats[idx].tag = tag;
+ vb->stats[idx].val = val;
+}
+
+#define pages_to_bytes(x) ((u64)(x) << PAGE_SHIFT)
+
+static void update_balloon_stats(struct virtio_balloon *vb)
+{
+ unsigned long events[NR_VM_EVENT_ITEMS];
+ struct sysinfo i;
+ int idx = 0;
+
+ all_vm_events(events);
+ si_meminfo(&i);
+
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_IN,
+ pages_to_bytes(events[PSWPIN]));
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_OUT,
+ pages_to_bytes(events[PSWPOUT]));
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]);
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]);
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE,
+ pages_to_bytes(i.freeram));
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMTOT,
+ pages_to_bytes(i.totalram));
+}
+
+/*
+ * While most virtqueues communicate guest-initiated requests to the hypervisor,
+ * the stats queue operates in reverse. The driver initializes the virtqueue
+ * with a single buffer. From that point forward, all conversations consist of
+ * a hypervisor request (a call to this function) which directs us to refill
+ * the virtqueue with a fresh stats buffer. Since stats collection can sleep,
+ * we notify our kthread which does the actual work via stats_handle_request().
+ */
+static void stats_request(struct virtqueue *vq)
+{
+ struct virtio_balloon *vb;
+ unsigned int len;
+
+ vb = vq->vq_ops->get_buf(vq, &len);
+ if (!vb)
+ return;
+ vb->need_stats_update = 1;
+ wake_up(&vb->config_change);
+}
+
+static void stats_handle_request(struct virtio_balloon *vb)
+{
+ struct virtqueue *vq;
+ struct scatterlist sg;
+
+ vb->need_stats_update = 0;
+ update_balloon_stats(vb);
+
+ vq = vb->stats_vq;
+ sg_init_one(&sg, vb->stats, sizeof(vb->stats));
+ if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0)
+ BUG();
+ vq->vq_ops->kick(vq);
+}
+
static void virtballoon_changed(struct virtio_device *vdev)
{
struct virtio_balloon *vb = vdev->priv;
@@ -190,8 +260,11 @@ static int balloon(void *_vballoon)
try_to_freeze();
wait_event_interruptible(vb->config_change,
(diff = towards_target(vb)) != 0
+ || vb->need_stats_update
|| kthread_should_stop()
|| freezing(current));
+ if (vb->need_stats_update)
+ stats_handle_request(vb);
if (diff > 0)
fill_balloon(vb, diff);
else if (diff < 0)
@@ -204,10 +277,10 @@ static int balloon(void *_vballoon)
static int virtballoon_probe(struct virtio_device *vdev)
{
struct virtio_balloon *vb;
- struct virtqueue *vqs[2];
- vq_callback_t *callbacks[] = { balloon_ack, balloon_ack };
- const char *names[] = { "inflate", "deflate" };
- int err;
+ struct virtqueue *vqs[3];
+ vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_request };
+ const char *names[] = { "inflate", "deflate", "stats" };
+ int err, nvqs;
vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
if (!vb) {
@@ -219,14 +292,31 @@ static int virtballoon_probe(struct virtio_device *vdev)
vb->num_pages = 0;
init_waitqueue_head(&vb->config_change);
vb->vdev = vdev;
+ vb->need_stats_update = 0;
- /* We expect two virtqueues. */
- err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+ /* We expect two virtqueues: inflate and deflate,
+ * and optionally stat. */
+ nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2;
+ err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names);
if (err)
goto out_free_vb;
vb->inflate_vq = vqs[0];
vb->deflate_vq = vqs[1];
+ if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) {
+ struct scatterlist sg;
+ vb->stats_vq = vqs[2];
+
+ /*
+ * Prime this virtqueue with one buffer so the hypervisor can
+ * use it to signal us later.
+ */
+ sg_init_one(&sg, vb->stats, sizeof vb->stats);
+ if (vb->stats_vq->vq_ops->add_buf(vb->stats_vq,
+ &sg, 1, 0, vb) < 0)
+ BUG();
+ vb->stats_vq->vq_ops->kick(vb->stats_vq);
+ }
vb->thread = kthread_run(balloon, vb, "vballoon");
if (IS_ERR(vb->thread)) {
@@ -264,7 +354,10 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev)
kfree(vb);
}
-static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST };
+static unsigned int features[] = {
+ VIRTIO_BALLOON_F_MUST_TELL_HOST,
+ VIRTIO_BALLOON_F_STATS_VQ,
+};
static struct virtio_driver virtio_balloon_driver = {
.feature_table = features,
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 28d9cf7cf72..1d5191fab62 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -702,7 +702,7 @@ static struct pci_driver virtio_pci_driver = {
.name = "virtio-pci",
.id_table = virtio_pci_id_table,
.probe = virtio_pci_probe,
- .remove = virtio_pci_remove,
+ .remove = __devexit_p(virtio_pci_remove),
#ifdef CONFIG_PM
.suspend = virtio_pci_suspend,
.resume = virtio_pci_resume,
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index fbd2ecde93e..0db906b3c95 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -21,6 +21,24 @@
#include <linux/virtio_config.h>
#include <linux/device.h>
+/* virtio guest is communicating with a virtual "device" that actually runs on
+ * a host processor. Memory barriers are used to control SMP effects. */
+#ifdef CONFIG_SMP
+/* Where possible, use SMP barriers which are more lightweight than mandatory
+ * barriers, because mandatory barriers control MMIO effects on accesses
+ * through relaxed memory I/O windows (which virtio does not use). */
+#define virtio_mb() smp_mb()
+#define virtio_rmb() smp_rmb()
+#define virtio_wmb() smp_wmb()
+#else
+/* We must force memory ordering even if guest is UP since host could be
+ * running on another CPU, but SMP barriers are defined to barrier() in that
+ * configuration. So fall back to mandatory barriers instead. */
+#define virtio_mb() mb()
+#define virtio_rmb() rmb()
+#define virtio_wmb() wmb()
+#endif
+
#ifdef DEBUG
/* For development, we want to crash whenever the ring is screwed. */
#define BAD_RING(_vq, fmt, args...) \
@@ -36,10 +54,9 @@
panic("%s:in_use = %i\n", \
(_vq)->vq.name, (_vq)->in_use); \
(_vq)->in_use = __LINE__; \
- mb(); \
} while (0)
#define END_USE(_vq) \
- do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0)
+ do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; } while(0)
#else
#define BAD_RING(_vq, fmt, args...) \
do { \
@@ -221,13 +238,13 @@ static void vring_kick(struct virtqueue *_vq)
START_USE(vq);
/* Descriptors and available array need to be set before we expose the
* new available array entries. */
- wmb();
+ virtio_wmb();
vq->vring.avail->idx += vq->num_added;
vq->num_added = 0;
/* Need to update avail index before checking if we should notify */
- mb();
+ virtio_mb();
if (!(vq->vring.used->flags & VRING_USED_F_NO_NOTIFY))
/* Prod other side to tell it about changes. */
@@ -286,7 +303,7 @@ static void *vring_get_buf(struct virtqueue *_vq, unsigned int *len)
}
/* Only get used array entries after they have been exposed by host. */
- rmb();
+ virtio_rmb();
i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id;
*len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len;
@@ -324,7 +341,7 @@ static bool vring_enable_cb(struct virtqueue *_vq)
/* We optimistically turn back on interrupts, then check if there was
* more to do. */
vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
- mb();
+ virtio_mb();
if (unlikely(more_used(vq))) {
END_USE(vq);
return false;
@@ -334,6 +351,30 @@ static bool vring_enable_cb(struct virtqueue *_vq)
return true;
}
+static void *vring_detach_unused_buf(struct virtqueue *_vq)
+{
+ struct vring_virtqueue *vq = to_vvq(_vq);
+ unsigned int i;
+ void *buf;
+
+ START_USE(vq);
+
+ for (i = 0; i < vq->vring.num; i++) {
+ if (!vq->data[i])
+ continue;
+ /* detach_buf clears data, so grab it now. */
+ buf = vq->data[i];
+ detach_buf(vq, i);
+ END_USE(vq);
+ return buf;
+ }
+ /* That should have freed everything. */
+ BUG_ON(vq->num_free != vq->vring.num);
+
+ END_USE(vq);
+ return NULL;
+}
+
irqreturn_t vring_interrupt(int irq, void *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
@@ -360,6 +401,7 @@ static struct virtqueue_ops vring_vq_ops = {
.kick = vring_kick,
.disable_cb = vring_disable_cb,
.enable_cb = vring_enable_cb,
+ .detach_unused_buf = vring_detach_unused_buf,
};
struct virtqueue *vring_new_virtqueue(unsigned int num,
@@ -406,8 +448,11 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
/* Put everything in free lists. */
vq->num_free = num;
vq->free_head = 0;
- for (i = 0; i < num-1; i++)
+ for (i = 0; i < num-1; i++) {
vq->vring.desc[i].next = i+1;
+ vq->data[i] = NULL;
+ }
+ vq->data[i] = NULL;
return &vq->vq;
}
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 2e94b71b20d..2bb95cd308c 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -34,6 +34,7 @@
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>
+#include <linux/clk.h>
#include <asm/addrspace.h>
#include <asm/mach-ar7/ar7.h>
@@ -80,6 +81,8 @@ static struct resource *ar7_regs_wdt;
/* Pointer to the remapped WDT IO space */
static struct ar7_wdt *ar7_wdt;
+static struct clk *vbus_clk;
+
static void ar7_wdt_kick(u32 value)
{
WRITE_REG(ar7_wdt->kick_lock, 0x5555);
@@ -138,17 +141,19 @@ static void ar7_wdt_disable(u32 value)
static void ar7_wdt_update_margin(int new_margin)
{
u32 change;
+ u32 vbus_rate;
- change = new_margin * (ar7_vbus_freq() / prescale_value);
+ vbus_rate = clk_get_rate(vbus_clk);
+ change = new_margin * (vbus_rate / prescale_value);
if (change < 1)
change = 1;
if (change > 0xffff)
change = 0xffff;
ar7_wdt_change(change);
- margin = change * prescale_value / ar7_vbus_freq();
+ margin = change * prescale_value / vbus_rate;
printk(KERN_INFO DRVNAME
": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
- margin, prescale_value, change, ar7_vbus_freq());
+ margin, prescale_value, change, vbus_rate);
}
static void ar7_wdt_enable_wdt(void)
@@ -298,6 +303,13 @@ static int __devinit ar7_wdt_probe(struct platform_device *pdev)
goto out_mem_region;
}
+ vbus_clk = clk_get(NULL, "vbus");
+ if (IS_ERR(vbus_clk)) {
+ printk(KERN_ERR DRVNAME ": could not get vbus clock\n");
+ rc = PTR_ERR(vbus_clk);
+ goto out_mem_region;
+ }
+
ar7_wdt_disable_wdt();
ar7_wdt_prescale(prescale_value);
ar7_wdt_update_margin(margin);
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c
index c7b3f9df231..2159e668751 100644
--- a/drivers/watchdog/bfin_wdt.c
+++ b/drivers/watchdog/bfin_wdt.c
@@ -1,9 +1,8 @@
/*
* Blackfin On-Chip Watchdog Driver
- * Supports BF53[123]/BF53[467]/BF54[2489]/BF561
*
* Originally based on softdog.c
- * Copyright 2006-2007 Analog Devices Inc.
+ * Copyright 2006-2010 Analog Devices Inc.
* Copyright 2006-2007 Michele d'Amico
* Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
@@ -137,13 +136,15 @@ static int bfin_wdt_running(void)
*/
static int bfin_wdt_set_timeout(unsigned long t)
{
- u32 cnt;
+ u32 cnt, max_t, sclk;
unsigned long flags;
- stampit();
+ sclk = get_sclk();
+ max_t = -1 / sclk;
+ cnt = t * sclk;
+ stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt);
- cnt = t * get_sclk();
- if (cnt < get_sclk()) {
+ if (t > max_t) {
printk(KERN_WARNING PFX "timeout value is too large\n");
return -EINVAL;
}
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 430a5848a9a..c7a9479934a 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -96,9 +96,6 @@ static void wdt_enable(void)
{
spin_lock(&io_lock);
- if (wdt_clk)
- clk_set_rate(wdt_clk, 1);
-
/* stop counter, initiate counter reset */
__raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
/*wait for reset to complete. 100% guarantee event */
@@ -125,19 +122,25 @@ static void wdt_disable(void)
spin_lock(&io_lock);
__raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */
- if (wdt_clk)
- clk_set_rate(wdt_clk, 0);
spin_unlock(&io_lock);
}
static int pnx4008_wdt_open(struct inode *inode, struct file *file)
{
+ int ret;
+
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ ret = clk_enable(wdt_clk);
+ if (ret) {
+ clear_bit(WDT_IN_USE, &wdt_status);
+ return ret;
+ }
+
wdt_enable();
return nonseekable_open(inode, file);
@@ -225,6 +228,7 @@ static int pnx4008_wdt_release(struct inode *inode, struct file *file)
printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n");
wdt_disable();
+ clk_disable(wdt_clk);
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
@@ -273,25 +277,33 @@ static int __devinit pnx4008_wdt_probe(struct platform_device *pdev)
}
wdt_base = (void __iomem *)IO_ADDRESS(res->start);
- wdt_clk = clk_get(&pdev->dev, "wdt_ck");
+ wdt_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt_clk)) {
ret = PTR_ERR(wdt_clk);
release_resource(wdt_mem);
kfree(wdt_mem);
goto out;
- } else
- clk_set_rate(wdt_clk, 1);
+ }
+
+ ret = clk_enable(wdt_clk);
+ if (ret) {
+ release_resource(wdt_mem);
+ kfree(wdt_mem);
+ goto out;
+ }
ret = misc_register(&pnx4008_wdt_miscdev);
if (ret < 0) {
printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
release_resource(wdt_mem);
kfree(wdt_mem);
- clk_set_rate(wdt_clk, 0);
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
} else {
boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
WDIOF_CARDRESET : 0;
wdt_disable(); /*disable for now */
+ clk_disable(wdt_clk);
set_bit(WDT_DEVICE_INITED, &wdt_status);
}
@@ -302,11 +314,10 @@ out:
static int __devexit pnx4008_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&pnx4008_wdt_miscdev);
- if (wdt_clk) {
- clk_set_rate(wdt_clk, 0);
- clk_put(wdt_clk);
- wdt_clk = NULL;
- }
+
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+
if (wdt_mem) {
release_resource(wdt_mem);
kfree(wdt_mem);