summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/apei/erst.c1
-rw-r--r--drivers/acpi/debug.c32
-rw-r--r--drivers/ata/Kconfig11
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/libata-core.c2
-rw-r--r--drivers/ata/libata-scsi.c4
-rw-r--r--drivers/ata/pata_mpc52xx.c8
-rw-r--r--drivers/ata/pata_of_platform.c4
-rw-r--r--drivers/ata/pata_pxa.c411
-rw-r--r--drivers/ata/sata_fsl.c8
-rw-r--r--drivers/atm/fore200e.c26
-rw-r--r--drivers/block/DAC960.c13
-rw-r--r--drivers/block/amiflop.c29
-rw-r--r--drivers/block/aoe/aoeblk.c6
-rw-r--r--drivers/block/ataflop.c32
-rw-r--r--drivers/block/brd.c9
-rw-r--r--drivers/block/cciss.c2165
-rw-r--r--drivers/block/cciss.h135
-rw-r--r--drivers/block/cciss_cmd.h36
-rw-r--r--drivers/block/cciss_scsi.c670
-rw-r--r--drivers/block/cpqarray.c78
-rw-r--r--drivers/block/drbd/drbd_actlog.c8
-rw-r--r--drivers/block/drbd/drbd_int.h16
-rw-r--r--drivers/block/drbd/drbd_main.c102
-rw-r--r--drivers/block/drbd/drbd_nl.c4
-rw-r--r--drivers/block/drbd/drbd_proc.c19
-rw-r--r--drivers/block/drbd/drbd_receiver.c135
-rw-r--r--drivers/block/drbd/drbd_req.c2
-rw-r--r--drivers/block/drbd/drbd_worker.c15
-rw-r--r--drivers/block/floppy.c182
-rw-r--r--drivers/block/hd.c2
-rw-r--r--drivers/block/loop.c9
-rw-r--r--drivers/block/mg_disk.c4
-rw-r--r--drivers/block/nbd.c7
-rw-r--r--drivers/block/osdblk.c15
-rw-r--r--drivers/block/paride/pcd.c21
-rw-r--r--drivers/block/paride/pd.c11
-rw-r--r--drivers/block/paride/pf.c26
-rw-r--r--drivers/block/pktcdvd.c20
-rw-r--r--drivers/block/ps3disk.c25
-rw-r--r--drivers/block/swim.c20
-rw-r--r--drivers/block/swim3.c32
-rw-r--r--drivers/block/ub.c35
-rw-r--r--drivers/block/umem.c2
-rw-r--r--drivers/block/viodasd.c21
-rw-r--r--drivers/block/virtio_blk.c88
-rw-r--r--drivers/block/xd.c19
-rw-r--r--drivers/block/xen-blkfront.c403
-rw-r--r--drivers/block/xsysace.c12
-rw-r--r--drivers/block/z2ram.c13
-rw-r--r--drivers/cdrom/cdrom.c46
-rw-r--r--drivers/cdrom/gdrom.c48
-rw-r--r--drivers/cdrom/viocd.c106
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/amiserial.c25
-rw-r--r--drivers/char/briq_panel.c6
-rw-r--r--drivers/char/cyclades.c22
-rw-r--r--drivers/char/epca.c4
-rw-r--r--drivers/char/hvc_iucv.c9
-rw-r--r--drivers/char/hw_random/n2-drv.c4
-rw-r--r--drivers/char/hw_random/n2rng.h2
-rw-r--r--drivers/char/hw_random/pasemi-rng.c4
-rw-r--r--drivers/char/ip2/ip2main.c4
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c61
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c42
-rw-r--r--drivers/char/isicom.c13
-rw-r--r--drivers/char/istallion.c68
-rw-r--r--drivers/char/keyboard.c10
-rw-r--r--drivers/char/mxser.c2
-rw-r--r--drivers/char/n_gsm.c3
-rw-r--r--drivers/char/n_hdlc.c16
-rw-r--r--drivers/char/n_r3964.c10
-rw-r--r--drivers/char/n_tty.c17
-rw-r--r--drivers/char/nozomi.c7
-rw-r--r--drivers/char/pty.c47
-rw-r--r--drivers/char/riscom8.c14
-rw-r--r--drivers/char/rocket.c28
-rw-r--r--drivers/char/rtc.c2
-rw-r--r--drivers/char/selection.c13
-rw-r--r--drivers/char/serial167.c8
-rw-r--r--drivers/char/specialix.c13
-rw-r--r--drivers/char/stallion.c20
-rw-r--r--drivers/char/sx.c12
-rw-r--r--drivers/char/synclink.c21
-rw-r--r--drivers/char/synclink_gt.c92
-rw-r--r--drivers/char/synclinkmp.c43
-rw-r--r--drivers/char/tty_io.c150
-rw-r--r--drivers/char/tty_ioctl.c18
-rw-r--r--drivers/char/tty_ldisc.c43
-rw-r--r--drivers/char/tty_mutex.c47
-rw-r--r--drivers/char/tty_port.c4
-rw-r--r--drivers/char/vc_screen.c4
-rw-r--r--drivers/char/vt.c37
-rw-r--r--drivers/char/vt_ioctl.c17
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c4
-rw-r--r--drivers/crypto/Kconfig2
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.c4
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.h2
-rw-r--r--drivers/crypto/ixp4xx_crypto.c21
-rw-r--r--drivers/crypto/n2_core.c26
-rw-r--r--drivers/crypto/talitos.c6
-rw-r--r--drivers/dma/Kconfig2
-rw-r--r--drivers/dma/fsldma.c4
-rw-r--r--drivers/dma/mpc512x_dma.c4
-rw-r--r--drivers/dma/ppc4xx/adma.c8
-rw-r--r--drivers/dma/shdma.c8
-rw-r--r--drivers/edac/i5000_edac.c2
-rw-r--r--drivers/edac/i5400_edac.c2
-rw-r--r--drivers/edac/mpc85xx_edac.c28
-rw-r--r--drivers/edac/ppc4xx_edac.c12
-rw-r--r--drivers/firmware/Kconfig10
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/iscsi_ibft.c6
-rw-r--r--drivers/gpio/Kconfig18
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpiolib.c96
-rw-r--r--drivers/gpio/max730x.c22
-rw-r--r--drivers/gpio/pcf857x.c9
-rw-r--r--drivers/gpio/stmpe-gpio.c399
-rw-r--r--drivers/gpio/sx150x.c645
-rw-r--r--drivers/gpio/wm831x-gpio.c32
-rw-r--r--drivers/gpu/drm/ati_pcigart.c2
-rw-r--r--drivers/gpu/drm/drm_bufs.c33
-rw-r--r--drivers/gpu/drm/drm_crtc.c6
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c10
-rw-r--r--drivers/gpu/drm/drm_edid.c840
-rw-r--r--drivers/gpu/drm/drm_edid_modes.h380
-rw-r--r--drivers/gpu/drm/drm_fops.c16
-rw-r--r--drivers/gpu/drm/drm_gem.c2
-rw-r--r--drivers/gpu/drm/drm_ioctl.c1
-rw-r--r--drivers/gpu/drm/i2c/ch7006_drv.c1
-rw-r--r--drivers/gpu/drm/i2c/ch7006_mode.c5
-rw-r--r--drivers/gpu/drm/i2c/ch7006_priv.h1
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c26
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c4
-rw-r--r--drivers/gpu/drm/i915/i915_opregion.c1
-rw-r--r--drivers/gpu/drm/i915/intel_display.c15
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c8
-rw-r--r--drivers/gpu/drm/nouveau/Makefile8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c194
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c19
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c87
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h45
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hw.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c41
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_irq.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_reg.h18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c91
-rw-r--r--drivers/gpu/drm/nouveau/nv04_crtc.c15
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dfp.c73
-rw-r--r--drivers/gpu/drm/nouveau/nv04_tv.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv10_graph.c175
-rw-r--r--drivers/gpu/drm/nouveau/nv30_fb.c24
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c34
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fb.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c96
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_instmem.c232
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c2
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c33
-rw-r--r--drivers/gpu/drm/radeon/radeon.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c180
-rw-r--r--drivers/gpu/drm/radeon/radeon_clocks.c81
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c388
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c45
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c82
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c28
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h24
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c24
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/rv5151
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c2
-rw-r--r--drivers/hid/hid-wacom.c49
-rw-r--r--drivers/hwmon/Kconfig14
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ams/ams.h2
-rw-r--r--drivers/hwmon/asc7621.c9
-rw-r--r--drivers/hwmon/coretemp.c3
-rw-r--r--drivers/hwmon/emc1403.c33
-rw-r--r--drivers/hwmon/emc2103.c740
-rw-r--r--drivers/hwmon/it87.c46
-rw-r--r--drivers/hwmon/k8temp.c3
-rw-r--r--drivers/hwmon/lm75.c39
-rw-r--r--drivers/hwmon/lm75.h1
-rw-r--r--drivers/hwmon/ltc4245.c177
-rw-r--r--drivers/hwmon/mc13783-adc.c17
-rw-r--r--drivers/hwmon/pc87360.c31
-rw-r--r--drivers/hwmon/pc87427.c862
-rw-r--r--drivers/hwmon/ultra45_env.c4
-rw-r--r--drivers/hwmon/via-cputemp.c2
-rw-r--r--drivers/hwmon/w83627ehf.c97
-rw-r--r--drivers/i2c/Kconfig13
-rw-r--r--drivers/i2c/Makefile3
-rw-r--r--drivers/i2c/busses/Kconfig24
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-cpm.c8
-rw-r--r--drivers/i2c/busses/i2c-davinci.c314
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c6
-rw-r--r--drivers/i2c/busses/i2c-mpc.c4
-rw-r--r--drivers/i2c/busses/i2c-nuc900.c709
-rw-r--r--drivers/i2c/busses/i2c-pxa.c2
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c121
-rw-r--r--drivers/i2c/i2c-core.c158
-rw-r--r--drivers/i2c/i2c-dev.c66
-rw-r--r--drivers/i2c/i2c-mux.c165
-rw-r--r--drivers/i2c/muxes/Kconfig18
-rw-r--r--drivers/i2c/muxes/Makefile8
-rw-r--r--drivers/i2c/muxes/pca954x.c301
-rw-r--r--drivers/ide/ide-atapi.c17
-rw-r--r--drivers/ide/ide-cd.c98
-rw-r--r--drivers/ide/ide-cd_ioctl.c2
-rw-r--r--drivers/ide/ide-disk.c18
-rw-r--r--drivers/ide/ide-disk_ioctl.c9
-rw-r--r--drivers/ide/ide-eh.c5
-rw-r--r--drivers/ide/ide-floppy.c27
-rw-r--r--drivers/ide/ide-floppy_ioctl.c12
-rw-r--r--drivers/ide/ide-gd.c19
-rw-r--r--drivers/ide/ide-io.c8
-rw-r--r--drivers/ide/ide-pm.c8
-rw-r--r--drivers/ide/ide-tape.c22
-rw-r--r--drivers/ide/ide.c20
-rw-r--r--drivers/idle/Kconfig3
-rwxr-xr-xdrivers/idle/intel_idle.c61
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c9
-rw-r--r--drivers/infiniband/hw/cxgb4/resource.c7
-rw-r--r--drivers/infiniband/hw/cxgb4/t4.h2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes.h2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c4
-rw-r--r--drivers/input/evdev.c152
-rw-r--r--drivers/input/input.c46
-rw-r--r--drivers/input/joydev.c31
-rw-r--r--drivers/input/joystick/a3d.c3
-rw-r--r--drivers/input/joystick/adi.c2
-rw-r--r--drivers/input/joystick/amijoy.c4
-rw-r--r--drivers/input/joystick/gf2k.c20
-rw-r--r--drivers/input/joystick/interact.c14
-rw-r--r--drivers/input/joystick/sidewinder.c18
-rw-r--r--drivers/input/joystick/xpad.c1
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/gpio_keys.c22
-rw-r--r--drivers/input/keyboard/hil_kbd.c21
-rw-r--r--drivers/input/keyboard/stmpe-keypad.c386
-rw-r--r--drivers/input/misc/adxl34x.c1
-rw-r--r--drivers/input/misc/ati_remote2.c26
-rw-r--r--drivers/input/misc/sparcspkr.c10
-rw-r--r--drivers/input/misc/uinput.c29
-rw-r--r--drivers/input/mouse/elantech.c31
-rw-r--r--drivers/input/mouse/elantech.h7
-rw-r--r--drivers/input/mouse/pc110pad.c4
-rw-r--r--drivers/input/mouse/psmouse-base.c14
-rw-r--r--drivers/input/mouse/synaptics.c4
-rw-r--r--drivers/input/mousedev.c44
-rw-r--r--drivers/input/serio/i8042-sparcio.h8
-rw-r--r--drivers/input/serio/i8042.c25
-rw-r--r--drivers/input/serio/xilinx_ps2.c4
-rw-r--r--drivers/input/tablet/aiptek.c15
-rw-r--r--drivers/input/tablet/wacom_wac.c4
-rw-r--r--drivers/input/touchscreen/Kconfig10
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c6
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c397
-rw-r--r--drivers/isdn/hardware/avm/c4.c1
-rw-r--r--drivers/isdn/hardware/avm/t1pci.c1
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNinfineon.c5
-rw-r--r--drivers/leds/leds-gpio.c4
-rw-r--r--drivers/macintosh/macio_sysfs.c6
-rw-r--r--drivers/macintosh/smu.c6
-rw-r--r--drivers/macintosh/therm_adt746x.c2
-rw-r--r--drivers/macintosh/therm_pm72.c6
-rw-r--r--drivers/macintosh/therm_windtunnel.c10
-rw-r--r--drivers/md/Kconfig18
-rw-r--r--drivers/md/Makefile77
-rw-r--r--drivers/md/bitmap.c508
-rw-r--r--drivers/md/bitmap.h6
-rw-r--r--drivers/md/dm-crypt.c342
-rw-r--r--drivers/md/dm-delay.c6
-rw-r--r--drivers/md/dm-exception-store.c4
-rw-r--r--drivers/md/dm-exception-store.h3
-rw-r--r--drivers/md/dm-io.c12
-rw-r--r--drivers/md/dm-ioctl.c207
-rw-r--r--drivers/md/dm-kcopyd.c2
-rw-r--r--drivers/md/dm-linear.c3
-rw-r--r--drivers/md/dm-mpath.c11
-rw-r--r--drivers/md/dm-raid1.c4
-rw-r--r--drivers/md/dm-snap-persistent.c6
-rw-r--r--drivers/md/dm-snap.c62
-rw-r--r--drivers/md/dm-stripe.c89
-rw-r--r--drivers/md/dm-table.c99
-rw-r--r--drivers/md/dm-target.c5
-rw-r--r--drivers/md/dm-zero.c5
-rw-r--r--drivers/md/dm.c374
-rw-r--r--drivers/md/dm.h14
-rw-r--r--drivers/md/linear.c2
-rw-r--r--drivers/md/md.c302
-rw-r--r--drivers/md/md.h59
-rw-r--r--drivers/md/mktables.c132
-rw-r--r--drivers/md/multipath.c8
-rw-r--r--drivers/md/raid0.c2
-rw-r--r--drivers/md/raid1.c22
-rw-r--r--drivers/md/raid10.c30
-rw-r--r--drivers/md/raid5.c170
-rw-r--r--drivers/md/raid5.h9
-rw-r--r--drivers/md/raid6algos.c154
-rw-r--r--drivers/md/raid6altivec.uc130
-rw-r--r--drivers/md/raid6int.uc117
-rw-r--r--drivers/md/raid6mmx.c142
-rw-r--r--drivers/md/raid6recov.c132
-rw-r--r--drivers/md/raid6sse1.c162
-rw-r--r--drivers/md/raid6sse2.c262
-rw-r--r--drivers/md/raid6test/Makefile75
-rw-r--r--drivers/md/raid6test/test.c124
-rw-r--r--drivers/md/raid6x86.h61
-rw-r--r--drivers/md/unroll.awk20
-rw-r--r--drivers/media/IR/Kconfig43
-rw-r--r--drivers/media/IR/Makefile2
-rw-r--r--drivers/media/IR/ene_ir.c1023
-rw-r--r--drivers/media/IR/ene_ir.h235
-rw-r--r--drivers/media/IR/imon.c20
-rw-r--r--drivers/media/IR/ir-core-priv.h13
-rw-r--r--drivers/media/IR/ir-jvc-decoder.c14
-rw-r--r--drivers/media/IR/ir-keytable.c13
-rw-r--r--drivers/media/IR/ir-lirc-codec.c124
-rw-r--r--drivers/media/IR/ir-nec-decoder.c25
-rw-r--r--drivers/media/IR/ir-raw-event.c159
-rw-r--r--drivers/media/IR/ir-sysfs.c2
-rw-r--r--drivers/media/IR/keymaps/Makefile2
-rw-r--r--drivers/media/IR/keymaps/rc-empty.c44
-rw-r--r--drivers/media/IR/keymaps/rc-rc5-streamzap.c81
-rw-r--r--drivers/media/IR/keymaps/rc-rc6-mce.c2
-rw-r--r--drivers/media/IR/mceusb.c21
-rw-r--r--drivers/media/IR/rc-map.c23
-rw-r--r--drivers/media/IR/streamzap.c741
-rw-r--r--drivers/media/common/tuners/Kconfig2
-rw-r--r--drivers/media/dvb/bt8xx/dst.c10
-rw-r--r--drivers/media/dvb/dm1105/Kconfig2
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig2
-rw-r--r--drivers/media/dvb/frontends/Kconfig2
-rw-r--r--drivers/media/dvb/siano/Kconfig2
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c6
-rw-r--r--drivers/media/dvb/ttpci/Kconfig2
-rw-r--r--drivers/media/video/Kconfig16
-rw-r--r--drivers/media/video/Makefile3
-rw-r--r--drivers/media/video/bt8xx/Kconfig2
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c2
-rw-r--r--drivers/media/video/cs53l32a.c107
-rw-r--r--drivers/media/video/cx18/Kconfig2
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c3
-rw-r--r--drivers/media/video/cx231xx/Kconfig2
-rw-r--r--drivers/media/video/cx2341x.c747
-rw-r--r--drivers/media/video/cx23885/Kconfig2
-rw-r--r--drivers/media/video/cx23885/Makefile5
-rw-r--r--drivers/media/video/cx23885/cx23885-av.c35
-rw-r--r--drivers/media/video/cx23885/cx23885-av.h27
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c114
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c124
-rw-r--r--drivers/media/video/cx23885/cx23885-i2c.c42
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c72
-rw-r--r--drivers/media/video/cx23885/cx23885-ir.c24
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h1
-rw-r--r--drivers/media/video/cx23885/cx23885-vbi.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c23
-rw-r--r--drivers/media/video/cx23885/cx23885.h9
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.c142
-rw-r--r--drivers/media/video/cx25840/Makefile2
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c144
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c540
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h52
-rw-r--r--drivers/media/video/cx25840/cx25840-ir.c1279
-rw-r--r--drivers/media/video/cx88/Kconfig2
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c19
-rw-r--r--drivers/media/video/em28xx/Kconfig2
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c2
-rw-r--r--drivers/media/video/fsl-viu.c8
-rw-r--r--drivers/media/video/gspca/gspca.c21
-rw-r--r--drivers/media/video/gspca/sonixj.c10
-rw-r--r--drivers/media/video/gspca/sq930x.c347
-rw-r--r--drivers/media/video/gspca/t613.c4
-rw-r--r--drivers/media/video/gspca/vc032x.c360
-rw-r--r--drivers/media/video/gspca/zc3xx.c1715
-rw-r--r--drivers/media/video/ivtv/Kconfig2
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c276
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.h6
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c26
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h4
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c23
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.c6
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c77
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c31
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c24
-rw-r--r--drivers/media/video/msp3400-driver.c248
-rw-r--r--drivers/media/video/msp3400-driver.h18
-rw-r--r--drivers/media/video/msp3400-kthreads.c16
-rw-r--r--drivers/media/video/mt9m111.c40
-rw-r--r--drivers/media/video/mx2_camera.c4
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c14
-rw-r--r--drivers/media/video/s5p-fimc/Makefile3
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c1586
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h471
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c527
-rw-r--r--drivers/media/video/s5p-fimc/regs-fimc.h293
-rw-r--r--drivers/media/video/saa7115.c183
-rw-r--r--drivers/media/video/saa7134/Kconfig2
-rw-r--r--drivers/media/video/saa717x.c323
-rw-r--r--drivers/media/video/soc_camera.c9
-rw-r--r--drivers/media/video/tlg2300/Kconfig2
-rw-r--r--drivers/media/video/tvp7002.c10
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c12
-rw-r--r--drivers/media/video/uvc/uvc_driver.c9
-rw-r--r--drivers/media/video/uvc/uvc_queue.c13
-rw-r--r--drivers/media/video/uvc/uvc_video.c19
-rw-r--r--drivers/media/video/uvc/uvcvideo.h5
-rw-r--r--drivers/media/video/v4l2-common.c482
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c7
-rw-r--r--drivers/media/video/v4l2-ctrls.c1852
-rw-r--r--drivers/media/video/v4l2-dev.c60
-rw-r--r--drivers/media/video/v4l2-device.c7
-rw-r--r--drivers/media/video/v4l2-ioctl.c46
-rw-r--r--drivers/media/video/wm8739.c179
-rw-r--r--drivers/media/video/wm8775.c79
-rw-r--r--drivers/memstick/core/mspro_block.c18
-rw-r--r--drivers/message/fusion/mptbase.c247
-rw-r--r--drivers/message/fusion/mptbase.h25
-rw-r--r--drivers/message/i2o/exec-osm.c8
-rw-r--r--drivers/message/i2o/i2o_block.c33
-rw-r--r--drivers/message/i2o/i2o_config.c18
-rw-r--r--drivers/message/i2o/i2o_scsi.c3
-rw-r--r--drivers/mfd/88pm860x-core.c84
-rw-r--r--drivers/mfd/Kconfig75
-rw-r--r--drivers/mfd/Makefile5
-rw-r--r--drivers/mfd/ab3100-otp.c16
-rw-r--r--drivers/mfd/ab3550-core.c23
-rw-r--r--drivers/mfd/ab8500-core.c4
-rw-r--r--drivers/mfd/ab8500-spi.c7
-rw-r--r--drivers/mfd/abx500-core.c2
-rw-r--r--drivers/mfd/davinci_voicecodec.c6
-rw-r--r--drivers/mfd/janz-cmodio.c1
-rw-r--r--drivers/mfd/jz4740-adc.c394
-rw-r--r--drivers/mfd/max8925-core.c27
-rw-r--r--drivers/mfd/max8998.c158
-rw-r--r--drivers/mfd/mc13783-core.c30
-rw-r--r--drivers/mfd/menelaus.c75
-rw-r--r--drivers/mfd/mfd-core.c4
-rw-r--r--drivers/mfd/stmpe.c985
-rw-r--r--drivers/mfd/stmpe.h183
-rw-r--r--drivers/mfd/t7l66xb.c3
-rw-r--r--drivers/mfd/tc6387xb.c16
-rw-r--r--drivers/mfd/tc6393xb.c4
-rw-r--r--drivers/mfd/tps6507x.c4
-rw-r--r--drivers/mfd/tps6586x.c375
-rw-r--r--drivers/mfd/twl6030-pwm.c163
-rw-r--r--drivers/mfd/ucb1400_core.c2
-rw-r--r--drivers/mfd/wm831x-core.c18
-rw-r--r--drivers/mfd/wm8350-core.c6
-rw-r--r--drivers/mfd/wm8994-core.c8
-rw-r--r--drivers/misc/cs5535-mfgpt.c11
-rw-r--r--drivers/misc/lkdtm.c4
-rw-r--r--drivers/mmc/card/block.c87
-rw-r--r--drivers/mmc/card/mmc_test.c811
-rw-r--r--drivers/mmc/card/queue.c21
-rw-r--r--drivers/mmc/core/bus.c9
-rw-r--r--drivers/mmc/core/core.c441
-rw-r--r--drivers/mmc/core/core.h2
-rw-r--r--drivers/mmc/core/host.c4
-rw-r--r--drivers/mmc/core/mmc.c75
-rw-r--r--drivers/mmc/core/sd.c331
-rw-r--r--drivers/mmc/core/sd.h17
-rw-r--r--drivers/mmc/core/sd_ops.c48
-rw-r--r--drivers/mmc/core/sd_ops.h1
-rw-r--r--drivers/mmc/core/sdio.c210
-rw-r--r--drivers/mmc/host/Kconfig18
-rw-r--r--drivers/mmc/host/Makefile7
-rw-r--r--drivers/mmc/host/mmc_spi.c60
-rw-r--r--drivers/mmc/host/msm_sdcc.c62
-rw-r--r--drivers/mmc/host/msm_sdcc.h6
-rw-r--r--drivers/mmc/host/omap_hsmmc.c47
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c97
-rw-r--r--drivers/mmc/host/sdhci-of-core.c12
-rw-r--r--drivers/mmc/host/sdhci-pci.c49
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c17
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h18
-rw-r--r--drivers/mmc/host/sdhci-s3c.c123
-rw-r--r--drivers/mmc/host/sdhci.c53
-rw-r--r--drivers/mmc/host/sdhci.h10
-rw-r--r--drivers/mtd/maps/physmap_of.c8
-rw-r--r--drivers/mtd/maps/sun_uflash.c6
-rw-r--r--drivers/mtd/mtd_blkdevs.c15
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c4
-rw-r--r--drivers/mtd/nand/fsl_upm.c4
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c4
-rw-r--r--drivers/mtd/nand/ndfc.c6
-rw-r--r--drivers/mtd/nand/pasemi_nand.c4
-rw-r--r--drivers/mtd/nand/socrates_nand.c4
-rw-r--r--drivers/net/arm/ixp4xx_eth.c2
-rw-r--r--drivers/net/caif/caif_spi_slave.c4
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c18
-rw-r--r--drivers/net/can/sja1000/sja1000_of_platform.c4
-rw-r--r--drivers/net/ehea/ehea.h4
-rw-r--r--drivers/net/ehea/ehea_main.c12
-rw-r--r--drivers/net/fec_mpc52xx.c8
-rw-r--r--drivers/net/fec_mpc52xx_phy.c4
-rw-r--r--drivers/net/fs_enet/fs_enet-main.c4
-rw-r--r--drivers/net/fs_enet/mac-fcc.c2
-rw-r--r--drivers/net/fs_enet/mac-fec.c2
-rw-r--r--drivers/net/fs_enet/mac-scc.c2
-rw-r--r--drivers/net/fs_enet/mii-bitbang.c4
-rw-r--r--drivers/net/fs_enet/mii-fec.c4
-rw-r--r--drivers/net/fsl_pq_mdio.c4
-rw-r--r--drivers/net/gianfar.c10
-rw-r--r--drivers/net/gianfar.h2
-rw-r--r--drivers/net/greth.c6
-rw-r--r--drivers/net/greth.h2
-rw-r--r--drivers/net/ibm_newemac/core.c6
-rw-r--r--drivers/net/ibm_newemac/core.h12
-rw-r--r--drivers/net/ibm_newemac/mal.c4
-rw-r--r--drivers/net/ibm_newemac/mal.h2
-rw-r--r--drivers/net/ibm_newemac/rgmii.c18
-rw-r--r--drivers/net/ibm_newemac/rgmii.h16
-rw-r--r--drivers/net/ibm_newemac/tah.c14
-rw-r--r--drivers/net/ibm_newemac/tah.h12
-rw-r--r--drivers/net/ibm_newemac/zmii.c18
-rw-r--r--drivers/net/ibm_newemac/zmii.h16
-rw-r--r--drivers/net/irda/sh_irda.c6
-rw-r--r--drivers/net/ll_temac_main.c8
-rw-r--r--drivers/net/myri10ge/myri10ge.c54
-rw-r--r--drivers/net/myri_sbus.c4
-rw-r--r--drivers/net/myri_sbus.h2
-rw-r--r--drivers/net/niu.c8
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c2
-rw-r--r--drivers/net/phy/Kconfig2
-rw-r--r--drivers/net/phy/mdio-gpio.c4
-rw-r--r--drivers/net/phy/phy.c2
-rw-r--r--drivers/net/qlcnic/qlcnic_main.c72
-rw-r--r--drivers/net/sunbmac.c18
-rw-r--r--drivers/net/sunbmac.h4
-rw-r--r--drivers/net/sunhme.c22
-rw-r--r--drivers/net/sunhme.h2
-rw-r--r--drivers/net/sunlance.c20
-rw-r--r--drivers/net/sunqe.c16
-rw-r--r--drivers/net/sunqe.h4
-rw-r--r--drivers/net/ucc_geth.c8
-rw-r--r--drivers/net/usb/usbnet.c22
-rw-r--r--drivers/net/wan/farsync.c15
-rw-r--r--drivers/net/wan/ixp4xx_hss.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c18
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c16
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c20
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c85
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c29
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c5
-rw-r--r--drivers/net/wireless/libertas/cfg.c3
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c32
-rw-r--r--drivers/net/wireless/libertas/if_usb.c3
-rw-r--r--drivers/net/wireless/libertas_tf/if_usb.c3
-rw-r--r--drivers/net/wireless/p54/p54pci.c2
-rw-r--r--drivers/net/xilinx_emaclite.c6
-rw-r--r--drivers/of/device.c2
-rw-r--r--drivers/parport/parport_serial.c1
-rw-r--r--drivers/parport/parport_sunbpp.c4
-rw-r--r--drivers/pcmcia/Kconfig2
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/electra_cf.c6
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c4
-rw-r--r--drivers/pcmcia/pxa2xx_balloon3.c158
-rw-r--r--drivers/platform/x86/Kconfig7
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/ideapad_acpi.c306
-rw-r--r--drivers/power/Kconfig13
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/intel_mid_battery.c799
-rw-r--r--drivers/power/olpc_battery.c3
-rw-r--r--drivers/power/s3c_adc_battery.c431
-rw-r--r--drivers/power/wm97xx_battery.c16
-rw-r--r--drivers/regulator/Kconfig34
-rw-r--r--drivers/regulator/Makefile5
-rw-r--r--drivers/regulator/ab8500.c427
-rw-r--r--drivers/regulator/ad5398.c288
-rw-r--r--drivers/regulator/isl6271a-regulator.c236
-rw-r--r--drivers/regulator/lp3971.c10
-rw-r--r--drivers/regulator/max1586.c10
-rw-r--r--drivers/regulator/max8660.c10
-rw-r--r--drivers/regulator/max8998.c635
-rw-r--r--drivers/regulator/tps65023-regulator.c2
-rw-r--r--drivers/regulator/tps6507x-regulator.c1
-rw-r--r--drivers/regulator/tps6586x-regulator.c396
-rw-r--r--drivers/regulator/wm8994-regulator.c5
-rw-r--r--drivers/rtc/Kconfig38
-rw-r--r--drivers/rtc/Makefile3
-rw-r--r--drivers/rtc/rtc-cmos.c6
-rw-r--r--drivers/rtc/rtc-ds3232.c326
-rw-r--r--drivers/rtc/rtc-fm3130.c181
-rw-r--r--drivers/rtc/rtc-imxdi.c519
-rw-r--r--drivers/rtc/rtc-isl12022.c327
-rw-r--r--drivers/rtc/rtc-m41t80.c4
-rw-r--r--drivers/rtc/rtc-m48t59.c5
-rw-r--r--drivers/rtc/rtc-m48t86.c2
-rw-r--r--drivers/rtc/rtc-max6900.c2
-rw-r--r--drivers/rtc/rtc-mpc5121.c4
-rw-r--r--drivers/rtc/rtc-mxc.c6
-rw-r--r--drivers/rtc/rtc-nuc900.c64
-rw-r--r--drivers/rtc/rtc-pcf8563.c8
-rw-r--r--drivers/rtc/rtc-pl031.c1
-rw-r--r--drivers/rtc/rtc-pxa.c42
-rw-r--r--drivers/rtc/rtc-rp5c01.c89
-rw-r--r--drivers/rtc/rtc-s3c.c44
-rw-r--r--drivers/s390/block/dasd.c12
-rw-r--r--drivers/s390/block/dasd_devmap.c44
-rw-r--r--drivers/s390/block/dasd_diag.c6
-rw-r--r--drivers/s390/block/dasd_eckd.c94
-rw-r--r--drivers/s390/block/dasd_eckd.h7
-rw-r--r--drivers/s390/block/dasd_fba.c4
-rw-r--r--drivers/s390/block/dasd_int.h8
-rw-r--r--drivers/s390/block/dcssblk.c5
-rw-r--r--drivers/s390/char/tape_block.c8
-rw-r--r--drivers/s390/cio/ccwreq.c16
-rw-r--r--drivers/s390/cio/chsc.c48
-rw-r--r--drivers/s390/cio/chsc.h2
-rw-r--r--drivers/s390/cio/device.c47
-rw-r--r--drivers/s390/cio/device_pgid.c3
-rw-r--r--drivers/s390/cio/io_sch.h10
-rw-r--r--drivers/s390/net/claw.c118
-rw-r--r--drivers/s390/net/claw.h4
-rw-r--r--drivers/s390/net/ctcm_fsms.c60
-rw-r--r--drivers/s390/net/ctcm_main.c80
-rw-r--r--drivers/s390/net/ctcm_main.h4
-rw-r--r--drivers/s390/net/ctcm_mpc.c64
-rw-r--r--drivers/s390/net/ctcm_sysfs.c20
-rw-r--r--drivers/s390/net/smsgiucv_app.c7
-rw-r--r--drivers/sbus/char/bbc_envctrl.c6
-rw-r--r--drivers/sbus/char/bbc_i2c.c18
-rw-r--r--drivers/sbus/char/bbc_i2c.h10
-rw-r--r--drivers/sbus/char/display7seg.c4
-rw-r--r--drivers/sbus/char/envctrl.c4
-rw-r--r--drivers/sbus/char/flash.c4
-rw-r--r--drivers/sbus/char/uctrl.c4
-rw-r--r--drivers/scsi/53c700.c3
-rw-r--r--drivers/scsi/Kconfig10
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/NCR5380.c4
-rw-r--r--drivers/scsi/aacraid/rx.c5
-rw-r--r--drivers/scsi/aha1542.c25
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c4
-rw-r--r--drivers/scsi/be2iscsi/Kconfig1
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h147
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c76
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h2
-rw-r--r--drivers/scsi/be2iscsi/be_main.c311
-rw-r--r--drivers/scsi/be2iscsi/be_main.h7
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c71
-rw-r--r--drivers/scsi/bfa/bfa_fcport.c2
-rw-r--r--drivers/scsi/bfa/bfad.c2
-rw-r--r--drivers/scsi/bfa/bfad_im.c4
-rw-r--r--drivers/scsi/bfa/include/protocol/fcp.h4
-rw-r--r--drivers/scsi/bnx2i/Kconfig3
-rw-r--r--drivers/scsi/ch.c89
-rw-r--r--drivers/scsi/dc395x.c2
-rw-r--r--drivers/scsi/fnic/fnic.h2
-rw-r--r--drivers/scsi/g_NCR5380.c47
-rw-r--r--drivers/scsi/g_NCR5380.h6
-rw-r--r--drivers/scsi/gdth.c2
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c380
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h6
-rw-r--r--drivers/scsi/initio.c1
-rw-r--r--drivers/scsi/iscsi_boot_sysfs.c (renamed from drivers/firmware/iscsi_boot_sysfs.c)0
-rw-r--r--drivers/scsi/libfc/fc_fcp.c4
-rw-r--r--drivers/scsi/lpfc/lpfc.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c69
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c9
-rw-r--r--drivers/scsi/lpfc/lpfc_compat.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c76
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c17
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h32
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c132
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c8
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c8
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c36
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/osd/osd_initiator.c8
-rw-r--r--drivers/scsi/osst.c3
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c13
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c4
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h36
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h20
-rw-r--r--drivers/scsi/qla4xxx/ql4_glbl.h2
-rw-r--r--drivers/scsi/qla4xxx/ql4_init.c14
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c3
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c48
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c17
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c315
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h2
-rw-r--r--drivers/scsi/qlogicpti.c14
-rw-r--r--drivers/scsi/qlogicpti.h2
-rw-r--r--drivers/scsi/scsi_error.c23
-rw-r--r--drivers/scsi/scsi_lib.c20
-rw-r--r--drivers/scsi/scsi_tgt_lib.c1
-rw-r--r--drivers/scsi/scsi_transport_fc.c12
-rw-r--r--drivers/scsi/sd.c138
-rw-r--r--drivers/scsi/sd.h2
-rw-r--r--drivers/scsi/sg.c11
-rw-r--r--drivers/scsi/sr.c25
-rw-r--r--drivers/scsi/sun3_NCR5380.c2
-rw-r--r--drivers/scsi/sun3_scsi.c2
-rw-r--r--drivers/scsi/sun3_scsi_vme.c2
-rw-r--r--drivers/scsi/sun_esp.c44
-rw-r--r--drivers/serial/21285.c10
-rw-r--r--drivers/serial/68328serial.c26
-rw-r--r--drivers/serial/68360serial.c4
-rw-r--r--drivers/serial/8250.c46
-rw-r--r--drivers/serial/8250_early.c57
-rw-r--r--drivers/serial/8250_pci.c13
-rw-r--r--drivers/serial/Kconfig48
-rw-r--r--drivers/serial/Makefile4
-rw-r--r--drivers/serial/altera_uart.c2
-rw-r--r--drivers/serial/apbuart.c2
-rw-r--r--drivers/serial/atmel_serial.c11
-rw-r--r--drivers/serial/bfin_5xx.c7
-rw-r--r--drivers/serial/cpm_uart/cpm_uart_core.c4
-rw-r--r--drivers/serial/crisv10.c12
-rw-r--r--drivers/serial/imx.c10
-rw-r--r--drivers/serial/ioc3_serial.c9
-rw-r--r--drivers/serial/ioc4_serial.c9
-rw-r--r--drivers/serial/max3100.c7
-rw-r--r--drivers/serial/max3107-aava.c344
-rw-r--r--drivers/serial/max3107.c1197
-rw-r--r--drivers/serial/max3107.h441
-rw-r--r--drivers/serial/mcf.c31
-rw-r--r--drivers/serial/mfd.c1498
-rw-r--r--drivers/serial/mpc52xx_uart.c8
-rw-r--r--drivers/serial/mrst_max3110.c844
-rw-r--r--drivers/serial/mrst_max3110.h59
-rw-r--r--drivers/serial/nwpserial.c2
-rw-r--r--drivers/serial/of_serial.c6
-rw-r--r--drivers/serial/s5pv210.c8
-rw-r--r--drivers/serial/samsung.c9
-rw-r--r--drivers/serial/serial_core.c288
-rw-r--r--drivers/serial/sh-sci.c42
-rw-r--r--drivers/serial/sh-sci.h29
-rw-r--r--drivers/serial/sunhv.c4
-rw-r--r--drivers/serial/sunsab.c8
-rw-r--r--drivers/serial/sunsu.c8
-rw-r--r--drivers/serial/sunzilog.c6
-rw-r--r--drivers/serial/timbuart.c6
-rw-r--r--drivers/serial/uartlite.c4
-rw-r--r--drivers/serial/ucc_uart.c4
-rw-r--r--drivers/sh/Makefile5
-rw-r--r--drivers/sh/clk-cpg.c58
-rw-r--r--drivers/spi/amba-pl022.c6
-rw-r--r--drivers/spi/mpc512x_psc_spi.c16
-rw-r--r--drivers/spi/mpc52xx_psc_spi.c4
-rw-r--r--drivers/spi/mpc52xx_spi.c4
-rw-r--r--drivers/spi/omap_spi_100k.c23
-rw-r--r--drivers/spi/spi.c225
-rw-r--r--drivers/spi/spi_bitbang.c9
-rw-r--r--drivers/spi/spi_bitbang_txrx.h16
-rw-r--r--drivers/spi/spi_butterfly.c2
-rw-r--r--drivers/spi/spi_gpio.c109
-rw-r--r--drivers/spi/spi_lm70llp.c2
-rw-r--r--drivers/spi/spi_mpc8xxx.c4
-rw-r--r--drivers/spi/spi_ppc4xx.c6
-rw-r--r--drivers/spi/spi_s3c24xx_gpio.c8
-rw-r--r--drivers/spi/spi_sh_sci.c8
-rw-r--r--drivers/spi/xilinx_spi_of.c6
-rw-r--r--drivers/staging/easycap/easycap.h8
-rw-r--r--drivers/staging/easycap/easycap_ioctl.c52
-rw-r--r--drivers/staging/easycap/easycap_main.c38
-rw-r--r--drivers/staging/hv/blkvsc_drv.c13
-rw-r--r--drivers/staging/lirc/Kconfig29
-rw-r--r--drivers/staging/lirc/Makefile2
-rw-r--r--drivers/staging/lirc/lirc_ene0100.c646
-rw-r--r--drivers/staging/lirc/lirc_it87.c9
-rw-r--r--drivers/staging/lirc/lirc_parallel.c4
-rw-r--r--drivers/staging/lirc/lirc_streamzap.c821
-rw-r--r--drivers/staging/rtl8187se/r8180_core.c6
-rw-r--r--drivers/staging/rtl8192e/r8192E_core.c6
-rw-r--r--drivers/staging/rtl8192su/r8192U_core.c6
-rw-r--r--drivers/staging/usbip/vhci_hcd.c6
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/atm/cxacru.c11
-rw-r--r--drivers/usb/atm/speedtch.c10
-rw-r--r--drivers/usb/atm/ueagle-atm.c7
-rw-r--r--drivers/usb/atm/usbatm.c23
-rw-r--r--drivers/usb/atm/usbatm.h22
-rw-r--r--drivers/usb/atm/xusbatm.c10
-rw-r--r--drivers/usb/c67x00/c67x00-hcd.c4
-rw-r--r--drivers/usb/class/cdc-acm.c12
-rw-r--r--drivers/usb/class/usblp.c371
-rw-r--r--drivers/usb/core/devio.c7
-rw-r--r--drivers/usb/core/driver.c11
-rw-r--r--drivers/usb/core/endpoint.c9
-rw-r--r--drivers/usb/core/generic.c4
-rw-r--r--drivers/usb/core/hcd-pci.c202
-rw-r--r--drivers/usb/core/hcd.c79
-rw-r--r--drivers/usb/core/hub.c13
-rw-r--r--drivers/usb/core/inode.c4
-rw-r--r--drivers/usb/core/quirks.c3
-rw-r--r--drivers/usb/core/urb.c50
-rw-r--r--drivers/usb/core/usb.c6
-rw-r--r--drivers/usb/gadget/Kconfig52
-rw-r--r--drivers/usb/gadget/Makefile3
-rw-r--r--drivers/usb/gadget/audio.c4
-rw-r--r--drivers/usb/gadget/cdc2.c4
-rw-r--r--drivers/usb/gadget/composite.c73
-rw-r--r--drivers/usb/gadget/dbgp.c434
-rw-r--r--drivers/usb/gadget/dummy_hcd.c6
-rw-r--r--drivers/usb/gadget/ether.c6
-rw-r--r--drivers/usb/gadget/f_fs.c38
-rw-r--r--drivers/usb/gadget/f_hid.c6
-rw-r--r--drivers/usb/gadget/f_loopback.c4
-rw-r--r--drivers/usb/gadget/f_mass_storage.c125
-rw-r--r--drivers/usb/gadget/f_sourcesink.c2
-rw-r--r--drivers/usb/gadget/file_storage.c104
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c10
-rw-r--r--drivers/usb/gadget/g_ffs.c176
-rw-r--r--drivers/usb/gadget/gmidi.c2
-rw-r--r--drivers/usb/gadget/hid.c4
-rw-r--r--drivers/usb/gadget/inode.c16
-rw-r--r--drivers/usb/gadget/langwell_udc.c6
-rw-r--r--drivers/usb/gadget/mass_storage.c24
-rw-r--r--drivers/usb/gadget/multi.c262
-rw-r--r--drivers/usb/gadget/printer.c9
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c153
-rw-r--r--drivers/usb/gadget/serial.c4
-rw-r--r--drivers/usb/gadget/storage_common.c105
-rw-r--r--drivers/usb/gadget/u_ether.c15
-rw-r--r--drivers/usb/gadget/u_serial.c1
-rw-r--r--drivers/usb/gadget/webcam.c4
-rw-r--r--drivers/usb/gadget/zero.c2
-rw-r--r--drivers/usb/host/Kconfig11
-rw-r--r--drivers/usb/host/ehci-au1xxx.c2
-rw-r--r--drivers/usb/host/ehci-dbg.c196
-rw-r--r--drivers/usb/host/ehci-fsl.c3
-rw-r--r--drivers/usb/host/ehci-hcd.c49
-rw-r--r--drivers/usb/host/ehci-hub.c25
-rw-r--r--drivers/usb/host/ehci-lpm.c83
-rw-r--r--drivers/usb/host/ehci-omap.c36
-rw-r--r--drivers/usb/host/ehci-pci.c26
-rw-r--r--drivers/usb/host/ehci-ppc-of.c6
-rw-r--r--drivers/usb/host/ehci-q.c3
-rw-r--r--drivers/usb/host/ehci-sched.c182
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c12
-rw-r--r--drivers/usb/host/ehci.h18
-rw-r--r--drivers/usb/host/fhci-hcd.c4
-rw-r--r--drivers/usb/host/hwa-hc.c4
-rw-r--r--drivers/usb/host/imx21-hcd.c2
-rw-r--r--drivers/usb/host/isp1362.h24
-rw-r--r--drivers/usb/host/isp1760-hcd.c3
-rw-r--r--drivers/usb/host/isp1760-if.c4
-rw-r--r--drivers/usb/host/ohci-dbg.c4
-rw-r--r--drivers/usb/host/ohci-hcd.c6
-rw-r--r--drivers/usb/host/ohci-hub.c23
-rw-r--r--drivers/usb/host/ohci-pci.c2
-rw-r--r--drivers/usb/host/ohci-pnx4008.c2
-rw-r--r--drivers/usb/host/ohci-ppc-of.c6
-rw-r--r--drivers/usb/host/ohci-ssb.c52
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c7
-rw-r--r--drivers/usb/host/sl811-hcd.c5
-rw-r--r--drivers/usb/host/uhci-debug.c23
-rw-r--r--drivers/usb/host/uhci-hcd.c87
-rw-r--r--drivers/usb/host/uhci-hcd.h7
-rw-r--r--drivers/usb/host/uhci-hub.c6
-rw-r--r--drivers/usb/host/uhci-q.c4
-rw-r--r--drivers/usb/host/whci/hcd.c2
-rw-r--r--drivers/usb/host/whci/qset.c2
-rw-r--r--drivers/usb/host/xhci-mem.c101
-rw-r--r--drivers/usb/host/xhci-pci.c9
-rw-r--r--drivers/usb/host/xhci-ring.c1332
-rw-r--r--drivers/usb/host/xhci.c344
-rw-r--r--drivers/usb/host/xhci.h30
-rw-r--r--drivers/usb/misc/ftdi-elan.c4
-rw-r--r--drivers/usb/misc/iowarrior.c23
-rw-r--r--drivers/usb/misc/legousbtower.c6
-rw-r--r--drivers/usb/misc/rio500.c15
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c10
-rw-r--r--drivers/usb/misc/usblcd.c24
-rw-r--r--drivers/usb/misc/usbtest.c14
-rw-r--r--drivers/usb/mon/mon_bin.c24
-rw-r--r--drivers/usb/musb/musb_core.c7
-rw-r--r--drivers/usb/musb/musb_debugfs.c32
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c3
-rw-r--r--drivers/usb/musb/musb_virthub.c2
-rw-r--r--drivers/usb/musb/musbhsdma.c5
-rw-r--r--drivers/usb/musb/omap2430.c6
-rw-r--r--drivers/usb/otg/Kconfig2
-rw-r--r--drivers/usb/otg/ulpi.c134
-rw-r--r--drivers/usb/serial/Kconfig9
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/cp210x.c4
-rw-r--r--drivers/usb/serial/digi_acceleport.c14
-rw-r--r--drivers/usb/serial/ftdi_sio.c4
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h10
-rw-r--r--drivers/usb/serial/generic.c15
-rw-r--r--drivers/usb/serial/io_ti.c4
-rw-r--r--drivers/usb/serial/ipaq.c1
-rw-r--r--drivers/usb/serial/iuu_phoenix.c54
-rw-r--r--drivers/usb/serial/option.c17
-rw-r--r--drivers/usb/serial/ssu100.c698
-rw-r--r--drivers/usb/serial/usb-serial.c32
-rw-r--r--drivers/usb/storage/freecom.c23
-rw-r--r--drivers/usb/storage/isd200.c3
-rw-r--r--drivers/usb/storage/usb.c4
-rw-r--r--drivers/usb/usb-skeleton.c8
-rw-r--r--drivers/video/Kconfig17
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/bw2.c4
-rw-r--r--drivers/video/cg14.c6
-rw-r--r--drivers/video/cg3.c4
-rw-r--r--drivers/video/cg6.c6
-rw-r--r--drivers/video/console/bitblit.c2
-rw-r--r--drivers/video/console/fbcon.c6
-rw-r--r--drivers/video/console/fbcon_ccw.c2
-rw-r--r--drivers/video/console/fbcon_cw.c2
-rw-r--r--drivers/video/console/fbcon_ud.c2
-rw-r--r--drivers/video/console/vgacon.c2
-rw-r--r--drivers/video/efifb.c8
-rw-r--r--drivers/video/fbmem.c6
-rw-r--r--drivers/video/ffb.c4
-rw-r--r--drivers/video/fsl-diu-fb.c8
-rw-r--r--drivers/video/igafb.c5
-rw-r--r--drivers/video/imxfb.c19
-rw-r--r--drivers/video/leo.c6
-rw-r--r--drivers/video/matrox/i2c-matroxfb.c2
-rw-r--r--drivers/video/mb862xx/mb862xxfb.c4
-rw-r--r--drivers/video/msm/mddi.c4
-rw-r--r--drivers/video/msm/mdp.c1
-rw-r--r--drivers/video/p9100.c4
-rw-r--r--drivers/video/platinumfb.c4
-rw-r--r--drivers/video/s3c-fb.c811
-rw-r--r--drivers/video/sh_mipi_dsi.c505
-rw-r--r--drivers/video/sh_mobile_hdmi.c1028
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c196
-rw-r--r--drivers/video/sunxvr1000.c4
-rw-r--r--drivers/video/tcx.c6
-rw-r--r--drivers/video/uvesafb.c7
-rw-r--r--drivers/video/vt8623fb.c2
-rw-r--r--drivers/video/xilinxfb.c4
-rw-r--r--drivers/watchdog/Kconfig37
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/cpwd.c4
-rw-r--r--drivers/watchdog/f71808e_wdt.c768
-rw-r--r--drivers/watchdog/gef_wdt.c2
-rw-r--r--drivers/watchdog/hpwdt.c310
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c4
-rw-r--r--drivers/watchdog/riowd.c4
-rw-r--r--drivers/watchdog/s3c2410_wdt.c17
-rw-r--r--drivers/watchdog/sch311x_wdt.c4
-rw-r--r--drivers/watchdog/sp805_wdt.c387
-rw-r--r--drivers/watchdog/wdt_pci.c15
-rw-r--r--drivers/xen/Kconfig5
-rw-r--r--drivers/xen/Makefile1
-rw-r--r--drivers/xen/balloon.c15
-rw-r--r--drivers/xen/swiotlb-xen.c515
-rw-r--r--drivers/xen/xenbus/xenbus_client.c90
-rw-r--r--drivers/zorro/proc.c17
976 files changed, 53578 insertions, 16767 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 864dd46..18645f4 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -33,6 +33,7 @@
#include <linux/uaccess.h>
#include <linux/cper.h>
#include <linux/nmi.h>
+#include <linux/hardirq.h>
#include <acpi/apei.h>
#include "apei-internal.h"
diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c
index 146135e..295dbfa 100644
--- a/drivers/acpi/debug.c
+++ b/drivers/acpi/debug.c
@@ -96,7 +96,8 @@ static const struct acpi_dlevel acpi_debug_levels[] = {
/* --------------------------------------------------------------------------
FS Interface (/sys)
-------------------------------------------------------------------------- */
-static int param_get_debug_layer(char *buffer, struct kernel_param *kp) {
+static int param_get_debug_layer(char *buffer, const struct kernel_param *kp)
+{
int result = 0;
int i;
@@ -118,7 +119,8 @@ static int param_get_debug_layer(char *buffer, struct kernel_param *kp) {
return result;
}
-static int param_get_debug_level(char *buffer, struct kernel_param *kp) {
+static int param_get_debug_level(char *buffer, const struct kernel_param *kp)
+{
int result = 0;
int i;
@@ -137,8 +139,18 @@ static int param_get_debug_level(char *buffer, struct kernel_param *kp) {
return result;
}
-module_param_call(debug_layer, param_set_uint, param_get_debug_layer, &acpi_dbg_layer, 0644);
-module_param_call(debug_level, param_set_uint, param_get_debug_level, &acpi_dbg_level, 0644);
+static struct kernel_param_ops acpi_debug_layer_ops = {
+ .set = param_set_uint,
+ .get = param_get_debug_layer,
+};
+
+static struct kernel_param_ops acpi_debug_level_ops = {
+ .set = param_set_uint,
+ .get = param_get_debug_level,
+};
+
+module_param_cb(debug_layer, &acpi_debug_layer_ops, &acpi_dbg_layer, 0644);
+module_param_cb(debug_level, &acpi_debug_level_ops, &acpi_dbg_level, 0644);
static char trace_method_name[6];
module_param_string(trace_method_name, trace_method_name, 6, 0644);
@@ -147,7 +159,7 @@ module_param(trace_debug_layer, uint, 0644);
static unsigned int trace_debug_level;
module_param(trace_debug_level, uint, 0644);
-static int param_set_trace_state(const char *val, struct kernel_param *kp)
+static int param_set_trace_state(const char *val, const struct kernel_param *kp)
{
int result = 0;
@@ -181,7 +193,7 @@ exit:
return result;
}
-static int param_get_trace_state(char *buffer, struct kernel_param *kp)
+static int param_get_trace_state(char *buffer, const struct kernel_param *kp)
{
if (!acpi_gbl_trace_method_name)
return sprintf(buffer, "disable");
@@ -194,8 +206,12 @@ static int param_get_trace_state(char *buffer, struct kernel_param *kp)
return 0;
}
-module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
- NULL, 0644);
+static struct kernel_param_ops param_ops_trace_state = {
+ .set = param_set_trace_state,
+ .get = param_get_trace_state,
+};
+
+module_param_cb(trace_state, &param_ops_trace_state, NULL, 0644);
/* --------------------------------------------------------------------------
DebugFS Interface
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 8fae6af..65e3e27 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -651,6 +651,17 @@ config PATA_VIA
If unsure, say N.
+config PATA_PXA
+ tristate "PXA DMA-capable PATA support"
+ depends on ARCH_PXA
+ help
+ This option enables support for harddrive attached to PXA CPU's bus.
+
+ NOTE: This driver utilizes PXA DMA controller, in case your hardware
+ is not capable of doing MWDMA, use pata_platform instead.
+
+ If unsure, say N.
+
config PATA_WINBOND
tristate "Winbond SL82C105 PATA support"
depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6540632..158eaa9 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -91,6 +91,8 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
+obj-$(CONFIG_PATA_PXA) += pata_pxa.o
+
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
# Should be last but one libata driver
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 4972fdf..7ef7c4f 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4281,7 +4281,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
* The special characters ?, [, -, or *, can be matched using a set, eg. [*]
* Behaviour with malformed patterns is undefined, though generally reasonable.
*
- * Example patterns: "SD1?", "SD1[0-5]", "*R0", SD*1?[012]*xx"
+ * Sample patterns: "SD1?", "SD1[0-5]", "*R0", "SD*1?[012]*xx"
*
* This function uses one level of recursion per '*' in pattern.
* Since it calls _nothing_ else, and has _no_ explicit local variables,
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index d75c9c4..a89172c 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1111,10 +1111,10 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
*/
static int atapi_drain_needed(struct request *rq)
{
- if (likely(!blk_pc_request(rq)))
+ if (likely(rq->cmd_type != REQ_TYPE_BLOCK_PC))
return 0;
- if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_RW))
+ if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_WRITE))
return 0;
return atapi_cmd_type(rq->cmd[0]) == ATAPI_MISC;
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index f087ab5..8cc536e 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -680,7 +680,7 @@ mpc52xx_ata_remove_one(struct device *dev)
/* ======================================================================== */
static int __devinit
-mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match)
+mpc52xx_ata_probe(struct platform_device *op, const struct of_device_id *match)
{
unsigned int ipb_freq;
struct resource res_mem;
@@ -821,7 +821,7 @@ mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match)
}
static int
-mpc52xx_ata_remove(struct of_device *op)
+mpc52xx_ata_remove(struct platform_device *op)
{
struct mpc52xx_ata_priv *priv;
int task_irq;
@@ -848,7 +848,7 @@ mpc52xx_ata_remove(struct of_device *op)
#ifdef CONFIG_PM
static int
-mpc52xx_ata_suspend(struct of_device *op, pm_message_t state)
+mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
@@ -856,7 +856,7 @@ mpc52xx_ata_suspend(struct of_device *op, pm_message_t state)
}
static int
-mpc52xx_ata_resume(struct of_device *op)
+mpc52xx_ata_resume(struct platform_device *op)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
struct mpc52xx_ata_priv *priv = host->private_data;
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index 5a1b82c..480e043 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -14,7 +14,7 @@
#include <linux/of_platform.h>
#include <linux/ata_platform.h>
-static int __devinit pata_of_platform_probe(struct of_device *ofdev,
+static int __devinit pata_of_platform_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int ret;
@@ -78,7 +78,7 @@ static int __devinit pata_of_platform_probe(struct of_device *ofdev,
reg_shift, pio_mask);
}
-static int __devexit pata_of_platform_remove(struct of_device *ofdev)
+static int __devexit pata_of_platform_remove(struct platform_device *ofdev)
{
return __pata_platform_remove(&ofdev->dev);
}
diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c
new file mode 100644
index 0000000..1898c6e
--- /dev/null
+++ b/drivers/ata/pata_pxa.c
@@ -0,0 +1,411 @@
+/*
+ * Generic PXA PATA driver
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi_host.h>
+
+#include <mach/pxa2xx-regs.h>
+#include <mach/pata_pxa.h>
+#include <mach/dma.h>
+
+#define DRV_NAME "pata_pxa"
+#define DRV_VERSION "0.1"
+
+struct pata_pxa_data {
+ uint32_t dma_channel;
+ struct pxa_dma_desc *dma_desc;
+ dma_addr_t dma_desc_addr;
+ uint32_t dma_desc_id;
+
+ /* DMA IO physical address */
+ uint32_t dma_io_addr;
+ /* PXA DREQ<0:2> pin selector */
+ uint32_t dma_dreq;
+ /* DMA DCSR register value */
+ uint32_t dma_dcsr;
+
+ struct completion dma_done;
+};
+
+/*
+ * Setup the DMA descriptors. The size is transfer capped at 4k per descriptor,
+ * if the transfer is longer, it is split into multiple chained descriptors.
+ */
+static void pxa_load_dmac(struct scatterlist *sg, struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+
+ uint32_t cpu_len, seg_len;
+ dma_addr_t cpu_addr;
+
+ cpu_addr = sg_dma_address(sg);
+ cpu_len = sg_dma_len(sg);
+
+ do {
+ seg_len = (cpu_len > 0x1000) ? 0x1000 : cpu_len;
+
+ pd->dma_desc[pd->dma_desc_id].ddadr = pd->dma_desc_addr +
+ ((pd->dma_desc_id + 1) * sizeof(struct pxa_dma_desc));
+
+ pd->dma_desc[pd->dma_desc_id].dcmd = DCMD_BURST32 |
+ DCMD_WIDTH2 | (DCMD_LENGTH & seg_len);
+
+ if (qc->tf.flags & ATA_TFLAG_WRITE) {
+ pd->dma_desc[pd->dma_desc_id].dsadr = cpu_addr;
+ pd->dma_desc[pd->dma_desc_id].dtadr = pd->dma_io_addr;
+ pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCSRCADDR |
+ DCMD_FLOWTRG;
+ } else {
+ pd->dma_desc[pd->dma_desc_id].dsadr = pd->dma_io_addr;
+ pd->dma_desc[pd->dma_desc_id].dtadr = cpu_addr;
+ pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCTRGADDR |
+ DCMD_FLOWSRC;
+ }
+
+ cpu_len -= seg_len;
+ cpu_addr += seg_len;
+ pd->dma_desc_id++;
+
+ } while (cpu_len);
+
+ /* Should not happen */
+ if (seg_len & 0x1f)
+ DALGN |= (1 << pd->dma_dreq);
+}
+
+/*
+ * Prepare taskfile for submission.
+ */
+static void pxa_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+ int si = 0;
+ struct scatterlist *sg;
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ pd->dma_desc_id = 0;
+
+ DCSR(pd->dma_channel) = 0;
+ DALGN &= ~(1 << pd->dma_dreq);
+
+ for_each_sg(qc->sg, sg, qc->n_elem, si)
+ pxa_load_dmac(sg, qc);
+
+ pd->dma_desc[pd->dma_desc_id - 1].ddadr = DDADR_STOP;
+
+ /* Fire IRQ only at the end of last block */
+ pd->dma_desc[pd->dma_desc_id - 1].dcmd |= DCMD_ENDIRQEN;
+
+ DDADR(pd->dma_channel) = pd->dma_desc_addr;
+ DRCMR(pd->dma_dreq) = DRCMR_MAPVLD | pd->dma_channel;
+
+}
+
+/*
+ * Configure the DMA controller, load the DMA descriptors, but don't start the
+ * DMA controller yet. Only issue the ATA command.
+ */
+static void pxa_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ qc->ap->ops->sff_exec_command(qc->ap, &qc->tf);
+}
+
+/*
+ * Execute the DMA transfer.
+ */
+static void pxa_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+ init_completion(&pd->dma_done);
+ DCSR(pd->dma_channel) = DCSR_RUN;
+}
+
+/*
+ * Wait until the DMA transfer completes, then stop the DMA controller.
+ */
+static void pxa_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+
+ if ((DCSR(pd->dma_channel) & DCSR_RUN) &&
+ wait_for_completion_timeout(&pd->dma_done, HZ))
+ dev_err(qc->ap->dev, "Timeout waiting for DMA completion!");
+
+ DCSR(pd->dma_channel) = 0;
+}
+
+/*
+ * Read DMA status. The bmdma_stop() will take care of properly finishing the
+ * DMA transfer so we always have DMA-complete interrupt here.
+ */
+static unsigned char pxa_bmdma_status(struct ata_port *ap)
+{
+ struct pata_pxa_data *pd = ap->private_data;
+ unsigned char ret = ATA_DMA_INTR;
+
+ if (pd->dma_dcsr & DCSR_BUSERR)
+ ret |= ATA_DMA_ERR;
+
+ return ret;
+}
+
+/*
+ * No IRQ register present so we do nothing.
+ */
+static void pxa_irq_clear(struct ata_port *ap)
+{
+}
+
+/*
+ * Check for ATAPI DMA. ATAPI DMA is unsupported by this driver. It's still
+ * unclear why ATAPI has DMA issues.
+ */
+static int pxa_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct scsi_host_template pxa_ata_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations pxa_ata_port_ops = {
+ .inherits = &ata_bmdma_port_ops,
+ .cable_detect = ata_cable_40wire,
+
+ .bmdma_setup = pxa_bmdma_setup,
+ .bmdma_start = pxa_bmdma_start,
+ .bmdma_stop = pxa_bmdma_stop,
+ .bmdma_status = pxa_bmdma_status,
+
+ .check_atapi_dma = pxa_check_atapi_dma,
+
+ .sff_irq_clear = pxa_irq_clear,
+
+ .qc_prep = pxa_qc_prep,
+};
+
+/*
+ * DMA interrupt handler.
+ */
+static void pxa_ata_dma_irq(int dma, void *port)
+{
+ struct ata_port *ap = port;
+ struct pata_pxa_data *pd = ap->private_data;
+
+ pd->dma_dcsr = DCSR(dma);
+ DCSR(dma) = pd->dma_dcsr;
+
+ if (pd->dma_dcsr & DCSR_STOPSTATE)
+ complete(&pd->dma_done);
+}
+
+static int __devinit pxa_ata_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct pata_pxa_data *data;
+ struct resource *cmd_res;
+ struct resource *ctl_res;
+ struct resource *dma_res;
+ struct resource *irq_res;
+ struct pata_pxa_pdata *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ /*
+ * Resource validation, three resources are needed:
+ * - CMD port base address
+ * - CTL port base address
+ * - DMA port base address
+ * - IRQ pin
+ */
+ if (pdev->num_resources != 4) {
+ dev_err(&pdev->dev, "invalid number of resources\n");
+ return -EINVAL;
+ }
+
+ /*
+ * CMD port base address
+ */
+ cmd_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(cmd_res == NULL))
+ return -EINVAL;
+
+ /*
+ * CTL port base address
+ */
+ ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (unlikely(ctl_res == NULL))
+ return -EINVAL;
+
+ /*
+ * DMA port base address
+ */
+ dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (unlikely(dma_res == NULL))
+ return -EINVAL;
+
+ /*
+ * IRQ pin
+ */
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (unlikely(irq_res == NULL))
+ return -EINVAL;
+
+ /*
+ * Allocate the host
+ */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ ap = host->ports[0];
+ ap->ops = &pxa_ata_port_ops;
+ ap->pio_mask = ATA_PIO4;
+ ap->mwdma_mask = ATA_MWDMA2;
+ ap->flags = ATA_FLAG_MMIO;
+
+ ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start,
+ resource_size(cmd_res));
+ ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
+ resource_size(ctl_res));
+ ap->ioaddr.bmdma_addr = devm_ioremap(&pdev->dev, dma_res->start,
+ resource_size(dma_res));
+
+ /*
+ * Adjust register offsets
+ */
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+ ap->ioaddr.data_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_DATA << pdata->reg_shift);
+ ap->ioaddr.error_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_ERR << pdata->reg_shift);
+ ap->ioaddr.feature_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_FEATURE << pdata->reg_shift);
+ ap->ioaddr.nsect_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_NSECT << pdata->reg_shift);
+ ap->ioaddr.lbal_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAL << pdata->reg_shift);
+ ap->ioaddr.lbam_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAM << pdata->reg_shift);
+ ap->ioaddr.lbah_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAH << pdata->reg_shift);
+ ap->ioaddr.device_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_DEVICE << pdata->reg_shift);
+ ap->ioaddr.status_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_STATUS << pdata->reg_shift);
+ ap->ioaddr.command_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_CMD << pdata->reg_shift);
+
+ /*
+ * Allocate and load driver's internal data structure
+ */
+ data = devm_kzalloc(&pdev->dev, sizeof(struct pata_pxa_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ap->private_data = data;
+ data->dma_dreq = pdata->dma_dreq;
+ data->dma_io_addr = dma_res->start;
+
+ /*
+ * Allocate space for the DMA descriptors
+ */
+ data->dma_desc = dmam_alloc_coherent(&pdev->dev, PAGE_SIZE,
+ &data->dma_desc_addr, GFP_KERNEL);
+ if (!data->dma_desc)
+ return -EINVAL;
+
+ /*
+ * Request the DMA channel
+ */
+ data->dma_channel = pxa_request_dma(DRV_NAME, DMA_PRIO_LOW,
+ pxa_ata_dma_irq, ap);
+ if (data->dma_channel < 0)
+ return -EBUSY;
+
+ /*
+ * Stop and clear the DMA channel
+ */
+ DCSR(data->dma_channel) = 0;
+
+ /*
+ * Activate the ATA host
+ */
+ ret = ata_host_activate(host, irq_res->start, ata_sff_interrupt,
+ pdata->irq_flags, &pxa_ata_sht);
+ if (ret)
+ pxa_free_dma(data->dma_channel);
+
+ return ret;
+}
+
+static int __devexit pxa_ata_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_pxa_data *data = host->ports[0]->private_data;
+
+ pxa_free_dma(data->dma_channel);
+
+ ata_host_detach(host);
+
+ return 0;
+}
+
+static struct platform_driver pxa_ata_driver = {
+ .probe = pxa_ata_probe,
+ .remove = __devexit_p(pxa_ata_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pxa_ata_init(void)
+{
+ return platform_driver_register(&pxa_ata_driver);
+}
+
+static void __exit pxa_ata_exit(void)
+{
+ platform_driver_unregister(&pxa_ata_driver);
+}
+
+module_init(pxa_ata_init);
+module_exit(pxa_ata_exit);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("DMA-capable driver for PATA on PXA CPU");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 18c986d..7325f77 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -1296,7 +1296,7 @@ static const struct ata_port_info sata_fsl_port_info[] = {
},
};
-static int sata_fsl_probe(struct of_device *ofdev,
+static int sata_fsl_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int retval = -ENXIO;
@@ -1370,7 +1370,7 @@ error_exit_with_cleanup:
return retval;
}
-static int sata_fsl_remove(struct of_device *ofdev)
+static int sata_fsl_remove(struct platform_device *ofdev)
{
struct ata_host *host = dev_get_drvdata(&ofdev->dev);
struct sata_fsl_host_priv *host_priv = host->private_data;
@@ -1387,13 +1387,13 @@ static int sata_fsl_remove(struct of_device *ofdev)
}
#ifdef CONFIG_PM
-static int sata_fsl_suspend(struct of_device *op, pm_message_t state)
+static int sata_fsl_suspend(struct platform_device *op, pm_message_t state)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
return ata_host_suspend(host, state);
}
-static int sata_fsl_resume(struct of_device *op)
+static int sata_fsl_resume(struct platform_device *op)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
struct sata_fsl_host_priv *host_priv = host->private_data;
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index b7385e0..c8fc69c 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -674,7 +674,7 @@ static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr)
static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
u32 dma_addr;
dma_addr = dma_map_single(&op->dev, virt_addr, size, direction);
@@ -687,7 +687,7 @@ static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int
static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n",
dma_addr, size, direction);
@@ -697,7 +697,7 @@ static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int
static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
@@ -706,7 +706,7 @@ static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_add
static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
@@ -719,7 +719,7 @@ static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_
static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk,
int size, int nbr, int alignment)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
chunk->alloc_size = chunk->align_size = size * nbr;
@@ -738,7 +738,7 @@ static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk
/* free a DVMA consistent chunk of memory */
static void fore200e_sba_dma_chunk_free(struct fore200e *fore200e, struct chunk *chunk)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
dma_free_coherent(&op->dev, chunk->alloc_size,
chunk->alloc_addr, chunk->dma_addr);
@@ -770,7 +770,7 @@ static void fore200e_sba_reset(struct fore200e *fore200e)
static int __init fore200e_sba_map(struct fore200e *fore200e)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
unsigned int bursts;
/* gain access to the SBA specific registers */
@@ -800,7 +800,7 @@ static int __init fore200e_sba_map(struct fore200e *fore200e)
static void fore200e_sba_unmap(struct fore200e *fore200e)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH);
of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH);
@@ -816,7 +816,7 @@ static int __init fore200e_sba_configure(struct fore200e *fore200e)
static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
const u8 *prop;
int len;
@@ -840,7 +840,7 @@ static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_
static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
const struct linux_prom_registers *regs;
regs = of_get_property(op->dev.of_node, "reg", NULL);
@@ -2513,7 +2513,7 @@ fore200e_load_and_start_fw(struct fore200e* fore200e)
device = &((struct pci_dev *) fore200e->bus_dev)->dev;
#ifdef CONFIG_SBUS
else if (strcmp(fore200e->bus->model_name, "SBA-200E") == 0)
- device = &((struct of_device *) fore200e->bus_dev)->dev;
+ device = &((struct platform_device *) fore200e->bus_dev)->dev;
#endif
else
return err;
@@ -2643,7 +2643,7 @@ fore200e_init(struct fore200e* fore200e)
}
#ifdef CONFIG_SBUS
-static int __devinit fore200e_sba_probe(struct of_device *op,
+static int __devinit fore200e_sba_probe(struct platform_device *op,
const struct of_device_id *match)
{
const struct fore200e_bus *bus = match->data;
@@ -2675,7 +2675,7 @@ static int __devinit fore200e_sba_probe(struct of_device *op,
return 0;
}
-static int __devexit fore200e_sba_remove(struct of_device *op)
+static int __devexit fore200e_sba_remove(struct platform_device *op)
{
struct fore200e *fore200e = dev_get_drvdata(&op->dev);
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index c5f22bb..4e2c367 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -79,23 +79,28 @@ static int DAC960_open(struct block_device *bdev, fmode_t mode)
struct gendisk *disk = bdev->bd_disk;
DAC960_Controller_T *p = disk->queue->queuedata;
int drive_nr = (long)disk->private_data;
+ int ret = -ENXIO;
+ lock_kernel();
if (p->FirmwareType == DAC960_V1_Controller) {
if (p->V1.LogicalDriveInformation[drive_nr].
LogicalDriveState == DAC960_V1_LogicalDrive_Offline)
- return -ENXIO;
+ goto out;
} else {
DAC960_V2_LogicalDeviceInfo_T *i =
p->V2.LogicalDeviceInformation[drive_nr];
if (!i || i->LogicalDeviceState == DAC960_V2_LogicalDevice_Offline)
- return -ENXIO;
+ goto out;
}
check_disk_change(bdev);
if (!get_capacity(p->disks[drive_nr]))
- return -ENXIO;
- return 0;
+ goto out;
+ ret = 0;
+out:
+ unlock_kernel();
+ return ret;
}
static int DAC960_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 832798a..76f114f 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -60,6 +60,7 @@
#include <linux/hdreg.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/smp_lock.h>
#include <linux/amifdreg.h>
#include <linux/amifd.h>
#include <linux/buffer_head.h>
@@ -1423,7 +1424,7 @@ static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return 0;
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
struct amiga_floppy_struct *p = bdev->bd_disk->private_data;
@@ -1500,6 +1501,18 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode,
return 0;
}
+static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = fd_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
static void fd_probe(int dev)
{
unsigned long code;
@@ -1542,10 +1555,13 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
int old_dev;
unsigned long flags;
+ lock_kernel();
old_dev = fd_device[drive];
- if (fd_ref[drive] && old_dev != system)
+ if (fd_ref[drive] && old_dev != system) {
+ unlock_kernel();
return -EBUSY;
+ }
if (mode & (FMODE_READ|FMODE_WRITE)) {
check_disk_change(bdev);
@@ -1558,8 +1574,10 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
fd_deselect (drive);
rel_fdc();
- if (wrprot)
+ if (wrprot) {
+ unlock_kernel();
return -EROFS;
+ }
}
}
@@ -1576,6 +1594,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
unit[drive].type->name, data_types[system].name);
+ unlock_kernel();
return 0;
}
@@ -1584,6 +1603,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
struct amiga_floppy_struct *p = disk->private_data;
int drive = p - unit;
+ lock_kernel();
if (unit[drive].dirty == 1) {
del_timer (flush_track_timer + drive);
non_int_flush_track (drive);
@@ -1597,6 +1617,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
/* the mod_use counter is handled this way */
floppy_off (drive | 0x40000000);
#endif
+ unlock_kernel();
return 0;
}
@@ -1638,7 +1659,7 @@ static const struct block_device_operations floppy_fops = {
.owner = THIS_MODULE,
.open = floppy_open,
.release = floppy_release,
- .locked_ioctl = fd_ioctl,
+ .ioctl = fd_ioctl,
.getgeo = fd_getgeo,
.media_changed = amiga_floppy_change,
};
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 035cefe..a946929 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/genhd.h>
#include <linux/netdevice.h>
+#include <linux/smp_lock.h>
#include "aoe.h"
static struct kmem_cache *buf_pool_cache;
@@ -124,13 +125,16 @@ aoeblk_open(struct block_device *bdev, fmode_t mode)
struct aoedev *d = bdev->bd_disk->private_data;
ulong flags;
+ lock_kernel();
spin_lock_irqsave(&d->lock, flags);
if (d->flags & DEVFL_UP) {
d->nopen++;
spin_unlock_irqrestore(&d->lock, flags);
+ unlock_kernel();
return 0;
}
spin_unlock_irqrestore(&d->lock, flags);
+ unlock_kernel();
return -ENODEV;
}
@@ -173,7 +177,7 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
BUG();
bio_endio(bio, -ENXIO);
return 0;
- } else if (bio_rw_flagged(bio, BIO_RW_BARRIER)) {
+ } else if (bio->bi_rw & REQ_HARDBARRIER) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
} else if (bio->bi_io_vec == NULL) {
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index e35cf59..aceb964 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -67,6 +67,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <asm/atafd.h>
#include <asm/atafdreg.h>
@@ -359,7 +360,7 @@ static void finish_fdc( void );
static void finish_fdc_done( int dummy );
static void setup_req_params( int drive );
static void redo_fd_request( void);
-static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
cmd, unsigned long param);
static void fd_probe( int drive );
static int fd_test_drive_present( int drive );
@@ -1480,7 +1481,7 @@ void do_fd_request(struct request_queue * q)
atari_enable_irq( IRQ_MFP_FDC );
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
struct gendisk *disk = bdev->bd_disk;
@@ -1665,6 +1666,17 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode,
}
}
+static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ lock_kernel();
+ ret = fd_locked_ioctl(bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
/* Initialize the 'unit' variable for drive 'drive' */
@@ -1838,24 +1850,36 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return 0;
}
+static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
static int floppy_release(struct gendisk *disk, fmode_t mode)
{
struct atari_floppy_struct *p = disk->private_data;
+ lock_kernel();
if (p->ref < 0)
p->ref = 0;
else if (!p->ref--) {
printk(KERN_ERR "floppy_release with fd_ref == 0");
p->ref = 0;
}
+ unlock_kernel();
return 0;
}
static const struct block_device_operations floppy_fops = {
.owner = THIS_MODULE,
- .open = floppy_open,
+ .open = floppy_unlocked_open,
.release = floppy_release,
- .locked_ioctl = fd_ioctl,
+ .ioctl = fd_ioctl,
.media_changed = check_floppy_change,
.revalidate_disk= floppy_revalidate,
};
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index f1bf79d..1c7f637 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -15,6 +15,7 @@
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/highmem.h>
+#include <linux/smp_lock.h>
#include <linux/radix-tree.h>
#include <linux/buffer_head.h> /* invalidate_bh_lrus() */
#include <linux/slab.h>
@@ -340,7 +341,7 @@ static int brd_make_request(struct request_queue *q, struct bio *bio)
get_capacity(bdev->bd_disk))
goto out;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_DISCARD))) {
+ if (unlikely(bio->bi_rw & REQ_DISCARD)) {
err = 0;
discard_from_brd(brd, sector, bio->bi_size);
goto out;
@@ -401,6 +402,7 @@ static int brd_ioctl(struct block_device *bdev, fmode_t mode,
* ram device BLKFLSBUF has special semantics, we want to actually
* release and destroy the ramdisk data.
*/
+ lock_kernel();
mutex_lock(&bdev->bd_mutex);
error = -EBUSY;
if (bdev->bd_openers <= 1) {
@@ -417,13 +419,14 @@ static int brd_ioctl(struct block_device *bdev, fmode_t mode,
error = 0;
}
mutex_unlock(&bdev->bd_mutex);
+ unlock_kernel();
return error;
}
static const struct block_device_operations brd_fops = {
.owner = THIS_MODULE,
- .locked_ioctl = brd_ioctl,
+ .ioctl = brd_ioctl,
#ifdef CONFIG_BLK_DEV_XIP
.direct_access = brd_direct_access,
#endif
@@ -479,7 +482,7 @@ static struct brd_device *brd_alloc(int i)
if (!brd->brd_queue)
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_ordered(brd->brd_queue, QUEUE_ORDERED_TAG);
blk_queue_max_hw_sectors(brd->brd_queue, 1024);
blk_queue_bounce_limit(brd->brd_queue, BLK_BOUNCE_ANY);
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index e1e7143..31064df 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -56,16 +56,14 @@
#include <linux/kthread.h>
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "HP CISS Driver (v 3.6.20)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(3, 6, 20)
+#define DRIVER_NAME "HP CISS Driver (v 3.6.26)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(3, 6, 26)
/* Embedded module documentation macros - see modules.h */
MODULE_AUTHOR("Hewlett-Packard Company");
MODULE_DESCRIPTION("Driver for HP Smart Array Controllers");
-MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
- " SA6i P600 P800 P400 P400i E200 E200i E500 P700m"
- " Smart Array G2 Series SAS/SATA Controllers");
-MODULE_VERSION("3.6.20");
+MODULE_SUPPORTED_DEVICE("HP Smart Array Controllers");
+MODULE_VERSION("3.6.26");
MODULE_LICENSE("GPL");
static int cciss_allow_hpsa;
@@ -107,6 +105,11 @@ static const struct pci_device_id cciss_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, 0x3250},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3251},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3252},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3253},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3254},
{0,}
};
@@ -146,6 +149,11 @@ static struct board_type products[] = {
{0x3249103C, "Smart Array P812", &SA5_access},
{0x324A103C, "Smart Array P712m", &SA5_access},
{0x324B103C, "Smart Array P711m", &SA5_access},
+ {0x3250103C, "Smart Array", &SA5_access},
+ {0x3251103C, "Smart Array", &SA5_access},
+ {0x3252103C, "Smart Array", &SA5_access},
+ {0x3253103C, "Smart Array", &SA5_access},
+ {0x3254103C, "Smart Array", &SA5_access},
};
/* How long to wait (in milliseconds) for board to go into simple mode */
@@ -167,9 +175,13 @@ static DEFINE_MUTEX(scan_mutex);
static LIST_HEAD(scan_q);
static void do_cciss_request(struct request_queue *q);
-static irqreturn_t do_cciss_intr(int irq, void *dev_id);
+static irqreturn_t do_cciss_intx(int irq, void *dev_id);
+static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id);
static int cciss_open(struct block_device *bdev, fmode_t mode);
+static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode);
static int cciss_release(struct gendisk *disk, fmode_t mode);
+static int do_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg);
static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg);
static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
@@ -179,25 +191,23 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl);
static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all, int via_ioctl);
-static void cciss_read_capacity(int ctlr, int logvol,
+static void cciss_read_capacity(ctlr_info_t *h, int logvol,
sector_t *total_size, unsigned int *block_size);
-static void cciss_read_capacity_16(int ctlr, int logvol,
+static void cciss_read_capacity_16(ctlr_info_t *h, int logvol,
sector_t *total_size, unsigned int *block_size);
-static void cciss_geometry_inquiry(int ctlr, int logvol,
+static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol,
sector_t total_size,
unsigned int block_size, InquiryData_struct *inq_buff,
drive_info_struct *drv);
-static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
- __u32);
+static void __devinit cciss_interrupt_mode(ctlr_info_t *);
static void start_io(ctlr_info_t *h);
-static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
+static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
int cmd_type);
static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
int attempt_retry);
static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);
-static void fail_all_cmds(unsigned long ctlr);
static int add_to_scan_list(struct ctlr_info *h);
static int scan_thread(void *data);
static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
@@ -205,11 +215,23 @@ static void cciss_hba_release(struct device *dev);
static void cciss_device_release(struct device *dev);
static void cciss_free_gendisk(ctlr_info_t *h, int drv_index);
static void cciss_free_drive_info(ctlr_info_t *h, int drv_index);
+static inline u32 next_command(ctlr_info_t *h);
+static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev,
+ void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+ u64 *cfg_offset);
+static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev,
+ unsigned long *memory_bar);
+
+
+/* performant mode helper functions */
+static void calc_bucket_map(int *bucket, int num_buckets, int nsgs,
+ int *bucket_map);
+static void cciss_put_controller_into_performant_mode(ctlr_info_t *h);
#ifdef CONFIG_PROC_FS
-static void cciss_procinit(int i);
+static void cciss_procinit(ctlr_info_t *h);
#else
-static void cciss_procinit(int i)
+static void cciss_procinit(ctlr_info_t *h)
{
}
#endif /* CONFIG_PROC_FS */
@@ -221,9 +243,9 @@ static int cciss_compat_ioctl(struct block_device *, fmode_t,
static const struct block_device_operations cciss_fops = {
.owner = THIS_MODULE,
- .open = cciss_open,
+ .open = cciss_unlocked_open,
.release = cciss_release,
- .locked_ioctl = cciss_ioctl,
+ .ioctl = do_ioctl,
.getgeo = cciss_getgeo,
#ifdef CONFIG_COMPAT
.compat_ioctl = cciss_compat_ioctl,
@@ -231,6 +253,16 @@ static const struct block_device_operations cciss_fops = {
.revalidate_disk = cciss_revalidate,
};
+/* 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(ctlr_info_t *h, CommandList_struct *c)
+{
+ if (likely(h->transMethod == CFGTBL_Trans_Performant))
+ c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
+}
+
/*
* Enqueuing and dequeuing functions for cmdlists.
*/
@@ -257,6 +289,18 @@ static inline void removeQ(CommandList_struct *c)
hlist_del_init(&c->list);
}
+static void enqueue_cmd_and_start_io(ctlr_info_t *h,
+ CommandList_struct *c)
+{
+ unsigned long flags;
+ set_performant_mode(h, c);
+ spin_lock_irqsave(&h->lock, flags);
+ addQ(&h->reqQ, c);
+ h->Qdepth++;
+ start_io(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
static void cciss_free_sg_chain_blocks(SGDescriptor_struct **cmd_sg_list,
int nr_cmds)
{
@@ -366,32 +410,31 @@ static void cciss_seq_show_header(struct seq_file *seq)
h->product_name,
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
- h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT],
+ h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT],
h->num_luns,
h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);
#ifdef CONFIG_CISS_SCSI_TAPE
- cciss_seq_tape_report(seq, h->ctlr);
+ cciss_seq_tape_report(seq, h);
#endif /* CONFIG_CISS_SCSI_TAPE */
}
static void *cciss_seq_start(struct seq_file *seq, loff_t *pos)
{
ctlr_info_t *h = seq->private;
- unsigned ctlr = h->ctlr;
unsigned long flags;
/* prevent displaying bogus info during configuration
* or deconfiguration of a logical volume
*/
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return ERR_PTR(-EBUSY);
}
h->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (*pos == 0)
cciss_seq_show_header(seq);
@@ -499,7 +542,7 @@ cciss_proc_write(struct file *file, const char __user *buf,
struct seq_file *seq = file->private_data;
ctlr_info_t *h = seq->private;
- err = cciss_engage_scsi(h->ctlr);
+ err = cciss_engage_scsi(h);
if (err == 0)
err = length;
} else
@@ -522,7 +565,7 @@ static const struct file_operations cciss_proc_fops = {
.write = cciss_proc_write,
};
-static void __devinit cciss_procinit(int i)
+static void __devinit cciss_procinit(ctlr_info_t *h)
{
struct proc_dir_entry *pde;
@@ -530,9 +573,9 @@ static void __devinit cciss_procinit(int i)
proc_cciss = proc_mkdir("driver/cciss", NULL);
if (!proc_cciss)
return;
- pde = proc_create_data(hba[i]->devname, S_IWUSR | S_IRUSR | S_IRGRP |
+ pde = proc_create_data(h->devname, S_IWUSR | S_IRUSR | S_IRGRP |
S_IROTH, proc_cciss,
- &cciss_proc_fops, hba[i]);
+ &cciss_proc_fops, h);
}
#endif /* CONFIG_PROC_FS */
@@ -565,12 +608,12 @@ static ssize_t dev_show_unique_id(struct device *dev,
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(sn, drv->serial_no, sizeof(sn));
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
@@ -595,12 +638,12 @@ static ssize_t dev_show_vendor(struct device *dev,
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(vendor, drv->vendor, VENDOR_LEN + 1);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
@@ -619,12 +662,12 @@ static ssize_t dev_show_model(struct device *dev,
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(model, drv->model, MODEL_LEN + 1);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
@@ -643,12 +686,12 @@ static ssize_t dev_show_rev(struct device *dev,
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(rev, drv->rev, REV_LEN + 1);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
@@ -665,17 +708,17 @@ static ssize_t cciss_show_lunid(struct device *dev,
unsigned long flags;
unsigned char lunid[8];
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
if (!drv->heads) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -ENOTTY;
}
memcpy(lunid, drv->LunID, sizeof(lunid));
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
lunid[0], lunid[1], lunid[2], lunid[3],
lunid[4], lunid[5], lunid[6], lunid[7]);
@@ -690,13 +733,13 @@ static ssize_t cciss_show_raid_level(struct device *dev,
int raid;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
raid = drv->raid_level;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (raid < 0 || raid > RAID_UNKNOWN)
raid = RAID_UNKNOWN;
@@ -713,13 +756,13 @@ static ssize_t cciss_show_usage_count(struct device *dev,
unsigned long flags;
int count;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
count = drv->usage_count;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return snprintf(buf, 20, "%d\n", count);
}
static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
@@ -864,60 +907,70 @@ static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
/*
* For operations that cannot sleep, a command block is allocated at init,
* and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
- * which ones are free or in use. For operations that can wait for kmalloc
- * to possible sleep, this routine can be called with get_from_pool set to 0.
- * cmd_free() MUST be called with a got_from_pool set to 0 if cmd_alloc was.
+ * which ones are free or in use.
*/
-static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool)
+static CommandList_struct *cmd_alloc(ctlr_info_t *h)
{
CommandList_struct *c;
int i;
u64bit temp64;
dma_addr_t cmd_dma_handle, err_dma_handle;
- if (!get_from_pool) {
- c = (CommandList_struct *) pci_alloc_consistent(h->pdev,
- sizeof(CommandList_struct), &cmd_dma_handle);
- if (c == NULL)
+ do {
+ i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
+ if (i == h->nr_cmds)
return NULL;
- memset(c, 0, sizeof(CommandList_struct));
+ } while (test_and_set_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0);
+ c = h->cmd_pool + i;
+ memset(c, 0, sizeof(CommandList_struct));
+ cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(CommandList_struct);
+ c->err_info = h->errinfo_pool + i;
+ memset(c->err_info, 0, sizeof(ErrorInfo_struct));
+ err_dma_handle = h->errinfo_pool_dhandle
+ + i * sizeof(ErrorInfo_struct);
+ h->nr_allocs++;
- c->cmdindex = -1;
+ c->cmdindex = i;
- c->err_info = (ErrorInfo_struct *)
- pci_alloc_consistent(h->pdev, sizeof(ErrorInfo_struct),
- &err_dma_handle);
+ INIT_HLIST_NODE(&c->list);
+ 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(ErrorInfo_struct);
- if (c->err_info == NULL) {
- pci_free_consistent(h->pdev,
- sizeof(CommandList_struct), c, cmd_dma_handle);
- return NULL;
- }
- memset(c->err_info, 0, sizeof(ErrorInfo_struct));
- } else { /* get it out of the controllers pool */
-
- do {
- i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
- if (i == h->nr_cmds)
- return NULL;
- } while (test_and_set_bit
- (i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: using command buffer %d\n", i);
-#endif
- c = h->cmd_pool + i;
- memset(c, 0, sizeof(CommandList_struct));
- cmd_dma_handle = h->cmd_pool_dhandle
- + i * sizeof(CommandList_struct);
- c->err_info = h->errinfo_pool + i;
- memset(c->err_info, 0, sizeof(ErrorInfo_struct));
- err_dma_handle = h->errinfo_pool_dhandle
- + i * sizeof(ErrorInfo_struct);
- h->nr_allocs++;
+ c->ctlr = h->ctlr;
+ return c;
+}
- c->cmdindex = i;
+/* allocate a command using pci_alloc_consistent, used for ioctls,
+ * etc., not for the main i/o path.
+ */
+static CommandList_struct *cmd_special_alloc(ctlr_info_t *h)
+{
+ CommandList_struct *c;
+ u64bit temp64;
+ dma_addr_t cmd_dma_handle, err_dma_handle;
+
+ c = (CommandList_struct *) pci_alloc_consistent(h->pdev,
+ sizeof(CommandList_struct), &cmd_dma_handle);
+ if (c == NULL)
+ return NULL;
+ memset(c, 0, sizeof(CommandList_struct));
+
+ c->cmdindex = -1;
+
+ c->err_info = (ErrorInfo_struct *)
+ pci_alloc_consistent(h->pdev, sizeof(ErrorInfo_struct),
+ &err_dma_handle);
+
+ if (c->err_info == NULL) {
+ pci_free_consistent(h->pdev,
+ sizeof(CommandList_struct), c, cmd_dma_handle);
+ return NULL;
}
+ memset(c->err_info, 0, sizeof(ErrorInfo_struct));
INIT_HLIST_NODE(&c->list);
c->busaddr = (__u32) cmd_dma_handle;
@@ -930,27 +983,26 @@ static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool)
return c;
}
-/*
- * Frees a command block that was previously allocated with cmd_alloc().
- */
-static void cmd_free(ctlr_info_t *h, CommandList_struct *c, int got_from_pool)
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c)
{
int i;
+
+ i = c - h->cmd_pool;
+ clear_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG));
+ h->nr_frees++;
+}
+
+static void cmd_special_free(ctlr_info_t *h, CommandList_struct *c)
+{
u64bit temp64;
- if (!got_from_pool) {
- temp64.val32.lower = c->ErrDesc.Addr.lower;
- temp64.val32.upper = c->ErrDesc.Addr.upper;
- pci_free_consistent(h->pdev, sizeof(ErrorInfo_struct),
- c->err_info, (dma_addr_t) temp64.val);
- pci_free_consistent(h->pdev, sizeof(CommandList_struct),
- c, (dma_addr_t) c->busaddr);
- } else {
- i = c - h->cmd_pool;
- clear_bit(i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG));
- h->nr_frees++;
- }
+ temp64.val32.lower = c->ErrDesc.Addr.lower;
+ temp64.val32.upper = c->ErrDesc.Addr.upper;
+ pci_free_consistent(h->pdev, sizeof(ErrorInfo_struct),
+ c->err_info, (dma_addr_t) temp64.val);
+ pci_free_consistent(h->pdev, sizeof(CommandList_struct),
+ c, (dma_addr_t) c->busaddr);
}
static inline ctlr_info_t *get_host(struct gendisk *disk)
@@ -968,13 +1020,10 @@ static inline drive_info_struct *get_drv(struct gendisk *disk)
*/
static int cciss_open(struct block_device *bdev, fmode_t mode)
{
- ctlr_info_t *host = get_host(bdev->bd_disk);
+ ctlr_info_t *h = get_host(bdev->bd_disk);
drive_info_struct *drv = get_drv(bdev->bd_disk);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss_open %s\n", bdev->bd_disk->disk_name);
-#endif /* CCISS_DEBUG */
-
+ dev_dbg(&h->pdev->dev, "cciss_open %s\n", bdev->bd_disk->disk_name);
if (drv->busy_configuring)
return -EBUSY;
/*
@@ -1000,29 +1049,39 @@ static int cciss_open(struct block_device *bdev, fmode_t mode)
return -EPERM;
}
drv->usage_count++;
- host->usage_count++;
+ h->usage_count++;
return 0;
}
+static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = cciss_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
/*
* Close. Sync first.
*/
static int cciss_release(struct gendisk *disk, fmode_t mode)
{
- ctlr_info_t *host = get_host(disk);
- drive_info_struct *drv = get_drv(disk);
-
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss_release %s\n", disk->disk_name);
-#endif /* CCISS_DEBUG */
+ ctlr_info_t *h;
+ drive_info_struct *drv;
+ lock_kernel();
+ h = get_host(disk);
+ drv = get_drv(disk);
+ dev_dbg(&h->pdev->dev, "cciss_release %s\n", disk->disk_name);
drv->usage_count--;
- host->usage_count--;
+ h->usage_count--;
+ unlock_kernel();
return 0;
}
-#ifdef CONFIG_COMPAT
-
static int do_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
@@ -1033,6 +1092,8 @@ static int do_ioctl(struct block_device *bdev, fmode_t mode,
return ret;
}
+#ifdef CONFIG_COMPAT
+
static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg);
static int cciss_ioctl32_big_passthru(struct block_device *bdev, fmode_t mode,
@@ -1163,11 +1224,11 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return 0;
}
-static void check_ioctl_unit_attention(ctlr_info_t *host, CommandList_struct *c)
+static void check_ioctl_unit_attention(ctlr_info_t *h, CommandList_struct *c)
{
if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION)
- (void)check_for_unit_attention(host, c);
+ (void)check_for_unit_attention(h, c);
}
/*
* ioctl
@@ -1176,15 +1237,12 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct gendisk *disk = bdev->bd_disk;
- ctlr_info_t *host = get_host(disk);
+ ctlr_info_t *h = get_host(disk);
drive_info_struct *drv = get_drv(disk);
- int ctlr = host->ctlr;
void __user *argp = (void __user *)arg;
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg);
-#endif /* CCISS_DEBUG */
-
+ dev_dbg(&h->pdev->dev, "cciss_ioctl: Called with cmd=%x %lx\n",
+ cmd, arg);
switch (cmd) {
case CCISS_GETPCIINFO:
{
@@ -1192,10 +1250,10 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (!arg)
return -EINVAL;
- pciinfo.domain = pci_domain_nr(host->pdev->bus);
- pciinfo.bus = host->pdev->bus->number;
- pciinfo.dev_fn = host->pdev->devfn;
- pciinfo.board_id = host->board_id;
+ pciinfo.domain = pci_domain_nr(h->pdev->bus);
+ pciinfo.bus = h->pdev->bus->number;
+ pciinfo.dev_fn = h->pdev->devfn;
+ pciinfo.board_id = h->board_id;
if (copy_to_user
(argp, &pciinfo, sizeof(cciss_pci_info_struct)))
return -EFAULT;
@@ -1207,9 +1265,9 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (!arg)
return -EINVAL;
intinfo.delay =
- readl(&host->cfgtable->HostWrite.CoalIntDelay);
+ readl(&h->cfgtable->HostWrite.CoalIntDelay);
intinfo.count =
- readl(&host->cfgtable->HostWrite.CoalIntCount);
+ readl(&h->cfgtable->HostWrite.CoalIntCount);
if (copy_to_user
(argp, &intinfo, sizeof(cciss_coalint_struct)))
return -EFAULT;
@@ -1229,26 +1287,23 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
(&intinfo, argp, sizeof(cciss_coalint_struct)))
return -EFAULT;
if ((intinfo.delay == 0) && (intinfo.count == 0))
- {
-// printk("cciss_ioctl: delay and count cannot be 0\n");
return -EINVAL;
- }
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
/* Update the field, and then ring the doorbell */
writel(intinfo.delay,
- &(host->cfgtable->HostWrite.CoalIntDelay));
+ &(h->cfgtable->HostWrite.CoalIntDelay));
writel(intinfo.count,
- &(host->cfgtable->HostWrite.CoalIntCount));
- writel(CFGTBL_ChangeReq, host->vaddr + SA5_DOORBELL);
+ &(h->cfgtable->HostWrite.CoalIntCount));
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) {
- if (!(readl(host->vaddr + SA5_DOORBELL)
+ if (!(readl(h->vaddr + SA5_DOORBELL)
& CFGTBL_ChangeReq))
break;
/* delay and try again */
udelay(1000);
}
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (i >= MAX_IOCTL_CONFIG_WAIT)
return -EAGAIN;
return 0;
@@ -1262,7 +1317,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
return -EINVAL;
for (i = 0; i < 16; i++)
NodeName[i] =
- readb(&host->cfgtable->ServerName[i]);
+ readb(&h->cfgtable->ServerName[i]);
if (copy_to_user(argp, NodeName, sizeof(NodeName_type)))
return -EFAULT;
return 0;
@@ -1282,23 +1337,23 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
(NodeName, argp, sizeof(NodeName_type)))
return -EFAULT;
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
/* Update the field, and then ring the doorbell */
for (i = 0; i < 16; i++)
writeb(NodeName[i],
- &host->cfgtable->ServerName[i]);
+ &h->cfgtable->ServerName[i]);
- writel(CFGTBL_ChangeReq, host->vaddr + SA5_DOORBELL);
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) {
- if (!(readl(host->vaddr + SA5_DOORBELL)
+ if (!(readl(h->vaddr + SA5_DOORBELL)
& CFGTBL_ChangeReq))
break;
/* delay and try again */
udelay(1000);
}
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (i >= MAX_IOCTL_CONFIG_WAIT)
return -EAGAIN;
return 0;
@@ -1310,7 +1365,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (!arg)
return -EINVAL;
- heartbeat = readl(&host->cfgtable->HeartBeat);
+ heartbeat = readl(&h->cfgtable->HeartBeat);
if (copy_to_user
(argp, &heartbeat, sizeof(Heartbeat_type)))
return -EFAULT;
@@ -1322,7 +1377,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (!arg)
return -EINVAL;
- BusTypes = readl(&host->cfgtable->BusTypes);
+ BusTypes = readl(&h->cfgtable->BusTypes);
if (copy_to_user
(argp, &BusTypes, sizeof(BusTypes_type)))
return -EFAULT;
@@ -1334,7 +1389,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (!arg)
return -EINVAL;
- memcpy(firmware, host->firm_ver, 4);
+ memcpy(firmware, h->firm_ver, 4);
if (copy_to_user
(argp, firmware, sizeof(FirmwareVer_type)))
@@ -1357,7 +1412,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
case CCISS_DEREGDISK:
case CCISS_REGNEWD:
case CCISS_REVALIDVOLS:
- return rebuild_lun_table(host, 0, 1);
+ return rebuild_lun_table(h, 0, 1);
case CCISS_GETLUNINFO:{
LogvolInfo_struct luninfo;
@@ -1377,7 +1432,6 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
CommandList_struct *c;
char *buff = NULL;
u64bit temp64;
- unsigned long flags;
DECLARE_COMPLETION_ONSTACK(wait);
if (!arg)
@@ -1413,7 +1467,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
} else {
memset(buff, 0, iocommand.buf_size);
}
- if ((c = cmd_alloc(host, 0)) == NULL) {
+ c = cmd_special_alloc(h);
+ if (!c) {
kfree(buff);
return -ENOMEM;
}
@@ -1439,7 +1494,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
/* Fill in the scatter gather information */
if (iocommand.buf_size > 0) {
- temp64.val = pci_map_single(host->pdev, buff,
+ temp64.val = pci_map_single(h->pdev, buff,
iocommand.buf_size,
PCI_DMA_BIDIRECTIONAL);
c->SG[0].Addr.lower = temp64.val32.lower;
@@ -1449,30 +1504,24 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
}
c->waiting = &wait;
- /* Put the request on the tail of the request queue */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- addQ(&host->reqQ, c);
- host->Qdepth++;
- start_io(host);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
/* unlock the buffers from DMA */
temp64.val32.lower = c->SG[0].Addr.lower;
temp64.val32.upper = c->SG[0].Addr.upper;
- pci_unmap_single(host->pdev, (dma_addr_t) temp64.val,
+ pci_unmap_single(h->pdev, (dma_addr_t) temp64.val,
iocommand.buf_size,
PCI_DMA_BIDIRECTIONAL);
- check_ioctl_unit_attention(host, c);
+ check_ioctl_unit_attention(h, c);
/* Copy the error information out */
iocommand.error_info = *(c->err_info);
if (copy_to_user
(argp, &iocommand, sizeof(IOCTL_Command_struct))) {
kfree(buff);
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
return -EFAULT;
}
@@ -1481,12 +1530,12 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (copy_to_user
(iocommand.buf, buff, iocommand.buf_size)) {
kfree(buff);
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
return -EFAULT;
}
}
kfree(buff);
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
return 0;
}
case CCISS_BIG_PASSTHRU:{
@@ -1495,7 +1544,6 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
unsigned char **buff = NULL;
int *buff_size = NULL;
u64bit temp64;
- unsigned long flags;
BYTE sg_used = 0;
int status = 0;
int i;
@@ -1569,7 +1617,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
data_ptr += sz;
sg_used++;
}
- if ((c = cmd_alloc(host, 0)) == NULL) {
+ c = cmd_special_alloc(h);
+ if (!c) {
status = -ENOMEM;
goto cleanup1;
}
@@ -1590,7 +1639,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
if (ioc->buf_size > 0) {
for (i = 0; i < sg_used; i++) {
temp64.val =
- pci_map_single(host->pdev, buff[i],
+ pci_map_single(h->pdev, buff[i],
buff_size[i],
PCI_DMA_BIDIRECTIONAL);
c->SG[i].Addr.lower =
@@ -1602,26 +1651,21 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
}
}
c->waiting = &wait;
- /* Put the request on the tail of the request queue */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- addQ(&host->reqQ, c);
- host->Qdepth++;
- start_io(host);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
/* unlock the buffers from DMA */
for (i = 0; i < sg_used; i++) {
temp64.val32.lower = c->SG[i].Addr.lower;
temp64.val32.upper = c->SG[i].Addr.upper;
- pci_unmap_single(host->pdev,
+ pci_unmap_single(h->pdev,
(dma_addr_t) temp64.val, buff_size[i],
PCI_DMA_BIDIRECTIONAL);
}
- check_ioctl_unit_attention(host, c);
+ check_ioctl_unit_attention(h, c);
/* Copy the error information out */
ioc->error_info = *(c->err_info);
if (copy_to_user(argp, ioc, sizeof(*ioc))) {
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
status = -EFAULT;
goto cleanup1;
}
@@ -1631,14 +1675,14 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
for (i = 0; i < sg_used; i++) {
if (copy_to_user
(ptr, buff[i], buff_size[i])) {
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
status = -EFAULT;
goto cleanup1;
}
ptr += buff_size[i];
}
}
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
status = 0;
cleanup1:
if (buff) {
@@ -1726,26 +1770,26 @@ static void cciss_check_queues(ctlr_info_t *h)
static void cciss_softirq_done(struct request *rq)
{
- CommandList_struct *cmd = rq->completion_data;
- ctlr_info_t *h = hba[cmd->ctlr];
- SGDescriptor_struct *curr_sg = cmd->SG;
- unsigned long flags;
+ CommandList_struct *c = rq->completion_data;
+ ctlr_info_t *h = hba[c->ctlr];
+ SGDescriptor_struct *curr_sg = c->SG;
u64bit temp64;
+ unsigned long flags;
int i, ddir;
int sg_index = 0;
- if (cmd->Request.Type.Direction == XFER_READ)
+ if (c->Request.Type.Direction == XFER_READ)
ddir = PCI_DMA_FROMDEVICE;
else
ddir = PCI_DMA_TODEVICE;
/* command did not need to be retried */
/* unmap the DMA mapping for all the scatter gather elements */
- for (i = 0; i < cmd->Header.SGList; i++) {
+ for (i = 0; i < c->Header.SGList; i++) {
if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) {
- cciss_unmap_sg_chain_block(h, cmd);
+ cciss_unmap_sg_chain_block(h, c);
/* Point to the next block */
- curr_sg = h->cmd_sg_list[cmd->cmdindex];
+ curr_sg = h->cmd_sg_list[c->cmdindex];
sg_index = 0;
}
temp64.val32.lower = curr_sg[sg_index].Addr.lower;
@@ -1755,18 +1799,16 @@ static void cciss_softirq_done(struct request *rq)
++sg_index;
}
-#ifdef CCISS_DEBUG
- printk("Done with %p\n", rq);
-#endif /* CCISS_DEBUG */
+ dev_dbg(&h->pdev->dev, "Done with %p\n", rq);
/* set the residual count for pc requests */
- if (blk_pc_request(rq))
- rq->resid_len = cmd->err_info->ResidualCnt;
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
+ rq->resid_len = c->err_info->ResidualCnt;
blk_end_request_all(rq, (rq->errors == 0) ? 0 : -EIO);
spin_lock_irqsave(&h->lock, flags);
- cmd_free(h, cmd, 1);
+ cmd_free(h, c);
cciss_check_queues(h);
spin_unlock_irqrestore(&h->lock, flags);
}
@@ -1782,7 +1824,7 @@ static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
* via the inquiry page 0. Model, vendor, and rev are set to empty strings if
* they cannot be read.
*/
-static void cciss_get_device_descr(int ctlr, int logvol,
+static void cciss_get_device_descr(ctlr_info_t *h, int logvol,
char *vendor, char *model, char *rev)
{
int rc;
@@ -1797,8 +1839,8 @@ static void cciss_get_device_descr(int ctlr, int logvol,
if (!inq_buf)
return;
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf, sizeof(*inq_buf), 0,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ rc = sendcmd_withirq(h, CISS_INQUIRY, inq_buf, sizeof(*inq_buf), 0,
scsi3addr, TYPE_CMD);
if (rc == IO_OK) {
memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN);
@@ -1818,7 +1860,7 @@ static void cciss_get_device_descr(int ctlr, int logvol,
* number cannot be had, for whatever reason, 16 bytes of 0xff
* are returned instead.
*/
-static void cciss_get_serial_no(int ctlr, int logvol,
+static void cciss_get_serial_no(ctlr_info_t *h, int logvol,
unsigned char *serial_no, int buflen)
{
#define PAGE_83_INQ_BYTES 64
@@ -1833,8 +1875,8 @@ static void cciss_get_serial_no(int ctlr, int logvol,
if (!buf)
return;
memset(serial_no, 0, buflen);
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ rc = sendcmd_withirq(h, CISS_INQUIRY, buf,
PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
if (rc == IO_OK)
memcpy(serial_no, &buf[8], buflen);
@@ -1900,10 +1942,9 @@ init_queue_failure:
* is also the controller node. Any changes to disk 0 will show up on
* the next reboot.
*/
-static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
- int via_ioctl)
+static void cciss_update_drive_info(ctlr_info_t *h, int drv_index,
+ int first_time, int via_ioctl)
{
- ctlr_info_t *h = hba[ctlr];
struct gendisk *disk;
InquiryData_struct *inq_buff = NULL;
unsigned int block_size;
@@ -1920,16 +1961,16 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
/* testing to see if 16-byte CDBs are already being used */
if (h->cciss_read == CCISS_READ_16) {
- cciss_read_capacity_16(h->ctlr, drv_index,
+ cciss_read_capacity_16(h, drv_index,
&total_size, &block_size);
} else {
- cciss_read_capacity(ctlr, drv_index, &total_size, &block_size);
+ cciss_read_capacity(h, drv_index, &total_size, &block_size);
/* if read_capacity returns all F's this volume is >2TB */
/* in size so we switch to 16-byte CDB's for all */
/* read/write ops */
if (total_size == 0xFFFFFFFFULL) {
- cciss_read_capacity_16(ctlr, drv_index,
+ cciss_read_capacity_16(h, drv_index,
&total_size, &block_size);
h->cciss_read = CCISS_READ_16;
h->cciss_write = CCISS_WRITE_16;
@@ -1939,14 +1980,14 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
}
}
- cciss_geometry_inquiry(ctlr, drv_index, total_size, block_size,
+ cciss_geometry_inquiry(h, drv_index, total_size, block_size,
inq_buff, drvinfo);
drvinfo->block_size = block_size;
drvinfo->nr_blocks = total_size + 1;
- cciss_get_device_descr(ctlr, drv_index, drvinfo->vendor,
+ cciss_get_device_descr(h, drv_index, drvinfo->vendor,
drvinfo->model, drvinfo->rev);
- cciss_get_serial_no(ctlr, drv_index, drvinfo->serial_no,
+ cciss_get_serial_no(h, drv_index, drvinfo->serial_no,
sizeof(drvinfo->serial_no));
/* Save the lunid in case we deregister the disk, below. */
memcpy(drvinfo->LunID, h->drv[drv_index]->LunID,
@@ -1971,10 +2012,10 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
* (unless it's the first disk (for the controller node).
*/
if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) {
- printk(KERN_WARNING "disk %d has changed.\n", drv_index);
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ dev_warn(&h->pdev->dev, "disk %d has changed.\n", drv_index);
+ spin_lock_irqsave(&h->lock, flags);
h->drv[drv_index]->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
/* deregister_disk sets h->drv[drv_index]->queue = NULL
* which keeps the interrupt handler from starting
@@ -2024,8 +2065,8 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
if (cciss_add_disk(h, disk, drv_index) != 0) {
cciss_free_gendisk(h, drv_index);
cciss_free_drive_info(h, drv_index);
- printk(KERN_WARNING "cciss:%d could not update "
- "disk %d\n", h->ctlr, drv_index);
+ dev_warn(&h->pdev->dev, "could not update disk %d\n",
+ drv_index);
--h->num_luns;
}
}
@@ -2035,7 +2076,7 @@ freeret:
kfree(drvinfo);
return;
mem_msg:
- printk(KERN_ERR "cciss: out of memory\n");
+ dev_err(&h->pdev->dev, "out of memory\n");
goto freeret;
}
@@ -2127,9 +2168,9 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
h->gendisk[drv_index] =
alloc_disk(1 << NWD_SHIFT);
if (!h->gendisk[drv_index]) {
- printk(KERN_ERR "cciss%d: could not "
- "allocate a new disk %d\n",
- h->ctlr, drv_index);
+ dev_err(&h->pdev->dev,
+ "could not allocate a new disk %d\n",
+ drv_index);
goto err_free_drive_info;
}
}
@@ -2180,8 +2221,7 @@ static void cciss_add_controller_node(ctlr_info_t *h)
cciss_free_gendisk(h, drv_index);
cciss_free_drive_info(h, drv_index);
error:
- printk(KERN_WARNING "cciss%d: could not "
- "add disk 0.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "could not add disk 0.\n");
return;
}
@@ -2196,7 +2236,6 @@ error:
static int rebuild_lun_table(ctlr_info_t *h, int first_time,
int via_ioctl)
{
- int ctlr = h->ctlr;
int num_luns;
ReportLunData_struct *ld_buff = NULL;
int return_code;
@@ -2211,27 +2250,27 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
return -EPERM;
/* Set busy_configuring flag for this operation */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
h->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
if (ld_buff == NULL)
goto mem_msg;
- return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff,
+ return_code = sendcmd_withirq(h, CISS_REPORT_LOG, ld_buff,
sizeof(ReportLunData_struct),
0, CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK)
listlength = be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
else { /* reading number of logical volumes failed */
- printk(KERN_WARNING "cciss: report logical volume"
- " command failed\n");
+ dev_warn(&h->pdev->dev,
+ "report logical volume command failed\n");
listlength = 0;
goto freeret;
}
@@ -2239,7 +2278,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
num_luns = listlength / 8; /* 8 bytes per entry */
if (num_luns > CISS_MAX_LUN) {
num_luns = CISS_MAX_LUN;
- printk(KERN_WARNING "cciss: more luns configured"
+ dev_warn(&h->pdev->dev, "more luns configured"
" on controller than can be handled by"
" this driver.\n");
}
@@ -2270,9 +2309,9 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
}
if (!drv_found) {
/* Deregister it from the OS, it's gone. */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
h->drv[i]->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return_code = deregister_disk(h, i, 1, via_ioctl);
if (h->drv[i] != NULL)
h->drv[i]->busy_configuring = 0;
@@ -2311,8 +2350,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
if (drv_index == -1)
goto freeret;
}
- cciss_update_drive_info(ctlr, drv_index, first_time,
- via_ioctl);
+ cciss_update_drive_info(h, drv_index, first_time, via_ioctl);
} /* end for */
freeret:
@@ -2324,7 +2362,7 @@ freeret:
*/
return -1;
mem_msg:
- printk(KERN_ERR "cciss: out of memory\n");
+ dev_err(&h->pdev->dev, "out of memory\n");
h->busy_configuring = 0;
goto freeret;
}
@@ -2444,11 +2482,10 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
return 0;
}
-static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
size_t size, __u8 page_code, unsigned char *scsi3addr,
int cmd_type)
{
- ctlr_info_t *h = hba[ctlr];
u64bit buff_dma_handle;
int status = IO_OK;
@@ -2532,8 +2569,7 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
c->Request.Timeout = 0;
break;
default:
- printk(KERN_WARNING
- "cciss%d: Unknown Command 0x%c\n", ctlr, cmd);
+ dev_warn(&h->pdev->dev, "Unknown Command 0x%c\n", cmd);
return IO_ERROR;
}
} else if (cmd_type == TYPE_MSG) {
@@ -2565,13 +2601,12 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
c->Request.CDB[0] = cmd;
break;
default:
- printk(KERN_WARNING
- "cciss%d: unknown message type %d\n", ctlr, cmd);
+ dev_warn(&h->pdev->dev,
+ "unknown message type %d\n", cmd);
return IO_ERROR;
}
} else {
- printk(KERN_WARNING
- "cciss%d: unknown command type %d\n", ctlr, cmd_type);
+ dev_warn(&h->pdev->dev, "unknown command type %d\n", cmd_type);
return IO_ERROR;
}
/* Fill in the scatter gather information */
@@ -2599,15 +2634,14 @@ static int check_target_status(ctlr_info_t *h, CommandList_struct *c)
default:
if (check_for_unit_attention(h, c))
return IO_NEEDS_RETRY;
- printk(KERN_WARNING "cciss%d: cmd 0x%02x "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x "
"check condition, sense key = 0x%02x\n",
- h->ctlr, c->Request.CDB[0],
- c->err_info->SenseInfo[2]);
+ c->Request.CDB[0], c->err_info->SenseInfo[2]);
}
break;
default:
- printk(KERN_WARNING "cciss%d: cmd 0x%02x"
- "scsi status = 0x%02x\n", h->ctlr,
+ dev_warn(&h->pdev->dev, "cmd 0x%02x"
+ "scsi status = 0x%02x\n",
c->Request.CDB[0], c->err_info->ScsiStatus);
break;
}
@@ -2630,43 +2664,42 @@ static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c)
/* expected for inquiry and report lun commands */
break;
case CMD_INVALID:
- printk(KERN_WARNING "cciss: cmd 0x%02x is "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x is "
"reported invalid\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cmd 0x%02x has "
- "protocol error \n", c->Request.CDB[0]);
+ dev_warn(&h->pdev->dev, "cmd 0x%02x has "
+ "protocol error\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_HARDWARE_ERR:
- printk(KERN_WARNING "cciss: cmd 0x%02x had "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x had "
" hardware error\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cmd 0x%02x had "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x had "
"connection lost\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cmd 0x%02x was "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x was "
"aborted\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cmd 0x%02x reports "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x reports "
"abort failed\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING
- "cciss%d: unsolicited abort 0x%02x\n", h->ctlr,
+ dev_warn(&h->pdev->dev, "unsolicited abort 0x%02x\n",
c->Request.CDB[0]);
return_status = IO_NEEDS_RETRY;
break;
default:
- printk(KERN_WARNING "cciss: cmd 0x%02x returned "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x returned "
"unknown status %x\n", c->Request.CDB[0],
c->err_info->CommandStatus);
return_status = IO_ERROR;
@@ -2679,17 +2712,11 @@ static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
{
DECLARE_COMPLETION_ONSTACK(wait);
u64bit buff_dma_handle;
- unsigned long flags;
int return_status = IO_OK;
resend_cmd2:
c->waiting = &wait;
- /* Put the request on the tail of the queue and send it */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
- addQ(&h->reqQ, c);
- h->Qdepth++;
- start_io(h);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
@@ -2700,7 +2727,7 @@ resend_cmd2:
if (return_status == IO_NEEDS_RETRY &&
c->retry_count < MAX_CMD_RETRIES) {
- printk(KERN_WARNING "cciss%d: retrying 0x%02x\n", h->ctlr,
+ dev_warn(&h->pdev->dev, "retrying 0x%02x\n",
c->Request.CDB[0]);
c->retry_count++;
/* erase the old error information */
@@ -2719,27 +2746,26 @@ command_done:
return return_status;
}
-static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
+static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
int cmd_type)
{
- ctlr_info_t *h = hba[ctlr];
CommandList_struct *c;
int return_status;
- c = cmd_alloc(h, 0);
+ c = cmd_special_alloc(h);
if (!c)
return -ENOMEM;
- return_status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
+ return_status = fill_cmd(h, c, cmd, buff, size, page_code,
scsi3addr, cmd_type);
if (return_status == IO_OK)
return_status = sendcmd_withirq_core(h, c, 1);
- cmd_free(h, c, 0);
+ cmd_special_free(h, c);
return return_status;
}
-static void cciss_geometry_inquiry(int ctlr, int logvol,
+static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol,
sector_t total_size,
unsigned int block_size,
InquiryData_struct *inq_buff,
@@ -2750,13 +2776,13 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
unsigned char scsi3addr[8];
memset(inq_buff, 0, sizeof(InquiryData_struct));
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buff,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ return_code = sendcmd_withirq(h, CISS_INQUIRY, inq_buff,
sizeof(*inq_buff), 0xC1, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
if (inq_buff->data_byte[8] == 0xFF) {
- printk(KERN_WARNING
- "cciss: reading geometry failed, volume "
+ dev_warn(&h->pdev->dev,
+ "reading geometry failed, volume "
"does not support reading geometry\n");
drv->heads = 255;
drv->sectors = 32; /* Sectors per track */
@@ -2780,12 +2806,12 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
drv->cylinders = real_size;
}
} else { /* Get geometry failed */
- printk(KERN_WARNING "cciss: reading geometry failed\n");
+ dev_warn(&h->pdev->dev, "reading geometry failed\n");
}
}
static void
-cciss_read_capacity(int ctlr, int logvol, sector_t *total_size,
+cciss_read_capacity(ctlr_info_t *h, int logvol, sector_t *total_size,
unsigned int *block_size)
{
ReadCapdata_struct *buf;
@@ -2794,25 +2820,25 @@ cciss_read_capacity(int ctlr, int logvol, sector_t *total_size,
buf = kzalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
if (!buf) {
- printk(KERN_WARNING "cciss: out of memory\n");
+ dev_warn(&h->pdev->dev, "out of memory\n");
return;
}
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- return_code = sendcmd_withirq(CCISS_READ_CAPACITY, ctlr, buf,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ return_code = sendcmd_withirq(h, CCISS_READ_CAPACITY, buf,
sizeof(ReadCapdata_struct), 0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be32_to_cpu(*(__be32 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
} else { /* read capacity command failed */
- printk(KERN_WARNING "cciss: read capacity failed\n");
+ dev_warn(&h->pdev->dev, "read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
kfree(buf);
}
-static void cciss_read_capacity_16(int ctlr, int logvol,
+static void cciss_read_capacity_16(ctlr_info_t *h, int logvol,
sector_t *total_size, unsigned int *block_size)
{
ReadCapdata_struct_16 *buf;
@@ -2821,23 +2847,23 @@ static void cciss_read_capacity_16(int ctlr, int logvol,
buf = kzalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL);
if (!buf) {
- printk(KERN_WARNING "cciss: out of memory\n");
+ dev_warn(&h->pdev->dev, "out of memory\n");
return;
}
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
- ctlr, buf, sizeof(ReadCapdata_struct_16),
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ return_code = sendcmd_withirq(h, CCISS_READ_CAPACITY_16,
+ buf, sizeof(ReadCapdata_struct_16),
0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be64_to_cpu(*(__be64 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
} else { /* read capacity command failed */
- printk(KERN_WARNING "cciss: read capacity failed\n");
+ dev_warn(&h->pdev->dev, "read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
- printk(KERN_INFO " blocks= %llu block_size= %d\n",
+ dev_info(&h->pdev->dev, " blocks= %llu block_size= %d\n",
(unsigned long long)*total_size+1, *block_size);
kfree(buf);
}
@@ -2865,17 +2891,17 @@ static int cciss_revalidate(struct gendisk *disk)
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL) {
- printk(KERN_WARNING "cciss: out of memory\n");
+ dev_warn(&h->pdev->dev, "out of memory\n");
return 1;
}
if (h->cciss_read == CCISS_READ_10) {
- cciss_read_capacity(h->ctlr, logvol,
+ cciss_read_capacity(h, logvol,
&total_size, &block_size);
} else {
- cciss_read_capacity_16(h->ctlr, logvol,
+ cciss_read_capacity_16(h, logvol,
&total_size, &block_size);
}
- cciss_geometry_inquiry(h->ctlr, logvol, total_size, block_size,
+ cciss_geometry_inquiry(h, logvol, total_size, block_size,
inq_buff, drv);
blk_queue_logical_block_size(drv->queue, drv->block_size);
@@ -2909,7 +2935,7 @@ static void start_io(ctlr_info_t *h)
c = hlist_entry(h->reqQ.first, CommandList_struct, list);
/* can't do anything if fifo is full */
if ((h->access.fifo_full(h))) {
- printk(KERN_WARNING "cciss: fifo full\n");
+ dev_warn(&h->pdev->dev, "fifo full\n");
break;
}
@@ -2925,7 +2951,7 @@ static void start_io(ctlr_info_t *h)
}
}
-/* Assumes that CCISS_LOCK(h->ctlr) is held. */
+/* Assumes that h->lock is held. */
/* Zeros out the error record and then resends the command back */
/* to the controller */
static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
@@ -2966,7 +2992,7 @@ static inline int evaluate_target_status(ctlr_info_t *h,
driver_byte = DRIVER_OK;
msg_byte = cmd->err_info->CommandStatus; /* correct? seems too device specific */
- if (blk_pc_request(cmd->rq))
+ if (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC)
host_byte = DID_PASSTHROUGH;
else
host_byte = DID_OK;
@@ -2975,8 +3001,8 @@ static inline int evaluate_target_status(ctlr_info_t *h,
host_byte, driver_byte);
if (cmd->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) {
- if (!blk_pc_request(cmd->rq))
- printk(KERN_WARNING "cciss: cmd %p "
+ if (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC)
+ dev_warn(&h->pdev->dev, "cmd %p "
"has SCSI Status 0x%x\n",
cmd, cmd->err_info->ScsiStatus);
return error_value;
@@ -2985,17 +3011,19 @@ static inline int evaluate_target_status(ctlr_info_t *h,
/* check the sense key */
sense_key = 0xf & cmd->err_info->SenseInfo[2];
/* no status or recovered error */
- if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq))
+ if (((sense_key == 0x0) || (sense_key == 0x1)) &&
+ (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC))
error_value = 0;
if (check_for_unit_attention(h, cmd)) {
- *retry_cmd = !blk_pc_request(cmd->rq);
+ *retry_cmd = !(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC);
return 0;
}
- if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */
+ /* Not SG_IO or similar? */
+ if (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC) {
if (error_value != 0)
- printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION"
+ dev_warn(&h->pdev->dev, "cmd %p has CHECK CONDITION"
" sense key = 0x%x\n", cmd, sense_key);
return error_value;
}
@@ -3035,90 +3063,97 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
rq->errors = evaluate_target_status(h, cmd, &retry_cmd);
break;
case CMD_DATA_UNDERRUN:
- if (blk_fs_request(cmd->rq)) {
- printk(KERN_WARNING "cciss: cmd %p has"
+ if (cmd->rq->cmd_type == REQ_TYPE_FS) {
+ dev_warn(&h->pdev->dev, "cmd %p has"
" completed with data underrun "
"reported\n", cmd);
cmd->rq->resid_len = cmd->err_info->ResidualCnt;
}
break;
case CMD_DATA_OVERRUN:
- if (blk_fs_request(cmd->rq))
- printk(KERN_WARNING "cciss: cmd %p has"
+ if (cmd->rq->cmd_type == REQ_TYPE_FS)
+ dev_warn(&h->pdev->dev, "cciss: cmd %p has"
" completed with data overrun "
"reported\n", cmd);
break;
case CMD_INVALID:
- printk(KERN_WARNING "cciss: cmd %p is "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p is "
"reported invalid\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cmd %p has "
- "protocol error \n", cmd);
+ dev_warn(&h->pdev->dev, "cciss: cmd %p has "
+ "protocol error\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_HARDWARE_ERR:
- printk(KERN_WARNING "cciss: cmd %p had "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p had "
" hardware error\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cmd %p had "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p had "
"connection lost\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cmd %p was "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p was "
"aborted\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ABORT);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ABORT);
break;
case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cmd %p reports "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p reports "
"abort failed\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING "cciss%d: unsolicited "
+ dev_warn(&h->pdev->dev, "cciss%d: unsolicited "
"abort %p\n", h->ctlr, cmd);
if (cmd->retry_count < MAX_CMD_RETRIES) {
retry_cmd = 1;
- printk(KERN_WARNING
- "cciss%d: retrying %p\n", h->ctlr, cmd);
+ dev_warn(&h->pdev->dev, "retrying %p\n", cmd);
cmd->retry_count++;
} else
- printk(KERN_WARNING
- "cciss%d: %p retried too "
- "many times\n", h->ctlr, cmd);
+ dev_warn(&h->pdev->dev,
+ "%p retried too many times\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ABORT);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ABORT);
break;
case CMD_TIMEOUT:
- printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd);
+ dev_warn(&h->pdev->dev, "cmd %p timedout\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
default:
- printk(KERN_WARNING "cciss: cmd %p returned "
+ dev_warn(&h->pdev->dev, "cmd %p returned "
"unknown status %x\n", cmd,
cmd->err_info->CommandStatus);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
}
after_error_processing:
@@ -3132,6 +3167,34 @@ after_error_processing:
blk_complete_request(cmd->rq);
}
+static inline u32 cciss_tag_contains_index(u32 tag)
+{
+#define DIRECT_LOOKUP_BIT 0x10
+ return tag & DIRECT_LOOKUP_BIT;
+}
+
+static inline u32 cciss_tag_to_index(u32 tag)
+{
+#define DIRECT_LOOKUP_SHIFT 5
+ return tag >> DIRECT_LOOKUP_SHIFT;
+}
+
+static inline u32 cciss_tag_discard_error_bits(u32 tag)
+{
+#define CCISS_ERROR_BITS 0x03
+ return tag & ~CCISS_ERROR_BITS;
+}
+
+static inline void cciss_mark_tag_indexed(u32 *tag)
+{
+ *tag |= DIRECT_LOOKUP_BIT;
+}
+
+static inline void cciss_set_tag_index(u32 *tag, u32 index)
+{
+ *tag |= (index << DIRECT_LOOKUP_SHIFT);
+}
+
/*
* Get a request and submit it to the controller.
*/
@@ -3163,7 +3226,8 @@ static void do_cciss_request(struct request_queue *q)
BUG_ON(creq->nr_phys_segments > h->maxsgentries);
- if ((c = cmd_alloc(h, 1)) == NULL)
+ c = cmd_alloc(h);
+ if (!c)
goto full;
blk_start_request(creq);
@@ -3180,8 +3244,8 @@ static void do_cciss_request(struct request_queue *q)
/* 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. */
+ cciss_set_tag_index(&c->Header.Tag.lower, c->cmdindex);
+ cciss_mark_tag_indexed(&c->Header.Tag.lower);
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. */
@@ -3192,11 +3256,8 @@ static void do_cciss_request(struct request_queue *q)
c->Request.CDB[0] =
(rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
start_blk = blk_rq_pos(creq);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",
+ dev_dbg(&h->pdev->dev, "sector =%d nr_sectors=%d\n",
(int)blk_rq_pos(creq), (int)blk_rq_sectors(creq));
-#endif /* CCISS_DEBUG */
-
sg_init_table(tmp_sg, h->maxsgentries);
seg = blk_rq_map_sg(q, creq, tmp_sg);
@@ -3236,17 +3297,18 @@ static void do_cciss_request(struct request_queue *q)
if (seg > h->maxSG)
h->maxSG = seg;
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: Submitting %ld sectors in %d segments "
+ dev_dbg(&h->pdev->dev, "Submitting %u sectors in %d segments "
"chained[%d]\n",
blk_rq_sectors(creq), seg, chained);
-#endif /* CCISS_DEBUG */
- c->Header.SGList = c->Header.SGTotal = seg + chained;
- if (seg > h->max_cmd_sgentries)
+ c->Header.SGTotal = seg + chained;
+ if (seg <= h->max_cmd_sgentries)
+ c->Header.SGList = c->Header.SGTotal;
+ else
c->Header.SGList = h->max_cmd_sgentries;
+ set_performant_mode(h, c);
- if (likely(blk_fs_request(creq))) {
+ if (likely(creq->cmd_type == REQ_TYPE_FS)) {
if(h->cciss_read == CCISS_READ_10) {
c->Request.CDB[1] = 0;
c->Request.CDB[2] = (start_blk >> 24) & 0xff; /* MSB */
@@ -3276,11 +3338,12 @@ static void do_cciss_request(struct request_queue *q)
c->Request.CDB[13]= blk_rq_sectors(creq) & 0xff;
c->Request.CDB[14] = c->Request.CDB[15] = 0;
}
- } else if (blk_pc_request(creq)) {
+ } else if (creq->cmd_type == REQ_TYPE_BLOCK_PC) {
c->Request.CDBLen = creq->cmd_len;
memcpy(c->Request.CDB, creq->cmd, BLK_MAX_CDB);
} else {
- printk(KERN_WARNING "cciss%d: bad request type %d\n", h->ctlr, creq->cmd_type);
+ dev_warn(&h->pdev->dev, "bad request type %d\n",
+ creq->cmd_type);
BUG();
}
@@ -3313,72 +3376,131 @@ static inline int interrupt_pending(ctlr_info_t *h)
static inline long interrupt_not_for_us(ctlr_info_t *h)
{
- return (((h->access.intr_pending(h) == 0) ||
- (h->interrupts_enabled == 0)));
+ return ((h->access.intr_pending(h) == 0) ||
+ (h->interrupts_enabled == 0));
}
-static irqreturn_t do_cciss_intr(int irq, void *dev_id)
+static inline int bad_tag(ctlr_info_t *h, u32 tag_index,
+ u32 raw_tag)
{
- ctlr_info_t *h = dev_id;
+ if (unlikely(tag_index >= h->nr_cmds)) {
+ dev_warn(&h->pdev->dev, "bad tag 0x%08x ignored.\n", raw_tag);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void finish_cmd(ctlr_info_t *h, CommandList_struct *c,
+ u32 raw_tag)
+{
+ removeQ(c);
+ if (likely(c->cmd_type == CMD_RWREQ))
+ complete_command(h, c, 0);
+ else if (c->cmd_type == CMD_IOCTL_PEND)
+ complete(c->waiting);
+#ifdef CONFIG_CISS_SCSI_TAPE
+ else if (c->cmd_type == CMD_SCSI)
+ complete_scsi_command(c, 0, raw_tag);
+#endif
+}
+
+static inline u32 next_command(ctlr_info_t *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;
+}
+
+/* process completion of an indexed ("direct lookup") command */
+static inline u32 process_indexed_cmd(ctlr_info_t *h, u32 raw_tag)
+{
+ u32 tag_index;
CommandList_struct *c;
+
+ tag_index = cciss_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(h, c, raw_tag);
+ return next_command(h);
+}
+
+/* process completion of a non-indexed command */
+static inline u32 process_nonindexed_cmd(ctlr_info_t *h, u32 raw_tag)
+{
+ u32 tag;
+ CommandList_struct *c = NULL;
+ struct hlist_node *tmp;
+ __u32 busaddr_masked, tag_masked;
+
+ tag = cciss_tag_discard_error_bits(raw_tag);
+ hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+ busaddr_masked = cciss_tag_discard_error_bits(c->busaddr);
+ tag_masked = cciss_tag_discard_error_bits(tag);
+ if (busaddr_masked == tag_masked) {
+ finish_cmd(h, c, raw_tag);
+ return next_command(h);
+ }
+ }
+ bad_tag(h, h->nr_cmds + 1, raw_tag);
+ return next_command(h);
+}
+
+static irqreturn_t do_cciss_intx(int irq, void *dev_id)
+{
+ ctlr_info_t *h = dev_id;
unsigned long flags;
- __u32 a, a1, a2;
+ u32 raw_tag;
if (interrupt_not_for_us(h))
return IRQ_NONE;
- /*
- * If there are completed commands in the completion queue,
- * we had better do something about it.
- */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
while (interrupt_pending(h)) {
- while ((a = get_next_completion(h)) != FIFO_EMPTY) {
- a1 = a;
- if ((a & 0x04)) {
- a2 = (a >> 3);
- if (a2 >= h->nr_cmds) {
- printk(KERN_WARNING
- "cciss: controller cciss%d failed, stopping.\n",
- h->ctlr);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- fail_all_cmds(h->ctlr);
- return IRQ_HANDLED;
- }
-
- c = h->cmd_pool + a2;
- a = c->busaddr;
-
- } else {
- struct hlist_node *tmp;
-
- a &= ~3;
- c = NULL;
- hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
- if (c->busaddr == a)
- break;
- }
- }
- /*
- * If we've found the command, take it off the
- * completion Q and free it
- */
- if (c && c->busaddr == a) {
- removeQ(c);
- if (c->cmd_type == CMD_RWREQ) {
- complete_command(h, c, 0);
- } else if (c->cmd_type == CMD_IOCTL_PEND) {
- complete(c->waiting);
- }
-# ifdef CONFIG_CISS_SCSI_TAPE
- else if (c->cmd_type == CMD_SCSI)
- complete_scsi_command(c, 0, a1);
-# endif
- continue;
- }
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY) {
+ if (cciss_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;
+}
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+/* Add a second interrupt handler for MSI/MSI-X mode. In this mode we never
+ * check the interrupt pending register because it is not set.
+ */
+static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id)
+{
+ ctlr_info_t *h = dev_id;
+ unsigned long flags;
+ u32 raw_tag;
+
+ spin_lock_irqsave(&h->lock, flags);
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY) {
+ if (cciss_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;
}
@@ -3510,18 +3632,17 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
switch (c->err_info->SenseInfo[12]) {
case STATE_CHANGED:
- printk(KERN_WARNING "cciss%d: a state change "
- "detected, command retried\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "a state change "
+ "detected, command retried\n");
return 1;
break;
case LUN_FAILED:
- printk(KERN_WARNING "cciss%d: LUN failure "
- "detected, action required\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "LUN failure "
+ "detected, action required\n");
return 1;
break;
case REPORT_LUNS_CHANGED:
- printk(KERN_WARNING "cciss%d: report LUN data "
- "changed\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "report LUN data changed\n");
/*
* Here, we could call add_to_scan_list and wake up the scan thread,
* except that it's quite likely that we will get more than one
@@ -3541,19 +3662,18 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
return 1;
break;
case POWER_OR_RESET:
- printk(KERN_WARNING "cciss%d: a power on "
- "or device reset detected\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "a power on or device reset detected\n");
return 1;
break;
case UNIT_ATTENTION_CLEARED:
- printk(KERN_WARNING "cciss%d: unit attention "
- "cleared by another initiator\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "unit attention cleared by another initiator\n");
return 1;
break;
default:
- printk(KERN_WARNING "cciss%d: unknown "
- "unit attention detected\n", h->ctlr);
- return 1;
+ dev_warn(&h->pdev->dev, "unknown unit attention detected\n");
+ return 1;
}
}
@@ -3562,39 +3682,41 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
* the io functions.
* This is for debug only.
*/
-#ifdef CCISS_DEBUG
-static void print_cfg_table(CfgTable_struct *tb)
+static void print_cfg_table(ctlr_info_t *h)
{
int i;
char temp_name[17];
+ CfgTable_struct *tb = h->cfgtable;
- printk("Controller Configuration information\n");
- printk("------------------------------------\n");
+ dev_dbg(&h->pdev->dev, "Controller Configuration information\n");
+ dev_dbg(&h->pdev->dev, "------------------------------------\n");
for (i = 0; i < 4; i++)
temp_name[i] = readb(&(tb->Signature[i]));
temp_name[4] = '\0';
- printk(" Signature = %s\n", temp_name);
- printk(" Spec Number = %d\n", readl(&(tb->SpecValence)));
- printk(" Transport methods supported = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Signature = %s\n", temp_name);
+ dev_dbg(&h->pdev->dev, " Spec Number = %d\n",
+ readl(&(tb->SpecValence)));
+ dev_dbg(&h->pdev->dev, " Transport methods supported = 0x%x\n",
readl(&(tb->TransportSupport)));
- printk(" Transport methods active = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Transport methods active = 0x%x\n",
readl(&(tb->TransportActive)));
- printk(" Requested transport Method = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Requested transport Method = 0x%x\n",
readl(&(tb->HostWrite.TransportRequest)));
- printk(" Coalesce Interrupt Delay = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Coalesce Interrupt Delay = 0x%x\n",
readl(&(tb->HostWrite.CoalIntDelay)));
- printk(" Coalesce Interrupt Count = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Coalesce Interrupt Count = 0x%x\n",
readl(&(tb->HostWrite.CoalIntCount)));
- printk(" Max outstanding commands = 0x%d\n",
+ dev_dbg(&h->pdev->dev, " Max outstanding commands = 0x%d\n",
readl(&(tb->CmdsOutMax)));
- printk(" Bus Types = 0x%x\n", readl(&(tb->BusTypes)));
+ dev_dbg(&h->pdev->dev, " Bus Types = 0x%x\n",
+ readl(&(tb->BusTypes)));
for (i = 0; i < 16; i++)
temp_name[i] = readb(&(tb->ServerName[i]));
temp_name[16] = '\0';
- printk(" Server Name = %s\n", temp_name);
- printk(" Heartbeat Counter = 0x%x\n\n\n", readl(&(tb->HeartBeat)));
+ dev_dbg(&h->pdev->dev, " Server Name = %s\n", temp_name);
+ dev_dbg(&h->pdev->dev, " Heartbeat Counter = 0x%x\n\n\n",
+ readl(&(tb->HeartBeat)));
}
-#endif /* CCISS_DEBUG */
static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
{
@@ -3618,7 +3740,7 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
offset += 8;
break;
default: /* reserved in PCI 2.2 */
- printk(KERN_WARNING
+ dev_warn(&pdev->dev,
"Base address is invalid\n");
return -1;
break;
@@ -3630,12 +3752,182 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
return -1;
}
+/* 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 __devinit cciss_wait_for_mode_change_ack(ctlr_info_t *h)
+{
+ int i;
+
+ /* 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 (i = 0; i < MAX_CONFIG_WAIT; i++) {
+ if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
+ break;
+ msleep(10);
+ }
+}
+
+static __devinit void cciss_enter_performant_mode(ctlr_info_t *h)
+{
+ /* This is a bit complicated. There are 8 registers on
+ * the controller which we write to to tell it 8 different
+ * sizes of commands which there may be. It's a way of
+ * reducing the DMA done to fetch each command. Encoded into
+ * each command's tag are 3 bits which communicate to the controller
+ * which of the eight sizes that command fits within. The size of
+ * each command depends on how many scatter gather entries there are.
+ * Each SG entry requires 16 bytes. The eight registers are programmed
+ * with the number of 16-byte blocks a command of that size requires.
+ * The smallest command possible requires 5 such 16 byte blocks.
+ * the largest command possible requires MAXSGENTRIES + 4 16-byte
+ * blocks. Note, this only extends to the SG entries contained
+ * within the command block, and does not extend to chained blocks
+ * of SG elements. bft[] contains the eight values we write to
+ * the registers. They are not evenly distributed, but have more
+ * sizes for small commands, and fewer sizes for larger commands.
+ */
+ __u32 trans_offset;
+ int bft[8] = { 5, 6, 8, 10, 12, 20, 28, MAXSGENTRIES + 4};
+ /*
+ * 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
+ */
+ unsigned long register_value;
+ BUILD_BUG_ON(28 > MAXSGENTRIES + 4);
+
+ h->reply_pool_wraparound = 1; /* spec: init to 1 */
+
+ /* Controller spec: zero out this buffer. */
+ memset(h->reply_pool, 0, h->max_commands * sizeof(__u64));
+ h->reply_pool_head = h->reply_pool;
+
+ trans_offset = readl(&(h->cfgtable->TransMethodOffset));
+ calc_bucket_map(bft, ARRAY_SIZE(bft), h->maxsgentries,
+ h->blockFetchTable);
+ writel(bft[0], &h->transtable->BlockFetch0);
+ writel(bft[1], &h->transtable->BlockFetch1);
+ writel(bft[2], &h->transtable->BlockFetch2);
+ writel(bft[3], &h->transtable->BlockFetch3);
+ writel(bft[4], &h->transtable->BlockFetch4);
+ writel(bft[5], &h->transtable->BlockFetch5);
+ writel(bft[6], &h->transtable->BlockFetch6);
+ writel(bft[7], &h->transtable->BlockFetch7);
+
+ /* 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);
+ cciss_wait_for_mode_change_ack(h);
+ register_value = readl(&(h->cfgtable->TransportActive));
+ if (!(register_value & CFGTBL_Trans_Performant))
+ dev_warn(&h->pdev->dev, "cciss: unable to get board into"
+ " performant mode\n");
+}
+
+static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h)
+{
+ __u32 trans_support;
+
+ dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n");
+ /* Attempt to put controller into performant mode if supported */
+ /* Does board support performant mode? */
+ trans_support = readl(&(h->cfgtable->TransportSupport));
+ if (!(trans_support & PERFORMANT_MODE))
+ return;
+
+ dev_dbg(&h->pdev->dev, "Placing controller into performant mode\n");
+ /* Performant mode demands commands on a 32 byte boundary
+ * pci_alloc_consistent aligns on page boundarys already.
+ * Just need to check if divisible by 32
+ */
+ if ((sizeof(CommandList_struct) % 32) != 0) {
+ dev_warn(&h->pdev->dev, "%s %d %s\n",
+ "cciss info: command size[",
+ (int)sizeof(CommandList_struct),
+ "] not divisible by 32, no performant mode..\n");
+ return;
+ }
+
+ /* Performant mode ring buffer and supporting data structures */
+ h->reply_pool = (__u64 *)pci_alloc_consistent(
+ h->pdev, h->max_commands * sizeof(__u64),
+ &(h->reply_pool_dhandle));
+
+ /* Need a block fetch table for performant mode */
+ h->blockFetchTable = kmalloc(((h->maxsgentries+1) *
+ sizeof(__u32)), GFP_KERNEL);
+
+ if ((h->reply_pool == NULL) || (h->blockFetchTable == NULL))
+ goto clean_up;
+
+ cciss_enter_performant_mode(h);
+
+ /* Change the access methods to the performant access methods */
+ h->access = SA5_performant_access;
+ h->transMethod = CFGTBL_Trans_Performant;
+
+ return;
+clean_up:
+ kfree(h->blockFetchTable);
+ if (h->reply_pool)
+ pci_free_consistent(h->pdev,
+ h->max_commands * sizeof(__u64),
+ h->reply_pool,
+ h->reply_pool_dhandle);
+ return;
+
+} /* cciss_put_controller_into_performant_mode */
+
/* If MSI/MSI-X is supported by the kernel we will try to enable it on
* controllers that are capable. If not, we use IO-APIC mode.
*/
-static void __devinit cciss_interrupt_mode(ctlr_info_t *c,
- struct pci_dev *pdev, __u32 board_id)
+static void __devinit cciss_interrupt_mode(ctlr_info_t *h)
{
#ifdef CONFIG_PCI_MSI
int err;
@@ -3644,268 +3936,283 @@ static void __devinit cciss_interrupt_mode(ctlr_info_t *c,
};
/* Some boards advertise MSI but don't really support it */
- if ((board_id == 0x40700E11) ||
- (board_id == 0x40800E11) ||
- (board_id == 0x40820E11) || (board_id == 0x40830E11))
+ if ((h->board_id == 0x40700E11) || (h->board_id == 0x40800E11) ||
+ (h->board_id == 0x40820E11) || (h->board_id == 0x40830E11))
goto default_int_mode;
- if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) {
- err = pci_enable_msix(pdev, cciss_msix_entries, 4);
+ if (pci_find_capability(h->pdev, PCI_CAP_ID_MSIX)) {
+ err = pci_enable_msix(h->pdev, cciss_msix_entries, 4);
if (!err) {
- c->intr[0] = cciss_msix_entries[0].vector;
- c->intr[1] = cciss_msix_entries[1].vector;
- c->intr[2] = cciss_msix_entries[2].vector;
- c->intr[3] = cciss_msix_entries[3].vector;
- c->msix_vector = 1;
+ h->intr[0] = cciss_msix_entries[0].vector;
+ h->intr[1] = cciss_msix_entries[1].vector;
+ h->intr[2] = cciss_msix_entries[2].vector;
+ h->intr[3] = cciss_msix_entries[3].vector;
+ h->msix_vector = 1;
return;
}
if (err > 0) {
- printk(KERN_WARNING "cciss: only %d MSI-X vectors "
- "available\n", err);
+ dev_warn(&h->pdev->dev,
+ "only %d MSI-X vectors available\n", err);
goto default_int_mode;
} else {
- printk(KERN_WARNING "cciss: MSI-X init failed %d\n",
- err);
+ dev_warn(&h->pdev->dev,
+ "MSI-X init failed %d\n", err);
goto default_int_mode;
}
}
- if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) {
- if (!pci_enable_msi(pdev)) {
- c->msi_vector = 1;
- } else {
- printk(KERN_WARNING "cciss: MSI init failed\n");
- }
+ if (pci_find_capability(h->pdev, PCI_CAP_ID_MSI)) {
+ if (!pci_enable_msi(h->pdev))
+ h->msi_vector = 1;
+ else
+ dev_warn(&h->pdev->dev, "MSI init failed\n");
}
default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
- c->intr[SIMPLE_MODE_INT] = pdev->irq;
+ h->intr[PERF_MODE_INT] = h->pdev->irq;
return;
}
-static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
+static int __devinit cciss_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
{
- 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;
- int i, prod_index, err;
+ int i;
+ u32 subsystem_vendor_id, subsystem_device_id;
subsystem_vendor_id = pdev->subsystem_vendor;
subsystem_device_id = pdev->subsystem_device;
- board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) |
- subsystem_vendor_id);
+ *board_id = ((subsystem_device_id << 16) & 0xffff0000) |
+ subsystem_vendor_id;
for (i = 0; i < ARRAY_SIZE(products); i++) {
/* Stand aside for hpsa driver on request */
if (cciss_allow_hpsa && products[i].board_id == HPSA_BOUNDARY)
return -ENODEV;
- if (board_id == products[i].board_id)
- break;
- }
- prod_index = i;
- if (prod_index == ARRAY_SIZE(products)) {
- dev_warn(&pdev->dev,
- "unrecognized board ID: 0x%08lx, ignoring.\n",
- (unsigned long) board_id);
- return -ENODEV;
+ if (*board_id == products[i].board_id)
+ return i;
}
+ dev_warn(&pdev->dev, "unrecognized board ID: 0x%08x, ignoring.\n",
+ *board_id);
+ return -ENODEV;
+}
- /* check to see if controller has been disabled */
- /* BEFORE trying to enable it */
- (void)pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & 0x02)) {
- printk(KERN_WARNING
- "cciss: controller appears to be disabled\n");
- return -ENODEV;
- }
+static inline bool cciss_board_disabled(ctlr_info_t *h)
+{
+ u16 command;
- err = pci_enable_device(pdev);
- if (err) {
- printk(KERN_ERR "cciss: Unable to Enable PCI device\n");
- return err;
- }
+ (void) pci_read_config_word(h->pdev, PCI_COMMAND, &command);
+ return ((command & PCI_COMMAND_MEMORY) == 0);
+}
- err = pci_request_regions(pdev, "cciss");
- if (err) {
- printk(KERN_ERR "cciss: Cannot obtain PCI resources, "
- "aborting\n");
- return err;
- }
+static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev,
+ unsigned long *memory_bar)
+{
+ int i;
-#ifdef CCISS_DEBUG
- printk("command = %x\n", command);
- printk("irq = %x\n", pdev->irq);
- printk("board_id = %x\n", board_id);
-#endif /* CCISS_DEBUG */
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ /* addressing mode bits already removed */
+ *memory_bar = pci_resource_start(pdev, i);
+ dev_dbg(&pdev->dev, "memory BAR = %lx\n",
+ *memory_bar);
+ return 0;
+ }
+ dev_warn(&pdev->dev, "no memory BAR found\n");
+ return -ENODEV;
+}
-/* If the kernel supports MSI/MSI-X we will try to enable that functionality,
- * else we use the IO-APIC interrupt assigned to us by system ROM.
- */
- cciss_interrupt_mode(c, pdev, board_id);
+static int __devinit cciss_wait_for_board_ready(ctlr_info_t *h)
+{
+ int i;
+ u32 scratchpad;
- /* find the memory BAR */
- for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
- if (pci_resource_flags(pdev, i) & IORESOURCE_MEM)
- break;
- }
- if (i == DEVICE_COUNT_RESOURCE) {
- printk(KERN_WARNING "cciss: No memory BAR found\n");
- err = -ENODEV;
- goto err_out_free_res;
+ for (i = 0; i < CCISS_BOARD_READY_ITERATIONS; i++) {
+ scratchpad = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
+ if (scratchpad == CCISS_FIRMWARE_READY)
+ return 0;
+ msleep(CCISS_BOARD_READY_POLL_INTERVAL_MSECS);
}
+ dev_warn(&h->pdev->dev, "board not ready, timed out.\n");
+ return -ENODEV;
+}
- c->paddr = pci_resource_start(pdev, i); /* addressing mode bits
- * already removed
- */
+static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev,
+ void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+ u64 *cfg_offset)
+{
+ *cfg_base_addr = readl(vaddr + SA5_CTCFG_OFFSET);
+ *cfg_offset = readl(vaddr + SA5_CTMEM_OFFSET);
+ *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, "
+ "*cfg_base_addr = 0x%08x\n", *cfg_base_addr);
+ return -ENODEV;
+ }
+ return 0;
+}
-#ifdef CCISS_DEBUG
- printk("address 0 = %lx\n", c->paddr);
-#endif /* CCISS_DEBUG */
- c->vaddr = remap_pci_mem(c->paddr, 0x250);
+static int __devinit cciss_find_cfgtables(ctlr_info_t *h)
+{
+ u64 cfg_offset;
+ u32 cfg_base_addr;
+ u64 cfg_base_addr_index;
+ u32 trans_offset;
+ int rc;
- /* Wait for the board to become ready. (PCI hotplug needs this.)
- * We poll for up to 120 secs, once per 100ms. */
- for (i = 0; i < 1200; i++) {
- scratchpad = readl(c->vaddr + SA5_SCRATCHPAD_OFFSET);
- if (scratchpad == CCISS_FIRMWARE_READY)
- break;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(100)); /* wait 100ms */
- }
- if (scratchpad != CCISS_FIRMWARE_READY) {
- printk(KERN_WARNING "cciss: Board not ready. Timed out.\n");
- err = -ENODEV;
- goto err_out_free_res;
- }
+ rc = cciss_find_cfg_addrs(h->pdev, h->vaddr, &cfg_base_addr,
+ &cfg_base_addr_index, &cfg_offset);
+ if (rc)
+ return rc;
+ h->cfgtable = remap_pci_mem(pci_resource_start(h->pdev,
+ cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable));
+ if (!h->cfgtable)
+ return -ENOMEM;
+ /* Find performant mode table. */
+ trans_offset = readl(&h->cfgtable->TransMethodOffset);
+ h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
+ cfg_base_addr_index)+cfg_offset+trans_offset,
+ sizeof(*h->transtable));
+ if (!h->transtable)
+ return -ENOMEM;
+ return 0;
+}
- /* get the address index number */
- cfg_base_addr = readl(c->vaddr + SA5_CTCFG_OFFSET);
- cfg_base_addr &= (__u32) 0x0000ffff;
-#ifdef CCISS_DEBUG
- printk("cfg base address = %x\n", cfg_base_addr);
-#endif /* CCISS_DEBUG */
- cfg_base_addr_index = find_PCI_BAR_index(pdev, cfg_base_addr);
-#ifdef CCISS_DEBUG
- printk("cfg base address index = %llx\n",
- (unsigned long long)cfg_base_addr_index);
-#endif /* CCISS_DEBUG */
- if (cfg_base_addr_index == -1) {
- printk(KERN_WARNING "cciss: Cannot find cfg_base_addr_index\n");
- err = -ENODEV;
- goto err_out_free_res;
+static void __devinit cciss_get_max_perf_mode_cmds(struct ctlr_info *h)
+{
+ h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+ if (h->max_commands < 16) {
+ dev_warn(&h->pdev->dev, "Controller reports "
+ "max supported commands of %d, an obvious lie. "
+ "Using 16. Ensure that firmware is up to date.\n",
+ h->max_commands);
+ h->max_commands = 16;
}
+}
- cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET);
-#ifdef CCISS_DEBUG
- printk("cfg offset = %llx\n", (unsigned long long)cfg_offset);
-#endif /* CCISS_DEBUG */
- c->cfgtable = remap_pci_mem(pci_resource_start(pdev,
- cfg_base_addr_index) +
- cfg_offset, sizeof(CfgTable_struct));
- c->board_id = board_id;
-
-#ifdef CCISS_DEBUG
- print_cfg_table(c->cfgtable);
-#endif /* CCISS_DEBUG */
-
- /* Some controllers support Zero Memory Raid (ZMR).
- * When configured in ZMR mode the number of supported
- * commands drops to 64. So instead of just setting an
- * arbitrary value we make the driver a little smarter.
- * We read the config table to tell us how many commands
- * are supported on the controller then subtract 4 to
- * leave a little room for ioctl calls.
- */
- c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
- c->maxsgentries = readl(&(c->cfgtable->MaxSGElements));
-
+/* Interrogate the hardware for some limits:
+ * max commands, max SG elements without chaining, and with chaining,
+ * SG chain block size, etc.
+ */
+static void __devinit cciss_find_board_params(ctlr_info_t *h)
+{
+ cciss_get_max_perf_mode_cmds(h);
+ h->nr_cmds = h->max_commands - 4; /* Allow room for some ioctls */
+ h->maxsgentries = readl(&(h->cfgtable->MaxSGElements));
/*
- * Limit native command to 32 s/g elements to save dma'able memory.
+ * Limit in-command s/g elements to 32 save dma'able memory.
* Howvever spec says if 0, use 31
*/
-
- c->max_cmd_sgentries = 31;
- if (c->maxsgentries > 512) {
- c->max_cmd_sgentries = 32;
- c->chainsize = c->maxsgentries - c->max_cmd_sgentries + 1;
- c->maxsgentries -= 1; /* account for chain pointer */
+ h->max_cmd_sgentries = 31;
+ if (h->maxsgentries > 512) {
+ h->max_cmd_sgentries = 32;
+ h->chainsize = h->maxsgentries - h->max_cmd_sgentries + 1;
+ h->maxsgentries--; /* save one for chain pointer */
} else {
- c->maxsgentries = 31; /* Default to traditional value */
- c->chainsize = 0; /* traditional */
+ h->maxsgentries = 31; /* default to traditional values */
+ h->chainsize = 0;
}
+}
- c->product_name = products[prod_index].product_name;
- c->access = *(products[prod_index].access);
- c->nr_cmds = c->max_commands - 4;
- if ((readb(&c->cfgtable->Signature[0]) != 'C') ||
- (readb(&c->cfgtable->Signature[1]) != 'I') ||
- (readb(&c->cfgtable->Signature[2]) != 'S') ||
- (readb(&c->cfgtable->Signature[3]) != 'S')) {
- printk("Does not appear to be a valid CISS config table\n");
- err = -ENODEV;
- goto err_out_free_res;
+static inline bool CISS_signature_present(ctlr_info_t *h)
+{
+ if ((readb(&h->cfgtable->Signature[0]) != 'C') ||
+ (readb(&h->cfgtable->Signature[1]) != 'I') ||
+ (readb(&h->cfgtable->Signature[2]) != 'S') ||
+ (readb(&h->cfgtable->Signature[3]) != 'S')) {
+ dev_warn(&h->pdev->dev, "not a valid CISS config table\n");
+ return false;
}
+ return true;
+}
+
+/* Need to enable prefetch in the SCSI core for 6400 in x86 */
+static inline void cciss_enable_scsi_prefetch(ctlr_info_t *h)
+{
#ifdef CONFIG_X86
- {
- /* Need to enable prefetch in the SCSI core for 6400 in x86 */
- __u32 prefetch;
- prefetch = readl(&(c->cfgtable->SCSI_Prefetch));
- prefetch |= 0x100;
- writel(prefetch, &(c->cfgtable->SCSI_Prefetch));
- }
+ u32 prefetch;
+
+ prefetch = readl(&(h->cfgtable->SCSI_Prefetch));
+ prefetch |= 0x100;
+ writel(prefetch, &(h->cfgtable->SCSI_Prefetch));
#endif
+}
- /* Disabling DMA prefetch and refetch for the P600.
- * An ASIC bug may result in accesses to invalid memory addresses.
- * We've disabled prefetch for some time now. Testing with XEN
- * kernels revealed a bug in the refetch if dom0 resides on a P600.
- */
- if(board_id == 0x3225103C) {
- __u32 dma_prefetch;
- __u32 dma_refetch;
- dma_prefetch = readl(c->vaddr + I2O_DMA1_CFG);
- dma_prefetch |= 0x8000;
- writel(dma_prefetch, c->vaddr + I2O_DMA1_CFG);
- pci_read_config_dword(pdev, PCI_COMMAND_PARITY, &dma_refetch);
- dma_refetch |= 0x1;
- pci_write_config_dword(pdev, PCI_COMMAND_PARITY, dma_refetch);
+/* Disable DMA prefetch for the P600. Otherwise an ASIC bug may result
+ * in a prefetch beyond physical memory.
+ */
+static inline void cciss_p600_dma_prefetch_quirk(ctlr_info_t *h)
+{
+ u32 dma_prefetch;
+ __u32 dma_refetch;
+
+ if (h->board_id != 0x3225103C)
+ return;
+ dma_prefetch = readl(h->vaddr + I2O_DMA1_CFG);
+ dma_prefetch |= 0x8000;
+ writel(dma_prefetch, h->vaddr + I2O_DMA1_CFG);
+ pci_read_config_dword(h->pdev, PCI_COMMAND_PARITY, &dma_refetch);
+ dma_refetch |= 0x1;
+ pci_write_config_dword(h->pdev, PCI_COMMAND_PARITY, dma_refetch);
+}
+
+static int __devinit cciss_pci_init(ctlr_info_t *h)
+{
+ int prod_index, err;
+
+ prod_index = cciss_lookup_board_id(h->pdev, &h->board_id);
+ if (prod_index < 0)
+ return -ENODEV;
+ h->product_name = products[prod_index].product_name;
+ h->access = *(products[prod_index].access);
+
+ if (cciss_board_disabled(h)) {
+ dev_warn(&h->pdev->dev, "controller appears to be disabled\n");
+ return -ENODEV;
+ }
+ err = pci_enable_device(h->pdev);
+ if (err) {
+ dev_warn(&h->pdev->dev, "Unable to Enable PCI device\n");
+ return err;
}
-#ifdef CCISS_DEBUG
- printk("Trying to put board into Simple mode\n");
-#endif /* CCISS_DEBUG */
- c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
- /* Update the field, and then ring the doorbell */
- writel(CFGTBL_Trans_Simple, &(c->cfgtable->HostWrite.TransportRequest));
- writel(CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL);
+ err = pci_request_regions(h->pdev, "cciss");
+ if (err) {
+ dev_warn(&h->pdev->dev,
+ "Cannot obtain PCI resources, aborting\n");
+ return err;
+ }
- /* 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 (i = 0; i < MAX_CONFIG_WAIT; i++) {
- if (!(readl(c->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
- break;
- /* delay and try again */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(1));
+ dev_dbg(&h->pdev->dev, "irq = %x\n", h->pdev->irq);
+ dev_dbg(&h->pdev->dev, "board_id = %x\n", h->board_id);
+
+/* If the kernel supports MSI/MSI-X we will try to enable that functionality,
+ * else we use the IO-APIC interrupt assigned to us by system ROM.
+ */
+ cciss_interrupt_mode(h);
+ err = cciss_pci_find_memory_BAR(h->pdev, &h->paddr);
+ if (err)
+ goto err_out_free_res;
+ h->vaddr = remap_pci_mem(h->paddr, 0x250);
+ if (!h->vaddr) {
+ err = -ENOMEM;
+ goto err_out_free_res;
}
+ err = cciss_wait_for_board_ready(h);
+ if (err)
+ goto err_out_free_res;
+ err = cciss_find_cfgtables(h);
+ if (err)
+ goto err_out_free_res;
+ print_cfg_table(h);
+ cciss_find_board_params(h);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "I counter got to %d %x\n", i,
- readl(c->vaddr + SA5_DOORBELL));
-#endif /* CCISS_DEBUG */
-#ifdef CCISS_DEBUG
- print_cfg_table(c->cfgtable);
-#endif /* CCISS_DEBUG */
-
- if (!(readl(&(c->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) {
- printk(KERN_WARNING "cciss: unable to get board into"
- " simple mode\n");
+ if (!CISS_signature_present(h)) {
err = -ENODEV;
goto err_out_free_res;
}
+ cciss_enable_scsi_prefetch(h);
+ cciss_p600_dma_prefetch_quirk(h);
+ cciss_put_controller_into_performant_mode(h);
return 0;
err_out_free_res:
@@ -3913,42 +4220,47 @@ err_out_free_res:
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
- pci_release_regions(pdev);
+ if (h->transtable)
+ iounmap(h->transtable);
+ if (h->cfgtable)
+ iounmap(h->cfgtable);
+ if (h->vaddr)
+ iounmap(h->vaddr);
+ pci_release_regions(h->pdev);
return err;
}
/* Function to find the first free pointer into our hba[] array
* Returns -1 if no free entries are left.
*/
-static int alloc_cciss_hba(void)
+static int alloc_cciss_hba(struct pci_dev *pdev)
{
int i;
for (i = 0; i < MAX_CTLR; i++) {
if (!hba[i]) {
- ctlr_info_t *p;
+ ctlr_info_t *h;
- p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
- if (!p)
+ h = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+ if (!h)
goto Enomem;
- hba[i] = p;
+ hba[i] = h;
return i;
}
}
- printk(KERN_WARNING "cciss: This driver supports a maximum"
+ dev_warn(&pdev->dev, "This driver supports a maximum"
" of %d controllers.\n", MAX_CTLR);
return -1;
Enomem:
- printk(KERN_ERR "cciss: out of memory.\n");
+ dev_warn(&pdev->dev, "out of memory.\n");
return -1;
}
-static void free_hba(int n)
+static void free_hba(ctlr_info_t *h)
{
- ctlr_info_t *h = hba[n];
int i;
- hba[n] = NULL;
+ hba[h->ctlr] = NULL;
for (i = 0; i < h->highest_lun + 1; i++)
if (h->gendisk[i] != NULL)
put_disk(h->gendisk[i]);
@@ -4028,7 +4340,8 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u
/* we leak the DMA buffer here ... no choice since the controller could
still complete the command. */
if (i == 10) {
- printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n",
+ dev_err(&pdev->dev,
+ "controller message %02x:%02x timed out\n",
opcode, type);
return -ETIMEDOUT;
}
@@ -4036,12 +4349,12 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u
pci_free_consistent(pdev, cmd_sz, cmd, paddr64);
if (tag & 2) {
- printk(KERN_ERR "cciss: controller message %02x:%02x failed\n",
+ dev_err(&pdev->dev, "controller message %02x:%02x failed\n",
opcode, type);
return -EIO;
}
- printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n",
+ dev_info(&pdev->dev, "controller message %02x:%02x succeeded\n",
opcode, type);
return 0;
}
@@ -4062,7 +4375,7 @@ static __devinit int cciss_reset_msi(struct pci_dev *pdev)
if (pos) {
pci_read_config_word(pdev, msi_control_reg(pos), &control);
if (control & PCI_MSI_FLAGS_ENABLE) {
- printk(KERN_INFO "cciss: resetting MSI\n");
+ dev_info(&pdev->dev, "resetting MSI\n");
pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE);
}
}
@@ -4071,7 +4384,7 @@ static __devinit int cciss_reset_msi(struct pci_dev *pdev)
if (pos) {
pci_read_config_word(pdev, msi_control_reg(pos), &control);
if (control & PCI_MSIX_FLAGS_ENABLE) {
- printk(KERN_INFO "cciss: resetting MSI-X\n");
+ dev_info(&pdev->dev, "resetting MSI-X\n");
pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE);
}
}
@@ -4079,68 +4392,144 @@ static __devinit int cciss_reset_msi(struct pci_dev *pdev)
return 0;
}
-/* This does a hard reset of the controller using PCI power management
- * states. */
-static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev)
+static int cciss_controller_hard_reset(struct pci_dev *pdev,
+ void * __iomem vaddr, bool use_doorbell)
{
- u16 pmcsr, saved_config_space[32];
- int i, pos;
+ u16 pmcsr;
+ int pos;
- printk(KERN_INFO "cciss: using PCI PM to reset controller\n");
+ if (use_doorbell) {
+ /* For everything after the P600, the PCI power state method
+ * of resetting the controller doesn't work, so we have this
+ * other way using the doorbell register.
+ */
+ dev_info(&pdev->dev, "using doorbell to reset controller\n");
+ writel(DOORBELL_CTLR_RESET, vaddr + SA5_DOORBELL);
+ msleep(1000);
+ } else { /* Try to do it the PCI power state way */
+
+ /* Quoting from the Open CISS Specification: "The Power
+ * Management Control/Status Register (CSR) controls the power
+ * state of the device. The normal operating state is D0,
+ * CSR=00h. The software off state is D3, CSR=03h. To reset
+ * the controller, place the interface device in D3 then to D0,
+ * this causes a secondary PCI reset which will reset the
+ * controller." */
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pos == 0) {
+ dev_err(&pdev->dev,
+ "cciss_controller_hard_reset: "
+ "PCI PM not supported\n");
+ return -ENODEV;
+ }
+ dev_info(&pdev->dev, "using PCI PM to reset controller\n");
+ /* enter the D3hot power management state */
+ pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ pmcsr |= PCI_D3hot;
+ pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
- /* This is very nearly the same thing as
+ msleep(500);
- pci_save_state(pci_dev);
- pci_set_power_state(pci_dev, PCI_D3hot);
- pci_set_power_state(pci_dev, PCI_D0);
- pci_restore_state(pci_dev);
+ /* enter the D0 power management state */
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ pmcsr |= PCI_D0;
+ pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
- but we can't use these nice canned kernel routines on
- kexec, because they also check the MSI/MSI-X state in PCI
- configuration space and do the wrong thing when it is
- set/cleared. Also, the pci_save/restore_state functions
- violate the ordering requirements for restoring the
- configuration space from the CCISS document (see the
- comment below). So we roll our own .... */
+ msleep(500);
+ }
+ return 0;
+}
- for (i = 0; i < 32; i++)
- pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
+/* This does a hard reset of the controller using PCI power management
+ * states or using the doorbell register. */
+static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
+{
+ u16 saved_config_space[32];
+ u64 cfg_offset;
+ u32 cfg_base_addr;
+ u64 cfg_base_addr_index;
+ void __iomem *vaddr;
+ unsigned long paddr;
+ u32 misc_fw_support, active_transport;
+ int rc, i;
+ CfgTable_struct __iomem *cfgtable;
+ bool use_doorbell;
+ u32 board_id;
+
+ /* For controllers as old a the p600, this is very nearly
+ * the same thing as
+ *
+ * pci_save_state(pci_dev);
+ * pci_set_power_state(pci_dev, PCI_D3hot);
+ * pci_set_power_state(pci_dev, PCI_D0);
+ * pci_restore_state(pci_dev);
+ *
+ * but we can't use these nice canned kernel routines on
+ * kexec, because they also check the MSI/MSI-X state in PCI
+ * configuration space and do the wrong thing when it is
+ * set/cleared. Also, the pci_save/restore_state functions
+ * violate the ordering requirements for restoring the
+ * configuration space from the CCISS document (see the
+ * comment below). So we roll our own ....
+ *
+ * For controllers newer than the P600, the pci power state
+ * method of resetting doesn't work so we have another way
+ * using the doorbell register.
+ */
- pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pos == 0) {
- printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n");
+ /* Exclude 640x boards. These are two pci devices in one slot
+ * which share a battery backed cache module. One controls the
+ * cache, the other accesses the cache through the one that controls
+ * it. If we reset the one controlling the cache, the other will
+ * likely not be happy. Just forbid resetting this conjoined mess.
+ */
+ cciss_lookup_board_id(pdev, &board_id);
+ if (board_id == 0x409C0E11 || board_id == 0x409D0E11) {
+ dev_warn(&pdev->dev, "Cannot reset Smart Array 640x "
+ "due to shared cache module.");
return -ENODEV;
}
- /* Quoting from the Open CISS Specification: "The Power
- * Management Control/Status Register (CSR) controls the power
- * state of the device. The normal operating state is D0,
- * CSR=00h. The software off state is D3, CSR=03h. To reset
- * the controller, place the interface device in D3 then to
- * D0, this causes a secondary PCI reset which will reset the
- * controller." */
+ for (i = 0; i < 32; i++)
+ pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
- /* enter the D3hot power management state */
- pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= PCI_D3hot;
- pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+ /* find the first memory BAR, so we can find the cfg table */
+ rc = cciss_pci_find_memory_BAR(pdev, &paddr);
+ if (rc)
+ return rc;
+ vaddr = remap_pci_mem(paddr, 0x250);
+ if (!vaddr)
+ return -ENOMEM;
- schedule_timeout_uninterruptible(HZ >> 1);
+ /* find cfgtable in order to check if reset via doorbell is supported */
+ rc = cciss_find_cfg_addrs(pdev, vaddr, &cfg_base_addr,
+ &cfg_base_addr_index, &cfg_offset);
+ if (rc)
+ goto unmap_vaddr;
+ cfgtable = remap_pci_mem(pci_resource_start(pdev,
+ cfg_base_addr_index) + cfg_offset, sizeof(*cfgtable));
+ if (!cfgtable) {
+ rc = -ENOMEM;
+ goto unmap_vaddr;
+ }
- /* enter the D0 power management state */
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= PCI_D0;
- pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+ /* If reset via doorbell register is supported, use that. */
+ misc_fw_support = readl(&cfgtable->misc_fw_support);
+ use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
- schedule_timeout_uninterruptible(HZ >> 1);
+ rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell);
+ if (rc)
+ goto unmap_cfgtable;
/* Restore the PCI configuration space. The Open CISS
* Specification says, "Restore the PCI Configuration
* Registers, offsets 00h through 60h. It is important to
* restore the command register, 16-bits at offset 04h,
* last. Do not restore the configuration status register,
- * 16-bits at offset 06h." Note that the offset is 2*i. */
+ * 16-bits at offset 06h." Note that the offset is 2*i.
+ */
for (i = 0; i < 32; i++) {
if (i == 2 || i == 3)
continue;
@@ -4149,6 +4538,63 @@ static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev)
wmb();
pci_write_config_word(pdev, 4, saved_config_space[2]);
+ /* Some devices (notably the HP Smart Array 5i Controller)
+ need a little pause here */
+ msleep(CCISS_POST_RESET_PAUSE_MSECS);
+
+ /* Controller should be in simple mode at this point. If it's not,
+ * It means we're on one of those controllers which doesn't support
+ * the doorbell reset method and on which the PCI power management reset
+ * method doesn't work (P800, for example.)
+ * In those cases, don't try to proceed, as it generally doesn't work.
+ */
+ active_transport = readl(&cfgtable->TransportActive);
+ if (active_transport & PERFORMANT_MODE) {
+ dev_warn(&pdev->dev, "Unable to successfully reset controller,"
+ " Ignoring controller.\n");
+ rc = -ENODEV;
+ }
+
+unmap_cfgtable:
+ iounmap(cfgtable);
+
+unmap_vaddr:
+ iounmap(vaddr);
+ return rc;
+}
+
+static __devinit int cciss_init_reset_devices(struct pci_dev *pdev)
+{
+ int rc, i;
+
+ if (!reset_devices)
+ return 0;
+
+ /* Reset the controller with a PCI power-cycle or via doorbell */
+ rc = cciss_kdump_hard_reset_controller(pdev);
+
+ /* -ENOTSUPP here means we cannot reset the controller
+ * but it's already (and still) up and running in
+ * "performant mode". Or, it might be 640x, which can't reset
+ * due to concerns about shared bbwc between 6402/6404 pair.
+ */
+ if (rc == -ENOTSUPP)
+ return 0; /* just try to do the kdump anyhow. */
+ if (rc)
+ return -ENODEV;
+ if (cciss_reset_msi(pdev))
+ return -ENODEV;
+
+ /* Now try to get the controller to respond to a no-op */
+ for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) {
+ if (cciss_noop(pdev) == 0)
+ break;
+ else
+ dev_warn(&pdev->dev, "no-op failed%s\n",
+ (i < CCISS_POST_RESET_NOOP_RETRIES - 1 ?
+ "; re-trying" : ""));
+ msleep(CCISS_POST_RESET_NOOP_INTERVAL_MSECS);
+ }
return 0;
}
@@ -4166,46 +4612,31 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
int rc;
int dac, return_code;
InquiryData_struct *inq_buff;
+ ctlr_info_t *h;
- if (reset_devices) {
- /* Reset the controller with a PCI power-cycle */
- if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev))
- return -ENODEV;
-
- /* Now try to get the controller to respond to a no-op. Some
- devices (notably the HP Smart Array 5i Controller) need
- up to 30 seconds to respond. */
- for (i=0; i<30; i++) {
- if (cciss_noop(pdev) == 0)
- break;
-
- schedule_timeout_uninterruptible(HZ);
- }
- if (i == 30) {
- printk(KERN_ERR "cciss: controller seems dead\n");
- return -EBUSY;
- }
- }
-
- i = alloc_cciss_hba();
+ rc = cciss_init_reset_devices(pdev);
+ if (rc)
+ return rc;
+ i = alloc_cciss_hba(pdev);
if (i < 0)
return -1;
- hba[i]->busy_initializing = 1;
- INIT_HLIST_HEAD(&hba[i]->cmpQ);
- INIT_HLIST_HEAD(&hba[i]->reqQ);
- mutex_init(&hba[i]->busy_shutting_down);
+ h = hba[i];
+ h->pdev = pdev;
+ h->busy_initializing = 1;
+ INIT_HLIST_HEAD(&h->cmpQ);
+ INIT_HLIST_HEAD(&h->reqQ);
+ mutex_init(&h->busy_shutting_down);
- if (cciss_pci_init(hba[i], pdev) != 0)
+ if (cciss_pci_init(h) != 0)
goto clean_no_release_regions;
- sprintf(hba[i]->devname, "cciss%d", i);
- hba[i]->ctlr = i;
- hba[i]->pdev = pdev;
+ sprintf(h->devname, "cciss%d", i);
+ h->ctlr = i;
- init_completion(&hba[i]->scan_wait);
+ init_completion(&h->scan_wait);
- if (cciss_create_hba_sysfs_entry(hba[i]))
+ if (cciss_create_hba_sysfs_entry(h))
goto clean0;
/* configure PCI DMA stuff */
@@ -4214,7 +4645,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))
dac = 0;
else {
- printk(KERN_ERR "cciss: no suitable DMA available\n");
+ dev_err(&h->pdev->dev, "no suitable DMA available\n");
goto clean1;
}
@@ -4224,151 +4655,161 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
* 8 controller support.
*/
if (i < MAX_CTLR_ORIG)
- hba[i]->major = COMPAQ_CISS_MAJOR + i;
- rc = register_blkdev(hba[i]->major, hba[i]->devname);
+ h->major = COMPAQ_CISS_MAJOR + i;
+ rc = register_blkdev(h->major, h->devname);
if (rc == -EBUSY || rc == -EINVAL) {
- printk(KERN_ERR
- "cciss: Unable to get major number %d for %s "
- "on hba %d\n", hba[i]->major, hba[i]->devname, i);
+ dev_err(&h->pdev->dev,
+ "Unable to get major number %d for %s "
+ "on hba %d\n", h->major, h->devname, i);
goto clean1;
} else {
if (i >= MAX_CTLR_ORIG)
- hba[i]->major = rc;
+ h->major = rc;
}
/* make sure the board interrupts are off */
- hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
- if (request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr,
- IRQF_DISABLED | IRQF_SHARED, hba[i]->devname, hba[i])) {
- printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
- hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
- goto clean2;
+ h->access.set_intr_mask(h, CCISS_INTR_OFF);
+ if (h->msi_vector || h->msix_vector) {
+ if (request_irq(h->intr[PERF_MODE_INT],
+ do_cciss_msix_intr,
+ IRQF_DISABLED, h->devname, h)) {
+ dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
+ h->intr[PERF_MODE_INT], h->devname);
+ goto clean2;
+ }
+ } else {
+ if (request_irq(h->intr[PERF_MODE_INT], do_cciss_intx,
+ IRQF_DISABLED, h->devname, h)) {
+ dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
+ h->intr[PERF_MODE_INT], h->devname);
+ goto clean2;
+ }
}
- printk(KERN_INFO "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
- hba[i]->devname, pdev->device, pci_name(pdev),
- hba[i]->intr[SIMPLE_MODE_INT], dac ? "" : " not");
+ dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
+ h->devname, pdev->device, pci_name(pdev),
+ h->intr[PERF_MODE_INT], dac ? "" : " not");
- hba[i]->cmd_pool_bits =
- kmalloc(DIV_ROUND_UP(hba[i]->nr_cmds, BITS_PER_LONG)
+ h->cmd_pool_bits =
+ kmalloc(DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG)
* sizeof(unsigned long), GFP_KERNEL);
- hba[i]->cmd_pool = (CommandList_struct *)
- pci_alloc_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(CommandList_struct),
- &(hba[i]->cmd_pool_dhandle));
- hba[i]->errinfo_pool = (ErrorInfo_struct *)
- pci_alloc_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
- &(hba[i]->errinfo_pool_dhandle));
- if ((hba[i]->cmd_pool_bits == NULL)
- || (hba[i]->cmd_pool == NULL)
- || (hba[i]->errinfo_pool == NULL)) {
- printk(KERN_ERR "cciss: out of memory");
+ h->cmd_pool = (CommandList_struct *)
+ pci_alloc_consistent(h->pdev,
+ h->nr_cmds * sizeof(CommandList_struct),
+ &(h->cmd_pool_dhandle));
+ h->errinfo_pool = (ErrorInfo_struct *)
+ pci_alloc_consistent(h->pdev,
+ h->nr_cmds * sizeof(ErrorInfo_struct),
+ &(h->errinfo_pool_dhandle));
+ if ((h->cmd_pool_bits == NULL)
+ || (h->cmd_pool == NULL)
+ || (h->errinfo_pool == NULL)) {
+ dev_err(&h->pdev->dev, "out of memory");
goto clean4;
}
/* Need space for temp scatter list */
- hba[i]->scatter_list = kmalloc(hba[i]->max_commands *
+ h->scatter_list = kmalloc(h->max_commands *
sizeof(struct scatterlist *),
GFP_KERNEL);
- for (k = 0; k < hba[i]->nr_cmds; k++) {
- hba[i]->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
- hba[i]->maxsgentries,
+ for (k = 0; k < h->nr_cmds; k++) {
+ h->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
+ h->maxsgentries,
GFP_KERNEL);
- if (hba[i]->scatter_list[k] == NULL) {
- printk(KERN_ERR "cciss%d: could not allocate "
- "s/g lists\n", i);
+ if (h->scatter_list[k] == NULL) {
+ dev_err(&h->pdev->dev,
+ "could not allocate s/g lists\n");
goto clean4;
}
}
- 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)
+ h->cmd_sg_list = cciss_allocate_sg_chain_blocks(h,
+ h->chainsize, h->nr_cmds);
+ if (!h->cmd_sg_list && h->chainsize > 0)
goto clean4;
- spin_lock_init(&hba[i]->lock);
+ spin_lock_init(&h->lock);
/* Initialize the pdev driver private data.
- have it point to hba[i]. */
- pci_set_drvdata(pdev, hba[i]);
+ have it point to h. */
+ pci_set_drvdata(pdev, h);
/* command and error info recs zeroed out before
they are used */
- memset(hba[i]->cmd_pool_bits, 0,
- DIV_ROUND_UP(hba[i]->nr_cmds, BITS_PER_LONG)
+ memset(h->cmd_pool_bits, 0,
+ DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG)
* sizeof(unsigned long));
- hba[i]->num_luns = 0;
- hba[i]->highest_lun = -1;
+ h->num_luns = 0;
+ h->highest_lun = -1;
for (j = 0; j < CISS_MAX_LUN; j++) {
- hba[i]->drv[j] = NULL;
- hba[i]->gendisk[j] = NULL;
+ h->drv[j] = NULL;
+ h->gendisk[j] = NULL;
}
- cciss_scsi_setup(i);
+ cciss_scsi_setup(h);
/* Turn the interrupts on so we can service requests */
- hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
+ h->access.set_intr_mask(h, CCISS_INTR_ON);
/* Get the firmware version */
inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL) {
- printk(KERN_ERR "cciss: out of memory\n");
+ dev_err(&h->pdev->dev, "out of memory\n");
goto clean4;
}
- return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff,
+ return_code = sendcmd_withirq(h, CISS_INQUIRY, inq_buff,
sizeof(InquiryData_struct), 0, CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK) {
- hba[i]->firm_ver[0] = inq_buff->data_byte[32];
- hba[i]->firm_ver[1] = inq_buff->data_byte[33];
- hba[i]->firm_ver[2] = inq_buff->data_byte[34];
- hba[i]->firm_ver[3] = inq_buff->data_byte[35];
+ h->firm_ver[0] = inq_buff->data_byte[32];
+ h->firm_ver[1] = inq_buff->data_byte[33];
+ h->firm_ver[2] = inq_buff->data_byte[34];
+ h->firm_ver[3] = inq_buff->data_byte[35];
} else { /* send command failed */
- printk(KERN_WARNING "cciss: unable to determine firmware"
+ dev_warn(&h->pdev->dev, "unable to determine firmware"
" version of controller\n");
}
kfree(inq_buff);
- cciss_procinit(i);
+ cciss_procinit(h);
- hba[i]->cciss_max_sectors = 8192;
+ h->cciss_max_sectors = 8192;
- rebuild_lun_table(hba[i], 1, 0);
- hba[i]->busy_initializing = 0;
+ rebuild_lun_table(h, 1, 0);
+ h->busy_initializing = 0;
return 1;
clean4:
- kfree(hba[i]->cmd_pool_bits);
+ kfree(h->cmd_pool_bits);
/* Free up sg elements */
- for (k = 0; k < hba[i]->nr_cmds; k++)
- kfree(hba[i]->scatter_list[k]);
- kfree(hba[i]->scatter_list);
- 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),
- hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
- if (hba[i]->errinfo_pool)
- pci_free_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
- hba[i]->errinfo_pool,
- hba[i]->errinfo_pool_dhandle);
- free_irq(hba[i]->intr[SIMPLE_MODE_INT], hba[i]);
+ for (k = 0; k < h->nr_cmds; k++)
+ kfree(h->scatter_list[k]);
+ kfree(h->scatter_list);
+ cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
+ if (h->cmd_pool)
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(CommandList_struct),
+ h->cmd_pool, h->cmd_pool_dhandle);
+ if (h->errinfo_pool)
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(ErrorInfo_struct),
+ h->errinfo_pool,
+ h->errinfo_pool_dhandle);
+ free_irq(h->intr[PERF_MODE_INT], h);
clean2:
- unregister_blkdev(hba[i]->major, hba[i]->devname);
+ unregister_blkdev(h->major, h->devname);
clean1:
- cciss_destroy_hba_sysfs_entry(hba[i]);
+ cciss_destroy_hba_sysfs_entry(h);
clean0:
pci_release_regions(pdev);
clean_no_release_regions:
- hba[i]->busy_initializing = 0;
+ h->busy_initializing = 0;
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
pci_set_drvdata(pdev, NULL);
- free_hba(i);
+ free_hba(h);
return -1;
}
@@ -4381,55 +4822,51 @@ static void cciss_shutdown(struct pci_dev *pdev)
h = pci_get_drvdata(pdev);
flush_buf = kzalloc(4, GFP_KERNEL);
if (!flush_buf) {
- printk(KERN_WARNING
- "cciss:%d cache not flushed, out of memory.\n",
- h->ctlr);
+ dev_warn(&h->pdev->dev, "cache not flushed, out of memory.\n");
return;
}
/* write all data in the battery backed cache to disk */
memset(flush_buf, 0, 4);
- return_code = sendcmd_withirq(CCISS_CACHE_FLUSH, h->ctlr, flush_buf,
+ return_code = sendcmd_withirq(h, CCISS_CACHE_FLUSH, flush_buf,
4, 0, CTLR_LUNID, TYPE_CMD);
kfree(flush_buf);
if (return_code != IO_OK)
- printk(KERN_WARNING "cciss%d: Error flushing cache\n",
- h->ctlr);
+ dev_warn(&h->pdev->dev, "Error flushing cache\n");
h->access.set_intr_mask(h, CCISS_INTR_OFF);
- free_irq(h->intr[2], h);
+ free_irq(h->intr[PERF_MODE_INT], h);
}
static void __devexit cciss_remove_one(struct pci_dev *pdev)
{
- ctlr_info_t *tmp_ptr;
+ ctlr_info_t *h;
int i, j;
if (pci_get_drvdata(pdev) == NULL) {
- printk(KERN_ERR "cciss: Unable to remove device \n");
+ dev_err(&pdev->dev, "Unable to remove device\n");
return;
}
- tmp_ptr = pci_get_drvdata(pdev);
- i = tmp_ptr->ctlr;
+ h = pci_get_drvdata(pdev);
+ i = h->ctlr;
if (hba[i] == NULL) {
- printk(KERN_ERR "cciss: device appears to "
- "already be removed \n");
+ dev_err(&pdev->dev, "device appears to already be removed\n");
return;
}
- mutex_lock(&hba[i]->busy_shutting_down);
+ mutex_lock(&h->busy_shutting_down);
- remove_from_scan_list(hba[i]);
- remove_proc_entry(hba[i]->devname, proc_cciss);
- unregister_blkdev(hba[i]->major, hba[i]->devname);
+ remove_from_scan_list(h);
+ remove_proc_entry(h->devname, proc_cciss);
+ unregister_blkdev(h->major, h->devname);
/* remove it from the disk list */
for (j = 0; j < CISS_MAX_LUN; j++) {
- struct gendisk *disk = hba[i]->gendisk[j];
+ struct gendisk *disk = h->gendisk[j];
if (disk) {
struct request_queue *q = disk->queue;
if (disk->flags & GENHD_FL_UP) {
- cciss_destroy_ld_sysfs_entry(hba[i], j, 1);
+ cciss_destroy_ld_sysfs_entry(h, j, 1);
del_gendisk(disk);
}
if (q)
@@ -4438,39 +4875,41 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
}
#ifdef CONFIG_CISS_SCSI_TAPE
- cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
+ cciss_unregister_scsi(h); /* unhook from SCSI subsystem */
#endif
cciss_shutdown(pdev);
#ifdef CONFIG_PCI_MSI
- if (hba[i]->msix_vector)
- pci_disable_msix(hba[i]->pdev);
- else if (hba[i]->msi_vector)
- pci_disable_msi(hba[i]->pdev);
+ if (h->msix_vector)
+ pci_disable_msix(h->pdev);
+ else if (h->msi_vector)
+ pci_disable_msi(h->pdev);
#endif /* CONFIG_PCI_MSI */
- iounmap(hba[i]->vaddr);
+ iounmap(h->transtable);
+ iounmap(h->cfgtable);
+ iounmap(h->vaddr);
- pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct),
- hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
- pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
- hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
- kfree(hba[i]->cmd_pool_bits);
+ pci_free_consistent(h->pdev, h->nr_cmds * sizeof(CommandList_struct),
+ h->cmd_pool, h->cmd_pool_dhandle);
+ pci_free_consistent(h->pdev, h->nr_cmds * sizeof(ErrorInfo_struct),
+ h->errinfo_pool, h->errinfo_pool_dhandle);
+ kfree(h->cmd_pool_bits);
/* Free up sg elements */
- for (j = 0; j < hba[i]->nr_cmds; j++)
- kfree(hba[i]->scatter_list[j]);
- kfree(hba[i]->scatter_list);
- cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
+ for (j = 0; j < h->nr_cmds; j++)
+ kfree(h->scatter_list[j]);
+ kfree(h->scatter_list);
+ cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
- cciss_destroy_hba_sysfs_entry(hba[i]);
- mutex_unlock(&hba[i]->busy_shutting_down);
- free_hba(i);
+ cciss_destroy_hba_sysfs_entry(h);
+ mutex_unlock(&h->busy_shutting_down);
+ free_hba(h);
}
static struct pci_driver cciss_pci_driver = {
@@ -4495,7 +4934,6 @@ static int __init cciss_init(void)
* array of them, the size must be a multiple of 8 bytes.
*/
BUILD_BUG_ON(sizeof(CommandList_struct) % COMMANDLIST_ALIGNMENT);
-
printk(KERN_INFO DRIVER_NAME "\n");
err = bus_register(&cciss_bus_type);
@@ -4532,8 +4970,8 @@ static void __exit cciss_cleanup(void)
/* double check that all controller entrys have been removed */
for (i = 0; i < MAX_CTLR; i++) {
if (hba[i] != NULL) {
- printk(KERN_WARNING "cciss: had to remove"
- " controller %d\n", i);
+ dev_warn(&hba[i]->pdev->dev,
+ "had to remove controller\n");
cciss_remove_one(hba[i]->pdev);
}
}
@@ -4542,46 +4980,5 @@ static void __exit cciss_cleanup(void)
bus_unregister(&cciss_bus_type);
}
-static void fail_all_cmds(unsigned long ctlr)
-{
- /* If we get here, the board is apparently dead. */
- ctlr_info_t *h = hba[ctlr];
- CommandList_struct *c;
- unsigned long flags;
-
- printk(KERN_WARNING "cciss%d: controller not responding.\n", h->ctlr);
- h->alive = 0; /* the controller apparently died... */
-
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
-
- pci_disable_device(h->pdev); /* Make sure it is really dead. */
-
- /* move everything off the request queue onto the completed queue */
- while (!hlist_empty(&h->reqQ)) {
- c = hlist_entry(h->reqQ.first, CommandList_struct, list);
- removeQ(c);
- h->Qdepth--;
- addQ(&h->cmpQ, c);
- }
-
- /* Now, fail everything on the completed queue with a HW error */
- while (!hlist_empty(&h->cmpQ)) {
- c = hlist_entry(h->cmpQ.first, CommandList_struct, list);
- removeQ(c);
- if (c->cmd_type != CMD_MSG_STALE)
- c->err_info->CommandStatus = CMD_HARDWARE_ERR;
- if (c->cmd_type == CMD_RWREQ) {
- complete_command(h, c, 0);
- } else if (c->cmd_type == CMD_IOCTL_PEND)
- complete(c->waiting);
-#ifdef CONFIG_CISS_SCSI_TAPE
- else if (c->cmd_type == CMD_SCSI)
- complete_scsi_command(c, 0, 0);
-#endif
- }
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- return;
-}
-
module_init(cciss_init);
module_exit(cciss_cleanup);
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index c5d4111..ae340ff 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -25,7 +25,7 @@ struct access_method {
void (*submit_command)(ctlr_info_t *h, CommandList_struct *c);
void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
unsigned long (*fifo_full)(ctlr_info_t *h);
- unsigned long (*intr_pending)(ctlr_info_t *h);
+ bool (*intr_pending)(ctlr_info_t *h);
unsigned long (*command_completed)(ctlr_info_t *h);
};
typedef struct _drive_info_struct
@@ -85,8 +85,8 @@ struct ctlr_info
int max_cmd_sgentries;
SGDescriptor_struct **cmd_sg_list;
-# 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];
@@ -137,10 +137,27 @@ struct ctlr_info
struct list_head scan_list;
struct completion scan_wait;
struct device dev;
+ /*
+ * 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;
};
-/* Defining the diffent access_menthods */
-/*
+/* Defining the diffent access_methods
+ *
* Memory mapped FIFO interface (SMART 53xx cards)
*/
#define SA5_DOORBELL 0x20
@@ -159,19 +176,47 @@ struct ctlr_info
#define SA5B_INTR_PENDING 0x04
#define FIFO_EMPTY 0xffffffff
#define CCISS_FIRMWARE_READY 0xffff0000 /* value in scratchpad register */
+/* Perf. 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 CISS_ERROR_BIT 0x02
#define CCISS_INTR_ON 1
#define CCISS_INTR_OFF 0
+
+
+/* CCISS_BOARD_READY_WAIT_SECS is how long to wait for a board
+ * to become ready, in seconds, before giving up on it.
+ * CCISS_BOARD_READY_POLL_INTERVAL_MSECS * is how long to wait
+ * between polling the board to see if it is ready, in
+ * milliseconds. CCISS_BOARD_READY_ITERATIONS is derived
+ * the above.
+ */
+#define CCISS_BOARD_READY_WAIT_SECS (120)
+#define CCISS_BOARD_READY_POLL_INTERVAL_MSECS (100)
+#define CCISS_BOARD_READY_ITERATIONS \
+ ((CCISS_BOARD_READY_WAIT_SECS * 1000) / \
+ CCISS_BOARD_READY_POLL_INTERVAL_MSECS)
+#define CCISS_POST_RESET_PAUSE_MSECS (3000)
+#define CCISS_POST_RESET_NOOP_INTERVAL_MSECS (1000)
+#define CCISS_POST_RESET_NOOP_RETRIES (12)
+
/*
Send the command to the hardware
*/
static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c)
{
#ifdef CCISS_DEBUG
- printk("Sending %x - down to controller\n", c->busaddr );
-#endif /* CCISS_DEBUG */
+ printk(KERN_WARNING "cciss%d: Sending %08x - down to controller\n",
+ h->ctlr, c->busaddr);
+#endif /* CCISS_DEBUG */
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
h->commands_outstanding++;
if ( h->commands_outstanding > h->max_outstanding)
@@ -214,6 +259,20 @@ static void SA5B_intr_mask(ctlr_info_t *h, unsigned long val)
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
+
+/* Performant mode intr_mask */
+static void SA5_performant_intr_mask(ctlr_info_t *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);
+ }
+}
+
/*
* Returns true if fifo is full.
*
@@ -250,10 +309,44 @@ static unsigned long SA5_completed(ctlr_info_t *h)
return ( register_value);
}
+
+/* Performant mode command completed */
+static unsigned long SA5_performant_completed(ctlr_info_t *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 an interrupt is pending..
*/
-static unsigned long SA5_intr_pending(ctlr_info_t *h)
+static bool SA5_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
@@ -268,7 +361,7 @@ static unsigned long SA5_intr_pending(ctlr_info_t *h)
/*
* Returns true if an interrupt is pending..
*/
-static unsigned long SA5B_intr_pending(ctlr_info_t *h)
+static bool SA5B_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
@@ -280,6 +373,20 @@ static unsigned long SA5B_intr_pending(ctlr_info_t *h)
return 0 ;
}
+static bool SA5_performant_intr_pending(ctlr_info_t *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,
@@ -297,6 +404,14 @@ static struct access_method SA5B_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;
char *product_name;
@@ -304,6 +419,4 @@ struct board_type {
int nr_cmds; /* Max cmds this kind of ctlr can handle. */
};
-#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 e624ff9..eb060f1 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -52,8 +52,10 @@
/* Configuration Table */
#define CFGTBL_ChangeReq 0x00000001l
#define CFGTBL_AccCmds 0x00000001l
+#define DOORBELL_CTLR_RESET 0x00000004l
#define CFGTBL_Trans_Simple 0x00000002l
+#define CFGTBL_Trans_Performant 0x00000004l
#define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l
@@ -173,12 +175,15 @@ typedef struct _SGDescriptor_struct {
* PAD_64 can be adjusted independently as needed for 32-bit
* and 64-bits systems.
*/
-#define COMMANDLIST_ALIGNMENT (8)
+#define COMMANDLIST_ALIGNMENT (32)
#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)
+#define DIRECT_LOOKUP_BIT 0x10
+#define DIRECT_LOOKUP_SHIFT 5
+
typedef struct _CommandList_struct {
CommandListHeader_struct Header;
RequestBlock_struct Request;
@@ -195,7 +200,7 @@ typedef struct _CommandList_struct {
struct completion *waiting;
int retry_count;
void * scsi_cmd;
- char pad[PADSIZE];
+ char pad[PADSIZE];
} CommandList_struct;
/* Configuration Table Structure */
@@ -209,12 +214,15 @@ typedef struct _HostWrite_struct {
typedef struct _CfgTable_struct {
BYTE Signature[4];
DWORD SpecValence;
+#define SIMPLE_MODE 0x02
+#define PERFORMANT_MODE 0x04
+#define MEMQ_MODE 0x08
DWORD TransportSupport;
DWORD TransportActive;
HostWrite_struct HostWrite;
DWORD CmdsOutMax;
DWORD BusTypes;
- DWORD Reserved;
+ DWORD TransMethodOffset;
BYTE ServerName[16];
DWORD HeartBeat;
DWORD SCSI_Prefetch;
@@ -222,6 +230,28 @@ typedef struct _CfgTable_struct {
DWORD MaxLogicalUnits;
DWORD MaxPhysicalDrives;
DWORD MaxPhysicalDrivesPerLogicalUnit;
+ DWORD MaxPerformantModeCommands;
+ u8 reserved[0x78 - 0x58];
+ u32 misc_fw_support; /* offset 0x78 */
+#define MISC_FW_DOORBELL_RESET (0x02)
} CfgTable_struct;
+
+struct TransTable_struct {
+ u32 BlockFetch0;
+ u32 BlockFetch1;
+ u32 BlockFetch2;
+ u32 BlockFetch3;
+ u32 BlockFetch4;
+ u32 BlockFetch5;
+ u32 BlockFetch6;
+ u32 BlockFetch7;
+ u32 RepQSize;
+ u32 RepQCount;
+ u32 RepQCtrAddrLow32;
+ u32 RepQCtrAddrHigh32;
+ u32 RepQAddr0Low32;
+ u32 RepQAddr0High32;
+};
+
#pragma pack()
#endif /* CCISS_CMD_H */
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 72dae92..575495f 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -44,13 +44,15 @@
#define CCISS_ABORT_MSG 0x00
#define CCISS_RESET_MSG 0x01
-static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
size_t size,
__u8 page_code, unsigned char *scsi3addr,
int cmd_type);
-static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool);
-static void cmd_free(ctlr_info_t *h, CommandList_struct *c, int got_from_pool);
+static CommandList_struct *cmd_alloc(ctlr_info_t *h);
+static CommandList_struct *cmd_special_alloc(ctlr_info_t *h);
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c);
+static void cmd_special_free(ctlr_info_t *h, CommandList_struct *c);
static int cciss_scsi_proc_info(
struct Scsi_Host *sh,
@@ -93,8 +95,8 @@ static struct scsi_host_template cciss_driver_template = {
#pragma pack(1)
-#define SCSI_PAD_32 0
-#define SCSI_PAD_64 0
+#define SCSI_PAD_32 8
+#define SCSI_PAD_64 8
struct cciss_scsi_cmd_stack_elem_t {
CommandList_struct cmd;
@@ -127,16 +129,16 @@ struct cciss_scsi_adapter_data_t {
spinlock_t lock; // to protect ccissscsi[ctlr];
};
-#define CPQ_TAPE_LOCK(ctlr, flags) spin_lock_irqsave( \
- &hba[ctlr]->scsi_ctlr->lock, flags);
-#define CPQ_TAPE_UNLOCK(ctlr, flags) spin_unlock_irqrestore( \
- &hba[ctlr]->scsi_ctlr->lock, flags);
+#define CPQ_TAPE_LOCK(h, flags) spin_lock_irqsave( \
+ &h->scsi_ctlr->lock, flags);
+#define CPQ_TAPE_UNLOCK(h, flags) spin_unlock_irqrestore( \
+ &h->scsi_ctlr->lock, flags);
static CommandList_struct *
scsi_cmd_alloc(ctlr_info_t *h)
{
/* assume only one process in here at a time, locking done by caller. */
- /* use CCISS_LOCK(ctlr) */
+ /* use h->lock */
/* might be better to rewrite how we allocate scsi commands in a way that */
/* needs no locking at all. */
@@ -177,10 +179,10 @@ scsi_cmd_alloc(ctlr_info_t *h)
}
static void
-scsi_cmd_free(ctlr_info_t *h, CommandList_struct *cmd)
+scsi_cmd_free(ctlr_info_t *h, CommandList_struct *c)
{
/* assume only one process in here at a time, locking done by caller. */
- /* use CCISS_LOCK(ctlr) */
+ /* use h->lock */
/* drop the free memory chunk on top of the stack. */
struct cciss_scsi_adapter_data_t *sa;
@@ -190,22 +192,23 @@ scsi_cmd_free(ctlr_info_t *h, CommandList_struct *cmd)
stk = &sa->cmd_stack;
stk->top++;
if (stk->top >= CMD_STACK_SIZE) {
- printk("cciss: scsi_cmd_free called too many times.\n");
+ dev_err(&h->pdev->dev,
+ "scsi_cmd_free called too many times.\n");
BUG();
}
- stk->elem[stk->top] = (struct cciss_scsi_cmd_stack_elem_t *) cmd;
+ stk->elem[stk->top] = (struct cciss_scsi_cmd_stack_elem_t *) c;
}
static int
-scsi_cmd_stack_setup(int ctlr, struct cciss_scsi_adapter_data_t *sa)
+scsi_cmd_stack_setup(ctlr_info_t *h, struct cciss_scsi_adapter_data_t *sa)
{
int i;
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)
+ sa->cmd_sg_list = cciss_allocate_sg_chain_blocks(h,
+ h->chainsize, CMD_STACK_SIZE);
+ if (!sa->cmd_sg_list && h->chainsize > 0)
return -ENOMEM;
stk = &sa->cmd_stack;
@@ -215,7 +218,7 @@ scsi_cmd_stack_setup(int ctlr, struct cciss_scsi_adapter_data_t *sa)
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);
+ pci_alloc_consistent(h->pdev, size, &stk->cmd_pool_handle);
if (stk->pool == NULL) {
cciss_free_sg_chain_blocks(sa->cmd_sg_list, CMD_STACK_SIZE);
@@ -234,23 +237,22 @@ scsi_cmd_stack_setup(int ctlr, struct cciss_scsi_adapter_data_t *sa)
}
static void
-scsi_cmd_stack_free(int ctlr)
+scsi_cmd_stack_free(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
size_t size;
- sa = hba[ctlr]->scsi_ctlr;
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
if (stk->top != CMD_STACK_SIZE-1) {
- printk( "cciss: %d scsi commands are still outstanding.\n",
+ dev_warn(&h->pdev->dev,
+ "bug: %d scsi commands are still outstanding.\n",
CMD_STACK_SIZE - stk->top);
- // BUG();
- printk("WE HAVE A BUG HERE!!! stk=0x%p\n", stk);
}
size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * CMD_STACK_SIZE;
- pci_free_consistent(hba[ctlr]->pdev, size, stk->pool, stk->cmd_pool_handle);
+ pci_free_consistent(h->pdev, size, stk->pool, stk->cmd_pool_handle);
stk->pool = NULL;
cciss_free_sg_chain_blocks(sa->cmd_sg_list, CMD_STACK_SIZE);
}
@@ -342,20 +344,20 @@ print_cmd(CommandList_struct *cp)
#endif
static int
-find_bus_target_lun(int ctlr, int *bus, int *target, int *lun)
+find_bus_target_lun(ctlr_info_t *h, int *bus, int *target, int *lun)
{
/* finds an unused bus, target, lun for a new device */
- /* assumes hba[ctlr]->scsi_ctlr->lock is held */
+ /* assumes h->scsi_ctlr->lock is held */
int i, found=0;
unsigned char target_taken[CCISS_MAX_SCSI_DEVS_PER_HBA];
memset(&target_taken[0], 0, CCISS_MAX_SCSI_DEVS_PER_HBA);
target_taken[SELF_SCSI_ID] = 1;
- for (i=0;i<ccissscsi[ctlr].ndevices;i++)
- target_taken[ccissscsi[ctlr].dev[i].target] = 1;
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++)
+ target_taken[ccissscsi[h->ctlr].dev[i].target] = 1;
- for (i=0;i<CCISS_MAX_SCSI_DEVS_PER_HBA;i++) {
+ for (i = 0; i < CCISS_MAX_SCSI_DEVS_PER_HBA; i++) {
if (!target_taken[i]) {
*bus = 0; *target=i; *lun = 0; found=1;
break;
@@ -369,19 +371,19 @@ struct scsi2map {
};
static int
-cciss_scsi_add_entry(int ctlr, int hostno,
+cciss_scsi_add_entry(ctlr_info_t *h, int hostno,
struct cciss_scsi_dev_t *device,
struct scsi2map *added, int *nadded)
{
- /* assumes hba[ctlr]->scsi_ctlr->lock is held */
- int n = ccissscsi[ctlr].ndevices;
+ /* assumes h->scsi_ctlr->lock is held */
+ int n = ccissscsi[h->ctlr].ndevices;
struct cciss_scsi_dev_t *sd;
int i, bus, target, lun;
unsigned char addr1[8], addr2[8];
if (n >= CCISS_MAX_SCSI_DEVS_PER_HBA) {
- printk("cciss%d: Too many devices, "
- "some will be inaccessible.\n", ctlr);
+ dev_warn(&h->pdev->dev, "Too many devices, "
+ "some will be inaccessible.\n");
return -1;
}
@@ -397,7 +399,7 @@ cciss_scsi_add_entry(int ctlr, int hostno,
memcpy(addr1, device->scsi3addr, 8);
addr1[4] = 0;
for (i = 0; i < n; i++) {
- sd = &ccissscsi[ctlr].dev[i];
+ sd = &ccissscsi[h->ctlr].dev[i];
memcpy(addr2, sd->scsi3addr, 8);
addr2[4] = 0;
/* differ only in byte 4? */
@@ -410,9 +412,9 @@ cciss_scsi_add_entry(int ctlr, int hostno,
}
}
- sd = &ccissscsi[ctlr].dev[n];
+ sd = &ccissscsi[h->ctlr].dev[n];
if (lun == 0) {
- if (find_bus_target_lun(ctlr,
+ if (find_bus_target_lun(h,
&sd->bus, &sd->target, &sd->lun) != 0)
return -1;
} else {
@@ -431,37 +433,37 @@ cciss_scsi_add_entry(int ctlr, int hostno,
memcpy(sd->device_id, device->device_id, sizeof(sd->device_id));
sd->devtype = device->devtype;
- ccissscsi[ctlr].ndevices++;
+ ccissscsi[h->ctlr].ndevices++;
/* initially, (before registering with scsi layer) we don't
know our hostno and we don't want to print anything first
time anyway (the scsi layer's inquiries will show that info) */
if (hostno != -1)
- printk("cciss%d: %s device c%db%dt%dl%d added.\n",
- ctlr, scsi_device_type(sd->devtype), hostno,
+ dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n",
+ scsi_device_type(sd->devtype), hostno,
sd->bus, sd->target, sd->lun);
return 0;
}
static void
-cciss_scsi_remove_entry(int ctlr, int hostno, int entry,
+cciss_scsi_remove_entry(ctlr_info_t *h, int hostno, int entry,
struct scsi2map *removed, int *nremoved)
{
- /* assumes hba[ctlr]->scsi_ctlr->lock is held */
+ /* assumes h->ctlr]->scsi_ctlr->lock is held */
int i;
struct cciss_scsi_dev_t sd;
if (entry < 0 || entry >= CCISS_MAX_SCSI_DEVS_PER_HBA) return;
- sd = ccissscsi[ctlr].dev[entry];
+ sd = ccissscsi[h->ctlr].dev[entry];
removed[*nremoved].bus = sd.bus;
removed[*nremoved].target = sd.target;
removed[*nremoved].lun = sd.lun;
(*nremoved)++;
- for (i=entry;i<ccissscsi[ctlr].ndevices-1;i++)
- ccissscsi[ctlr].dev[i] = ccissscsi[ctlr].dev[i+1];
- ccissscsi[ctlr].ndevices--;
- printk("cciss%d: %s device c%db%dt%dl%d removed.\n",
- ctlr, scsi_device_type(sd.devtype), hostno,
+ for (i = entry; i < ccissscsi[h->ctlr].ndevices-1; i++)
+ ccissscsi[h->ctlr].dev[i] = ccissscsi[h->ctlr].dev[i+1];
+ ccissscsi[h->ctlr].ndevices--;
+ dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n",
+ scsi_device_type(sd.devtype), hostno,
sd.bus, sd.target, sd.lun);
}
@@ -476,24 +478,24 @@ cciss_scsi_remove_entry(int ctlr, int hostno, int entry,
(a)[1] == (b)[1] && \
(a)[0] == (b)[0])
-static void fixup_botched_add(int ctlr, char *scsi3addr)
+static void fixup_botched_add(ctlr_info_t *h, char *scsi3addr)
{
/* called when scsi_add_device fails in order to re-adjust */
/* ccissscsi[] to match the mid layer's view. */
unsigned long flags;
int i, j;
- CPQ_TAPE_LOCK(ctlr, flags);
- for (i = 0; i < ccissscsi[ctlr].ndevices; i++) {
+ CPQ_TAPE_LOCK(h, flags);
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) {
if (memcmp(scsi3addr,
- ccissscsi[ctlr].dev[i].scsi3addr, 8) == 0) {
- for (j = i; j < ccissscsi[ctlr].ndevices-1; j++)
- ccissscsi[ctlr].dev[j] =
- ccissscsi[ctlr].dev[j+1];
- ccissscsi[ctlr].ndevices--;
+ ccissscsi[h->ctlr].dev[i].scsi3addr, 8) == 0) {
+ for (j = i; j < ccissscsi[h->ctlr].ndevices-1; j++)
+ ccissscsi[h->ctlr].dev[j] =
+ ccissscsi[h->ctlr].dev[j+1];
+ ccissscsi[h->ctlr].ndevices--;
break;
}
}
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
}
static int device_is_the_same(struct cciss_scsi_dev_t *dev1,
@@ -513,7 +515,7 @@ static int device_is_the_same(struct cciss_scsi_dev_t *dev1,
}
static int
-adjust_cciss_scsi_table(int ctlr, int hostno,
+adjust_cciss_scsi_table(ctlr_info_t *h, int hostno,
struct cciss_scsi_dev_t sd[], int nsds)
{
/* sd contains scsi3 addresses and devtypes, but
@@ -534,15 +536,15 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
GFP_KERNEL);
if (!added || !removed) {
- printk(KERN_WARNING "cciss%d: Out of memory in "
- "adjust_cciss_scsi_table\n", ctlr);
+ dev_warn(&h->pdev->dev,
+ "Out of memory in adjust_cciss_scsi_table\n");
goto free_and_out;
}
- CPQ_TAPE_LOCK(ctlr, flags);
+ CPQ_TAPE_LOCK(h, flags);
if (hostno != -1) /* if it's not the first time... */
- sh = hba[ctlr]->scsi_ctlr->scsi_host;
+ sh = h->scsi_ctlr->scsi_host;
/* find any devices in ccissscsi[] that are not in
sd[] and remove them from ccissscsi[] */
@@ -550,8 +552,8 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
i = 0;
nremoved = 0;
nadded = 0;
- while(i<ccissscsi[ctlr].ndevices) {
- csd = &ccissscsi[ctlr].dev[i];
+ while (i < ccissscsi[h->ctlr].ndevices) {
+ csd = &ccissscsi[h->ctlr].dev[i];
found=0;
for (j=0;j<nsds;j++) {
if (SCSI3ADDR_EQ(sd[j].scsi3addr,
@@ -566,20 +568,18 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
if (found == 0) { /* device no longer present. */
changes++;
- /* printk("cciss%d: %s device c%db%dt%dl%d removed.\n",
- ctlr, scsi_device_type(csd->devtype), hostno,
- csd->bus, csd->target, csd->lun); */
- cciss_scsi_remove_entry(ctlr, hostno, i,
+ cciss_scsi_remove_entry(h, hostno, i,
removed, &nremoved);
/* remove ^^^, hence i not incremented */
} else if (found == 1) { /* device is different in some way */
changes++;
- printk("cciss%d: device c%db%dt%dl%d has changed.\n",
- ctlr, hostno, csd->bus, csd->target, csd->lun);
- cciss_scsi_remove_entry(ctlr, hostno, i,
+ dev_info(&h->pdev->dev,
+ "device c%db%dt%dl%d has changed.\n",
+ hostno, csd->bus, csd->target, csd->lun);
+ cciss_scsi_remove_entry(h, hostno, i,
removed, &nremoved);
/* remove ^^^, hence i not incremented */
- if (cciss_scsi_add_entry(ctlr, hostno, &sd[j],
+ if (cciss_scsi_add_entry(h, hostno, &sd[j],
added, &nadded) != 0)
/* we just removed one, so add can't fail. */
BUG();
@@ -601,8 +601,8 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
for (i=0;i<nsds;i++) {
found=0;
- for (j=0;j<ccissscsi[ctlr].ndevices;j++) {
- csd = &ccissscsi[ctlr].dev[j];
+ for (j = 0; j < ccissscsi[h->ctlr].ndevices; j++) {
+ csd = &ccissscsi[h->ctlr].dev[j];
if (SCSI3ADDR_EQ(sd[i].scsi3addr,
csd->scsi3addr)) {
if (device_is_the_same(&sd[i], csd))
@@ -614,18 +614,18 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
}
if (!found) {
changes++;
- if (cciss_scsi_add_entry(ctlr, hostno, &sd[i],
+ if (cciss_scsi_add_entry(h, hostno, &sd[i],
added, &nadded) != 0)
break;
} else if (found == 1) {
/* should never happen... */
changes++;
- printk(KERN_WARNING "cciss%d: device "
- "unexpectedly changed\n", ctlr);
+ dev_warn(&h->pdev->dev,
+ "device unexpectedly changed\n");
/* but if it does happen, we just ignore that device */
}
}
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
/* Don't notify scsi mid layer of any changes the first time through */
/* (or if there are no changes) scsi_scan_host will do it later the */
@@ -645,9 +645,9 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
/* We don't expect to get here. */
/* future cmds to this device will get selection */
/* timeout as if the device was gone. */
- printk(KERN_WARNING "cciss%d: didn't find "
+ dev_warn(&h->pdev->dev, "didn't find "
"c%db%dt%dl%d\n for removal.",
- ctlr, hostno, removed[i].bus,
+ hostno, removed[i].bus,
removed[i].target, removed[i].lun);
}
}
@@ -659,13 +659,12 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
added[i].target, added[i].lun);
if (rc == 0)
continue;
- printk(KERN_WARNING "cciss%d: scsi_add_device "
+ dev_warn(&h->pdev->dev, "scsi_add_device "
"c%db%dt%dl%d failed, device not added.\n",
- ctlr, hostno,
- added[i].bus, added[i].target, added[i].lun);
+ hostno, added[i].bus, added[i].target, added[i].lun);
/* now we have to remove it from ccissscsi, */
/* since it didn't get added to scsi mid layer */
- fixup_botched_add(ctlr, added[i].scsi3addr);
+ fixup_botched_add(h, added[i].scsi3addr);
}
free_and_out:
@@ -675,33 +674,33 @@ free_and_out:
}
static int
-lookup_scsi3addr(int ctlr, int bus, int target, int lun, char *scsi3addr)
+lookup_scsi3addr(ctlr_info_t *h, int bus, int target, int lun, char *scsi3addr)
{
int i;
struct cciss_scsi_dev_t *sd;
unsigned long flags;
- CPQ_TAPE_LOCK(ctlr, flags);
- for (i=0;i<ccissscsi[ctlr].ndevices;i++) {
- sd = &ccissscsi[ctlr].dev[i];
+ CPQ_TAPE_LOCK(h, flags);
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) {
+ sd = &ccissscsi[h->ctlr].dev[i];
if (sd->bus == bus &&
sd->target == target &&
sd->lun == lun) {
memcpy(scsi3addr, &sd->scsi3addr[0], 8);
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
return 0;
}
}
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
return -1;
}
static void
-cciss_scsi_setup(int cntl_num)
+cciss_scsi_setup(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t * shba;
- ccissscsi[cntl_num].ndevices = 0;
+ ccissscsi[h->ctlr].ndevices = 0;
shba = (struct cciss_scsi_adapter_data_t *)
kmalloc(sizeof(*shba), GFP_KERNEL);
if (shba == NULL)
@@ -709,35 +708,35 @@ cciss_scsi_setup(int cntl_num)
shba->scsi_host = NULL;
spin_lock_init(&shba->lock);
shba->registered = 0;
- if (scsi_cmd_stack_setup(cntl_num, shba) != 0) {
+ if (scsi_cmd_stack_setup(h, shba) != 0) {
kfree(shba);
shba = NULL;
}
- hba[cntl_num]->scsi_ctlr = shba;
+ h->scsi_ctlr = shba;
return;
}
-static void
-complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
+static void complete_scsi_command(CommandList_struct *c, int timeout,
+ __u32 tag)
{
struct scsi_cmnd *cmd;
- ctlr_info_t *ctlr;
+ ctlr_info_t *h;
ErrorInfo_struct *ei;
- ei = cp->err_info;
+ ei = c->err_info;
/* First, see if it was a message rather than a command */
- if (cp->Request.Type.Type == TYPE_MSG) {
- cp->cmd_type = CMD_MSG_DONE;
+ if (c->Request.Type.Type == TYPE_MSG) {
+ c->cmd_type = CMD_MSG_DONE;
return;
}
- cmd = (struct scsi_cmnd *) cp->scsi_cmd;
- ctlr = hba[cp->ctlr];
+ cmd = (struct scsi_cmnd *) c->scsi_cmd;
+ h = hba[c->ctlr];
scsi_dma_unmap(cmd);
- if (cp->Header.SGTotal > ctlr->max_cmd_sgentries)
- cciss_unmap_sg_chain_block(ctlr, cp);
+ if (c->Header.SGTotal > h->max_cmd_sgentries)
+ cciss_unmap_sg_chain_block(h, c);
cmd->result = (DID_OK << 16); /* host byte */
cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
@@ -764,9 +763,8 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
{
#if 0
printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- cp,
- ei->ScsiStatus);
+ "has SCSI Status = %x\n",
+ c, ei->ScsiStatus);
#endif
cmd->result |= (ei->ScsiStatus << 1);
}
@@ -786,13 +784,13 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
break;
case CMD_DATA_OVERRUN:
- printk(KERN_WARNING "cciss: cp %p has"
+ dev_warn(&h->pdev->dev, "%p has"
" completed with data overrun "
- "reported\n", cp);
+ "reported\n", c);
break;
case CMD_INVALID: {
- /* print_bytes(cp, sizeof(*cp), 1, 0);
- print_cmd(cp); */
+ /* print_bytes(c, sizeof(*c), 1, 0);
+ print_cmd(c); */
/* We get CMD_INVALID if you address a non-existent tape drive instead
of a selection timeout (no response). You will see this if you yank
out a tape drive, then try to access it. This is kind of a shame
@@ -802,54 +800,50 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
}
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cp %p has "
- "protocol error \n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p has protocol error\n", c);
break;
case CMD_HARDWARE_ERR:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p had "
- " hardware error\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p had hardware error\n", c);
break;
case CMD_CONNECTION_LOST:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p had "
- "connection lost\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p had connection lost\n", c);
break;
case CMD_ABORTED:
cmd->result = DID_ABORT << 16;
- printk(KERN_WARNING "cciss: cp %p was "
- "aborted\n", cp);
+ dev_warn(&h->pdev->dev, "%p was aborted\n", c);
break;
case CMD_ABORT_FAILED:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p reports "
- "abort failed\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p reports abort failed\n", c);
break;
case CMD_UNSOLICITED_ABORT:
cmd->result = DID_ABORT << 16;
- printk(KERN_WARNING "cciss: cp %p aborted "
- "do to an unsolicited abort\n", cp);
+ dev_warn(&h->pdev->dev, "%p aborted do to an "
+ "unsolicited abort\n", c);
break;
case CMD_TIMEOUT:
cmd->result = DID_TIME_OUT << 16;
- printk(KERN_WARNING "cciss: cp %p timedout\n",
- cp);
+ dev_warn(&h->pdev->dev, "%p timedout\n", c);
break;
default:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p returned "
- "unknown status %x\n", cp,
+ dev_warn(&h->pdev->dev,
+ "%p returned unknown status %x\n", c,
ei->CommandStatus);
}
}
- // printk("c:%p:c%db%dt%dl%d ", cmd, ctlr->ctlr, cmd->channel,
- // cmd->target, cmd->lun);
cmd->scsi_done(cmd);
- scsi_cmd_free(ctlr, cp);
+ scsi_cmd_free(h, c);
}
static int
-cciss_scsi_detect(int ctlr)
+cciss_scsi_detect(ctlr_info_t *h)
{
struct Scsi_Host *sh;
int error;
@@ -860,15 +854,15 @@ 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;
+ sh->sg_tablesize = h->maxsgentries;
sh->max_cmd_len = MAX_COMMAND_SIZE;
((struct cciss_scsi_adapter_data_t *)
- hba[ctlr]->scsi_ctlr)->scsi_host = sh;
- sh->hostdata[0] = (unsigned long) hba[ctlr];
- sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT];
+ h->scsi_ctlr)->scsi_host = sh;
+ sh->hostdata[0] = (unsigned long) h;
+ sh->irq = h->intr[SIMPLE_MODE_INT];
sh->unique_id = sh->irq;
- error = scsi_add_host(sh, &hba[ctlr]->pdev->dev);
+ error = scsi_add_host(sh, &h->pdev->dev);
if (error)
goto fail_host_put;
scsi_scan_host(sh);
@@ -882,20 +876,20 @@ cciss_scsi_detect(int ctlr)
static void
cciss_unmap_one(struct pci_dev *pdev,
- CommandList_struct *cp,
+ CommandList_struct *c,
size_t buflen,
int data_direction)
{
u64bit addr64;
- addr64.val32.lower = cp->SG[0].Addr.lower;
- addr64.val32.upper = cp->SG[0].Addr.upper;
+ addr64.val32.lower = c->SG[0].Addr.lower;
+ addr64.val32.upper = c->SG[0].Addr.upper;
pci_unmap_single(pdev, (dma_addr_t) addr64.val, buflen, data_direction);
}
static void
cciss_map_one(struct pci_dev *pdev,
- CommandList_struct *cp,
+ CommandList_struct *c,
unsigned char *buf,
size_t buflen,
int data_direction)
@@ -903,164 +897,149 @@ cciss_map_one(struct pci_dev *pdev,
__u64 addr64;
addr64 = (__u64) pci_map_single(pdev, buf, buflen, data_direction);
- cp->SG[0].Addr.lower =
+ c->SG[0].Addr.lower =
(__u32) (addr64 & (__u64) 0x00000000FFFFFFFF);
- cp->SG[0].Addr.upper =
+ c->SG[0].Addr.upper =
(__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 */
+ c->SG[0].Len = buflen;
+ c->Header.SGList = (__u8) 1; /* no. SGs contig in this cmd */
+ c->Header.SGTotal = (__u16) 1; /* total sgs in this cmd list */
}
static int
-cciss_scsi_do_simple_cmd(ctlr_info_t *c,
- CommandList_struct *cp,
+cciss_scsi_do_simple_cmd(ctlr_info_t *h,
+ CommandList_struct *c,
unsigned char *scsi3addr,
unsigned char *cdb,
unsigned char cdblen,
unsigned char *buf, int bufsize,
int direction)
{
- unsigned long flags;
DECLARE_COMPLETION_ONSTACK(wait);
- cp->cmd_type = CMD_IOCTL_PEND; // treat this like an ioctl
- cp->scsi_cmd = NULL;
- cp->Header.ReplyQueue = 0; // unused in simple mode
- memcpy(&cp->Header.LUN, scsi3addr, sizeof(cp->Header.LUN));
- cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag
+ c->cmd_type = CMD_IOCTL_PEND; /* treat this like an ioctl */
+ c->scsi_cmd = NULL;
+ c->Header.ReplyQueue = 0; /* unused in simple mode */
+ memcpy(&c->Header.LUN, scsi3addr, sizeof(c->Header.LUN));
+ c->Header.Tag.lower = c->busaddr; /* Use k. address of cmd as tag */
// Fill in the request block...
/* printk("Using scsi3addr 0x%02x%0x2%0x2%0x2%0x2%0x2%0x2%0x2\n",
scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3],
scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]); */
- memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB));
- memcpy(cp->Request.CDB, cdb, cdblen);
- cp->Request.Timeout = 0;
- cp->Request.CDBLen = cdblen;
- cp->Request.Type.Type = TYPE_CMD;
- cp->Request.Type.Attribute = ATTR_SIMPLE;
- cp->Request.Type.Direction = direction;
+ memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+ memcpy(c->Request.CDB, cdb, cdblen);
+ c->Request.Timeout = 0;
+ c->Request.CDBLen = cdblen;
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Type.Direction = direction;
/* Fill in the SG list and do dma mapping */
- cciss_map_one(c->pdev, cp, (unsigned char *) buf,
+ cciss_map_one(h->pdev, c, (unsigned char *) buf,
bufsize, DMA_FROM_DEVICE);
- cp->waiting = &wait;
-
- /* Put the request on the tail of the request queue */
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- addQ(&c->reqQ, cp);
- c->Qdepth++;
- start_io(c);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
-
+ c->waiting = &wait;
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
/* undo the dma mapping */
- cciss_unmap_one(c->pdev, cp, bufsize, DMA_FROM_DEVICE);
+ cciss_unmap_one(h->pdev, c, bufsize, DMA_FROM_DEVICE);
return(0);
}
static void
-cciss_scsi_interpret_error(CommandList_struct *cp)
+cciss_scsi_interpret_error(ctlr_info_t *h, CommandList_struct *c)
{
ErrorInfo_struct *ei;
- ei = cp->err_info;
+ ei = c->err_info;
switch(ei->CommandStatus)
{
case CMD_TARGET_STATUS:
- printk(KERN_WARNING "cciss: cmd %p has "
- "completed with errors\n", cp);
- printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- cp,
- ei->ScsiStatus);
+ dev_warn(&h->pdev->dev,
+ "cmd %p has completed with errors\n", c);
+ dev_warn(&h->pdev->dev,
+ "cmd %p has SCSI Status = %x\n",
+ c, ei->ScsiStatus);
if (ei->ScsiStatus == 0)
- printk(KERN_WARNING
- "cciss:SCSI status is abnormally zero. "
+ dev_warn(&h->pdev->dev,
+ "SCSI status is abnormally zero. "
"(probably indicates selection timeout "
"reported incorrectly due to a known "
"firmware bug, circa July, 2001.)\n");
break;
case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
- printk("UNDERRUN\n");
+ dev_info(&h->pdev->dev, "UNDERRUN\n");
break;
case CMD_DATA_OVERRUN:
- printk(KERN_WARNING "cciss: cp %p has"
+ dev_warn(&h->pdev->dev, "%p has"
" completed with data overrun "
- "reported\n", cp);
+ "reported\n", c);
break;
case CMD_INVALID: {
/* controller unfortunately reports SCSI passthru's */
/* to non-existent targets as invalid commands. */
- printk(KERN_WARNING "cciss: cp %p is "
- "reported invalid (probably means "
- "target device no longer present)\n",
- cp);
- /* print_bytes((unsigned char *) cp, sizeof(*cp), 1, 0);
- print_cmd(cp); */
+ dev_warn(&h->pdev->dev,
+ "%p is reported invalid (probably means "
+ "target device no longer present)\n", c);
+ /* print_bytes((unsigned char *) c, sizeof(*c), 1, 0);
+ print_cmd(c); */
}
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cp %p has "
- "protocol error \n", cp);
+ dev_warn(&h->pdev->dev, "%p has protocol error\n", c);
break;
case CMD_HARDWARE_ERR:
/* cmd->result = DID_ERROR << 16; */
- printk(KERN_WARNING "cciss: cp %p had "
- " hardware error\n", cp);
+ dev_warn(&h->pdev->dev, "%p had hardware error\n", c);
break;
case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cp %p had "
- "connection lost\n", cp);
+ dev_warn(&h->pdev->dev, "%p had connection lost\n", c);
break;
case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cp %p was "
- "aborted\n", cp);
+ dev_warn(&h->pdev->dev, "%p was aborted\n", c);
break;
case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cp %p reports "
- "abort failed\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p reports abort failed\n", c);
break;
case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING "cciss: cp %p aborted "
- "do to an unsolicited abort\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p aborted do to an unsolicited abort\n", c);
break;
case CMD_TIMEOUT:
- printk(KERN_WARNING "cciss: cp %p timedout\n",
- cp);
+ dev_warn(&h->pdev->dev, "%p timedout\n", c);
break;
default:
- printk(KERN_WARNING "cciss: cp %p returned "
- "unknown status %x\n", cp,
- ei->CommandStatus);
+ dev_warn(&h->pdev->dev,
+ "%p returned unknown status %x\n",
+ c, ei->CommandStatus);
}
}
static int
-cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr,
+cciss_scsi_do_inquiry(ctlr_info_t *h, unsigned char *scsi3addr,
unsigned char page, unsigned char *buf,
unsigned char bufsize)
{
int rc;
- CommandList_struct *cp;
+ CommandList_struct *c;
char cdb[6];
ErrorInfo_struct *ei;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- cp = scsi_cmd_alloc(c);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
+ c = scsi_cmd_alloc(h);
+ spin_unlock_irqrestore(&h->lock, flags);
- if (cp == NULL) { /* trouble... */
+ if (c == NULL) { /* trouble... */
printk("cmd_alloc returned NULL!\n");
return -1;
}
- ei = cp->err_info;
+ ei = c->err_info;
cdb[0] = CISS_INQUIRY;
cdb[1] = (page != 0);
@@ -1068,24 +1047,24 @@ cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr,
cdb[3] = 0;
cdb[4] = bufsize;
cdb[5] = 0;
- rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, cdb,
+ rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr, cdb,
6, buf, bufsize, XFER_READ);
if (rc != 0) return rc; /* something went wrong */
if (ei->CommandStatus != 0 &&
ei->CommandStatus != CMD_DATA_UNDERRUN) {
- cciss_scsi_interpret_error(cp);
+ cciss_scsi_interpret_error(h, c);
rc = -1;
}
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- scsi_cmd_free(c, cp);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
+ scsi_cmd_free(h, c);
+ spin_unlock_irqrestore(&h->lock, flags);
return rc;
}
/* Get the device id from inquiry page 0x83 */
-static int cciss_scsi_get_device_id(ctlr_info_t *c, unsigned char *scsi3addr,
+static int cciss_scsi_get_device_id(ctlr_info_t *h, unsigned char *scsi3addr,
unsigned char *device_id, int buflen)
{
int rc;
@@ -1096,7 +1075,7 @@ static int cciss_scsi_get_device_id(ctlr_info_t *c, unsigned char *scsi3addr,
buf = kzalloc(64, GFP_KERNEL);
if (!buf)
return -1;
- rc = cciss_scsi_do_inquiry(c, scsi3addr, 0x83, buf, 64);
+ rc = cciss_scsi_do_inquiry(h, scsi3addr, 0x83, buf, 64);
if (rc == 0)
memcpy(device_id, &buf[8], buflen);
kfree(buf);
@@ -1104,20 +1083,20 @@ static int cciss_scsi_get_device_id(ctlr_info_t *c, unsigned char *scsi3addr,
}
static int
-cciss_scsi_do_report_phys_luns(ctlr_info_t *c,
+cciss_scsi_do_report_phys_luns(ctlr_info_t *h,
ReportLunData_struct *buf, int bufsize)
{
int rc;
- CommandList_struct *cp;
+ CommandList_struct *c;
unsigned char cdb[12];
unsigned char scsi3addr[8];
ErrorInfo_struct *ei;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- cp = scsi_cmd_alloc(c);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
- if (cp == NULL) { /* trouble... */
+ spin_lock_irqsave(&h->lock, flags);
+ c = scsi_cmd_alloc(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+ if (c == NULL) { /* trouble... */
printk("cmd_alloc returned NULL!\n");
return -1;
}
@@ -1136,27 +1115,27 @@ cciss_scsi_do_report_phys_luns(ctlr_info_t *c,
cdb[10] = 0;
cdb[11] = 0;
- rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr,
+ rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr,
cdb, 12,
(unsigned char *) buf,
bufsize, XFER_READ);
if (rc != 0) return rc; /* something went wrong */
- ei = cp->err_info;
+ ei = c->err_info;
if (ei->CommandStatus != 0 &&
ei->CommandStatus != CMD_DATA_UNDERRUN) {
- cciss_scsi_interpret_error(cp);
+ cciss_scsi_interpret_error(h, c);
rc = -1;
}
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- scsi_cmd_free(c, cp);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
+ scsi_cmd_free(h, c);
+ spin_unlock_irqrestore(&h->lock, flags);
return rc;
}
static void
-cciss_update_non_disk_devices(int cntl_num, int hostno)
+cciss_update_non_disk_devices(ctlr_info_t *h, int hostno)
{
/* the idea here is we could get notified from /proc
that some devices have changed, so we do a report
@@ -1189,7 +1168,6 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
ReportLunData_struct *ld_buff;
unsigned char *inq_buff;
unsigned char scsi3addr[8];
- ctlr_info_t *c;
__u32 num_luns=0;
unsigned char *ch;
struct cciss_scsi_dev_t *currentsd, *this_device;
@@ -1197,7 +1175,6 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
int reportlunsize = sizeof(*ld_buff) + CISS_MAX_PHYS_LUN * 8;
int i;
- c = (ctlr_info_t *) hba[cntl_num];
ld_buff = kzalloc(reportlunsize, GFP_KERNEL);
inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
currentsd = kzalloc(sizeof(*currentsd) *
@@ -1207,7 +1184,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
goto out;
}
this_device = &currentsd[CCISS_MAX_SCSI_DEVS_PER_HBA];
- if (cciss_scsi_do_report_phys_luns(c, ld_buff, reportlunsize) == 0) {
+ if (cciss_scsi_do_report_phys_luns(h, ld_buff, reportlunsize) == 0) {
ch = &ld_buff->LUNListLength[0];
num_luns = ((ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | ch[3]) / 8;
if (num_luns > CISS_MAX_PHYS_LUN) {
@@ -1231,7 +1208,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE);
memcpy(&scsi3addr[0], &ld_buff->LUN[i][0], 8);
- if (cciss_scsi_do_inquiry(hba[cntl_num], scsi3addr, 0, inq_buff,
+ if (cciss_scsi_do_inquiry(h, scsi3addr, 0, inq_buff,
(unsigned char) OBDR_TAPE_INQ_SIZE) != 0)
/* Inquiry failed (msg printed already) */
continue; /* so we will skip this device. */
@@ -1249,7 +1226,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
sizeof(this_device->revision));
memset(this_device->device_id, 0,
sizeof(this_device->device_id));
- cciss_scsi_get_device_id(hba[cntl_num], scsi3addr,
+ cciss_scsi_get_device_id(h, scsi3addr,
this_device->device_id, sizeof(this_device->device_id));
switch (this_device->devtype)
@@ -1276,7 +1253,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
case 0x08: /* medium changer */
if (ncurrent >= CCISS_MAX_SCSI_DEVS_PER_HBA) {
printk(KERN_INFO "cciss%d: %s ignored, "
- "too many devices.\n", cntl_num,
+ "too many devices.\n", h->ctlr,
scsi_device_type(this_device->devtype));
break;
}
@@ -1288,7 +1265,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
}
}
- adjust_cciss_scsi_table(cntl_num, hostno, currentsd, ncurrent);
+ adjust_cciss_scsi_table(h, hostno, currentsd, ncurrent);
out:
kfree(inq_buff);
kfree(ld_buff);
@@ -1307,12 +1284,12 @@ is_keyword(char *ptr, int len, char *verb) // Thanks to ncr53c8xx.c
}
static int
-cciss_scsi_user_command(int ctlr, int hostno, char *buffer, int length)
+cciss_scsi_user_command(ctlr_info_t *h, int hostno, char *buffer, int length)
{
int arg_len;
if ((arg_len = is_keyword(buffer, length, "rescan")) != 0)
- cciss_update_non_disk_devices(ctlr, hostno);
+ cciss_update_non_disk_devices(h, hostno);
else
return -EINVAL;
return length;
@@ -1329,20 +1306,16 @@ cciss_scsi_proc_info(struct Scsi_Host *sh,
{
int buflen, datalen;
- ctlr_info_t *ci;
+ ctlr_info_t *h;
int i;
- int cntl_num;
-
- ci = (ctlr_info_t *) sh->hostdata[0];
- if (ci == NULL) /* This really shouldn't ever happen. */
+ h = (ctlr_info_t *) sh->hostdata[0];
+ if (h == NULL) /* This really shouldn't ever happen. */
return -EINVAL;
- cntl_num = ci->ctlr; /* Get our index into the hba[] array */
-
if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */
buflen = sprintf(buffer, "cciss%d: SCSI host: %d\n",
- cntl_num, sh->host_no);
+ h->ctlr, sh->host_no);
/* this information is needed by apps to know which cciss
device corresponds to which scsi host number without
@@ -1352,8 +1325,9 @@ cciss_scsi_proc_info(struct Scsi_Host *sh,
this info is for an app to be able to use to know how to
get them back in sync. */
- for (i=0;i<ccissscsi[cntl_num].ndevices;i++) {
- struct cciss_scsi_dev_t *sd = &ccissscsi[cntl_num].dev[i];
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) {
+ struct cciss_scsi_dev_t *sd =
+ &ccissscsi[h->ctlr].dev[i];
buflen += sprintf(&buffer[buflen], "c%db%dt%dl%d %02d "
"0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
sh->host_no, sd->bus, sd->target, sd->lun,
@@ -1371,15 +1345,15 @@ cciss_scsi_proc_info(struct Scsi_Host *sh,
*start = buffer + offset;
return(datalen);
} else /* User is writing to /proc/scsi/cciss*?/?* ... */
- return cciss_scsi_user_command(cntl_num, sh->host_no,
+ return cciss_scsi_user_command(h, sh->host_no,
buffer, length);
}
/* cciss_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
dma mapping and fills in the scatter gather entries of the
- cciss command, cp. */
+ cciss command, c. */
-static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
+static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *c,
struct scsi_cmnd *cmd)
{
unsigned int len;
@@ -1393,7 +1367,7 @@ static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
chained = 0;
sg_index = 0;
- curr_sg = cp->SG;
+ curr_sg = c->SG;
request_nsgs = scsi_dma_map(cmd);
if (request_nsgs) {
scsi_for_each_sg(cmd, sg, request_nsgs, i) {
@@ -1401,7 +1375,7 @@ static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
!chained && request_nsgs - i > 1) {
chained = 1;
sg_index = 0;
- curr_sg = sa->cmd_sg_list[cp->cmdindex];
+ curr_sg = sa->cmd_sg_list[c->cmdindex];
}
addr64 = (__u64) sg_dma_address(sg);
len = sg_dma_len(sg);
@@ -1414,19 +1388,19 @@ static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
++sg_index;
}
if (chained)
- cciss_map_sg_chain_block(h, cp,
- sa->cmd_sg_list[cp->cmdindex],
+ cciss_map_sg_chain_block(h, c,
+ sa->cmd_sg_list[c->cmdindex],
(request_nsgs - (h->max_cmd_sgentries - 1)) *
sizeof(SGDescriptor_struct));
}
/* track how many SG entries we are using */
if (request_nsgs > h->maxSG)
h->maxSG = request_nsgs;
- cp->Header.SGTotal = (__u8) request_nsgs + chained;
+ c->Header.SGTotal = (__u8) request_nsgs + chained;
if (request_nsgs > h->max_cmd_sgentries)
- cp->Header.SGList = h->max_cmd_sgentries;
+ c->Header.SGList = h->max_cmd_sgentries;
else
- cp->Header.SGList = cp->Header.SGTotal;
+ c->Header.SGList = c->Header.SGTotal;
return;
}
@@ -1434,18 +1408,17 @@ static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
static int
cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
{
- ctlr_info_t *c;
- int ctlr, rc;
+ ctlr_info_t *h;
+ int rc;
unsigned char scsi3addr[8];
- CommandList_struct *cp;
+ CommandList_struct *c;
unsigned long flags;
// 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;
+ h = (ctlr_info_t *) cmd->device->host->hostdata[0];
- rc = lookup_scsi3addr(ctlr, cmd->device->channel, cmd->device->id,
+ rc = lookup_scsi3addr(h, cmd->device->channel, cmd->device->id,
cmd->device->lun, scsi3addr);
if (rc != 0) {
/* the scsi nexus does not match any that we presented... */
@@ -1457,19 +1430,14 @@ cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd
return 0;
}
- /* printk("cciss_queue_command, p=%p, cmd=0x%02x, c%db%dt%dl%d\n",
- cmd, cmd->cmnd[0], ctlr, cmd->channel, cmd->target, cmd->lun);*/
- // printk("q:%p:c%db%dt%dl%d ", cmd, ctlr, cmd->channel,
- // cmd->target, cmd->lun);
-
/* Ok, we have a reasonable scsi nexus, so send the cmd down, and
see what the device thinks of it. */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- cp = scsi_cmd_alloc(c);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- if (cp == NULL) { /* trouble... */
- printk("scsi_cmd_alloc returned NULL!\n");
+ spin_lock_irqsave(&h->lock, flags);
+ c = scsi_cmd_alloc(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+ if (c == NULL) { /* trouble... */
+ dev_warn(&h->pdev->dev, "scsi_cmd_alloc returned NULL!\n");
/* FIXME: next 3 lines are -> BAD! <- */
cmd->result = DID_NO_CONNECT << 16;
done(cmd);
@@ -1480,35 +1448,41 @@ cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd
cmd->scsi_done = done; // save this for use by completion code
- // save cp in case we have to abort it
- cmd->host_scribble = (unsigned char *) cp;
+ /* save c in case we have to abort it */
+ cmd->host_scribble = (unsigned char *) c;
- cp->cmd_type = CMD_SCSI;
- cp->scsi_cmd = cmd;
- cp->Header.ReplyQueue = 0; // unused in simple mode
- memcpy(&cp->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
- cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag
+ c->cmd_type = CMD_SCSI;
+ 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 */
// Fill in the request block...
- cp->Request.Timeout = 0;
- memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB));
- BUG_ON(cmd->cmd_len > sizeof(cp->Request.CDB));
- cp->Request.CDBLen = cmd->cmd_len;
- memcpy(cp->Request.CDB, cmd->cmnd, cmd->cmd_len);
- cp->Request.Type.Type = TYPE_CMD;
- cp->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Timeout = 0;
+ memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+ BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB));
+ c->Request.CDBLen = cmd->cmd_len;
+ memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len);
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
switch(cmd->sc_data_direction)
{
- case DMA_TO_DEVICE: cp->Request.Type.Direction = XFER_WRITE; break;
- case DMA_FROM_DEVICE: cp->Request.Type.Direction = XFER_READ; break;
- case DMA_NONE: cp->Request.Type.Direction = XFER_NONE; break;
+ case DMA_TO_DEVICE:
+ c->Request.Type.Direction = XFER_WRITE;
+ break;
+ case DMA_FROM_DEVICE:
+ c->Request.Type.Direction = XFER_READ;
+ break;
+ case DMA_NONE:
+ c->Request.Type.Direction = XFER_NONE;
+ break;
case DMA_BIDIRECTIONAL:
// This can happen if a buggy application does a scsi passthru
// and sets both inlen and outlen to non-zero. ( see
// ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() )
- cp->Request.Type.Direction = XFER_RSVD;
+ c->Request.Type.Direction = XFER_RSVD;
// This is technically wrong, and cciss controllers should
// reject it with CMD_INVALID, which is the most correct
// response, but non-fibre backends appear to let it
@@ -1519,27 +1493,18 @@ cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd
break;
default:
- printk("cciss: unknown data direction: %d\n",
+ dev_warn(&h->pdev->dev, "unknown data direction: %d\n",
cmd->sc_data_direction);
BUG();
break;
}
- 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);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-
+ cciss_scatter_gather(h, c, cmd);
+ enqueue_cmd_and_start_io(h, c);
/* the cmd'll come back via intr handler in complete_scsi_command() */
return 0;
}
-static void
-cciss_unregister_scsi(int ctlr)
+static void cciss_unregister_scsi(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
@@ -1547,59 +1512,58 @@ cciss_unregister_scsi(int ctlr)
/* we are being forcibly unloaded, and may not refuse. */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- sa = hba[ctlr]->scsi_ctlr;
+ spin_lock_irqsave(&h->lock, flags);
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
/* if we weren't ever actually registered, don't unregister */
if (sa->registered) {
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
scsi_remove_host(sa->scsi_host);
scsi_host_put(sa->scsi_host);
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
}
/* set scsi_host to NULL so our detect routine will
find us on register */
sa->scsi_host = NULL;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- scsi_cmd_stack_free(ctlr);
+ spin_unlock_irqrestore(&h->lock, flags);
+ scsi_cmd_stack_free(h);
kfree(sa);
}
-static int
-cciss_engage_scsi(int ctlr)
+static int cciss_engage_scsi(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- sa = hba[ctlr]->scsi_ctlr;
+ spin_lock_irqsave(&h->lock, flags);
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
if (sa->registered) {
- printk("cciss%d: SCSI subsystem already engaged.\n", ctlr);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ dev_info(&h->pdev->dev, "SCSI subsystem already engaged.\n");
+ spin_unlock_irqrestore(&h->lock, flags);
return -ENXIO;
}
sa->registered = 1;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- cciss_update_non_disk_devices(ctlr, -1);
- cciss_scsi_detect(ctlr);
+ spin_unlock_irqrestore(&h->lock, flags);
+ cciss_update_non_disk_devices(h, -1);
+ cciss_scsi_detect(h);
return 0;
}
static void
-cciss_seq_tape_report(struct seq_file *seq, int ctlr)
+cciss_seq_tape_report(struct seq_file *seq, ctlr_info_t *h)
{
unsigned long flags;
- CPQ_TAPE_LOCK(ctlr, flags);
+ CPQ_TAPE_LOCK(h, flags);
seq_printf(seq,
"Sequential access devices: %d\n\n",
- ccissscsi[ctlr].ndevices);
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ ccissscsi[h->ctlr].ndevices);
+ CPQ_TAPE_UNLOCK(h, flags);
}
static int wait_for_device_to_become_ready(ctlr_info_t *h,
@@ -1610,10 +1574,10 @@ static int wait_for_device_to_become_ready(ctlr_info_t *h,
int waittime = HZ;
CommandList_struct *c;
- c = cmd_alloc(h, 1);
+ c = cmd_alloc(h);
if (!c) {
- printk(KERN_WARNING "cciss%d: out of memory in "
- "wait_for_device_to_become_ready.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "out of memory in "
+ "wait_for_device_to_become_ready.\n");
return IO_ERROR;
}
@@ -1631,7 +1595,7 @@ static int wait_for_device_to_become_ready(ctlr_info_t *h,
waittime = waittime * 2;
/* Send the Test Unit Ready */
- rc = fill_cmd(c, TEST_UNIT_READY, h->ctlr, NULL, 0, 0,
+ rc = fill_cmd(h, c, TEST_UNIT_READY, NULL, 0, 0,
lunaddr, TYPE_CMD);
if (rc == 0)
rc = sendcmd_withirq_core(h, c, 0);
@@ -1657,18 +1621,18 @@ static int wait_for_device_to_become_ready(ctlr_info_t *h,
}
}
retry_tur:
- printk(KERN_WARNING "cciss%d: Waiting %d secs "
+ dev_warn(&h->pdev->dev, "Waiting %d secs "
"for device to become ready.\n",
- h->ctlr, waittime / HZ);
+ waittime / HZ);
rc = 1; /* device not ready. */
}
if (rc)
- printk("cciss%d: giving up on device.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "giving up on device.\n");
else
- printk(KERN_WARNING "cciss%d: device is ready.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "device is ready.\n");
- cmd_free(h, c, 1);
+ cmd_free(h, c);
return rc;
}
@@ -1688,26 +1652,24 @@ 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;
- int ctlr;
+ ctlr_info_t *h;
/* find the controller to which the command to be aborted was sent */
- c = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
- if (c == NULL) /* paranoia */
+ h = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
+ if (h == NULL) /* paranoia */
return FAILED;
- ctlr = c->ctlr;
- printk(KERN_WARNING "cciss%d: resetting tape drive or medium changer.\n", ctlr);
+ dev_warn(&h->pdev->dev, "resetting tape drive or medium changer.\n");
/* find the command that's giving us trouble */
cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble;
if (cmd_in_trouble == NULL) /* paranoia */
return FAILED;
memcpy(lunaddr, &cmd_in_trouble->Header.LUN.LunAddrBytes[0], 8);
/* send a reset to the SCSI LUN which the command was sent to */
- rc = sendcmd_withirq(CCISS_RESET_MSG, ctlr, NULL, 0, 0, lunaddr,
+ rc = sendcmd_withirq(h, CCISS_RESET_MSG, 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(h, lunaddr) == 0)
return SUCCESS;
- printk(KERN_WARNING "cciss%d: resetting device failed.\n", ctlr);
+ dev_warn(&h->pdev->dev, "resetting device failed.\n");
return FAILED;
}
@@ -1716,22 +1678,20 @@ 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;
- int ctlr;
+ ctlr_info_t *h;
/* find the controller to which the command to be aborted was sent */
- c = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
- if (c == NULL) /* paranoia */
+ h = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
+ if (h == NULL) /* paranoia */
return FAILED;
- ctlr = c->ctlr;
- printk(KERN_WARNING "cciss%d: aborting tardy SCSI cmd\n", ctlr);
+ dev_warn(&h->pdev->dev, "aborting tardy SCSI cmd\n");
/* find the command to be aborted */
cmd_to_abort = (CommandList_struct *) scsicmd->host_scribble;
if (cmd_to_abort == NULL) /* paranoia */
return FAILED;
memcpy(lunaddr, &cmd_to_abort->Header.LUN.LunAddrBytes[0], 8);
- rc = sendcmd_withirq(CCISS_ABORT_MSG, ctlr, &cmd_to_abort->Header.Tag,
+ rc = sendcmd_withirq(h, CCISS_ABORT_MSG, &cmd_to_abort->Header.Tag,
0, 0, lunaddr, TYPE_MSG);
if (rc == 0)
return SUCCESS;
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index abb4ec6..d53b029 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -35,6 +35,7 @@
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/hdreg.h>
+#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
@@ -157,7 +158,7 @@ static int sendcmd(
unsigned int blkcnt,
unsigned int log_unit );
-static int ida_open(struct block_device *bdev, fmode_t mode);
+static int ida_unlocked_open(struct block_device *bdev, fmode_t mode);
static int ida_release(struct gendisk *disk, fmode_t mode);
static int ida_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg);
static int ida_getgeo(struct block_device *bdev, struct hd_geometry *geo);
@@ -195,9 +196,9 @@ static inline ctlr_info_t *get_host(struct gendisk *disk)
static const struct block_device_operations ida_fops = {
.owner = THIS_MODULE,
- .open = ida_open,
+ .open = ida_unlocked_open,
.release = ida_release,
- .locked_ioctl = ida_ioctl,
+ .ioctl = ida_ioctl,
.getgeo = ida_getgeo,
.revalidate_disk= ida_revalidate,
};
@@ -840,13 +841,29 @@ static int ida_open(struct block_device *bdev, fmode_t mode)
return 0;
}
+static int ida_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ida_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
/*
* Close. Sync first.
*/
static int ida_release(struct gendisk *disk, fmode_t mode)
{
- ctlr_info_t *host = get_host(disk);
+ ctlr_info_t *host;
+
+ lock_kernel();
+ host = get_host(disk);
host->usage_count--;
+ unlock_kernel();
+
return 0;
}
@@ -1128,7 +1145,7 @@ static int ida_getgeo(struct block_device *bdev, struct hd_geometry *geo)
* ida_ioctl does some miscellaneous stuff like reporting drive geometry,
* setting readahead and submitting commands from userspace to the controller.
*/
-static int ida_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
+static int ida_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
drv_info_t *drv = get_drv(bdev->bd_disk);
ctlr_info_t *host = get_host(bdev->bd_disk);
@@ -1162,7 +1179,8 @@ out_passthru:
return error;
case IDAGETCTLRSIG:
if (!arg) return -EINVAL;
- put_user(host->ctlr_sig, (int __user *)arg);
+ if (put_user(host->ctlr_sig, (int __user *)arg))
+ return -EFAULT;
return 0;
case IDAREVALIDATEVOLS:
if (MINOR(bdev->bd_dev) != 0)
@@ -1170,7 +1188,8 @@ out_passthru:
return revalidate_allvol(host);
case IDADRIVERVERSION:
if (!arg) return -EINVAL;
- put_user(DRIVER_VERSION, (unsigned long __user *)arg);
+ if (put_user(DRIVER_VERSION, (unsigned long __user *)arg))
+ return -EFAULT;
return 0;
case IDAGETPCIINFO:
{
@@ -1192,6 +1211,19 @@ out_passthru:
}
}
+
+static int ida_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ida_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
/*
* ida_ctlr_ioctl is for passing commands to the controller from userspace.
* The command block (io) has already been copied to kernel space for us,
@@ -1225,17 +1257,11 @@ static int ida_ctlr_ioctl(ctlr_info_t *h, int dsk, ida_ioctl_t *io)
/* Pre submit processing */
switch(io->cmd) {
case PASSTHRU_A:
- p = kmalloc(io->sg[0].size, GFP_KERNEL);
- if (!p)
- {
- error = -ENOMEM;
- cmd_free(h, c, 0);
- return(error);
- }
- if (copy_from_user(p, io->sg[0].addr, io->sg[0].size)) {
- kfree(p);
- cmd_free(h, c, 0);
- return -EFAULT;
+ p = memdup_user(io->sg[0].addr, io->sg[0].size);
+ if (IS_ERR(p)) {
+ error = PTR_ERR(p);
+ cmd_free(h, c, 0);
+ return error;
}
c->req.hdr.blk = pci_map_single(h->pci_dev, &(io->c),
sizeof(ida_ioctl_t),
@@ -1266,18 +1292,12 @@ static int ida_ctlr_ioctl(ctlr_info_t *h, int dsk, ida_ioctl_t *io)
case DIAG_PASS_THRU:
case COLLECT_BUFFER:
case WRITE_FLASH_ROM:
- p = kmalloc(io->sg[0].size, GFP_KERNEL);
- if (!p)
- {
- error = -ENOMEM;
- cmd_free(h, c, 0);
- return(error);
+ p = memdup_user(io->sg[0].addr, io->sg[0].size);
+ if (IS_ERR(p)) {
+ error = PTR_ERR(p);
+ cmd_free(h, c, 0);
+ return error;
}
- if (copy_from_user(p, io->sg[0].addr, io->sg[0].size)) {
- kfree(p);
- cmd_free(h, c, 0);
- return -EFAULT;
- }
c->req.sg[0].size = io->sg[0].size;
c->req.sg[0].addr = pci_map_single(h->pci_dev, p,
c->req.sg[0].size, PCI_DMA_BIDIRECTIONAL);
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index df01899..9400845 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -79,8 +79,8 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
md_io.error = 0;
if ((rw & WRITE) && !test_bit(MD_NO_BARRIER, &mdev->flags))
- rw |= (1 << BIO_RW_BARRIER);
- rw |= ((1<<BIO_RW_UNPLUG) | (1<<BIO_RW_SYNCIO));
+ rw |= REQ_HARDBARRIER;
+ rw |= REQ_UNPLUG | REQ_SYNC;
retry:
bio = bio_alloc(GFP_NOIO, 1);
@@ -103,11 +103,11 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
/* check for unsupported barrier op.
* would rather check on EOPNOTSUPP, but that is not reliable.
* don't try again for ANY return value != 0 */
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && !ok)) {
+ if (unlikely((bio->bi_rw & REQ_HARDBARRIER) && !ok)) {
/* Try again with no barrier */
dev_warn(DEV, "Barriers not supported on meta data device - disabling\n");
set_bit(MD_NO_BARRIER, &mdev->flags);
- rw &= ~(1 << BIO_RW_BARRIER);
+ rw &= ~REQ_HARDBARRIER;
bio_put(bio);
goto retry;
}
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 485ed8c..352441b 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -550,12 +550,6 @@ struct p_delay_probe {
u32 offset; /* usecs the probe got sent after the reference time point */
} __packed;
-struct delay_probe {
- struct list_head list;
- unsigned int seq_num;
- struct timeval time;
-};
-
/* DCBP: Drbd Compressed Bitmap Packet ... */
static inline enum drbd_bitmap_code
DCBP_get_code(struct p_compressed_bm *p)
@@ -942,11 +936,9 @@ struct drbd_conf {
unsigned int ko_count;
struct drbd_work resync_work,
unplug_work,
- md_sync_work,
- delay_probe_work;
+ md_sync_work;
struct timer_list resync_timer;
struct timer_list md_sync_timer;
- struct timer_list delay_probe_timer;
/* Used after attach while negotiating new disk state. */
union drbd_state new_state_tmp;
@@ -1062,12 +1054,6 @@ struct drbd_conf {
u64 ed_uuid; /* UUID of the exposed data */
struct mutex state_mutex;
char congestion_reason; /* Why we where congested... */
- struct list_head delay_probes; /* protected by peer_seq_lock */
- int data_delay; /* Delay of packets on the data-sock behind meta-sock */
- unsigned int delay_seq; /* To generate sequence numbers of delay probes */
- struct timeval dps_time; /* delay-probes-start-time */
- unsigned int dp_volume_last; /* send_cnt of last delay probe */
- int c_sync_rate; /* current resync rate after delay_probe magic */
};
static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 7258c95..fa650dd 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2184,43 +2184,6 @@ int drbd_send_ov_request(struct drbd_conf *mdev, sector_t sector, int size)
return ok;
}
-static int drbd_send_delay_probe(struct drbd_conf *mdev, struct drbd_socket *ds)
-{
- struct p_delay_probe dp;
- int offset, ok = 0;
- struct timeval now;
-
- mutex_lock(&ds->mutex);
- if (likely(ds->socket)) {
- do_gettimeofday(&now);
- offset = now.tv_usec - mdev->dps_time.tv_usec +
- (now.tv_sec - mdev->dps_time.tv_sec) * 1000000;
- dp.seq_num = cpu_to_be32(mdev->delay_seq);
- dp.offset = cpu_to_be32(offset);
-
- ok = _drbd_send_cmd(mdev, ds->socket, P_DELAY_PROBE,
- (struct p_header *)&dp, sizeof(dp), 0);
- }
- mutex_unlock(&ds->mutex);
-
- return ok;
-}
-
-static int drbd_send_delay_probes(struct drbd_conf *mdev)
-{
- int ok;
-
- mdev->delay_seq++;
- do_gettimeofday(&mdev->dps_time);
- ok = drbd_send_delay_probe(mdev, &mdev->meta);
- ok = ok && drbd_send_delay_probe(mdev, &mdev->data);
-
- mdev->dp_volume_last = mdev->send_cnt;
- mod_timer(&mdev->delay_probe_timer, jiffies + mdev->sync_conf.dp_interval * HZ / 10);
-
- return ok;
-}
-
/* called on sndtimeo
* returns FALSE if we should retry,
* TRUE if we think connection is dead
@@ -2369,31 +2332,6 @@ static int _drbd_send_zc_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e)
return 1;
}
-static void consider_delay_probes(struct drbd_conf *mdev)
-{
- if (mdev->state.conn != C_SYNC_SOURCE || mdev->agreed_pro_version < 93)
- return;
-
- if (mdev->dp_volume_last + mdev->sync_conf.dp_volume * 2 < mdev->send_cnt)
- drbd_send_delay_probes(mdev);
-}
-
-static int w_delay_probes(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
-{
- if (!cancel && mdev->state.conn == C_SYNC_SOURCE)
- drbd_send_delay_probes(mdev);
-
- return 1;
-}
-
-static void delay_probe_timer_fn(unsigned long data)
-{
- struct drbd_conf *mdev = (struct drbd_conf *) data;
-
- if (list_empty(&mdev->delay_probe_work.list))
- drbd_queue_work(&mdev->data.work, &mdev->delay_probe_work);
-}
-
/* Used to send write requests
* R_PRIMARY -> Peer (P_DATA)
*/
@@ -2425,15 +2363,15 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
/* NOTE: no need to check if barriers supported here as we would
* not pass the test in make_request_common in that case
*/
- if (bio_rw_flagged(req->master_bio, BIO_RW_BARRIER)) {
+ if (req->master_bio->bi_rw & REQ_HARDBARRIER) {
dev_err(DEV, "ASSERT FAILED would have set DP_HARDBARRIER\n");
/* dp_flags |= DP_HARDBARRIER; */
}
- if (bio_rw_flagged(req->master_bio, BIO_RW_SYNCIO))
+ if (req->master_bio->bi_rw & REQ_SYNC)
dp_flags |= DP_RW_SYNC;
/* for now handle SYNCIO and UNPLUG
* as if they still were one and the same flag */
- if (bio_rw_flagged(req->master_bio, BIO_RW_UNPLUG))
+ if (req->master_bio->bi_rw & REQ_UNPLUG)
dp_flags |= DP_RW_SYNC;
if (mdev->state.conn >= C_SYNC_SOURCE &&
mdev->state.conn <= C_PAUSED_SYNC_T)
@@ -2457,9 +2395,6 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
drbd_put_data_sock(mdev);
- if (ok)
- consider_delay_probes(mdev);
-
return ok;
}
@@ -2506,9 +2441,6 @@ int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd,
drbd_put_data_sock(mdev);
- if (ok)
- consider_delay_probes(mdev);
-
return ok;
}
@@ -2604,6 +2536,7 @@ static int drbd_open(struct block_device *bdev, fmode_t mode)
unsigned long flags;
int rv = 0;
+ lock_kernel();
spin_lock_irqsave(&mdev->req_lock, flags);
/* to have a stable mdev->state.role
* and no race with updating open_cnt */
@@ -2618,6 +2551,7 @@ static int drbd_open(struct block_device *bdev, fmode_t mode)
if (!rv)
mdev->open_cnt++;
spin_unlock_irqrestore(&mdev->req_lock, flags);
+ unlock_kernel();
return rv;
}
@@ -2625,7 +2559,9 @@ static int drbd_open(struct block_device *bdev, fmode_t mode)
static int drbd_release(struct gendisk *gd, fmode_t mode)
{
struct drbd_conf *mdev = gd->private_data;
+ lock_kernel();
mdev->open_cnt--;
+ unlock_kernel();
return 0;
}
@@ -2660,9 +2596,20 @@ static void drbd_unplug_fn(struct request_queue *q)
static void drbd_set_defaults(struct drbd_conf *mdev)
{
- mdev->sync_conf.after = DRBD_AFTER_DEF;
- mdev->sync_conf.rate = DRBD_RATE_DEF;
- mdev->sync_conf.al_extents = DRBD_AL_EXTENTS_DEF;
+ /* This way we get a compile error when sync_conf grows,
+ and we forgot to initialize it here */
+ mdev->sync_conf = (struct syncer_conf) {
+ /* .rate = */ DRBD_RATE_DEF,
+ /* .after = */ DRBD_AFTER_DEF,
+ /* .al_extents = */ DRBD_AL_EXTENTS_DEF,
+ /* .verify_alg = */ {}, 0,
+ /* .cpu_mask = */ {}, 0,
+ /* .csums_alg = */ {}, 0,
+ /* .use_rle = */ 0
+ };
+
+ /* Have to use that way, because the layout differs between
+ big endian and little endian */
mdev->state = (union drbd_state) {
{ .role = R_SECONDARY,
.peer = R_UNKNOWN,
@@ -2721,24 +2668,17 @@ void drbd_init_set_defaults(struct drbd_conf *mdev)
INIT_LIST_HEAD(&mdev->unplug_work.list);
INIT_LIST_HEAD(&mdev->md_sync_work.list);
INIT_LIST_HEAD(&mdev->bm_io_work.w.list);
- INIT_LIST_HEAD(&mdev->delay_probes);
- INIT_LIST_HEAD(&mdev->delay_probe_work.list);
mdev->resync_work.cb = w_resync_inactive;
mdev->unplug_work.cb = w_send_write_hint;
mdev->md_sync_work.cb = w_md_sync;
mdev->bm_io_work.w.cb = w_bitmap_io;
- mdev->delay_probe_work.cb = w_delay_probes;
init_timer(&mdev->resync_timer);
init_timer(&mdev->md_sync_timer);
- init_timer(&mdev->delay_probe_timer);
mdev->resync_timer.function = resync_timer_fn;
mdev->resync_timer.data = (unsigned long) mdev;
mdev->md_sync_timer.function = md_sync_timer_fn;
mdev->md_sync_timer.data = (unsigned long) mdev;
- mdev->delay_probe_timer.function = delay_probe_timer_fn;
- mdev->delay_probe_timer.data = (unsigned long) mdev;
-
init_waitqueue_head(&mdev->misc_wait);
init_waitqueue_head(&mdev->state_wait);
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 2151f18..73131c5 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1557,10 +1557,6 @@ static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
sc.rate = DRBD_RATE_DEF;
sc.after = DRBD_AFTER_DEF;
sc.al_extents = DRBD_AL_EXTENTS_DEF;
- sc.dp_volume = DRBD_DP_VOLUME_DEF;
- sc.dp_interval = DRBD_DP_INTERVAL_DEF;
- sc.throttle_th = DRBD_RS_THROTTLE_TH_DEF;
- sc.hold_off_th = DRBD_RS_HOLD_OFF_TH_DEF;
} else
memcpy(&sc, &mdev->sync_conf, sizeof(struct syncer_conf));
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index d0f1767..be3374b 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -73,21 +73,14 @@ static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
seq_printf(seq, "sync'ed:%3u.%u%% ", res / 10, res % 10);
/* if more than 1 GB display in MB */
if (mdev->rs_total > 0x100000L)
- seq_printf(seq, "(%lu/%lu)M",
+ seq_printf(seq, "(%lu/%lu)M\n\t",
(unsigned long) Bit2KB(rs_left >> 10),
(unsigned long) Bit2KB(mdev->rs_total >> 10));
else
- seq_printf(seq, "(%lu/%lu)K",
+ seq_printf(seq, "(%lu/%lu)K\n\t",
(unsigned long) Bit2KB(rs_left),
(unsigned long) Bit2KB(mdev->rs_total));
- if (mdev->state.conn == C_SYNC_TARGET)
- seq_printf(seq, " queue_delay: %d.%d ms\n\t",
- mdev->data_delay / 1000,
- (mdev->data_delay % 1000) / 100);
- else if (mdev->state.conn == C_SYNC_SOURCE)
- seq_printf(seq, " delay_probe: %u\n\t", mdev->delay_seq);
-
/* see drivers/md/md.c
* We do not want to overflow, so the order of operands and
* the * 100 / 100 trick are important. We do a +1 to be
@@ -135,14 +128,6 @@ static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
else
seq_printf(seq, " (%ld)", dbdt);
- if (mdev->state.conn == C_SYNC_TARGET) {
- if (mdev->c_sync_rate > 1000)
- seq_printf(seq, " want: %d,%03d",
- mdev->c_sync_rate / 1000, mdev->c_sync_rate % 1000);
- else
- seq_printf(seq, " want: %d", mdev->c_sync_rate);
- }
-
seq_printf(seq, " K/sec\n");
}
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index ec1711f..081522d 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -1180,7 +1180,7 @@ next_bio:
bio->bi_sector = sector;
bio->bi_bdev = mdev->ldev->backing_bdev;
/* we special case some flags in the multi-bio case, see below
- * (BIO_RW_UNPLUG, BIO_RW_BARRIER) */
+ * (REQ_UNPLUG, REQ_HARDBARRIER) */
bio->bi_rw = rw;
bio->bi_private = e;
bio->bi_end_io = drbd_endio_sec;
@@ -1209,16 +1209,16 @@ next_bio:
bios = bios->bi_next;
bio->bi_next = NULL;
- /* strip off BIO_RW_UNPLUG unless it is the last bio */
+ /* strip off REQ_UNPLUG unless it is the last bio */
if (bios)
- bio->bi_rw &= ~(1<<BIO_RW_UNPLUG);
+ bio->bi_rw &= ~REQ_UNPLUG;
drbd_generic_make_request(mdev, fault_type, bio);
- /* strip off BIO_RW_BARRIER,
+ /* strip off REQ_HARDBARRIER,
* unless it is the first or last bio */
if (bios && bios->bi_next)
- bios->bi_rw &= ~(1<<BIO_RW_BARRIER);
+ bios->bi_rw &= ~REQ_HARDBARRIER;
} while (bios);
maybe_kick_lo(mdev);
return 0;
@@ -1233,7 +1233,7 @@ fail:
}
/**
- * w_e_reissue() - Worker callback; Resubmit a bio, without BIO_RW_BARRIER set
+ * w_e_reissue() - Worker callback; Resubmit a bio, without REQ_HARDBARRIER set
* @mdev: DRBD device.
* @w: work object.
* @cancel: The connection will be closed anyways (unused in this callback)
@@ -1245,7 +1245,7 @@ int w_e_reissue(struct drbd_conf *mdev, struct drbd_work *w, int cancel) __relea
(and DE_BARRIER_IN_NEXT_EPOCH_ISSUED in the previous Epoch)
so that we can finish that epoch in drbd_may_finish_epoch().
That is necessary if we already have a long chain of Epochs, before
- we realize that BIO_RW_BARRIER is actually not supported */
+ we realize that REQ_HARDBARRIER is actually not supported */
/* As long as the -ENOTSUPP on the barrier is reported immediately
that will never trigger. If it is reported late, we will just
@@ -1824,14 +1824,14 @@ static int receive_Data(struct drbd_conf *mdev, struct p_header *h)
epoch = list_entry(e->epoch->list.prev, struct drbd_epoch, list);
if (epoch == e->epoch) {
set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags);
- rw |= (1<<BIO_RW_BARRIER);
+ rw |= REQ_HARDBARRIER;
e->flags |= EE_IS_BARRIER;
} else {
if (atomic_read(&epoch->epoch_size) > 1 ||
!test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) {
set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags);
set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags);
- rw |= (1<<BIO_RW_BARRIER);
+ rw |= REQ_HARDBARRIER;
e->flags |= EE_IS_BARRIER;
}
}
@@ -1841,10 +1841,10 @@ static int receive_Data(struct drbd_conf *mdev, struct p_header *h)
dp_flags = be32_to_cpu(p->dp_flags);
if (dp_flags & DP_HARDBARRIER) {
dev_err(DEV, "ASSERT FAILED would have submitted barrier request\n");
- /* rw |= (1<<BIO_RW_BARRIER); */
+ /* rw |= REQ_HARDBARRIER; */
}
if (dp_flags & DP_RW_SYNC)
- rw |= (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
if (dp_flags & DP_MAY_SET_IN_SYNC)
e->flags |= EE_MAY_SET_IN_SYNC;
@@ -3555,14 +3555,15 @@ static int receive_bitmap(struct drbd_conf *mdev, struct p_header *h)
return ok;
}
-static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
+static int receive_skip_(struct drbd_conf *mdev, struct p_header *h, int silent)
{
/* TODO zero copy sink :) */
static char sink[128];
int size, want, r;
- dev_warn(DEV, "skipping unknown optional packet type %d, l: %d!\n",
- h->command, h->length);
+ if (!silent)
+ dev_warn(DEV, "skipping unknown optional packet type %d, l: %d!\n",
+ h->command, h->length);
size = h->length;
while (size > 0) {
@@ -3574,101 +3575,25 @@ static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
return size == 0;
}
-static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
-{
- if (mdev->state.disk >= D_INCONSISTENT)
- drbd_kick_lo(mdev);
-
- /* Make sure we've acked all the TCP data associated
- * with the data requests being unplugged */
- drbd_tcp_quickack(mdev->data.socket);
-
- return TRUE;
-}
-
-static void timeval_sub_us(struct timeval* tv, unsigned int us)
+static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
{
- tv->tv_sec -= us / 1000000;
- us = us % 1000000;
- if (tv->tv_usec > us) {
- tv->tv_usec += 1000000;
- tv->tv_sec--;
- }
- tv->tv_usec -= us;
+ return receive_skip_(mdev, h, 0);
}
-static void got_delay_probe(struct drbd_conf *mdev, int from, struct p_delay_probe *p)
+static int receive_skip_silent(struct drbd_conf *mdev, struct p_header *h)
{
- struct delay_probe *dp;
- struct list_head *le;
- struct timeval now;
- int seq_num;
- int offset;
- int data_delay;
-
- seq_num = be32_to_cpu(p->seq_num);
- offset = be32_to_cpu(p->offset);
-
- spin_lock(&mdev->peer_seq_lock);
- if (!list_empty(&mdev->delay_probes)) {
- if (from == USE_DATA_SOCKET)
- le = mdev->delay_probes.next;
- else
- le = mdev->delay_probes.prev;
-
- dp = list_entry(le, struct delay_probe, list);
-
- if (dp->seq_num == seq_num) {
- list_del(le);
- spin_unlock(&mdev->peer_seq_lock);
- do_gettimeofday(&now);
- timeval_sub_us(&now, offset);
- data_delay =
- now.tv_usec - dp->time.tv_usec +
- (now.tv_sec - dp->time.tv_sec) * 1000000;
-
- if (data_delay > 0)
- mdev->data_delay = data_delay;
-
- kfree(dp);
- return;
- }
-
- if (dp->seq_num > seq_num) {
- spin_unlock(&mdev->peer_seq_lock);
- dev_warn(DEV, "Previous allocation failure of struct delay_probe?\n");
- return; /* Do not alloca a struct delay_probe.... */
- }
- }
- spin_unlock(&mdev->peer_seq_lock);
-
- dp = kmalloc(sizeof(struct delay_probe), GFP_NOIO);
- if (!dp) {
- dev_warn(DEV, "Failed to allocate a struct delay_probe, do not worry.\n");
- return;
- }
-
- dp->seq_num = seq_num;
- do_gettimeofday(&dp->time);
- timeval_sub_us(&dp->time, offset);
-
- spin_lock(&mdev->peer_seq_lock);
- if (from == USE_DATA_SOCKET)
- list_add(&dp->list, &mdev->delay_probes);
- else
- list_add_tail(&dp->list, &mdev->delay_probes);
- spin_unlock(&mdev->peer_seq_lock);
+ return receive_skip_(mdev, h, 1);
}
-static int receive_delay_probe(struct drbd_conf *mdev, struct p_header *h)
+static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
{
- struct p_delay_probe *p = (struct p_delay_probe *)h;
+ if (mdev->state.disk >= D_INCONSISTENT)
+ drbd_kick_lo(mdev);
- ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
- if (drbd_recv(mdev, h->payload, h->length) != h->length)
- return FALSE;
+ /* Make sure we've acked all the TCP data associated
+ * with the data requests being unplugged */
+ drbd_tcp_quickack(mdev->data.socket);
- got_delay_probe(mdev, USE_DATA_SOCKET, p);
return TRUE;
}
@@ -3695,7 +3620,7 @@ static drbd_cmd_handler_f drbd_default_handler[] = {
[P_OV_REQUEST] = receive_DataRequest,
[P_OV_REPLY] = receive_DataRequest,
[P_CSUM_RS_REQUEST] = receive_DataRequest,
- [P_DELAY_PROBE] = receive_delay_probe,
+ [P_DELAY_PROBE] = receive_skip_silent,
/* anything missing from this table is in
* the asender_tbl, see get_asender_cmd */
[P_MAX_CMD] = NULL,
@@ -4472,11 +4397,9 @@ static int got_OVResult(struct drbd_conf *mdev, struct p_header *h)
return TRUE;
}
-static int got_delay_probe_m(struct drbd_conf *mdev, struct p_header *h)
+static int got_something_to_ignore_m(struct drbd_conf *mdev, struct p_header *h)
{
- struct p_delay_probe *p = (struct p_delay_probe *)h;
-
- got_delay_probe(mdev, USE_META_SOCKET, p);
+ /* IGNORE */
return TRUE;
}
@@ -4504,7 +4427,7 @@ static struct asender_cmd *get_asender_cmd(int cmd)
[P_BARRIER_ACK] = { sizeof(struct p_barrier_ack), got_BarrierAck },
[P_STATE_CHG_REPLY] = { sizeof(struct p_req_state_reply), got_RqSReply },
[P_RS_IS_IN_SYNC] = { sizeof(struct p_block_ack), got_IsInSync },
- [P_DELAY_PROBE] = { sizeof(struct p_delay_probe), got_delay_probe_m },
+ [P_DELAY_PROBE] = { sizeof(struct p_delay_probe), got_something_to_ignore_m },
[P_MAX_CMD] = { 0, NULL },
};
if (cmd > P_MAX_CMD || asender_tbl[cmd].process == NULL)
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 654f1ef..f761d98 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -997,7 +997,7 @@ int drbd_make_request_26(struct request_queue *q, struct bio *bio)
* because of those XXX, this is not yet enabled,
* i.e. in drbd_init_set_defaults we set the NO_BARRIER_SUPP bit.
*/
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags)) {
/* dev_warn(DEV, "Rejecting barrier request as underlying device does not support\n"); */
bio_endio(bio, -EOPNOTSUPP);
return 0;
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index b623cee..ca4a16c 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -424,18 +424,6 @@ void resync_timer_fn(unsigned long data)
drbd_queue_work(&mdev->data.work, &mdev->resync_work);
}
-static int calc_resync_rate(struct drbd_conf *mdev)
-{
- int d = mdev->data_delay / 1000; /* us -> ms */
- int td = mdev->sync_conf.throttle_th * 100; /* 0.1s -> ms */
- int hd = mdev->sync_conf.hold_off_th * 100; /* 0.1s -> ms */
- int cr = mdev->sync_conf.rate;
-
- return d <= td ? cr :
- d >= hd ? 0 :
- cr + (cr * (td - d) / (hd - td));
-}
-
int w_make_resync_request(struct drbd_conf *mdev,
struct drbd_work *w, int cancel)
{
@@ -473,8 +461,7 @@ int w_make_resync_request(struct drbd_conf *mdev,
max_segment_size = mdev->agreed_pro_version < 94 ?
queue_max_segment_size(mdev->rq_queue) : DRBD_MAX_SEGMENT_SIZE;
- mdev->c_sync_rate = calc_resync_rate(mdev);
- number = SLEEP_TIME * mdev->c_sync_rate / ((BM_BLOCK_SIZE / 1024) * HZ);
+ number = SLEEP_TIME * mdev->sync_conf.rate / ((BM_BLOCK_SIZE / 1024) * HZ);
pe = atomic_read(&mdev->rs_pending_cnt);
mutex_lock(&mdev->data.mutex);
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 90c4038..cf04c1b 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -178,6 +178,7 @@ static int print_unex = 1;
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/bio.h>
+#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/jiffies.h>
#include <linux/fcntl.h>
@@ -514,8 +515,6 @@ static unsigned long fdc_busy;
static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
static DECLARE_WAIT_QUEUE_HEAD(command_done);
-#define NO_SIGNAL (!interruptible || !signal_pending(current))
-
/* Errors during formatting are counted here. */
static int format_errors;
@@ -539,7 +538,7 @@ static int max_buffer_sectors;
static int *errors;
typedef void (*done_f)(int);
-static struct cont_t {
+static const struct cont_t {
void (*interrupt)(void);
/* this is called after the interrupt of the
* main command */
@@ -578,7 +577,7 @@ static void reset_fdc(void);
#define NEED_1_RECAL -2
#define NEED_2_RECAL -3
-static int usage_count;
+static atomic_t usage_count = ATOMIC_INIT(0);
/* buffer related variables */
static int buffer_track = -1;
@@ -858,36 +857,15 @@ static void set_fdc(int drive)
}
/* locks the driver */
-static int _lock_fdc(int drive, bool interruptible, int line)
+static int lock_fdc(int drive, bool interruptible)
{
- if (!usage_count) {
- pr_err("Trying to lock fdc while usage count=0 at line %d\n",
- line);
+ if (WARN(atomic_read(&usage_count) == 0,
+ "Trying to lock fdc while usage count=0\n"))
return -1;
- }
-
- if (test_and_set_bit(0, &fdc_busy)) {
- DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&fdc_wait, &wait);
-
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (!test_and_set_bit(0, &fdc_busy))
- break;
- schedule();
-
- if (!NO_SIGNAL) {
- remove_wait_queue(&fdc_wait, &wait);
- return -EINTR;
- }
- }
+ if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy)))
+ return -EINTR;
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&fdc_wait, &wait);
- flush_scheduled_work();
- }
command_status = FD_COMMAND_NONE;
__reschedule_timeout(drive, "lock fdc");
@@ -895,11 +873,8 @@ static int _lock_fdc(int drive, bool interruptible, int line)
return 0;
}
-#define lock_fdc(drive, interruptible) \
- _lock_fdc(drive, interruptible, __LINE__)
-
/* unlocks the driver */
-static inline void unlock_fdc(void)
+static void unlock_fdc(void)
{
unsigned long flags;
@@ -1224,7 +1199,7 @@ static int need_more_output(void)
/* Set perpendicular mode as required, based on data rate, if supported.
* 82077 Now tested. 1Mbps data rate only possible with 82077-1.
*/
-static inline void perpendicular_mode(void)
+static void perpendicular_mode(void)
{
unsigned char perp_mode;
@@ -1995,14 +1970,14 @@ static void do_wakeup(void)
wake_up(&command_done);
}
-static struct cont_t wakeup_cont = {
+static const struct cont_t wakeup_cont = {
.interrupt = empty,
.redo = do_wakeup,
.error = empty,
.done = (done_f)empty
};
-static struct cont_t intr_cont = {
+static const struct cont_t intr_cont = {
.interrupt = empty,
.redo = process_fd_request,
.error = empty,
@@ -2015,25 +1990,10 @@ static int wait_til_done(void (*handler)(void), bool interruptible)
schedule_bh(handler);
- if (command_status < 2 && NO_SIGNAL) {
- DECLARE_WAITQUEUE(wait, current);
-
- add_wait_queue(&command_done, &wait);
- for (;;) {
- set_current_state(interruptible ?
- TASK_INTERRUPTIBLE :
- TASK_UNINTERRUPTIBLE);
-
- if (command_status >= 2 || !NO_SIGNAL)
- break;
-
- is_alive(__func__, "");
- schedule();
- }
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&command_done, &wait);
- }
+ if (interruptible)
+ wait_event_interruptible(command_done, command_status >= 2);
+ else
+ wait_event(command_done, command_status >= 2);
if (command_status < 2) {
cancel_activity();
@@ -2223,7 +2183,7 @@ static void redo_format(void)
debugt(__func__, "queue format request");
}
-static struct cont_t format_cont = {
+static const struct cont_t format_cont = {
.interrupt = format_interrupt,
.redo = redo_format,
.error = bad_flp_intr,
@@ -2583,10 +2543,8 @@ static int make_raw_rw_request(void)
int tracksize;
int ssize;
- if (max_buffer_sectors == 0) {
- pr_info("VFS: Block I/O scheduled on unopened device\n");
+ if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n"))
return 0;
- }
set_fdc((long)current_req->rq_disk->private_data);
@@ -2921,7 +2879,7 @@ do_request:
return;
}
-static struct cont_t rw_cont = {
+static const struct cont_t rw_cont = {
.interrupt = rw_interrupt,
.redo = redo_fd_request,
.error = bad_flp_intr,
@@ -2936,19 +2894,16 @@ static void process_fd_request(void)
static void do_fd_request(struct request_queue *q)
{
- if (max_buffer_sectors == 0) {
- pr_info("VFS: %s called on non-open device\n", __func__);
+ if (WARN(max_buffer_sectors == 0,
+ "VFS: %s called on non-open device\n", __func__))
return;
- }
- if (usage_count == 0) {
- pr_info("warning: usage count=0, current_req=%p exiting\n",
- current_req);
- pr_info("sect=%ld type=%x flags=%x\n",
- (long)blk_rq_pos(current_req), current_req->cmd_type,
- current_req->cmd_flags);
+ if (WARN(atomic_read(&usage_count) == 0,
+ "warning: usage count=0, current_req=%p sect=%ld type=%x flags=%x\n",
+ current_req, (long)blk_rq_pos(current_req), current_req->cmd_type,
+ current_req->cmd_flags))
return;
- }
+
if (test_bit(0, &fdc_busy)) {
/* fdc busy, this new request will be treated when the
current one is done */
@@ -2960,7 +2915,7 @@ static void do_fd_request(struct request_queue *q)
is_alive(__func__, "");
}
-static struct cont_t poll_cont = {
+static const struct cont_t poll_cont = {
.interrupt = success_and_wakeup,
.redo = floppy_ready,
.error = generic_failure,
@@ -2991,7 +2946,7 @@ static void reset_intr(void)
pr_info("weird, reset interrupt called\n");
}
-static struct cont_t reset_cont = {
+static const struct cont_t reset_cont = {
.interrupt = reset_intr,
.redo = success_and_wakeup,
.error = generic_failure,
@@ -3033,7 +2988,7 @@ static inline int fd_copyin(void __user *param, void *address,
return copy_from_user(address, param, size) ? -EFAULT : 0;
}
-static inline const char *drive_name(int type, int drive)
+static const char *drive_name(int type, int drive)
{
struct floppy_struct *floppy;
@@ -3096,14 +3051,14 @@ static void raw_cmd_done(int flag)
generic_done(flag);
}
-static struct cont_t raw_cmd_cont = {
+static const struct cont_t raw_cmd_cont = {
.interrupt = success_and_wakeup,
.redo = floppy_start,
.error = generic_failure,
.done = raw_cmd_done
};
-static inline int raw_cmd_copyout(int cmd, void __user *param,
+static int raw_cmd_copyout(int cmd, void __user *param,
struct floppy_raw_cmd *ptr)
{
int ret;
@@ -3148,7 +3103,7 @@ static void raw_cmd_free(struct floppy_raw_cmd **ptr)
}
}
-static inline int raw_cmd_copyin(int cmd, void __user *param,
+static int raw_cmd_copyin(int cmd, void __user *param,
struct floppy_raw_cmd **rcmd)
{
struct floppy_raw_cmd *ptr;
@@ -3266,7 +3221,7 @@ static int invalidate_drive(struct block_device *bdev)
return 0;
}
-static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
+static int set_geometry(unsigned int cmd, struct floppy_struct *g,
int drive, int type, struct block_device *bdev)
{
int cnt;
@@ -3337,7 +3292,7 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
}
/* handle obsolete ioctl's */
-static int ioctl_table[] = {
+static unsigned int ioctl_table[] = {
FDCLRPRM,
FDSETPRM,
FDDEFPRM,
@@ -3365,7 +3320,7 @@ static int ioctl_table[] = {
FDTWADDLE
};
-static inline int normalize_ioctl(int *cmd, int *size)
+static int normalize_ioctl(unsigned int *cmd, int *size)
{
int i;
@@ -3417,7 +3372,7 @@ static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return 0;
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
unsigned long param)
{
int drive = (long)bdev->bd_disk->private_data;
@@ -3593,6 +3548,18 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
return 0;
}
+static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = fd_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
static void __init config_types(void)
{
bool has_drive = false;
@@ -3649,6 +3616,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
{
int drive = (long)disk->private_data;
+ lock_kernel();
mutex_lock(&open_lock);
if (UDRS->fd_ref < 0)
UDRS->fd_ref = 0;
@@ -3659,6 +3627,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
if (!UDRS->fd_ref)
opened_bdev[drive] = NULL;
mutex_unlock(&open_lock);
+ unlock_kernel();
return 0;
}
@@ -3676,6 +3645,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
int res = -EBUSY;
char *tmp;
+ lock_kernel();
mutex_lock(&open_lock);
old_dev = UDRS->fd_device;
if (opened_bdev[drive] && opened_bdev[drive] != bdev)
@@ -3752,6 +3722,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
goto out;
}
mutex_unlock(&open_lock);
+ unlock_kernel();
return 0;
out:
if (UDRS->fd_ref < 0)
@@ -3762,6 +3733,7 @@ out:
opened_bdev[drive] = NULL;
out2:
mutex_unlock(&open_lock);
+ unlock_kernel();
return res;
}
@@ -3829,6 +3801,7 @@ static int __floppy_read_block_0(struct block_device *bdev)
bio.bi_size = size;
bio.bi_bdev = bdev;
bio.bi_sector = 0;
+ bio.bi_flags = BIO_QUIET;
init_completion(&complete);
bio.bi_private = &complete;
bio.bi_end_io = floppy_rb0_complete;
@@ -3857,10 +3830,10 @@ static int floppy_revalidate(struct gendisk *disk)
if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) ||
test_bit(FD_VERIFY_BIT, &UDRS->flags) ||
test_bit(drive, &fake_change) || NO_GEOM) {
- if (usage_count == 0) {
- pr_info("VFS: revalidate called on non-open device.\n");
+ if (WARN(atomic_read(&usage_count) == 0,
+ "VFS: revalidate called on non-open device.\n"))
return -EFAULT;
- }
+
lock_fdc(drive, false);
cf = (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) ||
test_bit(FD_VERIFY_BIT, &UDRS->flags));
@@ -3893,7 +3866,7 @@ static const struct block_device_operations floppy_fops = {
.owner = THIS_MODULE,
.open = floppy_open,
.release = floppy_release,
- .locked_ioctl = fd_ioctl,
+ .ioctl = fd_ioctl,
.getgeo = fd_getgeo,
.media_changed = check_floppy_change,
.revalidate_disk = floppy_revalidate,
@@ -4126,7 +4099,7 @@ static ssize_t floppy_cmos_show(struct device *dev,
return sprintf(buf, "%X\n", UDP->cmos);
}
-DEVICE_ATTR(cmos, S_IRUGO, floppy_cmos_show, NULL);
+static DEVICE_ATTR(cmos, S_IRUGO, floppy_cmos_show, NULL);
static void floppy_device_release(struct device *dev)
{
@@ -4175,6 +4148,9 @@ static int __init floppy_init(void)
int i, unit, drive;
int err, dr;
+ set_debugt();
+ interruptjiffies = resultjiffies = jiffies;
+
#if defined(CONFIG_PPC)
if (check_legacy_ioport(FDC1))
return -ENODEV;
@@ -4353,7 +4329,7 @@ out_unreg_platform_dev:
platform_device_unregister(&floppy_device[drive]);
out_flush_work:
flush_scheduled_work();
- if (usage_count)
+ if (atomic_read(&usage_count))
floppy_release_irq_and_dma();
out_unreg_region:
blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
@@ -4370,8 +4346,6 @@ out_put_disk:
return err;
}
-static DEFINE_SPINLOCK(floppy_usage_lock);
-
static const struct io_region {
int offset;
int size;
@@ -4417,14 +4391,8 @@ static void floppy_release_regions(int fdc)
static int floppy_grab_irq_and_dma(void)
{
- unsigned long flags;
-
- spin_lock_irqsave(&floppy_usage_lock, flags);
- if (usage_count++) {
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ if (atomic_inc_return(&usage_count) > 1)
return 0;
- }
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
/*
* We might have scheduled a free_irq(), wait it to
@@ -4435,9 +4403,7 @@ static int floppy_grab_irq_and_dma(void)
if (fd_request_irq()) {
DPRINT("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
- spin_lock_irqsave(&floppy_usage_lock, flags);
- usage_count--;
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ atomic_dec(&usage_count);
return -1;
}
if (fd_request_dma()) {
@@ -4447,9 +4413,7 @@ static int floppy_grab_irq_and_dma(void)
use_virtual_dma = can_use_virtual_dma = 1;
if (!(can_use_virtual_dma & 1)) {
fd_free_irq();
- spin_lock_irqsave(&floppy_usage_lock, flags);
- usage_count--;
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ atomic_dec(&usage_count);
return -1;
}
}
@@ -4484,9 +4448,7 @@ cleanup:
fd_free_dma();
while (--fdc >= 0)
floppy_release_regions(fdc);
- spin_lock_irqsave(&floppy_usage_lock, flags);
- usage_count--;
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ atomic_dec(&usage_count);
return -1;
}
@@ -4498,14 +4460,10 @@ static void floppy_release_irq_and_dma(void)
#endif
long tmpsize;
unsigned long tmpaddr;
- unsigned long flags;
- spin_lock_irqsave(&floppy_usage_lock, flags);
- if (--usage_count) {
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ if (!atomic_dec_and_test(&usage_count))
return;
- }
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+
if (irqdma_allocated) {
fd_disable_dma();
fd_free_dma();
@@ -4598,7 +4556,7 @@ static void __exit floppy_module_exit(void)
del_timer_sync(&fd_timer);
blk_cleanup_queue(floppy_queue);
- if (usage_count)
+ if (atomic_read(&usage_count))
floppy_release_irq_and_dma();
/* eject disk, if any */
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 81c78b3..30ec6b3 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -627,7 +627,7 @@ repeat:
req_data_dir(req) == READ ? "read" : "writ",
cyl, head, sec, nsect, req->buffer);
#endif
- if (blk_fs_request(req)) {
+ if (req->cmd_type == REQ_TYPE_FS) {
switch (rq_data_dir(req)) {
case READ:
hd_out(disk, nsect, sec, head, cyl, ATA_CMD_PIO_READ,
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 6120922..f3c636d 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -67,6 +67,7 @@
#include <linux/compat.h>
#include <linux/suspend.h>
#include <linux/freezer.h>
+#include <linux/smp_lock.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h> /* for invalidate_bdev() */
#include <linux/completion.h>
@@ -476,7 +477,7 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;
if (bio_rw(bio) == WRITE) {
- bool barrier = bio_rw_flagged(bio, BIO_RW_BARRIER);
+ bool barrier = (bio->bi_rw & REQ_HARDBARRIER);
struct file *file = lo->lo_backing_file;
if (barrier) {
@@ -831,7 +832,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
lo->lo_queue->unplug_fn = loop_unplug;
if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
- blk_queue_ordered(lo->lo_queue, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(lo->lo_queue, QUEUE_ORDERED_DRAIN);
set_capacity(lo->lo_disk, size);
bd_set_size(bdev, size << 9);
@@ -1408,9 +1409,11 @@ static int lo_open(struct block_device *bdev, fmode_t mode)
{
struct loop_device *lo = bdev->bd_disk->private_data;
+ lock_kernel();
mutex_lock(&lo->lo_ctl_mutex);
lo->lo_refcnt++;
mutex_unlock(&lo->lo_ctl_mutex);
+ unlock_kernel();
return 0;
}
@@ -1420,6 +1423,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
struct loop_device *lo = disk->private_data;
int err;
+ lock_kernel();
mutex_lock(&lo->lo_ctl_mutex);
if (--lo->lo_refcnt)
@@ -1444,6 +1448,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
out:
mutex_unlock(&lo->lo_ctl_mutex);
out_unlocked:
+ lock_kernel();
return 0;
}
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
index 28db925..b82c5ce 100644
--- a/drivers/block/mg_disk.c
+++ b/drivers/block/mg_disk.c
@@ -670,7 +670,7 @@ static void mg_request_poll(struct request_queue *q)
break;
}
- if (unlikely(!blk_fs_request(host->req))) {
+ if (unlikely(host->req->cmd_type != REQ_TYPE_FS)) {
mg_end_request_cur(host, -EIO);
continue;
}
@@ -756,7 +756,7 @@ static void mg_request(struct request_queue *q)
continue;
}
- if (unlikely(!blk_fs_request(req))) {
+ if (unlikely(req->cmd_type != REQ_TYPE_FS)) {
mg_end_request_cur(host, -EIO);
continue;
}
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 16c3c86..0daa422 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -24,6 +24,7 @@
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/ioctl.h>
+#include <linux/smp_lock.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/kernel.h>
@@ -448,7 +449,7 @@ static void nbd_clear_que(struct nbd_device *lo)
static void nbd_handle_req(struct nbd_device *lo, struct request *req)
{
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
goto error_out;
nbd_cmd(req) = NBD_CMD_READ;
@@ -716,9 +717,11 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
+ lock_kernel();
mutex_lock(&lo->tx_lock);
error = __nbd_ioctl(bdev, lo, cmd, arg);
mutex_unlock(&lo->tx_lock);
+ unlock_kernel();
return error;
}
@@ -726,7 +729,7 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
static const struct block_device_operations nbd_fops =
{
.owner = THIS_MODULE,
- .locked_ioctl = nbd_ioctl,
+ .ioctl = nbd_ioctl,
};
/*
diff --git a/drivers/block/osdblk.c b/drivers/block/osdblk.c
index 6cd8b70..2284b4f 100644
--- a/drivers/block/osdblk.c
+++ b/drivers/block/osdblk.c
@@ -310,7 +310,8 @@ static void osdblk_rq_fn(struct request_queue *q)
break;
/* filter out block requests we don't understand */
- if (!blk_fs_request(rq) && !blk_barrier_rq(rq)) {
+ if (rq->cmd_type != REQ_TYPE_FS &&
+ !(rq->cmd_flags & REQ_HARDBARRIER)) {
blk_end_request_all(rq, 0);
continue;
}
@@ -322,7 +323,7 @@ static void osdblk_rq_fn(struct request_queue *q)
* driver-specific, etc.
*/
- do_flush = (rq->special == (void *) 0xdeadbeefUL);
+ do_flush = rq->cmd_flags & REQ_FLUSH;
do_write = (rq_data_dir(rq) == WRITE);
if (!do_flush) { /* osd_flush does not use a bio */
@@ -379,14 +380,6 @@ static void osdblk_rq_fn(struct request_queue *q)
}
}
-static void osdblk_prepare_flush(struct request_queue *q, struct request *rq)
-{
- /* add driver-specific marker, to indicate that this request
- * is a flush command
- */
- rq->special = (void *) 0xdeadbeefUL;
-}
-
static void osdblk_free_disk(struct osdblk_device *osdev)
{
struct gendisk *disk = osdev->disk;
@@ -446,7 +439,7 @@ static int osdblk_init_disk(struct osdblk_device *osdev)
blk_queue_stack_limits(q, osd_request_queue(osdev->osd));
blk_queue_prep_rq(q, blk_queue_start_tag);
- blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH, osdblk_prepare_flush);
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH);
disk->queue = q;
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 71acf4e..76f8565 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -138,6 +138,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
#include <linux/cdrom.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
static DEFINE_SPINLOCK(pcd_lock);
@@ -224,13 +225,21 @@ static char *pcd_buf; /* buffer for request in progress */
static int pcd_block_open(struct block_device *bdev, fmode_t mode)
{
struct pcd_unit *cd = bdev->bd_disk->private_data;
- return cdrom_open(&cd->info, bdev, mode);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_open(&cd->info, bdev, mode);
+ unlock_kernel();
+
+ return ret;
}
static int pcd_block_release(struct gendisk *disk, fmode_t mode)
{
struct pcd_unit *cd = disk->private_data;
+ lock_kernel();
cdrom_release(&cd->info, mode);
+ unlock_kernel();
return 0;
}
@@ -238,7 +247,13 @@ static int pcd_block_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
struct pcd_unit *cd = bdev->bd_disk->private_data;
- return cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
static int pcd_block_media_changed(struct gendisk *disk)
@@ -251,7 +266,7 @@ static const struct block_device_operations pcd_bdops = {
.owner = THIS_MODULE,
.open = pcd_block_open,
.release = pcd_block_release,
- .locked_ioctl = pcd_block_ioctl,
+ .ioctl = pcd_block_ioctl,
.media_changed = pcd_block_media_changed,
};
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index c1e5cd0..985f0d4 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -153,6 +153,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_GEO, D_SBY, D_DLY, D_SLV};
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/kernel.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
@@ -439,7 +440,7 @@ static char *pd_buf; /* buffer for request in progress */
static enum action do_pd_io_start(void)
{
- if (blk_special_request(pd_req)) {
+ if (pd_req->cmd_type == REQ_TYPE_SPECIAL) {
phase = pd_special;
return pd_special();
}
@@ -735,12 +736,14 @@ static int pd_open(struct block_device *bdev, fmode_t mode)
{
struct pd_unit *disk = bdev->bd_disk->private_data;
+ lock_kernel();
disk->access++;
if (disk->removable) {
pd_special_command(disk, pd_media_check);
pd_special_command(disk, pd_door_lock);
}
+ unlock_kernel();
return 0;
}
@@ -768,8 +771,10 @@ static int pd_ioctl(struct block_device *bdev, fmode_t mode,
switch (cmd) {
case CDROMEJECT:
+ lock_kernel();
if (disk->access == 1)
pd_special_command(disk, pd_eject);
+ unlock_kernel();
return 0;
default:
return -EINVAL;
@@ -780,8 +785,10 @@ static int pd_release(struct gendisk *p, fmode_t mode)
{
struct pd_unit *disk = p->private_data;
+ lock_kernel();
if (!--disk->access && disk->removable)
pd_special_command(disk, pd_door_unlock);
+ unlock_kernel();
return 0;
}
@@ -812,7 +819,7 @@ static const struct block_device_operations pd_fops = {
.owner = THIS_MODULE,
.open = pd_open,
.release = pd_release,
- .locked_ioctl = pd_ioctl,
+ .ioctl = pd_ioctl,
.getgeo = pd_getgeo,
.media_changed = pd_check_media,
.revalidate_disk= pd_revalidate
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index c059aab..4457b49 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -152,6 +152,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_LUN, D_DLY};
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
static DEFINE_SPINLOCK(pf_spin_lock);
@@ -266,7 +267,7 @@ static const struct block_device_operations pf_fops = {
.owner = THIS_MODULE,
.open = pf_open,
.release = pf_release,
- .locked_ioctl = pf_ioctl,
+ .ioctl = pf_ioctl,
.getgeo = pf_getgeo,
.media_changed = pf_check_media,
};
@@ -299,20 +300,26 @@ static void __init pf_init_units(void)
static int pf_open(struct block_device *bdev, fmode_t mode)
{
struct pf_unit *pf = bdev->bd_disk->private_data;
+ int ret;
+ lock_kernel();
pf_identify(pf);
+ ret = -ENODEV;
if (pf->media_status == PF_NM)
- return -ENODEV;
+ goto out;
+ ret = -EROFS;
if ((pf->media_status == PF_RO) && (mode & FMODE_WRITE))
- return -EROFS;
+ goto out;
+ ret = 0;
pf->access++;
if (pf->removable)
pf_lock(pf, 1);
-
- return 0;
+out:
+ unlock_kernel();
+ return ret;
}
static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo)
@@ -342,7 +349,10 @@ static int pf_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u
if (pf->access != 1)
return -EBUSY;
+ lock_kernel();
pf_eject(pf);
+ unlock_kernel();
+
return 0;
}
@@ -350,14 +360,18 @@ static int pf_release(struct gendisk *disk, fmode_t mode)
{
struct pf_unit *pf = disk->private_data;
- if (pf->access <= 0)
+ lock_kernel();
+ if (pf->access <= 0) {
+ unlock_kernel();
return -EINVAL;
+ }
pf->access--;
if (!pf->access && pf->removable)
pf_lock(pf, 0);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 8a549db..b1cbeb5 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -57,6 +57,7 @@
#include <linux/seq_file.h>
#include <linux/miscdevice.h>
#include <linux/freezer.h>
+#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <scsi/scsi_cmnd.h>
@@ -1221,7 +1222,7 @@ static int pkt_start_recovery(struct packet_data *pkt)
pkt->bio->bi_flags = 1 << BIO_UPTODATE;
pkt->bio->bi_idx = 0;
- BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW));
+ BUG_ON(pkt->bio->bi_rw != REQ_WRITE);
BUG_ON(pkt->bio->bi_vcnt != pkt->frames);
BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);
BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);
@@ -2382,6 +2383,7 @@ static int pkt_open(struct block_device *bdev, fmode_t mode)
VPRINTK(DRIVER_NAME": entering open\n");
+ lock_kernel();
mutex_lock(&ctl_mutex);
pd = pkt_find_dev_from_minor(MINOR(bdev->bd_dev));
if (!pd) {
@@ -2409,6 +2411,7 @@ static int pkt_open(struct block_device *bdev, fmode_t mode)
}
mutex_unlock(&ctl_mutex);
+ unlock_kernel();
return 0;
out_dec:
@@ -2416,6 +2419,7 @@ out_dec:
out:
VPRINTK(DRIVER_NAME": failed open (%d)\n", ret);
mutex_unlock(&ctl_mutex);
+ unlock_kernel();
return ret;
}
@@ -2424,6 +2428,7 @@ static int pkt_close(struct gendisk *disk, fmode_t mode)
struct pktcdvd_device *pd = disk->private_data;
int ret = 0;
+ lock_kernel();
mutex_lock(&ctl_mutex);
pd->refcnt--;
BUG_ON(pd->refcnt < 0);
@@ -2432,6 +2437,7 @@ static int pkt_close(struct gendisk *disk, fmode_t mode)
pkt_release_dev(pd, flush);
}
mutex_unlock(&ctl_mutex);
+ unlock_kernel();
return ret;
}
@@ -2762,10 +2768,12 @@ out_mem:
static int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
struct pktcdvd_device *pd = bdev->bd_disk->private_data;
+ int ret;
VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd,
MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
+ lock_kernel();
switch (cmd) {
case CDROMEJECT:
/*
@@ -2783,14 +2791,16 @@ static int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
case CDROM_LAST_WRITTEN:
case CDROM_SEND_PACKET:
case SCSI_IOCTL_SEND_COMMAND:
- return __blkdev_driver_ioctl(pd->bdev, mode, cmd, arg);
+ ret = __blkdev_driver_ioctl(pd->bdev, mode, cmd, arg);
+ break;
default:
VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd);
- return -ENOTTY;
+ ret = -ENOTTY;
}
+ unlock_kernel();
- return 0;
+ return ret;
}
static int pkt_media_changed(struct gendisk *disk)
@@ -2812,7 +2822,7 @@ static const struct block_device_operations pktcdvd_ops = {
.owner = THIS_MODULE,
.open = pkt_open,
.release = pkt_close,
- .locked_ioctl = pkt_ioctl,
+ .ioctl = pkt_ioctl,
.media_changed = pkt_media_changed,
};
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 3b419e3..e9da874 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -196,13 +196,12 @@ static void ps3disk_do_request(struct ps3_storage_device *dev,
dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
while ((req = blk_fetch_request(q))) {
- if (blk_fs_request(req)) {
- if (ps3disk_submit_request_sg(dev, req))
- break;
- } else if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
- req->cmd[0] == REQ_LB_OP_FLUSH) {
+ if (req->cmd_flags & REQ_FLUSH) {
if (ps3disk_submit_flush_request(dev, req))
break;
+ } else if (req->cmd_type == REQ_TYPE_FS) {
+ if (ps3disk_submit_request_sg(dev, req))
+ break;
} else {
blk_dump_rq_flags(req, DEVICE_NAME " bad request");
__blk_end_request_all(req, -EIO);
@@ -257,8 +256,7 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
- if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
- req->cmd[0] == REQ_LB_OP_FLUSH) {
+ if (req->cmd_flags & REQ_FLUSH) {
read = 0;
op = "flush";
} else {
@@ -398,16 +396,6 @@ static int ps3disk_identify(struct ps3_storage_device *dev)
return 0;
}
-static void ps3disk_prepare_flush(struct request_queue *q, struct request *req)
-{
- struct ps3_storage_device *dev = q->queuedata;
-
- dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
-
- req->cmd_type = REQ_TYPE_LINUX_BLOCK;
- req->cmd[0] = REQ_LB_OP_FLUSH;
-}
-
static unsigned long ps3disk_mask;
static DEFINE_MUTEX(ps3disk_mask_mutex);
@@ -480,8 +468,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
blk_queue_dma_alignment(queue, dev->blk_size-1);
blk_queue_logical_block_size(queue, dev->blk_size);
- blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH,
- ps3disk_prepare_flush);
+ blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH);
blk_queue_max_segments(queue, -1);
blk_queue_max_segment_size(queue, dev->bounce_size);
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index e463657..2e46815 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -20,6 +20,7 @@
#include <linux/fd.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -661,11 +662,23 @@ out:
return err;
}
+static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
static int floppy_release(struct gendisk *disk, fmode_t mode)
{
struct floppy_state *fs = disk->private_data;
struct swim __iomem *base = fs->swd->base;
+ lock_kernel();
if (fs->ref_count < 0)
fs->ref_count = 0;
else if (fs->ref_count > 0)
@@ -673,6 +686,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
if (fs->ref_count == 0)
swim_motor(base, OFF);
+ unlock_kernel();
return 0;
}
@@ -690,7 +704,9 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
case FDEJECT:
if (fs->ref_count != 1)
return -EBUSY;
+ lock_kernel();
err = floppy_eject(fs);
+ unlock_kernel();
return err;
case FDGETPRM:
@@ -751,9 +767,9 @@ static int floppy_revalidate(struct gendisk *disk)
static const struct block_device_operations floppy_fops = {
.owner = THIS_MODULE,
- .open = floppy_open,
+ .open = floppy_unlocked_open,
.release = floppy_release,
- .locked_ioctl = floppy_ioctl,
+ .ioctl = floppy_ioctl,
.getgeo = floppy_getgeo,
.media_changed = floppy_check_change,
.revalidate_disk = floppy_revalidate,
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index ed6fb91..cc6a386 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -25,6 +25,7 @@
#include <linux/ioctl.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/io.h>
@@ -839,7 +840,7 @@ static int fd_eject(struct floppy_state *fs)
static struct floppy_struct floppy_type =
{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */
-static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+static int floppy_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
struct floppy_state *fs = bdev->bd_disk->private_data;
@@ -867,6 +868,18 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
return -ENOTTY;
}
+static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
static int floppy_open(struct block_device *bdev, fmode_t mode)
{
struct floppy_state *fs = bdev->bd_disk->private_data;
@@ -936,15 +949,28 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return 0;
}
+static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
static int floppy_release(struct gendisk *disk, fmode_t mode)
{
struct floppy_state *fs = disk->private_data;
struct swim3 __iomem *sw = fs->swim3;
+ lock_kernel();
if (fs->ref_count > 0 && --fs->ref_count == 0) {
swim3_action(fs, MOTOR_OFF);
out_8(&sw->control_bic, 0xff);
swim3_select(fs, RELAX);
}
+ unlock_kernel();
return 0;
}
@@ -995,9 +1021,9 @@ static int floppy_revalidate(struct gendisk *disk)
}
static const struct block_device_operations floppy_fops = {
- .open = floppy_open,
+ .open = floppy_unlocked_open,
.release = floppy_release,
- .locked_ioctl = floppy_ioctl,
+ .ioctl = floppy_ioctl,
.media_changed = floppy_check_change,
.revalidate_disk= floppy_revalidate,
};
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 0536b5b..c48e148 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -28,6 +28,7 @@
#include <linux/timer.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <scsi/scsi.h>
#define DRV_NAME "ub"
@@ -648,7 +649,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
return 0;
}
- if (lun->changed && !blk_pc_request(rq)) {
+ if (lun->changed && rq->cmd_type != REQ_TYPE_BLOCK_PC) {
blk_start_request(rq);
ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
return 0;
@@ -684,7 +685,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
}
urq->nsg = n_elem;
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
ub_cmd_build_packet(sc, lun, cmd, urq);
} else {
ub_cmd_build_block(sc, lun, cmd, urq);
@@ -781,7 +782,7 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
rq = urq->rq;
if (cmd->error == 0) {
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
if (cmd->act_len >= rq->resid_len)
rq->resid_len = 0;
else
@@ -795,7 +796,7 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
}
}
} else {
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
rq->sense_len = UB_SENSE_SIZE;
@@ -1710,6 +1711,18 @@ err_open:
return rc;
}
+static int ub_bd_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ub_bd_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
+
/*
*/
static int ub_bd_release(struct gendisk *disk, fmode_t mode)
@@ -1717,7 +1730,10 @@ static int ub_bd_release(struct gendisk *disk, fmode_t mode)
struct ub_lun *lun = disk->private_data;
struct ub_dev *sc = lun->udev;
+ lock_kernel();
ub_put(sc);
+ unlock_kernel();
+
return 0;
}
@@ -1729,8 +1745,13 @@ static int ub_bd_ioctl(struct block_device *bdev, fmode_t mode,
{
struct gendisk *disk = bdev->bd_disk;
void __user *usermem = (void __user *) arg;
+ int ret;
+
+ lock_kernel();
+ ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, usermem);
+ unlock_kernel();
- return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, usermem);
+ return ret;
}
/*
@@ -1792,9 +1813,9 @@ static int ub_bd_media_changed(struct gendisk *disk)
static const struct block_device_operations ub_bd_fops = {
.owner = THIS_MODULE,
- .open = ub_bd_open,
+ .open = ub_bd_unlocked_open,
.release = ub_bd_release,
- .locked_ioctl = ub_bd_ioctl,
+ .ioctl = ub_bd_ioctl,
.media_changed = ub_bd_media_changed,
.revalidate_disk = ub_bd_revalidate,
};
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 2f9470f..8be5715 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -478,7 +478,7 @@ static void process_page(unsigned long data)
le32_to_cpu(desc->local_addr)>>9,
le32_to_cpu(desc->transfer_size));
dump_dmastat(card, control);
- } else if (test_bit(BIO_RW, &bio->bi_rw) &&
+ } else if ((bio->bi_rw & REQ_WRITE) &&
le32_to_cpu(desc->local_addr) >> 9 ==
card->init_size) {
card->init_size += le32_to_cpu(desc->transfer_size) >> 9;
diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c
index 788d938..f651e51 100644
--- a/drivers/block/viodasd.c
+++ b/drivers/block/viodasd.c
@@ -41,6 +41,7 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/string.h>
+#include <linux/smp_lock.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/device.h>
@@ -175,6 +176,18 @@ static int viodasd_open(struct block_device *bdev, fmode_t mode)
return 0;
}
+static int viodasd_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = viodasd_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
+
/*
* External release entry point.
*/
@@ -183,6 +196,7 @@ static int viodasd_release(struct gendisk *disk, fmode_t mode)
struct viodasd_device *d = disk->private_data;
HvLpEvent_Rc hvrc;
+ lock_kernel();
/* Send the event to OS/400. We DON'T expect a response */
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo,
@@ -195,6 +209,9 @@ static int viodasd_release(struct gendisk *disk, fmode_t mode)
0, 0, 0);
if (hvrc != 0)
pr_warning("HV close call failed %d\n", (int)hvrc);
+
+ unlock_kernel();
+
return 0;
}
@@ -219,7 +236,7 @@ static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
*/
static const struct block_device_operations viodasd_fops = {
.owner = THIS_MODULE,
- .open = viodasd_open,
+ .open = viodasd_unlocked_open,
.release = viodasd_release,
.getgeo = viodasd_getgeo,
};
@@ -361,7 +378,7 @@ static void do_viodasd_request(struct request_queue *q)
if (req == NULL)
return;
/* check that request contains a valid command */
- if (!blk_fs_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS) {
viodasd_end_request(req, -EIO, blk_rq_sectors(req));
continue;
}
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 23b7c48..2aafafc 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -2,6 +2,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/virtio.h>
#include <linux/virtio_blk.h>
@@ -65,13 +66,18 @@ static void blk_done(struct virtqueue *vq)
break;
}
- if (blk_pc_request(vbr->req)) {
+ switch (vbr->req->cmd_type) {
+ case REQ_TYPE_BLOCK_PC:
vbr->req->resid_len = vbr->in_hdr.residual;
vbr->req->sense_len = vbr->in_hdr.sense_len;
vbr->req->errors = vbr->in_hdr.errors;
- }
- if (blk_special_request(vbr->req))
+ break;
+ case REQ_TYPE_SPECIAL:
vbr->req->errors = (error != 0);
+ break;
+ default:
+ break;
+ }
__blk_end_request_all(vbr->req, error);
list_del(&vbr->list);
@@ -94,36 +100,35 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
return false;
vbr->req = req;
- switch (req->cmd_type) {
- case REQ_TYPE_FS:
- vbr->out_hdr.type = 0;
- vbr->out_hdr.sector = blk_rq_pos(vbr->req);
- vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
- break;
- case REQ_TYPE_BLOCK_PC:
- vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD;
- vbr->out_hdr.sector = 0;
- vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
- break;
- case REQ_TYPE_SPECIAL:
- vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID;
+
+ if (req->cmd_flags & REQ_FLUSH) {
+ vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
vbr->out_hdr.sector = 0;
vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
- break;
- case REQ_TYPE_LINUX_BLOCK:
- if (req->cmd[0] == REQ_LB_OP_FLUSH) {
- vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
+ } else {
+ switch (req->cmd_type) {
+ case REQ_TYPE_FS:
+ vbr->out_hdr.type = 0;
+ vbr->out_hdr.sector = blk_rq_pos(vbr->req);
+ vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
+ break;
+ case REQ_TYPE_BLOCK_PC:
+ vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD;
vbr->out_hdr.sector = 0;
vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
break;
+ case REQ_TYPE_SPECIAL:
+ vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID;
+ vbr->out_hdr.sector = 0;
+ vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
+ break;
+ default:
+ /* We don't put anything else in the queue. */
+ BUG();
}
- /*FALLTHRU*/
- default:
- /* We don't put anything else in the queue. */
- BUG();
}
- if (blk_barrier_rq(vbr->req))
+ if (vbr->req->cmd_flags & REQ_HARDBARRIER)
vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
sg_set_buf(&vblk->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
@@ -134,12 +139,12 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
* block, and before the normal inhdr we put the sense data and the
* inhdr with additional status information before the normal inhdr.
*/
- if (blk_pc_request(vbr->req))
+ if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC)
sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len);
num = blk_rq_map_sg(q, vbr->req, vblk->sg + out);
- if (blk_pc_request(vbr->req)) {
+ if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC) {
sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, 96);
sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr,
sizeof(vbr->in_hdr));
@@ -190,12 +195,6 @@ static void do_virtblk_request(struct request_queue *q)
virtqueue_kick(vblk->vq);
}
-static void virtblk_prepare_flush(struct request_queue *q, struct request *req)
-{
- req->cmd_type = REQ_TYPE_LINUX_BLOCK;
- req->cmd[0] = REQ_LB_OP_FLUSH;
-}
-
/* return id (s/n) string for *disk to *id_str
*/
static int virtblk_get_id(struct gendisk *disk, char *id_str)
@@ -219,7 +218,7 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
return blk_execute_rq(vblk->disk->queue, vblk->disk, req, false);
}
-static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
+static int virtblk_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long data)
{
struct gendisk *disk = bdev->bd_disk;
@@ -235,6 +234,18 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
(void __user *)data);
}
+static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = virtblk_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
/* We provide getgeo only to please some old bootloader/partitioning tools */
static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
{
@@ -261,7 +272,7 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
}
static const struct block_device_operations virtblk_fops = {
- .locked_ioctl = virtblk_ioctl,
+ .ioctl = virtblk_ioctl,
.owner = THIS_MODULE,
.getgeo = virtblk_getgeo,
};
@@ -383,8 +394,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
* flushing a volatile write cache on the host. Use that
* to implement write barrier support.
*/
- blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH,
- virtblk_prepare_flush);
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH);
} else if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) {
/*
* If the BARRIER feature is supported the host expects us
@@ -393,7 +403,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
* never re-orders outstanding I/O. This feature is not
* useful for real life scenarious and deprecated.
*/
- blk_queue_ordered(q, QUEUE_ORDERED_TAG, NULL);
+ blk_queue_ordered(q, QUEUE_ORDERED_TAG);
} else {
/*
* If the FLUSH feature is not supported we must assume that
@@ -401,7 +411,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
* caching. We still need to drain the queue to provider
* proper barrier semantics.
*/
- blk_queue_ordered(q, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN);
}
/* If disk is read-only in the host, the guest should obey */
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index 18a80ff..d5a3cd7 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -46,6 +46,7 @@
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -133,7 +134,7 @@ static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static const struct block_device_operations xd_fops = {
.owner = THIS_MODULE,
- .locked_ioctl = xd_ioctl,
+ .ioctl = xd_ioctl,
.getgeo = xd_getgeo,
};
static DECLARE_WAIT_QUEUE_HEAD(xd_wait_int);
@@ -322,7 +323,7 @@ static void do_xd_request (struct request_queue * q)
int res = -EIO;
int retry;
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
goto done;
if (block + count > get_capacity(req->rq_disk))
goto done;
@@ -347,7 +348,7 @@ static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
}
/* xd_ioctl: handle device ioctl's */
-static int xd_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long arg)
+static int xd_locked_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long arg)
{
switch (cmd) {
case HDIO_SET_DMA:
@@ -375,6 +376,18 @@ static int xd_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long a
}
}
+static int xd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = xd_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
/* xd_readwrite: handle a read/write request */
static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_int count)
{
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index f63ac3d..ac1b682e 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -41,6 +41,7 @@
#include <linux/cdrom.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/scatterlist.h>
#include <xen/xen.h>
@@ -79,6 +80,7 @@ static const struct block_device_operations xlvbd_block_fops;
*/
struct blkfront_info
{
+ struct mutex mutex;
struct xenbus_device *xbdev;
struct gendisk *gd;
int vdevice;
@@ -95,16 +97,14 @@ struct blkfront_info
unsigned long shadow_free;
int feature_barrier;
int is_ready;
-
- /**
- * The number of people holding this device open. We won't allow a
- * hot-unplug unless this is 0.
- */
- int users;
};
static DEFINE_SPINLOCK(blkif_io_lock);
+static unsigned int nr_minors;
+static unsigned long *minors;
+static DEFINE_SPINLOCK(minor_lock);
+
#define MAXIMUM_OUTSTANDING_BLOCK_REQS \
(BLKIF_MAX_SEGMENTS_PER_REQUEST * BLK_RING_SIZE)
#define GRANT_INVALID_REF 0
@@ -139,6 +139,55 @@ static void add_id_to_freelist(struct blkfront_info *info,
info->shadow_free = id;
}
+static int xlbd_reserve_minors(unsigned int minor, unsigned int nr)
+{
+ unsigned int end = minor + nr;
+ int rc;
+
+ if (end > nr_minors) {
+ unsigned long *bitmap, *old;
+
+ bitmap = kzalloc(BITS_TO_LONGS(end) * sizeof(*bitmap),
+ GFP_KERNEL);
+ if (bitmap == NULL)
+ return -ENOMEM;
+
+ spin_lock(&minor_lock);
+ if (end > nr_minors) {
+ old = minors;
+ memcpy(bitmap, minors,
+ BITS_TO_LONGS(nr_minors) * sizeof(*bitmap));
+ minors = bitmap;
+ nr_minors = BITS_TO_LONGS(end) * BITS_PER_LONG;
+ } else
+ old = bitmap;
+ spin_unlock(&minor_lock);
+ kfree(old);
+ }
+
+ spin_lock(&minor_lock);
+ if (find_next_bit(minors, end, minor) >= end) {
+ for (; minor < end; ++minor)
+ __set_bit(minor, minors);
+ rc = 0;
+ } else
+ rc = -EBUSY;
+ spin_unlock(&minor_lock);
+
+ return rc;
+}
+
+static void xlbd_release_minors(unsigned int minor, unsigned int nr)
+{
+ unsigned int end = minor + nr;
+
+ BUG_ON(end > nr_minors);
+ spin_lock(&minor_lock);
+ for (; minor < end; ++minor)
+ __clear_bit(minor, minors);
+ spin_unlock(&minor_lock);
+}
+
static void blkif_restart_queue_callback(void *arg)
{
struct blkfront_info *info = (struct blkfront_info *)arg;
@@ -239,7 +288,7 @@ static int blkif_queue_request(struct request *req)
ring_req->operation = rq_data_dir(req) ?
BLKIF_OP_WRITE : BLKIF_OP_READ;
- if (blk_barrier_rq(req))
+ if (req->cmd_flags & REQ_HARDBARRIER)
ring_req->operation = BLKIF_OP_WRITE_BARRIER;
ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg);
@@ -310,7 +359,7 @@ static void do_blkif_request(struct request_queue *rq)
blk_start_request(req);
- if (!blk_fs_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS) {
__blk_end_request_all(req, -EIO);
continue;
}
@@ -372,17 +421,22 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
static int xlvbd_barrier(struct blkfront_info *info)
{
int err;
+ const char *barrier;
- err = blk_queue_ordered(info->rq,
- info->feature_barrier ? QUEUE_ORDERED_DRAIN : QUEUE_ORDERED_NONE,
- NULL);
+ switch (info->feature_barrier) {
+ case QUEUE_ORDERED_DRAIN: barrier = "enabled (drain)"; break;
+ case QUEUE_ORDERED_TAG: barrier = "enabled (tag)"; break;
+ case QUEUE_ORDERED_NONE: barrier = "disabled"; break;
+ default: return -EINVAL;
+ }
+
+ err = blk_queue_ordered(info->rq, info->feature_barrier);
if (err)
return err;
printk(KERN_INFO "blkfront: %s: barriers %s\n",
- info->gd->disk_name,
- info->feature_barrier ? "enabled" : "disabled");
+ info->gd->disk_name, barrier);
return 0;
}
@@ -418,9 +472,14 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
if ((minor % nr_parts) == 0)
nr_minors = nr_parts;
+ err = xlbd_reserve_minors(minor, nr_minors);
+ if (err)
+ goto out;
+ err = -ENODEV;
+
gd = alloc_disk(nr_minors);
if (gd == NULL)
- goto out;
+ goto release;
offset = minor / nr_parts;
@@ -451,14 +510,13 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
if (xlvbd_init_blk_queue(gd, sector_size)) {
del_gendisk(gd);
- goto out;
+ goto release;
}
info->rq = gd->queue;
info->gd = gd;
- if (info->feature_barrier)
- xlvbd_barrier(info);
+ xlvbd_barrier(info);
if (vdisk_info & VDISK_READONLY)
set_disk_ro(gd, 1);
@@ -471,10 +529,45 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
return 0;
+ release:
+ xlbd_release_minors(minor, nr_minors);
out:
return err;
}
+static void xlvbd_release_gendisk(struct blkfront_info *info)
+{
+ unsigned int minor, nr_minors;
+ unsigned long flags;
+
+ if (info->rq == NULL)
+ return;
+
+ spin_lock_irqsave(&blkif_io_lock, flags);
+
+ /* No more blkif_request(). */
+ blk_stop_queue(info->rq);
+
+ /* No more gnttab callback work. */
+ gnttab_cancel_free_callback(&info->callback);
+ spin_unlock_irqrestore(&blkif_io_lock, flags);
+
+ /* Flush gnttab callback work. Must be done with no locks held. */
+ flush_scheduled_work();
+
+ del_gendisk(info->gd);
+
+ minor = info->gd->first_minor;
+ nr_minors = info->gd->minors;
+ xlbd_release_minors(minor, nr_minors);
+
+ blk_cleanup_queue(info->rq);
+ info->rq = NULL;
+
+ put_disk(info->gd);
+ info->gd = NULL;
+}
+
static void kick_pending_request_queues(struct blkfront_info *info)
{
if (!RING_FULL(&info->ring)) {
@@ -569,7 +662,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
printk(KERN_WARNING "blkfront: %s: write barrier op failed\n",
info->gd->disk_name);
error = -EOPNOTSUPP;
- info->feature_barrier = 0;
+ info->feature_barrier = QUEUE_ORDERED_NONE;
xlvbd_barrier(info);
}
/* fall through */
@@ -652,7 +745,7 @@ fail:
/* Common code used when first setting up, and when resuming. */
-static int talk_to_backend(struct xenbus_device *dev,
+static int talk_to_blkback(struct xenbus_device *dev,
struct blkfront_info *info)
{
const char *message = NULL;
@@ -712,7 +805,6 @@ again:
return err;
}
-
/**
* Entry point to this code when a new device is created. Allocate the basic
* structures and the ring buffer for communication with the backend, and
@@ -773,6 +865,7 @@ static int blkfront_probe(struct xenbus_device *dev,
return -ENOMEM;
}
+ mutex_init(&info->mutex);
info->xbdev = dev;
info->vdevice = vdevice;
info->connected = BLKIF_STATE_DISCONNECTED;
@@ -786,7 +879,7 @@ static int blkfront_probe(struct xenbus_device *dev,
info->handle = simple_strtoul(strrchr(dev->nodename, '/')+1, NULL, 0);
dev_set_drvdata(&dev->dev, info);
- err = talk_to_backend(dev, info);
+ err = talk_to_blkback(dev, info);
if (err) {
kfree(info);
dev_set_drvdata(&dev->dev, NULL);
@@ -881,13 +974,50 @@ static int blkfront_resume(struct xenbus_device *dev)
blkif_free(info, info->connected == BLKIF_STATE_CONNECTED);
- err = talk_to_backend(dev, info);
+ err = talk_to_blkback(dev, info);
if (info->connected == BLKIF_STATE_SUSPENDED && !err)
err = blkif_recover(info);
return err;
}
+static void
+blkfront_closing(struct blkfront_info *info)
+{
+ struct xenbus_device *xbdev = info->xbdev;
+ struct block_device *bdev = NULL;
+
+ mutex_lock(&info->mutex);
+
+ if (xbdev->state == XenbusStateClosing) {
+ mutex_unlock(&info->mutex);
+ return;
+ }
+
+ if (info->gd)
+ bdev = bdget_disk(info->gd, 0);
+
+ mutex_unlock(&info->mutex);
+
+ if (!bdev) {
+ xenbus_frontend_closed(xbdev);
+ return;
+ }
+
+ mutex_lock(&bdev->bd_mutex);
+
+ if (bdev->bd_openers) {
+ xenbus_dev_error(xbdev, -EBUSY,
+ "Device in use; refusing to close");
+ xenbus_switch_state(xbdev, XenbusStateClosing);
+ } else {
+ xlvbd_release_gendisk(info);
+ xenbus_frontend_closed(xbdev);
+ }
+
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdev);
+}
/*
* Invoked when the backend is finally 'ready' (and has told produced
@@ -899,11 +1029,31 @@ static void blkfront_connect(struct blkfront_info *info)
unsigned long sector_size;
unsigned int binfo;
int err;
-
- if ((info->connected == BLKIF_STATE_CONNECTED) ||
- (info->connected == BLKIF_STATE_SUSPENDED) )
+ int barrier;
+
+ switch (info->connected) {
+ case BLKIF_STATE_CONNECTED:
+ /*
+ * Potentially, the back-end may be signalling
+ * a capacity change; update the capacity.
+ */
+ err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "sectors", "%Lu", &sectors);
+ if (XENBUS_EXIST_ERR(err))
+ return;
+ printk(KERN_INFO "Setting capacity to %Lu\n",
+ sectors);
+ set_capacity(info->gd, sectors);
+ revalidate_disk(info->gd);
+
+ /* fall through */
+ case BLKIF_STATE_SUSPENDED:
return;
+ default:
+ break;
+ }
+
dev_dbg(&info->xbdev->dev, "%s:%s.\n",
__func__, info->xbdev->otherend);
@@ -920,10 +1070,26 @@ static void blkfront_connect(struct blkfront_info *info)
}
err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
- "feature-barrier", "%lu", &info->feature_barrier,
+ "feature-barrier", "%lu", &barrier,
NULL);
+
+ /*
+ * If there's no "feature-barrier" defined, then it means
+ * we're dealing with a very old backend which writes
+ * synchronously; draining will do what needs to get done.
+ *
+ * If there are barriers, then we can do full queued writes
+ * with tagged barriers.
+ *
+ * If barriers are not supported, then there's no much we can
+ * do, so just set ordering to NONE.
+ */
if (err)
- info->feature_barrier = 0;
+ info->feature_barrier = QUEUE_ORDERED_DRAIN;
+ else if (barrier)
+ info->feature_barrier = QUEUE_ORDERED_TAG;
+ else
+ info->feature_barrier = QUEUE_ORDERED_NONE;
err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size);
if (err) {
@@ -946,52 +1112,14 @@ static void blkfront_connect(struct blkfront_info *info)
}
/**
- * Handle the change of state of the backend to Closing. We must delete our
- * device-layer structures now, to ensure that writes are flushed through to
- * the backend. Once is this done, we can switch to Closed in
- * acknowledgement.
- */
-static void blkfront_closing(struct xenbus_device *dev)
-{
- struct blkfront_info *info = dev_get_drvdata(&dev->dev);
- unsigned long flags;
-
- dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename);
-
- if (info->rq == NULL)
- goto out;
-
- spin_lock_irqsave(&blkif_io_lock, flags);
-
- /* No more blkif_request(). */
- blk_stop_queue(info->rq);
-
- /* No more gnttab callback work. */
- gnttab_cancel_free_callback(&info->callback);
- spin_unlock_irqrestore(&blkif_io_lock, flags);
-
- /* Flush gnttab callback work. Must be done with no locks held. */
- flush_scheduled_work();
-
- blk_cleanup_queue(info->rq);
- info->rq = NULL;
-
- del_gendisk(info->gd);
-
- out:
- xenbus_frontend_closed(dev);
-}
-
-/**
* Callback received when the backend's state changes.
*/
-static void backend_changed(struct xenbus_device *dev,
+static void blkback_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
struct blkfront_info *info = dev_get_drvdata(&dev->dev);
- struct block_device *bd;
- dev_dbg(&dev->dev, "blkfront:backend_changed.\n");
+ dev_dbg(&dev->dev, "blkfront:blkback_changed to state %d.\n", backend_state);
switch (backend_state) {
case XenbusStateInitialising:
@@ -1006,35 +1134,56 @@ static void backend_changed(struct xenbus_device *dev,
break;
case XenbusStateClosing:
- if (info->gd == NULL) {
- xenbus_frontend_closed(dev);
- break;
- }
- bd = bdget_disk(info->gd, 0);
- if (bd == NULL)
- xenbus_dev_fatal(dev, -ENODEV, "bdget failed");
-
- mutex_lock(&bd->bd_mutex);
- if (info->users > 0)
- xenbus_dev_error(dev, -EBUSY,
- "Device in use; refusing to close");
- else
- blkfront_closing(dev);
- mutex_unlock(&bd->bd_mutex);
- bdput(bd);
+ blkfront_closing(info);
break;
}
}
-static int blkfront_remove(struct xenbus_device *dev)
+static int blkfront_remove(struct xenbus_device *xbdev)
{
- struct blkfront_info *info = dev_get_drvdata(&dev->dev);
+ struct blkfront_info *info = dev_get_drvdata(&xbdev->dev);
+ struct block_device *bdev = NULL;
+ struct gendisk *disk;
- dev_dbg(&dev->dev, "blkfront_remove: %s removed\n", dev->nodename);
+ dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
blkif_free(info, 0);
- kfree(info);
+ mutex_lock(&info->mutex);
+
+ disk = info->gd;
+ if (disk)
+ bdev = bdget_disk(disk, 0);
+
+ info->xbdev = NULL;
+ mutex_unlock(&info->mutex);
+
+ if (!bdev) {
+ kfree(info);
+ return 0;
+ }
+
+ /*
+ * The xbdev was removed before we reached the Closed
+ * state. See if it's safe to remove the disk. If the bdev
+ * isn't closed yet, we let release take care of it.
+ */
+
+ mutex_lock(&bdev->bd_mutex);
+ info = disk->private_data;
+
+ dev_warn(disk_to_dev(disk),
+ "%s was hot-unplugged, %d stale handles\n",
+ xbdev->nodename, bdev->bd_openers);
+
+ if (info && !bdev->bd_openers) {
+ xlvbd_release_gendisk(info);
+ disk->private_data = NULL;
+ kfree(info);
+ }
+
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdev);
return 0;
}
@@ -1043,30 +1192,78 @@ static int blkfront_is_ready(struct xenbus_device *dev)
{
struct blkfront_info *info = dev_get_drvdata(&dev->dev);
- return info->is_ready;
+ return info->is_ready && info->xbdev;
}
static int blkif_open(struct block_device *bdev, fmode_t mode)
{
- struct blkfront_info *info = bdev->bd_disk->private_data;
- info->users++;
- return 0;
+ struct gendisk *disk = bdev->bd_disk;
+ struct blkfront_info *info;
+ int err = 0;
+
+ lock_kernel();
+
+ info = disk->private_data;
+ if (!info) {
+ /* xbdev gone */
+ err = -ERESTARTSYS;
+ goto out;
+ }
+
+ mutex_lock(&info->mutex);
+
+ if (!info->gd)
+ /* xbdev is closed */
+ err = -ERESTARTSYS;
+
+ mutex_unlock(&info->mutex);
+
+out:
+ unlock_kernel();
+ return err;
}
static int blkif_release(struct gendisk *disk, fmode_t mode)
{
struct blkfront_info *info = disk->private_data;
- info->users--;
- if (info->users == 0) {
- /* Check whether we have been instructed to close. We will
- have ignored this request initially, as the device was
- still mounted. */
- struct xenbus_device *dev = info->xbdev;
- enum xenbus_state state = xenbus_read_driver_state(dev->otherend);
-
- if (state == XenbusStateClosing && info->is_ready)
- blkfront_closing(dev);
+ struct block_device *bdev;
+ struct xenbus_device *xbdev;
+
+ lock_kernel();
+
+ bdev = bdget_disk(disk, 0);
+ bdput(bdev);
+
+ if (bdev->bd_openers)
+ goto out;
+
+ /*
+ * Check if we have been instructed to close. We will have
+ * deferred this request, because the bdev was still open.
+ */
+
+ mutex_lock(&info->mutex);
+ xbdev = info->xbdev;
+
+ if (xbdev && xbdev->state == XenbusStateClosing) {
+ /* pending switch to state closed */
+ dev_info(disk_to_dev(bdev->bd_disk), "releasing disk\n");
+ xlvbd_release_gendisk(info);
+ xenbus_frontend_closed(info->xbdev);
+ }
+
+ mutex_unlock(&info->mutex);
+
+ if (!xbdev) {
+ /* sudden device removal */
+ dev_info(disk_to_dev(bdev->bd_disk), "releasing disk\n");
+ xlvbd_release_gendisk(info);
+ disk->private_data = NULL;
+ kfree(info);
}
+
+out:
+ unlock_kernel();
return 0;
}
@@ -1076,7 +1273,7 @@ static const struct block_device_operations xlvbd_block_fops =
.open = blkif_open,
.release = blkif_release,
.getgeo = blkif_getgeo,
- .locked_ioctl = blkif_ioctl,
+ .ioctl = blkif_ioctl,
};
@@ -1092,7 +1289,7 @@ static struct xenbus_driver blkfront = {
.probe = blkfront_probe,
.remove = blkfront_remove,
.resume = blkfront_resume,
- .otherend_changed = backend_changed,
+ .otherend_changed = blkback_changed,
.is_ready = blkfront_is_ready,
};
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index a7b83c0..2982b3e 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -89,6 +89,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/ata.h>
#include <linux/hdreg.h>
#include <linux/platform_device.h>
@@ -465,7 +466,7 @@ struct request *ace_get_next_request(struct request_queue * q)
struct request *req;
while ((req = blk_peek_request(q)) != NULL) {
- if (blk_fs_request(req))
+ if (req->cmd_type == REQ_TYPE_FS)
break;
blk_start_request(req);
__blk_end_request_all(req, -EIO);
@@ -901,11 +902,14 @@ static int ace_open(struct block_device *bdev, fmode_t mode)
dev_dbg(ace->dev, "ace_open() users=%i\n", ace->users + 1);
+ lock_kernel();
spin_lock_irqsave(&ace->lock, flags);
ace->users++;
spin_unlock_irqrestore(&ace->lock, flags);
check_disk_change(bdev);
+ unlock_kernel();
+
return 0;
}
@@ -917,6 +921,7 @@ static int ace_release(struct gendisk *disk, fmode_t mode)
dev_dbg(ace->dev, "ace_release() users=%i\n", ace->users - 1);
+ lock_kernel();
spin_lock_irqsave(&ace->lock, flags);
ace->users--;
if (ace->users == 0) {
@@ -924,6 +929,7 @@ static int ace_release(struct gendisk *disk, fmode_t mode)
ace_out(ace, ACE_CTRL, val & ~ACE_CTRL_LOCKREQ);
}
spin_unlock_irqrestore(&ace->lock, flags);
+ unlock_kernel();
return 0;
}
@@ -1188,7 +1194,7 @@ static struct platform_driver ace_platform_driver = {
#if defined(CONFIG_OF)
static int __devinit
-ace_of_probe(struct of_device *op, const struct of_device_id *match)
+ace_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct resource res;
resource_size_t physaddr;
@@ -1220,7 +1226,7 @@ ace_of_probe(struct of_device *op, const struct of_device_id *match)
return ace_alloc(&op->dev, id ? *id : 0, physaddr, irq, bus_width);
}
-static int __devexit ace_of_remove(struct of_device *op)
+static int __devexit ace_of_remove(struct platform_device *op)
{
ace_free(&op->dev);
return 0;
diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c
index 9114654..d75b2bb 100644
--- a/drivers/block/z2ram.c
+++ b/drivers/block/z2ram.c
@@ -33,6 +33,7 @@
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/bitops.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <asm/setup.h>
@@ -153,6 +154,7 @@ static int z2_open(struct block_device *bdev, fmode_t mode)
device = MINOR(bdev->bd_dev);
+ lock_kernel();
if ( current_device != -1 && current_device != device )
{
rc = -EBUSY;
@@ -294,20 +296,25 @@ static int z2_open(struct block_device *bdev, fmode_t mode)
set_capacity(z2ram_gendisk, z2ram_size >> 9);
}
+ unlock_kernel();
return 0;
err_out_kfree:
kfree(z2ram_map);
err_out:
+ unlock_kernel();
return rc;
}
static int
z2_release(struct gendisk *disk, fmode_t mode)
{
- if ( current_device == -1 )
- return 0;
-
+ lock_kernel();
+ if ( current_device == -1 ) {
+ unlock_kernel();
+ return 0;
+ }
+ unlock_kernel();
/*
* FIXME: unmap memory
*/
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index e3749d0..af13c62 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -242,6 +242,8 @@
-------------------------------------------------------------------------*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define REVISION "Revision: 3.20"
#define VERSION "Id: cdrom.c 3.20 2003/12/17"
@@ -314,11 +316,17 @@ static const char *mrw_format_status[] = {
static const char *mrw_address_space[] = { "DMA", "GAA" };
#if (ERRLOGMASK!=CD_NOTHING)
-#define cdinfo(type, fmt, args...) \
- if ((ERRLOGMASK & type) || debug==1 ) \
- printk(KERN_INFO "cdrom: " fmt, ## args)
+#define cdinfo(type, fmt, args...) \
+do { \
+ if ((ERRLOGMASK & type) || debug == 1) \
+ pr_info(fmt, ##args); \
+} while (0)
#else
-#define cdinfo(type, fmt, args...)
+#define cdinfo(type, fmt, args...) \
+do { \
+ if (0 && (ERRLOGMASK & type) || debug == 1) \
+ pr_info(fmt, ##args); \
+} while (0)
#endif
/* These are used to simplify getting data in from and back to user land */
@@ -395,7 +403,7 @@ int register_cdrom(struct cdrom_device_info *cdi)
if (cdo->open == NULL || cdo->release == NULL)
return -EINVAL;
if (!banner_printed) {
- printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
+ pr_info("Uniform CD-ROM driver " REVISION "\n");
banner_printed = 1;
cdrom_sysctl_register();
}
@@ -546,7 +554,7 @@ static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont)
unsigned char buffer[12];
int ret;
- printk(KERN_INFO "cdrom: %sstarting format\n", cont ? "Re" : "");
+ pr_info("%sstarting format\n", cont ? "Re" : "");
/*
* FmtData bit set (bit 4), format type is 1
@@ -576,7 +584,7 @@ static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont)
ret = cdi->ops->generic_packet(cdi, &cgc);
if (ret)
- printk(KERN_INFO "cdrom: bgformat failed\n");
+ pr_info("bgformat failed\n");
return ret;
}
@@ -622,8 +630,7 @@ static int cdrom_mrw_exit(struct cdrom_device_info *cdi)
ret = 0;
if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) {
- printk(KERN_INFO "cdrom: issuing MRW back ground "
- "format suspend\n");
+ pr_info("issuing MRW background format suspend\n");
ret = cdrom_mrw_bgformat_susp(cdi, 0);
}
@@ -658,7 +665,8 @@ static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space)
if ((ret = cdrom_mode_select(cdi, &cgc)))
return ret;
- printk(KERN_INFO "cdrom: %s: mrw address space %s selected\n", cdi->name, mrw_address_space[space]);
+ pr_info("%s: mrw address space %s selected\n",
+ cdi->name, mrw_address_space[space]);
return 0;
}
@@ -762,7 +770,7 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi)
* always reset to DMA lba space on open
*/
if (cdrom_mrw_set_lba_space(cdi, MRW_LBA_DMA)) {
- printk(KERN_ERR "cdrom: failed setting lba address space\n");
+ pr_err("failed setting lba address space\n");
return 1;
}
@@ -781,8 +789,7 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi)
* 3 - MRW formatting complete
*/
ret = 0;
- printk(KERN_INFO "cdrom open: mrw_status '%s'\n",
- mrw_format_status[di.mrw_status]);
+ pr_info("open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]);
if (!di.mrw_status)
ret = 1;
else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE &&
@@ -932,8 +939,7 @@ static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi)
return;
}
- printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n",
- cdi->name);
+ pr_info("%s: dirty DVD+RW media, \"finalizing\"\n", cdi->name);
init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
cgc.cmd[0] = GPCMD_FLUSH_CACHE;
@@ -2176,7 +2182,7 @@ retry:
* frame dma, so drop to single frame dma if we need to
*/
if (cdi->cdda_method == CDDA_BPC_FULL && nframes > 1) {
- printk("cdrom: dropping to single frame dma\n");
+ pr_info("dropping to single frame dma\n");
cdi->cdda_method = CDDA_BPC_SINGLE;
goto retry;
}
@@ -2189,7 +2195,7 @@ retry:
if (cdi->last_sense != 0x04 && cdi->last_sense != 0x0b)
return ret;
- printk("cdrom: dropping to old style cdda (sense=%x)\n", cdi->last_sense);
+ pr_info("dropping to old style cdda (sense=%x)\n", cdi->last_sense);
cdi->cdda_method = CDDA_OLD;
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
}
@@ -3401,7 +3407,7 @@ static int cdrom_print_info(const char *header, int val, char *info,
"\t%d", CDROM_CAN(val) != 0);
break;
default:
- printk(KERN_INFO "cdrom: invalid option%d\n", option);
+ pr_info("invalid option%d\n", option);
return 1;
}
if (!ret)
@@ -3491,7 +3497,7 @@ doit:
mutex_unlock(&cdrom_mutex);
return proc_dostring(ctl, write, buffer, lenp, ppos);
done:
- printk(KERN_INFO "cdrom: info buffer too small\n");
+ pr_info("info buffer too small\n");
goto doit;
}
@@ -3665,7 +3671,7 @@ static int __init cdrom_init(void)
static void __exit cdrom_exit(void)
{
- printk(KERN_INFO "Uniform CD-ROM driver unloaded\n");
+ pr_info("Uniform CD-ROM driver unloaded\n");
cdrom_sysctl_unregister();
}
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index 03c71f7..261107d 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
@@ -32,6 +34,7 @@
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/device.h>
+#include <linux/smp_lock.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
@@ -339,8 +342,7 @@ static int gdrom_get_last_session(struct cdrom_device_info *cd_info,
tocuse = 0;
err = gdrom_readtoc_cmd(gd.toc, 0);
if (err) {
- printk(KERN_INFO "GDROM: Could not get CD "
- "table of contents\n");
+ pr_info("Could not get CD table of contents\n");
return -ENXIO;
}
}
@@ -357,8 +359,7 @@ static int gdrom_get_last_session(struct cdrom_device_info *cd_info,
} while (track >= fentry);
if ((track > 100) || (track < get_entry_track(gd.toc->first))) {
- printk(KERN_INFO "GDROM: No data on the last "
- "session of the CD\n");
+ pr_info("No data on the last session of the CD\n");
gdrom_getsense(NULL);
return -ENXIO;
}
@@ -451,14 +452,14 @@ static int gdrom_getsense(short *bufstring)
goto cleanup_sense;
insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
if (sense[1] & 40) {
- printk(KERN_INFO "GDROM: Drive not ready - command aborted\n");
+ pr_info("Drive not ready - command aborted\n");
goto cleanup_sense;
}
sense_key = sense[1] & 0x0F;
if (sense_key < ARRAY_SIZE(sense_texts))
- printk(KERN_INFO "GDROM: %s\n", sense_texts[sense_key].text);
+ pr_info("%s\n", sense_texts[sense_key].text);
else
- printk(KERN_ERR "GDROM: Unknown sense key: %d\n", sense_key);
+ pr_err("Unknown sense key: %d\n", sense_key);
if (bufstring) /* return addional sense data */
memcpy(bufstring, &sense[4], 2);
if (sense_key < 2)
@@ -492,12 +493,18 @@ static struct cdrom_device_ops gdrom_ops = {
static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode)
{
- return cdrom_open(gd.cd_info, bdev, mode);
+ int ret;
+ lock_kernel();
+ ret = cdrom_open(gd.cd_info, bdev, mode);
+ unlock_kernel();
+ return ret;
}
static int gdrom_bdops_release(struct gendisk *disk, fmode_t mode)
{
+ lock_kernel();
cdrom_release(gd.cd_info, mode);
+ unlock_kernel();
return 0;
}
@@ -509,7 +516,13 @@ static int gdrom_bdops_mediachanged(struct gendisk *disk)
static int gdrom_bdops_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
- return cdrom_ioctl(gd.cd_info, bdev, mode, cmd, arg);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_ioctl(gd.cd_info, bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
static const struct block_device_operations gdrom_bdops = {
@@ -517,7 +530,7 @@ static const struct block_device_operations gdrom_bdops = {
.open = gdrom_bdops_open,
.release = gdrom_bdops_release,
.media_changed = gdrom_bdops_mediachanged,
- .locked_ioctl = gdrom_bdops_ioctl,
+ .ioctl = gdrom_bdops_ioctl,
};
static irqreturn_t gdrom_command_interrupt(int irq, void *dev_id)
@@ -643,14 +656,13 @@ static void gdrom_request(struct request_queue *rq)
struct request *req;
while ((req = blk_fetch_request(rq)) != NULL) {
- if (!blk_fs_request(req)) {
- printk(KERN_DEBUG "GDROM: Non-fs request ignored\n");
+ if (req->cmd_type != REQ_TYPE_FS) {
+ printk(KERN_DEBUG "gdrom: Non-fs request ignored\n");
__blk_end_request_all(req, -EIO);
continue;
}
if (rq_data_dir(req) != READ) {
- printk(KERN_NOTICE "GDROM: Read only device -");
- printk(" write request ignored\n");
+ pr_notice("Read only device - write request ignored\n");
__blk_end_request_all(req, -EIO);
continue;
}
@@ -685,7 +697,7 @@ static int __devinit gdrom_outputversion(void)
firmw_ver = kstrndup(id->firmver, 16, GFP_KERNEL);
if (!firmw_ver)
goto free_manuf_name;
- printk(KERN_INFO "GDROM: %s from %s with firmware %s\n",
+ pr_info("%s from %s with firmware %s\n",
model_name, manuf_name, firmw_ver);
err = 0;
kfree(firmw_ver);
@@ -757,7 +769,7 @@ static int __devinit probe_gdrom(struct platform_device *devptr)
int err;
/* Start the device */
if (gdrom_execute_diagnostic() != 1) {
- printk(KERN_WARNING "GDROM: ATA Probe for GDROM failed.\n");
+ pr_warning("ATA Probe for GDROM failed\n");
return -ENODEV;
}
/* Print out firmware ID */
@@ -767,7 +779,7 @@ static int __devinit probe_gdrom(struct platform_device *devptr)
gdrom_major = register_blkdev(0, GDROM_DEV_NAME);
if (gdrom_major <= 0)
return gdrom_major;
- printk(KERN_INFO "GDROM: Registered with major number %d\n",
+ pr_info("Registered with major number %d\n",
gdrom_major);
/* Specify basic properties of drive */
gd.cd_info = kzalloc(sizeof(struct cdrom_device_info), GFP_KERNEL);
@@ -818,7 +830,7 @@ probe_fail_no_disk:
unregister_blkdev(gdrom_major, GDROM_DEV_NAME);
gdrom_major = 0;
probe_fail_no_mem:
- printk(KERN_WARNING "GDROM: Probe failed - error is 0x%X\n", err);
+ pr_warning("Probe failed - error is 0x%X\n", err);
return err;
}
diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c
index 451cd70..56bf9f4 100644
--- a/drivers/cdrom/viocd.c
+++ b/drivers/cdrom/viocd.c
@@ -31,6 +31,8 @@
* the OS/400 partition.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/cdrom.h>
@@ -40,6 +42,7 @@
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
@@ -53,9 +56,6 @@
#define VIOCD_VERS "1.06"
-#define VIOCD_KERN_WARNING KERN_WARNING "viocd: "
-#define VIOCD_KERN_INFO KERN_INFO "viocd: "
-
/*
* Should probably make this a module parameter....sigh
*/
@@ -154,13 +154,21 @@ static const struct file_operations proc_viocd_operations = {
static int viocd_blk_open(struct block_device *bdev, fmode_t mode)
{
struct disk_info *di = bdev->bd_disk->private_data;
- return cdrom_open(&di->viocd_info, bdev, mode);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_open(&di->viocd_info, bdev, mode);
+ unlock_kernel();
+
+ return ret;
}
static int viocd_blk_release(struct gendisk *disk, fmode_t mode)
{
struct disk_info *di = disk->private_data;
+ lock_kernel();
cdrom_release(&di->viocd_info, mode);
+ unlock_kernel();
return 0;
}
@@ -168,7 +176,13 @@ static int viocd_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
struct disk_info *di = bdev->bd_disk->private_data;
- return cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
static int viocd_blk_media_changed(struct gendisk *disk)
@@ -181,7 +195,7 @@ static const struct block_device_operations viocd_fops = {
.owner = THIS_MODULE,
.open = viocd_blk_open,
.release = viocd_blk_release,
- .locked_ioctl = viocd_blk_ioctl,
+ .ioctl = viocd_blk_ioctl,
.media_changed = viocd_blk_media_changed,
};
@@ -202,9 +216,8 @@ static int viocd_open(struct cdrom_device_info *cdi, int purpose)
(u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOCD_KERN_WARNING
- "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
return -EIO;
}
@@ -213,8 +226,8 @@ static int viocd_open(struct cdrom_device_info *cdi, int purpose)
if (we.rc) {
const struct vio_error_entry *err =
vio_lookup_rc(viocd_err_table, we.sub_result);
- printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on open: %s\n",
- we.rc, we.sub_result, err->msg);
+ pr_warning("bad rc %d:0x%04X on open: %s\n",
+ we.rc, we.sub_result, err->msg);
return -err->errno;
}
@@ -234,9 +247,8 @@ static void viocd_release(struct cdrom_device_info *cdi)
viopath_targetinst(viopath_hostLp), 0,
VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0);
if (hvrc != 0)
- printk(VIOCD_KERN_WARNING
- "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
}
/* Send a read or write request to OS/400 */
@@ -262,13 +274,12 @@ static int send_request(struct request *req)
sg_init_table(&sg, 1);
if (blk_rq_map_sg(req->q, req, &sg) == 0) {
- printk(VIOCD_KERN_WARNING
- "error setting up scatter/gather list\n");
+ pr_warning("error setting up scatter/gather list\n");
return -1;
}
if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) {
- printk(VIOCD_KERN_WARNING "error allocating sg tce\n");
+ pr_warning("error allocating sg tce\n");
return -1;
}
dmaaddr = sg_dma_address(&sg);
@@ -284,7 +295,7 @@ static int send_request(struct request *req)
((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr,
(u64)blk_rq_pos(req) * 512, len, 0);
if (hvrc != HvLpEvent_Rc_Good) {
- printk(VIOCD_KERN_WARNING "hv error on op %d\n", (int)hvrc);
+ pr_warning("hv error on op %d\n", (int)hvrc);
return -1;
}
@@ -298,11 +309,10 @@ static void do_viocd_request(struct request_queue *q)
struct request *req;
while ((rwreq == 0) && ((req = blk_fetch_request(q)) != NULL)) {
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
__blk_end_request_all(req, -EIO);
else if (send_request(req) < 0) {
- printk(VIOCD_KERN_WARNING
- "unable to send message to OS/400!");
+ pr_warning("unable to send message to OS/400!\n");
__blk_end_request_all(req, -EIO);
} else
rwreq++;
@@ -327,8 +337,8 @@ static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
(u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
return -EIO;
}
@@ -338,9 +348,8 @@ static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
if (we.rc) {
const struct vio_error_entry *err =
vio_lookup_rc(viocd_err_table, we.sub_result);
- printk(VIOCD_KERN_WARNING
- "bad rc %d:0x%04X on check_change: %s; Assuming no change\n",
- we.rc, we.sub_result, err->msg);
+ pr_warning("bad rc %d:0x%04X on check_change: %s; Assuming no change\n",
+ we.rc, we.sub_result, err->msg);
return 0;
}
@@ -367,8 +376,8 @@ static int viocd_lock_door(struct cdrom_device_info *cdi, int locking)
(u64)&we, VIOVERSION << 16,
(device_no << 48) | (flags << 32), 0, 0, 0);
if (hvrc != 0) {
- printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
return -EIO;
}
@@ -455,8 +464,7 @@ static void vio_handle_cd_event(struct HvLpEvent *event)
return;
/* First, we should NEVER get an int here...only acks */
if (hvlpevent_is_int(event)) {
- printk(VIOCD_KERN_WARNING
- "Yikes! got an int in viocd event handler!\n");
+ pr_warning("Yikes! got an int in viocd event handler!\n");
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
@@ -510,10 +518,9 @@ return_complete:
const struct vio_error_entry *err =
vio_lookup_rc(viocd_err_table,
bevent->sub_result);
- printk(VIOCD_KERN_WARNING "request %p failed "
- "with rc %d:0x%04X: %s\n",
- req, event->xRc,
- bevent->sub_result, err->msg);
+ pr_warning("request %p failed with rc %d:0x%04X: %s\n",
+ req, event->xRc,
+ bevent->sub_result, err->msg);
__blk_end_request_all(req, -EIO);
} else
__blk_end_request_all(req, 0);
@@ -524,9 +531,8 @@ return_complete:
break;
default:
- printk(VIOCD_KERN_WARNING
- "message with invalid subtype %0x04X!\n",
- event->xSubtype & VIOMINOR_SUBTYPE_MASK);
+ pr_warning("message with invalid subtype %0x04X!\n",
+ event->xSubtype & VIOMINOR_SUBTYPE_MASK);
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
@@ -593,23 +599,19 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);
if (register_cdrom(c) != 0) {
- printk(VIOCD_KERN_WARNING "Cannot register viocd CD-ROM %s!\n",
- c->name);
+ pr_warning("Cannot register viocd CD-ROM %s!\n", c->name);
goto out;
}
- printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s "
- "type %4.4s, model %3.3s\n",
- c->name, d->rsrcname, d->type, d->model);
+ pr_info("cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n",
+ c->name, d->rsrcname, d->type, d->model);
q = blk_init_queue(do_viocd_request, &viocd_reqlock);
if (q == NULL) {
- printk(VIOCD_KERN_WARNING "Cannot allocate queue for %s!\n",
- c->name);
+ pr_warning("Cannot allocate queue for %s!\n", c->name);
goto out_unregister_cdrom;
}
gendisk = alloc_disk(1);
if (gendisk == NULL) {
- printk(VIOCD_KERN_WARNING "Cannot create gendisk for %s!\n",
- c->name);
+ pr_warning("Cannot create gendisk for %s!\n", c->name);
goto out_cleanup_queue;
}
gendisk->major = VIOCD_MAJOR;
@@ -682,21 +684,19 @@ static int __init viocd_init(void)
return -ENODEV;
}
- printk(VIOCD_KERN_INFO "vers " VIOCD_VERS ", hosting partition %d\n",
- viopath_hostLp);
+ pr_info("vers " VIOCD_VERS ", hosting partition %d\n", viopath_hostLp);
if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) {
- printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n",
- VIOCD_MAJOR, VIOCD_DEVICE);
+ pr_warning("Unable to get major %d for %s\n",
+ VIOCD_MAJOR, VIOCD_DEVICE);
return -EIO;
}
ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio,
MAX_CD_REQ + 2);
if (ret) {
- printk(VIOCD_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 out_unregister;
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 7cfcc62..3d44ec7 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -1002,7 +1002,7 @@ config SCx200_GPIO
config PC8736x_GPIO
tristate "NatSemi PC8736x GPIO Support"
- depends on X86
+ depends on X86_32
default SCx200_GPIO # mostly N
select NSC_GPIO # needed for support routines
help
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 273cee1..dc96416 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,7 @@ FONTMAPFILE = cp437.uni
obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
+obj-y += tty_mutex.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
index 4f8d60c..a11c8c9 100644
--- a/drivers/char/amiserial.c
+++ b/drivers/char/amiserial.c
@@ -1072,7 +1072,7 @@ static int get_serial_info(struct async_struct * info,
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
- lock_kernel();
+ tty_lock();
tmp.type = state->type;
tmp.line = state->line;
tmp.port = state->port;
@@ -1083,7 +1083,7 @@ static int get_serial_info(struct async_struct * info,
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
- unlock_kernel();
+ tty_unlock();
if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
return -EFAULT;
return 0;
@@ -1100,14 +1100,14 @@ static int set_serial_info(struct async_struct * info,
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
- lock_kernel();
+ tty_lock();
state = info->state;
old_state = *state;
change_irq = new_serial.irq != state->irq;
change_port = (new_serial.port != state->port);
if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
- unlock_kernel();
+ tty_unlock();
return -EINVAL;
}
@@ -1127,7 +1127,7 @@ static int set_serial_info(struct async_struct * info,
}
if (new_serial.baud_base < 9600) {
- unlock_kernel();
+ tty_unlock();
return -EINVAL;
}
@@ -1163,7 +1163,7 @@ check_and_exit:
}
} else
retval = startup(info);
- unlock_kernel();
+ tty_unlock();
return retval;
}
@@ -1528,6 +1528,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct async_struct * info = tty->driver_data;
unsigned long orig_jiffies, char_time;
+ int tty_was_locked = tty_locked();
int lsr;
if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
@@ -1538,7 +1539,12 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
orig_jiffies = jiffies;
- lock_kernel();
+ /*
+ * tty_wait_until_sent is called from lots of places,
+ * with or without the BTM.
+ */
+ if (!tty_was_locked)
+ tty_lock();
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
@@ -1579,7 +1585,8 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
break;
}
__set_current_state(TASK_RUNNING);
- unlock_kernel();
+ if (!tty_was_locked)
+ tty_unlock();
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
#endif
@@ -1703,7 +1710,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, state->count);
#endif
+ tty_unlock();
schedule();
+ tty_lock();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c
index 555cd93..d5fa113 100644
--- a/drivers/char/briq_panel.c
+++ b/drivers/char/briq_panel.c
@@ -67,15 +67,15 @@ static void set_led(char state)
static int briq_panel_open(struct inode *ino, struct file *filep)
{
- lock_kernel();
+ tty_lock();
/* enforce single access, vfd_is_open is protected by BKL */
if (vfd_is_open) {
- unlock_kernel();
+ tty_unlock();
return -EBUSY;
}
vfd_is_open = 1;
- unlock_kernel();
+ tty_unlock();
return 0;
}
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index 9824b416..27aad94 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -65,7 +65,6 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
-#include <linux/smp_lock.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
@@ -1608,7 +1607,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
* If the port is the middle of closing, bail out now
*/
if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
- wait_event_interruptible(info->port.close_wait,
+ wait_event_interruptible_tty(info->port.close_wait,
!(info->port.flags & ASYNC_CLOSING));
return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
}
@@ -1655,7 +1654,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
return; /* Just in case.... */
orig_jiffies = jiffies;
- lock_kernel();
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
@@ -1702,7 +1700,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
}
/* Run one more char cycle */
msleep_interruptible(jiffies_to_msecs(char_time * 5));
- unlock_kernel();
#ifdef CY_DEBUG_WAIT_UNTIL_SENT
printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies);
#endif
@@ -1959,7 +1956,6 @@ static int cy_chars_in_buffer(struct tty_struct *tty)
int char_count;
__u32 tx_put, tx_get, tx_bufsize;
- lock_kernel();
tx_get = readl(&buf_ctrl->tx_get);
tx_put = readl(&buf_ctrl->tx_put);
tx_bufsize = readl(&buf_ctrl->tx_bufsize);
@@ -1971,7 +1967,6 @@ static int cy_chars_in_buffer(struct tty_struct *tty)
printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
info->line, info->xmit_cnt + char_count);
#endif
- unlock_kernel();
return info->xmit_cnt + char_count;
}
#endif /* Z_EXT_CHARS_IN_BUFFER */
@@ -2359,17 +2354,22 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
struct serial_struct __user *new_info)
{
struct serial_struct new_serial;
+ int ret;
if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
return -EFAULT;
+ mutex_lock(&info->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if (new_serial.close_delay != info->port.close_delay ||
new_serial.baud_base != info->baud ||
(new_serial.flags & ASYNC_FLAGS &
~ASYNC_USR_MASK) !=
(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
+ {
+ mutex_unlock(&info->port.mutex);
return -EPERM;
+ }
info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK);
info->baud = new_serial.baud_base;
@@ -2392,10 +2392,12 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
check_and_exit:
if (info->port.flags & ASYNC_INITIALIZED) {
cy_set_line_char(info, tty);
- return 0;
+ ret = 0;
} else {
- return cy_startup(info, tty);
+ ret = cy_startup(info, tty);
}
+ mutex_unlock(&info->port.mutex);
+ return ret;
} /* set_serial_info */
/*
@@ -2438,7 +2440,6 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
card = info->card;
- lock_kernel();
if (!cy_is_Z(card)) {
unsigned long flags;
int channel = info->line - card->first_line;
@@ -2478,7 +2479,6 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
}
end:
- unlock_kernel();
return result;
} /* cy_tiomget */
@@ -2696,7 +2696,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
info->line, cmd, arg);
#endif
- lock_kernel();
switch (cmd) {
case CYGETMON:
@@ -2817,7 +2816,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
default:
ret_val = -ENOIOCTLCMD;
}
- unlock_kernel();
#ifdef CY_DEBUG_OTHER
printk(KERN_DEBUG "cyc:cy_ioctl done\n");
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 6f5ffe1..d9df46a 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -36,7 +36,7 @@
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
@@ -2105,7 +2105,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file,
break;
case DIGI_SETAW:
case DIGI_SETAF:
- lock_kernel();
if (cmd == DIGI_SETAW) {
/* Setup an event to indicate when the transmit
buffer empties */
@@ -2118,7 +2117,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file,
if (tty->ldisc->ops->flush_buffer)
tty->ldisc->ops->flush_buffer(tty);
}
- unlock_kernel();
/* Fall Thru */
case DIGI_SETA:
if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index 5a80ad6..7b01bc6 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -1149,7 +1149,7 @@ out_err:
* Note: If it is called early in the boot process, @val is stored and
* parsed later in hvc_iucv_init().
*/
-static int param_set_vmidfilter(const char *val, struct kernel_param *kp)
+static int param_set_vmidfilter(const char *val, const struct kernel_param *kp)
{
int rc;
@@ -1176,7 +1176,7 @@ static int param_set_vmidfilter(const char *val, struct kernel_param *kp)
* The function stores the filter as a comma-separated list of z/VM user IDs
* in @buffer. Typically, sysfs routines call this function for attr show.
*/
-static int param_get_vmidfilter(char *buffer, struct kernel_param *kp)
+static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp)
{
int rc;
size_t index, len;
@@ -1203,6 +1203,11 @@ static int param_get_vmidfilter(char *buffer, struct kernel_param *kp)
#define param_check_vmidfilter(name, p) __param_check(name, p, void)
+static struct kernel_param_ops param_ops_vmidfilter = {
+ .set = param_set_vmidfilter,
+ .get = param_get_vmidfilter,
+};
+
/**
* hvc_iucv_init() - z/VM IUCV HVC device driver initialization
*/
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index 7a4f080..1acdb25 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -619,7 +619,7 @@ static void __devinit n2rng_driver_version(void)
pr_info("%s", version);
}
-static int __devinit n2rng_probe(struct of_device *op,
+static int __devinit n2rng_probe(struct platform_device *op,
const struct of_device_id *match)
{
int victoria_falls = (match->data != NULL);
@@ -714,7 +714,7 @@ out:
return err;
}
-static int __devexit n2rng_remove(struct of_device *op)
+static int __devexit n2rng_remove(struct platform_device *op)
{
struct n2rng *np = dev_get_drvdata(&op->dev);
diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h
index a2b81e7..4bea07f 100644
--- a/drivers/char/hw_random/n2rng.h
+++ b/drivers/char/hw_random/n2rng.h
@@ -65,7 +65,7 @@ struct n2rng_unit {
};
struct n2rng {
- struct of_device *op;
+ struct platform_device *op;
unsigned long flags;
#define N2RNG_FLAG_VF 0x00000001 /* Victoria Falls RNG, else N2 */
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c
index 261ba8f..a31c830 100644
--- a/drivers/char/hw_random/pasemi-rng.c
+++ b/drivers/char/hw_random/pasemi-rng.c
@@ -94,7 +94,7 @@ static struct hwrng pasemi_rng = {
.data_read = pasemi_rng_data_read,
};
-static int __devinit rng_probe(struct of_device *ofdev,
+static int __devinit rng_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
void __iomem *rng_regs;
@@ -123,7 +123,7 @@ static int __devinit rng_probe(struct of_device *ofdev,
return err;
}
-static int __devexit rng_remove(struct of_device *dev)
+static int __devexit rng_remove(struct platform_device *dev)
{
void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv;
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
index 911e1da..07f3ea3 100644
--- a/drivers/char/ip2/ip2main.c
+++ b/drivers/char/ip2/ip2main.c
@@ -1486,7 +1486,9 @@ ip2_open( PTTY tty, struct file *pFile )
if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
if ( pCh->flags & ASYNC_CLOSING ) {
+ tty_unlock();
schedule();
+ tty_lock();
}
if ( tty_hung_up_p(pFile) ) {
set_current_state( TASK_RUNNING );
@@ -1548,7 +1550,9 @@ ip2_open( PTTY tty, struct file *pFile )
rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state( TASK_RUNNING );
remove_wait_queue(&pCh->open_wait, &wait);
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index ff68e7c..3822b4f 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -1804,9 +1804,12 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
info->irq_setup = std_irq_setup;
info->slave_addr = ipmb;
- if (!add_smi(info))
+ if (!add_smi(info)) {
if (try_smi_init(info))
cleanup_one_si(info);
+ } else {
+ kfree(info);
+ }
} else {
/* remove */
struct smi_info *e, *tmp_e;
@@ -1890,9 +1893,12 @@ static __devinit void hardcode_find_bmc(void)
info->irq_setup = std_irq_setup;
info->slave_addr = slave_addrs[i];
- if (!add_smi(info))
+ if (!add_smi(info)) {
if (try_smi_init(info))
cleanup_one_si(info);
+ } else {
+ kfree(info);
+ }
}
}
@@ -1965,8 +1971,8 @@ static int acpi_gpe_irq_setup(struct smi_info *info)
/*
* Defined at
- * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/
- * Docs/TechPapers/IA64/hpspmi.pdf
+ * http://h21007.www2.hp.com/portal/download/files
+ * /unprot/hpspmi.pdf
*/
struct SPMITable {
s8 Signature[4];
@@ -2013,18 +2019,12 @@ struct SPMITable {
static __devinit int try_init_spmi(struct SPMITable *spmi)
{
struct smi_info *info;
- u8 addr_space;
if (spmi->IPMIlegacy != 1) {
printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
return -ENODEV;
}
- if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
- addr_space = IPMI_MEM_ADDR_SPACE;
- else
- addr_space = IPMI_IO_ADDR_SPACE;
-
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
printk(KERN_ERR PFX "Could not allocate SI data (3)\n");
@@ -2088,7 +2088,13 @@ static __devinit int try_init_spmi(struct SPMITable *spmi)
}
info->io.addr_data = spmi->addr.address;
- add_smi(info);
+ pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n",
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ info->io.addr_data, info->io.regsize, info->io.regspacing,
+ info->irq);
+
+ if (add_smi(info))
+ kfree(info);
return 0;
}
@@ -2204,7 +2210,10 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
res, info->io.regsize, info->io.regspacing,
info->irq);
- return add_smi(info);
+ if (add_smi(info))
+ goto err_free;
+
+ return 0;
err_free:
kfree(info);
@@ -2362,7 +2371,13 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
if (info->irq)
info->irq_setup = std_irq_setup;
- add_smi(info);
+ pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ info->io.addr_data, info->io.regsize, info->io.regspacing,
+ info->irq);
+
+ if (add_smi(info))
+ kfree(info);
}
static void __devinit dmi_find_bmc(void)
@@ -2468,7 +2483,10 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
&pdev->resource[0], info->io.regsize, info->io.regspacing,
info->irq);
- return add_smi(info);
+ if (add_smi(info))
+ kfree(info);
+
+ return 0;
}
static void __devexit ipmi_pci_remove(struct pci_dev *pdev)
@@ -2510,7 +2528,7 @@ static struct pci_driver ipmi_pci_driver = {
#ifdef CONFIG_PPC_OF
-static int __devinit ipmi_of_probe(struct of_device *dev,
+static int __devinit ipmi_of_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct smi_info *info;
@@ -2581,10 +2599,15 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
dev_set_drvdata(&dev->dev, info);
- return add_smi(info);
+ if (add_smi(info)) {
+ kfree(info);
+ return -EBUSY;
+ }
+
+ return 0;
}
-static int __devexit ipmi_of_remove(struct of_device *dev)
+static int __devexit ipmi_of_remove(struct platform_device *dev)
{
cleanup_one_si(dev_get_drvdata(&dev->dev));
return 0;
@@ -3014,6 +3037,8 @@ static __devinit void default_find_bmc(void)
info->io.addr_data);
} else
cleanup_one_si(info);
+ } else {
+ kfree(info);
}
}
}
@@ -3041,7 +3066,7 @@ static int add_smi(struct smi_info *new_smi)
si_to_str[new_smi->si_type]);
mutex_lock(&smi_infos_lock);
if (!is_new_interface(new_smi)) {
- printk(KERN_CONT PFX "duplicate interface\n");
+ printk(KERN_CONT " duplicate interface\n");
rv = -EBUSY;
goto out_err;
}
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 82bcdb2..654d566 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -196,7 +196,7 @@ static void ipmi_unregister_watchdog(int ipmi_intf);
*/
static int start_now;
-static int set_param_int(const char *val, struct kernel_param *kp)
+static int set_param_timeout(const char *val, const struct kernel_param *kp)
{
char *endp;
int l;
@@ -215,10 +215,11 @@ static int set_param_int(const char *val, struct kernel_param *kp)
return rv;
}
-static int get_param_int(char *buffer, struct kernel_param *kp)
-{
- return sprintf(buffer, "%i", *((int *)kp->arg));
-}
+static struct kernel_param_ops param_ops_timeout = {
+ .set = set_param_timeout,
+ .get = param_get_int,
+};
+#define param_check_timeout param_check_int
typedef int (*action_fn)(const char *intval, char *outval);
@@ -227,7 +228,7 @@ static int preaction_op(const char *inval, char *outval);
static int preop_op(const char *inval, char *outval);
static void check_parms(void);
-static int set_param_str(const char *val, struct kernel_param *kp)
+static int set_param_str(const char *val, const struct kernel_param *kp)
{
action_fn fn = (action_fn) kp->arg;
int rv = 0;
@@ -251,7 +252,7 @@ static int set_param_str(const char *val, struct kernel_param *kp)
return rv;
}
-static int get_param_str(char *buffer, struct kernel_param *kp)
+static int get_param_str(char *buffer, const struct kernel_param *kp)
{
action_fn fn = (action_fn) kp->arg;
int rv;
@@ -263,7 +264,7 @@ static int get_param_str(char *buffer, struct kernel_param *kp)
}
-static int set_param_wdog_ifnum(const char *val, struct kernel_param *kp)
+static int set_param_wdog_ifnum(const char *val, const struct kernel_param *kp)
{
int rv = param_set_int(val, kp);
if (rv)
@@ -276,27 +277,38 @@ static int set_param_wdog_ifnum(const char *val, struct kernel_param *kp)
return 0;
}
-module_param_call(ifnum_to_use, set_param_wdog_ifnum, get_param_int,
- &ifnum_to_use, 0644);
+static struct kernel_param_ops param_ops_wdog_ifnum = {
+ .set = set_param_wdog_ifnum,
+ .get = param_get_int,
+};
+
+#define param_check_wdog_ifnum param_check_int
+
+static struct kernel_param_ops param_ops_str = {
+ .set = set_param_str,
+ .get = get_param_str,
+};
+
+module_param(ifnum_to_use, wdog_ifnum, 0644);
MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog "
"timer. Setting to -1 defaults to the first registered "
"interface");
-module_param_call(timeout, set_param_int, get_param_int, &timeout, 0644);
+module_param(timeout, timeout, 0644);
MODULE_PARM_DESC(timeout, "Timeout value in seconds.");
-module_param_call(pretimeout, set_param_int, get_param_int, &pretimeout, 0644);
+module_param(pretimeout, timeout, 0644);
MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds.");
-module_param_call(action, set_param_str, get_param_str, action_op, 0644);
+module_param_cb(action, &param_ops_str, action_op, 0644);
MODULE_PARM_DESC(action, "Timeout action. One of: "
"reset, none, power_cycle, power_off.");
-module_param_call(preaction, set_param_str, get_param_str, preaction_op, 0644);
+module_param_cb(preaction, &param_ops_str, preaction_op, 0644);
MODULE_PARM_DESC(preaction, "Pretimeout action. One of: "
"pre_none, pre_smi, pre_nmi, pre_int.");
-module_param_call(preop, set_param_str, get_param_str, preop_op, 0644);
+module_param_cb(preop, &param_ops_str, preop_op, 0644);
MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: "
"preop_none, preop_panic, preop_give_data.");
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 98310e1..c27e9d2 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -124,7 +124,6 @@
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/serial.h>
-#include <linux/smp_lock.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
@@ -872,7 +871,6 @@ static struct tty_port *isicom_find_port(struct tty_struct *tty)
static int isicom_open(struct tty_struct *tty, struct file *filp)
{
struct isi_port *port;
- struct isi_board *card;
struct tty_port *tport;
tport = isicom_find_port(tty);
@@ -1118,8 +1116,7 @@ static int isicom_set_serial_info(struct tty_struct *tty,
if (copy_from_user(&newinfo, info, sizeof(newinfo)))
return -EFAULT;
- lock_kernel();
-
+ mutex_lock(&port->port.mutex);
reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
(newinfo.flags & ASYNC_SPD_MASK));
@@ -1128,7 +1125,7 @@ static int isicom_set_serial_info(struct tty_struct *tty,
(newinfo.closing_wait != port->port.closing_wait) ||
((newinfo.flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
@@ -1145,7 +1142,7 @@ static int isicom_set_serial_info(struct tty_struct *tty,
isicom_config_port(tty);
spin_unlock_irqrestore(&port->card->card_lock, flags);
}
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return 0;
}
@@ -1154,7 +1151,7 @@ static int isicom_get_serial_info(struct isi_port *port,
{
struct serial_struct out_info;
- lock_kernel();
+ mutex_lock(&port->port.mutex);
memset(&out_info, 0, sizeof(out_info));
/* out_info.type = ? */
out_info.line = port - isi_ports;
@@ -1164,7 +1161,7 @@ static int isicom_get_serial_info(struct isi_port *port,
/* out_info.baud_base = ? */
out_info.close_delay = port->port.close_delay;
out_info.closing_wait = port->port.closing_wait;
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
if (copy_to_user(info, &out_info, sizeof(out_info)))
return -EFAULT;
return 0;
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 4e395c9..be28391 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -203,9 +203,9 @@ static int stli_shared;
* the board has been detected, and whether it is actually running a slave
* or not.
*/
-#define BST_FOUND 0x1
-#define BST_STARTED 0x2
-#define BST_PROBED 0x4
+#define BST_FOUND 0
+#define BST_STARTED 1
+#define BST_PROBED 2
/*
* Define the set of port state flags. These are marked for internal
@@ -816,7 +816,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
brdp = stli_brds[brdnr];
if (brdp == NULL)
return -ENODEV;
- if ((brdp->state & BST_STARTED) == 0)
+ if (!test_bit(BST_STARTED, &brdp->state))
return -ENODEV;
portnr = MINOR2PORT(minordev);
if (portnr > brdp->nrports)
@@ -954,7 +954,7 @@ static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned l
* order of opens and closes may not be preserved across shared
* memory, so we must wait until it is complete.
*/
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
return -ERESTARTSYS;
@@ -989,7 +989,7 @@ static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned l
set_bit(ST_OPENING, &portp->state);
spin_unlock_irqrestore(&brd_lock, flags);
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_OPENING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
@@ -1020,7 +1020,7 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned
* occurs on this port.
*/
if (wait) {
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
return -ERESTARTSYS;
@@ -1052,7 +1052,7 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned
* to come back.
*/
rc = 0;
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
@@ -1073,6 +1073,10 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned
static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
{
+ /*
+ * no need for wait_event_tty because clearing ST_CMDING cannot block
+ * on BTM
+ */
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CMDING, &portp->state));
if (signal_pending(current))
@@ -1846,7 +1850,7 @@ static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stlip
rc = stli_portcmdstats(NULL, portp);
uart = "UNKNOWN";
- if (brdp->state & BST_STARTED) {
+ if (test_bit(BST_STARTED, &brdp->state)) {
switch (stli_comstats.hwid) {
case 0: uart = "2681"; break;
case 1: uart = "SC26198"; break;
@@ -1855,7 +1859,7 @@ static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stlip
}
seq_printf(m, "%d: uart:%s ", portnr, uart);
- if ((brdp->state & BST_STARTED) && (rc >= 0)) {
+ if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) {
char sep;
seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal,
@@ -2355,7 +2359,7 @@ static void stli_poll(unsigned long arg)
brdp = stli_brds[brdnr];
if (brdp == NULL)
continue;
- if ((brdp->state & BST_STARTED) == 0)
+ if (!test_bit(BST_STARTED, &brdp->state))
continue;
spin_lock(&brd_lock);
@@ -3140,7 +3144,7 @@ static int stli_initecp(struct stlibrd *brdp)
}
- brdp->state |= BST_FOUND;
+ set_bit(BST_FOUND, &brdp->state);
return 0;
err_unmap:
iounmap(brdp->membase);
@@ -3297,7 +3301,7 @@ static int stli_initonb(struct stlibrd *brdp)
brdp->panels[0] = brdp->nrports;
- brdp->state |= BST_FOUND;
+ set_bit(BST_FOUND, &brdp->state);
return 0;
err_unmap:
iounmap(brdp->membase);
@@ -3407,7 +3411,7 @@ stli_donestartup:
spin_unlock_irqrestore(&brd_lock, flags);
if (rc == 0)
- brdp->state |= BST_STARTED;
+ set_bit(BST_STARTED, &brdp->state);
if (! stli_timeron) {
stli_timeron++;
@@ -3710,7 +3714,7 @@ static int __devinit stli_pciprobe(struct pci_dev *pdev,
if (retval)
goto err_null;
- brdp->state |= BST_PROBED;
+ set_bit(BST_PROBED, &brdp->state);
pci_set_drvdata(pdev, brdp);
EBRDENABLE(brdp);
@@ -3841,7 +3845,7 @@ static int __init stli_initbrds(void)
brdp = stli_brds[i];
if (brdp == NULL)
continue;
- if (brdp->state & BST_FOUND) {
+ if (test_bit(BST_FOUND, &brdp->state)) {
EBRDENABLE(brdp);
brdp->enable = NULL;
brdp->disable = NULL;
@@ -4011,6 +4015,7 @@ static int stli_getbrdstats(combrd_t __user *bp)
return -ENODEV;
memset(&stli_brdstats, 0, sizeof(combrd_t));
+
stli_brdstats.brd = brdp->brdnr;
stli_brdstats.type = brdp->brdtype;
stli_brdstats.hwid = 0;
@@ -4076,10 +4081,13 @@ static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
if (brdp == NULL)
return -ENODEV;
- if (brdp->state & BST_STARTED) {
+ mutex_lock(&portp->port.mutex);
+ if (test_bit(BST_STARTED, &brdp->state)) {
if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
- &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
+ &stli_cdkstats, sizeof(asystats_t), 1)) < 0) {
+ mutex_unlock(&portp->port.mutex);
return rc;
+ }
} else {
memset(&stli_cdkstats, 0, sizeof(asystats_t));
}
@@ -4124,6 +4132,7 @@ static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
stli_comstats.modem = stli_cdkstats.dcdcnt;
stli_comstats.hwid = stli_cdkstats.hwid;
stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
+ mutex_unlock(&portp->port.mutex);
return 0;
}
@@ -4186,15 +4195,20 @@ static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp)
if (!brdp)
return -ENODEV;
- if (brdp->state & BST_STARTED) {
- if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0)
+ mutex_lock(&portp->port.mutex);
+
+ if (test_bit(BST_STARTED, &brdp->state)) {
+ if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) {
+ mutex_unlock(&portp->port.mutex);
return rc;
+ }
}
memset(&stli_comstats, 0, sizeof(comstats_t));
stli_comstats.brd = portp->brdnr;
stli_comstats.panel = portp->panelnr;
stli_comstats.port = portp->portnr;
+ mutex_unlock(&portp->port.mutex);
if (copy_to_user(cp, &stli_comstats, sizeof(comstats_t)))
return -EFAULT;
@@ -4266,8 +4280,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
done = 0;
rc = 0;
- lock_kernel();
-
switch (cmd) {
case COM_GETPORTSTATS:
rc = stli_getportstats(NULL, NULL, argp);
@@ -4290,8 +4302,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
done++;
break;
}
- unlock_kernel();
-
if (done)
return rc;
@@ -4308,8 +4318,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
if (brdp->state == 0)
return -ENODEV;
- lock_kernel();
-
switch (cmd) {
case STL_BINTR:
EBRDINTR(brdp);
@@ -4318,10 +4326,10 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
rc = stli_startbrd(brdp);
break;
case STL_BSTOP:
- brdp->state &= ~BST_STARTED;
+ clear_bit(BST_STARTED, &brdp->state);
break;
case STL_BRESET:
- brdp->state &= ~BST_STARTED;
+ clear_bit(BST_STARTED, &brdp->state);
EBRDRESET(brdp);
if (stli_shared == 0) {
if (brdp->reenable != NULL)
@@ -4332,7 +4340,6 @@ static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
return rc;
}
@@ -4378,7 +4385,8 @@ static void istallion_cleanup_isa(void)
unsigned int j;
for (j = 0; (j < stli_nrbrds); j++) {
- if ((brdp = stli_brds[j]) == NULL || (brdp->state & BST_PROBED))
+ if ((brdp = stli_brds[j]) == NULL ||
+ test_bit(BST_PROBED, &brdp->state))
continue;
stli_cleanup_ports(brdp);
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 25be210..a7ca752 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -299,7 +299,7 @@ int kbd_rate(struct kbd_repeat *rep)
*/
static void put_queue(struct vc_data *vc, int ch)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (tty) {
tty_insert_flip_char(tty, ch, 0);
@@ -309,7 +309,7 @@ static void put_queue(struct vc_data *vc, int ch)
static void puts_queue(struct vc_data *vc, char *cp)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (!tty)
return;
@@ -485,7 +485,7 @@ static void fn_show_ptregs(struct vc_data *vc)
static void fn_hold(struct vc_data *vc)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (rep || !tty)
return;
@@ -563,7 +563,7 @@ static void fn_inc_console(struct vc_data *vc)
static void fn_send_intr(struct vc_data *vc)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (!tty)
return;
@@ -1162,7 +1162,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
int rc;
- tty = vc->vc_tty;
+ tty = vc->port.tty;
if (tty && (!tty->driver_data)) {
/* No driver data? Strange. Okay we fix it then. */
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index d2692d4..3fc89da 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -2193,7 +2193,7 @@ static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port
port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
port->icount.tx += (cnt - port->xmit_cnt);
- if (port->xmit_cnt < WAKEUP_CHARS && tty)
+ if (port->xmit_cnt < WAKEUP_CHARS)
tty_wakeup(tty);
if (port->xmit_cnt <= 0) {
diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c
index e4089c4..04ef3ef 100644
--- a/drivers/char/n_gsm.c
+++ b/drivers/char/n_gsm.c
@@ -43,7 +43,6 @@
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
-#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/string.h>
@@ -920,7 +919,7 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
else
len = gsm_dlci_data_output_framed(gsm, dlci);
if (len < 0)
- return;
+ break;
/* DLCI empty - try the next */
if (len == 0)
i++;
diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c
index c68118e..47d3228 100644
--- a/drivers/char/n_hdlc.c
+++ b/drivers/char/n_hdlc.c
@@ -598,18 +598,18 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
return -EFAULT;
}
- lock_kernel();
+ tty_lock();
for (;;) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
- unlock_kernel();
+ tty_unlock();
return -EIO;
}
n_hdlc = tty2n_hdlc (tty);
if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
tty != n_hdlc->tty) {
- unlock_kernel();
+ tty_unlock();
return 0;
}
@@ -619,13 +619,13 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
/* no data */
if (file->f_flags & O_NONBLOCK) {
- unlock_kernel();
+ tty_unlock();
return -EAGAIN;
}
interruptible_sleep_on (&tty->read_wait);
if (signal_pending(current)) {
- unlock_kernel();
+ tty_unlock();
return -EINTR;
}
}
@@ -648,7 +648,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
kfree(rbuf);
else
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
- unlock_kernel();
+ tty_unlock();
return ret;
} /* end of n_hdlc_tty_read() */
@@ -691,7 +691,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
count = maxframe;
}
- lock_kernel();
+ tty_lock();
add_wait_queue(&tty->write_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
@@ -731,7 +731,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
n_hdlc_send_frames(n_hdlc,tty);
}
- unlock_kernel();
+ tty_unlock();
return error;
} /* end of n_hdlc_tty_write() */
diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c
index c1d8b54..a98290d 100644
--- a/drivers/char/n_r3964.c
+++ b/drivers/char/n_r3964.c
@@ -1067,7 +1067,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
TRACE_L("read()");
- lock_kernel();
+ tty_lock();
pClient = findClient(pInfo, task_pid(current));
if (pClient) {
@@ -1079,7 +1079,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
goto unlock;
}
/* block until there is a message: */
- wait_event_interruptible(pInfo->read_wait,
+ wait_event_interruptible_tty(pInfo->read_wait,
(pMsg = remove_msg(pInfo, pClient)));
}
@@ -1109,7 +1109,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
}
ret = -EPERM;
unlock:
- unlock_kernel();
+ tty_unlock();
return ret;
}
@@ -1158,7 +1158,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
pHeader->locks = 0;
pHeader->owner = NULL;
- lock_kernel();
+ tty_lock();
pClient = findClient(pInfo, task_pid(current));
if (pClient) {
@@ -1177,7 +1177,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
add_tx_queue(pInfo, pHeader);
trigger_transmit(pInfo);
- unlock_kernel();
+ tty_unlock();
return 0;
}
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index bdae832..428f4fe 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -1102,6 +1102,11 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
if (I_IUCLC(tty) && L_IEXTEN(tty))
c = tolower(c);
+ if (L_EXTPROC(tty)) {
+ put_tty_queue(c, tty);
+ return;
+ }
+
if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
@@ -1409,7 +1414,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
n_tty_set_room(tty);
- if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
+ if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
+ L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
@@ -1585,7 +1591,7 @@ static int n_tty_open(struct tty_struct *tty)
static inline int input_available_p(struct tty_struct *tty, int amt)
{
tty_flush_to_ldisc(tty);
- if (tty->icanon) {
+ if (tty->icanon && !L_EXTPROC(tty)) {
if (tty->canon_data)
return 1;
} else if (tty->read_cnt >= (amt ? amt : 1))
@@ -1632,6 +1638,11 @@ static int copy_from_read_buf(struct tty_struct *tty,
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
+ /* Turn single EOF into zero-length read */
+ if (L_EXTPROC(tty) && tty->icanon && n == 1) {
+ if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
+ n--;
+ }
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
@@ -1812,7 +1823,7 @@ do_it_again:
nr--;
}
- if (tty->icanon) {
+ if (tty->icanon && !L_EXTPROC(tty)) {
/* N.B. avoid overrun if nr == 0 */
while (nr && tty->read_cnt) {
int eol;
diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c
index a663800..817169c 100644
--- a/drivers/char/nozomi.c
+++ b/drivers/char/nozomi.c
@@ -1611,6 +1611,8 @@ static int ntty_install(struct tty_driver *driver, struct tty_struct *tty)
ret = tty_init_termios(tty);
if (ret == 0) {
tty_driver_kref_get(driver);
+ tty->count++;
+ tty->driver_data = port;
driver->ttys[tty->index] = tty;
}
return ret;
@@ -1639,7 +1641,7 @@ static int ntty_activate(struct tty_port *tport, struct tty_struct *tty)
static int ntty_open(struct tty_struct *tty, struct file *filp)
{
- struct port *port = get_port_by_tty(tty);
+ struct port *port = tty->driver_data;
return tty_port_open(&port->port, tty, filp);
}
@@ -1741,8 +1743,7 @@ static int ntty_write_room(struct tty_struct *tty)
if (dc) {
mutex_lock(&port->tty_sem);
if (port->port.count)
- room = port->fifo_ul.size -
- kfifo_len(&port->fifo_ul);
+ room = kfifo_avail(&port->fifo_ul);
mutex_unlock(&port->tty_sem);
}
return room;
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index d83a431..ad46eae 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -62,7 +62,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
if (tty->driver == ptm_driver)
devpts_pty_kill(tty->link);
#endif
+ tty_unlock();
tty_vhangup(tty->link);
+ tty_lock();
}
}
@@ -171,6 +173,23 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg)
return 0;
}
+/* Send a signal to the slave */
+static int pty_signal(struct tty_struct *tty, int sig)
+{
+ unsigned long flags;
+ struct pid *pgrp;
+
+ if (tty->link) {
+ spin_lock_irqsave(&tty->link->ctrl_lock, flags);
+ pgrp = get_pid(tty->link->pgrp);
+ spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
+
+ kill_pgrp(pgrp, sig, 1);
+ put_pid(pgrp);
+ }
+ return 0;
+}
+
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
@@ -321,6 +340,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
switch (cmd) {
case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
return pty_set_lock(tty, (int __user *) arg);
+ case TIOCSIG: /* Send signal to other side of pty */
+ return pty_signal(tty, (int) arg);
}
return -ENOIOCTLCMD;
}
@@ -476,6 +497,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
return pty_set_lock(tty, (int __user *)arg);
case TIOCGPTN: /* Get PT Number */
return put_user(tty->index, (unsigned int __user *)arg);
+ case TIOCSIG: /* Send signal to other side of pty */
+ return pty_signal(tty, (int) arg);
}
return -ENOIOCTLCMD;
@@ -626,7 +649,7 @@ static const struct tty_operations pty_unix98_ops = {
* allocated_ptys_lock handles the list of free pty numbers
*/
-static int __ptmx_open(struct inode *inode, struct file *filp)
+static int ptmx_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int retval;
@@ -635,11 +658,14 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
nonseekable_open(inode, filp);
/* find a device that is not in use. */
+ tty_lock();
index = devpts_new_index(inode);
+ tty_unlock();
if (index < 0)
return index;
mutex_lock(&tty_mutex);
+ tty_lock();
tty = tty_init_dev(ptm_driver, index, 1);
mutex_unlock(&tty_mutex);
@@ -657,26 +683,21 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
goto out1;
retval = ptm_driver->ops->open(tty, filp);
- if (!retval)
- return 0;
+ if (retval)
+ goto out2;
out1:
+ tty_unlock();
+ return retval;
+out2:
+ tty_unlock();
tty_release(inode, filp);
return retval;
out:
devpts_kill_index(inode, index);
+ tty_unlock();
return retval;
}
-static int ptmx_open(struct inode *inode, struct file *filp)
-{
- int ret;
-
- lock_kernel();
- ret = __ptmx_open(inode, filp);
- unlock_kernel();
- return ret;
-}
-
static struct file_operations ptmx_fops;
static void __init unix98_pty_init(void)
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index b02332a..af4de1f 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -47,7 +47,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/tty_flip.h>
-#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/device.h>
@@ -1184,6 +1183,7 @@ static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
return -EFAULT;
+ mutex_lock(&port->port.mutex);
change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
(tmp.flags & ASYNC_SPD_MASK));
@@ -1191,8 +1191,10 @@ static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
if ((tmp.close_delay != port->port.close_delay) ||
(tmp.closing_wait != port->port.closing_wait) ||
((tmp.flags & ~ASYNC_USR_MASK) !=
- (port->port.flags & ~ASYNC_USR_MASK)))
+ (port->port.flags & ~ASYNC_USR_MASK))) {
+ mutex_unlock(&port->port.mutex);
return -EPERM;
+ }
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
(tmp.flags & ASYNC_USR_MASK));
} else {
@@ -1208,6 +1210,7 @@ static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
rc_change_speed(tty, bp, port);
spin_unlock_irqrestore(&riscom_lock, flags);
}
+ mutex_unlock(&port->port.mutex);
return 0;
}
@@ -1220,12 +1223,15 @@ static int rc_get_serial_info(struct riscom_port *port,
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_CIRRUS;
tmp.line = port - rc_port;
+
+ mutex_lock(&port->port.mutex);
tmp.port = bp->base;
tmp.irq = bp->irq;
tmp.flags = port->port.flags;
tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
tmp.close_delay = port->port.close_delay * HZ/100;
tmp.closing_wait = port->port.closing_wait * HZ/100;
+ mutex_unlock(&port->port.mutex);
tmp.xmit_fifo_size = CD180_NFIFO;
return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
@@ -1242,14 +1248,10 @@ static int rc_ioctl(struct tty_struct *tty, struct file *filp,
switch (cmd) {
case TIOCGSERIAL:
- lock_kernel();
retval = rc_get_serial_info(port, argp);
- unlock_kernel();
break;
case TIOCSSERIAL:
- lock_kernel();
retval = rc_set_serial_info(tty, port, argp);
- unlock_kernel();
break;
default:
retval = -ENOIOCTLCMD;
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
index 0e29a23..79c3bc6 100644
--- a/drivers/char/rocket.c
+++ b/drivers/char/rocket.c
@@ -73,7 +73,6 @@
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
-#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
@@ -1017,6 +1016,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp)
if (tty_port_close_start(port, tty, filp) == 0)
return;
+ mutex_lock(&port->mutex);
cp = &info->channel;
/*
* Before we drop DTR, make sure the UART transmitter
@@ -1060,9 +1060,13 @@ static void rp_close(struct tty_struct *tty, struct file *filp)
info->xmit_buf = NULL;
}
}
+ spin_lock_irq(&port->lock);
info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
tty->closing = 0;
+ spin_unlock_irq(&port->lock);
+ mutex_unlock(&port->mutex);
tty_port_tty_set(port, NULL);
+
wake_up_interruptible(&port->close_wait);
complete_all(&info->close_wait);
atomic_dec(&rp_num_ports_open);
@@ -1210,11 +1214,13 @@ static int get_config(struct r_port *info, struct rocket_config __user *retinfo)
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof (tmp));
+ mutex_lock(&info->port.mutex);
tmp.line = info->line;
tmp.flags = info->flags;
tmp.close_delay = info->port.close_delay;
tmp.closing_wait = info->port.closing_wait;
tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
+ mutex_unlock(&info->port.mutex);
if (copy_to_user(retinfo, &tmp, sizeof (*retinfo)))
return -EFAULT;
@@ -1229,10 +1235,13 @@ static int set_config(struct tty_struct *tty, struct r_port *info,
if (copy_from_user(&new_serial, new_info, sizeof (new_serial)))
return -EFAULT;
+ mutex_lock(&info->port.mutex);
if (!capable(CAP_SYS_ADMIN))
{
- if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK))
+ if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) {
+ mutex_unlock(&info->port.mutex);
return -EPERM;
+ }
info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
configure_r_port(tty, info, NULL);
return 0;
@@ -1250,6 +1259,7 @@ static int set_config(struct tty_struct *tty, struct r_port *info,
tty->alt_speed = 230400;
if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
tty->alt_speed = 460800;
+ mutex_unlock(&info->port.mutex);
configure_r_port(tty, info, NULL);
return 0;
@@ -1325,8 +1335,6 @@ static int rp_ioctl(struct tty_struct *tty, struct file *file,
if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl"))
return -ENXIO;
- lock_kernel();
-
switch (cmd) {
case RCKP_GET_STRUCT:
if (copy_to_user(argp, info, sizeof (struct r_port)))
@@ -1350,7 +1358,6 @@ static int rp_ioctl(struct tty_struct *tty, struct file *file,
default:
ret = -ENOIOCTLCMD;
}
- unlock_kernel();
return ret;
}
@@ -1471,7 +1478,6 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
jiffies);
printk(KERN_INFO "cps=%d...\n", info->cps);
#endif
- lock_kernel();
while (1) {
txcnt = sGetTxCnt(cp);
if (!txcnt) {
@@ -1499,7 +1505,6 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
break;
}
__set_current_state(TASK_RUNNING);
- unlock_kernel();
#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
#endif
@@ -1512,6 +1517,7 @@ static void rp_hangup(struct tty_struct *tty)
{
CHANNEL_t *cp;
struct r_port *info = tty->driver_data;
+ unsigned long flags;
if (rocket_paranoia_check(info, "rp_hangup"))
return;
@@ -1520,11 +1526,15 @@ static void rp_hangup(struct tty_struct *tty)
printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line);
#endif
rp_flush_buffer(tty);
- if (info->port.flags & ASYNC_CLOSING)
+ spin_lock_irqsave(&info->port.lock, flags);
+ if (info->port.flags & ASYNC_CLOSING) {
+ spin_unlock_irqrestore(&info->port.lock, flags);
return;
+ }
if (info->port.count)
atomic_dec(&rp_num_ports_open);
clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+ spin_unlock_irqrestore(&info->port.lock, flags);
tty_port_hangup(&info->port);
@@ -1535,7 +1545,7 @@ static void rp_hangup(struct tty_struct *tty)
sDisCTSFlowCtl(cp);
sDisTxSoftFlowCtl(cp);
sClrTxXOFF(cp);
- info->port.flags &= ~ASYNC_INITIALIZED;
+ clear_bit(ASYNCB_INITIALIZED, &info->port.flags);
wake_up_interruptible(&info->port.open_wait);
}
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 95acb8c..dfa8b30 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -961,7 +961,7 @@ static int __init rtc_init(void)
#endif
#ifdef CONFIG_SPARC32
struct device_node *ebus_dp;
- struct of_device *op;
+ struct platform_device *op;
#else
void *r;
#ifdef RTC_IRQ
diff --git a/drivers/char/selection.c b/drivers/char/selection.c
index f97b9e8..ebae344 100644
--- a/drivers/char/selection.c
+++ b/drivers/char/selection.c
@@ -26,6 +26,7 @@
#include <linux/selection.h>
#include <linux/tiocl.h>
#include <linux/console.h>
+#include <linux/smp_lock.h>
/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
#define isspace(c) ((c) == ' ')
@@ -312,12 +313,20 @@ int paste_selection(struct tty_struct *tty)
struct tty_ldisc *ld;
DECLARE_WAITQUEUE(wait, current);
+ /* always called with BTM from vt_ioctl */
+ WARN_ON(!tty_locked());
+
acquire_console_sem();
poke_blanked_console();
release_console_sem();
- ld = tty_ldisc_ref_wait(tty);
-
+ ld = tty_ldisc_ref(tty);
+ if (!ld) {
+ tty_unlock();
+ ld = tty_ldisc_ref_wait(tty);
+ tty_lock();
+ }
+
add_wait_queue(&vc->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c
index ecbe479..f646725 100644
--- a/drivers/char/serial167.c
+++ b/drivers/char/serial167.c
@@ -1505,7 +1505,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */
#endif
- lock_kernel();
+ tty_lock();
switch (cmd) {
case CYGETMON:
@@ -1561,7 +1561,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file,
default:
ret_val = -ENOIOCTLCMD;
}
- unlock_kernel();
+ tty_unlock();
#ifdef SERIAL_DEBUG_OTHER
printk("cy_ioctl done\n");
@@ -1786,7 +1786,9 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
tty->name, info->count);
/**/
#endif
- schedule();
+ tty_unlock();
+ schedule();
+ tty_lock();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
index 2c24fcd..9f8495b 100644
--- a/drivers/char/specialix.c
+++ b/drivers/char/specialix.c
@@ -1365,7 +1365,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
retval = -ERESTARTSYS;
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
@@ -1863,8 +1865,7 @@ static int sx_set_serial_info(struct specialix_port *port,
return -EFAULT;
}
- lock_kernel();
-
+ mutex_lock(&port->port.mutex);
change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
(tmp.flags & ASYNC_SPD_MASK));
change_speed |= (tmp.custom_divisor != port->custom_divisor);
@@ -1875,7 +1876,7 @@ static int sx_set_serial_info(struct specialix_port *port,
((tmp.flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
func_exit();
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
@@ -1892,7 +1893,7 @@ static int sx_set_serial_info(struct specialix_port *port,
sx_change_speed(bp, port);
func_exit();
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return 0;
}
@@ -1906,7 +1907,7 @@ static int sx_get_serial_info(struct specialix_port *port,
func_enter();
memset(&tmp, 0, sizeof(tmp));
- lock_kernel();
+ mutex_lock(&port->port.mutex);
tmp.type = PORT_CIRRUS;
tmp.line = port - sx_port;
tmp.port = bp->base;
@@ -1917,7 +1918,7 @@ static int sx_get_serial_info(struct specialix_port *port,
tmp.closing_wait = port->port.closing_wait * HZ/100;
tmp.custom_divisor = port->custom_divisor;
tmp.xmit_fifo_size = CD186x_NFIFO;
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
func_exit();
return -EFAULT;
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 6049fd7..f2167f8 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -807,7 +807,6 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)
timeout = HZ;
tend = jiffies + timeout;
- lock_kernel();
while (stl_datastate(portp)) {
if (signal_pending(current))
break;
@@ -815,7 +814,6 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)
if (time_after_eq(jiffies, tend))
break;
}
- unlock_kernel();
}
/*****************************************************************************/
@@ -1029,6 +1027,8 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp)
pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp);
memset(&sio, 0, sizeof(struct serial_struct));
+
+ mutex_lock(&portp->port.mutex);
sio.line = portp->portnr;
sio.port = portp->ioaddr;
sio.flags = portp->port.flags;
@@ -1048,6 +1048,7 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp)
brdp = stl_brds[portp->brdnr];
if (brdp != NULL)
sio.irq = brdp->irq;
+ mutex_unlock(&portp->port.mutex);
return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ? -EFAULT : 0;
}
@@ -1069,12 +1070,15 @@ static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp
if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
return -EFAULT;
+ mutex_lock(&portp->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if ((sio.baud_base != portp->baud_base) ||
(sio.close_delay != portp->close_delay) ||
((sio.flags & ~ASYNC_USR_MASK) !=
- (portp->port.flags & ~ASYNC_USR_MASK)))
+ (portp->port.flags & ~ASYNC_USR_MASK))) {
+ mutex_unlock(&portp->port.mutex);
return -EPERM;
+ }
}
portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
@@ -1083,6 +1087,7 @@ static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp
portp->close_delay = sio.close_delay;
portp->closing_wait = sio.closing_wait;
portp->custom_divisor = sio.custom_divisor;
+ mutex_unlock(&portp->port.mutex);
stl_setport(portp, tty->termios);
return 0;
}
@@ -1147,8 +1152,6 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd
rc = 0;
- lock_kernel();
-
switch (cmd) {
case TIOCGSERIAL:
rc = stl_getserial(portp, argp);
@@ -1173,7 +1176,6 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
return rc;
}
@@ -2327,6 +2329,7 @@ static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comst
return -ENODEV;
}
+ mutex_lock(&portp->port.mutex);
portp->stats.state = portp->istate;
portp->stats.flags = portp->port.flags;
portp->stats.hwid = portp->hwid;
@@ -2358,6 +2361,7 @@ static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comst
(STL_TXBUFSIZE - (tail - head));
portp->stats.signals = (unsigned long) stl_getsignals(portp);
+ mutex_unlock(&portp->port.mutex);
return copy_to_user(cp, &portp->stats,
sizeof(comstats_t)) ? -EFAULT : 0;
@@ -2382,10 +2386,12 @@ static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp)
return -ENODEV;
}
+ mutex_lock(&portp->port.mutex);
memset(&portp->stats, 0, sizeof(comstats_t));
portp->stats.brd = portp->brdnr;
portp->stats.panel = portp->panelnr;
portp->stats.port = portp->portnr;
+ mutex_unlock(&portp->port.mutex);
return copy_to_user(cp, &portp->stats,
sizeof(comstats_t)) ? -EFAULT : 0;
}
@@ -2451,7 +2457,6 @@ static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
return -ENODEV;
rc = 0;
- lock_kernel();
switch (cmd) {
case COM_GETPORTSTATS:
rc = stl_getportstats(NULL, NULL, argp);
@@ -2472,7 +2477,6 @@ static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
return rc;
}
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index a81ec4f..5b24db4 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -1699,7 +1699,7 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- lock_kernel();
+ tty_lock();
sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
@@ -1848,7 +1848,7 @@ static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
break;
}
out:
- unlock_kernel();
+ tty_unlock();
func_exit();
return rc;
}
@@ -1859,7 +1859,7 @@ static int sx_break(struct tty_struct *tty, int flag)
int rv;
func_enter();
- lock_kernel();
+ tty_lock();
if (flag)
rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK);
@@ -1868,7 +1868,7 @@ static int sx_break(struct tty_struct *tty, int flag)
if (rv != 1)
printk(KERN_ERR "sx: couldn't send break (%x).\n",
read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
- unlock_kernel();
+ tty_unlock();
func_exit();
return 0;
}
@@ -1909,7 +1909,7 @@ static int sx_ioctl(struct tty_struct *tty, struct file *filp,
/* func_enter2(); */
rc = 0;
- lock_kernel();
+ tty_lock();
switch (cmd) {
case TIOCGSERIAL:
rc = gs_getserial(&port->gs, argp);
@@ -1921,7 +1921,7 @@ static int sx_ioctl(struct tty_struct *tty, struct file *filp,
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
+ tty_unlock();
/* func_exit(); */
return rc;
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
index 0658fc5..a2a5800 100644
--- a/drivers/char/synclink.c
+++ b/drivers/char/synclink.c
@@ -81,7 +81,6 @@
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
@@ -2436,7 +2435,9 @@ static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *
if (!user_icount) {
memset(&info->icount, 0, sizeof(info->icount));
} else {
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+ mutex_unlock(&info->port.mutex);
if (err)
return -EFAULT;
}
@@ -2461,7 +2462,9 @@ static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_p
printk("%s(%d):mgsl_get_params(%s)\n",
__FILE__,__LINE__, info->device_name);
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+ mutex_unlock(&info->port.mutex);
if (err) {
if ( debug_level >= DEBUG_LEVEL_INFO )
printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
@@ -2501,11 +2504,13 @@ static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_pa
return -EFAULT;
}
+ mutex_lock(&info->port.mutex);
spin_lock_irqsave(&info->irq_spinlock,flags);
memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
spin_unlock_irqrestore(&info->irq_spinlock,flags);
mgsl_change_params(info);
+ mutex_unlock(&info->port.mutex);
return 0;
@@ -2935,7 +2940,6 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct mgsl_struct * info = tty->driver_data;
- int ret;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
@@ -2950,10 +2954,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file,
return -EIO;
}
- lock_kernel();
- ret = mgsl_ioctl_common(info, cmd, arg);
- unlock_kernel();
- return ret;
+ return mgsl_ioctl_common(info, cmd, arg);
}
static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
@@ -3109,12 +3110,14 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
-
+
+ mutex_lock(&info->port.mutex);
if (info->port.flags & ASYNC_INITIALIZED)
mgsl_wait_until_sent(tty, info->timeout);
mgsl_flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
+ mutex_unlock(&info->port.mutex);
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
@@ -3162,7 +3165,6 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
* Note: use tight timings here to satisfy the NIST-PCTS.
*/
- lock_kernel();
if ( info->params.data_rate ) {
char_time = info->timeout/(32 * 5);
if (!char_time)
@@ -3192,7 +3194,6 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
break;
}
}
- unlock_kernel();
exit:
if (debug_level >= DEBUG_LEVEL_INFO)
@@ -3348,7 +3349,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
printk("%s(%d):block_til_ready blocking on %s count=%d\n",
__FILE__,__LINE__, tty->driver->name, port->count );
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c
index 334cf5c..fef80cf 100644
--- a/drivers/char/synclink_gt.c
+++ b/drivers/char/synclink_gt.c
@@ -40,8 +40,8 @@
#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
-//#define DBGTBUF(info) dump_tbufs(info)
-//#define DBGRBUF(info) dump_rbufs(info)
+/*#define DBGTBUF(info) dump_tbufs(info)*/
+/*#define DBGRBUF(info) dump_rbufs(info)*/
#include <linux/module.h>
@@ -62,7 +62,6 @@
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
@@ -676,12 +675,14 @@ static int open(struct tty_struct *tty, struct file *filp)
goto cleanup;
}
+ mutex_lock(&info->port.mutex);
info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
spin_lock_irqsave(&info->netlock, flags);
if (info->netcount) {
retval = -EBUSY;
spin_unlock_irqrestore(&info->netlock, flags);
+ mutex_unlock(&info->port.mutex);
goto cleanup;
}
info->port.count++;
@@ -693,7 +694,7 @@ static int open(struct tty_struct *tty, struct file *filp)
if (retval < 0)
goto cleanup;
}
-
+ mutex_unlock(&info->port.mutex);
retval = block_til_ready(tty, filp, info);
if (retval) {
DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
@@ -725,12 +726,14 @@ static void close(struct tty_struct *tty, struct file *filp)
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
+ mutex_lock(&info->port.mutex);
if (info->port.flags & ASYNC_INITIALIZED)
wait_until_sent(tty, info->timeout);
flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
+ mutex_unlock(&info->port.mutex);
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
@@ -741,17 +744,23 @@ cleanup:
static void hangup(struct tty_struct *tty)
{
struct slgt_info *info = tty->driver_data;
+ unsigned long flags;
if (sanity_check(info, tty->name, "hangup"))
return;
DBGINFO(("%s hangup\n", info->device_name));
flush_buffer(tty);
+
+ mutex_lock(&info->port.mutex);
shutdown(info);
+ spin_lock_irqsave(&info->port.lock, flags);
info->port.count = 0;
info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
info->port.tty = NULL;
+ spin_unlock_irqrestore(&info->port.lock, flags);
+ mutex_unlock(&info->port.mutex);
wake_up_interruptible(&info->port.open_wait);
}
@@ -901,8 +910,6 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
* Note: use tight timings here to satisfy the NIST-PCTS.
*/
- lock_kernel();
-
if (info->params.data_rate) {
char_time = info->timeout/(32 * 5);
if (!char_time)
@@ -920,8 +927,6 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
- unlock_kernel();
-
exit:
DBGINFO(("%s wait_until_sent exit\n", info->device_name));
}
@@ -1041,8 +1046,37 @@ static int ioctl(struct tty_struct *tty, struct file *file,
return -EIO;
}
- lock_kernel();
-
+ switch (cmd) {
+ case MGSL_IOCWAITEVENT:
+ return wait_mgsl_event(info, argp);
+ case TIOCMIWAIT:
+ return modem_input_wait(info,(int)arg);
+ case TIOCGICOUNT:
+ spin_lock_irqsave(&info->lock,flags);
+ cnow = info->icount;
+ spin_unlock_irqrestore(&info->lock,flags);
+ p_cuser = argp;
+ if (put_user(cnow.cts, &p_cuser->cts) ||
+ put_user(cnow.dsr, &p_cuser->dsr) ||
+ put_user(cnow.rng, &p_cuser->rng) ||
+ put_user(cnow.dcd, &p_cuser->dcd) ||
+ put_user(cnow.rx, &p_cuser->rx) ||
+ put_user(cnow.tx, &p_cuser->tx) ||
+ put_user(cnow.frame, &p_cuser->frame) ||
+ put_user(cnow.overrun, &p_cuser->overrun) ||
+ put_user(cnow.parity, &p_cuser->parity) ||
+ put_user(cnow.brk, &p_cuser->brk) ||
+ put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
+ return -EFAULT;
+ return 0;
+ case MGSL_IOCSGPIO:
+ return set_gpio(info, argp);
+ case MGSL_IOCGGPIO:
+ return get_gpio(info, argp);
+ case MGSL_IOCWAITGPIO:
+ return wait_gpio(info, argp);
+ }
+ mutex_lock(&info->port.mutex);
switch (cmd) {
case MGSL_IOCGPARAMS:
ret = get_params(info, argp);
@@ -1068,50 +1102,16 @@ static int ioctl(struct tty_struct *tty, struct file *file,
case MGSL_IOCGSTATS:
ret = get_stats(info, argp);
break;
- case MGSL_IOCWAITEVENT:
- ret = wait_mgsl_event(info, argp);
- break;
- case TIOCMIWAIT:
- ret = modem_input_wait(info,(int)arg);
- break;
case MGSL_IOCGIF:
ret = get_interface(info, argp);
break;
case MGSL_IOCSIF:
ret = set_interface(info,(int)arg);
break;
- case MGSL_IOCSGPIO:
- ret = set_gpio(info, argp);
- break;
- case MGSL_IOCGGPIO:
- ret = get_gpio(info, argp);
- break;
- case MGSL_IOCWAITGPIO:
- ret = wait_gpio(info, argp);
- break;
- case TIOCGICOUNT:
- spin_lock_irqsave(&info->lock,flags);
- cnow = info->icount;
- spin_unlock_irqrestore(&info->lock,flags);
- p_cuser = argp;
- if (put_user(cnow.cts, &p_cuser->cts) ||
- put_user(cnow.dsr, &p_cuser->dsr) ||
- put_user(cnow.rng, &p_cuser->rng) ||
- put_user(cnow.dcd, &p_cuser->dcd) ||
- put_user(cnow.rx, &p_cuser->rx) ||
- put_user(cnow.tx, &p_cuser->tx) ||
- put_user(cnow.frame, &p_cuser->frame) ||
- put_user(cnow.overrun, &p_cuser->overrun) ||
- put_user(cnow.parity, &p_cuser->parity) ||
- put_user(cnow.brk, &p_cuser->brk) ||
- put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
- ret = -EFAULT;
- ret = 0;
- break;
default:
ret = -ENOIOCTLCMD;
}
- unlock_kernel();
+ mutex_unlock(&info->port.mutex);
return ret;
}
@@ -3244,7 +3244,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
}
DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
index 2b18adc..e56caf7 100644
--- a/drivers/char/synclinkmp.c
+++ b/drivers/char/synclinkmp.c
@@ -52,7 +52,6 @@
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
@@ -813,13 +812,15 @@ static void close(struct tty_struct *tty, struct file *filp)
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
-
+
+ mutex_lock(&info->port.mutex);
if (info->port.flags & ASYNC_INITIALIZED)
wait_until_sent(tty, info->timeout);
flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
+ mutex_unlock(&info->port.mutex);
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
@@ -835,6 +836,7 @@ cleanup:
static void hangup(struct tty_struct *tty)
{
SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s hangup()\n",
@@ -843,12 +845,16 @@ static void hangup(struct tty_struct *tty)
if (sanity_check(info, tty->name, "hangup"))
return;
+ mutex_lock(&info->port.mutex);
flush_buffer(tty);
shutdown(info);
+ spin_lock_irqsave(&info->port.lock, flags);
info->port.count = 0;
info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
info->port.tty = NULL;
+ spin_unlock_irqrestore(&info->port.lock, flags);
+ mutex_unlock(&info->port.mutex);
wake_up_interruptible(&info->port.open_wait);
}
@@ -1062,9 +1068,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
if (sanity_check(info, tty->name, "wait_until_sent"))
return;
- lock_kernel();
-
- if (!(info->port.flags & ASYNC_INITIALIZED))
+ if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags))
goto exit;
orig_jiffies = jiffies;
@@ -1094,8 +1098,10 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
break;
}
} else {
- //TODO: determine if there is something similar to USC16C32
- // TXSTATUS_ALL_SENT status
+ /*
+ * TODO: determine if there is something similar to USC16C32
+ * TXSTATUS_ALL_SENT status
+ */
while ( info->tx_active && info->tx_enabled) {
msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
@@ -1106,7 +1112,6 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
}
exit:
- unlock_kernel();
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s wait_until_sent() exit\n",
__FILE__,__LINE__, info->device_name );
@@ -1122,7 +1127,6 @@ static int write_room(struct tty_struct *tty)
if (sanity_check(info, tty->name, "write_room"))
return 0;
- lock_kernel();
if (info->params.mode == MGSL_MODE_HDLC) {
ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
} else {
@@ -1130,7 +1134,6 @@ static int write_room(struct tty_struct *tty)
if (ret < 0)
ret = 0;
}
- unlock_kernel();
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s write_room()=%d\n",
@@ -1251,7 +1254,7 @@ static void tx_release(struct tty_struct *tty)
*
* Return Value: 0 if success, otherwise error code
*/
-static int do_ioctl(struct tty_struct *tty, struct file *file,
+static int ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
SLMP_INFO *info = tty->driver_data;
@@ -1341,16 +1344,6 @@ static int do_ioctl(struct tty_struct *tty, struct file *file,
return 0;
}
-static int ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret;
- lock_kernel();
- ret = do_ioctl(tty, file, cmd, arg);
- unlock_kernel();
- return ret;
-}
-
/*
* /proc fs routines....
*/
@@ -2883,7 +2876,9 @@ static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount)
if (!user_icount) {
memset(&info->icount, 0, sizeof(info->icount));
} else {
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+ mutex_unlock(&info->port.mutex);
if (err)
return -EFAULT;
}
@@ -2898,7 +2893,9 @@ static int get_params(SLMP_INFO * info, MGSL_PARAMS __user *user_params)
printk("%s(%d):%s get_params()\n",
__FILE__,__LINE__, info->device_name);
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+ mutex_unlock(&info->port.mutex);
if (err) {
if ( debug_level >= DEBUG_LEVEL_INFO )
printk( "%s(%d):%s get_params() user buffer copy failed\n",
@@ -2926,11 +2923,13 @@ static int set_params(SLMP_INFO * info, MGSL_PARAMS __user *new_params)
return -EFAULT;
}
+ mutex_lock(&info->port.mutex);
spin_lock_irqsave(&info->lock,flags);
memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
spin_unlock_irqrestore(&info->lock,flags);
change_params(info);
+ mutex_unlock(&info->port.mutex);
return 0;
}
@@ -3366,7 +3365,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
printk("%s(%d):%s block_til_ready() count=%d\n",
__FILE__,__LINE__, tty->driver->name, port->count );
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 507441a..0350c42 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -149,6 +149,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
#else
#define tty_compat_ioctl NULL
#endif
+static int __tty_fasync(int fd, struct file *filp, int on);
static int tty_fasync(int fd, struct file *filp, int on);
static void release_tty(struct tty_struct *tty, int idx);
static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
@@ -470,7 +471,7 @@ void tty_wakeup(struct tty_struct *tty)
EXPORT_SYMBOL_GPL(tty_wakeup);
/**
- * do_tty_hangup - actual handler for hangup events
+ * __tty_hangup - actual handler for hangup events
* @work: tty device
*
* This can be called by the "eventd" kernel thread. That is process
@@ -483,7 +484,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
* remains intact.
*
* Locking:
- * BKL
+ * BTM
* redirect lock for undoing redirection
* file list lock for manipulating list of ttys
* tty_ldisc_lock from called functions
@@ -491,10 +492,8 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
* tasklist_lock to walk task list for hangup event
* ->siglock to protect ->signal/->sighand
*/
-static void do_tty_hangup(struct work_struct *work)
+void __tty_hangup(struct tty_struct *tty)
{
- struct tty_struct *tty =
- container_of(work, struct tty_struct, hangup_work);
struct file *cons_filp = NULL;
struct file *filp, *f = NULL;
struct task_struct *p;
@@ -513,9 +512,12 @@ static void do_tty_hangup(struct work_struct *work)
}
spin_unlock(&redirect_lock);
- /* inuse_filps is protected by the single kernel lock */
- lock_kernel();
- check_tty_count(tty, "do_tty_hangup");
+ tty_lock();
+
+ /* inuse_filps is protected by the single tty lock,
+ this really needs to change if we want to flush the
+ workqueue with the lock held */
+ check_tty_count(tty, "tty_hangup");
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
@@ -525,7 +527,7 @@ static void do_tty_hangup(struct work_struct *work)
if (filp->f_op->write != tty_write)
continue;
closecount++;
- tty_fasync(-1, filp, 0); /* can't block */
+ __tty_fasync(-1, filp, 0); /* can't block */
filp->f_op = &hung_up_tty_fops;
}
file_list_unlock();
@@ -594,11 +596,21 @@ static void do_tty_hangup(struct work_struct *work)
*/
set_bit(TTY_HUPPED, &tty->flags);
tty_ldisc_enable(tty);
- unlock_kernel();
+
+ tty_unlock();
+
if (f)
fput(f);
}
+static void do_tty_hangup(struct work_struct *work)
+{
+ struct tty_struct *tty =
+ container_of(work, struct tty_struct, hangup_work);
+
+ __tty_hangup(tty);
+}
+
/**
* tty_hangup - trigger a hangup event
* @tty: tty to hangup
@@ -634,11 +646,12 @@ void tty_vhangup(struct tty_struct *tty)
printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
#endif
- do_tty_hangup(&tty->hangup_work);
+ __tty_hangup(tty);
}
EXPORT_SYMBOL(tty_vhangup);
+
/**
* tty_vhangup_self - process vhangup for own ctty
*
@@ -696,7 +709,8 @@ static void session_clear_tty(struct pid *session)
* exiting; it is 0 if called by the ioctl TIOCNOTTY.
*
* Locking:
- * BKL is taken for hysterical raisins
+ * BTM is taken for hysterical raisins, and held when
+ * called from no_tty().
* tty_mutex is taken to protect tty
* ->siglock is taken to protect ->signal/->sighand
* tasklist_lock is taken to walk process list for sessions
@@ -714,10 +728,10 @@ void disassociate_ctty(int on_exit)
tty = get_current_tty();
if (tty) {
tty_pgrp = get_pid(tty->pgrp);
- lock_kernel();
- if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
- tty_vhangup(tty);
- unlock_kernel();
+ if (on_exit) {
+ if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
+ tty_vhangup(tty);
+ }
tty_kref_put(tty);
} else if (on_exit) {
struct pid *old_pgrp;
@@ -774,9 +788,9 @@ void disassociate_ctty(int on_exit)
void no_tty(void)
{
struct task_struct *tsk = current;
- lock_kernel();
+ tty_lock();
disassociate_ctty(0);
- unlock_kernel();
+ tty_unlock();
proc_clear_tty(tsk);
}
@@ -879,7 +893,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
struct inode *inode;
struct tty_ldisc *ld;
- tty = (struct tty_struct *)file->private_data;
+ tty = file->private_data;
inode = file->f_path.dentry->d_inode;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
@@ -1013,19 +1027,19 @@ out:
* We don't put it into the syslog queue right now maybe in the future if
* really needed.
*
- * We must still hold the BKL and test the CLOSING flag for the moment.
+ * We must still hold the BTM and test the CLOSING flag for the moment.
*/
void tty_write_message(struct tty_struct *tty, char *msg)
{
if (tty) {
mutex_lock(&tty->atomic_write_lock);
- lock_kernel();
+ tty_lock();
if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
- unlock_kernel();
+ tty_unlock();
tty->ops->write(tty, msg, strlen(msg));
} else
- unlock_kernel();
+ tty_unlock();
tty_write_unlock(tty);
}
return;
@@ -1056,7 +1070,7 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
ssize_t ret;
struct tty_ldisc *ld;
- tty = (struct tty_struct *)file->private_data;
+ tty = file->private_data;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
@@ -1208,18 +1222,14 @@ static int tty_driver_install_tty(struct tty_driver *driver,
int ret;
if (driver->ops->install) {
- lock_kernel();
ret = driver->ops->install(driver, tty);
- unlock_kernel();
return ret;
}
if (tty_init_termios(tty) == 0) {
- lock_kernel();
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[idx] = tty;
- unlock_kernel();
return 0;
}
return -ENOMEM;
@@ -1312,14 +1322,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
struct tty_struct *tty;
int retval;
- lock_kernel();
/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
- unlock_kernel();
return ERR_PTR(-EIO);
}
- unlock_kernel();
/*
* First time open is complex, especially for PTY devices.
@@ -1363,9 +1370,7 @@ release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
- lock_kernel();
release_tty(tty, idx);
- unlock_kernel();
return ERR_PTR(retval);
}
@@ -1508,14 +1513,14 @@ int tty_release(struct inode *inode, struct file *filp)
int idx;
char buf[64];
- tty = (struct tty_struct *)filp->private_data;
+ tty = filp->private_data;
if (tty_paranoia_check(tty, inode, "tty_release_dev"))
return 0;
- lock_kernel();
+ tty_lock();
check_tty_count(tty, "tty_release_dev");
- tty_fasync(-1, filp, 0);
+ __tty_fasync(-1, filp, 0);
idx = tty->index;
pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
@@ -1527,18 +1532,18 @@ int tty_release(struct inode *inode, struct file *filp)
if (idx < 0 || idx >= tty->driver->num) {
printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
"free (%s)\n", tty->name);
- unlock_kernel();
+ tty_unlock();
return 0;
}
if (!devpts) {
if (tty != tty->driver->ttys[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name);
return 0;
}
if (tty->termios != tty->driver->termios[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, tty->name);
@@ -1556,21 +1561,21 @@ int tty_release(struct inode *inode, struct file *filp)
if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
"not o_tty for (%s)\n",
idx, tty->name);
return 0 ;
}
if (o_tty->termios != tty->driver->other->termios[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
"not o_termios for (%s)\n",
idx, tty->name);
return 0;
}
if (o_tty->link != tty) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
return 0;
}
@@ -1579,7 +1584,7 @@ int tty_release(struct inode *inode, struct file *filp)
if (tty->ops->close)
tty->ops->close(tty, filp);
- unlock_kernel();
+ tty_unlock();
/*
* Sanity check: if tty->count is going to zero, there shouldn't be
* any waiters on tty->read_wait or tty->write_wait. We test the
@@ -1602,7 +1607,7 @@ int tty_release(struct inode *inode, struct file *filp)
opens on /dev/tty */
mutex_lock(&tty_mutex);
- lock_kernel();
+ tty_lock();
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
@@ -1633,7 +1638,7 @@ int tty_release(struct inode *inode, struct file *filp)
printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
schedule();
}
@@ -1698,7 +1703,7 @@ int tty_release(struct inode *inode, struct file *filp)
/* check whether both sides are closing ... */
if (!tty_closing || (o_tty && !o_tty_closing)) {
- unlock_kernel();
+ tty_unlock();
return 0;
}
@@ -1718,7 +1723,7 @@ int tty_release(struct inode *inode, struct file *filp)
/* Make this pty number available for reallocation */
if (devpts)
devpts_kill_index(inode, idx);
- unlock_kernel();
+ tty_unlock();
return 0;
}
@@ -1760,12 +1765,12 @@ retry_open:
retval = 0;
mutex_lock(&tty_mutex);
- lock_kernel();
+ tty_lock();
if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
@@ -1797,14 +1802,14 @@ retry_open:
goto got_driver;
}
}
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
driver = get_tty_driver(device, &index);
if (!driver) {
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
@@ -1814,7 +1819,7 @@ got_driver:
tty = tty_driver_lookup_tty(driver, inode, index);
if (IS_ERR(tty)) {
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
@@ -1830,7 +1835,7 @@ got_driver:
mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
- unlock_kernel();
+ tty_unlock();
return PTR_ERR(tty);
}
@@ -1860,29 +1865,29 @@ got_driver:
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
+ tty_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
- if (retval != -ERESTARTSYS) {
- unlock_kernel();
+ if (retval != -ERESTARTSYS)
return retval;
- }
- if (signal_pending(current)) {
- unlock_kernel();
+
+ if (signal_pending(current))
return retval;
- }
+
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
+ tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
- unlock_kernel();
+ tty_unlock();
goto retry_open;
}
- unlock_kernel();
+ tty_unlock();
mutex_lock(&tty_mutex);
- lock_kernel();
+ tty_lock();
spin_lock_irq(&current->sighand->siglock);
if (!noctty &&
current->signal->leader &&
@@ -1890,7 +1895,7 @@ got_driver:
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(&current->sighand->siglock);
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return 0;
}
@@ -1915,7 +1920,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
struct tty_ldisc *ld;
int ret = 0;
- tty = (struct tty_struct *)filp->private_data;
+ tty = filp->private_data;
if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
return 0;
@@ -1926,14 +1931,13 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
return ret;
}
-static int tty_fasync(int fd, struct file *filp, int on)
+static int __tty_fasync(int fd, struct file *filp, int on)
{
struct tty_struct *tty;
unsigned long flags;
int retval = 0;
- lock_kernel();
- tty = (struct tty_struct *)filp->private_data;
+ tty = filp->private_data;
if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
goto out;
@@ -1966,7 +1970,15 @@ static int tty_fasync(int fd, struct file *filp, int on)
}
retval = 0;
out:
- unlock_kernel();
+ return retval;
+}
+
+static int tty_fasync(int fd, struct file *filp, int on)
+{
+ int retval;
+ tty_lock();
+ retval = __tty_fasync(fd, filp, on);
+ tty_unlock();
return retval;
}
@@ -2485,7 +2497,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct tty_ldisc *ld;
struct inode *inode = file->f_dentry->d_inode;
- tty = (struct tty_struct *)file->private_data;
+ tty = file->private_data;
if (tty_paranoia_check(tty, inode, "tty_ioctl"))
return -EINVAL;
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 6bd5f88..0c18899 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -517,19 +517,25 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
/* See if packet mode change of state. */
if (tty->link && tty->link->packet) {
+ int extproc = (old_termios.c_lflag & EXTPROC) |
+ (tty->termios->c_lflag & EXTPROC);
int old_flow = ((old_termios.c_iflag & IXON) &&
(old_termios.c_cc[VSTOP] == '\023') &&
(old_termios.c_cc[VSTART] == '\021'));
int new_flow = (I_IXON(tty) &&
STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021');
- if (old_flow != new_flow) {
+ if ((old_flow != new_flow) || extproc) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
- tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
- if (new_flow)
- tty->ctrl_status |= TIOCPKT_DOSTOP;
- else
- tty->ctrl_status |= TIOCPKT_NOSTOP;
+ if (old_flow != new_flow) {
+ tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+ if (new_flow)
+ tty->ctrl_status |= TIOCPKT_DOSTOP;
+ else
+ tty->ctrl_status |= TIOCPKT_NOSTOP;
+ }
+ if (extproc)
+ tty->ctrl_status |= TIOCPKT_IOCTL;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
wake_up_interruptible(&tty->link->read_wait);
}
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index 500e740..412f977 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -440,6 +440,8 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
*
* A helper opening method. Also a convenient debugging and check
* point.
+ *
+ * Locking: always called with BTM already held.
*/
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
@@ -447,10 +449,9 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
if (ld->ops->open) {
int ret;
- /* BKL here locks verus a hangup event */
- lock_kernel();
+ /* BTM here locks versus a hangup event */
+ WARN_ON(!tty_locked());
ret = ld->ops->open(tty);
- unlock_kernel();
return ret;
}
return 0;
@@ -553,7 +554,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);
- lock_kernel();
+ tty_lock();
/*
* We need to look at the tty locking here for pty/tty pairs
* when both sides try to change in parallel.
@@ -567,12 +568,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
*/
if (tty->ldisc->ops->num == ldisc) {
- unlock_kernel();
+ tty_unlock();
tty_ldisc_put(new_ldisc);
return 0;
}
- unlock_kernel();
+ tty_unlock();
/*
* Problem: What do we do if this blocks ?
* We could deadlock here
@@ -580,6 +581,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty_wait_until_sent(tty, 0);
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
/*
@@ -589,13 +591,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
mutex_unlock(&tty->ldisc_mutex);
+ tty_unlock();
wait_event(tty_ldisc_wait,
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
}
- lock_kernel();
-
set_bit(TTY_LDISC_CHANGING, &tty->flags);
/*
@@ -607,7 +609,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
o_ldisc = tty->ldisc;
- unlock_kernel();
+ tty_unlock();
/*
* Make sure we don't change while someone holds a
* reference to the line discipline. The TTY_LDISC bit
@@ -632,15 +634,15 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
flush_scheduled_work();
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
- lock_kernel();
if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
mutex_unlock(&tty->ldisc_mutex);
tty_ldisc_put(new_ldisc);
- unlock_kernel();
+ tty_unlock();
return -EIO;
}
@@ -682,7 +684,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1);
mutex_unlock(&tty->ldisc_mutex);
- unlock_kernel();
+ tty_unlock();
return retval;
}
@@ -780,7 +782,20 @@ void tty_ldisc_hangup(struct tty_struct *tty)
* Avoid racing set_ldisc or tty_ldisc_release
*/
mutex_lock(&tty->ldisc_mutex);
- tty_ldisc_halt(tty);
+
+ /*
+ * this is like tty_ldisc_halt, but we need to give up
+ * the BTM before calling cancel_delayed_work_sync,
+ * which may need to wait for another function taking the BTM
+ */
+ clear_bit(TTY_LDISC, &tty->flags);
+ tty_unlock();
+ cancel_delayed_work_sync(&tty->buf.work);
+ mutex_unlock(&tty->ldisc_mutex);
+
+ tty_lock();
+ mutex_lock(&tty->ldisc_mutex);
+
/* At this point we have a closed ldisc and we want to
reopen it. We could defer this to the next open but
it means auditing a lot of other paths so this is
@@ -851,8 +866,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
* race with the set_ldisc code path.
*/
+ tty_unlock();
tty_ldisc_halt(tty);
flush_scheduled_work();
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
/*
diff --git a/drivers/char/tty_mutex.c b/drivers/char/tty_mutex.c
new file mode 100644
index 0000000..1336975
--- /dev/null
+++ b/drivers/char/tty_mutex.c
@@ -0,0 +1,47 @@
+/*
+ * drivers/char/tty_lock.c
+ */
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+
+/*
+ * The 'big tty mutex'
+ *
+ * This mutex is taken and released by tty_lock() and tty_unlock(),
+ * replacing the older big kernel lock.
+ * It can no longer be taken recursively, and does not get
+ * released implicitly while sleeping.
+ *
+ * Don't use in new code.
+ */
+static DEFINE_MUTEX(big_tty_mutex);
+struct task_struct *__big_tty_mutex_owner;
+EXPORT_SYMBOL_GPL(__big_tty_mutex_owner);
+
+/*
+ * Getting the big tty mutex.
+ */
+void __lockfunc tty_lock(void)
+{
+ struct task_struct *task = current;
+
+ WARN_ON(__big_tty_mutex_owner == task);
+
+ mutex_lock(&big_tty_mutex);
+ __big_tty_mutex_owner = task;
+}
+EXPORT_SYMBOL(tty_lock);
+
+void __lockfunc tty_unlock(void)
+{
+ struct task_struct *task = current;
+
+ WARN_ON(__big_tty_mutex_owner != task);
+ __big_tty_mutex_owner = NULL;
+
+ mutex_unlock(&big_tty_mutex);
+}
+EXPORT_SYMBOL(tty_unlock);
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index a3bd1d0..33d37d2 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -231,7 +231,7 @@ int tty_port_block_til_ready(struct tty_port *port,
/* block if port is in the process of being closed */
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
- wait_event_interruptible(port->close_wait,
+ wait_event_interruptible_tty(port->close_wait,
!(port->flags & ASYNC_CLOSING));
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
@@ -294,7 +294,9 @@ int tty_port_block_til_ready(struct tty_port *port,
retval = -ERESTARTSYS;
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
finish_wait(&port->open_wait, &wait);
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index c1791a6..bcce46c 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -463,10 +463,10 @@ vcs_open(struct inode *inode, struct file *filp)
unsigned int currcons = iminor(inode) & 127;
int ret = 0;
- lock_kernel();
+ tty_lock();
if(currcons && !vc_cons_allocated(currcons-1))
ret = -ENXIO;
- unlock_kernel();
+ tty_unlock();
return ret;
}
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 44f03dd..c734f9b 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -105,6 +105,7 @@
#include <asm/system.h>
#include <linux/uaccess.h>
#include <linux/kdb.h>
+#include <linux/ctype.h>
#define MAX_NR_CON_DRIVER 16
@@ -286,8 +287,12 @@ static inline unsigned short *screenpos(struct vc_data *vc, int offset, int view
return p;
}
+/* Called from the keyboard irq path.. */
static inline void scrolldelta(int lines)
{
+ /* FIXME */
+ /* scrolldelta needs some kind of consistency lock, but the BKL was
+ and still is not protecting versus the scheduled back end */
scrollback_delta += lines;
schedule_console_callback();
}
@@ -704,7 +709,10 @@ void redraw_screen(struct vc_data *vc, int is_switch)
update_attr(vc);
clear_buffer_attributes(vc);
}
- if (update && vc->vc_mode != KD_GRAPHICS)
+
+ /* Forcibly update if we're panicing */
+ if ((update && vc->vc_mode != KD_GRAPHICS) ||
+ vt_force_oops_output(vc))
do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
}
set_cursor(vc);
@@ -742,6 +750,7 @@ static void visual_init(struct vc_data *vc, int num, int init)
vc->vc_hi_font_mask = 0;
vc->vc_complement_mask = 0;
vc->vc_can_do_color = 0;
+ vc->vc_panic_force_write = false;
vc->vc_sw->con_init(vc, init);
if (!vc->vc_complement_mask)
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
@@ -774,6 +783,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
if (!vc)
return -ENOMEM;
vc_cons[currcons].d = vc;
+ tty_port_init(&vc->port);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
visual_init(vc, currcons, 1);
if (!*vc->vc_uni_pagedir_loc)
@@ -963,12 +973,12 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
* Resize a virtual console as seen from the console end of things. We
* use the common vc_do_resize methods to update the structures. The
* caller must hold the console sem to protect console internals and
- * vc->vc_tty
+ * vc->port.tty
*/
int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
{
- return vc_do_resize(vc->vc_tty, vc, cols, rows);
+ return vc_do_resize(vc->port.tty, vc, cols, rows);
}
/**
@@ -1796,8 +1806,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
vc->vc_state = ESnormal;
return;
case ESpalette:
- if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
- vc->vc_par[vc->vc_npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0');
+ if (isxdigit(c)) {
+ vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
if (vc->vc_npar == 7) {
int i = vc->vc_par[0] * 3, j = 1;
vc->vc_palette[i] = 16 * vc->vc_par[j++];
@@ -2505,7 +2515,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
goto quit;
}
- if (vc->vc_mode != KD_TEXT)
+ if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
goto quit;
/* undraw cursor first */
@@ -2611,8 +2621,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
return -EFAULT;
ret = 0;
- lock_kernel();
-
switch (type)
{
case TIOCL_SETSEL:
@@ -2687,7 +2695,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
ret = -EINVAL;
break;
}
- unlock_kernel();
return ret;
}
@@ -2800,12 +2807,12 @@ static int con_open(struct tty_struct *tty, struct file *filp)
struct vc_data *vc = vc_cons[currcons].d;
/* Still being freed */
- if (vc->vc_tty) {
+ if (vc->port.tty) {
release_console_sem();
return -ERESTARTSYS;
}
tty->driver_data = vc;
- vc->vc_tty = tty;
+ vc->port.tty = tty;
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
@@ -2833,7 +2840,7 @@ static void con_shutdown(struct tty_struct *tty)
struct vc_data *vc = tty->driver_data;
BUG_ON(vc == NULL);
acquire_console_sem();
- vc->vc_tty = NULL;
+ vc->port.tty = NULL;
release_console_sem();
tty_shutdown(tty);
}
@@ -2915,6 +2922,7 @@ static int __init con_init(void)
for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+ tty_port_init(&vc->port);
visual_init(vc, currcons, 1);
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, vc->vc_rows, vc->vc_cols,
@@ -3783,7 +3791,8 @@ void do_unblank_screen(int leaving_gfx)
return;
}
vc = vc_cons[fg_console].d;
- if (vc->vc_mode != KD_TEXT)
+ /* Try to unblank in oops case too */
+ if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
return; /* but leave console_blanked != 0 */
if (blankinterval) {
@@ -3792,7 +3801,7 @@ void do_unblank_screen(int leaving_gfx)
}
console_blanked = 0;
- if (vc->vc_sw->con_blank(vc, 0, leaving_gfx))
+ if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
/* Low-level driver cannot restore -> do it ourselves */
update_screen(vc);
if (console_blank_hook)
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c
index cb19dbc..2bbeaae 100644
--- a/drivers/char/vt_ioctl.c
+++ b/drivers/char/vt_ioctl.c
@@ -133,7 +133,7 @@ static void vt_event_wait(struct vt_event_wait *vw)
list_add(&vw->list, &vt_events);
spin_unlock_irqrestore(&vt_event_lock, flags);
/* Wait for it to pass */
- wait_event_interruptible(vt_event_waitqueue, vw->done);
+ wait_event_interruptible_tty(vt_event_waitqueue, vw->done);
/* Dequeue it */
spin_lock_irqsave(&vt_event_lock, flags);
list_del(&vw->list);
@@ -509,7 +509,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
console = vc->vc_num;
- lock_kernel();
+ tty_lock();
if (!vc_cons_allocated(console)) { /* impossible? */
ret = -ENOIOCTLCMD;
@@ -1336,7 +1336,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
ret = -ENOIOCTLCMD;
}
out:
- unlock_kernel();
+ tty_unlock();
return ret;
eperm:
ret = -EPERM;
@@ -1369,7 +1369,7 @@ void vc_SAK(struct work_struct *work)
acquire_console_sem();
vc = vc_con->d;
if (vc) {
- tty = vc->vc_tty;
+ tty = vc->port.tty;
/*
* SAK should also work in all raw modes and reset
* them properly.
@@ -1503,7 +1503,7 @@ long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
console = vc->vc_num;
- lock_kernel();
+ tty_lock();
if (!vc_cons_allocated(console)) { /* impossible? */
ret = -ENOIOCTLCMD;
@@ -1571,11 +1571,11 @@ long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
goto fallback;
}
out:
- unlock_kernel();
+ tty_unlock();
return ret;
fallback:
- unlock_kernel();
+ tty_unlock();
return vt_ioctl(tty, file, cmd, arg);
}
@@ -1761,10 +1761,13 @@ int vt_move_to_console(unsigned int vt, int alloc)
return -EIO;
}
release_console_sem();
+ tty_lock();
if (vt_waitactive(vt + 1)) {
pr_debug("Suspend: Can't switch VCs.");
+ tty_unlock();
return -EINTR;
}
+ tty_unlock();
return prev;
}
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index ed8a9ce..0ed763c 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -761,7 +761,7 @@ static struct platform_driver hwicap_platform_driver = {
#if defined(CONFIG_OF)
static int __devinit
-hwicap_of_probe(struct of_device *op, const struct of_device_id *match)
+hwicap_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct resource res;
const unsigned int *id;
@@ -798,7 +798,7 @@ hwicap_of_probe(struct of_device *op, const struct of_device_id *match)
regs);
}
-static int __devexit hwicap_of_remove(struct of_device *op)
+static int __devexit hwicap_of_remove(struct platform_device *op)
{
return hwicap_remove(&op->dev);
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index fbf94cf..ea0b386 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -77,7 +77,7 @@ config ZCRYPT
config ZCRYPT_MONOLITHIC
bool "Monolithic zcrypt module"
- depends on ZCRYPT="m"
+ depends on ZCRYPT
help
Select this option if you want to have a single module z90crypt,
that contains all parts of the crypto device driver (ap bus,
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 983530b..2b1baee 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -1150,7 +1150,7 @@ struct crypto4xx_alg_common crypto4xx_alg[] = {
/**
* Module Initialization Routine
*/
-static int __init crypto4xx_probe(struct of_device *ofdev,
+static int __init crypto4xx_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int rc;
@@ -1258,7 +1258,7 @@ err_alloc_dev:
return rc;
}
-static int __exit crypto4xx_remove(struct of_device *ofdev)
+static int __exit crypto4xx_remove(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
index da9cbe3..bac0bde 100644
--- a/drivers/crypto/amcc/crypto4xx_core.h
+++ b/drivers/crypto/amcc/crypto4xx_core.h
@@ -104,7 +104,7 @@ struct crypto4xx_device {
struct crypto4xx_core_device {
struct device *device;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct crypto4xx_device *dev;
u32 int_status;
u32 irq;
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index f17ddf3..0d66221 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -97,8 +97,13 @@
struct buffer_desc {
u32 phys_next;
+#ifdef __ARMEB__
u16 buf_len;
u16 pkt_len;
+#else
+ u16 pkt_len;
+ u16 buf_len;
+#endif
u32 phys_addr;
u32 __reserved[4];
struct buffer_desc *next;
@@ -106,17 +111,30 @@ struct buffer_desc {
};
struct crypt_ctl {
+#ifdef __ARMEB__
u8 mode; /* NPE_OP_* operation mode */
u8 init_len;
u16 reserved;
+#else
+ u16 reserved;
+ u8 init_len;
+ u8 mode; /* NPE_OP_* operation mode */
+#endif
u8 iv[MAX_IVLEN]; /* IV for CBC mode or CTR IV for CTR mode */
u32 icv_rev_aes; /* icv or rev aes */
u32 src_buf;
u32 dst_buf;
+#ifdef __ARMEB__
u16 auth_offs; /* Authentication start offset */
u16 auth_len; /* Authentication data length */
u16 crypt_offs; /* Cryption start offset */
u16 crypt_len; /* Cryption data length */
+#else
+ u16 auth_len; /* Authentication data length */
+ u16 auth_offs; /* Authentication start offset */
+ u16 crypt_len; /* Cryption data length */
+ u16 crypt_offs; /* Cryption start offset */
+#endif
u32 aadAddr; /* Additional Auth Data Addr for CCM mode */
u32 crypto_ctx; /* NPE Crypto Param structure address */
@@ -652,6 +670,9 @@ static int setup_auth(struct crypto_tfm *tfm, int encrypt, unsigned authsize,
/* write cfg word to cryptinfo */
cfgword = algo->cfgword | ( authsize << 6); /* (authsize/4) << 8 */
+#ifndef __ARMEB__
+ cfgword ^= 0xAA000000; /* change the "byte swap" flags */
+#endif
*(u32*)cinfo = cpu_to_be32(cfgword);
cinfo += sizeof(cfgword);
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 26af2dd..88ee015 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1552,7 +1552,7 @@ static void __exit n2_unregister_algs(void)
/* To map CWQ queues to interrupt sources, the hypervisor API provides
* a devino. This isn't very useful to us because all of the
- * interrupts listed in the of_device node have been translated to
+ * interrupts listed in the device_node have been translated to
* Linux virtual IRQ cookie numbers.
*
* So we have to back-translate, going through the 'intr' and 'ino'
@@ -1560,7 +1560,7 @@ static void __exit n2_unregister_algs(void)
* 'interrupts' property entries, in order to to figure out which
* devino goes to which already-translated IRQ.
*/
-static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip,
+static int find_devino_index(struct platform_device *dev, struct spu_mdesc_info *ip,
unsigned long dev_ino)
{
const unsigned int *dev_intrs;
@@ -1580,7 +1580,7 @@ static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip,
if (!dev_intrs)
return -ENODEV;
- for (i = 0; i < dev->num_irqs; i++) {
+ for (i = 0; i < dev->archdata.num_irqs; i++) {
if (dev_intrs[i] == intr)
return i;
}
@@ -1588,7 +1588,7 @@ static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip,
return -ENODEV;
}
-static int spu_map_ino(struct of_device *dev, struct spu_mdesc_info *ip,
+static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip,
const char *irq_name, struct spu_queue *p,
irq_handler_t handler)
{
@@ -1603,7 +1603,7 @@ static int spu_map_ino(struct of_device *dev, struct spu_mdesc_info *ip,
if (index < 0)
return index;
- p->irq = dev->irqs[index];
+ p->irq = dev->archdata.irqs[index];
sprintf(p->irq_name, "%s-%d", irq_name, index);
@@ -1736,7 +1736,7 @@ static void spu_list_destroy(struct list_head *list)
* gathering cpu membership information.
*/
static int spu_mdesc_walk_arcs(struct mdesc_handle *mdesc,
- struct of_device *dev,
+ struct platform_device *dev,
u64 node, struct spu_queue *p,
struct spu_queue **table)
{
@@ -1763,7 +1763,7 @@ static int spu_mdesc_walk_arcs(struct mdesc_handle *mdesc,
/* Process an 'exec-unit' MDESC node of type 'cwq'. */
static int handle_exec_unit(struct spu_mdesc_info *ip, struct list_head *list,
- struct of_device *dev, struct mdesc_handle *mdesc,
+ struct platform_device *dev, struct mdesc_handle *mdesc,
u64 node, const char *iname, unsigned long q_type,
irq_handler_t handler, struct spu_queue **table)
{
@@ -1794,7 +1794,7 @@ static int handle_exec_unit(struct spu_mdesc_info *ip, struct list_head *list,
return spu_map_ino(dev, ip, iname, p, handler);
}
-static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct of_device *dev,
+static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct platform_device *dev,
struct spu_mdesc_info *ip, struct list_head *list,
const char *exec_name, unsigned long q_type,
irq_handler_t handler, struct spu_queue **table)
@@ -1855,7 +1855,7 @@ static int __devinit get_irq_props(struct mdesc_handle *mdesc, u64 node,
}
static int __devinit grab_mdesc_irq_props(struct mdesc_handle *mdesc,
- struct of_device *dev,
+ struct platform_device *dev,
struct spu_mdesc_info *ip,
const char *node_name)
{
@@ -2004,7 +2004,7 @@ static void __devinit n2_spu_driver_version(void)
pr_info("%s", version);
}
-static int __devinit n2_crypto_probe(struct of_device *dev,
+static int __devinit n2_crypto_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct mdesc_handle *mdesc;
@@ -2081,7 +2081,7 @@ out_free_n2cp:
return err;
}
-static int __devexit n2_crypto_remove(struct of_device *dev)
+static int __devexit n2_crypto_remove(struct platform_device *dev)
{
struct n2_crypto *np = dev_get_drvdata(&dev->dev);
@@ -2116,7 +2116,7 @@ static void free_ncp(struct n2_mau *mp)
kfree(mp);
}
-static int __devinit n2_mau_probe(struct of_device *dev,
+static int __devinit n2_mau_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct mdesc_handle *mdesc;
@@ -2184,7 +2184,7 @@ out_free_ncp:
return err;
}
-static int __devexit n2_mau_remove(struct of_device *dev)
+static int __devexit n2_mau_remove(struct platform_device *dev)
{
struct n2_mau *mp = dev_get_drvdata(&dev->dev);
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 97f4af1..4bcd825 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -118,7 +118,7 @@ struct talitos_channel {
struct talitos_private {
struct device *dev;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
void __iomem *reg;
int irq;
@@ -2308,7 +2308,7 @@ static int hw_supports(struct device *dev, __be32 desc_hdr_template)
return ret;
}
-static int talitos_remove(struct of_device *ofdev)
+static int talitos_remove(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct talitos_private *priv = dev_get_drvdata(dev);
@@ -2401,7 +2401,7 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
return t_alg;
}
-static int talitos_probe(struct of_device *ofdev,
+static int talitos_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device *dev = &ofdev->dev;
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index fed5763..9520cf0 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -141,7 +141,7 @@ config TXX9_DMAC
config SH_DMAE
tristate "Renesas SuperH DMAC support"
- depends on SUPERH && SH_DMA
+ depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
depends on !SH_DMA_API
select DMA_ENGINE
help
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index f0fd6db..cea08be 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -1297,7 +1297,7 @@ static void fsl_dma_chan_remove(struct fsldma_chan *chan)
kfree(chan);
}
-static int __devinit fsldma_of_probe(struct of_device *op,
+static int __devinit fsldma_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct fsldma_device *fdev;
@@ -1382,7 +1382,7 @@ out_return:
return err;
}
-static int fsldma_of_remove(struct of_device *op)
+static int fsldma_of_remove(struct platform_device *op)
{
struct fsldma_device *fdev;
unsigned int i;
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index 14a8c0f..4e9cbf3 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -627,7 +627,7 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
return &mdesc->desc;
}
-static int __devinit mpc_dma_probe(struct of_device *op,
+static int __devinit mpc_dma_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
@@ -753,7 +753,7 @@ static int __devinit mpc_dma_probe(struct of_device *op,
return retval;
}
-static int __devexit mpc_dma_remove(struct of_device *op)
+static int __devexit mpc_dma_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct mpc_dma *mdma = dev_get_drvdata(dev);
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index 7c37479..0d58a4a 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -4257,11 +4257,11 @@ static int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
struct ppc440spe_adma_chan *chan,
int *initcode)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct device_node *np;
int ret;
- ofdev = container_of(adev->dev, struct of_device, dev);
+ ofdev = container_of(adev->dev, struct platform_device, dev);
np = ofdev->dev.of_node;
if (adev->id != PPC440SPE_XOR_ID) {
adev->err_irq = irq_of_parse_and_map(np, 1);
@@ -4393,7 +4393,7 @@ static void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
/**
* ppc440spe_adma_probe - probe the asynch device
*/
-static int __devinit ppc440spe_adma_probe(struct of_device *ofdev,
+static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -4625,7 +4625,7 @@ out:
/**
* ppc440spe_adma_remove - remove the asynch device
*/
-static int __devexit ppc440spe_adma_remove(struct of_device *ofdev)
+static int __devexit ppc440spe_adma_remove(struct platform_device *ofdev)
{
struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev);
struct device_node *np = ofdev->dev.of_node;
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index a2a519f..fb64cf3 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -816,7 +816,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data)
return ret;
}
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
@@ -1057,7 +1057,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
/* Default transfer size of 32 bytes requires 32-byte alignment */
shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!chanirq_res)
@@ -1082,7 +1082,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
#else
chanirq_res = errirq_res;
-#endif /* CONFIG_CPU_SH4 */
+#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */
if (chanirq_res->start == chanirq_res->end &&
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
@@ -1129,7 +1129,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
chan_probe_err:
sh_dmae_chan_remove(shdev);
eirqres:
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
free_irq(errirq, shdev);
eirq_err:
#endif
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 996c1bd..a5cefab 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1482,7 +1482,7 @@ static int __devinit i5000_init_one(struct pci_dev *pdev,
/* wake up device */
rc = pci_enable_device(pdev);
- if (rc == -EIO)
+ if (rc)
return rc;
/* now probe and enable the device */
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 010c1d6..38a9be9 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -1348,7 +1348,7 @@ static int __devinit i5400_init_one(struct pci_dev *pdev,
/* wake up device */
rc = pci_enable_device(pdev);
- if (rc == -EIO)
+ if (rc)
return rc;
/* now probe and enable the device */
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 1052340..b123bb3 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -43,7 +43,7 @@ static u32 orig_pci_err_en;
#endif
static u32 orig_l2_err_disable;
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static u32 orig_hid1[2];
#endif
@@ -200,7 +200,7 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit mpc85xx_pci_err_probe(struct of_device *op,
+static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct edac_pci_ctl_info *pci;
@@ -305,7 +305,7 @@ err:
return res;
}
-static int mpc85xx_pci_err_remove(struct of_device *op)
+static int mpc85xx_pci_err_remove(struct platform_device *op)
{
struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev);
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
@@ -503,7 +503,7 @@ static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit mpc85xx_l2_err_probe(struct of_device *op,
+static int __devinit mpc85xx_l2_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct edac_device_ctl_info *edac_dev;
@@ -613,7 +613,7 @@ err:
return res;
}
-static int mpc85xx_l2_err_remove(struct of_device *op)
+static int mpc85xx_l2_err_remove(struct platform_device *op)
{
struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev);
struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
@@ -647,7 +647,10 @@ static struct of_device_id mpc85xx_l2_err_of_match[] = {
{ .compatible = "fsl,mpc8555-l2-cache-controller", },
{ .compatible = "fsl,mpc8560-l2-cache-controller", },
{ .compatible = "fsl,mpc8568-l2-cache-controller", },
+ { .compatible = "fsl,mpc8569-l2-cache-controller", },
{ .compatible = "fsl,mpc8572-l2-cache-controller", },
+ { .compatible = "fsl,p1020-l2-cache-controller", },
+ { .compatible = "fsl,p1021-l2-cache-controller", },
{ .compatible = "fsl,p2020-l2-cache-controller", },
{},
};
@@ -953,7 +956,7 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
}
}
-static int __devinit mpc85xx_mc_err_probe(struct of_device *op,
+static int __devinit mpc85xx_mc_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mem_ctl_info *mci;
@@ -1085,7 +1088,7 @@ err:
return res;
}
-static int mpc85xx_mc_err_remove(struct of_device *op)
+static int mpc85xx_mc_err_remove(struct platform_device *op)
{
struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
@@ -1125,7 +1128,10 @@ static struct of_device_id mpc85xx_mc_err_of_match[] = {
{ .compatible = "fsl,mpc8569-memory-controller", },
{ .compatible = "fsl,mpc8572-memory-controller", },
{ .compatible = "fsl,mpc8349-memory-controller", },
+ { .compatible = "fsl,p1020-memory-controller", },
+ { .compatible = "fsl,p1021-memory-controller", },
{ .compatible = "fsl,p2020-memory-controller", },
+ { .compatible = "fsl,p4080-memory-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match);
@@ -1140,7 +1146,7 @@ static struct of_platform_driver mpc85xx_mc_err_driver = {
},
};
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static void __init mpc85xx_mc_clear_rfxe(void *data)
{
orig_hid1[smp_processor_id()] = mfspr(SPRN_HID1);
@@ -1179,7 +1185,7 @@ static int __init mpc85xx_mc_init(void)
printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n");
#endif
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
/*
* need to clear HID1[RFXE] to disable machine check int
* so we can catch it
@@ -1193,7 +1199,7 @@ static int __init mpc85xx_mc_init(void)
module_init(mpc85xx_mc_init);
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static void __exit mpc85xx_mc_restore_hid1(void *data)
{
mtspr(SPRN_HID1, orig_hid1[smp_processor_id()]);
@@ -1202,7 +1208,7 @@ static void __exit mpc85xx_mc_restore_hid1(void *data)
static void __exit mpc85xx_mc_exit(void)
{
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0);
#endif
#ifdef CONFIG_PCI
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index e78839e..070cea4 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -184,9 +184,9 @@ struct ppc4xx_ecc_status {
/* Function Prototypes */
-static int ppc4xx_edac_probe(struct of_device *device,
+static int ppc4xx_edac_probe(struct platform_device *device,
const struct of_device_id *device_id);
-static int ppc4xx_edac_remove(struct of_device *device);
+static int ppc4xx_edac_remove(struct platform_device *device);
/* Global Variables */
@@ -1014,7 +1014,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
*/
static int __devinit
ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
- struct of_device *op,
+ struct platform_device *op,
const struct of_device_id *match,
const dcr_host_t *dcr_host,
u32 mcopt1)
@@ -1108,7 +1108,7 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
* mapped and assigned.
*/
static int __devinit
-ppc4xx_edac_register_irq(struct of_device *op, struct mem_ctl_info *mci)
+ppc4xx_edac_register_irq(struct platform_device *op, struct mem_ctl_info *mci)
{
int status = 0;
int ded_irq, sec_irq;
@@ -1238,7 +1238,7 @@ ppc4xx_edac_map_dcrs(const struct device_node *np, dcr_host_t *dcr_host)
* driver; otherwise, < 0 on error.
*/
static int __devinit
-ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match)
+ppc4xx_edac_probe(struct platform_device *op, const struct of_device_id *match)
{
int status = 0;
u32 mcopt1, memcheck;
@@ -1359,7 +1359,7 @@ ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match)
* Unconditionally returns 0.
*/
static int
-ppc4xx_edac_remove(struct of_device *op)
+ppc4xx_edac_remove(struct platform_device *op)
{
struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index a6c670b..280c9b5 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -122,18 +122,10 @@ config ISCSI_IBFT_FIND
is necessary for iSCSI Boot Firmware Table Attributes module to work
properly.
-config ISCSI_BOOT_SYSFS
- tristate "iSCSI Boot Sysfs Interface"
- default n
- help
- This option enables support for exposing iSCSI boot information
- via sysfs to userspace. If you wish to export this information,
- say Y. Otherwise, say N.
-
config ISCSI_IBFT
tristate "iSCSI Boot Firmware Table Attributes module"
select ISCSI_BOOT_SYSFS
- depends on ISCSI_IBFT_FIND
+ depends on ISCSI_IBFT_FIND && SCSI
default n
help
This option enables support for detection and exposing of iSCSI
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 5fe7e16..1c3c173 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -10,5 +10,4 @@ obj-$(CONFIG_DCDBAS) += dcdbas.o
obj-$(CONFIG_DMIID) += dmi-id.o
obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
-obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 4f04ec0..6148a1c 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -727,8 +727,10 @@ static void ibft_unregister(void)
static void ibft_cleanup(void)
{
- ibft_unregister();
- iscsi_boot_destroy_kset(boot_kset);
+ if (boot_kset) {
+ ibft_unregister();
+ iscsi_boot_destroy_kset(boot_kset);
+ }
}
static void __exit ibft_exit(void)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7face91..510aa20 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -195,6 +195,24 @@ config GPIO_PCF857X
This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls.
+config GPIO_SX150X
+ bool "Semtech SX150x I2C GPIO expander"
+ depends on I2C=y
+ default n
+ help
+ Say yes here to provide support for Semtech SX150-series I2C
+ GPIO expanders. Compatible models include:
+
+ 8 bits: sx1508q
+ 16 bits: sx1509q
+
+config GPIO_STMPE
+ bool "STMPE GPIOs"
+ depends on MFD_STMPE
+ help
+ This enables support for the GPIOs found on the STMPE I/O
+ Expanders.
+
config GPIO_TC35892
bool "TC35892 GPIOs"
depends on MFD_TC35892
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index e53dcff..fc6019d 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
obj-$(CONFIG_GPIO_PL061) += pl061.o
+obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
+obj-$(CONFIG_GPIO_SX150X) += sx150x.o
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6a6bd56..21da9c1 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -57,9 +57,9 @@ struct gpio_desc {
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
-#define PDESC_ID_SHIFT 16 /* add new flags before this one */
+#define ID_SHIFT 16 /* add new flags before this one */
-#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1)
+#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
@@ -69,12 +69,7 @@ struct gpio_desc {
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
#ifdef CONFIG_GPIO_SYSFS
-struct poll_desc {
- struct work_struct work;
- struct sysfs_dirent *value_sd;
-};
-
-static struct idr pdesc_idr;
+static DEFINE_IDR(dirent_idr);
#endif
static inline void desc_set_label(struct gpio_desc *d, const char *label)
@@ -325,24 +320,16 @@ static const DEVICE_ATTR(value, 0644,
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
- struct work_struct *work = priv;
+ struct sysfs_dirent *value_sd = priv;
- schedule_work(work);
+ sysfs_notify_dirent(value_sd);
return IRQ_HANDLED;
}
-static void gpio_notify_sysfs(struct work_struct *work)
-{
- struct poll_desc *pdesc;
-
- pdesc = container_of(work, struct poll_desc, work);
- sysfs_notify_dirent(pdesc->value_sd);
-}
-
static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
unsigned long gpio_flags)
{
- struct poll_desc *pdesc;
+ struct sysfs_dirent *value_sd;
unsigned long irq_flags;
int ret, irq, id;
@@ -353,18 +340,16 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
if (irq < 0)
return -EIO;
- id = desc->flags >> PDESC_ID_SHIFT;
- pdesc = idr_find(&pdesc_idr, id);
- if (pdesc) {
- free_irq(irq, &pdesc->work);
- cancel_work_sync(&pdesc->work);
- }
+ id = desc->flags >> ID_SHIFT;
+ value_sd = idr_find(&dirent_idr, id);
+ if (value_sd)
+ free_irq(irq, value_sd);
desc->flags &= ~GPIO_TRIGGER_MASK;
if (!gpio_flags) {
ret = 0;
- goto free_sd;
+ goto free_id;
}
irq_flags = IRQF_SHARED;
@@ -375,55 +360,46 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
- if (!pdesc) {
- pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
- if (!pdesc) {
- ret = -ENOMEM;
+ if (!value_sd) {
+ value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+ if (!value_sd) {
+ ret = -ENODEV;
goto err_out;
}
do {
ret = -ENOMEM;
- if (idr_pre_get(&pdesc_idr, GFP_KERNEL))
- ret = idr_get_new_above(&pdesc_idr,
- pdesc, 1, &id);
+ if (idr_pre_get(&dirent_idr, GFP_KERNEL))
+ ret = idr_get_new_above(&dirent_idr, value_sd,
+ 1, &id);
} while (ret == -EAGAIN);
if (ret)
- goto free_mem;
+ goto free_sd;
desc->flags &= GPIO_FLAGS_MASK;
- desc->flags |= (unsigned long)id << PDESC_ID_SHIFT;
+ desc->flags |= (unsigned long)id << ID_SHIFT;
- if (desc->flags >> PDESC_ID_SHIFT != id) {
+ if (desc->flags >> ID_SHIFT != id) {
ret = -ERANGE;
goto free_id;
}
-
- pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
- if (!pdesc->value_sd) {
- ret = -ENODEV;
- goto free_id;
- }
- INIT_WORK(&pdesc->work, gpio_notify_sysfs);
}
- ret = request_irq(irq, gpio_sysfs_irq, irq_flags,
- "gpiolib", &pdesc->work);
- if (ret)
- goto free_sd;
+ ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
+ "gpiolib", value_sd);
+ if (ret < 0)
+ goto free_id;
desc->flags |= gpio_flags;
return 0;
-free_sd:
- if (pdesc)
- sysfs_put(pdesc->value_sd);
free_id:
- idr_remove(&pdesc_idr, id);
+ idr_remove(&dirent_idr, id);
desc->flags &= GPIO_FLAGS_MASK;
-free_mem:
- kfree(pdesc);
+free_sd:
+ if (value_sd)
+ sysfs_put(value_sd);
err_out:
return ret;
}
@@ -994,8 +970,6 @@ static int __init gpiolib_sysfs_init(void)
unsigned long flags;
unsigned gpio;
- idr_init(&pdesc_idr);
-
status = class_register(&gpio_class);
if (status < 0)
return status;
@@ -1272,7 +1246,7 @@ void gpio_free(unsigned gpio)
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
chip->free(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
}
@@ -1410,7 +1384,7 @@ int gpio_direction_input(unsigned gpio)
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
@@ -1463,7 +1437,7 @@ int gpio_direction_output(unsigned gpio, int value)
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
@@ -1521,7 +1495,7 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce)
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
return chip->set_debounce(chip, gpio, debounce);
@@ -1571,7 +1545,7 @@ int __gpio_get_value(unsigned gpio)
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
- WARN_ON(extra_checks && chip->can_sleep);
+ WARN_ON(chip->can_sleep);
return chip->get ? chip->get(chip, gpio - chip->base) : 0;
}
EXPORT_SYMBOL_GPL(__gpio_get_value);
@@ -1590,7 +1564,7 @@ void __gpio_set_value(unsigned gpio, int value)
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
- WARN_ON(extra_checks && chip->can_sleep);
+ WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(__gpio_set_value);
diff --git a/drivers/gpio/max730x.c b/drivers/gpio/max730x.c
index 7696a56..94ce773 100644
--- a/drivers/gpio/max730x.c
+++ b/drivers/gpio/max730x.c
@@ -54,7 +54,7 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct max7301 *ts = container_of(chip, struct max7301, chip);
u8 *config;
- u8 offset_bits;
+ u8 offset_bits, pin_config;
int ret;
/* First 4 pins are unused in the controller */
@@ -63,12 +63,15 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
config = &ts->port_config[offset >> 2];
+ if (ts->input_pullup_active & BIT(offset))
+ pin_config = PIN_CONFIG_IN_PULLUP;
+ else
+ pin_config = PIN_CONFIG_IN_WO_PULLUP;
+
mutex_lock(&ts->lock);
- /* Standard GPIO API doesn't support pull-ups, has to be extended.
- * Hard-coding no pollup for now. */
*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
- | (PIN_CONFIG_IN_WO_PULLUP << offset_bits);
+ | (pin_config << offset_bits);
ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
@@ -177,6 +180,7 @@ int __devinit __max730x_probe(struct max7301 *ts)
/* Power up the chip and disable IRQ output */
ts->write(dev, 0x04, 0x01);
+ ts->input_pullup_active = pdata->input_pullup_active;
ts->chip.label = dev->driver->name;
ts->chip.direction_input = max7301_direction_input;
@@ -191,13 +195,17 @@ int __devinit __max730x_probe(struct max7301 *ts)
ts->chip.owner = THIS_MODULE;
/*
- * tristate all pins in hardware and cache the
+ * initialize pullups according to platform data and cache the
* register values for later use.
*/
for (i = 1; i < 8; i++) {
int j;
- /* 0xAA means input with internal pullup disabled */
- ts->write(dev, 0x08 + i, 0xAA);
+ /*
+ * initialize port_config with "0xAA", which means
+ * input with internal pullup disabled. This is needed
+ * to avoid writing zeros (in the inner for loop),
+ * which is not allowed according to the datasheet.
+ */
ts->port_config[i] = 0xAA;
for (j = 0; j < 4; j++) {
int offset = (i - 1) * 4 + j;
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index 29f19ce..879b473 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -190,7 +190,6 @@ static int pcf857x_probe(struct i2c_client *client,
pdata = client->dev.platform_data;
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
- return -EINVAL;
}
/* Allocate, initialize, and register this gpio_chip. */
@@ -200,7 +199,7 @@ static int pcf857x_probe(struct i2c_client *client,
mutex_init(&gpio->lock);
- gpio->chip.base = pdata->gpio_base;
+ gpio->chip.base = pdata ? pdata->gpio_base : -1;
gpio->chip.can_sleep = 1;
gpio->chip.dev = &client->dev;
gpio->chip.owner = THIS_MODULE;
@@ -278,7 +277,7 @@ static int pcf857x_probe(struct i2c_client *client,
* to zero, our software copy of the "latch" then matches the chip's
* all-ones reset state. Otherwise it flags pins to be driven low.
*/
- gpio->out = ~pdata->n_latch;
+ gpio->out = pdata ? ~pdata->n_latch : ~0;
status = gpiochip_add(&gpio->chip);
if (status < 0)
@@ -299,7 +298,7 @@ static int pcf857x_probe(struct i2c_client *client,
/* Let platform code set up the GPIOs and their users.
* Now is the first time anyone could use them.
*/
- if (pdata->setup) {
+ if (pdata && pdata->setup) {
status = pdata->setup(client,
gpio->chip.base, gpio->chip.ngpio,
pdata->context);
@@ -322,7 +321,7 @@ static int pcf857x_remove(struct i2c_client *client)
struct pcf857x *gpio = i2c_get_clientdata(client);
int status = 0;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
status = pdata->teardown(client,
gpio->chip.base, gpio->chip.ngpio,
pdata->context);
diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c
new file mode 100644
index 0000000..4e1f1b9
--- /dev/null
+++ b/drivers/gpio/stmpe-gpio.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stmpe.h>
+
+/*
+ * These registers are modified under the irq bus lock and cached to avoid
+ * unnecessary writes in bus_sync_unlock.
+ */
+enum { REG_RE, REG_FE, REG_IE };
+
+#define CACHE_NR_REGS 3
+#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8)
+
+struct stmpe_gpio {
+ struct gpio_chip chip;
+ struct stmpe *stmpe;
+ struct device *dev;
+ struct mutex irq_lock;
+
+ int irq_base;
+
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
+ u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
+};
+
+static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct stmpe_gpio, chip);
+}
+
+static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+
+ ret = stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ return ret & mask;
+}
+
+static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB;
+ u8 reg = stmpe->regs[which] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_reg_write(stmpe, reg, mask);
+}
+
+static int stmpe_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_gpio_set(chip, offset, val);
+
+ return stmpe_set_bits(stmpe, reg, mask, mask);
+}
+
+static int stmpe_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ return stmpe_set_bits(stmpe, reg, mask, 0);
+}
+
+static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+
+ return stmpe_gpio->irq_base + offset;
+}
+
+static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+
+ return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "stmpe",
+ .owner = THIS_MODULE,
+ .direction_input = stmpe_gpio_direction_input,
+ .get = stmpe_gpio_get,
+ .direction_output = stmpe_gpio_direction_output,
+ .set = stmpe_gpio_set,
+ .to_irq = stmpe_gpio_to_irq,
+ .request = stmpe_gpio_request,
+ .can_sleep = 1,
+};
+
+static int stmpe_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+ return -EINVAL;
+
+ if (type == IRQ_TYPE_EDGE_RISING)
+ stmpe_gpio->regs[REG_RE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
+
+ if (type == IRQ_TYPE_EDGE_FALLING)
+ stmpe_gpio->regs[REG_FE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_lock(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+
+ mutex_lock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_sync_unlock(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ static const u8 regmap[] = {
+ [REG_RE] = STMPE_IDX_GPRER_LSB,
+ [REG_FE] = STMPE_IDX_GPFER_LSB,
+ [REG_IE] = STMPE_IDX_IEGPIOR_LSB,
+ };
+ int i, j;
+
+ for (i = 0; i < CACHE_NR_REGS; i++) {
+ for (j = 0; j < num_banks; j++) {
+ u8 old = stmpe_gpio->oldregs[i][j];
+ u8 new = stmpe_gpio->regs[i][j];
+
+ if (new == old)
+ continue;
+
+ stmpe_gpio->oldregs[i][j] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new);
+ }
+ }
+
+ mutex_unlock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_mask(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] &= ~mask;
+}
+
+static void stmpe_gpio_irq_unmask(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_gpio_irq_chip = {
+ .name = "stmpe-gpio",
+ .bus_lock = stmpe_gpio_irq_lock,
+ .bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
+ .mask = stmpe_gpio_irq_mask,
+ .unmask = stmpe_gpio_irq_unmask,
+ .set_type = stmpe_gpio_irq_set_type,
+};
+
+static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
+{
+ struct stmpe_gpio *stmpe_gpio = dev;
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ u8 status[num_banks];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < num_banks; i++) {
+ int bank = num_banks - i - 1;
+ unsigned int enabled = stmpe_gpio->regs[REG_IE][bank];
+ unsigned int stat = status[i];
+
+ stat &= enabled;
+ if (!stat)
+ continue;
+
+ while (stat) {
+ int bit = __ffs(stat);
+ int line = bank * 8 + bit;
+
+ handle_nested_irq(stmpe_gpio->irq_base + line);
+ stat &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i,
+ status[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
+{
+ int base = stmpe_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
+ set_irq_chip_data(irq, stmpe_gpio);
+ set_irq_chip_and_handler(irq, &stmpe_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
+{
+ int base = stmpe_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_gpio_platform_data *pdata;
+ struct stmpe_gpio *stmpe_gpio;
+ int ret;
+ int irq;
+
+ pdata = stmpe->pdata->gpio;
+ if (!pdata)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);
+ if (!stmpe_gpio)
+ return -ENOMEM;
+
+ mutex_init(&stmpe_gpio->irq_lock);
+
+ stmpe_gpio->dev = &pdev->dev;
+ stmpe_gpio->stmpe = stmpe;
+
+ stmpe_gpio->chip = template_chip;
+ stmpe_gpio->chip.ngpio = stmpe->num_gpios;
+ stmpe_gpio->chip.dev = &pdev->dev;
+ stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
+
+ stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret)
+ return ret;
+
+ ret = stmpe_gpio_irq_init(stmpe_gpio);
+ if (ret)
+ goto out_free;
+
+ ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT,
+ "stmpe-gpio", stmpe_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = gpiochip_add(&stmpe_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ goto out_freeirq;
+ }
+
+ if (pdata && pdata->setup)
+ pdata->setup(stmpe, stmpe_gpio->chip.base);
+
+ platform_set_drvdata(pdev, stmpe_gpio);
+
+ return 0;
+
+out_freeirq:
+ free_irq(irq, stmpe_gpio);
+out_removeirq:
+ stmpe_gpio_irq_remove(stmpe_gpio);
+out_free:
+ kfree(stmpe_gpio);
+ return ret;
+}
+
+static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
+{
+ struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio;
+ int irq = platform_get_irq(pdev, 0);
+ int ret;
+
+ if (pdata && pdata->remove)
+ pdata->remove(stmpe, stmpe_gpio->chip.base);
+
+ ret = gpiochip_remove(&stmpe_gpio->chip);
+ if (ret < 0) {
+ dev_err(stmpe_gpio->dev,
+ "unable to remove gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+
+ free_irq(irq, stmpe_gpio);
+ stmpe_gpio_irq_remove(stmpe_gpio);
+ platform_set_drvdata(pdev, NULL);
+ kfree(stmpe_gpio);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_gpio_driver = {
+ .driver.name = "stmpe-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_gpio_probe,
+ .remove = __devexit_p(stmpe_gpio_remove),
+};
+
+static int __init stmpe_gpio_init(void)
+{
+ return platform_driver_register(&stmpe_gpio_driver);
+}
+subsys_initcall(stmpe_gpio_init);
+
+static void __exit stmpe_gpio_exit(void)
+{
+ platform_driver_unregister(&stmpe_gpio_driver);
+}
+module_exit(stmpe_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx GPIO driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/sx150x.c
new file mode 100644
index 0000000..b42f42c
--- /dev/null
+++ b/drivers/gpio/sx150x.c
@@ -0,0 +1,645 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/sx150x.h>
+
+struct sx150x_device_data {
+ u8 reg_pullup;
+ u8 reg_pulldn;
+ u8 reg_drain;
+ u8 reg_polarity;
+ u8 reg_dir;
+ u8 reg_data;
+ u8 reg_irq_mask;
+ u8 reg_irq_src;
+ u8 reg_sense;
+ u8 reg_clock;
+ u8 reg_misc;
+ u8 reg_reset;
+ u8 ngpios;
+};
+
+struct sx150x_chip {
+ struct gpio_chip gpio_chip;
+ struct i2c_client *client;
+ const struct sx150x_device_data *dev_cfg;
+ int irq_summary;
+ int irq_base;
+ u32 irq_sense;
+ unsigned long irq_set_type_pending;
+ struct irq_chip irq_chip;
+ struct mutex lock;
+};
+
+static const struct sx150x_device_data sx150x_devices[] = {
+ [0] = { /* sx1508q */
+ .reg_pullup = 0x03,
+ .reg_pulldn = 0x04,
+ .reg_drain = 0x05,
+ .reg_polarity = 0x06,
+ .reg_dir = 0x07,
+ .reg_data = 0x08,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0c,
+ .reg_sense = 0x0b,
+ .reg_clock = 0x0f,
+ .reg_misc = 0x10,
+ .reg_reset = 0x7d,
+ .ngpios = 8
+ },
+ [1] = { /* sx1509q */
+ .reg_pullup = 0x07,
+ .reg_pulldn = 0x09,
+ .reg_drain = 0x0b,
+ .reg_polarity = 0x0d,
+ .reg_dir = 0x0f,
+ .reg_data = 0x11,
+ .reg_irq_mask = 0x13,
+ .reg_irq_src = 0x19,
+ .reg_sense = 0x17,
+ .reg_clock = 0x1e,
+ .reg_misc = 0x1f,
+ .reg_reset = 0x7d,
+ .ngpios = 16
+ },
+};
+
+static const struct i2c_device_id sx150x_id[] = {
+ {"sx1508q", 0},
+ {"sx1509q", 1},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sx150x_id);
+
+static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 err = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (err < 0)
+ dev_warn(&client->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, err);
+ return err;
+}
+
+static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ s32 err = i2c_smbus_read_byte_data(client, reg);
+
+ if (err >= 0)
+ *val = err;
+ else
+ dev_warn(&client->dev,
+ "i2c read fail: can't read from %02x: %d\n",
+ reg, err);
+ return err;
+}
+
+static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
+{
+ return (chip->dev_cfg->ngpios == offset);
+}
+
+/*
+ * These utility functions solve the common problem of locating and setting
+ * configuration bits. Configuration bits are grouped into registers
+ * whose indexes increase downwards. For example, with eight-bit registers,
+ * sixteen gpios would have their config bits grouped in the following order:
+ * REGISTER N-1 [ f e d c b a 9 8 ]
+ * N [ 7 6 5 4 3 2 1 0 ]
+ *
+ * For multi-bit configurations, the pattern gets wider:
+ * REGISTER N-3 [ f f e e d d c c ]
+ * N-2 [ b b a a 9 9 8 8 ]
+ * N-1 [ 7 7 6 6 5 5 4 4 ]
+ * N [ 3 3 2 2 1 1 0 0 ]
+ *
+ * Given the address of the starting register 'N', the index of the gpio
+ * whose configuration we seek to change, and the width in bits of that
+ * configuration, these functions allow us to locate the correct
+ * register and mask the correct bits.
+ */
+static inline void sx150x_find_cfg(u8 offset, u8 width,
+ u8 *reg, u8 *mask, u8 *shift)
+{
+ *reg -= offset * width / 8;
+ *mask = (1 << width) - 1;
+ *shift = (offset * width) % 8;
+ *mask <<= *shift;
+}
+
+static s32 sx150x_write_cfg(struct sx150x_chip *chip,
+ u8 offset, u8 width, u8 reg, u8 val)
+{
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, width, &reg, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err < 0)
+ return err;
+
+ data &= ~mask;
+ data |= (val << shift) & mask;
+ return sx150x_i2c_write(chip->client, reg, data);
+}
+
+static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
+{
+ u8 reg = chip->dev_cfg->reg_data;
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, 1, &reg, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err >= 0)
+ err = (data & mask) != 0 ? 1 : 0;
+
+ return err;
+}
+
+static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+{
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_clock,
+ (val ? 0x1f : 0x10));
+}
+
+static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+}
+
+static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
+{
+ return sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 1);
+}
+
+static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ int err;
+
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+ if (err >= 0)
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 0);
+ return err;
+}
+
+static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_get_io(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+
+ return status;
+}
+
+static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ mutex_lock(&chip->lock);
+ if (offset_is_oscio(chip, offset))
+ sx150x_set_oscio(chip, val);
+ else
+ sx150x_set_io(chip, offset, val);
+ mutex_unlock(&chip->lock);
+}
+
+static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_input(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset,
+ int val)
+{
+ struct sx150x_chip *chip;
+ int status = 0;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_output(chip, offset, val);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (offset >= chip->dev_cfg->ngpios)
+ return -EINVAL;
+
+ if (chip->irq_base < 0)
+ return -EINVAL;
+
+ return chip->irq_base + offset;
+}
+
+static void sx150x_irq_mask(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+}
+
+static void sx150x_irq_unmask(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+}
+
+static int sx150x_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n, val = 0;
+
+ if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ return -EINVAL;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ if (flow_type & IRQ_TYPE_EDGE_RISING)
+ val |= 0x1;
+ if (flow_type & IRQ_TYPE_EDGE_FALLING)
+ val |= 0x2;
+
+ chip->irq_sense &= ~(3UL << (n * 2));
+ chip->irq_sense |= val << (n * 2);
+ chip->irq_set_type_pending |= BIT(n);
+ return 0;
+}
+
+static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
+{
+ struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
+ unsigned nhandled = 0;
+ unsigned sub_irq;
+ unsigned n;
+ s32 err;
+ u8 val;
+ int i;
+
+ for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
+ err = sx150x_i2c_read(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ &val);
+ if (err < 0)
+ continue;
+
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ val);
+ for (n = 0; n < 8; ++n) {
+ if (val & (1 << n)) {
+ sub_irq = chip->irq_base + (i * 8) + n;
+ handle_nested_irq(sub_irq);
+ ++nhandled;
+ }
+ }
+ }
+
+ return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
+}
+
+static void sx150x_irq_bus_lock(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+
+ mutex_lock(&chip->lock);
+}
+
+static void sx150x_irq_bus_sync_unlock(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+
+ while (chip->irq_set_type_pending) {
+ n = __ffs(chip->irq_set_type_pending);
+ chip->irq_set_type_pending &= ~BIT(n);
+ if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED))
+ sx150x_write_cfg(chip, n, 2,
+ chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+ }
+
+ mutex_unlock(&chip->lock);
+}
+
+static void sx150x_init_chip(struct sx150x_chip *chip,
+ struct i2c_client *client,
+ kernel_ulong_t driver_data,
+ struct sx150x_platform_data *pdata)
+{
+ mutex_init(&chip->lock);
+
+ chip->client = client;
+ chip->dev_cfg = &sx150x_devices[driver_data];
+ chip->gpio_chip.label = client->name;
+ chip->gpio_chip.direction_input = sx150x_gpio_direction_input;
+ chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
+ chip->gpio_chip.get = sx150x_gpio_get;
+ chip->gpio_chip.set = sx150x_gpio_set;
+ chip->gpio_chip.to_irq = sx150x_gpio_to_irq;
+ chip->gpio_chip.base = pdata->gpio_base;
+ chip->gpio_chip.can_sleep = 1;
+ chip->gpio_chip.ngpio = chip->dev_cfg->ngpios;
+ if (pdata->oscio_is_gpo)
+ ++chip->gpio_chip.ngpio;
+
+ chip->irq_chip.name = client->name;
+ chip->irq_chip.mask = sx150x_irq_mask;
+ chip->irq_chip.unmask = sx150x_irq_unmask;
+ chip->irq_chip.set_type = sx150x_irq_set_type;
+ chip->irq_chip.bus_lock = sx150x_irq_bus_lock;
+ chip->irq_chip.bus_sync_unlock = sx150x_irq_bus_sync_unlock;
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ chip->irq_sense = 0;
+ chip->irq_set_type_pending = 0;
+}
+
+static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
+{
+ int err = 0;
+ unsigned n;
+
+ for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
+ err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
+ return err;
+}
+
+static int sx150x_init_hw(struct sx150x_chip *chip,
+ struct sx150x_platform_data *pdata)
+{
+ int err = 0;
+
+ err = i2c_smbus_write_word_data(chip->client,
+ chip->dev_cfg->reg_reset,
+ 0x3412);
+ if (err < 0)
+ return err;
+
+ err = sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_misc,
+ 0x01);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
+ pdata->io_pullup_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
+ pdata->io_pulldn_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_drain,
+ pdata->io_open_drain_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity,
+ pdata->io_polarity);
+ if (err < 0)
+ return err;
+
+ if (pdata->oscio_is_gpo)
+ sx150x_set_oscio(chip, 0);
+
+ return err;
+}
+
+static int sx150x_install_irq_chip(struct sx150x_chip *chip,
+ int irq_summary,
+ int irq_base)
+{
+ int err;
+ unsigned n;
+ unsigned irq;
+
+ chip->irq_summary = irq_summary;
+ chip->irq_base = irq_base;
+
+ for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
+ irq = irq_base + n;
+ set_irq_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ err = request_threaded_irq(irq_summary,
+ NULL,
+ sx150x_irq_thread_fn,
+ IRQF_SHARED | IRQF_TRIGGER_FALLING,
+ chip->irq_chip.name,
+ chip);
+ if (err < 0) {
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ }
+
+ return err;
+}
+
+static void sx150x_remove_irq_chip(struct sx150x_chip *chip)
+{
+ unsigned n;
+ unsigned irq;
+
+ free_irq(chip->irq_summary, chip);
+
+ for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
+ irq = chip->irq_base + n;
+ set_irq_handler(irq, NULL);
+ set_irq_chip(irq, NULL);
+ }
+}
+
+static int __devinit sx150x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+ struct sx150x_platform_data *pdata;
+ struct sx150x_chip *chip;
+ int rc;
+
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(client->adapter, i2c_funcs))
+ return -ENOSYS;
+
+ chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ sx150x_init_chip(chip, client, id->driver_data, pdata);
+ rc = sx150x_init_hw(chip, pdata);
+ if (rc < 0)
+ goto probe_fail_pre_gpiochip_add;
+
+ rc = gpiochip_add(&chip->gpio_chip);
+ if (rc < 0)
+ goto probe_fail_pre_gpiochip_add;
+
+ if (pdata->irq_summary >= 0) {
+ rc = sx150x_install_irq_chip(chip,
+ pdata->irq_summary,
+ pdata->irq_base);
+ if (rc < 0)
+ goto probe_fail_post_gpiochip_add;
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ return 0;
+probe_fail_post_gpiochip_add:
+ WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0);
+probe_fail_pre_gpiochip_add:
+ kfree(chip);
+ return rc;
+}
+
+static int __devexit sx150x_remove(struct i2c_client *client)
+{
+ struct sx150x_chip *chip;
+ int rc;
+
+ chip = i2c_get_clientdata(client);
+ rc = gpiochip_remove(&chip->gpio_chip);
+ if (rc < 0)
+ return rc;
+
+ if (chip->irq_summary >= 0)
+ sx150x_remove_irq_chip(chip);
+
+ kfree(chip);
+
+ return 0;
+}
+
+static struct i2c_driver sx150x_driver = {
+ .driver = {
+ .name = "sx150x",
+ .owner = THIS_MODULE
+ },
+ .probe = sx150x_probe,
+ .remove = __devexit_p(sx150x_remove),
+ .id_table = sx150x_id,
+};
+
+static int __init sx150x_init(void)
+{
+ return i2c_add_driver(&sx150x_driver);
+}
+subsys_initcall(sx150x_init);
+
+static void __exit sx150x_exit(void)
+{
+ return i2c_del_driver(&sx150x_driver);
+}
+module_exit(sx150x_exit);
+
+MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:sx150x");
diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c
index 1fa449a1..309644c 100644
--- a/drivers/gpio/wm831x-gpio.c
+++ b/drivers/gpio/wm831x-gpio.c
@@ -108,6 +108,37 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
}
+static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
+ unsigned debounce)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int reg = WM831X_GPIO1_CONTROL + offset;
+ int ret, fn;
+
+ ret = wm831x_reg_read(wm831x, reg);
+ if (ret < 0)
+ return ret;
+
+ switch (ret & WM831X_GPN_FN_MASK) {
+ case 0:
+ case 1:
+ break;
+ default:
+ /* Not in GPIO mode */
+ return -EBUSY;
+ }
+
+ if (debounce >= 32 && debounce <= 64)
+ fn = 0;
+ else if (debounce >= 4000 && debounce <= 8000)
+ fn = 1;
+ else
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn);
+}
+
#ifdef CONFIG_DEBUG_FS
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
@@ -208,6 +239,7 @@ static struct gpio_chip template_chip = {
.direction_output = wm831x_gpio_direction_out,
.set = wm831x_gpio_set,
.to_irq = wm831x_gpio_to_irq,
+ .set_debounce = wm831x_gpio_set_debounce,
.dbg_show = wm831x_gpio_dbg_show,
.can_sleep = 1,
};
diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
index 17be051..1c36492 100644
--- a/drivers/gpu/drm/ati_pcigart.c
+++ b/drivers/gpu/drm/ati_pcigart.c
@@ -152,7 +152,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga
/* we need to support large memory configurations */
entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i],
0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (entry->busaddr[i] == 0) {
+ if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
DRM_ERROR("unable to map PCIGART pages!\n");
drm_ati_pcigart_cleanup(dev, gart_info);
address = NULL;
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index a5c9ce9..3e257a5 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -328,14 +328,13 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
return -EINVAL;
}
- list = kmalloc(sizeof(*list), GFP_KERNEL);
+ list = kzalloc(sizeof(*list), GFP_KERNEL);
if (!list) {
if (map->type == _DRM_REGISTERS)
iounmap(map->handle);
kfree(map);
return -EINVAL;
}
- memset(list, 0, sizeof(*list));
list->map = map;
mutex_lock(&dev->struct_mutex);
@@ -678,13 +677,12 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
entry->buf_size = size;
entry->page_order = page_order;
@@ -708,7 +706,7 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -717,7 +715,6 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
@@ -832,22 +829,20 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
- entry->seglist = kmalloc(count * sizeof(*entry->seglist), GFP_KERNEL);
+ entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL);
if (!entry->seglist) {
kfree(entry->buflist);
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->seglist, 0, count * sizeof(*entry->seglist));
/* Keep the original pagelist until we know all the allocations
* have succeeded
@@ -911,8 +906,8 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size,
- GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size,
+ GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -923,7 +918,6 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
DRM_DEBUG("buffer %d @ %p\n",
entry->buf_count, buf->address);
@@ -1048,14 +1042,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist),
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist),
GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
entry->buf_size = size;
entry->page_order = page_order;
@@ -1080,7 +1073,7 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -1090,8 +1083,6 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
-
DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
offset += alignment;
@@ -1209,14 +1200,13 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist),
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist),
GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
entry->buf_size = size;
entry->page_order = page_order;
@@ -1240,7 +1230,7 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -1249,7 +1239,6 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4c68f76..37e0b4f 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1682,9 +1682,9 @@ int drm_mode_addfb(struct drm_device *dev,
/* TODO setup destructor callback */
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
- if (!fb) {
+ if (IS_ERR(fb)) {
DRM_ERROR("could not create framebuffer\n");
- ret = -EINVAL;
+ ret = PTR_ERR(fb);
goto out;
}
@@ -2541,7 +2541,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
goto out;
}
- crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+ crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
out:
mutex_unlock(&dev->mode_config.mutex);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index b9e4dbf..7e31d43 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -817,12 +817,12 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
if (encoder_funcs->dpms)
(*encoder_funcs->dpms) (encoder,
drm_helper_choose_encoder_dpms(encoder));
-
- crtc_funcs = crtc->helper_private;
- if (crtc_funcs->dpms)
- (*crtc_funcs->dpms) (crtc,
- drm_helper_choose_crtc_dpms(crtc));
}
+
+ crtc_funcs = crtc->helper_private;
+ if (crtc_funcs->dpms)
+ (*crtc_funcs->dpms) (crtc,
+ drm_helper_choose_crtc_dpms(crtc));
}
}
/* disable the unused connectors while restoring the modesetting */
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index dce5c4a..96e9631 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -33,6 +33,11 @@
#include <linux/i2c-algo-bit.h>
#include "drmP.h"
#include "drm_edid.h"
+#include "drm_edid_modes.h"
+
+#define version_greater(edid, maj, min) \
+ (((edid)->version > (maj)) || \
+ ((edid)->version == (maj) && (edid)->revision > (min)))
#define EDID_EST_TIMINGS 16
#define EDID_STD_TIMINGS 8
@@ -62,6 +67,13 @@
/* use +hsync +vsync for detailed mode */
#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
+struct detailed_mode_closure {
+ struct drm_connector *connector;
+ struct edid *edid;
+ bool preferred;
+ u32 quirks;
+ int modes;
+};
#define LEVEL_DMT 0
#define LEVEL_GTF 1
@@ -375,7 +387,6 @@ static u32 edid_get_quirks(struct edid *edid)
#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
-
/**
* edid_fixup_preferred - set preferred modes based on quirk list
* @connector: has mode list to fix up
@@ -422,245 +433,6 @@ static void edid_fixup_preferred(struct drm_connector *connector,
preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
}
-/*
- * Add the Autogenerated from the DMT spec.
- * This table is copied from xfree86/modes/xf86EdidModes.c.
- * But the mode with Reduced blank feature is deleted.
- */
-static struct drm_display_mode drm_dmt_modes[] = {
- /* 640x350@85Hz */
- { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
- 736, 832, 0, 350, 382, 385, 445, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x400@85Hz */
- { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
- 736, 832, 0, 400, 401, 404, 445, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 720x400@85Hz */
- { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
- 828, 936, 0, 400, 401, 404, 446, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 640x480@60Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
- 752, 800, 0, 480, 489, 492, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x480@72Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
- 704, 832, 0, 480, 489, 492, 520, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x480@75Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
- 720, 840, 0, 480, 481, 484, 500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x480@85Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
- 752, 832, 0, 480, 481, 484, 509, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 800x600@56Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
- 896, 1024, 0, 600, 601, 603, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@60Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
- 968, 1056, 0, 600, 601, 605, 628, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@72Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
- 976, 1040, 0, 600, 637, 643, 666, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@75Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
- 896, 1056, 0, 600, 601, 604, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@85Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
- 896, 1048, 0, 600, 601, 604, 631, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 848x480@60Hz */
- { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
- 976, 1088, 0, 480, 486, 494, 517, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1024x768@43Hz, interlace */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
- 1208, 1264, 0, 768, 768, 772, 817, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
- DRM_MODE_FLAG_INTERLACE) },
- /* 1024x768@60Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
- 1184, 1344, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1024x768@70Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
- 1184, 1328, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1024x768@75Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
- 1136, 1312, 0, 768, 769, 772, 800, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1024x768@85Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
- 1168, 1376, 0, 768, 769, 772, 808, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1152x864@75Hz */
- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
- 1344, 1600, 0, 864, 865, 868, 900, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x768@60Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
- 1472, 1664, 0, 768, 771, 778, 798, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x768@75Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
- 1488, 1696, 0, 768, 771, 778, 805, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1280x768@85Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
- 1496, 1712, 0, 768, 771, 778, 809, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x800@60Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
- 1480, 1680, 0, 800, 803, 809, 831, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1280x800@75Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
- 1488, 1696, 0, 800, 803, 809, 838, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x800@85Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
- 1496, 1712, 0, 800, 803, 809, 843, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x960@60Hz */
- { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
- 1488, 1800, 0, 960, 961, 964, 1000, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x960@85Hz */
- { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
- 1504, 1728, 0, 960, 961, 964, 1011, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@60Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@75Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@85Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
- 1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1360x768@60Hz */
- { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
- 1536, 1792, 0, 768, 771, 777, 795, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@60Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
- 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@75Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
- 1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@85Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
- 1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@60Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
- 1672, 1904, 0, 900, 903, 909, 934, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@75Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
- 1688, 1936, 0, 900, 903, 909, 942, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@85Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
- 1696, 1952, 0, 900, 903, 909, 948, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@60Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@65Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@70Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@75Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@85Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@60Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
- 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@75Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
- 1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@85Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
- 1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1792x1344@60Hz */
- { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
- 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1729x1344@75Hz */
- { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
- 2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1853x1392@60Hz */
- { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
- 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1856x1392@75Hz */
- { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
- 2208, 2560, 0, 1392, 1395, 1399, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@60Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
- 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@75Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
- 2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@85Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
- 2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1440@60Hz */
- { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
- 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1440@75Hz */
- { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
- 2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@60Hz */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
- 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@75HZ */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
- 3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@85HZ */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
- 3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-};
-static const int drm_num_dmt_modes =
- sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
-
struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh)
{
@@ -685,6 +457,46 @@ EXPORT_SYMBOL(drm_mode_find_dmt);
typedef void detailed_cb(struct detailed_timing *timing, void *closure);
static void
+cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+ int i, n = 0;
+ u8 rev = ext[0x01], d = ext[0x02];
+ u8 *det_base = ext + d;
+
+ switch (rev) {
+ case 0:
+ /* can't happen */
+ return;
+ case 1:
+ /* have to infer how many blocks we have, check pixel clock */
+ for (i = 0; i < 6; i++)
+ if (det_base[18*i] || det_base[18*i+1])
+ n++;
+ break;
+ default:
+ /* explicit count */
+ n = min(ext[0x03] & 0x0f, 6);
+ break;
+ }
+
+ for (i = 0; i < n; i++)
+ cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
+static void
+vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+ unsigned int i, n = min((int)ext[0x02], 6);
+ u8 *det_base = ext + 5;
+
+ if (ext[0x01] != 1)
+ return; /* unknown version */
+
+ for (i = 0; i < n; i++)
+ cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
+static void
drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
{
int i;
@@ -696,7 +508,19 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
cb(&(edid->detailed_timings[i]), closure);
- /* XXX extension block walk */
+ for (i = 1; i <= raw_edid[0x7e]; i++) {
+ u8 *ext = raw_edid + (i * EDID_LENGTH);
+ switch (*ext) {
+ case CEA_EXT:
+ cea_for_each_detailed_block(ext, cb, closure);
+ break;
+ case VTB_EXT:
+ vtb_for_each_detailed_block(ext, cb, closure);
+ break;
+ default:
+ break;
+ }
+ }
}
static void
@@ -1047,117 +871,6 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
return mode;
}
-/*
- * Detailed mode info for the EDID "established modes" data to use.
- */
-static struct drm_display_mode edid_est_modes[] = {
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
- 968, 1056, 0, 600, 601, 605, 628, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
- 896, 1024, 0, 600, 601, 603, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
- 720, 840, 0, 480, 481, 484, 500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
- 704, 832, 0, 480, 489, 491, 520, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
- 768, 864, 0, 480, 483, 486, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
- 752, 800, 0, 480, 490, 492, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
- { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
- 846, 900, 0, 400, 421, 423, 449, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
- { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
- 846, 900, 0, 400, 412, 414, 449, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
- 1136, 1312, 0, 768, 769, 772, 800, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
- 1184, 1328, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
- 1184, 1344, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
- 1208, 1264, 0, 768, 768, 776, 817, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
- { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
- 928, 1152, 0, 624, 625, 628, 667, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
- 896, 1056, 0, 600, 601, 604, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
- 976, 1040, 0, 600, 637, 643, 666, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
- 1344, 1600, 0, 864, 865, 868, 900, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
-};
-
-/**
- * add_established_modes - get est. modes from EDID and add them
- * @edid: EDID block to scan
- *
- * Each EDID block contains a bitmap of the supported "established modes" list
- * (defined above). Tease them out and add them to the global modes list.
- */
-static int add_established_modes(struct drm_connector *connector, struct edid *edid)
-{
- struct drm_device *dev = connector->dev;
- unsigned long est_bits = edid->established_timings.t1 |
- (edid->established_timings.t2 << 8) |
- ((edid->established_timings.mfg_rsvd & 0x80) << 9);
- int i, modes = 0;
-
- for (i = 0; i <= EDID_EST_TIMINGS; i++)
- if (est_bits & (1<<i)) {
- struct drm_display_mode *newmode;
- newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
-
- return modes;
-}
-
-/**
- * add_standard_modes - get std. modes from EDID and add them
- * @edid: EDID block to scan
- *
- * Standard modes can be calculated using the CVT standard. Grab them from
- * @edid, calculate them, and add them to the list.
- */
-static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
-{
- int i, modes = 0;
-
- for (i = 0; i < EDID_STD_TIMINGS; i++) {
- struct drm_display_mode *newmode;
-
- newmode = drm_mode_std(connector, edid,
- &edid->standard_timings[i],
- edid->revision);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
-
- return modes;
-}
-
static bool
mode_is_rb(struct drm_display_mode *mode)
{
@@ -1267,113 +980,33 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
return modes;
}
-static int drm_cvt_modes(struct drm_connector *connector,
- struct detailed_timing *timing)
+static void
+do_inferred_modes(struct detailed_timing *timing, void *c)
{
- int i, j, modes = 0;
- struct drm_display_mode *newmode;
- struct drm_device *dev = connector->dev;
- struct cvt_timing *cvt;
- const int rates[] = { 60, 85, 75, 60, 50 };
- const u8 empty[3] = { 0, 0, 0 };
-
- for (i = 0; i < 4; i++) {
- int uninitialized_var(width), height;
- cvt = &(timing->data.other_data.data.cvt[i]);
+ struct detailed_mode_closure *closure = c;
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ int gtf = (closure->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
- if (!memcmp(cvt->code, empty, 3))
- continue;
+ if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE)
+ closure->modes += drm_gtf_modes_for_range(closure->connector,
+ closure->edid,
+ timing);
+}
- height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
- switch (cvt->code[1] & 0x0c) {
- case 0x00:
- width = height * 4 / 3;
- break;
- case 0x04:
- width = height * 16 / 9;
- break;
- case 0x08:
- width = height * 16 / 10;
- break;
- case 0x0c:
- width = height * 15 / 9;
- break;
- }
+static int
+add_inferred_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
- for (j = 1; j < 5; j++) {
- if (cvt->code[2] & (1 << j)) {
- newmode = drm_cvt_mode(dev, width, height,
- rates[j], j == 0,
- false, false);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
- }
- }
+ if (version_greater(edid, 1, 0))
+ drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
+ &closure);
- return modes;
+ return closure.modes;
}
-static const struct {
- short w;
- short h;
- short r;
- short rb;
-} est3_modes[] = {
- /* byte 6 */
- { 640, 350, 85, 0 },
- { 640, 400, 85, 0 },
- { 720, 400, 85, 0 },
- { 640, 480, 85, 0 },
- { 848, 480, 60, 0 },
- { 800, 600, 85, 0 },
- { 1024, 768, 85, 0 },
- { 1152, 864, 75, 0 },
- /* byte 7 */
- { 1280, 768, 60, 1 },
- { 1280, 768, 60, 0 },
- { 1280, 768, 75, 0 },
- { 1280, 768, 85, 0 },
- { 1280, 960, 60, 0 },
- { 1280, 960, 85, 0 },
- { 1280, 1024, 60, 0 },
- { 1280, 1024, 85, 0 },
- /* byte 8 */
- { 1360, 768, 60, 0 },
- { 1440, 900, 60, 1 },
- { 1440, 900, 60, 0 },
- { 1440, 900, 75, 0 },
- { 1440, 900, 85, 0 },
- { 1400, 1050, 60, 1 },
- { 1400, 1050, 60, 0 },
- { 1400, 1050, 75, 0 },
- /* byte 9 */
- { 1400, 1050, 85, 0 },
- { 1680, 1050, 60, 1 },
- { 1680, 1050, 60, 0 },
- { 1680, 1050, 75, 0 },
- { 1680, 1050, 85, 0 },
- { 1600, 1200, 60, 0 },
- { 1600, 1200, 65, 0 },
- { 1600, 1200, 70, 0 },
- /* byte 10 */
- { 1600, 1200, 75, 0 },
- { 1600, 1200, 85, 0 },
- { 1792, 1344, 60, 0 },
- { 1792, 1344, 85, 0 },
- { 1856, 1392, 60, 0 },
- { 1856, 1392, 75, 0 },
- { 1920, 1200, 60, 1 },
- { 1920, 1200, 60, 0 },
- /* byte 11 */
- { 1920, 1200, 75, 0 },
- { 1920, 1200, 85, 0 },
- { 1920, 1440, 60, 0 },
- { 1920, 1440, 75, 0 },
-};
-
static int
drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
{
@@ -1403,37 +1036,63 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
return modes;
}
-static int add_detailed_modes(struct drm_connector *connector,
- struct detailed_timing *timing,
- struct edid *edid, u32 quirks, int preferred)
+static void
+do_established_modes(struct detailed_timing *timing, void *c)
{
- int i, modes = 0;
+ struct detailed_mode_closure *closure = c;
struct detailed_non_pixel *data = &timing->data.other_data;
- int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
- struct drm_display_mode *newmode;
- struct drm_device *dev = connector->dev;
- if (timing->pixel_clock) {
- newmode = drm_mode_detailed(dev, edid, timing, quirks);
- if (!newmode)
- return 0;
+ if (data->type == EDID_DETAIL_EST_TIMINGS)
+ closure->modes += drm_est3_modes(closure->connector, timing);
+}
- if (preferred)
- newmode->type |= DRM_MODE_TYPE_PREFERRED;
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above). Tease them out and add them to the global modes list.
+ */
+static int
+add_established_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ unsigned long est_bits = edid->established_timings.t1 |
+ (edid->established_timings.t2 << 8) |
+ ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+ int i, modes = 0;
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
- drm_mode_probed_add(connector, newmode);
- return 1;
+ for (i = 0; i <= EDID_EST_TIMINGS; i++) {
+ if (est_bits & (1<<i)) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
}
- /* other timing types */
- switch (data->type) {
- case EDID_DETAIL_MONITOR_RANGE:
- if (gtf)
- modes += drm_gtf_modes_for_range(connector, edid,
- timing);
- break;
- case EDID_DETAIL_STD_MODES:
- /* Six modes per detailed section */
+ if (version_greater(edid, 1, 0))
+ drm_for_each_detailed_block((u8 *)edid,
+ do_established_modes, &closure);
+
+ return modes + closure.modes;
+}
+
+static void
+do_standard_modes(struct detailed_timing *timing, void *c)
+{
+ struct detailed_mode_closure *closure = c;
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ struct drm_connector *connector = closure->connector;
+ struct edid *edid = closure->edid;
+
+ if (data->type == EDID_DETAIL_STD_MODES) {
+ int i;
for (i = 0; i < 6; i++) {
struct std_timing *std;
struct drm_display_mode *newmode;
@@ -1443,108 +1102,169 @@ static int add_detailed_modes(struct drm_connector *connector,
edid->revision);
if (newmode) {
drm_mode_probed_add(connector, newmode);
- modes++;
+ closure->modes++;
}
}
- break;
- case EDID_DETAIL_CVT_3BYTE:
- modes += drm_cvt_modes(connector, timing);
- break;
- case EDID_DETAIL_EST_TIMINGS:
- modes += drm_est3_modes(connector, timing);
- break;
- default:
- break;
}
-
- return modes;
}
/**
- * add_detailed_info - get detailed mode info from EDID data
- * @connector: attached connector
+ * add_standard_modes - get std. modes from EDID and add them
* @edid: EDID block to scan
- * @quirks: quirks to apply
*
- * Some of the detailed timing sections may contain mode information. Grab
- * it and add it to the list.
+ * Standard modes can be calculated using the appropriate standard (DMT,
+ * GTF or CVT. Grab them from @edid and add them to the list.
*/
-static int add_detailed_info(struct drm_connector *connector,
- struct edid *edid, u32 quirks)
+static int
+add_standard_modes(struct drm_connector *connector, struct edid *edid)
{
int i, modes = 0;
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
+
+ for (i = 0; i < EDID_STD_TIMINGS; i++) {
+ struct drm_display_mode *newmode;
+
+ newmode = drm_mode_std(connector, edid,
+ &edid->standard_timings[i],
+ edid->revision);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ if (version_greater(edid, 1, 0))
+ drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
+ &closure);
+
+ /* XXX should also look for standard codes in VTB blocks */
+
+ return modes + closure.modes;
+}
- for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
- struct detailed_timing *timing = &edid->detailed_timings[i];
- int preferred = (i == 0);
+static int drm_cvt_modes(struct drm_connector *connector,
+ struct detailed_timing *timing)
+{
+ int i, j, modes = 0;
+ struct drm_display_mode *newmode;
+ struct drm_device *dev = connector->dev;
+ struct cvt_timing *cvt;
+ const int rates[] = { 60, 85, 75, 60, 50 };
+ const u8 empty[3] = { 0, 0, 0 };
- if (preferred && edid->version == 1 && edid->revision < 4)
- preferred = (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+ for (i = 0; i < 4; i++) {
+ int uninitialized_var(width), height;
+ cvt = &(timing->data.other_data.data.cvt[i]);
- /* In 1.0, only timings are allowed */
- if (!timing->pixel_clock && edid->version == 1 &&
- edid->revision == 0)
+ if (!memcmp(cvt->code, empty, 3))
continue;
- modes += add_detailed_modes(connector, timing, edid, quirks,
- preferred);
+ height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
+ switch (cvt->code[1] & 0x0c) {
+ case 0x00:
+ width = height * 4 / 3;
+ break;
+ case 0x04:
+ width = height * 16 / 9;
+ break;
+ case 0x08:
+ width = height * 16 / 10;
+ break;
+ case 0x0c:
+ width = height * 15 / 9;
+ break;
+ }
+
+ for (j = 1; j < 5; j++) {
+ if (cvt->code[2] & (1 << j)) {
+ newmode = drm_cvt_mode(dev, width, height,
+ rates[j], j == 0,
+ false, false);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ }
}
return modes;
}
-/**
- * add_detailed_mode_eedid - get detailed mode info from addtional timing
- * EDID block
- * @connector: attached connector
- * @edid: EDID block to scan(It is only to get addtional timing EDID block)
- * @quirks: quirks to apply
- *
- * Some of the detailed timing sections may contain mode information. Grab
- * it and add it to the list.
- */
-static int add_detailed_info_eedid(struct drm_connector *connector,
- struct edid *edid, u32 quirks)
+static void
+do_cvt_mode(struct detailed_timing *timing, void *c)
{
- int i, modes = 0;
- char *edid_ext = NULL;
- struct detailed_timing *timing;
- int start_offset, end_offset;
+ struct detailed_mode_closure *closure = c;
+ struct detailed_non_pixel *data = &timing->data.other_data;
- if (edid->version == 1 && edid->revision < 3)
- return 0;
- if (!edid->extensions)
- return 0;
+ if (data->type == EDID_DETAIL_CVT_3BYTE)
+ closure->modes += drm_cvt_modes(closure->connector, timing);
+}
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == 0x02)
- break;
- }
+static int
+add_cvt_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
- if (i == edid->extensions)
- return 0;
+ if (version_greater(edid, 1, 2))
+ drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
- /* Get the start offset of detailed timing block */
- start_offset = edid_ext[2];
- if (start_offset == 0) {
- /* If the start_offset is zero, it means that neither detailed
- * info nor data block exist. In such case it is also
- * unnecessary to parse the detailed timing info.
- */
- return 0;
- }
+ /* XXX should also look for CVT codes in VTB blocks */
- end_offset = EDID_LENGTH;
- end_offset -= sizeof(struct detailed_timing);
- for (i = start_offset; i < end_offset;
- i += sizeof(struct detailed_timing)) {
- timing = (struct detailed_timing *)(edid_ext + i);
- modes += add_detailed_modes(connector, timing, edid, quirks, 0);
+ return closure.modes;
+}
+
+static void
+do_detailed_mode(struct detailed_timing *timing, void *c)
+{
+ struct detailed_mode_closure *closure = c;
+ struct drm_display_mode *newmode;
+
+ if (timing->pixel_clock) {
+ newmode = drm_mode_detailed(closure->connector->dev,
+ closure->edid, timing,
+ closure->quirks);
+ if (!newmode)
+ return;
+
+ if (closure->preferred)
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(closure->connector, newmode);
+ closure->modes++;
+ closure->preferred = 0;
}
+}
- return modes;
+/*
+ * add_detailed_modes - Add modes from detailed timings
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+ */
+static int
+add_detailed_modes(struct drm_connector *connector, struct edid *edid,
+ u32 quirks)
+{
+ struct detailed_mode_closure closure = {
+ connector,
+ edid,
+ 1,
+ quirks,
+ 0
+ };
+
+ if (closure.preferred && !version_greater(edid, 1, 3))
+ closure.preferred =
+ (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+
+ drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
+
+ return closure.modes;
}
#define HDMI_IDENTIFIER 0x000C03
@@ -1640,35 +1360,21 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
* - established timing codes
* - modes inferred from GTF or CVT range information
*
- * We don't quite implement this yet, but we're close.
+ * We get this pretty much right.
*
* XXX order for additional mode types in extension blocks?
*/
- num_modes += add_detailed_info(connector, edid, quirks);
- num_modes += add_detailed_info_eedid(connector, edid, quirks);
+ num_modes += add_detailed_modes(connector, edid, quirks);
+ num_modes += add_cvt_modes(connector, edid);
num_modes += add_standard_modes(connector, edid);
num_modes += add_established_modes(connector, edid);
+ num_modes += add_inferred_modes(connector, edid);
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
edid_fixup_preferred(connector, quirks);
- connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0;
- connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0;
- connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0;
- connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0;
- connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0;
- connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5;
- connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0;
connector->display_info.width_mm = edid->width_cm * 10;
connector->display_info.height_mm = edid->height_cm * 10;
- connector->display_info.gamma = edid->gamma;
- connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0;
- connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0;
- connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3;
- connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0;
- connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0;
- connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0;
- connector->display_info.gamma = edid->gamma;
return num_modes;
}
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h
new file mode 100644
index 0000000..6eb7592
--- /dev/null
+++ b/drivers/gpu/drm/drm_edid_modes.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include "drmP.h"
+#include "drm_edid.h"
+
+/*
+ * Autogenerated from the DMT spec.
+ * This table is copied from xfree86/modes/xf86EdidModes.c.
+ * But the mode with Reduced blank feature is deleted.
+ */
+static struct drm_display_mode drm_dmt_modes[] = {
+ /* 640x350@85Hz */
+ { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
+ 736, 832, 0, 350, 382, 385, 445, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x400@85Hz */
+ { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
+ 736, 832, 0, 400, 401, 404, 445, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 720x400@85Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
+ 828, 936, 0, 400, 401, 404, 446, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 640x480@60Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
+ 752, 800, 0, 480, 489, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 492, 520, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x480@85Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
+ 752, 832, 0, 480, 481, 484, 509, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 800x600@56Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@72Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@85Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
+ 896, 1048, 0, 600, 601, 604, 631, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 848x480@60Hz */
+ { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
+ 976, 1088, 0, 480, 486, 494, 517, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1024x768@43Hz, interlace */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 772, 817, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
+ DRM_MODE_FLAG_INTERLACE) },
+ /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1024x768@85Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
+ 1168, 1376, 0, 768, 769, 772, 808, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1152x864@75Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x768@60Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
+ 1472, 1664, 0, 768, 771, 778, 798, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x768@75Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
+ 1488, 1696, 0, 768, 771, 778, 805, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1280x768@85Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
+ 1496, 1712, 0, 768, 771, 778, 809, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x800@60Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
+ 1480, 1680, 0, 800, 803, 809, 831, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1280x800@75Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
+ 1488, 1696, 0, 800, 803, 809, 838, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x800@85Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
+ 1496, 1712, 0, 800, 803, 809, 843, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x960@60Hz */
+ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
+ 1488, 1800, 0, 960, 961, 964, 1000, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x960@85Hz */
+ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
+ 1504, 1728, 0, 960, 961, 964, 1011, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@60Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@75Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@85Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
+ 1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1360x768@60Hz */
+ { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
+ 1536, 1792, 0, 768, 771, 777, 795, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@60Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
+ 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@75Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
+ 1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@85Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
+ 1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@60Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
+ 1672, 1904, 0, 900, 903, 909, 934, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@75Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
+ 1688, 1936, 0, 900, 903, 909, 942, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@85Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
+ 1696, 1952, 0, 900, 903, 909, 948, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@60Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@65Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@70Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@75Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@85Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@60Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
+ 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@75Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
+ 1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@85Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
+ 1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1792x1344@60Hz */
+ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
+ 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1729x1344@75Hz */
+ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
+ 2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1853x1392@60Hz */
+ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
+ 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1856x1392@75Hz */
+ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
+ 2208, 2560, 0, 1392, 1395, 1399, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@60Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
+ 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@75Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
+ 2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@85Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
+ 2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1440@60Hz */
+ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
+ 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1440@75Hz */
+ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
+ 2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@60Hz */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
+ 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@75HZ */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
+ 3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@85HZ */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
+ 3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+};
+static const int drm_num_dmt_modes =
+ sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
+
+static struct drm_display_mode edid_est_modes[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+ 768, 864, 0, 480, 483, 486, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+ 846, 900, 0, 400, 421, 423, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+ 846, 900, 0, 400, 412, 414, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 776, 817, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+ 928, 1152, 0, 624, 625, 628, 667, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+static const struct {
+ short w;
+ short h;
+ short r;
+ short rb;
+} est3_modes[] = {
+ /* byte 6 */
+ { 640, 350, 85, 0 },
+ { 640, 400, 85, 0 },
+ { 720, 400, 85, 0 },
+ { 640, 480, 85, 0 },
+ { 848, 480, 60, 0 },
+ { 800, 600, 85, 0 },
+ { 1024, 768, 85, 0 },
+ { 1152, 864, 75, 0 },
+ /* byte 7 */
+ { 1280, 768, 60, 1 },
+ { 1280, 768, 60, 0 },
+ { 1280, 768, 75, 0 },
+ { 1280, 768, 85, 0 },
+ { 1280, 960, 60, 0 },
+ { 1280, 960, 85, 0 },
+ { 1280, 1024, 60, 0 },
+ { 1280, 1024, 85, 0 },
+ /* byte 8 */
+ { 1360, 768, 60, 0 },
+ { 1440, 900, 60, 1 },
+ { 1440, 900, 60, 0 },
+ { 1440, 900, 75, 0 },
+ { 1440, 900, 85, 0 },
+ { 1400, 1050, 60, 1 },
+ { 1400, 1050, 60, 0 },
+ { 1400, 1050, 75, 0 },
+ /* byte 9 */
+ { 1400, 1050, 85, 0 },
+ { 1680, 1050, 60, 1 },
+ { 1680, 1050, 60, 0 },
+ { 1680, 1050, 75, 0 },
+ { 1680, 1050, 85, 0 },
+ { 1600, 1200, 60, 0 },
+ { 1600, 1200, 65, 0 },
+ { 1600, 1200, 70, 0 },
+ /* byte 10 */
+ { 1600, 1200, 75, 0 },
+ { 1600, 1200, 85, 0 },
+ { 1792, 1344, 60, 0 },
+ { 1792, 1344, 85, 0 },
+ { 1856, 1392, 60, 0 },
+ { 1856, 1392, 75, 0 },
+ { 1920, 1200, 60, 1 },
+ { 1920, 1200, 60, 0 },
+ /* byte 11 */
+ { 1920, 1200, 75, 0 },
+ { 1920, 1200, 85, 0 },
+ { 1920, 1440, 60, 0 },
+ { 1920, 1440, 75, 0 },
+};
+static const int num_est3_modes = sizeof(est3_modes) / sizeof(est3_modes[0]);
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 2ca8df8..3a652a6 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -135,15 +135,9 @@ int drm_open(struct inode *inode, struct file *filp)
retcode = drm_open_helper(inode, filp, dev);
if (!retcode) {
atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
- spin_lock(&dev->count_lock);
- if (!dev->open_count++) {
- spin_unlock(&dev->count_lock);
+ if (!dev->open_count++)
retcode = drm_setup(dev);
- goto out;
- }
- spin_unlock(&dev->count_lock);
}
-out:
if (!retcode) {
mutex_lock(&dev->struct_mutex);
if (minor->type == DRM_MINOR_LEGACY) {
@@ -570,18 +564,14 @@ int drm_release(struct inode *inode, struct file *filp)
*/
atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
- spin_lock(&dev->count_lock);
if (!--dev->open_count) {
if (atomic_read(&dev->ioctl_count)) {
DRM_ERROR("Device busy: %d\n",
atomic_read(&dev->ioctl_count));
retcode = -EBUSY;
- goto out;
- }
- retcode = drm_lastclose(dev);
+ } else
+ retcode = drm_lastclose(dev);
}
-out:
- spin_unlock(&dev->count_lock);
mutex_unlock(&drm_global_mutex);
return retcode;
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 4f1b867..bf92d07 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -322,7 +322,7 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
again:
if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) {
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 7b03b19..47db4df 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -392,6 +392,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri
if (sv->drm_di_minor >= 1) {
/*
* Version 1.1 includes tying of DRM to specific device
+ * Version 1.4 has proper PCI domain support
*/
retcode = drm_set_busid(dev, file_priv);
if (retcode)
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
index 833b35f..08792a7 100644
--- a/drivers/gpu/drm/i2c/ch7006_drv.c
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -470,6 +470,7 @@ static int ch7006_encoder_init(struct i2c_client *client,
priv->hmargin = 50;
priv->vmargin = 50;
priv->last_dpms = -1;
+ priv->chip_version = ch7006_read(client, CH7006_VERSION_ID);
if (ch7006_tv_norm) {
for (i = 0; i < NUM_TV_NORMS; i++) {
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c
index e447dfb..c860f24 100644
--- a/drivers/gpu/drm/i2c/ch7006_mode.c
+++ b/drivers/gpu/drm/i2c/ch7006_mode.c
@@ -316,7 +316,10 @@ void ch7006_setup_power_state(struct drm_encoder *encoder)
}
} else {
- *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+ if (priv->chip_version >= 0x20)
+ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+ else
+ *power |= bitfs(CH7006_POWER_LEVEL, POWER_OFF);
}
}
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
index 1c6d2e3..17667b7 100644
--- a/drivers/gpu/drm/i2c/ch7006_priv.h
+++ b/drivers/gpu/drm/i2c/ch7006_priv.h
@@ -95,6 +95,7 @@ struct ch7006_priv {
int flicker;
int scale;
+ int chip_version;
int last_dpms;
};
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 2a4ed7c..0758c78 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -456,7 +456,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
/* Bounds check source.
@@ -919,7 +919,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
/* Bounds check destination.
@@ -1002,7 +1002,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
mutex_lock(&dev->struct_mutex);
@@ -1060,7 +1060,7 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
mutex_unlock(&dev->struct_mutex);
- return -EBADF;
+ return -ENOENT;
}
#if WATCH_BUF
@@ -1099,7 +1099,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
offset = args->offset;
@@ -1373,7 +1373,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
mutex_lock(&dev->struct_mutex);
@@ -3364,7 +3364,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
reloc->target_handle);
if (target_obj == NULL) {
i915_gem_object_unpin(obj);
- return -EBADF;
+ return -ENOENT;
}
target_obj_priv = to_intel_bo(target_obj);
@@ -3781,7 +3781,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
exec_list[i].handle, i);
/* prevent error path from reading uninitialized data */
args->buffer_count = i + 1;
- ret = -EBADF;
+ ret = -ENOENT;
goto err;
}
@@ -3791,7 +3791,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
object_list[i]);
/* prevent error path from reading uninitialized data */
args->buffer_count = i + 1;
- ret = -EBADF;
+ ret = -EINVAL;
goto err;
}
obj_priv->in_execbuffer = true;
@@ -4265,7 +4265,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n",
args->handle);
mutex_unlock(&dev->struct_mutex);
- return -EBADF;
+ return -ENOENT;
}
obj_priv = to_intel_bo(obj);
@@ -4321,7 +4321,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n",
args->handle);
mutex_unlock(&dev->struct_mutex);
- return -EBADF;
+ return -ENOENT;
}
obj_priv = to_intel_bo(obj);
@@ -4355,7 +4355,7 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
if (obj == NULL) {
DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n",
args->handle);
- return -EBADF;
+ return -ENOENT;
}
mutex_lock(&dev->struct_mutex);
@@ -4408,7 +4408,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
if (obj == NULL) {
DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n",
args->handle);
- return -EBADF;
+ return -ENOENT;
}
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 155719e..710eca7 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -275,7 +275,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EINVAL;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) {
@@ -362,7 +362,7 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EINVAL;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
index 8fcc75c..d1bf92b 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/i915_opregion.c
@@ -535,6 +535,7 @@ int intel_opregion_init(struct drm_device *dev, int resume)
err_out:
iounmap(opregion->header);
opregion->header = NULL;
+ acpi_video_register();
return err;
}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 1e5e0d3..5ec10e0 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4429,15 +4429,12 @@ void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
}
static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
- u16 *blue, uint32_t size)
+ u16 *blue, uint32_t start, uint32_t size)
{
+ int end = (start + size > 256) ? 256 : start + size, i;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int i;
-
- if (size != 256)
- return;
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
intel_crtc->lut_r[i] = red[i] >> 8;
intel_crtc->lut_g[i] = green[i] >> 8;
intel_crtc->lut_b[i] = blue[i] >> 8;
@@ -5412,18 +5409,18 @@ intel_user_framebuffer_create(struct drm_device *dev,
obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
if (!obj)
- return NULL;
+ return ERR_PTR(-ENOENT);
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
if (!intel_fb)
- return NULL;
+ return ERR_PTR(-ENOMEM);
ret = intel_framebuffer_init(dev, intel_fb,
mode_cmd, obj);
if (ret) {
drm_gem_object_unreference_unlocked(obj);
kfree(intel_fb);
- return NULL;
+ return ERR_PTR(ret);
}
return &intel_fb->base;
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 54acd8b..7bdc962 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -121,7 +121,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
info->par = ifbdev;
- intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
+ ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
+ if (ret)
+ goto out_unpin;
fb = &ifbdev->ifb.base;
@@ -130,7 +132,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
strcpy(info->fix.id, "inteldrmfb");
- info->flags = FBINFO_DEFAULT;
+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &intelfb_ops;
/* setup aperture base/size for vesafb takeover */
@@ -148,8 +150,6 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
info->fix.smem_len = size;
- info->flags = FBINFO_DEFAULT;
-
info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
size);
if (!info->screen_base) {
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 2405d5e..e9b06e4 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -12,12 +12,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nouveau_dp.o \
nv04_timer.o \
nv04_mc.o nv40_mc.o nv50_mc.o \
- nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o \
- nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
+ nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \
+ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \
nv04_graph.o nv10_graph.o nv20_graph.o \
- nv40_graph.o nv50_graph.o \
+ nv40_graph.o nv50_graph.o nvc0_graph.o \
nv40_grctx.o nv50_grctx.o \
- nv04_instmem.o nv50_instmem.o \
+ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 7369b5e..0b69a96 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -1928,6 +1928,31 @@ init_condition_time(struct nvbios *bios, uint16_t offset,
}
static int
+init_ltime(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_LTIME opcode: 0x57 ('V')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (16 bit): time
+ *
+ * Sleep for "time" miliseconds.
+ */
+
+ unsigned time = ROM16(bios->data[offset + 1]);
+
+ if (!iexec->execute)
+ return 3;
+
+ BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X miliseconds\n",
+ offset, time);
+
+ msleep(time);
+
+ return 3;
+}
+
+static int
init_zm_reg_sequence(struct nvbios *bios, uint16_t offset,
struct init_exec *iexec)
{
@@ -1995,6 +2020,64 @@ init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
}
static int
+init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_I2C_IF opcode: 0x5E ('^')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (8 bit): DCB I2C table entry index
+ * offset + 2 (8 bit): I2C slave address
+ * offset + 3 (8 bit): I2C register
+ * offset + 4 (8 bit): mask
+ * offset + 5 (8 bit): data
+ *
+ * Read the register given by "I2C register" on the device addressed
+ * by "I2C slave address" on the I2C bus given by "DCB I2C table
+ * entry index". Compare the result AND "mask" to "data".
+ * If they're not equal, skip subsequent opcodes until condition is
+ * inverted (INIT_NOT), or we hit INIT_RESUME
+ */
+
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t reg = bios->data[offset + 3];
+ uint8_t mask = bios->data[offset + 4];
+ uint8_t data = bios->data[offset + 5];
+ struct nouveau_i2c_chan *chan;
+ union i2c_smbus_data val;
+ int ret;
+
+ /* no execute check by design */
+
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n",
+ offset, i2c_index, i2c_address);
+
+ chan = init_i2c_device_find(bios->dev, i2c_index);
+ if (!chan)
+ return -ENODEV;
+
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ I2C_SMBUS_READ, reg,
+ I2C_SMBUS_BYTE_DATA, &val);
+ if (ret < 0) {
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: [no device], "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reg, mask, data);
+ iexec->execute = 0;
+ return 6;
+ }
+
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reg, val.byte, mask, data);
+
+ iexec->execute = ((val.byte & mask) == data);
+
+ return 6;
+}
+
+static int
init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
/*
@@ -2083,9 +2166,10 @@ peek_fb(struct drm_device *dev, struct io_mapping *fb,
uint32_t val = 0;
if (off < pci_resource_len(dev->pdev, 1)) {
- uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off, KM_USER0);
+ uint32_t __iomem *p =
+ io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0);
- val = ioread32(p);
+ val = ioread32(p + (off & ~PAGE_MASK));
io_mapping_unmap_atomic(p, KM_USER0);
}
@@ -2098,9 +2182,10 @@ poke_fb(struct drm_device *dev, struct io_mapping *fb,
uint32_t off, uint32_t val)
{
if (off < pci_resource_len(dev->pdev, 1)) {
- uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off, KM_USER0);
+ uint32_t __iomem *p =
+ io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0);
- iowrite32(val, p);
+ iowrite32(val, p + (off & ~PAGE_MASK));
wmb();
io_mapping_unmap_atomic(p, KM_USER0);
@@ -2165,7 +2250,7 @@ nv04_init_compute_mem(struct nvbios *bios)
NV04_PFB_BOOT_0_RAM_AMOUNT,
NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
- } else if (peek_fb(dev, fb, 0) == patt) {
+ } else if (peek_fb(dev, fb, 0) != patt) {
if (read_back_fb(dev, fb, 0x800000, patt))
bios_md32(bios, NV04_PFB_BOOT_0,
NV04_PFB_BOOT_0_RAM_AMOUNT,
@@ -2593,7 +2678,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset,
/* no iexec->execute check by design */
uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
- uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
+ uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & 0x40) >> 6;
if (bios->major_version > 2)
return 0;
@@ -3140,7 +3225,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
int i;
- if (dev_priv->card_type != NV_50) {
+ if (dev_priv->card_type < NV_50) {
NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
return 1;
}
@@ -3490,6 +3575,69 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
return len;
}
+static int
+init_i2c_long_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_I2C_LONG_IF opcode: 0x9A ('')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (8 bit): DCB I2C table entry index
+ * offset + 2 (8 bit): I2C slave address
+ * offset + 3 (16 bit): I2C register
+ * offset + 5 (8 bit): mask
+ * offset + 6 (8 bit): data
+ *
+ * Read the register given by "I2C register" on the device addressed
+ * by "I2C slave address" on the I2C bus given by "DCB I2C table
+ * entry index". Compare the result AND "mask" to "data".
+ * If they're not equal, skip subsequent opcodes until condition is
+ * inverted (INIT_NOT), or we hit INIT_RESUME
+ */
+
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t reglo = bios->data[offset + 3];
+ uint8_t reghi = bios->data[offset + 4];
+ uint8_t mask = bios->data[offset + 5];
+ uint8_t data = bios->data[offset + 6];
+ struct nouveau_i2c_chan *chan;
+ uint8_t buf0[2] = { reghi, reglo };
+ uint8_t buf1[1];
+ struct i2c_msg msg[2] = {
+ { i2c_address, 0, 1, buf0 },
+ { i2c_address, I2C_M_RD, 1, buf1 },
+ };
+ int ret;
+
+ /* no execute check by design */
+
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n",
+ offset, i2c_index, i2c_address);
+
+ chan = init_i2c_device_find(bios->dev, i2c_index);
+ if (!chan)
+ return -ENODEV;
+
+
+ ret = i2c_transfer(&chan->adapter, msg, 2);
+ if (ret < 0) {
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: [no device], "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reghi, reglo, mask, data);
+ iexec->execute = 0;
+ return 7;
+ }
+
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: 0x%02X, "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reghi, reglo, buf1[0], mask, data);
+
+ iexec->execute = ((buf1[0] & mask) == data);
+
+ return 7;
+}
+
static struct init_tbl_entry itbl_entry[] = {
/* command name , id , length , offset , mult , command handler */
/* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */
@@ -3516,9 +3664,11 @@ static struct init_tbl_entry itbl_entry[] = {
{ "INIT_ZM_CR" , 0x53, init_zm_cr },
{ "INIT_ZM_CR_GROUP" , 0x54, init_zm_cr_group },
{ "INIT_CONDITION_TIME" , 0x56, init_condition_time },
+ { "INIT_LTIME" , 0x57, init_ltime },
{ "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence },
/* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
{ "INIT_SUB_DIRECT" , 0x5B, init_sub_direct },
+ { "INIT_I2C_IF" , 0x5E, init_i2c_if },
{ "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg },
{ "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io },
{ "INIT_COMPUTE_MEM" , 0x63, init_compute_mem },
@@ -3552,6 +3702,7 @@ static struct init_tbl_entry itbl_entry[] = {
{ "INIT_97" , 0x97, init_97 },
{ "INIT_AUXCH" , 0x98, init_auxch },
{ "INIT_ZM_AUXCH" , 0x99, init_zm_auxch },
+ { "INIT_I2C_LONG_IF" , 0x9A, init_i2c_long_if },
{ NULL , 0 , NULL }
};
@@ -4410,7 +4561,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
bios->display.script_table_ptr,
table[2], table[3], table[0] >= 0x21);
if (!otable) {
- NV_ERROR(dev, "Couldn't find matching output script table\n");
+ NV_DEBUG_KMS(dev, "failed to match any output table\n");
return 1;
}
@@ -4467,7 +4618,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
if (script)
script = clkcmptable(bios, script, pxclk);
if (!script) {
- NV_ERROR(dev, "clock script 0 not found\n");
+ NV_DEBUG_KMS(dev, "clock script 0 not found\n");
return 1;
}
@@ -4826,7 +4977,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
pll_lim->min_p = record[12];
pll_lim->max_p = record[13];
/* where did this go to?? */
- if (limit_match == 0x00614100 || limit_match == 0x00614900)
+ if ((entry[0] & 0xf0) == 0x80)
pll_lim->refclk = 27000;
else
pll_lim->refclk = 100000;
@@ -5852,7 +6003,7 @@ static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads)
entry->i2c_index = i2c;
entry->heads = heads;
entry->location = DCB_LOC_ON_CHIP;
- /* "or" mostly unused in early gen crt modesetting, 0 is fine */
+ entry->or = 1;
}
static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads)
@@ -5980,7 +6131,13 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
}
break;
case OUTPUT_TMDS:
- entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+ if (dcb->version >= 0x40)
+ entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+ else if (dcb->version >= 0x30)
+ entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
+ else if (dcb->version >= 0x22)
+ entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4;
+
break;
case 0xe:
/* weird g80 mobile type that "nv" treats as a terminator */
@@ -6270,6 +6427,19 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
dcb->i2c_table = &bios->data[i2ctabptr];
if (dcb->version >= 0x30)
dcb->i2c_default_indices = dcb->i2c_table[4];
+
+ /*
+ * Parse the "management" I2C bus, used for hardware
+ * monitoring and some external TMDS transmitters.
+ */
+ if (dcb->version >= 0x22) {
+ int idx = (dcb->version >= 0x40 ?
+ dcb->i2c_default_indices & 0xf :
+ 2);
+
+ read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+ idx, &dcb->i2c[idx]);
+ }
}
if (entries > DCB_MAX_NUM_ENTRIES)
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 024458a..fd14dfd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -131,6 +131,7 @@ struct dcb_entry {
} dpconf;
struct {
struct sor_conf sor;
+ int slave_addr;
} tmdsconf;
};
bool i2c_upper_default;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 3ca8343..84f8518 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -51,9 +51,6 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
if (nvbo->tile)
nv10_mem_expire_tiling(dev, nvbo->tile, NULL);
- spin_lock(&dev_priv->ttm.bo_list_lock);
- list_del(&nvbo->head);
- spin_unlock(&dev_priv->ttm.bo_list_lock);
kfree(nvbo);
}
@@ -166,9 +163,6 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
}
nvbo->channel = NULL;
- spin_lock(&dev_priv->ttm.bo_list_lock);
- list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
- spin_unlock(&dev_priv->ttm.bo_list_lock);
*pnvbo = nvbo;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 734e926..b1b22ba 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -37,12 +37,6 @@
#include "nouveau_connector.h"
#include "nouveau_hw.h"
-static inline struct drm_encoder_slave_funcs *
-get_slave_funcs(struct nouveau_encoder *enc)
-{
- return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
-}
-
static struct nouveau_encoder *
find_encoder_by_type(struct drm_connector *connector, int type)
{
@@ -360,6 +354,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
struct drm_device *dev = connector->dev;
int ret;
@@ -432,8 +427,8 @@ nouveau_connector_set_property(struct drm_connector *connector,
}
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
- return get_slave_funcs(nv_encoder)->
- set_property(to_drm_encoder(nv_encoder), connector, property, value);
+ return get_slave_funcs(encoder)->set_property(
+ encoder, connector, property, value);
return -EINVAL;
}
@@ -545,6 +540,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
int ret = 0;
/* destroy the native mode, the attached monitor could have changed.
@@ -580,8 +576,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
}
if (nv_encoder->dcb->type == OUTPUT_TV)
- ret = get_slave_funcs(nv_encoder)->
- get_modes(to_drm_encoder(nv_encoder), connector);
+ ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
nv_connector->dcb->type == DCB_CONNECTOR_eDP)
@@ -597,6 +592,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
unsigned min_clock = 25000, max_clock = min_clock;
unsigned clock = mode->clock;
@@ -623,8 +619,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
max_clock = 350000;
break;
case OUTPUT_TV:
- return get_slave_funcs(nv_encoder)->
- mode_valid(to_drm_encoder(nv_encoder), mode);
+ return get_slave_funcs(encoder)->mode_valid(encoder, mode);
case OUTPUT_DP:
if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
max_clock = nv_encoder->dp.link_nr * 270000;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 74e6b4e..2e11fd6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -84,16 +84,16 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
if (!gem)
- return NULL;
+ return ERR_PTR(-ENOENT);
nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
if (!nouveau_fb)
- return NULL;
+ return ERR_PTR(-ENOMEM);
ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem));
if (ret) {
drm_gem_object_unreference(gem);
- return NULL;
+ return ERR_PTR(ret);
}
return &nouveau_fb->base;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 33742b1..8a1b188 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -572,47 +572,64 @@ out:
return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
}
-int
-nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
- uint8_t write_byte, uint8_t *read_byte)
+static int
+nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
- struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter;
+ struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap;
struct drm_device *dev = auxch->dev;
- int ret = 0, cmd, addr = algo_data->address;
- uint8_t *buf;
-
- if (mode == MODE_I2C_READ) {
- cmd = AUX_I2C_READ;
- buf = read_byte;
- } else {
- cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE;
- buf = &write_byte;
- }
+ struct i2c_msg *msg = msgs;
+ int ret, mcnt = num;
- if (!(mode & MODE_I2C_STOP))
- cmd |= AUX_I2C_MOT;
+ while (mcnt--) {
+ u8 remaining = msg->len;
+ u8 *ptr = msg->buf;
- if (mode & MODE_I2C_START)
- return 1;
+ while (remaining) {
+ u8 cnt = (remaining > 16) ? 16 : remaining;
+ u8 cmd;
- for (;;) {
- ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1);
- if (ret < 0)
- return ret;
-
- switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
- case NV50_AUXCH_STAT_REPLY_I2C_ACK:
- return 1;
- case NV50_AUXCH_STAT_REPLY_I2C_NACK:
- return -EREMOTEIO;
- case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
- udelay(100);
- break;
- default:
- NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret);
- return -EREMOTEIO;
+ if (msg->flags & I2C_M_RD)
+ cmd = AUX_I2C_READ;
+ else
+ cmd = AUX_I2C_WRITE;
+
+ if (mcnt || remaining > 16)
+ cmd |= AUX_I2C_MOT;
+
+ ret = nouveau_dp_auxch(auxch, cmd, msg->addr, ptr, cnt);
+ if (ret < 0)
+ return ret;
+
+ switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
+ case NV50_AUXCH_STAT_REPLY_I2C_ACK:
+ break;
+ case NV50_AUXCH_STAT_REPLY_I2C_NACK:
+ return -EREMOTEIO;
+ case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
+ udelay(100);
+ continue;
+ default:
+ NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret);
+ return -EREMOTEIO;
+ }
+
+ ptr += cnt;
+ remaining -= cnt;
}
+
+ msg++;
}
+
+ return num;
+}
+
+static u32
+nouveau_dp_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
+const struct i2c_algorithm nouveau_dp_i2c_algo = {
+ .master_xfer = nouveau_dp_i2c_xfer,
+ .functionality = nouveau_dp_i2c_func
+};
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index e15db15..e424bf7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -410,7 +410,7 @@ enum nv04_fp_display_regs {
struct nv04_crtc_reg {
unsigned char MiscOutReg; /* */
- uint8_t CRTC[0x9f];
+ uint8_t CRTC[0xa0];
uint8_t CR58[0x10];
uint8_t Sequencer[5];
uint8_t Graphics[9];
@@ -509,6 +509,7 @@ enum nouveau_card_type {
NV_30 = 0x30,
NV_40 = 0x40,
NV_50 = 0x50,
+ NV_C0 = 0xc0,
};
struct drm_nouveau_private {
@@ -536,8 +537,6 @@ struct drm_nouveau_private {
struct drm_global_reference mem_global_ref;
struct ttm_bo_global_ref bo_global_ref;
struct ttm_bo_device bdev;
- spinlock_t bo_list_lock;
- struct list_head bo_list;
atomic_t validate_sequence;
} ttm;
@@ -931,6 +930,10 @@ extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t,
extern int nv50_fb_init(struct drm_device *);
extern void nv50_fb_takedown(struct drm_device *);
+/* nvc0_fb.c */
+extern int nvc0_fb_init(struct drm_device *);
+extern void nvc0_fb_takedown(struct drm_device *);
+
/* nv04_fifo.c */
extern int nv04_fifo_init(struct drm_device *);
extern void nv04_fifo_disable(struct drm_device *);
@@ -968,6 +971,20 @@ extern void nv50_fifo_destroy_context(struct nouveau_channel *);
extern int nv50_fifo_load_context(struct nouveau_channel *);
extern int nv50_fifo_unload_context(struct drm_device *);
+/* nvc0_fifo.c */
+extern int nvc0_fifo_init(struct drm_device *);
+extern void nvc0_fifo_takedown(struct drm_device *);
+extern void nvc0_fifo_disable(struct drm_device *);
+extern void nvc0_fifo_enable(struct drm_device *);
+extern bool nvc0_fifo_reassign(struct drm_device *, bool);
+extern bool nvc0_fifo_cache_flush(struct drm_device *);
+extern bool nvc0_fifo_cache_pull(struct drm_device *, bool);
+extern int nvc0_fifo_channel_id(struct drm_device *);
+extern int nvc0_fifo_create_context(struct nouveau_channel *);
+extern void nvc0_fifo_destroy_context(struct nouveau_channel *);
+extern int nvc0_fifo_load_context(struct nouveau_channel *);
+extern int nvc0_fifo_unload_context(struct drm_device *);
+
/* nv04_graph.c */
extern struct nouveau_pgraph_object_class nv04_graph_grclass[];
extern int nv04_graph_init(struct drm_device *);
@@ -1032,6 +1049,16 @@ extern int nv50_graph_unload_context(struct drm_device *);
extern void nv50_graph_context_switch(struct drm_device *);
extern int nv50_grctx_init(struct nouveau_grctx *);
+/* nvc0_graph.c */
+extern int nvc0_graph_init(struct drm_device *);
+extern void nvc0_graph_takedown(struct drm_device *);
+extern void nvc0_graph_fifo_access(struct drm_device *, bool);
+extern struct nouveau_channel *nvc0_graph_channel(struct drm_device *);
+extern int nvc0_graph_create_context(struct nouveau_channel *);
+extern void nvc0_graph_destroy_context(struct nouveau_channel *);
+extern int nvc0_graph_load_context(struct nouveau_channel *);
+extern int nvc0_graph_unload_context(struct drm_device *);
+
/* nv04_instmem.c */
extern int nv04_instmem_init(struct drm_device *);
extern void nv04_instmem_takedown(struct drm_device *);
@@ -1058,6 +1085,18 @@ extern void nv50_instmem_flush(struct drm_device *);
extern void nv84_instmem_flush(struct drm_device *);
extern void nv50_vm_flush(struct drm_device *, int engine);
+/* nvc0_instmem.c */
+extern int nvc0_instmem_init(struct drm_device *);
+extern void nvc0_instmem_takedown(struct drm_device *);
+extern int nvc0_instmem_suspend(struct drm_device *);
+extern void nvc0_instmem_resume(struct drm_device *);
+extern int nvc0_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
+ uint32_t *size);
+extern void nvc0_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
+extern int nvc0_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
+extern int nvc0_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
+extern void nvc0_instmem_flush(struct drm_device *);
+
/* nv04_mc.c */
extern int nv04_mc_init(struct drm_device *);
extern void nv04_mc_takedown(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index a1a0d48..7c82d68 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -71,6 +71,12 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
return &enc->base.base;
}
+static inline struct drm_encoder_slave_funcs *
+get_slave_funcs(struct drm_encoder *enc)
+{
+ return to_encoder_slave(enc)->slave_funcs;
+}
+
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 2fb2444..dbd30b2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -250,6 +250,7 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_IMAGEBLIT;
+ info->flags |= FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &nouveau_fbcon_ops;
info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
dev_priv->vm_vram_base;
@@ -280,6 +281,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
if (dev_priv->channel && !nouveau_nofbaccel) {
switch (dev_priv->card_type) {
+ case NV_C0:
+ break;
case NV_50:
nv50_fbcon_accel_init(info);
info->fbops = &nv50_fbcon_ops;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 547f2c2..0f417ac 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -284,7 +284,7 @@ retry:
if (!gem) {
NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
validate_fini(op, NULL);
- return -EINVAL;
+ return -ENOENT;
}
nvbo = gem->driver_private;
@@ -759,7 +759,7 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
- return ret;
+ return -ENOENT;
nvbo = nouveau_gem_object(gem);
if (nvbo->cpu_filp) {
@@ -797,7 +797,7 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
- return ret;
+ return -ENOENT;
nvbo = nouveau_gem_object(gem);
if (nvbo->cpu_filp != file_priv)
@@ -822,7 +822,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
- return -EINVAL;
+ return -ENOENT;
ret = nouveau_gem_info(gem, req);
drm_gem_object_unreference_unlocked(gem);
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
index 7855b35..7b61368 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -865,8 +865,12 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
- if (dev_priv->card_type >= NV_30)
+
+ if (dev_priv->card_type >= NV_30) {
rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ rd_cio_state(dev, head, regp, 0x9f);
+ }
+
rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
@@ -971,8 +975,11 @@ nv_load_state_ext(struct drm_device *dev, int head,
wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
- if (dev_priv->card_type >= NV_30)
+
+ if (dev_priv->card_type >= NV_30) {
wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ wr_cio_state(dev, head, regp, 0x9f);
+ }
wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index cb0cb34..0bd407c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -163,7 +163,7 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
if (entry->chan)
return -EEXIST;
- if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) {
+ if (dev_priv->card_type == NV_C0 && entry->read >= NV50_I2C_PORTS) {
NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
return -EINVAL;
}
@@ -174,26 +174,26 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
switch (entry->port_type) {
case 0:
- i2c->algo.bit.setsda = nv04_i2c_setsda;
- i2c->algo.bit.setscl = nv04_i2c_setscl;
- i2c->algo.bit.getsda = nv04_i2c_getsda;
- i2c->algo.bit.getscl = nv04_i2c_getscl;
+ i2c->bit.setsda = nv04_i2c_setsda;
+ i2c->bit.setscl = nv04_i2c_setscl;
+ i2c->bit.getsda = nv04_i2c_getsda;
+ i2c->bit.getscl = nv04_i2c_getscl;
i2c->rd = entry->read;
i2c->wr = entry->write;
break;
case 4:
- i2c->algo.bit.setsda = nv4e_i2c_setsda;
- i2c->algo.bit.setscl = nv4e_i2c_setscl;
- i2c->algo.bit.getsda = nv4e_i2c_getsda;
- i2c->algo.bit.getscl = nv4e_i2c_getscl;
+ i2c->bit.setsda = nv4e_i2c_setsda;
+ i2c->bit.setscl = nv4e_i2c_setscl;
+ i2c->bit.getsda = nv4e_i2c_getsda;
+ i2c->bit.getscl = nv4e_i2c_getscl;
i2c->rd = 0x600800 + entry->read;
i2c->wr = 0x600800 + entry->write;
break;
case 5:
- i2c->algo.bit.setsda = nv50_i2c_setsda;
- i2c->algo.bit.setscl = nv50_i2c_setscl;
- i2c->algo.bit.getsda = nv50_i2c_getsda;
- i2c->algo.bit.getscl = nv50_i2c_getscl;
+ i2c->bit.setsda = nv50_i2c_setsda;
+ i2c->bit.setscl = nv50_i2c_setscl;
+ i2c->bit.getsda = nv50_i2c_getsda;
+ i2c->bit.getscl = nv50_i2c_getscl;
i2c->rd = nv50_i2c_port[entry->read];
i2c->wr = i2c->rd;
break;
@@ -216,17 +216,14 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
i2c_set_adapdata(&i2c->adapter, i2c);
if (entry->port_type < 6) {
- i2c->adapter.algo_data = &i2c->algo.bit;
- i2c->algo.bit.udelay = 40;
- i2c->algo.bit.timeout = usecs_to_jiffies(5000);
- i2c->algo.bit.data = i2c;
+ i2c->adapter.algo_data = &i2c->bit;
+ i2c->bit.udelay = 40;
+ i2c->bit.timeout = usecs_to_jiffies(5000);
+ i2c->bit.data = i2c;
ret = i2c_bit_add_bus(&i2c->adapter);
} else {
- i2c->adapter.algo_data = &i2c->algo.dp;
- i2c->algo.dp.running = false;
- i2c->algo.dp.address = 0;
- i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch;
- ret = i2c_dp_aux_add_bus(&i2c->adapter);
+ i2c->adapter.algo = &nouveau_dp_i2c_algo;
+ ret = i2c_add_adapter(&i2c->adapter);
}
if (ret) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
index 6dd2f87..f71cb32 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.h
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
@@ -33,10 +33,7 @@ struct dcb_i2c_entry;
struct nouveau_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
- union {
- struct i2c_algo_bit_data bit;
- struct i2c_algo_dp_aux_data dp;
- } algo;
+ struct i2c_algo_bit_data bit;
unsigned rd;
unsigned wr;
unsigned data;
@@ -49,7 +46,6 @@ bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
int nouveau_i2c_identify(struct drm_device *dev, const char *what,
struct i2c_board_info *info, int index);
-int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
- uint8_t *read_byte);
+extern const struct i2c_algorithm nouveau_dp_i2c_algo;
#endif /* __NOUVEAU_I2C_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 53360f1..794b0ee 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -49,7 +49,7 @@ nouveau_irq_preinstall(struct drm_device *dev)
/* Master disable */
nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
- if (dev_priv->card_type == NV_50) {
+ if (dev_priv->card_type >= NV_50) {
INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh);
INIT_LIST_HEAD(&dev_priv->vbl_waiting);
@@ -586,11 +586,11 @@ nouveau_pgraph_irq_handler(struct drm_device *dev)
}
if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
- nouveau_pgraph_intr_context_switch(dev);
-
status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
nv_wr32(dev, NV03_PGRAPH_INTR,
NV_PGRAPH_INTR_CONTEXT_SWITCH);
+
+ nouveau_pgraph_intr_context_switch(dev);
}
if (status) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index a9f36ab..9689d414 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -320,7 +320,8 @@ nouveau_mem_detect(struct drm_device *dev)
if (dev_priv->card_type < NV_50) {
dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
- } else {
+ } else
+ if (dev_priv->card_type < NV_C0) {
dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
dev_priv->vram_size &= 0xffffffff00ll;
@@ -328,6 +329,9 @@ nouveau_mem_detect(struct drm_device *dev)
dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10);
dev_priv->vram_sys_base <<= 12;
}
+ } else {
+ dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20;
+ dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
}
NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
@@ -351,7 +355,7 @@ nouveau_mem_reset_agp(struct drm_device *dev)
/* First of all, disable fast writes, otherwise if it's
* already enabled in the AGP bridge and we disable the card's
* AGP controller we might be locking ourselves out of it. */
- if (dev->agp->acquired) {
+ if (nv_rd32(dev, NV04_PBUS_PCI_NV_19) & PCI_AGP_COMMAND_FW) {
struct drm_agp_info info;
struct drm_agp_mode mode;
@@ -359,7 +363,7 @@ nouveau_mem_reset_agp(struct drm_device *dev)
if (ret)
return ret;
- mode.mode = info.mode & ~0x10;
+ mode.mode = info.mode & ~PCI_AGP_COMMAND_FW;
ret = drm_agp_enable(dev, mode);
if (ret)
return ret;
@@ -405,6 +409,8 @@ nouveau_mem_init_agp(struct drm_device *dev)
}
}
+ nouveau_mem_reset_agp(dev);
+
ret = drm_agp_info(dev, &info);
if (ret) {
NV_ERROR(dev, "Unable to get AGP info: %d\n", ret);
@@ -459,8 +465,6 @@ nouveau_mem_init(struct drm_device *dev)
return ret;
}
- INIT_LIST_HEAD(&dev_priv->ttm.bo_list);
- spin_lock_init(&dev_priv->ttm.bo_list_lock);
spin_lock_init(&dev_priv->tile.lock);
dev_priv->fb_available_size = dev_priv->vram_size;
@@ -494,7 +498,6 @@ nouveau_mem_init(struct drm_device *dev)
/* GART */
#if !defined(__powerpc__) && !defined(__ia64__)
if (drm_device_is_agp(dev) && dev->agp && !nouveau_noagp) {
- nouveau_mem_reset_agp(dev);
ret = nouveau_mem_init_agp(dev);
if (ret)
NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index 9c1056c..21a6e45 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -220,28 +220,21 @@
# define NV_PGRAPH_INTR_ERROR (1<<20)
#define NV10_PGRAPH_CTX_CONTROL 0x00400144
#define NV10_PGRAPH_CTX_USER 0x00400148
-#define NV10_PGRAPH_CTX_SWITCH1 0x0040014C
-#define NV10_PGRAPH_CTX_SWITCH2 0x00400150
-#define NV10_PGRAPH_CTX_SWITCH3 0x00400154
-#define NV10_PGRAPH_CTX_SWITCH4 0x00400158
-#define NV10_PGRAPH_CTX_SWITCH5 0x0040015C
+#define NV10_PGRAPH_CTX_SWITCH(i) (0x0040014C + 0x4*(i))
#define NV04_PGRAPH_CTX_SWITCH1 0x00400160
-#define NV10_PGRAPH_CTX_CACHE1 0x00400160
+#define NV10_PGRAPH_CTX_CACHE(i, j) (0x00400160 \
+ + 0x4*(i) + 0x20*(j))
#define NV04_PGRAPH_CTX_SWITCH2 0x00400164
#define NV04_PGRAPH_CTX_SWITCH3 0x00400168
#define NV04_PGRAPH_CTX_SWITCH4 0x0040016C
#define NV04_PGRAPH_CTX_CONTROL 0x00400170
#define NV04_PGRAPH_CTX_USER 0x00400174
#define NV04_PGRAPH_CTX_CACHE1 0x00400180
-#define NV10_PGRAPH_CTX_CACHE2 0x00400180
#define NV03_PGRAPH_CTX_CONTROL 0x00400190
#define NV03_PGRAPH_CTX_USER 0x00400194
#define NV04_PGRAPH_CTX_CACHE2 0x004001A0
-#define NV10_PGRAPH_CTX_CACHE3 0x004001A0
#define NV04_PGRAPH_CTX_CACHE3 0x004001C0
-#define NV10_PGRAPH_CTX_CACHE4 0x004001C0
#define NV04_PGRAPH_CTX_CACHE4 0x004001E0
-#define NV10_PGRAPH_CTX_CACHE5 0x004001E0
#define NV40_PGRAPH_CTXCTL_0304 0x00400304
#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001
#define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308
@@ -356,9 +349,12 @@
#define NV04_PGRAPH_FFINTFC_ST2 0x00400754
#define NV10_PGRAPH_RDI_DATA 0x00400754
#define NV04_PGRAPH_DMA_PITCH 0x00400760
-#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
+#define NV10_PGRAPH_FFINTFC_FIFO_PTR 0x00400760
#define NV04_PGRAPH_DVD_COLORFMT 0x00400764
+#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
#define NV04_PGRAPH_SCALED_FORMAT 0x00400768
+#define NV10_PGRAPH_FFINTFC_ST2_DL 0x00400768
+#define NV10_PGRAPH_FFINTFC_ST2_DH 0x0040076c
#define NV10_PGRAPH_DMA_PITCH 0x00400770
#define NV10_PGRAPH_DVD_COLORFMT 0x00400774
#define NV10_PGRAPH_SCALED_FORMAT 0x00400778
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index ee3729e..989322b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -359,6 +359,54 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.set = nv50_gpio_set;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
break;
+ case 0xC0:
+ engine->instmem.init = nvc0_instmem_init;
+ engine->instmem.takedown = nvc0_instmem_takedown;
+ engine->instmem.suspend = nvc0_instmem_suspend;
+ engine->instmem.resume = nvc0_instmem_resume;
+ engine->instmem.populate = nvc0_instmem_populate;
+ engine->instmem.clear = nvc0_instmem_clear;
+ engine->instmem.bind = nvc0_instmem_bind;
+ engine->instmem.unbind = nvc0_instmem_unbind;
+ engine->instmem.flush = nvc0_instmem_flush;
+ engine->mc.init = nv50_mc_init;
+ engine->mc.takedown = nv50_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+ engine->timer.read = nv04_timer_read;
+ engine->timer.takedown = nv04_timer_takedown;
+ engine->fb.init = nvc0_fb_init;
+ engine->fb.takedown = nvc0_fb_takedown;
+ engine->graph.grclass = NULL; //nvc0_graph_grclass;
+ engine->graph.init = nvc0_graph_init;
+ engine->graph.takedown = nvc0_graph_takedown;
+ engine->graph.fifo_access = nvc0_graph_fifo_access;
+ engine->graph.channel = nvc0_graph_channel;
+ engine->graph.create_context = nvc0_graph_create_context;
+ engine->graph.destroy_context = nvc0_graph_destroy_context;
+ engine->graph.load_context = nvc0_graph_load_context;
+ engine->graph.unload_context = nvc0_graph_unload_context;
+ engine->fifo.channels = 128;
+ engine->fifo.init = nvc0_fifo_init;
+ engine->fifo.takedown = nvc0_fifo_takedown;
+ engine->fifo.disable = nvc0_fifo_disable;
+ engine->fifo.enable = nvc0_fifo_enable;
+ engine->fifo.reassign = nvc0_fifo_reassign;
+ engine->fifo.channel_id = nvc0_fifo_channel_id;
+ engine->fifo.create_context = nvc0_fifo_create_context;
+ engine->fifo.destroy_context = nvc0_fifo_destroy_context;
+ engine->fifo.load_context = nvc0_fifo_load_context;
+ engine->fifo.unload_context = nvc0_fifo_unload_context;
+ engine->display.early_init = nv50_display_early_init;
+ engine->display.late_takedown = nv50_display_late_takedown;
+ engine->display.create = nv50_display_create;
+ engine->display.init = nv50_display_init;
+ engine->display.destroy = nv50_display_destroy;
+ engine->gpio.init = nv50_gpio_init;
+ engine->gpio.takedown = nouveau_stub_takedown;
+ engine->gpio.get = nv50_gpio_get;
+ engine->gpio.set = nv50_gpio_set;
+ engine->gpio.irq_enable = nv50_gpio_irq_enable;
+ break;
default:
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
return 1;
@@ -739,8 +787,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
int ret;
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
- if (!dev_priv)
- return -ENOMEM;
+ if (!dev_priv) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
dev->dev_private = dev_priv;
dev_priv->dev = dev;
@@ -750,8 +800,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev->pci_vendor, dev->pci_device, dev->pdev->class);
dev_priv->wq = create_workqueue("nouveau");
- if (!dev_priv->wq)
- return -EINVAL;
+ if (!dev_priv->wq) {
+ ret = -EINVAL;
+ goto err_priv;
+ }
/* resource 0 is mmio regs */
/* resource 1 is linear FB */
@@ -764,7 +816,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
if (!dev_priv->mmio) {
NV_ERROR(dev, "Unable to initialize the mmio mapping. "
"Please report your setup to " DRIVER_EMAIL "\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_wq;
}
NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
(unsigned long long)mmio_start_offs);
@@ -810,9 +863,13 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
case 0xa0:
dev_priv->card_type = NV_50;
break;
+ case 0xc0:
+ dev_priv->card_type = NV_C0;
+ break;
default:
NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_mmio;
}
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
@@ -820,7 +877,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
ret = nouveau_remove_conflicting_drivers(dev);
if (ret)
- return ret;
+ goto err_mmio;
/* Map PRAMIN BAR, or on older cards, the aperture withing BAR0 */
if (dev_priv->card_type >= NV_40) {
@@ -834,7 +891,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to PRAMIN BAR");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mmio;
}
} else {
dev_priv->ramin_size = 1 * 1024 * 1024;
@@ -842,7 +900,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mmio;
}
}
@@ -857,9 +916,21 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
/* For kernel modesetting, init card now and bring up fbcon */
ret = nouveau_card_init(dev);
if (ret)
- return ret;
+ goto err_ramin;
return 0;
+
+err_ramin:
+ iounmap(dev_priv->ramin);
+err_mmio:
+ iounmap(dev_priv->mmio);
+err_wq:
+ destroy_workqueue(dev_priv->wq);
+err_priv:
+ kfree(dev_priv);
+ dev->dev_private = NULL;
+err_out:
+ return ret;
}
void nouveau_lastclose(struct drm_device *dev)
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 1c20c08..497df87 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -542,6 +542,9 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
* 1 << 30 on 0x60.830), for no apparent reason */
regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
+ if (dev_priv->card_type >= NV_30)
+ regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1;
+
regp->crtc_830 = mode->crtc_vdisplay - 3;
regp->crtc_834 = mode->crtc_vdisplay - 1;
@@ -739,15 +742,13 @@ nv_crtc_gamma_load(struct drm_crtc *crtc)
}
static void
-nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size)
+nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start,
+ uint32_t size)
{
+ int end = (start + size > 256) ? 256 : start + size, i;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
- int i;
- if (size != 256)
- return;
-
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
nv_crtc->lut.r[i] = r[i];
nv_crtc->lut.g[i] = g[i];
nv_crtc->lut.b[i] = b[i];
@@ -914,7 +915,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
- return -EINVAL;
+ return -ENOENT;
cursor = nouveau_gem_object(gem);
ret = nouveau_bo_map(cursor);
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
index 3311f3a..a5dcf768 100644
--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -34,6 +34,8 @@
#include "nouveau_hw.h"
#include "nvreg.h"
+#include "i2c/sil164.h"
+
#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
@@ -144,6 +146,36 @@ void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
}
}
+static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+ struct drm_encoder *slave;
+
+ if (dcb->type != OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP)
+ return NULL;
+
+ /* Some BIOSes (e.g. the one in a Quadro FX1000) report several
+ * TMDS transmitters at the same I2C address, in the same I2C
+ * bus. This can still work because in that case one of them is
+ * always hard-wired to a reasonable configuration using straps,
+ * and the other one needs to be programmed.
+ *
+ * I don't think there's a way to know which is which, even the
+ * blob programs the one exposed via I2C for *both* heads, so
+ * let's do the same.
+ */
+ list_for_each_entry(slave, &dev->mode_config.encoder_list, head) {
+ struct dcb_entry *slave_dcb = nouveau_encoder(slave)->dcb;
+
+ if (slave_dcb->type == OUTPUT_TMDS && get_slave_funcs(slave) &&
+ slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr)
+ return slave;
+ }
+
+ return NULL;
+}
+
static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -429,6 +461,11 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
+ /* Init external transmitters */
+ if (get_tmds_slave(encoder))
+ get_slave_funcs(get_tmds_slave(encoder))->mode_set(
+ encoder, &nv_encoder->mode, &nv_encoder->mode);
+
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
@@ -550,10 +587,42 @@ static void nv04_dfp_destroy(struct drm_encoder *encoder)
NV_DEBUG_KMS(encoder->dev, "\n");
+ if (get_slave_funcs(encoder))
+ get_slave_funcs(encoder)->destroy(encoder);
+
drm_encoder_cleanup(encoder);
kfree(nv_encoder);
}
+static void nv04_tmds_slave_init(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+ struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2);
+ struct i2c_board_info info[] = {
+ {
+ .type = "sil164",
+ .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38),
+ .platform_data = &(struct sil164_encoder_params) {
+ SIL164_INPUT_EDGE_RISING
+ }
+ },
+ { }
+ };
+ int type;
+
+ if (!nv_gf4_disp_arch(dev) || !i2c ||
+ get_tmds_slave(encoder))
+ return;
+
+ type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2);
+ if (type < 0)
+ return;
+
+ drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
+ &i2c->adapter, &info[type]);
+}
+
static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
.dpms = nv04_lvds_dpms,
.save = nv04_dfp_save,
@@ -616,6 +685,10 @@ nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry)
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
+ if (entry->type == OUTPUT_TMDS &&
+ entry->location != DCB_LOC_ON_CHIP)
+ nv04_tmds_slave_init(encoder);
+
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
index 94e299c..0b5d012 100644
--- a/drivers/gpu/drm/nouveau/nv04_tv.c
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
@@ -89,7 +89,7 @@ static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
- to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
+ get_slave_funcs(encoder)->dpms(encoder, mode);
}
static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
@@ -152,7 +152,7 @@ static void nv04_tv_mode_set(struct drm_encoder *encoder,
regp->tv_vskew = 1;
regp->tv_vsync_delay = 1;
- to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
+ get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode);
}
static void nv04_tv_commit(struct drm_encoder *encoder)
@@ -171,8 +171,7 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
static void nv04_tv_destroy(struct drm_encoder *encoder)
{
- to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
-
+ get_slave_funcs(encoder)->destroy(encoder);
drm_encoder_cleanup(encoder);
kfree(encoder->helper_private);
@@ -229,7 +228,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
goto fail_cleanup;
/* Fill the function pointers */
- sfuncs = to_encoder_slave(encoder)->slave_funcs;
+ sfuncs = get_slave_funcs(encoder);
*hfuncs = (struct drm_encoder_helper_funcs) {
.dpms = nv04_tv_dpms,
@@ -243,7 +242,6 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
};
/* Attach it to the specified connector. */
- sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data);
sfuncs->create_resources(encoder, connector);
drm_mode_connector_attach_encoder(connector, encoder);
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
index fcf2cdd..b2f6a57 100644
--- a/drivers/gpu/drm/nouveau/nv10_graph.c
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -43,51 +43,51 @@ struct pipe_state {
};
static int nv10_graph_ctx_regs[] = {
- NV10_PGRAPH_CTX_SWITCH1,
- NV10_PGRAPH_CTX_SWITCH2,
- NV10_PGRAPH_CTX_SWITCH3,
- NV10_PGRAPH_CTX_SWITCH4,
- NV10_PGRAPH_CTX_SWITCH5,
- NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */
- NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */
- NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */
- NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */
- NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */
- 0x00400164,
- 0x00400184,
- 0x004001a4,
- 0x004001c4,
- 0x004001e4,
- 0x00400168,
- 0x00400188,
- 0x004001a8,
- 0x004001c8,
- 0x004001e8,
- 0x0040016c,
- 0x0040018c,
- 0x004001ac,
- 0x004001cc,
- 0x004001ec,
- 0x00400170,
- 0x00400190,
- 0x004001b0,
- 0x004001d0,
- 0x004001f0,
- 0x00400174,
- 0x00400194,
- 0x004001b4,
- 0x004001d4,
- 0x004001f4,
- 0x00400178,
- 0x00400198,
- 0x004001b8,
- 0x004001d8,
- 0x004001f8,
- 0x0040017c,
- 0x0040019c,
- 0x004001bc,
- 0x004001dc,
- 0x004001fc,
+ NV10_PGRAPH_CTX_SWITCH(0),
+ NV10_PGRAPH_CTX_SWITCH(1),
+ NV10_PGRAPH_CTX_SWITCH(2),
+ NV10_PGRAPH_CTX_SWITCH(3),
+ NV10_PGRAPH_CTX_SWITCH(4),
+ NV10_PGRAPH_CTX_CACHE(0, 0),
+ NV10_PGRAPH_CTX_CACHE(0, 1),
+ NV10_PGRAPH_CTX_CACHE(0, 2),
+ NV10_PGRAPH_CTX_CACHE(0, 3),
+ NV10_PGRAPH_CTX_CACHE(0, 4),
+ NV10_PGRAPH_CTX_CACHE(1, 0),
+ NV10_PGRAPH_CTX_CACHE(1, 1),
+ NV10_PGRAPH_CTX_CACHE(1, 2),
+ NV10_PGRAPH_CTX_CACHE(1, 3),
+ NV10_PGRAPH_CTX_CACHE(1, 4),
+ NV10_PGRAPH_CTX_CACHE(2, 0),
+ NV10_PGRAPH_CTX_CACHE(2, 1),
+ NV10_PGRAPH_CTX_CACHE(2, 2),
+ NV10_PGRAPH_CTX_CACHE(2, 3),
+ NV10_PGRAPH_CTX_CACHE(2, 4),
+ NV10_PGRAPH_CTX_CACHE(3, 0),
+ NV10_PGRAPH_CTX_CACHE(3, 1),
+ NV10_PGRAPH_CTX_CACHE(3, 2),
+ NV10_PGRAPH_CTX_CACHE(3, 3),
+ NV10_PGRAPH_CTX_CACHE(3, 4),
+ NV10_PGRAPH_CTX_CACHE(4, 0),
+ NV10_PGRAPH_CTX_CACHE(4, 1),
+ NV10_PGRAPH_CTX_CACHE(4, 2),
+ NV10_PGRAPH_CTX_CACHE(4, 3),
+ NV10_PGRAPH_CTX_CACHE(4, 4),
+ NV10_PGRAPH_CTX_CACHE(5, 0),
+ NV10_PGRAPH_CTX_CACHE(5, 1),
+ NV10_PGRAPH_CTX_CACHE(5, 2),
+ NV10_PGRAPH_CTX_CACHE(5, 3),
+ NV10_PGRAPH_CTX_CACHE(5, 4),
+ NV10_PGRAPH_CTX_CACHE(6, 0),
+ NV10_PGRAPH_CTX_CACHE(6, 1),
+ NV10_PGRAPH_CTX_CACHE(6, 2),
+ NV10_PGRAPH_CTX_CACHE(6, 3),
+ NV10_PGRAPH_CTX_CACHE(6, 4),
+ NV10_PGRAPH_CTX_CACHE(7, 0),
+ NV10_PGRAPH_CTX_CACHE(7, 1),
+ NV10_PGRAPH_CTX_CACHE(7, 2),
+ NV10_PGRAPH_CTX_CACHE(7, 3),
+ NV10_PGRAPH_CTX_CACHE(7, 4),
NV10_PGRAPH_CTX_USER,
NV04_PGRAPH_DMA_START_0,
NV04_PGRAPH_DMA_START_1,
@@ -653,6 +653,78 @@ static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
return -1;
}
+static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan,
+ uint32_t inst)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ uint32_t st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4];
+ uint32_t ctx_user, ctx_switch[5];
+ int i, subchan = -1;
+
+ /* NV10TCL_DMA_VTXBUF (method 0x18c) modifies hidden state
+ * that cannot be restored via MMIO. Do it through the FIFO
+ * instead.
+ */
+
+ /* Look for a celsius object */
+ for (i = 0; i < 8; i++) {
+ int class = nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(i, 0)) & 0xfff;
+
+ if (class == 0x56 || class == 0x96 || class == 0x99) {
+ subchan = i;
+ break;
+ }
+ }
+
+ if (subchan < 0 || !inst)
+ return;
+
+ /* Save the current ctx object */
+ ctx_user = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
+ for (i = 0; i < 5; i++)
+ ctx_switch[i] = nv_rd32(dev, NV10_PGRAPH_CTX_SWITCH(i));
+
+ /* Save the FIFO state */
+ st2 = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2);
+ st2_dl = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DL);
+ st2_dh = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DH);
+ fifo_ptr = nv_rd32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR);
+
+ for (i = 0; i < ARRAY_SIZE(fifo); i++)
+ fifo[i] = nv_rd32(dev, 0x4007a0 + 4 * i);
+
+ /* Switch to the celsius subchannel */
+ for (i = 0; i < 5; i++)
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i),
+ nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(subchan, i)));
+ nv_mask(dev, NV10_PGRAPH_CTX_USER, 0xe000, subchan << 13);
+
+ /* Inject NV10TCL_DMA_VTXBUF */
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, 0);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2,
+ 0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst);
+ nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000);
+ pgraph->fifo_access(dev, true);
+ pgraph->fifo_access(dev, false);
+
+ /* Restore the FIFO state */
+ for (i = 0; i < ARRAY_SIZE(fifo); i++)
+ nv_wr32(dev, 0x4007a0 + 4 * i, fifo[i]);
+
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, fifo_ptr);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, st2);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, st2_dl);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DH, st2_dh);
+
+ /* Restore the current ctx object */
+ for (i = 0; i < 5; i++)
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i), ctx_switch[i]);
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, ctx_user);
+}
+
int nv10_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
@@ -670,6 +742,8 @@ int nv10_graph_load_context(struct nouveau_channel *chan)
}
nv10_graph_load_pipe(chan);
+ nv10_graph_load_dma_vtxbuf(chan, (nv_rd32(dev, NV10_PGRAPH_GLOBALSTATE1)
+ & 0xffff));
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
@@ -856,11 +930,12 @@ int nv10_graph_init(struct drm_device *dev)
for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
nv10_graph_set_region_tiling(dev, i, 0, 0, 0);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(0), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(1), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(2), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(3), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(4), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_STATE, 0xFFFFFFFF);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
diff --git a/drivers/gpu/drm/nouveau/nv30_fb.c b/drivers/gpu/drm/nouveau/nv30_fb.c
index 9d35c8b..4a3f2f0 100644
--- a/drivers/gpu/drm/nouveau/nv30_fb.c
+++ b/drivers/gpu/drm/nouveau/nv30_fb.c
@@ -30,15 +30,25 @@
#include "nouveau_drm.h"
static int
-calc_ref(int b, int l, int i)
+calc_bias(struct drm_device *dev, int k, int i, int j)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int b = (dev_priv->chipset > 0x30 ?
+ nv_rd32(dev, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) :
+ 0) & 0xf;
+
+ return 2 * (b & 0x8 ? b - 0x10 : b);
+}
+
+static int
+calc_ref(struct drm_device *dev, int l, int k, int i)
{
int j, x = 0;
for (j = 0; j < 4; j++) {
- int n = (b >> (8 * j) & 0xf);
- int m = (l >> (8 * i) & 0xff) + 2 * (n & 0x8 ? n - 0x10 : n);
+ int m = (l >> (8 * i) & 0xff) + calc_bias(dev, k, i, j);
- x |= (0x80 | (m & 0x1f)) << (8 * j);
+ x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j);
}
return x;
@@ -63,18 +73,16 @@ nv30_fb_init(struct drm_device *dev)
dev_priv->chipset == 0x35) {
/* Related to ROP count */
int n = (dev_priv->chipset == 0x31 ? 2 : 4);
- int b = (dev_priv->chipset > 0x30 ?
- nv_rd32(dev, 0x122c) & 0xf : 0);
int l = nv_rd32(dev, 0x1003d0);
for (i = 0; i < n; i++) {
for (j = 0; j < 3; j++)
nv_wr32(dev, 0x10037c + 0xc * i + 0x4 * j,
- calc_ref(b, l, j));
+ calc_ref(dev, l, 0, j));
for (j = 0; j < 2; j++)
nv_wr32(dev, 0x1003ac + 0x8 * i + 0x4 * j,
- calc_ref(b, l, j));
+ calc_ref(dev, l, 1, j));
}
}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 5d11ea1..bfd4ca2 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -264,11 +264,16 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
int
nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
{
- uint32_t reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
struct pll_lims pll;
- uint32_t reg1, reg2;
+ uint32_t reg, reg1, reg2;
int ret, N1, M1, N2, M2, P;
+ if (dev_priv->chipset < NV_C0)
+ reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
+ else
+ reg = 0x614140 + (head * 0x800);
+
ret = get_pll_limits(dev, reg, &pll);
if (ret)
return ret;
@@ -286,7 +291,8 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
nv_wr32(dev, reg, 0x10000611);
nv_wr32(dev, reg + 4, reg1 | (M1 << 16) | N1);
nv_wr32(dev, reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
- } else {
+ } else
+ if (dev_priv->chipset < NV_C0) {
ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
if (ret <= 0)
return 0;
@@ -298,6 +304,17 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
nv_wr32(dev, reg, 0x50000610);
nv_wr32(dev, reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
nv_wr32(dev, reg + 8, N2);
+ } else {
+ ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
+ if (ret <= 0)
+ return 0;
+
+ NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
+ pclk, ret, N1, N2, M1, P);
+
+ nv_mask(dev, reg + 0x0c, 0x00000000, 0x00000100);
+ nv_wr32(dev, reg + 0x04, (P << 16) | (N1 << 8) | M1);
+ nv_wr32(dev, reg + 0x10, N2 << 16);
}
return 0;
@@ -348,7 +365,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
- return -EINVAL;
+ return -ENOENT;
cursor = nouveau_gem_object(gem);
ret = nouveau_bo_map(cursor);
@@ -381,15 +398,12 @@ nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
static void
nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
- uint32_t size)
+ uint32_t start, uint32_t size)
{
+ int end = (start + size > 256) ? 256 : start + size, i;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
- int i;
-
- if (size != 256)
- return;
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
nv_crtc->lut.r[i] = r[i];
nv_crtc->lut.g[i] = g[i];
nv_crtc->lut.b[i] = b[i];
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index f13ad0d..612fa6d 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -76,7 +76,10 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
nv_wo32(dev, obj, 2, offset);
nv_wo32(dev, obj, 3, 0x00000000);
nv_wo32(dev, obj, 4, 0x00000000);
- nv_wo32(dev, obj, 5, 0x00010000);
+ if (dev_priv->card_type < NV_C0)
+ nv_wo32(dev, obj, 5, 0x00010000);
+ else
+ nv_wo32(dev, obj, 5, 0x00020000);
dev_priv->engine.instmem.flush(dev);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
new file mode 100644
index 0000000..26a9960
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+int
+nvc0_fb_init(struct drm_device *dev)
+{
+ return 0;
+}
+
+void
+nvc0_fb_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
new file mode 100644
index 0000000..d643758
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+void
+nvc0_fifo_disable(struct drm_device *dev)
+{
+}
+
+void
+nvc0_fifo_enable(struct drm_device *dev)
+{
+}
+
+bool
+nvc0_fifo_reassign(struct drm_device *dev, bool enable)
+{
+ return false;
+}
+
+bool
+nvc0_fifo_cache_flush(struct drm_device *dev)
+{
+ return true;
+}
+
+bool
+nvc0_fifo_cache_pull(struct drm_device *dev, bool enable)
+{
+ return false;
+}
+
+int
+nvc0_fifo_channel_id(struct drm_device *dev)
+{
+ return 127;
+}
+
+int
+nvc0_fifo_create_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+void
+nvc0_fifo_destroy_context(struct nouveau_channel *chan)
+{
+}
+
+int
+nvc0_fifo_load_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+int
+nvc0_fifo_unload_context(struct drm_device *dev)
+{
+ return 0;
+}
+
+void
+nvc0_fifo_takedown(struct drm_device *dev)
+{
+}
+
+int
+nvc0_fifo_init(struct drm_device *dev)
+{
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
new file mode 100644
index 0000000..717a517
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+void
+nvc0_graph_fifo_access(struct drm_device *dev, bool enabled)
+{
+}
+
+struct nouveau_channel *
+nvc0_graph_channel(struct drm_device *dev)
+{
+ return NULL;
+}
+
+int
+nvc0_graph_create_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+void
+nvc0_graph_destroy_context(struct nouveau_channel *chan)
+{
+}
+
+int
+nvc0_graph_load_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+int
+nvc0_graph_unload_context(struct drm_device *dev)
+{
+ return 0;
+}
+
+void
+nvc0_graph_takedown(struct drm_device *dev)
+{
+}
+
+int
+nvc0_graph_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ dev_priv->engine.graph.accel_blocked = true;
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c
new file mode 100644
index 0000000..3ab3cdc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+int
+nvc0_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
+ uint32_t *size)
+{
+ int ret;
+
+ *size = ALIGN(*size, 4096);
+ if (*size == 0)
+ return -EINVAL;
+
+ ret = nouveau_bo_new(dev, NULL, *size, 0, TTM_PL_FLAG_VRAM, 0, 0x0000,
+ true, false, &gpuobj->im_backing);
+ if (ret) {
+ NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
+ return ret;
+ }
+
+ ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM);
+ if (ret) {
+ NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret);
+ nouveau_bo_ref(NULL, &gpuobj->im_backing);
+ return ret;
+ }
+
+ gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
+ gpuobj->im_backing_start <<= PAGE_SHIFT;
+ return 0;
+}
+
+void
+nvc0_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (gpuobj && gpuobj->im_backing) {
+ if (gpuobj->im_bound)
+ dev_priv->engine.instmem.unbind(dev, gpuobj);
+ nouveau_bo_unpin(gpuobj->im_backing);
+ nouveau_bo_ref(NULL, &gpuobj->im_backing);
+ gpuobj->im_backing = NULL;
+ }
+}
+
+int
+nvc0_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t pte, pte_end;
+ uint64_t vram;
+
+ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
+ return -EINVAL;
+
+ NV_DEBUG(dev, "st=0x%lx sz=0x%lx\n",
+ gpuobj->im_pramin->start, gpuobj->im_pramin->size);
+
+ pte = gpuobj->im_pramin->start >> 12;
+ pte_end = (gpuobj->im_pramin->size >> 12) + pte;
+ vram = gpuobj->im_backing_start;
+
+ NV_DEBUG(dev, "pramin=0x%lx, 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);
+
+ while (pte < pte_end) {
+ nv_wr32(dev, 0x702000 + (pte * 8), (vram >> 8) | 1);
+ nv_wr32(dev, 0x702004 + (pte * 8), 0);
+ vram += 4096;
+ pte++;
+ }
+ dev_priv->engine.instmem.flush(dev);
+
+ if (1) {
+ u32 chan = nv_rd32(dev, 0x1700) << 16;
+ nv_wr32(dev, 0x100cb8, (chan + 0x1000) >> 8);
+ nv_wr32(dev, 0x100cbc, 0x80000005);
+ }
+
+ gpuobj->im_bound = 1;
+ return 0;
+}
+
+int
+nvc0_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t pte, pte_end;
+
+ if (gpuobj->im_bound == 0)
+ return -EINVAL;
+
+ pte = gpuobj->im_pramin->start >> 12;
+ pte_end = (gpuobj->im_pramin->size >> 12) + pte;
+ while (pte < pte_end) {
+ nv_wr32(dev, 0x702000 + (pte * 8), 0);
+ nv_wr32(dev, 0x702004 + (pte * 8), 0);
+ pte++;
+ }
+ dev_priv->engine.instmem.flush(dev);
+
+ gpuobj->im_bound = 0;
+ return 0;
+}
+
+void
+nvc0_instmem_flush(struct drm_device *dev)
+{
+ nv_wr32(dev, 0x070000, 1);
+ if (!nv_wait(0x070000, 0x00000002, 0x00000000))
+ NV_ERROR(dev, "PRAMIN flush timeout\n");
+}
+
+int
+nvc0_instmem_suspend(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i;
+
+ dev_priv->susres.ramin_copy = vmalloc(65536);
+ if (!dev_priv->susres.ramin_copy)
+ return -ENOMEM;
+
+ for (i = 0x700000; i < 0x710000; i += 4)
+ dev_priv->susres.ramin_copy[i/4] = nv_rd32(dev, i);
+ return 0;
+}
+
+void
+nvc0_instmem_resume(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u64 chan;
+ int i;
+
+ chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram;
+ nv_wr32(dev, 0x001700, chan >> 16);
+
+ for (i = 0x700000; i < 0x710000; i += 4)
+ nv_wr32(dev, i, dev_priv->susres.ramin_copy[i/4]);
+ vfree(dev_priv->susres.ramin_copy);
+ dev_priv->susres.ramin_copy = NULL;
+
+ nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12));
+}
+
+int
+nvc0_instmem_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u64 chan, pgt3, imem, lim3 = dev_priv->ramin_size - 1;
+ int ret, i;
+
+ dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024;
+ chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram;
+ imem = 4096 + 4096 + 32768;
+
+ nv_wr32(dev, 0x001700, chan >> 16);
+
+ /* channel setup */
+ nv_wr32(dev, 0x700200, lower_32_bits(chan + 0x1000));
+ nv_wr32(dev, 0x700204, upper_32_bits(chan + 0x1000));
+ nv_wr32(dev, 0x700208, lower_32_bits(lim3));
+ nv_wr32(dev, 0x70020c, upper_32_bits(lim3));
+
+ /* point pgd -> pgt */
+ nv_wr32(dev, 0x701000, 0);
+ nv_wr32(dev, 0x701004, ((chan + 0x2000) >> 8) | 1);
+
+ /* point pgt -> physical vram for channel */
+ pgt3 = 0x2000;
+ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4096, pgt3 += 8) {
+ nv_wr32(dev, 0x700000 + pgt3, ((chan + i) >> 8) | 1);
+ nv_wr32(dev, 0x700004 + pgt3, 0);
+ }
+
+ /* clear rest of pgt */
+ for (; i < dev_priv->ramin_size; i += 4096, pgt3 += 8) {
+ nv_wr32(dev, 0x700000 + pgt3, 0);
+ nv_wr32(dev, 0x700004 + pgt3, 0);
+ }
+
+ /* point bar3 at the channel */
+ nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12));
+
+ /* Global PRAMIN heap */
+ ret = drm_mm_init(&dev_priv->ramin_heap, imem,
+ dev_priv->ramin_size - imem);
+ if (ret) {
+ NV_ERROR(dev, "Failed to init RAMIN heap\n");
+ return -ENOMEM;
+ }
+
+ /*XXX: incorrect, but needed to make hash func "work" */
+ dev_priv->ramht_offset = 0x10000;
+ dev_priv->ramht_bits = 9;
+ dev_priv->ramht_size = (1 << dev_priv->ramht_bits);
+ return 0;
+}
+
+void
+nvc0_instmem_takedown(struct drm_device *dev)
+{
+}
+
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index 68e6f43..4f4cd8b 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -200,7 +200,7 @@ int r600_page_table_init(struct drm_device *dev)
entry->pagelist[i], 0,
PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL);
- if (entry->busaddr[i] == 0) {
+ if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
DRM_ERROR("unable to map PCIGART pages!\n");
r600_page_table_cleanup(dev, gart_info);
goto done;
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index c3ea212..d886494 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -133,6 +133,7 @@ static inline int r600_bpe_from_format(u32 *bpe, u32 format)
case V_038004_FMT_GB_GR:
case V_038004_FMT_BG_RG:
case V_038004_COLOR_INVALID:
+ default:
*bpe = 16;
return -EINVAL;
}
@@ -174,7 +175,7 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
return -EINVAL;
}
- size = radeon_bo_size(track->cb_color_bo[i]);
+ size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i];
if (r600_bpe_from_format(&bpe, G_0280A0_FORMAT(track->cb_color_info[i]))) {
dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n",
__func__, __LINE__, G_0280A0_FORMAT(track->cb_color_info[i]),
@@ -327,7 +328,6 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
dev_warn(p->dev, "z/stencil buffer size not set\n");
return -EINVAL;
}
- printk_once(KERN_WARNING "You have old & broken userspace please consider updating mesa\n");
tmp = radeon_bo_size(track->db_bo) - track->db_offset;
tmp = (tmp / bpe) >> 6;
if (!tmp) {
@@ -882,8 +882,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return -EINVAL;
}
ib[idx] = track->cb_color_base_last[tmp];
- printk_once(KERN_WARNING "You have old & broken userspace "
- "please consider updating mesa & xf86-video-ati\n");
track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -910,8 +908,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return -EINVAL;
}
ib[idx] = track->cb_color_base_last[tmp];
- printk_once(KERN_WARNING "You have old & broken userspace "
- "please consider updating mesa & xf86-video-ati\n");
track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -938,7 +934,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return -EINVAL;
}
tmp = (reg - CB_COLOR0_BASE) / 4;
- track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
@@ -950,7 +946,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
"0x%04X\n", reg);
return -EINVAL;
}
- track->db_offset = radeon_get_ib_value(p, idx);
+ track->db_offset = radeon_get_ib_value(p, idx) << 8;
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_bo = reloc->robj;
break;
@@ -1055,10 +1051,10 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels
}
*l0_size = ALIGN((w0 * bpe), pitch_align) * h0 * d0;
*mipmap_size = offset;
- if (!blevel)
- *mipmap_size -= *l0_size;
if (!nlevels)
*mipmap_size = *l0_size;
+ if (!blevel)
+ *mipmap_size -= *l0_size;
}
/**
@@ -1165,14 +1161,14 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i
(pitch_align * bpe),
&l0_size, &mipmap_size);
/* using get ib will give us the offset into the texture bo */
- word0 = radeon_get_ib_value(p, idx + 2);
+ word0 = radeon_get_ib_value(p, idx + 2) << 8;
if ((l0_size + word0) > radeon_bo_size(texture)) {
dev_warn(p->dev, "texture bo too small (%d %d %d %d -> %d have %ld)\n",
w0, h0, bpe, word0, l0_size, radeon_bo_size(texture));
return -EINVAL;
}
/* using get ib will give us the offset into the mipmap bo */
- word0 = radeon_get_ib_value(p, idx + 3);
+ word0 = radeon_get_ib_value(p, idx + 3) << 8;
if ((mipmap_size + word0) > radeon_bo_size(mipmap)) {
dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n",
w0, h0, bpe, blevel, nlevels, word0, mipmap_size, radeon_bo_size(texture));
@@ -1366,7 +1362,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
}
for (i = 0; i < (pkt->count / 7); i++) {
struct radeon_bo *texture, *mipmap;
- u32 size, offset;
+ u32 size, offset, base_offset, mip_offset;
switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) {
case SQ_TEX_VTX_VALID_TEXTURE:
@@ -1376,7 +1372,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE\n");
return -EINVAL;
}
- ib[idx+1+(i*7)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
@@ -1388,12 +1384,14 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE\n");
return -EINVAL;
}
- ib[idx+1+(i*7)+3] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ mip_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
mipmap = reloc->robj;
r = r600_check_texture_resource(p, idx+(i*7)+1,
texture, mipmap, reloc->lobj.tiling_flags);
if (r)
return r;
+ ib[idx+1+(i*7)+2] += base_offset;
+ ib[idx+1+(i*7)+3] += mip_offset;
break;
case SQ_TEX_VTX_VALID_BUFFER:
/* vtx base */
@@ -1403,10 +1401,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
offset = radeon_get_ib_value(p, idx+1+(i*7)+0);
- size = radeon_get_ib_value(p, idx+1+(i*7)+1);
+ size = radeon_get_ib_value(p, idx+1+(i*7)+1) + 1;
if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
/* force size to size of the buffer */
- dev_warn(p->dev, "vbo resource seems too big for the bo\n");
+ dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n",
+ size + offset, radeon_bo_size(reloc->robj));
ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj);
}
ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 3cd1c47..3dfcfa3 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -1100,6 +1100,8 @@ struct radeon_device {
struct notifier_block acpi_nb;
/* only one userspace can use Hyperz features at a time */
struct drm_file *hyperz_filp;
+ /* i2c buses */
+ struct radeon_i2c_chan *i2c_bus[RADEON_MAX_I2C_BUS];
};
int radeon_device_init(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 3bc2bcd..6d30868 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -48,7 +48,8 @@ radeon_add_atom_connector(struct drm_device *dev,
struct radeon_i2c_bus_rec *i2c_bus,
bool linkb, uint32_t igp_lane_info,
uint16_t connector_object_id,
- struct radeon_hpd *hpd);
+ struct radeon_hpd *hpd,
+ struct radeon_router *router);
/* from radeon_legacy_encoder.c */
extern void
@@ -114,7 +115,8 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev
i2c.i2c_id = gpio->sucI2cId.ucAccess;
- i2c.valid = true;
+ if (i2c.mask_clk_reg)
+ i2c.valid = true;
break;
}
}
@@ -123,6 +125,66 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev
return i2c;
}
+void radeon_atombios_i2c_init(struct radeon_device *rdev)
+{
+ struct atom_context *ctx = rdev->mode_info.atom_context;
+ ATOM_GPIO_I2C_ASSIGMENT *gpio;
+ struct radeon_i2c_bus_rec i2c;
+ int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
+ struct _ATOM_GPIO_I2C_INFO *i2c_info;
+ uint16_t data_offset, size;
+ int i, num_indices;
+ char stmp[32];
+
+ memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
+
+ if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {
+ i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
+
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
+ sizeof(ATOM_GPIO_I2C_ASSIGMENT);
+
+ for (i = 0; i < num_indices; i++) {
+ gpio = &i2c_info->asGPIO_Info[i];
+ i2c.valid = false;
+ i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
+ i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
+ i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
+ i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
+ i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
+ i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
+ i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
+ i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
+ i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
+ i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
+ i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
+ i2c.en_data_mask = (1 << gpio->ucDataEnShift);
+ i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
+ i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
+ i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
+ i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
+
+ if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
+ i2c.hw_capable = true;
+ else
+ i2c.hw_capable = false;
+
+ if (gpio->sucI2cId.ucAccess == 0xa0)
+ i2c.mm_i2c = true;
+ else
+ i2c.mm_i2c = false;
+
+ i2c.i2c_id = gpio->sucI2cId.ucAccess;
+
+ if (i2c.mask_clk_reg) {
+ i2c.valid = true;
+ sprintf(stmp, "0x%x", i2c.i2c_id);
+ rdev->i2c_bus[i] = radeon_i2c_create(rdev->ddev, &i2c, stmp);
+ }
+ }
+ }
+}
+
static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
u8 id)
{
@@ -206,6 +268,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
uint16_t *line_mux,
struct radeon_hpd *hpd)
{
+ struct radeon_device *rdev = dev->dev_private;
/* Asus M2A-VM HDMI board lists the DVI port as HDMI */
if ((dev->pdev->device == 0x791e) &&
@@ -308,13 +371,22 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
}
}
- /* Acer laptop reports DVI-D as DVI-I */
+ /* Acer laptop reports DVI-D as DVI-I and hpd pins reversed */
if ((dev->pdev->device == 0x95c4) &&
(dev->pdev->subsystem_vendor == 0x1025) &&
(dev->pdev->subsystem_device == 0x013c)) {
+ struct radeon_gpio_rec gpio;
+
if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
- (supported_device == ATOM_DEVICE_DFP1_SUPPORT))
+ (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) {
+ gpio = radeon_lookup_gpio(rdev, 6);
+ *hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
*connector_type = DRM_MODE_CONNECTOR_DVID;
+ } else if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) &&
+ (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) {
+ gpio = radeon_lookup_gpio(rdev, 7);
+ *hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
+ }
}
/* XFX Pine Group device rv730 reports no VGA DDC lines
@@ -399,13 +471,15 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
u16 size, data_offset;
u8 frev, crev;
ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
+ ATOM_OBJECT_TABLE *router_obj;
ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
ATOM_OBJECT_HEADER *obj_header;
- int i, j, path_size, device_support;
+ int i, j, k, path_size, device_support;
int connector_type;
u16 igp_lane_info, conn_id, connector_object_id;
bool linkb;
struct radeon_i2c_bus_rec ddc_bus;
+ struct radeon_router router;
struct radeon_gpio_rec gpio;
struct radeon_hpd hpd;
@@ -415,6 +489,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
if (crev < 2)
return false;
+ router.valid = false;
+
obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);
path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)
(ctx->bios + data_offset +
@@ -422,6 +498,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *)
(ctx->bios + data_offset +
le16_to_cpu(obj_header->usConnectorObjectTableOffset));
+ router_obj = (ATOM_OBJECT_TABLE *)
+ (ctx->bios + data_offset +
+ le16_to_cpu(obj_header->usRouterObjectTableOffset));
device_support = le16_to_cpu(obj_header->usDeviceSupport);
path_size = 0;
@@ -508,33 +587,86 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
continue;
- for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2);
- j++) {
- uint8_t enc_obj_id, enc_obj_num, enc_obj_type;
+ for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) {
+ uint8_t grph_obj_id, grph_obj_num, grph_obj_type;
- enc_obj_id =
+ grph_obj_id =
(le16_to_cpu(path->usGraphicObjIds[j]) &
OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
- enc_obj_num =
+ grph_obj_num =
(le16_to_cpu(path->usGraphicObjIds[j]) &
ENUM_ID_MASK) >> ENUM_ID_SHIFT;
- enc_obj_type =
+ grph_obj_type =
(le16_to_cpu(path->usGraphicObjIds[j]) &
OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
- /* FIXME: add support for router objects */
- if (enc_obj_type == GRAPH_OBJECT_TYPE_ENCODER) {
- if (enc_obj_num == 2)
+ if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) {
+ if (grph_obj_num == 2)
linkb = true;
else
linkb = false;
radeon_add_atom_encoder(dev,
- enc_obj_id,
+ grph_obj_id,
le16_to_cpu
(path->
usDeviceTag));
+ } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) {
+ router.valid = false;
+ for (k = 0; k < router_obj->ucNumberOfObjects; k++) {
+ u16 router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID);
+ if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) {
+ ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *)
+ (ctx->bios + data_offset +
+ le16_to_cpu(router_obj->asObjects[k].usRecordOffset));
+ ATOM_I2C_RECORD *i2c_record;
+ ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
+ ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path;
+ ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table =
+ (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *)
+ (ctx->bios + data_offset +
+ le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset));
+ int enum_id;
+
+ router.router_id = router_obj_id;
+ for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst;
+ enum_id++) {
+ if (le16_to_cpu(path->usConnObjectId) ==
+ le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id]))
+ break;
+ }
+
+ while (record->ucRecordType > 0 &&
+ record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {
+ switch (record->ucRecordType) {
+ case ATOM_I2C_RECORD_TYPE:
+ i2c_record =
+ (ATOM_I2C_RECORD *)
+ record;
+ i2c_config =
+ (ATOM_I2C_ID_CONFIG_ACCESS *)
+ &i2c_record->sucI2cId;
+ router.i2c_info =
+ radeon_lookup_i2c_gpio(rdev,
+ i2c_config->
+ ucAccess);
+ router.i2c_addr = i2c_record->ucI2CAddr >> 1;
+ break;
+ case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE:
+ ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *)
+ record;
+ router.valid = true;
+ router.mux_type = ddc_path->ucMuxType;
+ router.mux_control_pin = ddc_path->ucMuxControlPin;
+ router.mux_state = ddc_path->ucMuxState[enum_id];
+ break;
+ }
+ record = (ATOM_COMMON_RECORD_HEADER *)
+ ((char *)record + record->ucRecordSize);
+ }
+ }
+ }
}
}
@@ -614,7 +746,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
connector_type, &ddc_bus,
linkb, igp_lane_info,
connector_object_id,
- &hpd);
+ &hpd,
+ &router);
}
}
@@ -691,6 +824,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
int i, j, max_device;
struct bios_connector *bios_connectors;
size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE;
+ struct radeon_router router;
+
+ router.valid = false;
bios_connectors = kzalloc(bc_size, GFP_KERNEL);
if (!bios_connectors)
@@ -862,7 +998,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
&bios_connectors[i].ddc_bus,
false, 0,
connector_object_id,
- &bios_connectors[i].hpd);
+ &bios_connectors[i].hpd,
+ &router);
}
}
@@ -1521,7 +1658,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
thermal_controller_names[power_info->info.ucOverdriveThermalController],
power_info->info.ucOverdriveControllerAddress >> 1);
i2c_bus = radeon_lookup_i2c_gpio(rdev, power_info->info.ucOverdriveI2cLine);
- rdev->pm.i2c_bus = radeon_i2c_create(rdev->ddev, &i2c_bus, "Thermal");
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
if (rdev->pm.i2c_bus) {
struct i2c_board_info info = { };
const char *name = thermal_controller_names[power_info->info.
@@ -1814,7 +1951,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
(controller->ucFanParameters &
ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
i2c_bus = radeon_lookup_i2c_gpio(rdev, controller->ucI2cLine);
- rdev->pm.i2c_bus = radeon_i2c_create(rdev->ddev, &i2c_bus, "Thermal");
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
if (rdev->pm.i2c_bus) {
struct i2c_board_info info = { };
const char *name = pp_lib_thermal_controller_names[controller->ucType];
@@ -1927,6 +2064,11 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
rdev->pm.power_state[state_index].type =
POWER_STATE_TYPE_PERFORMANCE;
break;
+ case ATOM_PPLIB_CLASSIFICATION_UI_NONE:
+ if (misc2 & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
+ rdev->pm.power_state[state_index].type =
+ POWER_STATE_TYPE_PERFORMANCE;
+ break;
}
rdev->pm.power_state[state_index].flags = 0;
if (misc & ATOM_PPLIB_SINGLE_DISPLAY_ONLY)
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
index f64936c..14448a7 100644
--- a/drivers/gpu/drm/radeon/radeon_clocks.c
+++ b/drivers/gpu/drm/radeon/radeon_clocks.c
@@ -91,6 +91,85 @@ uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
return mclk;
}
+#ifdef CONFIG_OF
+/*
+ * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
+ * tree. Hopefully, ATI OF driver is kind enough to fill these
+ */
+static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+{
+ struct radeon_device *rdev = dev->dev_private;
+ struct device_node *dp = rdev->pdev->dev.of_node;
+ const u32 *val;
+ struct radeon_pll *p1pll = &rdev->clock.p1pll;
+ struct radeon_pll *p2pll = &rdev->clock.p2pll;
+ struct radeon_pll *spll = &rdev->clock.spll;
+ struct radeon_pll *mpll = &rdev->clock.mpll;
+
+ if (dp == NULL)
+ return false;
+ val = of_get_property(dp, "ATY,RefCLK", NULL);
+ if (!val || !*val) {
+ printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n");
+ return false;
+ }
+ p1pll->reference_freq = p2pll->reference_freq = (*val) / 10;
+ p1pll->reference_div = RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff;
+ if (p1pll->reference_div < 2)
+ p1pll->reference_div = 12;
+ p2pll->reference_div = p1pll->reference_div;
+
+ /* These aren't in the device-tree */
+ if (rdev->family >= CHIP_R420) {
+ p1pll->pll_in_min = 100;
+ p1pll->pll_in_max = 1350;
+ p1pll->pll_out_min = 20000;
+ p1pll->pll_out_max = 50000;
+ p2pll->pll_in_min = 100;
+ p2pll->pll_in_max = 1350;
+ p2pll->pll_out_min = 20000;
+ p2pll->pll_out_max = 50000;
+ } else {
+ p1pll->pll_in_min = 40;
+ p1pll->pll_in_max = 500;
+ p1pll->pll_out_min = 12500;
+ p1pll->pll_out_max = 35000;
+ p2pll->pll_in_min = 40;
+ p2pll->pll_in_max = 500;
+ p2pll->pll_out_min = 12500;
+ p2pll->pll_out_max = 35000;
+ }
+
+ spll->reference_freq = mpll->reference_freq = p1pll->reference_freq;
+ spll->reference_div = mpll->reference_div =
+ RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) &
+ RADEON_M_SPLL_REF_DIV_MASK;
+
+ val = of_get_property(dp, "ATY,SCLK", NULL);
+ if (val && *val)
+ rdev->clock.default_sclk = (*val) / 10;
+ else
+ rdev->clock.default_sclk =
+ radeon_legacy_get_engine_clock(rdev);
+
+ val = of_get_property(dp, "ATY,MCLK", NULL);
+ if (val && *val)
+ rdev->clock.default_mclk = (*val) / 10;
+ else
+ rdev->clock.default_mclk =
+ radeon_legacy_get_memory_clock(rdev);
+
+ DRM_INFO("Using device-tree clock info\n");
+
+ return true;
+}
+#else
+static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+{
+ return false;
+}
+#endif /* CONFIG_OF */
+
void radeon_get_clock_info(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
@@ -105,6 +184,8 @@ void radeon_get_clock_info(struct drm_device *dev)
ret = radeon_atom_get_clock_info(dev);
else
ret = radeon_combios_get_clock_info(dev);
+ if (!ret)
+ ret = radeon_read_clocks_OF(dev);
if (ret) {
if (p1pll->reference_div < 2) {
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 5e1474c..885dcfa 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -480,9 +480,66 @@ radeon_combios_get_hardcoded_edid(struct radeon_device *rdev)
}
static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
- int ddc_line)
+ enum radeon_combios_ddc ddc,
+ u32 clk_mask,
+ u32 data_mask)
{
struct radeon_i2c_bus_rec i2c;
+ int ddc_line = 0;
+
+ /* ddc id = mask reg
+ * DDC_NONE_DETECTED = none
+ * DDC_DVI = RADEON_GPIO_DVI_DDC
+ * DDC_VGA = RADEON_GPIO_VGA_DDC
+ * DDC_LCD = RADEON_GPIOPAD_MASK
+ * DDC_GPIO = RADEON_MDGPIO_MASK
+ * r1xx/r2xx
+ * DDC_MONID = RADEON_GPIO_MONID
+ * DDC_CRT2 = RADEON_GPIO_CRT2_DDC
+ * r3xx
+ * DDC_MONID = RADEON_GPIO_MONID
+ * DDC_CRT2 = RADEON_GPIO_DVI_DDC
+ * rs3xx/rs4xx
+ * DDC_MONID = RADEON_GPIOPAD_MASK
+ * DDC_CRT2 = RADEON_GPIO_MONID
+ */
+ switch (ddc) {
+ case DDC_NONE_DETECTED:
+ default:
+ ddc_line = 0;
+ break;
+ case DDC_DVI:
+ ddc_line = RADEON_GPIO_DVI_DDC;
+ break;
+ case DDC_VGA:
+ ddc_line = RADEON_GPIO_VGA_DDC;
+ break;
+ case DDC_LCD:
+ ddc_line = RADEON_GPIOPAD_MASK;
+ break;
+ case DDC_GPIO:
+ ddc_line = RADEON_MDGPIO_MASK;
+ break;
+ case DDC_MONID:
+ if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480)
+ ddc_line = RADEON_GPIOPAD_MASK;
+ else
+ ddc_line = RADEON_GPIO_MONID;
+ break;
+ case DDC_CRT2:
+ if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480)
+ ddc_line = RADEON_GPIO_MONID;
+ else if (rdev->family >= CHIP_R300) {
+ ddc_line = RADEON_GPIO_DVI_DDC;
+ ddc = DDC_DVI;
+ } else
+ ddc_line = RADEON_GPIO_CRT2_DDC;
+ break;
+ }
if (ddc_line == RADEON_GPIOPAD_MASK) {
i2c.mask_clk_reg = RADEON_GPIOPAD_MASK;
@@ -503,15 +560,6 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
i2c.y_clk_reg = RADEON_MDGPIO_Y;
i2c.y_data_reg = RADEON_MDGPIO_Y;
} else {
- i2c.mask_clk_mask = RADEON_GPIO_EN_1;
- i2c.mask_data_mask = RADEON_GPIO_EN_0;
- i2c.a_clk_mask = RADEON_GPIO_A_1;
- i2c.a_data_mask = RADEON_GPIO_A_0;
- i2c.en_clk_mask = RADEON_GPIO_EN_1;
- i2c.en_data_mask = RADEON_GPIO_EN_0;
- i2c.y_clk_mask = RADEON_GPIO_Y_1;
- i2c.y_data_mask = RADEON_GPIO_Y_0;
-
i2c.mask_clk_reg = ddc_line;
i2c.mask_data_reg = ddc_line;
i2c.a_clk_reg = ddc_line;
@@ -522,6 +570,26 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
i2c.y_data_reg = ddc_line;
}
+ if (clk_mask && data_mask) {
+ i2c.mask_clk_mask = clk_mask;
+ i2c.mask_data_mask = data_mask;
+ i2c.a_clk_mask = clk_mask;
+ i2c.a_data_mask = data_mask;
+ i2c.en_clk_mask = clk_mask;
+ i2c.en_data_mask = data_mask;
+ i2c.y_clk_mask = clk_mask;
+ i2c.y_data_mask = data_mask;
+ } else {
+ i2c.mask_clk_mask = RADEON_GPIO_EN_1;
+ i2c.mask_data_mask = RADEON_GPIO_EN_0;
+ i2c.a_clk_mask = RADEON_GPIO_A_1;
+ i2c.a_data_mask = RADEON_GPIO_A_0;
+ i2c.en_clk_mask = RADEON_GPIO_EN_1;
+ i2c.en_data_mask = RADEON_GPIO_EN_0;
+ i2c.y_clk_mask = RADEON_GPIO_Y_1;
+ i2c.y_data_mask = RADEON_GPIO_Y_0;
+ }
+
switch (rdev->family) {
case CHIP_R100:
case CHIP_RV100:
@@ -599,7 +667,8 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
break;
}
i2c.mm_i2c = false;
- i2c.i2c_id = 0;
+
+ i2c.i2c_id = ddc;
i2c.hpd = RADEON_HPD_NONE;
if (ddc_line)
@@ -610,6 +679,62 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
return i2c;
}
+void radeon_combios_i2c_init(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct radeon_i2c_bus_rec i2c;
+
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
+ rdev->i2c_bus[0] = radeon_i2c_create(dev, &i2c, "DVI_DDC");
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
+ rdev->i2c_bus[1] = radeon_i2c_create(dev, &i2c, "VGA_DDC");
+
+ i2c.valid = true;
+ i2c.hw_capable = true;
+ i2c.mm_i2c = true;
+ i2c.i2c_id = 0xa0;
+ rdev->i2c_bus[2] = radeon_i2c_create(dev, &i2c, "MM_I2C");
+
+ if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480) {
+ u16 offset;
+ u8 id, blocks, clk, data;
+ int i;
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
+ rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
+
+ offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
+ if (offset) {
+ blocks = RBIOS8(offset + 2);
+ for (i = 0; i < blocks; i++) {
+ id = RBIOS8(offset + 3 + (i * 5) + 0);
+ if (id == 136) {
+ clk = RBIOS8(offset + 3 + (i * 5) + 3);
+ data = RBIOS8(offset + 3 + (i * 5) + 4);
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
+ clk, data);
+ rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
+ break;
+ }
+ }
+ }
+
+ } else if (rdev->family >= CHIP_R300) {
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
+ } else {
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
+ rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "CRT2_DDC");
+ }
+}
+
bool radeon_combios_get_clock_info(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
@@ -1247,8 +1372,8 @@ bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
struct radeon_i2c_bus_rec i2c_bus;
/* default for macs */
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+ i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
/* XXX some macs have duallink chips */
switch (rdev->mode_info.connector_table) {
@@ -1269,47 +1394,16 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder
struct drm_device *dev = encoder->base.dev;
struct radeon_device *rdev = dev->dev_private;
uint16_t offset;
- uint8_t ver, id, blocks, clk, data;
- int i;
+ uint8_t ver;
enum radeon_combios_ddc gpio;
struct radeon_i2c_bus_rec i2c_bus;
tmds->i2c_bus = NULL;
if (rdev->flags & RADEON_IS_IGP) {
- offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
- if (offset) {
- ver = RBIOS8(offset);
- DRM_INFO("GPIO Table revision: %d\n", ver);
- blocks = RBIOS8(offset + 2);
- for (i = 0; i < blocks; i++) {
- id = RBIOS8(offset + 3 + (i * 5) + 0);
- if (id == 136) {
- clk = RBIOS8(offset + 3 + (i * 5) + 3);
- data = RBIOS8(offset + 3 + (i * 5) + 4);
- i2c_bus.valid = true;
- i2c_bus.mask_clk_mask = (1 << clk);
- i2c_bus.mask_data_mask = (1 << data);
- i2c_bus.a_clk_mask = (1 << clk);
- i2c_bus.a_data_mask = (1 << data);
- i2c_bus.en_clk_mask = (1 << clk);
- i2c_bus.en_data_mask = (1 << data);
- i2c_bus.y_clk_mask = (1 << clk);
- i2c_bus.y_data_mask = (1 << data);
- i2c_bus.mask_clk_reg = RADEON_GPIOPAD_MASK;
- i2c_bus.mask_data_reg = RADEON_GPIOPAD_MASK;
- i2c_bus.a_clk_reg = RADEON_GPIOPAD_A;
- i2c_bus.a_data_reg = RADEON_GPIOPAD_A;
- i2c_bus.en_clk_reg = RADEON_GPIOPAD_EN;
- i2c_bus.en_data_reg = RADEON_GPIOPAD_EN;
- i2c_bus.y_clk_reg = RADEON_GPIOPAD_Y;
- i2c_bus.y_data_reg = RADEON_GPIOPAD_Y;
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- tmds->dvo_chip = DVO_SIL164;
- tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
- break;
- }
- }
- }
+ i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
+ tmds->dvo_chip = DVO_SIL164;
+ tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
} else {
offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
if (offset) {
@@ -1318,37 +1412,15 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder
tmds->slave_addr = RBIOS8(offset + 4 + 2);
tmds->slave_addr >>= 1; /* 7 bit addressing */
gpio = RBIOS8(offset + 4 + 3);
- switch (gpio) {
- case DDC_MONID:
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_DVI:
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_VGA:
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_CRT2:
- /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
- if (rdev->family >= CHIP_R300)
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- else
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_LCD: /* MM i2c */
+ if (gpio == DDC_LCD) {
+ /* MM i2c */
i2c_bus.valid = true;
i2c_bus.hw_capable = true;
i2c_bus.mm_i2c = true;
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- default:
- DRM_ERROR("Unsupported gpio %d\n", gpio);
- break;
- }
+ i2c_bus.i2c_id = 0xa0;
+ } else
+ i2c_bus = combios_setup_i2c_bus(rdev, gpio, 0, 0);
+ tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
}
}
@@ -1430,7 +1502,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
/* these are the most common settings */
if (rdev->flags & RADEON_SINGLE_CRTC) {
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1445,7 +1517,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
} else if (rdev->flags & RADEON_IS_MOBILITY) {
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, 0);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_NONE_DETECTED, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1460,7 +1532,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1475,7 +1547,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
} else {
/* DVI-I - tv dac, int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_1;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1496,7 +1568,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1532,7 +1604,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (ibook)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1544,7 +1616,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* VGA - TV DAC */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1573,7 +1645,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (powerbook external tmds)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1585,7 +1657,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* DVI-I - primary dac, ext tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_2; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1622,7 +1694,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (powerbook internal tmds)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1634,7 +1706,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* DVI-I - primary dac, int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_1; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1670,7 +1742,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (powerbook vga)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1682,7 +1754,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1711,7 +1783,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (mini external tmds)\n",
rdev->mode_info.connector_table);
/* DVI-I - tv dac, ext tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_2; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1748,7 +1820,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (mini internal tmds)\n",
rdev->mode_info.connector_table);
/* DVI-I - tv dac, int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_1; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1784,7 +1856,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (imac g5 isight)\n",
rdev->mode_info.connector_table);
/* DVI-D - int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
hpd.hpd = RADEON_HPD_1; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1796,7 +1868,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
&hpd);
/* VGA - tv dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1825,7 +1897,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (emac)\n",
rdev->mode_info.connector_table);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1837,7 +1909,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_VGA,
&hpd);
/* VGA - tv dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1866,7 +1938,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (rn50-power)\n",
rdev->mode_info.connector_table);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1877,7 +1949,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
CONNECTOR_OBJECT_ID_VGA,
&hpd);
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1907,31 +1979,6 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
struct radeon_i2c_bus_rec *ddc_i2c,
struct radeon_hpd *hpd)
{
- struct radeon_device *rdev = dev->dev_private;
-
- /* XPRESS DDC quirks */
- if ((rdev->family == CHIP_RS400 ||
- rdev->family == CHIP_RS480) &&
- ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
- *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- else if ((rdev->family == CHIP_RS400 ||
- rdev->family == CHIP_RS480) &&
- ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) {
- *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIOPAD_MASK);
- ddc_i2c->mask_clk_mask = (0x20 << 8);
- ddc_i2c->mask_data_mask = 0x80;
- ddc_i2c->a_clk_mask = (0x20 << 8);
- ddc_i2c->a_data_mask = 0x80;
- ddc_i2c->en_clk_mask = (0x20 << 8);
- ddc_i2c->en_data_mask = 0x80;
- ddc_i2c->y_clk_mask = (0x20 << 8);
- ddc_i2c->y_data_mask = 0x80;
- }
-
- /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
- if ((rdev->family >= CHIP_R300) &&
- ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
- *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
/* Certain IBM chipset RN50s have a BIOS reporting two VGAs,
one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */
@@ -2035,27 +2082,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
connector = (tmp >> 12) & 0xf;
ddc_type = (tmp >> 8) & 0xf;
- switch (ddc_type) {
- case DDC_MONID:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- break;
- case DDC_DVI:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
- break;
- case DDC_VGA:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
- break;
- case DDC_CRT2:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
- break;
- default:
- ddc_i2c.valid = false;
- break;
- }
+ ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
switch (connector) {
case CONNECTOR_PROPRIETARY_LEGACY:
@@ -2225,7 +2252,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
0),
ATOM_DEVICE_DFP1_SUPPORT);
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_1;
radeon_add_legacy_connector(dev,
0,
@@ -2245,7 +2272,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
ATOM_DEVICE_CRT1_SUPPORT,
1),
ATOM_DEVICE_CRT1_SUPPORT);
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_connector(dev,
0,
@@ -2278,70 +2305,25 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
if (lcd_ddc_info) {
ddc_type = RBIOS8(lcd_ddc_info + 2);
switch (ddc_type) {
- case DDC_MONID:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_MONID);
- break;
- case DDC_DVI:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_DVI_DDC);
- break;
- case DDC_VGA:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_VGA_DDC);
- break;
- case DDC_CRT2:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_CRT2_DDC);
- break;
case DDC_LCD:
ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIOPAD_MASK);
- ddc_i2c.mask_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.mask_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.a_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.a_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.en_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.en_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.y_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.y_data_mask =
- RBIOS32(lcd_ddc_info + 7);
+ combios_setup_i2c_bus(rdev,
+ DDC_LCD,
+ RBIOS32(lcd_ddc_info + 3),
+ RBIOS32(lcd_ddc_info + 7));
+ radeon_i2c_add(rdev, &ddc_i2c, "LCD");
break;
case DDC_GPIO:
ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_MDGPIO_MASK);
- ddc_i2c.mask_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.mask_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.a_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.a_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.en_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.en_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.y_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.y_data_mask =
- RBIOS32(lcd_ddc_info + 7);
+ combios_setup_i2c_bus(rdev,
+ DDC_GPIO,
+ RBIOS32(lcd_ddc_info + 3),
+ RBIOS32(lcd_ddc_info + 7));
+ radeon_i2c_add(rdev, &ddc_i2c, "LCD");
break;
default:
- ddc_i2c.valid = false;
+ ddc_i2c =
+ combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
break;
}
DRM_DEBUG_KMS("LCD DDC Info Table found!\n");
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 2395c86..47c4b27 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -518,8 +518,6 @@ static void radeon_connector_destroy(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
if (radeon_connector->edid)
kfree(radeon_connector->edid);
kfree(radeon_connector->con_priv);
@@ -955,8 +953,6 @@ static void radeon_dp_connector_destroy(struct drm_connector *connector)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
if (radeon_connector->edid)
kfree(radeon_connector->edid);
if (radeon_dig_connector->dp_i2c_bus)
@@ -1044,7 +1040,8 @@ radeon_add_atom_connector(struct drm_device *dev,
bool linkb,
uint32_t igp_lane_info,
uint16_t connector_object_id,
- struct radeon_hpd *hpd)
+ struct radeon_hpd *hpd,
+ struct radeon_router *router)
{
struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector;
@@ -1069,6 +1066,11 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = true;
shared_ddc = true;
}
+ if (radeon_connector->router_bus && router->valid &&
+ (radeon_connector->router.router_id == router->router_id)) {
+ radeon_connector->shared_ddc = false;
+ shared_ddc = false;
+ }
}
}
@@ -1083,12 +1085,18 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = shared_ddc;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
+ radeon_connector->router = *router;
+ if (router->valid) {
+ radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
+ if (!radeon_connector->router_bus)
+ goto failed;
+ }
switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1104,7 +1112,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1126,7 +1134,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1156,7 +1164,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "HDMI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1187,10 +1195,7 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
if (!radeon_dig_connector->dp_i2c_bus)
goto failed;
- if (connector_type == DRM_MODE_CONNECTOR_eDP)
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "eDP");
- else
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1230,7 +1235,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1252,8 +1257,6 @@ radeon_add_atom_connector(struct drm_device *dev,
return;
failed:
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -1300,7 +1303,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1316,7 +1319,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1332,7 +1335,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1372,7 +1375,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1393,8 +1396,6 @@ radeon_add_legacy_connector(struct drm_device *dev,
return;
failed:
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
drm_connector_cleanup(connector);
kfree(connector);
}
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index ae0fb73..fcc79b5 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -72,7 +72,7 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->relocs[i].gobj == NULL) {
DRM_ERROR("gem object lookup failed 0x%x\n",
r->handle);
- return -EINVAL;
+ return -ENOENT;
}
p->relocs_ptr[i] = &p->relocs[i];
p->relocs[i].robj = p->relocs[i].gobj->driver_private;
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 4eb67c0..5731fc9 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -170,7 +170,7 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
if (!obj) {
DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id);
- return -EINVAL;
+ return -ENOENT;
}
ret = radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index a64811a..4f7a170 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -347,7 +347,8 @@ int radeon_dummy_page_init(struct radeon_device *rdev)
return -ENOMEM;
rdev->dummy_page.addr = pci_map_page(rdev->pdev, rdev->dummy_page.page,
0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (!rdev->dummy_page.addr) {
+ if (pci_dma_mapping_error(rdev->pdev, rdev->dummy_page.addr)) {
+ dev_err(&rdev->pdev->dev, "Failed to DMA MAP the dummy page\n");
__free_page(rdev->dummy_page.page);
rdev->dummy_page.page = NULL;
return -ENOMEM;
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 74dac96..5764f4d 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -161,17 +161,13 @@ void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
}
static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
- u16 *blue, uint32_t size)
+ u16 *blue, uint32_t start, uint32_t size)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
- int i;
-
- if (size != 256) {
- return;
- }
+ int end = (start + size > 256) ? 256 : start + size, i;
/* userspace palettes are always correct as is */
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
radeon_crtc->lut_r[i] = red[i] >> 6;
radeon_crtc->lut_g[i] = green[i] >> 6;
radeon_crtc->lut_b[i] = blue[i] >> 6;
@@ -319,6 +315,10 @@ static void radeon_print_display_setup(struct drm_device *dev)
radeon_connector->ddc_bus->rec.en_data_reg,
radeon_connector->ddc_bus->rec.y_clk_reg,
radeon_connector->ddc_bus->rec.y_data_reg);
+ if (radeon_connector->router_bus)
+ DRM_INFO(" DDC Router 0x%x/0x%x\n",
+ radeon_connector->router.mux_control_pin,
+ radeon_connector->router.mux_state);
} else {
if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
@@ -395,6 +395,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
struct radeon_device *rdev = dev->dev_private;
int ret = 0;
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.valid)
+ radeon_router_select_port(radeon_connector);
+
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
@@ -425,6 +429,10 @@ static int radeon_ddc_dump(struct drm_connector *connector)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int ret = 0;
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.valid)
+ radeon_router_select_port(radeon_connector);
+
if (!radeon_connector->ddc_bus)
return -1;
edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
@@ -876,13 +884,12 @@ radeon_user_framebuffer_create(struct drm_device *dev,
if (obj == NULL) {
dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, "
"can't create framebuffer\n", mode_cmd->handle);
- return NULL;
+ return ERR_PTR(-ENOENT);
}
radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL);
- if (radeon_fb == NULL) {
- return NULL;
- }
+ if (radeon_fb == NULL)
+ return ERR_PTR(-ENOMEM);
radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj);
@@ -1040,6 +1047,9 @@ int radeon_modeset_init(struct radeon_device *rdev)
return ret;
}
+ /* init i2c buses */
+ radeon_i2c_init(rdev);
+
/* check combios for a valid hardcoded EDID - Sun servers */
if (!rdev->is_atom_bios) {
/* check for hardcoded EDID in BIOS */
@@ -1080,6 +1090,8 @@ void radeon_modeset_fini(struct radeon_device *rdev)
drm_mode_config_cleanup(rdev->ddev);
rdev->mode_info.mode_config_initialized = false;
}
+ /* free i2c buses */
+ radeon_i2c_fini(rdev);
}
bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index dc1634b..dbf8696 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -224,7 +224,7 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
- info->flags = FBINFO_DEFAULT;
+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &radeonfb_ops;
tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index a72a3ee..c578f26 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -226,7 +226,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
/* just do a BO wait for now */
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
@@ -245,7 +245,7 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
args->addr_ptr = radeon_bo_mmap_offset(robj);
@@ -264,7 +264,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
r = radeon_bo_wait(robj, &cur_placement, true);
@@ -294,7 +294,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
r = radeon_bo_wait(robj, NULL, false);
@@ -316,7 +316,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG("%d \n", args->handle);
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL)
- return -EINVAL;
+ return -ENOENT;
robj = gobj->driver_private;
r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch);
drm_gem_object_unreference_unlocked(gobj);
@@ -334,7 +334,7 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG("\n");
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL)
- return -EINVAL;
+ return -ENOENT;
rbo = gobj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 5def6f5..bfd2ce5 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -52,6 +52,10 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
}
};
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.valid)
+ radeon_router_select_port(radeon_connector);
+
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
if (ret == 2)
return true;
@@ -960,6 +964,59 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
kfree(i2c);
}
+/* Add the default buses */
+void radeon_i2c_init(struct radeon_device *rdev)
+{
+ if (rdev->is_atom_bios)
+ radeon_atombios_i2c_init(rdev);
+ else
+ radeon_combios_i2c_init(rdev);
+}
+
+/* remove all the buses */
+void radeon_i2c_fini(struct radeon_device *rdev)
+{
+ int i;
+
+ for (i = 0; i < RADEON_MAX_I2C_BUS; i++) {
+ if (rdev->i2c_bus[i]) {
+ radeon_i2c_destroy(rdev->i2c_bus[i]);
+ rdev->i2c_bus[i] = NULL;
+ }
+ }
+}
+
+/* Add additional buses */
+void radeon_i2c_add(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *rec,
+ const char *name)
+{
+ struct drm_device *dev = rdev->ddev;
+ int i;
+
+ for (i = 0; i < RADEON_MAX_I2C_BUS; i++) {
+ if (!rdev->i2c_bus[i]) {
+ rdev->i2c_bus[i] = radeon_i2c_create(dev, rec, name);
+ return;
+ }
+ }
+}
+
+/* looks up bus based on id */
+struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *i2c_bus)
+{
+ int i;
+
+ for (i = 0; i < RADEON_MAX_I2C_BUS; i++) {
+ if (rdev->i2c_bus[i] &&
+ (rdev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) {
+ return rdev->i2c_bus[i];
+ }
+ }
+ return NULL;
+}
+
struct drm_encoder *radeon_best_encoder(struct drm_connector *connector)
{
return NULL;
@@ -1020,3 +1077,28 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus,
addr, val);
}
+/* router switching */
+void radeon_router_select_port(struct radeon_connector *radeon_connector)
+{
+ u8 val;
+
+ if (!radeon_connector->router.valid)
+ return;
+
+ radeon_i2c_get_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x3, &val);
+ val &= radeon_connector->router.mux_control_pin;
+ radeon_i2c_put_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x3, val);
+ radeon_i2c_get_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x1, &val);
+ val &= radeon_connector->router.mux_control_pin;
+ val |= radeon_connector->router.mux_state;
+ radeon_i2c_put_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x1, val);
+}
+
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index ddcd3b1..b1c8ace 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -112,7 +112,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
info = data;
value_ptr = (uint32_t *)((unsigned long)info->value);
- value = *value_ptr;
+ if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value)))
+ return -EFAULT;
+
switch (info->request) {
case RADEON_INFO_DEVICE_ID:
value = dev->pci_device;
@@ -160,13 +162,27 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
}
case RADEON_INFO_WANT_HYPERZ:
+ /* The "value" here is both an input and output parameter.
+ * If the input value is 1, filp requests hyper-z access.
+ * If the input value is 0, filp revokes its hyper-z access.
+ *
+ * When returning, the value is 1 if filp owns hyper-z access,
+ * 0 otherwise. */
+ if (value >= 2) {
+ DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", value);
+ return -EINVAL;
+ }
mutex_lock(&dev->struct_mutex);
- if (rdev->hyperz_filp)
- value = 0;
- else {
- rdev->hyperz_filp = filp;
- value = 1;
+ if (value == 1) {
+ /* wants hyper-z */
+ if (!rdev->hyperz_filp)
+ rdev->hyperz_filp = filp;
+ } else if (value == 0) {
+ /* revokes hyper-z */
+ if (rdev->hyperz_filp == filp)
+ rdev->hyperz_filp = NULL;
}
+ value = rdev->hyperz_filp == filp ? 1 : 0;
mutex_unlock(&dev->struct_mutex);
break;
default:
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 71aea40..5bbc086 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -82,6 +82,8 @@ enum radeon_hpd_id {
RADEON_HPD_NONE = 0xff,
};
+#define RADEON_MAX_I2C_BUS 16
+
/* radeon gpio-based i2c
* 1. "mask" reg and bits
* grabs the gpio pins for software use
@@ -398,6 +400,16 @@ struct radeon_hpd {
struct radeon_gpio_rec gpio;
};
+struct radeon_router {
+ bool valid;
+ u32 router_id;
+ struct radeon_i2c_bus_rec i2c_info;
+ u8 i2c_addr;
+ u8 mux_type;
+ u8 mux_control_pin;
+ u8 mux_state;
+};
+
struct radeon_connector {
struct drm_connector base;
uint32_t connector_id;
@@ -413,6 +425,8 @@ struct radeon_connector {
bool dac_load_detect;
uint16_t connector_object_id;
struct radeon_hpd hpd;
+ struct radeon_router router;
+ struct radeon_i2c_chan *router_bus;
};
struct radeon_framebuffer {
@@ -445,6 +459,15 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
uint8_t write_byte, uint8_t *read_byte);
+extern void radeon_i2c_init(struct radeon_device *rdev);
+extern void radeon_i2c_fini(struct radeon_device *rdev);
+extern void radeon_combios_i2c_init(struct radeon_device *rdev);
+extern void radeon_atombios_i2c_init(struct radeon_device *rdev);
+extern void radeon_i2c_add(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *rec,
+ const char *name);
+extern struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *i2c_bus);
extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
struct radeon_i2c_bus_rec *rec,
const char *name);
@@ -460,6 +483,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c,
u8 slave_addr,
u8 addr,
u8 val);
+extern void radeon_router_select_port(struct radeon_connector *radeon_connector);
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 95f8b3a..58038f5c 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -472,9 +472,9 @@ static const struct attribute_group hwmon_attrgroup = {
.attrs = hwmon_attributes,
};
-static void radeon_hwmon_init(struct radeon_device *rdev)
+static int radeon_hwmon_init(struct radeon_device *rdev)
{
- int err;
+ int err = 0;
rdev->pm.int_hwmon_dev = NULL;
@@ -483,15 +483,26 @@ static void radeon_hwmon_init(struct radeon_device *rdev)
case THERMAL_TYPE_RV770:
case THERMAL_TYPE_EVERGREEN:
rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev);
+ if (IS_ERR(rdev->pm.int_hwmon_dev)) {
+ err = PTR_ERR(rdev->pm.int_hwmon_dev);
+ dev_err(rdev->dev,
+ "Unable to register hwmon device: %d\n", err);
+ break;
+ }
dev_set_drvdata(rdev->pm.int_hwmon_dev, rdev->ddev);
err = sysfs_create_group(&rdev->pm.int_hwmon_dev->kobj,
&hwmon_attrgroup);
- if (err)
- DRM_ERROR("Unable to create hwmon sysfs file: %d\n", err);
+ if (err) {
+ dev_err(rdev->dev,
+ "Unable to create hwmon sysfs file: %d\n", err);
+ hwmon_device_unregister(rdev->dev);
+ }
break;
default:
break;
}
+
+ return err;
}
static void radeon_hwmon_fini(struct radeon_device *rdev)
@@ -540,6 +551,7 @@ void radeon_pm_resume(struct radeon_device *rdev)
int radeon_pm_init(struct radeon_device *rdev)
{
int ret;
+
/* default to profile method */
rdev->pm.pm_method = PM_METHOD_PROFILE;
rdev->pm.profile = PM_PROFILE_DEFAULT;
@@ -561,7 +573,9 @@ int radeon_pm_init(struct radeon_device *rdev)
}
/* set up the internal thermal sensor if applicable */
- radeon_hwmon_init(rdev);
+ ret = radeon_hwmon_init(rdev);
+ if (ret)
+ return ret;
if (rdev->pm.num_power_states > 1) {
/* where's the best place to put these? */
ret = device_create_file(rdev->dev, &dev_attr_power_profile);
diff --git a/drivers/gpu/drm/radeon/reg_srcs/rv515 b/drivers/gpu/drm/radeon/reg_srcs/rv515
index 8293855..b3f9f1d 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/rv515
+++ b/drivers/gpu/drm/radeon/reg_srcs/rv515
@@ -316,6 +316,7 @@ rv515 0x6d40
0x4BD0 FG_FOG_COLOR_B
0x4BD4 FG_ALPHA_FUNC
0x4BD8 FG_DEPTH_SRC
+0x4BE0 FG_ALPHA_VALUE
0x4C00 US_ALU_CONST_R_0
0x4C04 US_ALU_CONST_G_0
0x4C08 US_ALU_CONST_B_0
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 437ac78..64d7f47 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -737,7 +737,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
- return NULL;
+ return ERR_PTR(ret);
}
return &vfb->base;
@@ -747,7 +747,7 @@ try_dmabuf:
ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo);
if (ret) {
DRM_ERROR("failed to find buffer: %i\n", ret);
- return NULL;
+ return ERR_PTR(-ENOENT);
}
ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
@@ -758,7 +758,7 @@ try_dmabuf:
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
- return NULL;
+ return ERR_PTR(ret);
}
return &vfb->base;
@@ -768,7 +768,7 @@ err_not_scanout:
/* vmw_user_surface_lookup takes one ref */
vmw_surface_unreference(&surface);
- return NULL;
+ return ERR_PTR(-EINVAL);
}
static struct drm_mode_config_funcs vmw_kms_funcs = {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index cfaf690..2ff5cf7 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -79,7 +79,7 @@ static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
u16 *r, u16 *g, u16 *b,
- uint32_t size)
+ uint32_t start, uint32_t size)
{
}
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 807dcd1..724f46e 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -230,7 +230,7 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
input_report_key(input, BTN_RIGHT, 0);
input_report_key(input, BTN_MIDDLE, 0);
input_report_abs(input, ABS_DISTANCE,
- input->absmax[ABS_DISTANCE]);
+ input_abs_get_max(input, ABS_DISTANCE));
} else {
input_report_key(input, BTN_TOUCH, 0);
input_report_key(input, BTN_STYLUS, 0);
@@ -383,38 +383,37 @@ move_on:
/* Basics */
input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
- input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
- BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
- input->relbit[0] |= BIT(REL_WHEEL);
- set_bit(BTN_TOOL_PEN, input->keybit);
- set_bit(BTN_TOUCH, input->keybit);
- set_bit(BTN_STYLUS, input->keybit);
- set_bit(BTN_STYLUS2, input->keybit);
- set_bit(BTN_LEFT, input->keybit);
- set_bit(BTN_RIGHT, input->keybit);
- set_bit(BTN_MIDDLE, input->keybit);
+
+ __set_bit(REL_WHEEL, input->relbit);
+
+ __set_bit(BTN_TOOL_PEN, input->keybit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_STYLUS, input->keybit);
+ __set_bit(BTN_STYLUS2, input->keybit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ __set_bit(BTN_MIDDLE, input->keybit);
/* Pad */
input->evbit[0] |= BIT(EV_MSC);
- input->mscbit[0] |= BIT(MSC_SERIAL);
- set_bit(BTN_0, input->keybit);
- set_bit(BTN_1, input->keybit);
- set_bit(BTN_TOOL_FINGER, input->keybit);
- /* Distance, rubber and mouse */
- input->absbit[0] |= BIT(ABS_DISTANCE);
- set_bit(BTN_TOOL_RUBBER, input->keybit);
- set_bit(BTN_TOOL_MOUSE, input->keybit);
+ __set_bit(MSC_SERIAL, input->mscbit);
- input->absmax[ABS_PRESSURE] = 511;
- input->absmax[ABS_DISTANCE] = 32;
+ __set_bit(BTN_0, input->keybit);
+ __set_bit(BTN_1, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
- input->absmax[ABS_X] = 16704;
- input->absmax[ABS_Y] = 12064;
- input->absfuzz[ABS_X] = 4;
- input->absfuzz[ABS_Y] = 4;
+ /* Distance, rubber and mouse */
+ __set_bit(BTN_TOOL_RUBBER, input->keybit);
+ __set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+ input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
+ input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
+ input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
return 0;
+
err_free:
kfree(wdata);
return ret;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f3adf18..0fba829 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -465,6 +465,7 @@ config SENSORS_JZ4740
config SENSORS_JC42
tristate "JEDEC JC42.4 compliant temperature sensors"
+ depends on I2C
help
If you say yes here you get support for Jedec JC42.4 compliant
temperature sensors. Support will include, but not be limited to,
@@ -711,7 +712,8 @@ config SENSORS_PC87427
functions of the National Semiconductor PC87427 Super-I/O chip.
The chip has two distinct logical devices, one for fan speed
monitoring and control, and one for voltage and temperature
- monitoring. Only fan speed monitoring is supported right now.
+ monitoring. Fan speed monitoring and control are supported, as
+ well as temperature monitoring. Voltages aren't supported yet.
This driver can also be built as a module. If so, the module
will be called pc87427.
@@ -804,6 +806,16 @@ config SENSORS_EMC1403
Threshold values can be configured using sysfs.
Data from the different diodes are accessible via sysfs.
+config SENSORS_EMC2103
+ tristate "SMSC EMC2103"
+ depends on I2C
+ help
+ If you say yes here you get support for the temperature
+ and fan sensors of the SMSC EMC2103 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called emc2103.
+
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
help
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 13d913e..e3c2484 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
+obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h
index b28d7e2..90f094d 100644
--- a/drivers/hwmon/ams/ams.h
+++ b/drivers/hwmon/ams/ams.h
@@ -23,7 +23,7 @@ struct ams {
/* General properties */
struct device_node *of_node;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
char has_device;
char vflag;
u32 orient1;
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
index 3b973f3..89b4f3b 100644
--- a/drivers/hwmon/asc7621.c
+++ b/drivers/hwmon/asc7621.c
@@ -1150,9 +1150,6 @@ static int asc7621_detect(struct i2c_client *client,
{
struct i2c_adapter *adapter = client->adapter;
int company, verstep, chip_index;
- struct device *dev;
-
- dev = &client->dev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -1169,13 +1166,11 @@ static int asc7621_detect(struct i2c_client *client,
if (company == asc7621_chips[chip_index].company_id &&
verstep == asc7621_chips[chip_index].verstep_id) {
- strlcpy(client->name, asc7621_chips[chip_index].name,
- I2C_NAME_SIZE);
strlcpy(info->type, asc7621_chips[chip_index].name,
I2C_NAME_SIZE);
- dev_info(&adapter->dev, "Matched %s\n",
- asc7621_chips[chip_index].name);
+ dev_info(&adapter->dev, "Matched %s at 0x%02x\n",
+ asc7621_chips[chip_index].name, client->addr);
return 0;
}
}
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index a92e28a..c070c97 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -501,13 +501,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
switch (action) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
coretemp_device_add(cpu);
break;
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
coretemp_device_remove(cpu);
break;
}
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index 0e4b564..5b58b20 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -89,6 +89,35 @@ static ssize_t store_temp(struct device *dev,
return count;
}
+static ssize_t store_bit(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct thermal_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
+ unsigned long val;
+ int retval;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ retval = i2c_smbus_read_byte_data(client, sda->nr);
+ if (retval < 0)
+ goto fail;
+
+ retval &= ~sda->index;
+ if (val)
+ retval |= sda->index;
+
+ retval = i2c_smbus_write_byte_data(client, sda->index, retval);
+ if (retval == 0)
+ retval = count;
+fail:
+ mutex_unlock(&data->mutex);
+ return retval;
+}
+
static ssize_t show_hyst(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -200,6 +229,9 @@ static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
show_hyst, store_hyst, 0x1A);
+static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR,
+ show_bit, store_bit, 0x03, 0x40);
+
static struct attribute *mid_att_thermal[] = {
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
@@ -225,6 +257,7 @@ static struct attribute *mid_att_thermal[] = {
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_power_state.dev_attr.attr,
NULL
};
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
new file mode 100644
index 0000000..af914ad
--- /dev/null
+++ b/drivers/hwmon/emc2103.c
@@ -0,0 +1,740 @@
+/*
+ emc2103.c - Support for SMSC EMC2103
+ Copyright (c) 2010 SMSC
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END };
+
+static const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 };
+static const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a };
+static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
+
+#define REG_CONF1 0x20
+#define REG_TEMP_MAX_ALARM 0x24
+#define REG_TEMP_MIN_ALARM 0x25
+#define REG_FAN_CONF1 0x42
+#define REG_FAN_TARGET_LO 0x4c
+#define REG_FAN_TARGET_HI 0x4d
+#define REG_FAN_TACH_HI 0x4e
+#define REG_FAN_TACH_LO 0x4f
+#define REG_PRODUCT_ID 0xfd
+#define REG_MFG_ID 0xfe
+
+/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
+#define FAN_RPM_FACTOR 3932160
+
+/* 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes
+ * in anti-parallel mode, and in this configuration both can be read
+ * independently (so we have 4 temperature inputs). The device can't
+ * detect if it's connected in this mode, so we have to manually enable
+ * it. Default is to leave the device in the state it's already in (-1).
+ * This parameter allows APD mode to be optionally forced on or off */
+static int apd = -1;
+module_param(apd, bool, 0);
+MODULE_PARM_DESC(init, "Set to zero to disable anti-parallel diode mode");
+
+struct temperature {
+ s8 degrees;
+ u8 fraction; /* 0-7 multiples of 0.125 */
+};
+
+struct emc2103_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ bool valid; /* registers are valid */
+ bool fan_rpm_control;
+ int temp_count; /* num of temp sensors */
+ unsigned long last_updated; /* in jiffies */
+ struct temperature temp[4]; /* internal + 3 external */
+ s8 temp_min[4]; /* no fractional part */
+ s8 temp_max[4]; /* no fractional part */
+ u8 temp_min_alarm;
+ u8 temp_max_alarm;
+ u8 fan_multiplier;
+ u16 fan_tach;
+ u16 fan_target;
+};
+
+static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output)
+{
+ int status = i2c_smbus_read_byte_data(client, i2c_reg);
+ if (status < 0) {
+ dev_warn(&client->dev, "reg 0x%02x, err %d\n",
+ i2c_reg, status);
+ } else {
+ *output = status;
+ }
+ return status;
+}
+
+static void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg,
+ struct temperature *temp)
+{
+ u8 degrees, fractional;
+
+ if (read_u8_from_i2c(client, i2c_reg, &degrees) < 0)
+ return;
+
+ if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0)
+ return;
+
+ temp->degrees = degrees;
+ temp->fraction = (fractional & 0xe0) >> 5;
+}
+
+static void read_fan_from_i2c(struct i2c_client *client, u16 *output,
+ u8 hi_addr, u8 lo_addr)
+{
+ u8 high_byte, lo_byte;
+
+ if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0)
+ return;
+
+ if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0)
+ return;
+
+ *output = ((u16)high_byte << 5) | (lo_byte >> 3);
+}
+
+static void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target)
+{
+ u8 high_byte = (new_target & 0x1fe0) >> 5;
+ u8 low_byte = (new_target & 0x001f) << 3;
+ i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte);
+ i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte);
+}
+
+static void read_fan_config_from_i2c(struct i2c_client *client)
+
+{
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ u8 conf1;
+
+ if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0)
+ return;
+
+ data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5);
+ data->fan_rpm_control = (conf1 & 0x80) != 0;
+}
+
+static struct emc2103_data *emc2103_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int i;
+
+ for (i = 0; i < data->temp_count; i++) {
+ read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]);
+ read_u8_from_i2c(client, REG_TEMP_MIN[i],
+ &data->temp_min[i]);
+ read_u8_from_i2c(client, REG_TEMP_MAX[i],
+ &data->temp_max[i]);
+ }
+
+ read_u8_from_i2c(client, REG_TEMP_MIN_ALARM,
+ &data->temp_min_alarm);
+ read_u8_from_i2c(client, REG_TEMP_MAX_ALARM,
+ &data->temp_max_alarm);
+
+ read_fan_from_i2c(client, &data->fan_tach,
+ REG_FAN_TACH_HI, REG_FAN_TACH_LO);
+ read_fan_from_i2c(client, &data->fan_target,
+ REG_FAN_TARGET_HI, REG_FAN_TARGET_LO);
+ read_fan_config_from_i2c(client);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp[nr].degrees * 1000
+ + data->temp[nr].fraction * 125;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+show_temp_min(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp_min[nr] * 1000;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+show_temp_max(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp_max[nr] * 1000;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+show_temp_fault(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool fault = (data->temp[nr].degrees == -128);
+ return sprintf(buf, "%d\n", fault ? 1 : 0);
+}
+
+static ssize_t
+show_temp_min_alarm(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool alarm = data->temp_min_alarm & (1 << nr);
+ return sprintf(buf, "%d\n", alarm ? 1 : 0);
+}
+
+static ssize_t
+show_temp_max_alarm(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool alarm = data->temp_max_alarm & (1 << nr);
+ return sprintf(buf, "%d\n", alarm ? 1 : 0);
+}
+
+static ssize_t set_temp_min(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ long val;
+
+ int result = strict_strtol(buf, 10, &val);
+ if (result < 0)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ if ((val < -63) || (val > 127))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = val;
+ i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_temp_max(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ long val;
+
+ int result = strict_strtol(buf, 10, &val);
+ if (result < 0)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ if ((val < -63) || (val > 127))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = val;
+ i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int rpm = 0;
+ if (data->fan_tach != 0)
+ rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int fan_div = 8 / data->fan_multiplier;
+ return sprintf(buf, "%d\n", fan_div);
+}
+
+/* Note: we also update the fan target here, because its value is
+ determined in part by the fan clock divider. This follows the principle
+ of least surprise; the user doesn't expect the fan target to change just
+ because the divider changed. */
+static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ int new_range_bits, old_div = 8 / data->fan_multiplier;
+ long new_div;
+
+ int status = strict_strtol(buf, 10, &new_div);
+ if (status < 0)
+ return -EINVAL;
+
+ if (new_div == old_div) /* No change */
+ return count;
+
+ switch (new_div) {
+ case 1:
+ new_range_bits = 3;
+ break;
+ case 2:
+ new_range_bits = 2;
+ break;
+ case 4:
+ new_range_bits = 1;
+ break;
+ case 8:
+ new_range_bits = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1);
+ if (status < 0) {
+ dev_dbg(&client->dev, "reg 0x%02x, err %d\n",
+ REG_FAN_CONF1, status);
+ mutex_unlock(&data->update_lock);
+ return -EIO;
+ }
+ status &= 0x9F;
+ status |= (new_range_bits << 5);
+ i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status);
+
+ data->fan_multiplier = 8 / new_div;
+
+ /* update fan target if high byte is not disabled */
+ if ((data->fan_target & 0x1fe0) != 0x1fe0) {
+ u16 new_target = (data->fan_target * old_div) / new_div;
+ data->fan_target = min(new_target, (u16)0x1fff);
+ write_fan_target_to_i2c(client, data->fan_target);
+ }
+
+ /* invalidate data to force re-read from hardware */
+ data->valid = false;
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_fan_target(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int rpm = 0;
+
+ /* high byte of 0xff indicates disabled so return 0 */
+ if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
+ rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
+ / data->fan_target;
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_fan_target(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ long rpm_target;
+
+ int result = strict_strtol(buf, 10, &rpm_target);
+ if (result < 0)
+ return -EINVAL;
+
+ /* Datasheet states 16384 as maximum RPM target (table 3.2) */
+ if ((rpm_target < 0) || (rpm_target > 16384))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ if (rpm_target == 0)
+ data->fan_target = 0x1fff;
+ else
+ data->fan_target = SENSORS_LIMIT(
+ (FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target,
+ 0, 0x1fff);
+
+ write_fan_target_to_i2c(client, data->fan_target);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_fan_fault(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
+ return sprintf(buf, "%d\n", fault ? 1 : 0);
+}
+
+static ssize_t
+show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0);
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ long new_value;
+ u8 conf_reg;
+
+ int result = strict_strtol(buf, 10, &new_value);
+ if (result < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ switch (new_value) {
+ case 0:
+ data->fan_rpm_control = false;
+ break;
+ case 3:
+ data->fan_rpm_control = true;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg);
+
+ if (data->fan_rpm_control)
+ conf_reg |= 0x80;
+ else
+ conf_reg &= ~0x80;
+
+ i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 1);
+
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR, show_temp_min,
+ set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max,
+ set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_temp_min_alarm,
+ NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_temp_max_alarm,
+ NULL, 3);
+
+static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
+static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div);
+static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_fan_target,
+ set_fan_target);
+static DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL);
+
+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
+ set_pwm_enable);
+
+/* sensors present on all models */
+static struct attribute *emc2103_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_target.attr,
+ &dev_attr_fan1_fault.attr,
+ &dev_attr_pwm1_enable.attr,
+ NULL
+};
+
+/* extra temperature sensors only present on 2103-2 and 2103-4 */
+static struct attribute *emc2103_attributes_temp3[] = {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ NULL
+};
+
+/* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */
+static struct attribute *emc2103_attributes_temp4[] = {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group emc2103_group = {
+ .attrs = emc2103_attributes,
+};
+
+static const struct attribute_group emc2103_temp3_group = {
+ .attrs = emc2103_attributes_temp3,
+};
+
+static const struct attribute_group emc2103_temp4_group = {
+ .attrs = emc2103_attributes_temp4,
+};
+
+static int
+emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct emc2103_data *data;
+ int status;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = kzalloc(sizeof(struct emc2103_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */
+ status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID);
+ if (status == 0x24) {
+ /* 2103-1 only has 1 external diode */
+ data->temp_count = 2;
+ } else {
+ /* 2103-2 and 2103-4 have 3 or 4 external diodes */
+ status = i2c_smbus_read_byte_data(client, REG_CONF1);
+ if (status < 0) {
+ dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1,
+ status);
+ goto exit_free;
+ }
+
+ /* detect current state of hardware */
+ data->temp_count = (status & 0x01) ? 4 : 3;
+
+ /* force APD state if module parameter is set */
+ if (apd == 0) {
+ /* force APD mode off */
+ data->temp_count = 3;
+ status &= ~(0x01);
+ i2c_smbus_write_byte_data(client, REG_CONF1, status);
+ } else if (apd == 1) {
+ /* force APD mode on */
+ data->temp_count = 4;
+ status |= 0x01;
+ i2c_smbus_write_byte_data(client, REG_CONF1, status);
+ }
+ }
+
+ /* Register sysfs hooks */
+ status = sysfs_create_group(&client->dev.kobj, &emc2103_group);
+ if (status)
+ goto exit_free;
+
+ if (data->temp_count >= 3) {
+ status = sysfs_create_group(&client->dev.kobj,
+ &emc2103_temp3_group);
+ if (status)
+ goto exit_remove;
+ }
+
+ if (data->temp_count == 4) {
+ status = sysfs_create_group(&client->dev.kobj,
+ &emc2103_temp4_group);
+ if (status)
+ goto exit_remove_temp3;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ status = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_temp4;
+ }
+
+ dev_info(&client->dev, "%s: sensor '%s'\n",
+ dev_name(data->hwmon_dev), client->name);
+
+ return 0;
+
+exit_remove_temp4:
+ if (data->temp_count == 4)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group);
+exit_remove_temp3:
+ if (data->temp_count >= 3)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &emc2103_group);
+exit_free:
+ kfree(data);
+ return status;
+}
+
+static int emc2103_remove(struct i2c_client *client)
+{
+ struct emc2103_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ if (data->temp_count == 4)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group);
+
+ if (data->temp_count >= 3)
+ sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
+
+ sysfs_remove_group(&client->dev.kobj, &emc2103_group);
+
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id emc2103_ids[] = {
+ { "emc2103", 0, },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, emc2103_ids);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int
+emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ int manufacturer, product;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID);
+ if (manufacturer != 0x5D)
+ return -ENODEV;
+
+ product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID);
+ if ((product != 0x24) && (product != 0x26))
+ return -ENODEV;
+
+ strlcpy(info->type, "emc2103", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static struct i2c_driver emc2103_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "emc2103",
+ },
+ .probe = emc2103_probe,
+ .remove = emc2103_remove,
+ .id_table = emc2103_ids,
+ .detect = emc2103_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init sensors_emc2103_init(void)
+{
+ return i2c_add_driver(&emc2103_driver);
+}
+
+static void __exit sensors_emc2103_exit(void)
+{
+ i2c_del_driver(&emc2103_driver);
+}
+
+MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>");
+MODULE_DESCRIPTION("SMSC EMC2103 hwmon driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_emc2103_init);
+module_exit(sensors_emc2103_exit);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 25763d2..f770129 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -259,6 +259,7 @@ struct it87_sio_data {
u8 revision;
u8 vid_value;
u8 beep_pin;
+ u8 internal; /* Internal sensors can be labeled */
/* Features skipped based on config or DMI */
u8 skip_vid;
u8 skip_fan;
@@ -1194,6 +1195,22 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static ssize_t show_label(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ static const char *labels[] = {
+ "+5V",
+ "5VSB",
+ "Vbat",
+ };
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", labels[nr]);
+}
+static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
+
static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
@@ -1434,6 +1451,17 @@ static const struct attribute_group it87_group_vid = {
.attrs = it87_attributes_vid,
};
+static struct attribute *it87_attributes_label[] = {
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group_label = {
+ .attrs = it87_attributes_vid,
+};
+
/* SuperIO detection - will change isa_address if a chip is found */
static int __init it87_find(unsigned short *address,
struct it87_sio_data *sio_data)
@@ -1487,6 +1515,9 @@ static int __init it87_find(unsigned short *address,
pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
chip_type, *address, sio_data->revision);
+ /* in8 (Vbat) is always internal */
+ sio_data->internal = (1 << 2);
+
/* Read GPIO config and VID value from LDN 7 (GPIO) */
if (sio_data->type == it87) {
/* The IT8705F doesn't have VID pins at all */
@@ -1540,9 +1571,9 @@ static int __init it87_find(unsigned short *address,
pr_notice("it87: Routing internal VCCH to in7\n");
}
if (reg & (1 << 0))
- pr_info("it87: in3 is VCC (+5V)\n");
+ sio_data->internal |= (1 << 0);
if (reg & (1 << 1))
- pr_info("it87: in7 is VCCH (+5V Stand-By)\n");
+ sio_data->internal |= (1 << 1);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
}
@@ -1600,6 +1631,7 @@ static void it87_remove_files(struct device *dev)
}
if (!sio_data->skip_vid)
sysfs_remove_group(&dev->kobj, &it87_group_vid);
+ sysfs_remove_group(&dev->kobj, &it87_group_label);
}
static int __devinit it87_probe(struct platform_device *pdev)
@@ -1725,6 +1757,16 @@ static int __devinit it87_probe(struct platform_device *pdev)
goto ERROR4;
}
+ /* Export labels for internal sensors */
+ for (i = 0; i < 3; i++) {
+ if (!(sio_data->internal & (1 << i)))
+ continue;
+ err = sysfs_create_file(&dev->kobj,
+ it87_attributes_label[i]);
+ if (err)
+ goto ERROR4;
+ }
+
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
index 8bdf80d..b9bb3e0 100644
--- a/drivers/hwmon/k8temp.c
+++ b/drivers/hwmon/k8temp.c
@@ -252,12 +252,13 @@ static int __devinit k8temp_probe(struct pci_dev *pdev,
&sensor_dev_attr_temp3_input.dev_attr);
if (err)
goto exit_remove;
- if (data->sensorsp & SEL_PLACE)
+ if (data->sensorsp & SEL_PLACE) {
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp4_input.
dev_attr);
if (err)
goto exit_remove;
+ }
}
err = device_create_file(&pdev->dev, &dev_attr_name);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 393f354..ab5b87a 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -280,10 +280,49 @@ static int lm75_detect(struct i2c_client *new_client,
return 0;
}
+#ifdef CONFIG_PM
+static int lm75_suspend(struct device *dev)
+{
+ int status;
+ struct i2c_client *client = to_i2c_client(dev);
+ status = lm75_read_value(client, LM75_REG_CONF);
+ if (status < 0) {
+ dev_dbg(&client->dev, "Can't read config? %d\n", status);
+ return status;
+ }
+ status = status | LM75_SHUTDOWN;
+ lm75_write_value(client, LM75_REG_CONF, status);
+ return 0;
+}
+
+static int lm75_resume(struct device *dev)
+{
+ int status;
+ struct i2c_client *client = to_i2c_client(dev);
+ status = lm75_read_value(client, LM75_REG_CONF);
+ if (status < 0) {
+ dev_dbg(&client->dev, "Can't read config? %d\n", status);
+ return status;
+ }
+ status = status & ~LM75_SHUTDOWN;
+ lm75_write_value(client, LM75_REG_CONF, status);
+ return 0;
+}
+
+static const struct dev_pm_ops lm75_dev_pm_ops = {
+ .suspend = lm75_suspend,
+ .resume = lm75_resume,
+};
+#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops)
+#else
+#define LM75_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
static struct i2c_driver lm75_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "lm75",
+ .pm = LM75_DEV_PM_OPS,
},
.probe = lm75_probe,
.remove = lm75_remove,
diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h
index 7c93454..e547a3e 100644
--- a/drivers/hwmon/lm75.h
+++ b/drivers/hwmon/lm75.h
@@ -30,6 +30,7 @@
/* straight from the datasheet */
#define LM75_TEMP_MIN (-55000)
#define LM75_TEMP_MAX 125000
+#define LM75_SHUTDOWN 0x01
/* TEMP: 0.001C/bit (-55C to +125C)
REG: (0.5C/bit, two's complement) << 7 */
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 21d201b..6593083 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -21,6 +21,7 @@
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
+#include <linux/i2c/ltc4245.h>
/* Here are names of the chip's registers (a.k.a. commands) */
enum ltc4245_cmd {
@@ -60,8 +61,72 @@ struct ltc4245_data {
/* Voltage registers */
u8 vregs[0x0d];
+
+ /* GPIO ADC registers */
+ bool use_extra_gpios;
+ int gpios[3];
};
+/*
+ * Update the readings from the GPIO pins. If the driver has been configured to
+ * sample all GPIO's as analog voltages, a round-robin sampling method is used.
+ * Otherwise, only the configured GPIO pin is sampled.
+ *
+ * LOCKING: must hold data->update_lock
+ */
+static void ltc4245_update_gpios(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc4245_data *data = i2c_get_clientdata(client);
+ u8 gpio_curr, gpio_next, gpio_reg;
+ int i;
+
+ /* no extra gpio support, we're basically done */
+ if (!data->use_extra_gpios) {
+ data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10];
+ return;
+ }
+
+ /*
+ * If the last reading was too long ago, then we mark all old GPIO
+ * readings as stale by setting them to -EAGAIN
+ */
+ if (time_after(jiffies, data->last_updated + 5 * HZ)) {
+ dev_dbg(&client->dev, "Marking GPIOs invalid\n");
+ for (i = 0; i < ARRAY_SIZE(data->gpios); i++)
+ data->gpios[i] = -EAGAIN;
+ }
+
+ /*
+ * Get the current GPIO pin
+ *
+ * The datasheet calls these GPIO[1-3], but we'll calculate the zero
+ * based array index instead, and call them GPIO[0-2]. This is much
+ * easier to think about.
+ */
+ gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6;
+ if (gpio_curr > 0)
+ gpio_curr -= 1;
+
+ /* Read the GPIO voltage from the GPIOADC register */
+ data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10];
+
+ /* Find the next GPIO pin to read */
+ gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios);
+
+ /*
+ * Calculate the correct setting for the GPIO register so it will
+ * sample the next GPIO pin
+ */
+ gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6);
+
+ /* Update the GPIO register */
+ i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg);
+
+ /* Update saved data */
+ data->cregs[LTC4245_GPIO] = gpio_reg;
+}
+
static struct ltc4245_data *ltc4245_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -93,6 +158,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
data->vregs[i] = val;
}
+ /* Update GPIO readings */
+ ltc4245_update_gpios(dev);
+
data->last_updated = jiffies;
data->valid = 1;
}
@@ -233,6 +301,22 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
}
+static ssize_t ltc4245_show_gpio(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4245_data *data = ltc4245_update_device(dev);
+ int val = data->gpios[attr->index];
+
+ /* handle stale GPIO's */
+ if (val < 0)
+ return val;
+
+ /* Convert to millivolts and print */
+ return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
+}
+
/* These macros are used below in constructing device attribute objects
* for use with sysfs_create_group() to make a sysfs device file
* for each register.
@@ -254,6 +338,10 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
ltc4245_show_alarm, NULL, (mask), reg)
+#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \
+ static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
+ ltc4245_show_gpio, NULL, gpio_num)
+
/* Construct a sensor_device_attribute structure for each register */
/* Input voltages */
@@ -293,7 +381,9 @@ LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2);
LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2);
/* GPIO voltages */
-LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC);
+LTC4245_GPIO_VOLTAGE(in9_input, 0);
+LTC4245_GPIO_VOLTAGE(in10_input, 1);
+LTC4245_GPIO_VOLTAGE(in11_input, 2);
/* Power Consumption (virtual) */
LTC4245_POWER(power1_input, LTC4245_12VSENSE);
@@ -304,7 +394,7 @@ LTC4245_POWER(power4_input, LTC4245_VEESENSE);
/* Finally, construct an array of pointers to members of the above objects,
* as required for sysfs_create_group()
*/
-static struct attribute *ltc4245_attributes[] = {
+static struct attribute *ltc4245_std_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
@@ -345,10 +435,77 @@ static struct attribute *ltc4245_attributes[] = {
NULL,
};
-static const struct attribute_group ltc4245_group = {
- .attrs = ltc4245_attributes,
+static struct attribute *ltc4245_gpio_attributes[] = {
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ltc4245_std_group = {
+ .attrs = ltc4245_std_attributes,
+};
+
+static const struct attribute_group ltc4245_gpio_group = {
+ .attrs = ltc4245_gpio_attributes,
};
+static int ltc4245_sysfs_create_groups(struct i2c_client *client)
+{
+ struct ltc4245_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int ret;
+
+ /* register the standard sysfs attributes */
+ ret = sysfs_create_group(&dev->kobj, &ltc4245_std_group);
+ if (ret) {
+ dev_err(dev, "unable to register standard attributes\n");
+ return ret;
+ }
+
+ /* if we're using the extra gpio support, register it's attributes */
+ if (data->use_extra_gpios) {
+ ret = sysfs_create_group(&dev->kobj, &ltc4245_gpio_group);
+ if (ret) {
+ dev_err(dev, "unable to register gpio attributes\n");
+ sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void ltc4245_sysfs_remove_groups(struct i2c_client *client)
+{
+ struct ltc4245_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+
+ if (data->use_extra_gpios)
+ sysfs_remove_group(&dev->kobj, &ltc4245_gpio_group);
+
+ sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
+}
+
+static bool ltc4245_use_extra_gpios(struct i2c_client *client)
+{
+ struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
+#ifdef CONFIG_OF
+ struct device_node *np = client->dev.of_node;
+#endif
+
+ /* prefer platform data */
+ if (pdata)
+ return pdata->use_extra_gpios;
+
+#ifdef CONFIG_OF
+ /* fallback on OF */
+ if (of_find_property(np, "ltc4245,use-extra-gpios", NULL))
+ return true;
+#endif
+
+ return false;
+}
+
static int ltc4245_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -367,15 +524,16 @@ static int ltc4245_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
+ data->use_extra_gpios = ltc4245_use_extra_gpios(client);
/* Initialize the LTC4245 chip */
i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
/* Register sysfs hooks */
- ret = sysfs_create_group(&client->dev.kobj, &ltc4245_group);
+ ret = ltc4245_sysfs_create_groups(client);
if (ret)
- goto out_sysfs_create_group;
+ goto out_sysfs_create_groups;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -386,8 +544,8 @@ static int ltc4245_probe(struct i2c_client *client,
return 0;
out_hwmon_device_register:
- sysfs_remove_group(&client->dev.kobj, &ltc4245_group);
-out_sysfs_create_group:
+ ltc4245_sysfs_remove_groups(client);
+out_sysfs_create_groups:
kfree(data);
out_kzalloc:
return ret;
@@ -398,8 +556,7 @@ static int ltc4245_remove(struct i2c_client *client)
struct ltc4245_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &ltc4245_group);
-
+ ltc4245_sysfs_remove_groups(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index ce3c7bc..d5226c9 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -18,7 +18,7 @@
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
#include <linux/platform_device.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
@@ -144,6 +144,14 @@ static const struct attribute_group mc13783_group_ts = {
.attrs = mc13783_attr_ts,
};
+static int mc13783_adc_use_touchscreen(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ unsigned flags = mc13783_get_flags(priv->mc13783);
+
+ return flags & MC13783_USE_TOUCHSCREEN;
+}
+
static int __init mc13783_adc_probe(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv;
@@ -162,10 +170,11 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
if (ret)
goto out_err_create1;
- if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ if (!mc13783_adc_use_touchscreen(pdev)) {
ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
if (ret)
goto out_err_create2;
+ }
priv->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(priv->hwmon_dev)) {
@@ -180,7 +189,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
out_err_register:
- if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ if (!mc13783_adc_use_touchscreen(pdev))
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
out_err_create2:
@@ -199,7 +208,7 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev)
hwmon_device_unregister(priv->hwmon_dev);
- if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ if (!mc13783_adc_use_touchscreen(pdev))
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index 4a64b85..68e69a4 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -1610,11 +1610,8 @@ static struct pc87360_data *pc87360_update_device(struct device *dev)
static int __init pc87360_device_add(unsigned short address)
{
- struct resource res = {
- .name = "pc87360",
- .flags = IORESOURCE_IO,
- };
- int err, i;
+ struct resource res[3];
+ int err, i, res_count;
pdev = platform_device_alloc("pc87360", address);
if (!pdev) {
@@ -1623,22 +1620,28 @@ static int __init pc87360_device_add(unsigned short address)
goto exit;
}
+ memset(res, 0, 3 * sizeof(struct resource));
+ res_count = 0;
for (i = 0; i < 3; i++) {
if (!extra_isa[i])
continue;
- res.start = extra_isa[i];
- res.end = extra_isa[i] + PC87360_EXTENT - 1;
+ res[res_count].start = extra_isa[i];
+ res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
+ res[res_count].name = "pc87360",
+ res[res_count].flags = IORESOURCE_IO,
- err = acpi_check_resource_conflict(&res);
+ err = acpi_check_resource_conflict(&res[res_count]);
if (err)
goto exit_device_put;
- err = platform_device_add_resources(pdev, &res, 1);
- if (err) {
- printk(KERN_ERR "pc87360: Device resource[%d] "
- "addition failed (%d)\n", i, err);
- goto exit_device_put;
- }
+ res_count++;
+ }
+
+ err = platform_device_add_resources(pdev, res, res_count);
+ if (err) {
+ printk(KERN_ERR "pc87360: Device resources addition failed "
+ "(%d)\n", err);
+ goto exit_device_put;
}
err = platform_device_add(pdev);
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c
index 3170b26..9ec4daa 100644
--- a/drivers/hwmon/pc87427.c
+++ b/drivers/hwmon/pc87427.c
@@ -1,7 +1,7 @@
/*
* pc87427.c - hardware monitoring driver for the
* National Semiconductor PC87427 Super-I/O chip
- * Copyright (C) 2006 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2006, 2008, 2010 Jean Delvare <khali@linux-fr.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
@@ -15,10 +15,11 @@
* Supports the following chips:
*
* Chip #vin #fan #pwm #temp devid
- * PC87427 - 8 - - 0xF2
+ * PC87427 - 8 4 6 0xF2
*
* This driver assumes that no more than one chip is present.
- * Only fan inputs are supported so far, although the chip can do much more.
+ * Only fans are fully supported so far. Temperatures are in read-only
+ * mode, and voltages aren't supported at all.
*/
#include <linux/module.h>
@@ -57,6 +58,25 @@ struct pc87427_data {
u16 fan[8]; /* register values */
u16 fan_min[8]; /* register values */
u8 fan_status[8]; /* register values */
+
+ u8 pwm_enabled; /* bit vector */
+ u8 pwm_auto_ok; /* bit vector */
+ u8 pwm_enable[4]; /* register values */
+ u8 pwm[4]; /* register values */
+
+ u8 temp_enabled; /* bit vector */
+ s16 temp[6]; /* register values */
+ s8 temp_min[6]; /* register values */
+ s8 temp_max[6]; /* register values */
+ s8 temp_crit[6]; /* register values */
+ u8 temp_status[6]; /* register values */
+ u8 temp_type[6]; /* register values */
+};
+
+struct pc87427_sio_data {
+ unsigned short address[2];
+ u8 has_fanin;
+ u8 has_fanout;
};
/*
@@ -65,6 +85,13 @@ struct pc87427_data {
#define SIOREG_LDSEL 0x07 /* Logical device select */
#define SIOREG_DEVID 0x20 /* Device ID */
+#define SIOREG_CF2 0x22 /* Configuration 2 */
+#define SIOREG_CF3 0x23 /* Configuration 3 */
+#define SIOREG_CF4 0x24 /* Configuration 4 */
+#define SIOREG_CF5 0x25 /* Configuration 5 */
+#define SIOREG_CFB 0x2B /* Configuration B */
+#define SIOREG_CFC 0x2C /* Configuration C */
+#define SIOREG_CFD 0x2D /* Configuration D */
#define SIOREG_ACT 0x30 /* Device activation */
#define SIOREG_MAP 0x50 /* I/O or memory mapping */
#define SIOREG_IOBASE 0x60 /* I/O base address */
@@ -102,6 +129,8 @@ static inline void superio_exit(int sioaddr)
#define BANK_FM(nr) (nr)
#define BANK_FT(nr) (0x08 + (nr))
#define BANK_FC(nr) (0x10 + (nr) * 2)
+#define BANK_TM(nr) (nr)
+#define BANK_VM(nr) (0x08 + (nr))
/*
* I/O access functions
@@ -179,6 +208,127 @@ static inline u16 fan_to_reg(unsigned long val)
}
/*
+ * PWM registers and conversions
+ */
+
+#define PC87427_REG_PWM_ENABLE 0x10
+#define PC87427_REG_PWM_DUTY 0x12
+
+#define PWM_ENABLE_MODE_MASK (7 << 4)
+#define PWM_ENABLE_CTLEN (1 << 0)
+
+#define PWM_MODE_MANUAL (0 << 4)
+#define PWM_MODE_AUTO (1 << 4)
+#define PWM_MODE_OFF (2 << 4)
+#define PWM_MODE_ON (7 << 4)
+
+/* Dedicated function to read all registers related to a given PWM output.
+ This saves us quite a few locks and bank selections.
+ Must be called with data->lock held.
+ nr is from 0 to 3 */
+static void pc87427_readall_pwm(struct pc87427_data *data, u8 nr)
+{
+ int iobase = data->address[LD_FAN];
+
+ outb(BANK_FC(nr), iobase + PC87427_REG_BANK);
+ data->pwm_enable[nr] = inb(iobase + PC87427_REG_PWM_ENABLE);
+ data->pwm[nr] = inb(iobase + PC87427_REG_PWM_DUTY);
+}
+
+static inline int pwm_enable_from_reg(u8 reg)
+{
+ switch (reg & PWM_ENABLE_MODE_MASK) {
+ case PWM_MODE_ON:
+ return 0;
+ case PWM_MODE_MANUAL:
+ case PWM_MODE_OFF:
+ return 1;
+ case PWM_MODE_AUTO:
+ return 2;
+ default:
+ return -EPROTO;
+ }
+}
+
+static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval)
+{
+ switch (val) {
+ default:
+ return PWM_MODE_ON;
+ case 1:
+ return pwmval ? PWM_MODE_MANUAL : PWM_MODE_OFF;
+ case 2:
+ return PWM_MODE_AUTO;
+ }
+}
+
+/*
+ * Temperature registers and conversions
+ */
+
+#define PC87427_REG_TEMP_STATUS 0x10
+#define PC87427_REG_TEMP 0x14
+#define PC87427_REG_TEMP_MAX 0x18
+#define PC87427_REG_TEMP_MIN 0x19
+#define PC87427_REG_TEMP_CRIT 0x1a
+#define PC87427_REG_TEMP_TYPE 0x1d
+
+#define TEMP_STATUS_CHANEN (1 << 0)
+#define TEMP_STATUS_LOWFLG (1 << 1)
+#define TEMP_STATUS_HIGHFLG (1 << 2)
+#define TEMP_STATUS_CRITFLG (1 << 3)
+#define TEMP_STATUS_SENSERR (1 << 5)
+#define TEMP_TYPE_MASK (3 << 5)
+
+#define TEMP_TYPE_THERMISTOR (1 << 5)
+#define TEMP_TYPE_REMOTE_DIODE (2 << 5)
+#define TEMP_TYPE_LOCAL_DIODE (3 << 5)
+
+/* Dedicated function to read all registers related to a given temperature
+ input. This saves us quite a few locks and bank selections.
+ Must be called with data->lock held.
+ nr is from 0 to 5 */
+static void pc87427_readall_temp(struct pc87427_data *data, u8 nr)
+{
+ int iobase = data->address[LD_TEMP];
+
+ outb(BANK_TM(nr), iobase + PC87427_REG_BANK);
+ data->temp[nr] = le16_to_cpu(inw(iobase + PC87427_REG_TEMP));
+ data->temp_max[nr] = inb(iobase + PC87427_REG_TEMP_MAX);
+ data->temp_min[nr] = inb(iobase + PC87427_REG_TEMP_MIN);
+ data->temp_crit[nr] = inb(iobase + PC87427_REG_TEMP_CRIT);
+ data->temp_type[nr] = inb(iobase + PC87427_REG_TEMP_TYPE);
+ data->temp_status[nr] = inb(iobase + PC87427_REG_TEMP_STATUS);
+ /* Clear fan alarm bits */
+ outb(data->temp_status[nr], iobase + PC87427_REG_TEMP_STATUS);
+}
+
+static inline unsigned int temp_type_from_reg(u8 reg)
+{
+ switch (reg & TEMP_TYPE_MASK) {
+ case TEMP_TYPE_THERMISTOR:
+ return 4;
+ case TEMP_TYPE_REMOTE_DIODE:
+ case TEMP_TYPE_LOCAL_DIODE:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+/* We assume 8-bit thermal sensors; 9-bit thermal sensors are possible
+ too, but I have no idea how to figure out when they are used. */
+static inline long temp_from_reg(s16 reg)
+{
+ return reg * 1000 / 256;
+}
+
+static inline long temp_from_reg8(s8 reg)
+{
+ return reg * 1000;
+}
+
+/*
* Data interface
*/
@@ -198,6 +348,21 @@ static struct pc87427_data *pc87427_update_device(struct device *dev)
continue;
pc87427_readall_fan(data, i);
}
+
+ /* PWM outputs */
+ for (i = 0; i < 4; i++) {
+ if (!(data->pwm_enabled & (1 << i)))
+ continue;
+ pc87427_readall_pwm(data, i);
+ }
+
+ /* Temperature channels */
+ for (i = 0; i < 6; i++) {
+ if (!(data->temp_enabled & (1 << i)))
+ continue;
+ pc87427_readall_temp(data, i);
+ }
+
data->last_updated = jiffies;
done:
@@ -208,9 +373,8 @@ done:
static ssize_t show_fan_input(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev);
- int nr = attr->index;
+ int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr]));
}
@@ -218,9 +382,8 @@ static ssize_t show_fan_input(struct device *dev, struct device_attribute
static ssize_t show_fan_min(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev);
- int nr = attr->index;
+ int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr]));
}
@@ -228,9 +391,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute
static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev);
- int nr = attr->index;
+ int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->fan_status[nr]
& FAN_STATUS_LOSPD));
@@ -239,9 +401,8 @@ static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
static ssize_t show_fan_fault(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev);
- int nr = attr->index;
+ int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->fan_status[nr]
& FAN_STATUS_STALL));
@@ -251,11 +412,13 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct pc87427_data *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- int nr = attr->index;
- unsigned long val = simple_strtoul(buf, NULL, 10);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ unsigned long val;
int iobase = data->address[LD_FAN];
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
mutex_lock(&data->lock);
outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
/* The low speed limit registers are read-only while monitoring
@@ -377,6 +540,390 @@ static const struct attribute_group pc87427_group_fan[8] = {
{ .attrs = pc87427_attributes_fan[7] },
};
+/* Must be called with data->lock held and pc87427_readall_pwm() freshly
+ called */
+static void update_pwm_enable(struct pc87427_data *data, int nr, u8 mode)
+{
+ int iobase = data->address[LD_FAN];
+ data->pwm_enable[nr] &= ~PWM_ENABLE_MODE_MASK;
+ data->pwm_enable[nr] |= mode;
+ outb(data->pwm_enable[nr], iobase + PC87427_REG_PWM_ENABLE);
+}
+
+static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ int pwm_enable;
+
+ pwm_enable = pwm_enable_from_reg(data->pwm_enable[nr]);
+ if (pwm_enable < 0)
+ return pwm_enable;
+ return sprintf(buf, "%d\n", pwm_enable);
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) < 0 || val > 2)
+ return -EINVAL;
+ /* Can't go to automatic mode if it isn't configured */
+ if (val == 2 && !(data->pwm_auto_ok & (1 << nr)))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ pc87427_readall_pwm(data, nr);
+ update_pwm_enable(data, nr, pwm_enable_to_reg(val, data->pwm[nr]));
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", (int)data->pwm[nr]);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ unsigned long val;
+ int iobase = data->address[LD_FAN];
+ u8 mode;
+
+ if (strict_strtoul(buf, 10, &val) < 0 || val > 0xff)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ pc87427_readall_pwm(data, nr);
+ mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK;
+ if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) {
+ dev_notice(dev, "Can't set PWM%d duty cycle while not in "
+ "manual mode\n", nr + 1);
+ mutex_unlock(&data->lock);
+ return -EPERM;
+ }
+
+ /* We may have to change the mode */
+ if (mode == PWM_MODE_MANUAL && val == 0) {
+ /* Transition from Manual to Off */
+ update_pwm_enable(data, nr, PWM_MODE_OFF);
+ mode = PWM_MODE_OFF;
+ dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
+ "manual", "off");
+ } else if (mode == PWM_MODE_OFF && val != 0) {
+ /* Transition from Off to Manual */
+ update_pwm_enable(data, nr, PWM_MODE_MANUAL);
+ mode = PWM_MODE_MANUAL;
+ dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
+ "off", "manual");
+ }
+
+ data->pwm[nr] = val;
+ if (mode == PWM_MODE_MANUAL)
+ outb(val, iobase + PC87427_REG_PWM_DUTY);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+ show_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+ show_pwm_enable, set_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
+ show_pwm_enable, set_pwm_enable, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+
+static struct attribute *pc87427_attributes_pwm[4][3] = {
+ {
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group pc87427_group_pwm[4] = {
+ { .attrs = pc87427_attributes_pwm[0] },
+ { .attrs = pc87427_attributes_pwm[1] },
+ { .attrs = pc87427_attributes_pwm[2] },
+ { .attrs = pc87427_attributes_pwm[3] },
+};
+
+static ssize_t show_temp_input(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr]));
+}
+
+static ssize_t show_temp_min(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_min[nr]));
+}
+
+static ssize_t show_temp_max(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_max[nr]));
+}
+
+static ssize_t show_temp_crit(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_crit[nr]));
+}
+
+static ssize_t show_temp_type(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%u\n", temp_type_from_reg(data->temp_type[nr]));
+}
+
+static ssize_t show_temp_min_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_LOWFLG));
+}
+
+static ssize_t show_temp_max_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_HIGHFLG));
+}
+
+static ssize_t show_temp_crit_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_CRITFLG));
+}
+
+static ssize_t show_temp_fault(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_SENSERR));
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_min, S_IRUGO, show_temp_min, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_max, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_type, S_IRUGO, show_temp_type, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_type, S_IRUGO, show_temp_type, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_type, S_IRUGO, show_temp_type, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO,
+ show_temp_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO,
+ show_temp_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO,
+ show_temp_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO,
+ show_temp_min_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO,
+ show_temp_min_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_min_alarm, S_IRUGO,
+ show_temp_min_alarm, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
+ show_temp_max_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO,
+ show_temp_max_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO,
+ show_temp_max_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO,
+ show_temp_max_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO,
+ show_temp_max_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO,
+ show_temp_max_alarm, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
+ show_temp_crit_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO,
+ show_temp_crit_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO,
+ show_temp_crit_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO,
+ show_temp_crit_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO,
+ show_temp_crit_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO,
+ show_temp_crit_alarm, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5);
+
+static struct attribute *pc87427_attributes_temp[6][10] = {
+ {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_type.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_type.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_min.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit.dev_attr.attr,
+ &sensor_dev_attr_temp6_type.dev_attr.attr,
+ &sensor_dev_attr_temp6_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_fault.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group pc87427_group_temp[6] = {
+ { .attrs = pc87427_attributes_temp[0] },
+ { .attrs = pc87427_attributes_temp[1] },
+ { .attrs = pc87427_attributes_temp[2] },
+ { .attrs = pc87427_attributes_temp[3] },
+ { .attrs = pc87427_attributes_temp[4] },
+ { .attrs = pc87427_attributes_temp[5] },
+};
+
static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
@@ -391,8 +938,49 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
* Device detection, attach and detach
*/
+static void pc87427_release_regions(struct platform_device *pdev, int count)
+{
+ struct resource *res;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IO, i);
+ release_region(res->start, resource_size(res));
+ }
+}
+
+static int __devinit pc87427_request_regions(struct platform_device *pdev,
+ int count)
+{
+ struct resource *res;
+ int i, err = 0;
+
+ for (i = 0; i < count; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IO, i);
+ if (!res) {
+ err = -ENOENT;
+ dev_err(&pdev->dev, "Missing resource #%d\n", i);
+ break;
+ }
+ if (!request_region(res->start, resource_size(res), DRVNAME)) {
+ err = -EBUSY;
+ dev_err(&pdev->dev,
+ "Failed to request region 0x%lx-0x%lx\n",
+ (unsigned long)res->start,
+ (unsigned long)res->end);
+ break;
+ }
+ }
+
+ if (err && i)
+ pc87427_release_regions(pdev, i);
+
+ return err;
+}
+
static void __devinit pc87427_init_device(struct device *dev)
{
+ struct pc87427_sio_data *sio_data = dev->platform_data;
struct pc87427_data *data = dev_get_drvdata(dev);
int i;
u8 reg;
@@ -400,10 +988,12 @@ static void __devinit pc87427_init_device(struct device *dev)
/* The FMC module should be ready */
reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK);
if (!(reg & 0x80))
- dev_warn(dev, "FMC module not ready!\n");
+ dev_warn(dev, "%s module not ready!\n", "FMC");
/* Check which fans are enabled */
for (i = 0; i < 8; i++) {
+ if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
+ continue;
reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i),
PC87427_REG_FAN_STATUS);
if (reg & FAN_STATUS_MONEN)
@@ -411,37 +1001,93 @@ static void __devinit pc87427_init_device(struct device *dev)
}
if (!data->fan_enabled) {
- dev_dbg(dev, "Enabling all fan inputs\n");
- for (i = 0; i < 8; i++)
+ dev_dbg(dev, "Enabling monitoring of all fans\n");
+ for (i = 0; i < 8; i++) {
+ if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
+ continue;
pc87427_write8_bank(data, LD_FAN, BANK_FM(i),
PC87427_REG_FAN_STATUS,
FAN_STATUS_MONEN);
- data->fan_enabled = 0xff;
+ }
+ data->fan_enabled = sio_data->has_fanin;
+ }
+
+ /* Check which PWM outputs are enabled */
+ for (i = 0; i < 4; i++) {
+ if (!(sio_data->has_fanout & (1 << i))) /* Not wired */
+ continue;
+ reg = pc87427_read8_bank(data, LD_FAN, BANK_FC(i),
+ PC87427_REG_PWM_ENABLE);
+ if (reg & PWM_ENABLE_CTLEN)
+ data->pwm_enabled |= (1 << i);
+
+ /* We don't expose an interface to reconfigure the automatic
+ fan control mode, so only allow to return to this mode if
+ it was originally set. */
+ if ((reg & PWM_ENABLE_MODE_MASK) == PWM_MODE_AUTO) {
+ dev_dbg(dev, "PWM%d is in automatic control mode\n",
+ i + 1);
+ data->pwm_auto_ok |= (1 << i);
+ }
+ }
+
+ /* The HMC module should be ready */
+ reg = pc87427_read8(data, LD_TEMP, PC87427_REG_BANK);
+ if (!(reg & 0x80))
+ dev_warn(dev, "%s module not ready!\n", "HMC");
+
+ /* Check which temperature channels are enabled */
+ for (i = 0; i < 6; i++) {
+ reg = pc87427_read8_bank(data, LD_TEMP, BANK_TM(i),
+ PC87427_REG_TEMP_STATUS);
+ if (reg & TEMP_STATUS_CHANEN)
+ data->temp_enabled |= (1 << i);
+ }
+}
+
+static void pc87427_remove_files(struct device *dev)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int i;
+
+ device_remove_file(dev, &dev_attr_name);
+ for (i = 0; i < 8; i++) {
+ if (!(data->fan_enabled & (1 << i)))
+ continue;
+ sysfs_remove_group(&dev->kobj, &pc87427_group_fan[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ if (!(data->pwm_enabled & (1 << i)))
+ continue;
+ sysfs_remove_group(&dev->kobj, &pc87427_group_pwm[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ if (!(data->temp_enabled & (1 << i)))
+ continue;
+ sysfs_remove_group(&dev->kobj, &pc87427_group_temp[i]);
}
}
static int __devinit pc87427_probe(struct platform_device *pdev)
{
+ struct pc87427_sio_data *sio_data = pdev->dev.platform_data;
struct pc87427_data *data;
- struct resource *res;
- int i, err;
+ int i, err, res_count;
- if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) {
+ data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL);
+ if (!data) {
err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Out of memory\n");
goto exit;
}
- /* This will need to be revisited when we add support for
- temperature and voltage monitoring. */
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start, resource_size(res), DRVNAME)) {
- err = -EBUSY;
- dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n",
- (unsigned long)res->start, (unsigned long)res->end);
+ data->address[0] = sio_data->address[0];
+ data->address[1] = sio_data->address[1];
+ res_count = (data->address[0] != 0) + (data->address[1] != 0);
+
+ err = pc87427_request_regions(pdev, res_count);
+ if (err)
goto exit_kfree;
- }
- data->address[0] = res->start;
mutex_init(&data->lock);
data->name = "pc87427";
@@ -449,13 +1095,31 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
pc87427_init_device(&pdev->dev);
/* Register sysfs hooks */
- if ((err = device_create_file(&pdev->dev, &dev_attr_name)))
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
goto exit_release_region;
for (i = 0; i < 8; i++) {
if (!(data->fan_enabled & (1 << i)))
continue;
- if ((err = sysfs_create_group(&pdev->dev.kobj,
- &pc87427_group_fan[i])))
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &pc87427_group_fan[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ for (i = 0; i < 4; i++) {
+ if (!(data->pwm_enabled & (1 << i)))
+ continue;
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &pc87427_group_pwm[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ for (i = 0; i < 6; i++) {
+ if (!(data->temp_enabled & (1 << i)))
+ continue;
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &pc87427_group_temp[i]);
+ if (err)
goto exit_remove_files;
}
@@ -469,13 +1133,9 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
return 0;
exit_remove_files:
- for (i = 0; i < 8; i++) {
- if (!(data->fan_enabled & (1 << i)))
- continue;
- sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
- }
+ pc87427_remove_files(&pdev->dev);
exit_release_region:
- release_region(res->start, resource_size(res));
+ pc87427_release_regions(pdev, res_count);
exit_kfree:
platform_set_drvdata(pdev, NULL);
kfree(data);
@@ -486,21 +1146,16 @@ exit:
static int __devexit pc87427_remove(struct platform_device *pdev)
{
struct pc87427_data *data = platform_get_drvdata(pdev);
- struct resource *res;
- int i;
+ int res_count;
+
+ res_count = (data->address[0] != 0) + (data->address[1] != 0);
hwmon_device_unregister(data->hwmon_dev);
- device_remove_file(&pdev->dev, &dev_attr_name);
- for (i = 0; i < 8; i++) {
- if (!(data->fan_enabled & (1 << i)))
- continue;
- sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
- }
+ pc87427_remove_files(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(data);
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- release_region(res->start, resource_size(res));
+ pc87427_release_regions(pdev, res_count);
return 0;
}
@@ -515,34 +1170,50 @@ static struct platform_driver pc87427_driver = {
.remove = __devexit_p(pc87427_remove),
};
-static int __init pc87427_device_add(unsigned short address)
+static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data)
{
- struct resource res = {
- .start = address,
- .end = address + REGION_LENGTH - 1,
- .name = logdev_str[0],
- .flags = IORESOURCE_IO,
+ struct resource res[2] = {
+ { .flags = IORESOURCE_IO },
+ { .flags = IORESOURCE_IO },
};
- int err;
+ int err, i, res_count;
- err = acpi_check_resource_conflict(&res);
- if (err)
- goto exit;
+ res_count = 0;
+ for (i = 0; i < 2; i++) {
+ if (!sio_data->address[i])
+ continue;
+ res[res_count].start = sio_data->address[i];
+ res[res_count].end = sio_data->address[i] + REGION_LENGTH - 1;
+ res[res_count].name = logdev_str[i];
- pdev = platform_device_alloc(DRVNAME, address);
+ err = acpi_check_resource_conflict(&res[res_count]);
+ if (err)
+ goto exit;
+
+ res_count++;
+ }
+
+ pdev = platform_device_alloc(DRVNAME, res[0].start);
if (!pdev) {
err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Device allocation failed\n");
goto exit;
}
- err = platform_device_add_resources(pdev, &res, 1);
+ err = platform_device_add_resources(pdev, res, res_count);
if (err) {
printk(KERN_ERR DRVNAME ": Device resource addition failed "
"(%d)\n", err);
goto exit_device_put;
}
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct pc87427_sio_data));
+ if (err) {
+ printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
err = platform_device_add(pdev);
if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
@@ -558,9 +1229,10 @@ exit:
return err;
}
-static int __init pc87427_find(int sioaddr, unsigned short *address)
+static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data)
{
u16 val;
+ u8 cfg, cfg_b;
int i, err = 0;
/* Identify device */
@@ -571,7 +1243,7 @@ static int __init pc87427_find(int sioaddr, unsigned short *address)
}
for (i = 0; i < 2; i++) {
- address[i] = 0;
+ sio_data->address[i] = 0;
/* Select logical device */
superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]);
@@ -596,9 +1268,58 @@ static int __init pc87427_find(int sioaddr, unsigned short *address)
"for logical device 0x%02x\n", logdev[i]);
continue;
}
- address[i] = val;
+ sio_data->address[i] = val;
}
+ /* No point in loading the driver if everything is disabled */
+ if (!sio_data->address[0] && !sio_data->address[1]) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /* Check which fan inputs are wired */
+ sio_data->has_fanin = (1 << 2) | (1 << 3); /* FANIN2, FANIN3 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CF2);
+ if (!(cfg & (1 << 3)))
+ sio_data->has_fanin |= (1 << 0); /* FANIN0 */
+ if (!(cfg & (1 << 2)))
+ sio_data->has_fanin |= (1 << 4); /* FANIN4 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CFD);
+ if (!(cfg & (1 << 0)))
+ sio_data->has_fanin |= (1 << 1); /* FANIN1 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CF4);
+ if (!(cfg & (1 << 0)))
+ sio_data->has_fanin |= (1 << 7); /* FANIN7 */
+ cfg_b = superio_inb(sioaddr, SIOREG_CFB);
+ if (!(cfg & (1 << 1)) && (cfg_b & (1 << 3)))
+ sio_data->has_fanin |= (1 << 5); /* FANIN5 */
+ cfg = superio_inb(sioaddr, SIOREG_CF3);
+ if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5)))
+ sio_data->has_fanin |= (1 << 6); /* FANIN6 */
+
+ /* Check which fan outputs are wired */
+ sio_data->has_fanout = (1 << 0); /* FANOUT0 */
+ if (cfg_b & (1 << 0))
+ sio_data->has_fanout |= (1 << 3); /* FANOUT3 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CFC);
+ if (!(cfg & (1 << 4))) {
+ if (cfg_b & (1 << 1))
+ sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
+ if (cfg_b & (1 << 2))
+ sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
+ }
+
+ /* FANOUT1 and FANOUT2 can each be routed to 2 different pins */
+ cfg = superio_inb(sioaddr, SIOREG_CF5);
+ if (cfg & (1 << 6))
+ sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
+ if (cfg & (1 << 5))
+ sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
+
exit:
superio_exit(sioaddr);
return err;
@@ -607,15 +1328,10 @@ exit:
static int __init pc87427_init(void)
{
int err;
- unsigned short address[2];
-
- if (pc87427_find(0x2e, address)
- && pc87427_find(0x4e, address))
- return -ENODEV;
+ struct pc87427_sio_data sio_data;
- /* For now the driver only handles fans so we only care about the
- first address. */
- if (!address[0])
+ if (pc87427_find(0x2e, &sio_data)
+ && pc87427_find(0x4e, &sio_data))
return -ENODEV;
err = platform_driver_register(&pc87427_driver);
@@ -623,7 +1339,7 @@ static int __init pc87427_init(void)
goto exit;
/* Sets global pdev as a side effect */
- err = pc87427_device_add(address[0]);
+ err = pc87427_device_add(&sio_data);
if (err)
goto exit_driver;
diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c
index 8964326..d863e13 100644
--- a/drivers/hwmon/ultra45_env.c
+++ b/drivers/hwmon/ultra45_env.c
@@ -234,7 +234,7 @@ static const struct attribute_group env_group = {
.attrs = env_attributes,
};
-static int __devinit env_probe(struct of_device *op,
+static int __devinit env_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct env *p = kzalloc(sizeof(*p), GFP_KERNEL);
@@ -276,7 +276,7 @@ out_free:
goto out;
}
-static int __devexit env_remove(struct of_device *op)
+static int __devexit env_remove(struct platform_device *op)
{
struct env *p = dev_get_drvdata(&op->dev);
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index 7442cf7..ffb793a 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -39,7 +39,7 @@
#define DRVNAME "via_cputemp"
-enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME } SHOW;
+enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
/*
* Functions declaration
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 0dcaba9..e96e69d 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -39,6 +39,7 @@
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
+ w83667hg-b 9 5 3 3 0xb350 0xc1 0x5ca3
*/
#include <linux/module.h>
@@ -55,7 +56,7 @@
#include <linux/io.h>
#include "lm75.h"
-enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg };
+enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b };
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * w83627ehf_device_names[] = {
@@ -63,6 +64,7 @@ static const char * w83627ehf_device_names[] = {
"w83627dhg",
"w83627dhg",
"w83667hg",
+ "w83667hg",
};
static unsigned short force_id;
@@ -91,6 +93,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
#define SIO_W83627DHG_ID 0xa020
#define SIO_W83627DHG_P_ID 0xb070
#define SIO_W83667HG_ID 0xa510
+#define SIO_W83667HG_B_ID 0xb350
#define SIO_ID_MASK 0xFFF0
static inline void
@@ -201,8 +204,14 @@ static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
static const u8 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 };
static const u8 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 };
-static const u8 W83627EHF_REG_FAN_MAX_OUTPUT[] = { 0xff, 0x67, 0xff, 0x69 };
-static const u8 W83627EHF_REG_FAN_STEP_OUTPUT[] = { 0xff, 0x68, 0xff, 0x6a };
+
+static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_COMMON[]
+ = { 0xff, 0x67, 0xff, 0x69 };
+static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[]
+ = { 0xff, 0x68, 0xff, 0x6a };
+
+static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b };
+static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] = { 0x68, 0x6a, 0x6c };
/*
* Conversions
@@ -277,6 +286,11 @@ struct w83627ehf_data {
struct device *hwmon_dev;
struct mutex lock;
+ const u8 *REG_FAN_START_OUTPUT;
+ const u8 *REG_FAN_STOP_OUTPUT;
+ const u8 *REG_FAN_MAX_OUTPUT;
+ const u8 *REG_FAN_STEP_OUTPUT;
+
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
@@ -524,7 +538,10 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
}
}
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
/* pwmcfg, tolerance mapped for i=0, i=1 to same reg */
if (i != 1) {
pwmcfg = w83627ehf_read_value(data,
@@ -546,6 +563,17 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
W83627EHF_REG_FAN_STOP_OUTPUT[i]);
data->fan_stop_time[i] = w83627ehf_read_value(data,
W83627EHF_REG_FAN_STOP_TIME[i]);
+
+ if (data->REG_FAN_MAX_OUTPUT[i] != 0xff)
+ data->fan_max_output[i] =
+ w83627ehf_read_value(data,
+ data->REG_FAN_MAX_OUTPUT[i]);
+
+ if (data->REG_FAN_STEP_OUTPUT[i] != 0xff)
+ data->fan_step_output[i] =
+ w83627ehf_read_value(data,
+ data->REG_FAN_STEP_OUTPUT[i]);
+
data->target_temp[i] =
w83627ehf_read_value(data,
W83627EHF_REG_TARGET[i]) &
@@ -1126,7 +1154,7 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
mutex_lock(&data->update_lock); \
data->reg[nr] = val; \
- w83627ehf_write_value(data, W83627EHF_REG_##REG[nr], val); \
+ w83627ehf_write_value(data, data->REG_##REG[nr], val); \
mutex_unlock(&data->update_lock); \
return count; \
}
@@ -1206,12 +1234,26 @@ static struct sensor_device_attribute sda_sf3_arrays[] = {
store_fan_stop_output, 1),
SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 2),
+};
- /* pwm1 and pwm3 don't support max and step settings */
+
+/*
+ * pwm1 and pwm3 don't support max and step settings on all chips.
+ * Need to check support while generating/removing attribute files.
+ */
+static struct sensor_device_attribute sda_sf3_max_step_arrays[] = {
+ SENSOR_ATTR(pwm1_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+ store_fan_max_output, 0),
+ SENSOR_ATTR(pwm1_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+ store_fan_step_output, 0),
SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
store_fan_max_output, 1),
SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
store_fan_step_output, 1),
+ SENSOR_ATTR(pwm3_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+ store_fan_max_output, 2),
+ SENSOR_ATTR(pwm3_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+ store_fan_step_output, 2),
};
static ssize_t
@@ -1235,6 +1277,12 @@ static void w83627ehf_device_remove_files(struct device *dev)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) {
+ struct sensor_device_attribute *attr =
+ &sda_sf3_max_step_arrays[i];
+ if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff)
+ device_remove_file(dev, &attr->dev_attr);
+ }
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
for (i = 0; i < data->in_num; i++) {
@@ -1343,22 +1391,37 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
/* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
/* 667HG has 3 pwms */
- data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4;
+ data->pwm_num = (sio_data->kind == w83667hg
+ || sio_data->kind == w83667hg_b) ? 3 : 4;
/* Check temp3 configuration bit for 667HG */
- if (sio_data->kind == w83667hg) {
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
data->temp3_disable = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[1]) & 0x01;
data->in6_skip = !data->temp3_disable;
}
+ data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT;
+ data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT;
+ if (sio_data->kind == w83667hg_b) {
+ data->REG_FAN_MAX_OUTPUT =
+ W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B;
+ data->REG_FAN_STEP_OUTPUT =
+ W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B;
+ } else {
+ data->REG_FAN_MAX_OUTPUT =
+ W83627EHF_REG_FAN_MAX_OUTPUT_COMMON;
+ data->REG_FAN_STEP_OUTPUT =
+ W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
+ }
+
/* Initialize the chip */
w83627ehf_init_device(data);
data->vrm = vid_which_vrm();
superio_enter(sio_data->sioreg);
/* Read VID value */
- if (sio_data->kind == w83667hg) {
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
/* W83667HG has different pins for VID input and output, so
we can get the VID input values directly at logical device D
0xe3. */
@@ -1409,7 +1472,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
}
/* fan4 and fan5 share some pins with the GPIO and serial flash */
- if (sio_data->kind == w83667hg) {
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
} else {
@@ -1440,6 +1503,15 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
&sda_sf3_arrays[i].dev_attr)))
goto exit_remove;
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) {
+ struct sensor_device_attribute *attr =
+ &sda_sf3_max_step_arrays[i];
+ if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) {
+ err = device_create_file(dev, &attr->dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
/* if fan4 is enabled create the sf3 files for it */
if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
@@ -1556,6 +1628,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
+ static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B";
u16 val;
const char *sio_name;
@@ -1588,6 +1661,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
break;
+ case SIO_W83667HG_B_ID:
+ sio_data->kind = w83667hg_b;
+ sio_name = sio_name_W83667HG_B;
+ break;
default:
if (val != 0xffff)
pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n",
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index d06083f..30f06e9 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -47,6 +47,19 @@ config I2C_CHARDEV
This support is also available as a module. If so, the module
will be called i2c-dev.
+config I2C_MUX
+ tristate "I2C bus multiplexing support"
+ depends on EXPERIMENTAL
+ help
+ Say Y here if you want the I2C core to support the ability to
+ handle multiplexed I2C bus topologies, by presenting each
+ multiplexed segment as a I2C adapter.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-mux.
+
+source drivers/i2c/muxes/Kconfig
+
config I2C_HELPER_AUTO
bool "Autoselect pertinent helper modules"
default y
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index a7d9b4be9..c00fd66 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -6,7 +6,8 @@ obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
-obj-y += algos/ busses/
+obj-$(CONFIG_I2C_MUX) += i2c-mux.o
+obj-y += algos/ busses/ muxes/
ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bceafbf..6539ac2 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -448,6 +448,13 @@ config I2C_NOMADIK
If you say yes to this option, support will be included for the
I2C interface from ST-Ericsson's Nomadik and Ux500 architectures.
+config I2C_NUC900
+ tristate "NUC900 I2C Driver"
+ depends on ARCH_W90X900
+ help
+ Say Y here to include support for I2C controller in the
+ Winbond/Nuvoton NUC900 based System-on-Chip devices.
+
config I2C_OCORES
tristate "OpenCores I2C Controller"
depends on EXPERIMENTAL
@@ -496,8 +503,8 @@ config I2C_PMCMSP
will be called i2c-pmcmsp.
config I2C_PNX
- tristate "I2C bus support for Philips PNX targets"
- depends on ARCH_PNX4008
+ tristate "I2C bus support for Philips PNX and NXP LPC targets"
+ depends on ARCH_PNX4008 || ARCH_LPC32XX
help
This driver supports the Philips IP3204 I2C IP block master and/or
slave controller
@@ -521,12 +528,19 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
+config HAVE_S3C2410_I2C
+ bool
+ help
+ This will include I2C support for Samsung SoCs. If you want to
+ include I2C support for any machine, kindly select this in the
+ respective Kconfig file.
+
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on HAVE_S3C2410_I2C
help
Say Y here to include support for I2C controller in the
- Samsung S3C2410 based System-on-Chip devices.
+ Samsung SoCs.
config I2C_S6000
tristate "S6000 I2C support"
@@ -549,7 +563,7 @@ config I2C_SH7760
config I2C_SH_MOBILE
tristate "SuperH Mobile I2C Controller"
- depends on SUPERH
+ depends on SUPERH || ARCH_SHMOBILE
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Renesas SH-Mobile processor.
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 936880b..c3ef492 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
+obj-$(CONFIG_I2C_NUC900) += i2c-nuc900.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index e591de1..f7bd261 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -105,7 +105,7 @@ struct i2c_reg {
struct cpm_i2c {
char *base;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct i2c_adapter adap;
uint dp_addr;
int version; /* CPM1=1, CPM2=2 */
@@ -428,7 +428,7 @@ static const struct i2c_adapter cpm_ops = {
static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm)
{
- struct of_device *ofdev = cpm->ofdev;
+ struct platform_device *ofdev = cpm->ofdev;
const u32 *data;
int len, ret, i;
void __iomem *i2c_base;
@@ -634,7 +634,7 @@ static void cpm_i2c_shutdown(struct cpm_i2c *cpm)
cpm_muram_free(cpm->i2c_addr);
}
-static int __devinit cpm_i2c_probe(struct of_device *ofdev,
+static int __devinit cpm_i2c_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int result, len;
@@ -687,7 +687,7 @@ out_free:
return result;
}
-static int __devexit cpm_i2c_remove(struct of_device *ofdev)
+static int __devexit cpm_i2c_remove(struct platform_device *ofdev)
{
struct cpm_i2c *cpm = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 4523364..2222c87 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -36,14 +36,16 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/cpufreq.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
-
#include <mach/i2c.h>
/* ----- global defines ----------------------------------------------- */
#define DAVINCI_I2C_TIMEOUT (1*HZ)
+#define DAVINCI_I2C_MAX_TRIES 2
#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_AAS | \
DAVINCI_I2C_IMR_SCD | \
DAVINCI_I2C_IMR_ARDY | \
@@ -72,37 +74,29 @@
#define DAVINCI_I2C_IVR_NACK 0x02
#define DAVINCI_I2C_IVR_AL 0x01
-#define DAVINCI_I2C_STR_BB (1 << 12)
-#define DAVINCI_I2C_STR_RSFULL (1 << 11)
-#define DAVINCI_I2C_STR_SCD (1 << 5)
-#define DAVINCI_I2C_STR_ARDY (1 << 2)
-#define DAVINCI_I2C_STR_NACK (1 << 1)
-#define DAVINCI_I2C_STR_AL (1 << 0)
-
-#define DAVINCI_I2C_MDR_NACK (1 << 15)
-#define DAVINCI_I2C_MDR_STT (1 << 13)
-#define DAVINCI_I2C_MDR_STP (1 << 11)
-#define DAVINCI_I2C_MDR_MST (1 << 10)
-#define DAVINCI_I2C_MDR_TRX (1 << 9)
-#define DAVINCI_I2C_MDR_XA (1 << 8)
-#define DAVINCI_I2C_MDR_RM (1 << 7)
-#define DAVINCI_I2C_MDR_IRS (1 << 5)
-
-#define DAVINCI_I2C_IMR_AAS (1 << 6)
-#define DAVINCI_I2C_IMR_SCD (1 << 5)
-#define DAVINCI_I2C_IMR_XRDY (1 << 4)
-#define DAVINCI_I2C_IMR_RRDY (1 << 3)
-#define DAVINCI_I2C_IMR_ARDY (1 << 2)
-#define DAVINCI_I2C_IMR_NACK (1 << 1)
-#define DAVINCI_I2C_IMR_AL (1 << 0)
-
-#define MOD_REG_BIT(val, mask, set) do { \
- if (set) { \
- val |= mask; \
- } else { \
- val &= ~mask; \
- } \
-} while (0)
+#define DAVINCI_I2C_STR_BB BIT(12)
+#define DAVINCI_I2C_STR_RSFULL BIT(11)
+#define DAVINCI_I2C_STR_SCD BIT(5)
+#define DAVINCI_I2C_STR_ARDY BIT(2)
+#define DAVINCI_I2C_STR_NACK BIT(1)
+#define DAVINCI_I2C_STR_AL BIT(0)
+
+#define DAVINCI_I2C_MDR_NACK BIT(15)
+#define DAVINCI_I2C_MDR_STT BIT(13)
+#define DAVINCI_I2C_MDR_STP BIT(11)
+#define DAVINCI_I2C_MDR_MST BIT(10)
+#define DAVINCI_I2C_MDR_TRX BIT(9)
+#define DAVINCI_I2C_MDR_XA BIT(8)
+#define DAVINCI_I2C_MDR_RM BIT(7)
+#define DAVINCI_I2C_MDR_IRS BIT(5)
+
+#define DAVINCI_I2C_IMR_AAS BIT(6)
+#define DAVINCI_I2C_IMR_SCD BIT(5)
+#define DAVINCI_I2C_IMR_XRDY BIT(4)
+#define DAVINCI_I2C_IMR_RRDY BIT(3)
+#define DAVINCI_I2C_IMR_ARDY BIT(2)
+#define DAVINCI_I2C_IMR_NACK BIT(1)
+#define DAVINCI_I2C_IMR_AL BIT(0)
struct davinci_i2c_dev {
struct device *dev;
@@ -113,8 +107,13 @@ struct davinci_i2c_dev {
u8 *buf;
size_t buf_len;
int irq;
+ int stop;
u8 terminate;
struct i2c_adapter adapter;
+#ifdef CONFIG_CPU_FREQ
+ struct completion xfr_complete;
+ struct notifier_block freq_transition;
+#endif
};
/* default platform data to use if not supplied in the platform_device */
@@ -134,12 +133,59 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg)
return __raw_readw(i2c_dev->base + reg);
}
-/*
- * This functions configures I2C and brings I2C out of reset.
- * This function is called during I2C init function. This function
- * also gets called if I2C encounters any errors.
+/* Generate a pulse on the i2c clock pin. */
+static void generic_i2c_clock_pulse(unsigned int scl_pin)
+{
+ u16 i;
+
+ if (scl_pin) {
+ /* Send high and low on the SCL line */
+ for (i = 0; i < 9; i++) {
+ gpio_set_value(scl_pin, 0);
+ udelay(20);
+ gpio_set_value(scl_pin, 1);
+ udelay(20);
+ }
+ }
+}
+
+/* This routine does i2c bus recovery as specified in the
+ * i2c protocol Rev. 03 section 3.16 titled "Bus clear"
*/
-static int i2c_davinci_init(struct davinci_i2c_dev *dev)
+static void i2c_recover_bus(struct davinci_i2c_dev *dev)
+{
+ u32 flag = 0;
+ struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
+
+ dev_err(dev->dev, "initiating i2c bus recovery\n");
+ /* Send NACK to the slave */
+ flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ flag |= DAVINCI_I2C_MDR_NACK;
+ /* write the data into mode register */
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
+ if (pdata)
+ generic_i2c_clock_pulse(pdata->scl_pin);
+ /* Send STOP */
+ flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ flag |= DAVINCI_I2C_MDR_STP;
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
+}
+
+static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev,
+ int val)
+{
+ u16 w;
+
+ w = davinci_i2c_read_reg(i2c_dev, DAVINCI_I2C_MDR_REG);
+ if (!val) /* put I2C into reset */
+ w &= ~DAVINCI_I2C_MDR_IRS;
+ else /* take I2C out of reset */
+ w |= DAVINCI_I2C_MDR_IRS;
+
+ davinci_i2c_write_reg(i2c_dev, DAVINCI_I2C_MDR_REG, w);
+}
+
+static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
{
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
u16 psc;
@@ -148,15 +194,6 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
u32 clkh;
u32 clkl;
u32 input_clock = clk_get_rate(dev->clk);
- u16 w;
-
- if (!pdata)
- pdata = &davinci_i2c_platform_data_default;
-
- /* put I2C into reset */
- w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
- MOD_REG_BIT(w, DAVINCI_I2C_MDR_IRS, 0);
- davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
/* NOTE: I2C Clock divider programming info
* As per I2C specs the following formulas provide prescaler
@@ -188,12 +225,32 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh);
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl);
+ dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk);
+}
+
+/*
+ * This function configures I2C and brings I2C out of reset.
+ * This function is called during I2C init function. This function
+ * also gets called if I2C encounters any errors.
+ */
+static int i2c_davinci_init(struct davinci_i2c_dev *dev)
+{
+ struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
+
+ if (!pdata)
+ pdata = &davinci_i2c_platform_data_default;
+
+ /* put I2C into reset */
+ davinci_i2c_reset_ctrl(dev, 0);
+
+ /* compute clock dividers */
+ i2c_davinci_calc_clk_dividers(dev);
+
/* Respond at reserved "SMBus Host" slave address" (and zero);
* we seem to have no option to not respond...
*/
davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, 0x08);
- dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk);
dev_dbg(dev->dev, "PSC = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG));
dev_dbg(dev->dev, "CLKL = %d\n",
@@ -204,9 +261,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
pdata->bus_freq, pdata->bus_delay);
/* Take the I2C module out of reset: */
- w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
- MOD_REG_BIT(w, DAVINCI_I2C_MDR_IRS, 1);
- davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
+ davinci_i2c_reset_ctrl(dev, 1);
/* Enable interrupts */
davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, I2C_DAVINCI_INTR_ALL);
@@ -221,14 +276,22 @@ static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev,
char allow_sleep)
{
unsigned long timeout;
+ static u16 to_cnt;
timeout = jiffies + dev->adapter.timeout;
while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG)
& DAVINCI_I2C_STR_BB) {
- if (time_after(jiffies, timeout)) {
- dev_warn(dev->dev,
- "timeout waiting for bus ready\n");
- return -ETIMEDOUT;
+ if (to_cnt <= DAVINCI_I2C_MAX_TRIES) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(dev->dev,
+ "timeout waiting for bus ready\n");
+ to_cnt++;
+ return -ETIMEDOUT;
+ } else {
+ to_cnt = 0;
+ i2c_recover_bus(dev);
+ i2c_davinci_init(dev);
+ }
}
if (allow_sleep)
schedule_timeout(1);
@@ -250,9 +313,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
u16 w;
int r;
- if (msg->len == 0)
- return -EINVAL;
-
if (!pdata)
pdata = &davinci_i2c_platform_data_default;
/* Introduce a delay, required for some boards (e.g Davinci EVM) */
@@ -264,6 +324,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
dev->buf = msg->buf;
dev->buf_len = msg->len;
+ dev->stop = stop;
davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len);
@@ -281,23 +342,40 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
flag |= DAVINCI_I2C_MDR_TRX;
if (stop)
flag |= DAVINCI_I2C_MDR_STP;
+ if (msg->len == 0) {
+ flag |= DAVINCI_I2C_MDR_RM;
+ flag &= ~DAVINCI_I2C_MDR_STP;
+ }
/* Enable receive or transmit interrupts */
w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG);
if (msg->flags & I2C_M_RD)
- MOD_REG_BIT(w, DAVINCI_I2C_IMR_RRDY, 1);
+ w |= DAVINCI_I2C_IMR_RRDY;
else
- MOD_REG_BIT(w, DAVINCI_I2C_IMR_XRDY, 1);
+ w |= DAVINCI_I2C_IMR_XRDY;
davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w);
dev->terminate = 0;
+
/* write the data into mode register */
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
+ /*
+ * First byte should be set here, not after interrupt,
+ * because transmit-data-ready interrupt can come before
+ * NACK-interrupt during sending of previous message and
+ * ICDXR may have wrong data
+ */
+ if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++);
+ dev->buf_len--;
+ }
+
r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
dev->adapter.timeout);
if (r == 0) {
dev_err(dev->dev, "controller timed out\n");
+ i2c_recover_bus(dev);
i2c_davinci_init(dev);
dev->buf_len = 0;
return -ETIMEDOUT;
@@ -334,7 +412,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
return msg->len;
if (stop) {
w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
- MOD_REG_BIT(w, DAVINCI_I2C_MDR_STP, 1);
+ w |= DAVINCI_I2C_MDR_STP;
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
}
return -EREMOTEIO;
@@ -367,12 +445,17 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (ret < 0)
return ret;
}
+
+#ifdef CONFIG_CPU_FREQ
+ complete(&dev->xfr_complete);
+#endif
+
return num;
}
static u32 i2c_davinci_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static void terminate_read(struct davinci_i2c_dev *dev)
@@ -431,6 +514,14 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
case DAVINCI_I2C_IVR_ARDY:
davinci_i2c_write_reg(dev,
DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ARDY);
+ if (((dev->buf_len == 0) && (dev->stop != 0)) ||
+ (dev->cmd_err & DAVINCI_I2C_STR_NACK)) {
+ w = davinci_i2c_read_reg(dev,
+ DAVINCI_I2C_MDR_REG);
+ w |= DAVINCI_I2C_MDR_STP;
+ davinci_i2c_write_reg(dev,
+ DAVINCI_I2C_MDR_REG, w);
+ }
complete(&dev->cmd_complete);
break;
@@ -462,7 +553,7 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
w = davinci_i2c_read_reg(dev,
DAVINCI_I2C_IMR_REG);
- MOD_REG_BIT(w, DAVINCI_I2C_IMR_XRDY, 0);
+ w &= ~DAVINCI_I2C_IMR_XRDY;
davinci_i2c_write_reg(dev,
DAVINCI_I2C_IMR_REG,
w);
@@ -491,6 +582,48 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
return count ? IRQ_HANDLED : IRQ_NONE;
}
+#ifdef CONFIG_CPU_FREQ
+static int i2c_davinci_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct davinci_i2c_dev *dev;
+
+ dev = container_of(nb, struct davinci_i2c_dev, freq_transition);
+ if (val == CPUFREQ_PRECHANGE) {
+ wait_for_completion(&dev->xfr_complete);
+ davinci_i2c_reset_ctrl(dev, 0);
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ i2c_davinci_calc_clk_dividers(dev);
+ davinci_i2c_reset_ctrl(dev, 1);
+ }
+
+ return 0;
+}
+
+static inline int i2c_davinci_cpufreq_register(struct davinci_i2c_dev *dev)
+{
+ dev->freq_transition.notifier_call = i2c_davinci_cpufreq_transition;
+
+ return cpufreq_register_notifier(&dev->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev)
+{
+ cpufreq_unregister_notifier(&dev->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+#else
+static inline int i2c_davinci_cpufreq_register(struct davinci_i2c_dev *dev)
+{
+ return 0;
+}
+
+static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev)
+{
+}
+#endif
+
static struct i2c_algorithm i2c_davinci_algo = {
.master_xfer = i2c_davinci_xfer,
.functionality = i2c_davinci_func,
@@ -530,6 +663,9 @@ static int davinci_i2c_probe(struct platform_device *pdev)
}
init_completion(&dev->cmd_complete);
+#ifdef CONFIG_CPU_FREQ
+ init_completion(&dev->xfr_complete);
+#endif
dev->dev = get_device(&pdev->dev);
dev->irq = irq->start;
platform_set_drvdata(pdev, dev);
@@ -541,7 +677,12 @@ static int davinci_i2c_probe(struct platform_device *pdev)
}
clk_enable(dev->clk);
- dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+ dev->base = ioremap(mem->start, resource_size(mem));
+ if (!dev->base) {
+ r = -EBUSY;
+ goto err_mem_ioremap;
+ }
+
i2c_davinci_init(dev);
r = request_irq(dev->irq, i2c_davinci_isr, 0, pdev->name, dev);
@@ -550,6 +691,12 @@ static int davinci_i2c_probe(struct platform_device *pdev)
goto err_unuse_clocks;
}
+ r = i2c_davinci_cpufreq_register(dev);
+ if (r) {
+ dev_err(&pdev->dev, "failed to register cpufreq\n");
+ goto err_free_irq;
+ }
+
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
@@ -571,6 +718,8 @@ static int davinci_i2c_probe(struct platform_device *pdev)
err_free_irq:
free_irq(dev->irq, dev);
err_unuse_clocks:
+ iounmap(dev->base);
+err_mem_ioremap:
clk_disable(dev->clk);
clk_put(dev->clk);
dev->clk = NULL;
@@ -589,6 +738,8 @@ static int davinci_i2c_remove(struct platform_device *pdev)
struct davinci_i2c_dev *dev = platform_get_drvdata(pdev);
struct resource *mem;
+ i2c_davinci_cpufreq_deregister(dev);
+
platform_set_drvdata(pdev, NULL);
i2c_del_adapter(&dev->adapter);
put_device(&pdev->dev);
@@ -599,6 +750,7 @@ static int davinci_i2c_remove(struct platform_device *pdev)
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, 0);
free_irq(IRQ_I2C, dev);
+ iounmap(dev->base);
kfree(dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -606,6 +758,41 @@ static int davinci_i2c_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int davinci_i2c_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ /* put I2C into reset */
+ davinci_i2c_reset_ctrl(i2c_dev, 0);
+ clk_disable(i2c_dev->clk);
+
+ return 0;
+}
+
+static int davinci_i2c_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ clk_enable(i2c_dev->clk);
+ /* take I2C out of reset */
+ davinci_i2c_reset_ctrl(i2c_dev, 1);
+
+ return 0;
+}
+
+static const struct dev_pm_ops davinci_i2c_pm = {
+ .suspend = davinci_i2c_suspend,
+ .resume = davinci_i2c_resume,
+};
+
+#define davinci_i2c_pm_ops (&davinci_i2c_pm)
+#else
+#define davinci_i2c_pm_ops NULL
+#endif
+
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:i2c_davinci");
@@ -615,6 +802,7 @@ static struct platform_driver davinci_i2c_driver = {
.driver = {
.name = "i2c_davinci",
.owner = THIS_MODULE,
+ .pm = davinci_i2c_pm_ops,
},
};
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 1168d61..43ca32f 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -661,7 +661,7 @@ static inline u8 iic_clckdiv(unsigned int opb)
return (u8)((opb + 9) / 10 - 1);
}
-static int __devinit iic_request_irq(struct of_device *ofdev,
+static int __devinit iic_request_irq(struct platform_device *ofdev,
struct ibm_iic_private *dev)
{
struct device_node *np = ofdev->dev.of_node;
@@ -692,7 +692,7 @@ static int __devinit iic_request_irq(struct of_device *ofdev,
/*
* Register single IIC interface
*/
-static int __devinit iic_probe(struct of_device *ofdev,
+static int __devinit iic_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -780,7 +780,7 @@ error_cleanup:
/*
* Cleanup initialized IIC interface
*/
-static int __devexit iic_remove(struct of_device *ofdev)
+static int __devexit iic_remove(struct platform_device *ofdev)
{
struct ibm_iic_private *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 6545d1c..a1c419a 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -560,7 +560,7 @@ static struct i2c_adapter mpc_ops = {
.timeout = HZ,
};
-static int __devinit fsl_i2c_probe(struct of_device *op,
+static int __devinit fsl_i2c_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mpc_i2c *i2c;
@@ -646,7 +646,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op,
return result;
};
-static int __devexit fsl_i2c_remove(struct of_device *op)
+static int __devexit fsl_i2c_remove(struct platform_device *op)
{
struct mpc_i2c *i2c = dev_get_drvdata(&op->dev);
diff --git a/drivers/i2c/busses/i2c-nuc900.c b/drivers/i2c/busses/i2c-nuc900.c
new file mode 100644
index 0000000..92d770d
--- /dev/null
+++ b/drivers/i2c/busses/i2c-nuc900.c
@@ -0,0 +1,709 @@
+/*
+ * linux/drivers/i2c/busses/i2c-nuc900.c
+ *
+ * Copyright (c) 2010 Nuvoton technology corporation.
+ *
+ * This driver based on S3C2410 I2C driver of Ben Dooks <ben-Y5A6D6n0/KfQXOPxS62xeg@public.gmane.org>.
+ * Written by Wan ZongShun <mcuos.com-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.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;version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <mach/mfp.h>
+#include <mach/i2c.h>
+
+/* nuc900 i2c registers offset */
+
+#define CSR 0x00
+#define DIVIDER 0x04
+#define CMDR 0x08
+#define SWR 0x0C
+#define RXR 0x10
+#define TXR 0x14
+
+/* nuc900 i2c CSR register bits */
+
+#define IRQEN 0x003
+#define I2CBUSY 0x400
+#define I2CSTART 0x018
+#define IRQFLAG 0x004
+#define ARBIT_LOST 0x200
+#define SLAVE_ACK 0x800
+
+/* nuc900 i2c CMDR register bits */
+
+#define I2C_CMD_START 0x10
+#define I2C_CMD_STOP 0x08
+#define I2C_CMD_READ 0x04
+#define I2C_CMD_WRITE 0x02
+#define I2C_CMD_NACK 0x01
+
+/* i2c controller state */
+
+enum nuc900_i2c_state {
+ STATE_IDLE,
+ STATE_START,
+ STATE_READ,
+ STATE_WRITE,
+ STATE_STOP
+};
+
+/* i2c controller private data */
+
+struct nuc900_i2c {
+ spinlock_t lock;
+ wait_queue_head_t wait;
+
+ struct i2c_msg *msg;
+ unsigned int msg_num;
+ unsigned int msg_idx;
+ unsigned int msg_ptr;
+ unsigned int irq;
+
+ enum nuc900_i2c_state state;
+
+ void __iomem *regs;
+ struct clk *clk;
+ struct device *dev;
+ struct resource *ioarea;
+ struct i2c_adapter adap;
+};
+
+/* nuc900_i2c_master_complete
+ *
+ * complete the message and wake up the caller, using the given return code,
+ * or zero to mean ok.
+*/
+
+static inline void nuc900_i2c_master_complete(struct nuc900_i2c *i2c, int ret)
+{
+ dev_dbg(i2c->dev, "master_complete %d\n", ret);
+
+ i2c->msg_ptr = 0;
+ i2c->msg = NULL;
+ i2c->msg_idx++;
+ i2c->msg_num = 0;
+ if (ret)
+ i2c->msg_idx = ret;
+
+ wake_up(&i2c->wait);
+}
+
+/* irq enable/disable functions */
+
+static inline void nuc900_i2c_disable_irq(struct nuc900_i2c *i2c)
+{
+ unsigned long tmp;
+
+ tmp = readl(i2c->regs + CSR);
+ writel(tmp & ~IRQEN, i2c->regs + CSR);
+}
+
+static inline void nuc900_i2c_enable_irq(struct nuc900_i2c *i2c)
+{
+ unsigned long tmp;
+
+ tmp = readl(i2c->regs + CSR);
+ writel(tmp | IRQEN, i2c->regs + CSR);
+}
+
+
+/* nuc900_i2c_message_start
+ *
+ * put the start of a message onto the bus
+*/
+
+static void nuc900_i2c_message_start(struct nuc900_i2c *i2c,
+ struct i2c_msg *msg)
+{
+ unsigned int addr = (msg->addr & 0x7f) << 1;
+
+ if (msg->flags & I2C_M_RD)
+ addr |= 0x1;
+ writel(addr & 0xff, i2c->regs + TXR);
+ writel(I2C_CMD_START | I2C_CMD_WRITE, i2c->regs + CMDR);
+}
+
+static inline void nuc900_i2c_stop(struct nuc900_i2c *i2c, int ret)
+{
+
+ dev_dbg(i2c->dev, "STOP\n");
+
+ /* stop the transfer */
+ i2c->state = STATE_STOP;
+ writel(I2C_CMD_STOP, i2c->regs + CMDR);
+
+ nuc900_i2c_master_complete(i2c, ret);
+ nuc900_i2c_disable_irq(i2c);
+}
+
+/* helper functions to determine the current state in the set of
+ * messages we are sending
+*/
+
+/* is_lastmsg()
+ *
+ * returns TRUE if the current message is the last in the set
+*/
+
+static inline int is_lastmsg(struct nuc900_i2c *i2c)
+{
+ return i2c->msg_idx >= (i2c->msg_num - 1);
+}
+
+/* is_msglast
+ *
+ * returns TRUE if we this is the last byte in the current message
+*/
+
+static inline int is_msglast(struct nuc900_i2c *i2c)
+{
+ return i2c->msg_ptr == i2c->msg->len-1;
+}
+
+/* is_msgend
+ *
+ * returns TRUE if we reached the end of the current message
+*/
+
+static inline int is_msgend(struct nuc900_i2c *i2c)
+{
+ return i2c->msg_ptr >= i2c->msg->len;
+}
+
+/* i2c_nuc900_irq_nextbyte
+ *
+ * process an interrupt and work out what to do
+ */
+
+static void i2c_nuc900_irq_nextbyte(struct nuc900_i2c *i2c,
+ unsigned long iicstat)
+{
+ unsigned char byte;
+
+ switch (i2c->state) {
+
+ case STATE_IDLE:
+ dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
+ break;
+
+ case STATE_STOP:
+ dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);
+ nuc900_i2c_disable_irq(i2c);
+ break;
+
+ case STATE_START:
+ /* last thing we did was send a start condition on the
+ * bus, or started a new i2c message
+ */
+
+ if (iicstat & SLAVE_ACK &&
+ !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+ /* ack was not received... */
+
+ dev_dbg(i2c->dev, "ack was not received\n");
+ nuc900_i2c_stop(i2c, -ENXIO);
+ break;
+ }
+
+ if (i2c->msg->flags & I2C_M_RD)
+ i2c->state = STATE_READ;
+ else
+ i2c->state = STATE_WRITE;
+
+ /* terminate the transfer if there is nothing to do
+ * as this is used by the i2c probe to find devices.
+ */
+
+ if (is_lastmsg(i2c) && i2c->msg->len == 0) {
+ nuc900_i2c_stop(i2c, 0);
+ break;
+ }
+
+ if (i2c->state == STATE_READ)
+ goto prepare_read;
+
+ /* fall through to the write state, as we will need to
+ * send a byte as well
+ */
+
+ case STATE_WRITE:
+ /* we are writing data to the device... check for the
+ * end of the message, and if so, work out what to do
+ */
+
+ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+ if (iicstat & SLAVE_ACK) {
+ dev_dbg(i2c->dev, "WRITE: No Ack\n");
+
+ nuc900_i2c_stop(i2c, -ECONNREFUSED);
+ break;
+ }
+ }
+
+retry_write:
+
+ if (!is_msgend(i2c)) {
+ byte = i2c->msg->buf[i2c->msg_ptr++];
+ writeb(byte, i2c->regs + TXR);
+ writel(I2C_CMD_WRITE, i2c->regs + CMDR);
+
+ } else if (!is_lastmsg(i2c)) {
+ /* we need to go to the next i2c message */
+
+ dev_dbg(i2c->dev, "WRITE: Next Message\n");
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+
+ /* check to see if we need to do another message */
+ if (i2c->msg->flags & I2C_M_NOSTART) {
+
+ if (i2c->msg->flags & I2C_M_RD) {
+ /* cannot do this, the controller
+ * forces us to send a new START
+ * when we change direction
+ */
+
+ nuc900_i2c_stop(i2c, -EINVAL);
+ }
+
+ goto retry_write;
+ } else {
+ /* send the new start */
+ nuc900_i2c_message_start(i2c, i2c->msg);
+ i2c->state = STATE_START;
+ }
+
+ } else {
+ /* send stop */
+
+ nuc900_i2c_stop(i2c, 0);
+ }
+ break;
+
+ case STATE_READ:
+ /* we have a byte of data in the data register, do
+ * something with it, and then work out wether we are
+ * going to do any more read/write
+ */
+
+ byte = readb(i2c->regs + RXR);
+ i2c->msg->buf[i2c->msg_ptr++] = byte;
+
+prepare_read:
+ if (is_msglast(i2c)) {
+ /* last byte of buffer */
+
+ if (is_lastmsg(i2c))
+ writel(I2C_CMD_READ | I2C_CMD_NACK,
+ i2c->regs + CMDR);
+
+ } else if (is_msgend(i2c)) {
+ /* ok, we've read the entire buffer, see if there
+ * is anything else we need to do
+ */
+
+ if (is_lastmsg(i2c)) {
+ /* last message, send stop and complete */
+ dev_dbg(i2c->dev, "READ: Send Stop\n");
+
+ nuc900_i2c_stop(i2c, 0);
+ } else {
+ /* go to the next transfer */
+ dev_dbg(i2c->dev, "READ: Next Transfer\n");
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+
+ writel(I2C_CMD_READ, i2c->regs + CMDR);
+ }
+
+ } else {
+ writel(I2C_CMD_READ, i2c->regs + CMDR);
+ }
+
+ break;
+ }
+}
+
+/* nuc900_i2c_irq
+ *
+ * top level IRQ servicing routine
+*/
+
+static irqreturn_t nuc900_i2c_irq(int irqno, void *dev_id)
+{
+ struct nuc900_i2c *i2c = dev_id;
+ unsigned long status;
+
+ status = readl(i2c->regs + CSR);
+ writel(status | IRQFLAG, i2c->regs + CSR);
+
+ if (status & ARBIT_LOST) {
+ /* deal with arbitration loss */
+ dev_err(i2c->dev, "deal with arbitration loss\n");
+ goto out;
+ }
+
+ if (i2c->state == STATE_IDLE) {
+ dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
+ goto out;
+ }
+
+ /* pretty much this leaves us with the fact that we've
+ * transmitted or received whatever byte we last sent
+ */
+
+ i2c_nuc900_irq_nextbyte(i2c, status);
+
+ out:
+ return IRQ_HANDLED;
+}
+
+
+/* nuc900_i2c_set_master
+ *
+ * get the i2c bus for a master transaction
+*/
+
+static int nuc900_i2c_set_master(struct nuc900_i2c *i2c)
+{
+ int timeout = 400;
+
+ while (timeout-- > 0) {
+ if (((readl(i2c->regs + SWR) & I2CSTART) == I2CSTART) &&
+ ((readl(i2c->regs + CSR) & I2CBUSY) == 0)) {
+ return 0;
+ }
+
+ msleep(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/* nuc900_i2c_doxfer
+ *
+ * this starts an i2c transfer
+*/
+
+static int nuc900_i2c_doxfer(struct nuc900_i2c *i2c,
+ struct i2c_msg *msgs, int num)
+{
+ unsigned long iicstat, timeout;
+ int spins = 20;
+ int ret;
+
+ ret = nuc900_i2c_set_master(i2c);
+ if (ret != 0) {
+ dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ spin_lock_irq(&i2c->lock);
+
+ i2c->msg = msgs;
+ i2c->msg_num = num;
+ i2c->msg_ptr = 0;
+ i2c->msg_idx = 0;
+ i2c->state = STATE_START;
+
+ nuc900_i2c_message_start(i2c, msgs);
+ spin_unlock_irq(&i2c->lock);
+
+ timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
+
+ ret = i2c->msg_idx;
+
+ /* having these next two as dev_err() makes life very
+ * noisy when doing an i2cdetect
+ */
+
+ if (timeout == 0)
+ dev_dbg(i2c->dev, "timeout\n");
+ else if (ret != num)
+ dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
+
+ /* ensure the stop has been through the bus */
+
+ dev_dbg(i2c->dev, "waiting for bus idle\n");
+
+ /* first, try busy waiting briefly */
+ do {
+ iicstat = readl(i2c->regs + CSR);
+ } while ((iicstat & I2CBUSY) && --spins);
+
+ /* if that timed out sleep */
+ if (!spins) {
+ msleep(1);
+ iicstat = readl(i2c->regs + CSR);
+ }
+
+ if (iicstat & I2CBUSY)
+ dev_warn(i2c->dev, "timeout waiting for bus idle\n");
+
+ out:
+ return ret;
+}
+
+/* nuc900_i2c_xfer
+ *
+ * first port of call from the i2c bus code when an message needs
+ * transferring across the i2c bus.
+*/
+
+static int nuc900_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct nuc900_i2c *i2c = (struct nuc900_i2c *)adap->algo_data;
+ int retry;
+ int ret;
+
+ nuc900_i2c_enable_irq(i2c);
+
+ for (retry = 0; retry < adap->retries; retry++) {
+
+ ret = nuc900_i2c_doxfer(i2c, msgs, num);
+
+ if (ret != -EAGAIN)
+ return ret;
+
+ dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
+
+ udelay(100);
+ }
+
+ return -EREMOTEIO;
+}
+
+/* declare our i2c functionality */
+static u32 nuc900_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+/* i2c bus registration info */
+
+static const struct i2c_algorithm nuc900_i2c_algorithm = {
+ .master_xfer = nuc900_i2c_xfer,
+ .functionality = nuc900_i2c_func,
+};
+
+/* nuc900_i2c_probe
+ *
+ * called by the bus driver when a suitable device is found
+*/
+
+static int __devinit nuc900_i2c_probe(struct platform_device *pdev)
+{
+ struct nuc900_i2c *i2c;
+ struct nuc900_platform_i2c *pdata;
+ struct resource *res;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ i2c = kzalloc(sizeof(struct nuc900_i2c), GFP_KERNEL);
+ if (!i2c) {
+ dev_err(&pdev->dev, "no memory for state\n");
+ return -ENOMEM;
+ }
+
+ strlcpy(i2c->adap.name, "nuc900-i2c0", sizeof(i2c->adap.name));
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.algo = &nuc900_i2c_algorithm;
+ i2c->adap.retries = 2;
+ i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+
+ spin_lock_init(&i2c->lock);
+ init_waitqueue_head(&i2c->wait);
+
+ /* find the clock and enable it */
+
+ i2c->dev = &pdev->dev;
+ i2c->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ ret = -ENOENT;
+ goto err_noclk;
+ }
+
+ dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
+
+ clk_enable(i2c->clk);
+
+ /* map the registers */
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ ret = -ENOENT;
+ goto err_clk;
+ }
+
+ i2c->ioarea = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+
+ if (i2c->ioarea == NULL) {
+ dev_err(&pdev->dev, "cannot request IO\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+
+ i2c->regs = ioremap(res->start, resource_size(res));
+
+ if (i2c->regs == NULL) {
+ dev_err(&pdev->dev, "cannot map IO\n");
+ ret = -ENXIO;
+ goto err_ioarea;
+ }
+
+ dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
+ i2c->regs, i2c->ioarea, res);
+
+ /* setup info block for the i2c core */
+
+ i2c->adap.algo_data = i2c;
+ i2c->adap.dev.parent = &pdev->dev;
+
+ mfp_set_groupg(&pdev->dev);
+
+ clk_get_rate(i2c->clk);
+
+ ret = (i2c->clk.apbfreq)/(pdata->bus_freq * 5) - 1;
+ writel(ret & 0xffff, i2c->regs + DIVIDER);
+
+ /* find the IRQ for this unit (note, this relies on the init call to
+ * ensure no current IRQs pending
+ */
+
+ i2c->irq = ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ dev_err(&pdev->dev, "cannot find IRQ\n");
+ goto err_iomap;
+ }
+
+ ret = request_irq(i2c->irq, nuc900_i2c_irq, IRQF_DISABLED | IRQF_SHARED,
+ dev_name(&pdev->dev), i2c);
+
+ if (ret != 0) {
+ dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
+ goto err_iomap;
+ }
+
+ /* Note, previous versions of the driver used i2c_add_adapter()
+ * to add the bus at any number. We now pass the bus number via
+ * the platform data, so if unset it will now default to always
+ * being bus 0.
+ */
+
+ i2c->adap.nr = pdata->bus_num;
+
+ ret = i2c_add_numbered_adapter(&i2c->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+ goto err_irq;
+ }
+
+ platform_set_drvdata(pdev, i2c);
+
+ dev_info(&pdev->dev, "%s: NUC900 I2C adapter\n",
+ dev_name(&i2c->adap.dev));
+ return 0;
+
+ err_irq:
+ free_irq(i2c->irq, i2c);
+
+ err_iomap:
+ iounmap(i2c->regs);
+
+ err_ioarea:
+ release_resource(i2c->ioarea);
+ kfree(i2c->ioarea);
+
+ err_clk:
+ clk_disable(i2c->clk);
+ clk_put(i2c->clk);
+
+ err_noclk:
+ kfree(i2c);
+ return ret;
+}
+
+/* nuc900_i2c_remove
+ *
+ * called when device is removed from the bus
+*/
+
+static int __devexit nuc900_i2c_remove(struct platform_device *pdev)
+{
+ struct nuc900_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+ free_irq(i2c->irq, i2c);
+
+ clk_disable(i2c->clk);
+ clk_put(i2c->clk);
+
+ iounmap(i2c->regs);
+
+ release_resource(i2c->ioarea);
+ kfree(i2c->ioarea);
+ kfree(i2c);
+
+ return 0;
+}
+
+static struct platform_driver nuc900_i2c_driver = {
+ .probe = nuc900_i2c_probe,
+ .remove = __devexit_p(nuc900_i2c_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "nuc900-i2c0",
+ },
+};
+
+static int __init i2c_adap_nuc900_init(void)
+{
+ return platform_driver_register(&nuc900_i2c_driver);
+}
+
+static void __exit i2c_adap_nuc900_exit(void)
+{
+ platform_driver_unregister(&nuc900_i2c_driver);
+}
+subsys_initcall(i2c_adap_nuc900_init);
+module_exit(i2c_adap_nuc900_exit);
+
+MODULE_DESCRIPTION("NUC900 I2C Bus driver");
+MODULE_AUTHOR("Wan ZongShun, <mcuos.com-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-i2c0");
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index 020ff23..c94e51b 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1001,7 +1001,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
struct pxa_i2c *i2c;
struct resource *res;
struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
- struct platform_device_id *id = platform_get_device_id(dev);
+ const struct platform_device_id *id = platform_get_device_id(dev);
int ret;
int irq;
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index ffb405d..598c49a 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -119,8 +119,10 @@ struct sh_mobile_i2c_data {
struct i2c_adapter adap;
struct clk *clk;
+ u_int8_t icic;
u_int8_t iccl;
u_int8_t icch;
+ u_int8_t flags;
spinlock_t lock;
wait_queue_head_t wait;
@@ -129,15 +131,17 @@ struct sh_mobile_i2c_data {
int sr;
};
+#define IIC_FLAG_HAS_ICIC67 (1 << 0)
+
#define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */
/* Register offsets */
-#define ICDR(pd) (pd->reg + 0x00)
-#define ICCR(pd) (pd->reg + 0x04)
-#define ICSR(pd) (pd->reg + 0x08)
-#define ICIC(pd) (pd->reg + 0x0c)
-#define ICCL(pd) (pd->reg + 0x10)
-#define ICCH(pd) (pd->reg + 0x14)
+#define ICDR 0x00
+#define ICCR 0x04
+#define ICSR 0x08
+#define ICIC 0x0c
+#define ICCL 0x10
+#define ICCH 0x14
/* Register bits */
#define ICCR_ICE 0x80
@@ -155,11 +159,32 @@ struct sh_mobile_i2c_data {
#define ICSR_WAIT 0x02
#define ICSR_DTE 0x01
+#define ICIC_ICCLB8 0x80
+#define ICIC_ICCHB8 0x40
#define ICIC_ALE 0x08
#define ICIC_TACKE 0x04
#define ICIC_WAITE 0x02
#define ICIC_DTEE 0x01
+static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data)
+{
+ if (offs == ICIC)
+ data |= pd->icic;
+
+ iowrite8(data, pd->reg + offs);
+}
+
+static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs)
+{
+ return ioread8(pd->reg + offs);
+}
+
+static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs,
+ unsigned char set, unsigned char clr)
+{
+ iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr);
+}
+
static void activate_ch(struct sh_mobile_i2c_data *pd)
{
unsigned long i2c_clk;
@@ -187,6 +212,14 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
else
pd->iccl = (u_int8_t)(num/denom);
+ /* one more bit of ICCL in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCLB8;
+ else
+ pd->icic &= ~ICIC_ICCLB8;
+ }
+
/* Calculate the value for icch. From the data sheet:
icch = (p clock / transfer rate) * (H / (L + H)) */
num = i2c_clk * 4;
@@ -196,25 +229,33 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
else
pd->icch = (u_int8_t)(num/denom);
+ /* one more bit of ICCH in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCHB8;
+ else
+ pd->icic &= ~ICIC_ICCHB8;
+ }
+
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Mask all interrupts */
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICIC, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
}
static void deactivate_ch(struct sh_mobile_i2c_data *pd)
{
/* Clear/disable interrupts */
- iowrite8(0, ICSR(pd));
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICSR, 0);
+ iic_wr(pd, ICIC, 0);
/* Disable channel */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Disable clock and mark device as idle */
clk_disable(pd->clk);
@@ -233,35 +274,35 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
switch (op) {
case OP_START: /* issue start and trigger DTE interrupt */
- iowrite8(0x94, ICCR(pd));
+ iic_wr(pd, ICCR, 0x94);
break;
case OP_TX_FIRST: /* disable DTE interrupt and write data */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE, ICIC(pd));
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICDR, data);
break;
case OP_TX: /* write data */
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICDR, data);
break;
case OP_TX_STOP: /* write data and issue a stop afterwards */
- iowrite8(data, ICDR(pd));
- iowrite8(0x90, ICCR(pd));
+ iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICCR, 0x90);
break;
case OP_TX_TO_RX: /* select read mode */
- iowrite8(0x81, ICCR(pd));
+ iic_wr(pd, ICCR, 0x81);
break;
case OP_RX: /* just read data */
- ret = ioread8(ICDR(pd));
+ ret = iic_rd(pd, ICDR);
break;
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICCR, 0xc0);
break;
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- ret = ioread8(ICDR(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ ret = iic_rd(pd, ICDR);
+ iic_wr(pd, ICCR, 0xc0);
break;
}
@@ -367,7 +408,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
unsigned char sr;
int wakeup;
- sr = ioread8(ICSR(pd));
+ sr = iic_rd(pd, ICSR);
pd->sr |= sr; /* remember state */
dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
@@ -376,7 +417,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
if (sr & (ICSR_AL | ICSR_TACK)) {
/* don't interrupt transaction - continue to issue stop */
- iowrite8(sr & ~(ICSR_AL | ICSR_TACK), ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
wakeup = 0;
} else if (pd->msg->flags & I2C_M_RD)
wakeup = sh_mobile_i2c_isr_rx(pd);
@@ -384,7 +425,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
wakeup = sh_mobile_i2c_isr_tx(pd);
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
- iowrite8(sr & ~ICSR_WAIT, ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
if (wakeup) {
pd->sr |= SW_DONE;
@@ -402,21 +443,21 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
}
/* Initialize channel registers */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
pd->msg = usr_msg;
pd->pos = -1;
pd->sr = 0;
/* Enable all interrupts to begin with */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE | ICIC_DTEE, ICIC(pd));
+ iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
return 0;
}
@@ -451,7 +492,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
retry_count = 1000;
again:
- val = ioread8(ICSR(pd));
+ val = iic_rd(pd, ICSR);
dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr);
@@ -576,6 +617,12 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_irq;
}
+ /* The IIC blocks on SH-Mobile ARM processors
+ * come with two new bits in ICIC.
+ */
+ if (size > 0x17)
+ pd->flags |= IIC_FLAG_HAS_ICIC67;
+
/* Enable Runtime PM for this device.
*
* Also tell the Runtime PM core to ignore children
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index df937df..6649176 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -20,7 +20,9 @@
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
- Jean Delvare <khali@linux-fr.org> */
+ Jean Delvare <khali@linux-fr.org>
+ Mux support by Rodolfo Giometti <giometti@enneenne.com> and
+ Michael Lawnick <michael.lawnick.ext@nsn.com> */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -423,11 +425,87 @@ static int __i2c_check_addr_busy(struct device *dev, void *addrp)
return 0;
}
+/* walk up mux tree */
+static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr)
+{
+ int result;
+
+ result = device_for_each_child(&adapter->dev, &addr,
+ __i2c_check_addr_busy);
+
+ if (!result && i2c_parent_is_i2c_adapter(adapter))
+ result = i2c_check_mux_parents(
+ to_i2c_adapter(adapter->dev.parent), addr);
+
+ return result;
+}
+
+/* recurse down mux tree */
+static int i2c_check_mux_children(struct device *dev, void *addrp)
+{
+ int result;
+
+ if (dev->type == &i2c_adapter_type)
+ result = device_for_each_child(dev, addrp,
+ i2c_check_mux_children);
+ else
+ result = __i2c_check_addr_busy(dev, addrp);
+
+ return result;
+}
+
static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr)
{
- return device_for_each_child(&adapter->dev, &addr,
- __i2c_check_addr_busy);
+ int result = 0;
+
+ if (i2c_parent_is_i2c_adapter(adapter))
+ result = i2c_check_mux_parents(
+ to_i2c_adapter(adapter->dev.parent), addr);
+
+ if (!result)
+ result = device_for_each_child(&adapter->dev, &addr,
+ i2c_check_mux_children);
+
+ return result;
+}
+
+/**
+ * i2c_lock_adapter - Get exclusive access to an I2C bus segment
+ * @adapter: Target I2C bus segment
+ */
+void i2c_lock_adapter(struct i2c_adapter *adapter)
+{
+ if (i2c_parent_is_i2c_adapter(adapter))
+ i2c_lock_adapter(to_i2c_adapter(adapter->dev.parent));
+ else
+ rt_mutex_lock(&adapter->bus_lock);
+}
+EXPORT_SYMBOL_GPL(i2c_lock_adapter);
+
+/**
+ * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment
+ * @adapter: Target I2C bus segment
+ */
+static int i2c_trylock_adapter(struct i2c_adapter *adapter)
+{
+ if (i2c_parent_is_i2c_adapter(adapter))
+ return i2c_trylock_adapter(to_i2c_adapter(adapter->dev.parent));
+ else
+ return rt_mutex_trylock(&adapter->bus_lock);
+}
+
+/**
+ * i2c_unlock_adapter - Release exclusive access to an I2C bus segment
+ * @adapter: Target I2C bus segment
+ */
+void i2c_unlock_adapter(struct i2c_adapter *adapter)
+{
+ if (i2c_parent_is_i2c_adapter(adapter))
+ i2c_unlock_adapter(to_i2c_adapter(adapter->dev.parent));
+ else
+ rt_mutex_unlock(&adapter->bus_lock);
}
+EXPORT_SYMBOL_GPL(i2c_unlock_adapter);
/**
* i2c_new_device - instantiate an i2c device
@@ -633,9 +711,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
return -EINVAL;
/* Keep track of the added device */
- i2c_lock_adapter(adap);
+ mutex_lock(&adap->userspace_clients_lock);
list_add_tail(&client->detected, &adap->userspace_clients);
- i2c_unlock_adapter(adap);
+ mutex_unlock(&adap->userspace_clients_lock);
dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
info.type, info.addr);
@@ -674,7 +752,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
/* Make sure the device was added through sysfs */
res = -ENOENT;
- i2c_lock_adapter(adap);
+ mutex_lock(&adap->userspace_clients_lock);
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
if (client->addr == addr) {
@@ -687,7 +765,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
break;
}
}
- i2c_unlock_adapter(adap);
+ mutex_unlock(&adap->userspace_clients_lock);
if (res < 0)
dev_err(dev, "%s: Can't find device in list\n",
@@ -714,10 +792,11 @@ static const struct attribute_group *i2c_adapter_attr_groups[] = {
NULL
};
-static struct device_type i2c_adapter_type = {
+struct device_type i2c_adapter_type = {
.groups = i2c_adapter_attr_groups,
.release = i2c_adapter_dev_release,
};
+EXPORT_SYMBOL_GPL(i2c_adapter_type);
#ifdef CONFIG_I2C_COMPAT
static struct class_compat *i2c_adapter_compat_class;
@@ -760,7 +839,7 @@ static int __process_new_adapter(struct device_driver *d, void *data)
static int i2c_register_adapter(struct i2c_adapter *adap)
{
- int res = 0, dummy;
+ int res = 0;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
@@ -769,6 +848,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
}
rt_mutex_init(&adap->bus_lock);
+ mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
@@ -801,8 +881,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
/* Notify drivers */
mutex_lock(&core_lock);
- dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
- __process_new_adapter);
+ bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
@@ -975,7 +1054,7 @@ int i2c_del_adapter(struct i2c_adapter *adap)
return res;
/* Remove devices instantiated from sysfs */
- i2c_lock_adapter(adap);
+ mutex_lock(&adap->userspace_clients_lock);
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,
@@ -983,7 +1062,7 @@ int i2c_del_adapter(struct i2c_adapter *adap)
list_del(&client->detected);
i2c_unregister_device(client);
}
- i2c_unlock_adapter(adap);
+ mutex_unlock(&adap->userspace_clients_lock);
/* Detach any active clients. This can't fail, thus we do not
checking the returned value. */
@@ -1238,12 +1317,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
#endif
if (in_atomic() || irqs_disabled()) {
- ret = rt_mutex_trylock(&adap->bus_lock);
+ ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
- rt_mutex_lock(&adap->bus_lock);
+ i2c_lock_adapter(adap);
}
/* Retry automatically on arbitration loss */
@@ -1255,7 +1334,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
- rt_mutex_unlock(&adap->bus_lock);
+ i2c_unlock_adapter(adap);
return ret;
} else {
@@ -1350,13 +1429,17 @@ static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
I2C_SMBUS_BYTE_DATA, &dummy);
else
#endif
- if ((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50
- || !i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
- err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
- I2C_SMBUS_BYTE, &dummy);
- else
+ if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50)
+ && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
I2C_SMBUS_QUICK, NULL);
+ else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE))
+ err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
+ I2C_SMBUS_BYTE, &dummy);
+ else {
+ dev_warn(&adap->dev, "No suitable probing method supported\n");
+ err = -EOPNOTSUPP;
+ }
return err >= 0;
}
@@ -1437,16 +1520,6 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
if (!(adapter->class & driver->class))
goto exit_free;
- /* Stop here if the bus doesn't support probing */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE)) {
- if (address_list[0] == I2C_CLIENT_END)
- goto exit_free;
-
- dev_warn(&adapter->dev, "Probing not supported\n");
- err = -EOPNOTSUPP;
- goto exit_free;
- }
-
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id, address_list[i]);
@@ -1461,18 +1534,23 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
return err;
}
+int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr)
+{
+ return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
+ I2C_SMBUS_QUICK, NULL) >= 0;
+}
+EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read);
+
struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
struct i2c_board_info *info,
- unsigned short const *addr_list)
+ unsigned short const *addr_list,
+ int (*probe)(struct i2c_adapter *, unsigned short addr))
{
int i;
- /* Stop here if the bus doesn't support probing */
- if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) {
- dev_err(&adap->dev, "Probing not supported\n");
- return NULL;
- }
+ if (!probe)
+ probe = i2c_default_probe;
for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
/* Check address validity */
@@ -1490,7 +1568,7 @@ i2c_new_probed_device(struct i2c_adapter *adap,
}
/* Test address responsiveness */
- if (i2c_default_probe(adap, addr_list[i]))
+ if (probe(adap, addr_list[i]))
break;
}
@@ -2002,7 +2080,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if (adapter->algo->smbus_xfer) {
- rt_mutex_lock(&adapter->bus_lock);
+ i2c_lock_adapter(adapter);
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
@@ -2016,7 +2094,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
orig_jiffies + adapter->timeout))
break;
}
- rt_mutex_unlock(&adapter->bus_lock);
+ i2c_unlock_adapter(adapter);
} else
res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data);
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index e0694e4..5f3a52d 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -167,13 +167,9 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,
if (count > 8192)
count = 8192;
- tmp = kmalloc(count, GFP_KERNEL);
- if (tmp == NULL)
- return -ENOMEM;
- if (copy_from_user(tmp, buf, count)) {
- kfree(tmp);
- return -EFAULT;
- }
+ tmp = memdup_user(buf, count);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file->f_path.dentry->d_inode), count);
@@ -193,12 +189,50 @@ static int i2cdev_check(struct device *dev, void *addrp)
return dev->driver ? -EBUSY : 0;
}
+/* walk up mux tree */
+static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr)
+{
+ int result;
+
+ result = device_for_each_child(&adapter->dev, &addr, i2cdev_check);
+
+ if (!result && i2c_parent_is_i2c_adapter(adapter))
+ result = i2cdev_check_mux_parents(
+ to_i2c_adapter(adapter->dev.parent), addr);
+
+ return result;
+}
+
+/* recurse down mux tree */
+static int i2cdev_check_mux_children(struct device *dev, void *addrp)
+{
+ int result;
+
+ if (dev->type == &i2c_adapter_type)
+ result = device_for_each_child(dev, addrp,
+ i2cdev_check_mux_children);
+ else
+ result = i2cdev_check(dev, addrp);
+
+ return result;
+}
+
/* This address checking function differs from the one in i2c-core
in that it considers an address with a registered device, but no
driver bound to it, as NOT busy. */
static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
{
- return device_for_each_child(&adapter->dev, &addr, i2cdev_check);
+ int result = 0;
+
+ if (i2c_parent_is_i2c_adapter(adapter))
+ result = i2cdev_check_mux_parents(
+ to_i2c_adapter(adapter->dev.parent), addr);
+
+ if (!result)
+ result = device_for_each_child(&adapter->dev, &addr,
+ i2cdev_check_mux_children);
+
+ return result;
}
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
@@ -219,9 +253,7 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;
- rdwr_pa = (struct i2c_msg *)
- kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
- GFP_KERNEL);
+ rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);
if (!rdwr_pa)
return -ENOMEM;
@@ -247,15 +279,9 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
- rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
- if (rdwr_pa[i].buf == NULL) {
- res = -ENOMEM;
- break;
- }
- if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
- rdwr_pa[i].len)) {
- ++i; /* Needs to be kfreed too */
- res = -EFAULT;
+ rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);
+ if (IS_ERR(rdwr_pa[i].buf)) {
+ res = PTR_ERR(rdwr_pa[i].buf);
break;
}
}
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
new file mode 100644
index 0000000..d32a484
--- /dev/null
+++ b/drivers/i2c/i2c-mux.c
@@ -0,0 +1,165 @@
+/*
+ * Multiplexed I2C bus driver.
+ *
+ * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (c) 2009-2010 NSN GmbH & Co KG <michael.lawnick.ext@nsn.com>
+ *
+ * Simplifies access to complex multiplexed I2C bus topologies, by presenting
+ * each multiplexed bus segment as an additional I2C adapter.
+ * Supports multi-level mux'ing (mux behind a mux).
+ *
+ * Based on:
+ * i2c-virt.c from Kumar Gala <galak@kernel.crashing.org>
+ * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc.
+ * i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+
+/* multiplexer per channel data */
+struct i2c_mux_priv {
+ struct i2c_adapter adap;
+ struct i2c_algorithm algo;
+
+ struct i2c_adapter *parent;
+ void *mux_dev; /* the mux chip/device */
+ u32 chan_id; /* the channel id */
+
+ int (*select)(struct i2c_adapter *, void *mux_dev, u32 chan_id);
+ int (*deselect)(struct i2c_adapter *, void *mux_dev, u32 chan_id);
+};
+
+static int i2c_mux_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_adapter *parent = priv->parent;
+ int ret;
+
+ /* Switch to the right mux port and perform the transfer. */
+
+ ret = priv->select(parent, priv->mux_dev, priv->chan_id);
+ if (ret >= 0)
+ ret = parent->algo->master_xfer(parent, msgs, num);
+ if (priv->deselect)
+ priv->deselect(parent, priv->mux_dev, priv->chan_id);
+
+ return ret;
+}
+
+static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
+ u16 addr, unsigned short flags,
+ char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_adapter *parent = priv->parent;
+ int ret;
+
+ /* Select the right mux port and perform the transfer. */
+
+ ret = priv->select(parent, priv->mux_dev, priv->chan_id);
+ if (ret >= 0)
+ ret = parent->algo->smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
+ if (priv->deselect)
+ priv->deselect(parent, priv->mux_dev, priv->chan_id);
+
+ return ret;
+}
+
+/* Return the parent's functionality */
+static u32 i2c_mux_functionality(struct i2c_adapter *adap)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_adapter *parent = priv->parent;
+
+ return parent->algo->functionality(parent);
+}
+
+struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
+ void *mux_dev, u32 force_nr, u32 chan_id,
+ int (*select) (struct i2c_adapter *,
+ void *, u32),
+ int (*deselect) (struct i2c_adapter *,
+ void *, u32))
+{
+ struct i2c_mux_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ /* Set up private adapter data */
+ priv->parent = parent;
+ priv->mux_dev = mux_dev;
+ priv->chan_id = chan_id;
+ priv->select = select;
+ priv->deselect = deselect;
+
+ /* Need to do algo dynamically because we don't know ahead
+ * of time what sort of physical adapter we'll be dealing with.
+ */
+ if (parent->algo->master_xfer)
+ priv->algo.master_xfer = i2c_mux_master_xfer;
+ if (parent->algo->smbus_xfer)
+ priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
+ priv->algo.functionality = i2c_mux_functionality;
+
+ /* Now fill out new adapter structure */
+ snprintf(priv->adap.name, sizeof(priv->adap.name),
+ "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id);
+ priv->adap.owner = THIS_MODULE;
+ priv->adap.id = parent->id;
+ priv->adap.algo = &priv->algo;
+ priv->adap.algo_data = priv;
+ priv->adap.dev.parent = &parent->dev;
+
+ if (force_nr) {
+ priv->adap.nr = force_nr;
+ ret = i2c_add_numbered_adapter(&priv->adap);
+ } else {
+ ret = i2c_add_adapter(&priv->adap);
+ }
+ if (ret < 0) {
+ dev_err(&parent->dev,
+ "failed to add mux-adapter (error=%d)\n",
+ ret);
+ kfree(priv);
+ return NULL;
+ }
+
+ dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
+ i2c_adapter_id(&priv->adap));
+
+ return &priv->adap;
+}
+EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
+
+int i2c_del_mux_adapter(struct i2c_adapter *adap)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ int ret;
+
+ ret = i2c_del_adapter(adap);
+ if (ret < 0)
+ return ret;
+ kfree(priv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_del_mux_adapter);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
new file mode 100644
index 0000000..4c9a99c
--- /dev/null
+++ b/drivers/i2c/muxes/Kconfig
@@ -0,0 +1,18 @@
+#
+# Multiplexer I2C chip drivers configuration
+#
+
+menu "Multiplexer I2C Chip support"
+ depends on I2C_MUX
+
+config I2C_MUX_PCA954x
+ tristate "Philips PCA954x I2C Mux/switches"
+ depends on EXPERIMENTAL
+ help
+ If you say yes here you get support for the Philips PCA954x
+ I2C mux/switch devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called pca954x.
+
+endmenu
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
new file mode 100644
index 0000000..bd83b527
--- /dev/null
+++ b/drivers/i2c/muxes/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for multiplexer I2C chip drivers.
+
+obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
+
+ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
new file mode 100644
index 0000000..6f9accf
--- /dev/null
+++ b/drivers/i2c/muxes/pca954x.c
@@ -0,0 +1,301 @@
+/*
+ * I2C multiplexer
+ *
+ * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
+ *
+ * This module supports the PCA954x series of I2C multiplexer/switch chips
+ * made by Philips Semiconductors.
+ * This includes the:
+ * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547
+ * and PCA9548.
+ *
+ * These chips are all controlled via the I2C bus itself, and all have a
+ * single 8-bit register. The upstream "parent" bus fans out to two,
+ * four, or eight downstream busses or channels; which of these
+ * are selected is determined by the chip type and register contents. A
+ * mux can select only one sub-bus at a time; a switch can select any
+ * combination simultaneously.
+ *
+ * Based on:
+ * pca954x.c from Kumar Gala <galak@kernel.crashing.org>
+ * Copyright (C) 2006
+ *
+ * Based on:
+ * pca954x.c from Ken Harrenstien
+ * Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
+ *
+ * Based on:
+ * i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
+ * and
+ * pca9540.c from Jean Delvare <khali@linux-fr.org>.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+
+#include <linux/i2c/pca954x.h>
+
+#define PCA954X_MAX_NCHANS 8
+
+enum pca_type {
+ pca_9540,
+ pca_9542,
+ pca_9543,
+ pca_9544,
+ pca_9545,
+ pca_9546,
+ pca_9547,
+ pca_9548,
+};
+
+struct pca954x {
+ enum pca_type type;
+ struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
+
+ u8 last_chan; /* last register value */
+};
+
+struct chip_desc {
+ u8 nchans;
+ u8 enable; /* used for muxes only */
+ enum muxtype {
+ pca954x_ismux = 0,
+ pca954x_isswi
+ } muxtype;
+};
+
+/* Provide specs for the PCA954x types we know about */
+static const struct chip_desc chips[] = {
+ [pca_9540] = {
+ .nchans = 2,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9543] = {
+ .nchans = 2,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9544] = {
+ .nchans = 4,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9545] = {
+ .nchans = 4,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9547] = {
+ .nchans = 8,
+ .enable = 0x8,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9548] = {
+ .nchans = 8,
+ .muxtype = pca954x_isswi,
+ },
+};
+
+static const struct i2c_device_id pca954x_id[] = {
+ { "pca9540", pca_9540 },
+ { "pca9542", pca_9540 },
+ { "pca9543", pca_9543 },
+ { "pca9544", pca_9544 },
+ { "pca9545", pca_9545 },
+ { "pca9546", pca_9545 },
+ { "pca9547", pca_9547 },
+ { "pca9548", pca_9548 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca954x_id);
+
+/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
+ for this as they will try to lock adapter a second time */
+static int pca954x_reg_write(struct i2c_adapter *adap,
+ struct i2c_client *client, u8 val)
+{
+ int ret = -ENODEV;
+
+ if (adap->algo->master_xfer) {
+ struct i2c_msg msg;
+ char buf[1];
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 1;
+ buf[0] = val;
+ msg.buf = buf;
+ ret = adap->algo->master_xfer(adap, &msg, 1);
+ } else {
+ union i2c_smbus_data data;
+ ret = adap->algo->smbus_xfer(adap, client->addr,
+ client->flags,
+ I2C_SMBUS_WRITE,
+ val, I2C_SMBUS_BYTE, &data);
+ }
+
+ return ret;
+}
+
+static int pca954x_select_chan(struct i2c_adapter *adap,
+ void *client, u32 chan)
+{
+ struct pca954x *data = i2c_get_clientdata(client);
+ const struct chip_desc *chip = &chips[data->type];
+ u8 regval;
+ int ret = 0;
+
+ /* we make switches look like muxes, not sure how to be smarter */
+ if (chip->muxtype == pca954x_ismux)
+ regval = chan | chip->enable;
+ else
+ regval = 1 << chan;
+
+ /* Only select the channel if its different from the last channel */
+ if (data->last_chan != regval) {
+ ret = pca954x_reg_write(adap, client, regval);
+ data->last_chan = regval;
+ }
+
+ return ret;
+}
+
+static int pca954x_deselect_mux(struct i2c_adapter *adap,
+ void *client, u32 chan)
+{
+ struct pca954x *data = i2c_get_clientdata(client);
+
+ /* Deselect active channel */
+ data->last_chan = 0;
+ return pca954x_reg_write(adap, client, data->last_chan);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int __devinit pca954x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
+ struct pca954x_platform_data *pdata = client->dev.platform_data;
+ int num, force;
+ struct pca954x *data;
+ int ret = -ENODEV;
+
+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
+ goto err;
+
+ data = kzalloc(sizeof(struct pca954x), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ i2c_set_clientdata(client, data);
+
+ /* Read the mux register at addr to verify
+ * that the mux is in fact present.
+ */
+ if (i2c_smbus_read_byte(client) < 0) {
+ dev_warn(&client->dev, "probe failed\n");
+ goto exit_free;
+ }
+
+ data->type = id->driver_data;
+ data->last_chan = 0; /* force the first selection */
+
+ /* Now create an adapter for each channel */
+ for (num = 0; num < chips[data->type].nchans; num++) {
+ force = 0; /* dynamic adap number */
+ if (pdata) {
+ if (num < pdata->num_modes)
+ /* force static number */
+ force = pdata->modes[num].adap_id;
+ else
+ /* discard unconfigured channels */
+ break;
+ }
+
+ data->virt_adaps[num] =
+ i2c_add_mux_adapter(adap, client,
+ force, num, pca954x_select_chan,
+ (pdata && pdata->modes[num].deselect_on_exit)
+ ? pca954x_deselect_mux : NULL);
+
+ if (data->virt_adaps[num] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev,
+ "failed to register multiplexed adapter"
+ " %d as bus %d\n", num, force);
+ goto virt_reg_failed;
+ }
+ }
+
+ dev_info(&client->dev,
+ "registered %d multiplexed busses for I2C %s %s\n",
+ num, chips[data->type].muxtype == pca954x_ismux
+ ? "mux" : "switch", client->name);
+
+ return 0;
+
+virt_reg_failed:
+ for (num--; num >= 0; num--)
+ i2c_del_mux_adapter(data->virt_adaps[num]);
+exit_free:
+ kfree(data);
+err:
+ return ret;
+}
+
+static int __devexit pca954x_remove(struct i2c_client *client)
+{
+ struct pca954x *data = i2c_get_clientdata(client);
+ const struct chip_desc *chip = &chips[data->type];
+ int i, err;
+
+ for (i = 0; i < chip->nchans; ++i)
+ if (data->virt_adaps[i]) {
+ err = i2c_del_mux_adapter(data->virt_adaps[i]);
+ if (err)
+ return err;
+ data->virt_adaps[i] = NULL;
+ }
+
+ kfree(data);
+ return 0;
+}
+
+static struct i2c_driver pca954x_driver = {
+ .driver = {
+ .name = "pca954x",
+ .owner = THIS_MODULE,
+ },
+ .probe = pca954x_probe,
+ .remove = __devexit_p(pca954x_remove),
+ .id_table = pca954x_id,
+};
+
+static int __init pca954x_init(void)
+{
+ return i2c_add_driver(&pca954x_driver);
+}
+
+static void __exit pca954x_exit(void)
+{
+ i2c_del_driver(&pca954x_driver);
+}
+
+module_init(pca954x_init);
+module_exit(pca954x_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
index f9daffd..e88a2cf 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -190,7 +190,7 @@ void ide_prep_sense(ide_drive_t *drive, struct request *rq)
BUG_ON(sense_len > sizeof(*sense));
- if (blk_sense_request(rq) || drive->sense_rq_armed)
+ if (rq->cmd_type == REQ_TYPE_SENSE || drive->sense_rq_armed)
return;
memset(sense, 0, sizeof(*sense));
@@ -307,13 +307,16 @@ EXPORT_SYMBOL_GPL(ide_cd_expiry);
int ide_cd_get_xferlen(struct request *rq)
{
- if (blk_fs_request(rq))
+ switch (rq->cmd_type) {
+ case REQ_TYPE_FS:
return 32768;
- else if (blk_sense_request(rq) || blk_pc_request(rq) ||
- rq->cmd_type == REQ_TYPE_ATA_PC)
+ case REQ_TYPE_SENSE:
+ case REQ_TYPE_BLOCK_PC:
+ case REQ_TYPE_ATA_PC:
return blk_rq_bytes(rq);
- else
+ default:
return 0;
+ }
}
EXPORT_SYMBOL_GPL(ide_cd_get_xferlen);
@@ -474,12 +477,12 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
if (uptodate == 0)
drive->failed_pc = NULL;
- if (blk_special_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_SPECIAL) {
rq->errors = 0;
error = 0;
} else {
- if (blk_fs_request(rq) == 0 && uptodate <= 0) {
+ if (rq->cmd_type != REQ_TYPE_FS && uptodate <= 0) {
if (rq->errors == 0)
rq->errors = -EIO;
}
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 2de76cc..31fc769 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -31,6 +31,7 @@
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
@@ -176,7 +177,7 @@ static void cdrom_analyze_sense_data(ide_drive_t *drive,
if (!sense->valid)
break;
if (failed_command == NULL ||
- !blk_fs_request(failed_command))
+ failed_command->cmd_type != REQ_TYPE_FS)
break;
sector = (sense->information[0] << 24) |
(sense->information[1] << 16) |
@@ -292,7 +293,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
"stat 0x%x",
rq->cmd[0], rq->cmd_type, err, stat);
- if (blk_sense_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_SENSE) {
/*
* We got an error trying to get sense info from the drive
* (probably while trying to recover from a former error).
@@ -303,7 +304,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
}
/* if we have an error, pass CHECK_CONDITION as the SCSI status byte */
- if (blk_pc_request(rq) && !rq->errors)
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC && !rq->errors)
rq->errors = SAM_STAT_CHECK_CONDITION;
if (blk_noretry_request(rq))
@@ -311,13 +312,14 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
switch (sense_key) {
case NOT_READY:
- if (blk_fs_request(rq) && rq_data_dir(rq) == WRITE) {
+ if (rq->cmd_type == REQ_TYPE_FS && rq_data_dir(rq) == WRITE) {
if (ide_cd_breathe(drive, rq))
return 1;
} else {
cdrom_saw_media_change(drive);
- if (blk_fs_request(rq) && !blk_rq_quiet(rq))
+ if (rq->cmd_type == REQ_TYPE_FS &&
+ !(rq->cmd_flags & REQ_QUIET))
printk(KERN_ERR PFX "%s: tray open\n",
drive->name);
}
@@ -326,7 +328,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
case UNIT_ATTENTION:
cdrom_saw_media_change(drive);
- if (blk_fs_request(rq) == 0)
+ if (rq->cmd_type != REQ_TYPE_FS)
return 0;
/*
@@ -352,7 +354,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
* No point in retrying after an illegal request or data
* protect error.
*/
- if (!blk_rq_quiet(rq))
+ if (!(rq->cmd_flags & REQ_QUIET))
ide_dump_status(drive, "command error", stat);
do_end_request = 1;
break;
@@ -361,20 +363,20 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
* No point in re-trying a zillion times on a bad sector.
* If we got here the error is not correctable.
*/
- if (!blk_rq_quiet(rq))
+ if (!(rq->cmd_flags & REQ_QUIET))
ide_dump_status(drive, "media error "
"(bad sector)", stat);
do_end_request = 1;
break;
case BLANK_CHECK:
/* disk appears blank? */
- if (!blk_rq_quiet(rq))
+ if (!(rq->cmd_flags & REQ_QUIET))
ide_dump_status(drive, "media error (blank)",
stat);
do_end_request = 1;
break;
default:
- if (blk_fs_request(rq) == 0)
+ if (rq->cmd_type != REQ_TYPE_FS)
break;
if (err & ~ATA_ABORTED) {
/* go to the default handler for other errors */
@@ -385,7 +387,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
do_end_request = 1;
}
- if (blk_fs_request(rq) == 0) {
+ if (rq->cmd_type != REQ_TYPE_FS) {
rq->cmd_flags |= REQ_FAILED;
do_end_request = 1;
}
@@ -532,7 +534,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
ide_expiry_t *expiry = NULL;
int dma_error = 0, dma, thislen, uptodate = 0;
int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0;
- int sense = blk_sense_request(rq);
+ int sense = (rq->cmd_type == REQ_TYPE_SENSE);
unsigned int timeout;
u16 len;
u8 ireason, stat;
@@ -575,7 +577,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
ide_read_bcount_and_ireason(drive, &len, &ireason);
- thislen = blk_fs_request(rq) ? len : cmd->nleft;
+ thislen = (rq->cmd_type == REQ_TYPE_FS) ? len : cmd->nleft;
if (thislen > len)
thislen = len;
@@ -584,7 +586,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
/* If DRQ is clear, the command has completed. */
if ((stat & ATA_DRQ) == 0) {
- if (blk_fs_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS) {
/*
* If we're not done reading/writing, complain.
* Otherwise, complete the command normally.
@@ -598,7 +600,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
rq->cmd_flags |= REQ_FAILED;
uptodate = 0;
}
- } else if (!blk_pc_request(rq)) {
+ } else if (rq->cmd_type != REQ_TYPE_BLOCK_PC) {
ide_cd_request_sense_fixup(drive, cmd);
uptodate = cmd->nleft ? 0 : 1;
@@ -647,7 +649,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
/* pad, if necessary */
if (len > 0) {
- if (blk_fs_request(rq) == 0 || write == 0)
+ if (rq->cmd_type != REQ_TYPE_FS || write == 0)
ide_pad_transfer(drive, write, len);
else {
printk(KERN_ERR PFX "%s: confused, missing data\n",
@@ -656,11 +658,11 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
}
}
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
timeout = rq->timeout;
} else {
timeout = ATAPI_WAIT_PC;
- if (!blk_fs_request(rq))
+ if (rq->cmd_type != REQ_TYPE_FS)
expiry = ide_cd_expiry;
}
@@ -669,7 +671,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
return ide_started;
out_end:
- if (blk_pc_request(rq) && rc == 0) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC && rc == 0) {
rq->resid_len = 0;
blk_end_request_all(rq, 0);
hwif->rq = NULL;
@@ -677,7 +679,7 @@ out_end:
if (sense && uptodate)
ide_cd_complete_failed_rq(drive, rq);
- if (blk_fs_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS) {
if (cmd->nleft == 0)
uptodate = 1;
} else {
@@ -690,7 +692,7 @@ out_end:
return ide_stopped;
/* make sure it's fully ended */
- if (blk_fs_request(rq) == 0) {
+ if (rq->cmd_type != REQ_TYPE_FS) {
rq->resid_len -= cmd->nbytes - cmd->nleft;
if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE))
rq->resid_len += cmd->last_xfer_len;
@@ -750,7 +752,7 @@ static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
ide_debug_log(IDE_DBG_PC, "rq->cmd[0]: 0x%x, rq->cmd_type: 0x%x",
rq->cmd[0], rq->cmd_type);
- if (blk_pc_request(rq))
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
rq->cmd_flags |= REQ_QUIET;
else
rq->cmd_flags &= ~REQ_FAILED;
@@ -791,21 +793,26 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
if (drive->debug_mask & IDE_DBG_RQ)
blk_dump_rq_flags(rq, "ide_cd_do_request");
- if (blk_fs_request(rq)) {
+ switch (rq->cmd_type) {
+ case REQ_TYPE_FS:
if (cdrom_start_rw(drive, rq) == ide_stopped)
goto out_end;
- } else if (blk_sense_request(rq) || blk_pc_request(rq) ||
- rq->cmd_type == REQ_TYPE_ATA_PC) {
+ break;
+ case REQ_TYPE_SENSE:
+ case REQ_TYPE_BLOCK_PC:
+ case REQ_TYPE_ATA_PC:
if (!rq->timeout)
rq->timeout = ATAPI_WAIT_PC;
cdrom_do_block_pc(drive, rq);
- } else if (blk_special_request(rq)) {
+ break;
+ case REQ_TYPE_SPECIAL:
/* right now this can only be a reset... */
uptodate = 1;
goto out_end;
- } else
+ default:
BUG();
+ }
/* prepare sense request for this command */
ide_prep_sense(drive, rq);
@@ -817,7 +824,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
cmd.rq = rq;
- if (blk_fs_request(rq) || blk_rq_bytes(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS || blk_rq_bytes(rq)) {
ide_init_sg_cmd(&cmd, blk_rq_bytes(rq));
ide_map_sg(drive, &cmd);
}
@@ -1373,9 +1380,9 @@ static int ide_cdrom_prep_pc(struct request *rq)
static int ide_cdrom_prep_fn(struct request_queue *q, struct request *rq)
{
- if (blk_fs_request(rq))
+ if (rq->cmd_type == REQ_TYPE_FS)
return ide_cdrom_prep_fs(q, rq);
- else if (blk_pc_request(rq))
+ else if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
return ide_cdrom_prep_pc(rq);
return 0;
@@ -1592,17 +1599,19 @@ static struct ide_driver ide_cdrom_driver = {
static int idecd_open(struct block_device *bdev, fmode_t mode)
{
- struct cdrom_info *info = ide_cd_get(bdev->bd_disk);
- int rc = -ENOMEM;
+ struct cdrom_info *info;
+ int rc = -ENXIO;
+ lock_kernel();
+ info = ide_cd_get(bdev->bd_disk);
if (!info)
- return -ENXIO;
+ goto out;
rc = cdrom_open(&info->devinfo, bdev, mode);
-
if (rc < 0)
ide_cd_put(info);
-
+out:
+ unlock_kernel();
return rc;
}
@@ -1610,9 +1619,11 @@ static int idecd_release(struct gendisk *disk, fmode_t mode)
{
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
+ lock_kernel();
cdrom_release(&info->devinfo, mode);
ide_cd_put(info);
+ unlock_kernel();
return 0;
}
@@ -1656,7 +1667,7 @@ static int idecd_get_spindown(struct cdrom_device_info *cdi, unsigned long arg)
return 0;
}
-static int idecd_ioctl(struct block_device *bdev, fmode_t mode,
+static int idecd_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info);
@@ -1678,6 +1689,19 @@ static int idecd_ioctl(struct block_device *bdev, fmode_t mode,
return err;
}
+static int idecd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ lock_kernel();
+ ret = idecd_locked_ioctl(bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
+
+
static int idecd_media_changed(struct gendisk *disk)
{
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
@@ -1698,7 +1722,7 @@ static const struct block_device_operations idecd_ops = {
.owner = THIS_MODULE,
.open = idecd_open,
.release = idecd_release,
- .locked_ioctl = idecd_ioctl,
+ .ioctl = idecd_ioctl,
.media_changed = idecd_media_changed,
.revalidate_disk = idecd_revalidate_disk
};
diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c
index 02712bf..766b3de 100644
--- a/drivers/ide/ide-cd_ioctl.c
+++ b/drivers/ide/ide-cd_ioctl.c
@@ -454,7 +454,7 @@ int ide_cdrom_packet(struct cdrom_device_info *cdi,
touch it at all. */
if (cgc->data_direction == CGC_DATA_WRITE)
- flags |= REQ_RW;
+ flags |= REQ_WRITE;
if (cgc->sense)
memset(cgc->sense, 0, sizeof(struct request_sense));
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 33d6503..7433e07 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -184,7 +184,7 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
ide_hwif_t *hwif = drive->hwif;
BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED);
- BUG_ON(!blk_fs_request(rq));
+ BUG_ON(rq->cmd_type != REQ_TYPE_FS);
ledtrig_ide_activity();
@@ -427,10 +427,15 @@ static void ide_disk_unlock_native_capacity(ide_drive_t *drive)
drive->dev_flags |= IDE_DFLAG_NOHPA; /* disable HPA on resume */
}
-static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
+static int idedisk_prep_fn(struct request_queue *q, struct request *rq)
{
ide_drive_t *drive = q->queuedata;
- struct ide_cmd *cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
+ struct ide_cmd *cmd;
+
+ if (!(rq->cmd_flags & REQ_FLUSH))
+ return BLKPREP_OK;
+
+ cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
/* FIXME: map struct ide_taskfile on rq->cmd[] */
BUG_ON(cmd == NULL);
@@ -448,6 +453,8 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
rq->special = cmd;
cmd->rq = rq;
+
+ return BLKPREP_OK;
}
ide_devset_get(multcount, mult_count);
@@ -513,7 +520,6 @@ static void update_ordered(ide_drive_t *drive)
{
u16 *id = drive->id;
unsigned ordered = QUEUE_ORDERED_NONE;
- prepare_flush_fn *prep_fn = NULL;
if (drive->dev_flags & IDE_DFLAG_WCACHE) {
unsigned long long capacity;
@@ -538,12 +544,12 @@ static void update_ordered(ide_drive_t *drive)
if (barrier) {
ordered = QUEUE_ORDERED_DRAIN_FLUSH;
- prep_fn = idedisk_prepare_flush;
+ blk_queue_prep_rq(drive->queue, idedisk_prep_fn);
}
} else
ordered = QUEUE_ORDERED_DRAIN;
- blk_queue_ordered(drive->queue, ordered, prep_fn);
+ blk_queue_ordered(drive->queue, ordered);
}
ide_devset_get_flag(wcache, IDE_DFLAG_WCACHE);
diff --git a/drivers/ide/ide-disk_ioctl.c b/drivers/ide/ide-disk_ioctl.c
index 7b783dd..ec94c66 100644
--- a/drivers/ide/ide-disk_ioctl.c
+++ b/drivers/ide/ide-disk_ioctl.c
@@ -1,6 +1,7 @@
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/hdreg.h>
+#include <linux/smp_lock.h>
#include "ide-disk.h"
@@ -18,9 +19,13 @@ int ide_disk_ioctl(ide_drive_t *drive, struct block_device *bdev, fmode_t mode,
{
int err;
+ lock_kernel();
err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings);
if (err != -EOPNOTSUPP)
- return err;
+ goto out;
- return generic_ide_ioctl(drive, bdev, cmd, arg);
+ err = generic_ide_ioctl(drive, bdev, cmd, arg);
+out:
+ unlock_kernel();
+ return err;
}
diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c
index e9abf2c..c0aa93f 100644
--- a/drivers/ide/ide-eh.c
+++ b/drivers/ide/ide-eh.c
@@ -122,7 +122,7 @@ ide_startstop_t ide_error(ide_drive_t *drive, const char *msg, u8 stat)
return ide_stopped;
/* retry only "normal" I/O: */
- if (!blk_fs_request(rq)) {
+ if (rq->cmd_type != REQ_TYPE_FS) {
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
struct ide_cmd *cmd = rq->special;
@@ -146,7 +146,8 @@ static inline void ide_complete_drive_reset(ide_drive_t *drive, int err)
{
struct request *rq = drive->hwif->rq;
- if (rq && blk_special_request(rq) && rq->cmd[0] == REQ_DRIVE_RESET) {
+ if (rq && rq->cmd_type == REQ_TYPE_SPECIAL &&
+ rq->cmd[0] == REQ_DRIVE_RESET) {
if (err <= 0 && rq->errors == 0)
rq->errors = -EIO;
ide_complete_rq(drive, err ? err : 0, blk_rq_bytes(rq));
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index 4713bdc..5406b6e 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -73,7 +73,7 @@ static int ide_floppy_callback(ide_drive_t *drive, int dsc)
drive->failed_pc = NULL;
if (pc->c[0] == GPCMD_READ_10 || pc->c[0] == GPCMD_WRITE_10 ||
- (rq && blk_pc_request(rq)))
+ (rq && rq->cmd_type == REQ_TYPE_BLOCK_PC))
uptodate = 1; /* FIXME */
else if (pc->c[0] == GPCMD_REQUEST_SENSE) {
@@ -98,7 +98,7 @@ static int ide_floppy_callback(ide_drive_t *drive, int dsc)
"Aborting request!\n");
}
- if (blk_special_request(rq))
+ if (rq->cmd_type == REQ_TYPE_SPECIAL)
rq->errors = uptodate ? 0 : IDE_DRV_ERROR_GENERAL;
return uptodate;
@@ -207,7 +207,7 @@ static void idefloppy_create_rw_cmd(ide_drive_t *drive,
memcpy(rq->cmd, pc->c, 12);
pc->rq = rq;
- if (rq->cmd_flags & REQ_RW)
+ if (rq->cmd_flags & REQ_WRITE)
pc->flags |= PC_FLAG_WRITING;
pc->flags |= PC_FLAG_DMA_OK;
@@ -247,14 +247,16 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
} else
printk(KERN_ERR PFX "%s: I/O error\n", drive->name);
- if (blk_special_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_SPECIAL) {
rq->errors = 0;
ide_complete_rq(drive, 0, blk_rq_bytes(rq));
return ide_stopped;
} else
goto out_end;
}
- if (blk_fs_request(rq)) {
+
+ switch (rq->cmd_type) {
+ case REQ_TYPE_FS:
if (((long)blk_rq_pos(rq) % floppy->bs_factor) ||
(blk_rq_sectors(rq) % floppy->bs_factor)) {
printk(KERN_ERR PFX "%s: unsupported r/w rq size\n",
@@ -263,13 +265,18 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
}
pc = &floppy->queued_pc;
idefloppy_create_rw_cmd(drive, pc, rq, (unsigned long)block);
- } else if (blk_special_request(rq) || blk_sense_request(rq)) {
+ break;
+ case REQ_TYPE_SPECIAL:
+ case REQ_TYPE_SENSE:
pc = (struct ide_atapi_pc *)rq->special;
- } else if (blk_pc_request(rq)) {
+ break;
+ case REQ_TYPE_BLOCK_PC:
pc = &floppy->queued_pc;
idefloppy_blockpc_cmd(floppy, pc, rq);
- } else
+ break;
+ default:
BUG();
+ }
ide_prep_sense(drive, rq);
@@ -280,7 +287,7 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
cmd.rq = rq;
- if (blk_fs_request(rq) || blk_rq_bytes(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS || blk_rq_bytes(rq)) {
ide_init_sg_cmd(&cmd, blk_rq_bytes(rq));
ide_map_sg(drive, &cmd);
}
@@ -290,7 +297,7 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
return ide_floppy_issue_pc(drive, &cmd, pc);
out_end:
drive->failed_pc = NULL;
- if (blk_fs_request(rq) == 0 && rq->errors == 0)
+ if (rq->cmd_type != REQ_TYPE_FS && rq->errors == 0)
rq->errors = -EIO;
ide_complete_rq(drive, -EIO, blk_rq_bytes(rq));
return ide_stopped;
diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c
index 9c22882..fd3d05a 100644
--- a/drivers/ide/ide-floppy_ioctl.c
+++ b/drivers/ide/ide-floppy_ioctl.c
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/cdrom.h>
+#include <linux/smp_lock.h>
#include <asm/unaligned.h>
@@ -275,12 +276,15 @@ int ide_floppy_ioctl(ide_drive_t *drive, struct block_device *bdev,
void __user *argp = (void __user *)arg;
int err;
- if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR)
- return ide_floppy_lockdoor(drive, &pc, arg, cmd);
+ lock_kernel();
+ if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR) {
+ err = ide_floppy_lockdoor(drive, &pc, arg, cmd);
+ goto out;
+ }
err = ide_floppy_format_ioctl(drive, &pc, mode, cmd, argp);
if (err != -ENOTTY)
- return err;
+ goto out;
/*
* skip SCSI_IOCTL_SEND_COMMAND (deprecated)
@@ -293,5 +297,7 @@ int ide_floppy_ioctl(ide_drive_t *drive, struct block_device *bdev,
if (err == -ENOTTY)
err = generic_ide_ioctl(drive, bdev, cmd, arg);
+out:
+ unlock_kernel();
return err;
}
diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c
index 7939953..70aeeb1 100644
--- a/drivers/ide/ide-gd.c
+++ b/drivers/ide/ide-gd.c
@@ -1,3 +1,4 @@
+#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
@@ -237,6 +238,18 @@ out_put_idkp:
return ret;
}
+static int ide_gd_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ide_gd_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
+
static int ide_gd_release(struct gendisk *disk, fmode_t mode)
{
struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
@@ -244,6 +257,7 @@ static int ide_gd_release(struct gendisk *disk, fmode_t mode)
ide_debug_log(IDE_DBG_FUNC, "enter");
+ lock_kernel();
if (idkp->openers == 1)
drive->disk_ops->flush(drive);
@@ -255,6 +269,7 @@ static int ide_gd_release(struct gendisk *disk, fmode_t mode)
idkp->openers--;
ide_disk_put(idkp);
+ unlock_kernel();
return 0;
}
@@ -321,9 +336,9 @@ static int ide_gd_ioctl(struct block_device *bdev, fmode_t mode,
static const struct block_device_operations ide_gd_ops = {
.owner = THIS_MODULE,
- .open = ide_gd_open,
+ .open = ide_gd_unlocked_open,
.release = ide_gd_release,
- .locked_ioctl = ide_gd_ioctl,
+ .ioctl = ide_gd_ioctl,
.getgeo = ide_gd_getgeo,
.media_changed = ide_gd_media_changed,
.unlock_native_capacity = ide_gd_unlock_native_capacity,
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index 172ac92..a381be8 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -135,7 +135,7 @@ EXPORT_SYMBOL(ide_complete_rq);
void ide_kill_rq(ide_drive_t *drive, struct request *rq)
{
- u8 drv_req = blk_special_request(rq) && rq->rq_disk;
+ u8 drv_req = (rq->cmd_type == REQ_TYPE_SPECIAL) && rq->rq_disk;
u8 media = drive->media;
drive->failed_pc = NULL;
@@ -145,7 +145,7 @@ void ide_kill_rq(ide_drive_t *drive, struct request *rq)
} else {
if (media == ide_tape)
rq->errors = IDE_DRV_ERROR_GENERAL;
- else if (blk_fs_request(rq) == 0 && rq->errors == 0)
+ else if (rq->cmd_type != REQ_TYPE_FS && rq->errors == 0)
rq->errors = -EIO;
}
@@ -307,7 +307,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
- BUG_ON(!blk_rq_started(rq));
+ BUG_ON(!(rq->cmd_flags & REQ_STARTED));
#ifdef DEBUG
printk("%s: start_request: current=0x%08lx\n",
@@ -353,7 +353,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
pm->pm_step == IDE_PM_COMPLETED)
ide_complete_pm_rq(drive, rq);
return startstop;
- } else if (!rq->rq_disk && blk_special_request(rq))
+ } else if (!rq->rq_disk && rq->cmd_type == REQ_TYPE_SPECIAL)
/*
* TODO: Once all ULDs have been modified to
* check for specific op codes rather than
diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c
index 1c08311..9240609 100644
--- a/drivers/ide/ide-pm.c
+++ b/drivers/ide/ide-pm.c
@@ -191,10 +191,10 @@ void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
#ifdef DEBUG_PM
printk("%s: completing PM request, %s\n", drive->name,
- blk_pm_suspend_request(rq) ? "suspend" : "resume");
+ (rq->cmd_type == REQ_TYPE_PM_SUSPEND) ? "suspend" : "resume");
#endif
spin_lock_irqsave(q->queue_lock, flags);
- if (blk_pm_suspend_request(rq))
+ if (rq->cmd_type == REQ_TYPE_PM_SUSPEND)
blk_stop_queue(q);
else
drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
@@ -210,11 +210,11 @@ void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
{
struct request_pm_state *pm = rq->special;
- if (blk_pm_suspend_request(rq) &&
+ if (rq->cmd_type == REQ_TYPE_PM_SUSPEND &&
pm->pm_step == IDE_PM_START_SUSPEND)
/* Mark drive blocked when starting the suspend sequence. */
drive->dev_flags |= IDE_DFLAG_BLOCKED;
- else if (blk_pm_resume_request(rq) &&
+ else if (rq->cmd_type == REQ_TYPE_PM_RESUME &&
pm->pm_step == IDE_PM_START_RESUME) {
/*
* The first thing we do on wakeup is to wait for BSY bit to
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index b072328..6d622cb 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -32,6 +32,7 @@
#include <linux/errno.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/ide.h>
@@ -577,7 +578,8 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
rq->cmd[0], (unsigned long long)blk_rq_pos(rq),
blk_rq_sectors(rq));
- BUG_ON(!(blk_special_request(rq) || blk_sense_request(rq)));
+ BUG_ON(!(rq->cmd_type == REQ_TYPE_SPECIAL ||
+ rq->cmd_type == REQ_TYPE_SENSE));
/* Retry a failed packet command */
if (drive->failed_pc && drive->pc->c[0] == REQUEST_SENSE) {
@@ -1905,7 +1907,11 @@ static const struct file_operations idetape_fops = {
static int idetape_open(struct block_device *bdev, fmode_t mode)
{
- struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk, false, 0);
+ struct ide_tape_obj *tape;
+
+ lock_kernel();
+ tape = ide_tape_get(bdev->bd_disk, false, 0);
+ unlock_kernel();
if (!tape)
return -ENXIO;
@@ -1917,7 +1923,10 @@ static int idetape_release(struct gendisk *disk, fmode_t mode)
{
struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj);
+ lock_kernel();
ide_tape_put(tape);
+ unlock_kernel();
+
return 0;
}
@@ -1926,9 +1935,14 @@ static int idetape_ioctl(struct block_device *bdev, fmode_t mode,
{
struct ide_tape_obj *tape = ide_drv_g(bdev->bd_disk, ide_tape_obj);
ide_drive_t *drive = tape->drive;
- int err = generic_ide_ioctl(drive, bdev, cmd, arg);
+ int err;
+
+ lock_kernel();
+ err = generic_ide_ioctl(drive, bdev, cmd, arg);
if (err == -EINVAL)
err = idetape_blkdev_ioctl(drive, cmd, arg);
+ unlock_kernel();
+
return err;
}
@@ -1936,7 +1950,7 @@ static const struct block_device_operations idetape_block_ops = {
.owner = THIS_MODULE,
.open = idetape_open,
.release = idetape_release,
- .locked_ioctl = idetape_ioctl,
+ .ioctl = idetape_ioctl,
};
static int ide_tape_probe(ide_drive_t *drive)
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 3cb9c4e..fa89621 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(ide_pci_clk);
module_param_named(pci_clock, ide_pci_clk, int, 0);
MODULE_PARM_DESC(pci_clock, "PCI bus clock frequency (in MHz)");
-static int ide_set_dev_param_mask(const char *s, struct kernel_param *kp)
+static int ide_set_dev_param_mask(const char *s, const struct kernel_param *kp)
{
int a, b, i, j = 1;
unsigned int *dev_param_mask = (unsigned int *)kp->arg;
@@ -200,34 +200,40 @@ static int ide_set_dev_param_mask(const char *s, struct kernel_param *kp)
return 0;
}
+static struct kernel_param_ops param_ops_ide_dev_mask = {
+ .set = ide_set_dev_param_mask
+};
+
+#define param_check_ide_dev_mask(name, p) param_check_uint(name, p)
+
static unsigned int ide_nodma;
-module_param_call(nodma, ide_set_dev_param_mask, NULL, &ide_nodma, 0);
+module_param_named(nodma, ide_nodma, ide_dev_mask, 0);
MODULE_PARM_DESC(nodma, "disallow DMA for a device");
static unsigned int ide_noflush;
-module_param_call(noflush, ide_set_dev_param_mask, NULL, &ide_noflush, 0);
+module_param_named(noflush, ide_noflush, ide_dev_mask, 0);
MODULE_PARM_DESC(noflush, "disable flush requests for a device");
static unsigned int ide_nohpa;
-module_param_call(nohpa, ide_set_dev_param_mask, NULL, &ide_nohpa, 0);
+module_param_named(nohpa, ide_nohpa, ide_dev_mask, 0);
MODULE_PARM_DESC(nohpa, "disable Host Protected Area for a device");
static unsigned int ide_noprobe;
-module_param_call(noprobe, ide_set_dev_param_mask, NULL, &ide_noprobe, 0);
+module_param_named(noprobe, ide_noprobe, ide_dev_mask, 0);
MODULE_PARM_DESC(noprobe, "skip probing for a device");
static unsigned int ide_nowerr;
-module_param_call(nowerr, ide_set_dev_param_mask, NULL, &ide_nowerr, 0);
+module_param_named(nowerr, ide_nowerr, ide_dev_mask, 0);
MODULE_PARM_DESC(nowerr, "ignore the ATA_DF bit for a device");
static unsigned int ide_cdroms;
-module_param_call(cdrom, ide_set_dev_param_mask, NULL, &ide_cdroms, 0);
+module_param_named(cdrom, ide_cdroms, ide_dev_mask, 0);
MODULE_PARM_DESC(cdrom, "force device as a CD-ROM");
struct chs_geom {
diff --git a/drivers/idle/Kconfig b/drivers/idle/Kconfig
index fb5c518..8489eb5 100644
--- a/drivers/idle/Kconfig
+++ b/drivers/idle/Kconfig
@@ -1,9 +1,8 @@
config INTEL_IDLE
- tristate "Cpuidle Driver for Intel Processors"
+ bool "Cpuidle Driver for Intel Processors"
depends on CPU_IDLE
depends on X86
depends on CPU_SUP_INTEL
- depends on EXPERIMENTAL
help
Enable intel_idle, a cpuidle driver that includes knowledge of
native Intel hardware idle features. The acpi_idle driver
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 03d202b..a10152b 100755
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -77,10 +77,8 @@ static struct cpuidle_driver intel_idle_driver = {
};
/* intel_idle.max_cstate=0 disables driver */
static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1;
-static int power_policy = 7; /* 0 = max perf; 15 = max powersave */
-static unsigned int substates;
-static int (*choose_substate)(int);
+static unsigned int mwait_substates;
/* Reliable LAPIC Timer States, bit 1 for C1 etc. */
static unsigned int lapic_timer_reliable_states;
@@ -168,41 +166,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
.enter = NULL }, /* disabled */
};
-/*
- * choose_tunable_substate()
- *
- * Run-time decision on which C-state substate to invoke
- * If power_policy = 0, choose shallowest substate (0)
- * If power_policy = 15, choose deepest substate
- * If power_policy = middle, choose middle substate etc.
- */
-static int choose_tunable_substate(int cstate)
-{
- unsigned int num_substates;
- unsigned int substate_choice;
-
- power_policy &= 0xF; /* valid range: 0-15 */
- cstate &= 7; /* valid range: 0-7 */
-
- num_substates = (substates >> ((cstate) * 4)) & MWAIT_SUBSTATE_MASK;
-
- if (num_substates <= 1)
- return 0;
-
- substate_choice = ((power_policy + (power_policy + 1) *
- (num_substates - 1)) / 16);
-
- return substate_choice;
-}
-
-/*
- * choose_zero_substate()
- */
-static int choose_zero_substate(int cstate)
-{
- return 0;
-}
-
/**
* intel_idle
* @dev: cpuidle_device
@@ -220,8 +183,6 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1;
- eax = eax + (choose_substate)(cstate);
-
local_irq_disable();
if (!(lapic_timer_reliable_states & (1 << (cstate))))
@@ -259,7 +220,7 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
*/
static int intel_idle_probe(void)
{
- unsigned int eax, ebx, ecx, edx;
+ unsigned int eax, ebx, ecx;
if (max_cstate == 0) {
pr_debug(PREFIX "disabled\n");
@@ -275,17 +236,13 @@ static int intel_idle_probe(void)
if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
return -ENODEV;
- cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
+ cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &mwait_substates);
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
!(ecx & CPUID5_ECX_INTERRUPT_BREAK))
return -ENODEV;
-#ifdef DEBUG
- if (substates == 0) /* can over-ride via modparam */
-#endif
- substates = edx;
- pr_debug(PREFIX "MWAIT substates: 0x%x\n", substates);
+ pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates);
if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */
lapic_timer_reliable_states = 0xFFFFFFFF;
@@ -299,18 +256,18 @@ static int intel_idle_probe(void)
case 0x1E: /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
case 0x2E: /* Nehalem-EX Xeon */
+ case 0x2F: /* Westmere-EX Xeon */
lapic_timer_reliable_states = (1 << 1); /* C1 */
case 0x25: /* Westmere */
case 0x2C: /* Westmere */
cpuidle_state_table = nehalem_cstates;
- choose_substate = choose_tunable_substate;
break;
case 0x1C: /* 28 - Atom Processor */
+ case 0x26: /* 38 - Lincroft Atom Processor */
lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */
cpuidle_state_table = atom_cstates;
- choose_substate = choose_zero_substate;
break;
#ifdef FUTURE_USE
case 0x17: /* 23 - Core 2 Duo */
@@ -376,7 +333,7 @@ static int intel_idle_cpuidle_devices_init(void)
}
/* does the state exist in CPUID.MWAIT? */
- num_substates = (substates >> ((cstate) * 4))
+ num_substates = (mwait_substates >> ((cstate) * 4))
& MWAIT_SUBSTATE_MASK;
if (num_substates == 0)
continue;
@@ -450,11 +407,7 @@ static void __exit intel_idle_exit(void)
module_init(intel_idle_init);
module_exit(intel_idle_exit);
-module_param(power_policy, int, 0644);
module_param(max_cstate, int, 0444);
-#ifdef DEBUG
-module_param(substates, int, 0444);
-#endif
MODULE_AUTHOR("Len Brown <len.brown@intel.com>");
MODULE_DESCRIPTION("Cpuidle driver for Intel Hardware v" INTEL_IDLE_VERSION);
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index d870f9c..9bbf491 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -250,12 +250,17 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
rdev->cqshift = PAGE_SHIFT - ilog2(rdev->lldi.ucq_density);
rdev->cqmask = rdev->lldi.ucq_density - 1;
PDBG("%s dev %s stag start 0x%0x size 0x%0x num stags %d "
- "pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x\n",
+ "pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x "
+ "qp qid start %u size %u cq qid start %u size %u\n",
__func__, pci_name(rdev->lldi.pdev), rdev->lldi.vr->stag.start,
rdev->lldi.vr->stag.size, c4iw_num_stags(rdev),
rdev->lldi.vr->pbl.start,
rdev->lldi.vr->pbl.size, rdev->lldi.vr->rq.start,
- rdev->lldi.vr->rq.size);
+ rdev->lldi.vr->rq.size,
+ rdev->lldi.vr->qp.start,
+ rdev->lldi.vr->qp.size,
+ rdev->lldi.vr->cq.start,
+ rdev->lldi.vr->cq.size);
PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu "
"qpmask 0x%x cqshift %lu cqmask 0x%x\n",
(unsigned)pci_resource_len(rdev->lldi.pdev, 2),
diff --git a/drivers/infiniband/hw/cxgb4/resource.c b/drivers/infiniband/hw/cxgb4/resource.c
index fb195d1..83b23df 100644
--- a/drivers/infiniband/hw/cxgb4/resource.c
+++ b/drivers/infiniband/hw/cxgb4/resource.c
@@ -110,11 +110,12 @@ static int c4iw_init_qid_fifo(struct c4iw_rdev *rdev)
spin_lock_init(&rdev->resource.qid_fifo_lock);
- if (kfifo_alloc(&rdev->resource.qid_fifo, T4_MAX_QIDS * sizeof(u32),
- GFP_KERNEL))
+ if (kfifo_alloc(&rdev->resource.qid_fifo, rdev->lldi.vr->qp.size *
+ sizeof(u32), GFP_KERNEL))
return -ENOMEM;
- for (i = T4_QID_BASE; i < T4_QID_BASE + T4_MAX_QIDS; i++)
+ for (i = rdev->lldi.vr->qp.start;
+ i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++)
if (!(i & rdev->qpmask))
kfifo_in(&rdev->resource.qid_fifo,
(unsigned char *) &i, sizeof(u32));
diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h
index aef55f4..24f3690 100644
--- a/drivers/infiniband/hw/cxgb4/t4.h
+++ b/drivers/infiniband/hw/cxgb4/t4.h
@@ -36,8 +36,6 @@
#include "t4_msg.h"
#include "t4fw_ri_api.h"
-#define T4_QID_BASE 1024
-#define T4_MAX_QIDS 256
#define T4_MAX_NUM_QP (1<<16)
#define T4_MAX_NUM_CQ (1<<15)
#define T4_MAX_NUM_PD (1<<15)
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index 0136abd..aaf6023 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -112,7 +112,7 @@ struct ehca_sport {
struct ehca_shca {
struct ib_device ib_device;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
u8 num_ports;
int hw_level;
struct list_head shca_list;
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index cfc4de7..c240e99 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -713,7 +713,7 @@ static struct attribute_group ehca_dev_attr_grp = {
.attrs = ehca_dev_attrs
};
-static int __devinit ehca_probe(struct of_device *dev,
+static int __devinit ehca_probe(struct platform_device *dev,
const struct of_device_id *id)
{
struct ehca_shca *shca;
@@ -879,7 +879,7 @@ probe1:
return -EINVAL;
}
-static int __devexit ehca_remove(struct of_device *dev)
+static int __devexit ehca_remove(struct platform_device *dev)
{
struct ehca_shca *shca = dev_get_drvdata(&dev->dev);
unsigned long flags;
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 054edf3..c908c5f 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -492,13 +492,15 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
}
#define OLD_KEY_MAX 0x1ff
-static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user *p, int compat_mode)
+static int handle_eviocgbit(struct input_dev *dev,
+ unsigned int type, unsigned int size,
+ void __user *p, int compat_mode)
{
static unsigned long keymax_warn_time;
unsigned long *bits;
int len;
- switch (_IOC_NR(cmd) & EV_MAX) {
+ switch (type) {
case 0: bits = dev->evbit; len = EV_MAX; break;
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
@@ -517,7 +519,7 @@ static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user
* EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len'
* should be in bytes, not in bits.
*/
- if ((_IOC_NR(cmd) & EV_MAX) == EV_KEY && _IOC_SIZE(cmd) == OLD_KEY_MAX) {
+ if (type == EV_KEY && size == OLD_KEY_MAX) {
len = OLD_KEY_MAX;
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
printk(KERN_WARNING
@@ -528,7 +530,7 @@ static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user
BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
}
- return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
+ return bits_to_user(bits, len, size, p, compat_mode);
}
#undef OLD_KEY_MAX
@@ -542,8 +544,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
+ unsigned int size;
int error;
+ /* First we check for fixed-length commands */
switch (cmd) {
case EVIOCGVERSION:
@@ -610,112 +614,102 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return evdev_grab(evdev, client);
else
return evdev_ungrab(evdev, client);
+ }
- default:
-
- if (_IOC_TYPE(cmd) != 'E')
- return -EINVAL;
-
- if (_IOC_DIR(cmd) == _IOC_READ) {
+ size = _IOC_SIZE(cmd);
- if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
- return handle_eviocgbit(dev, cmd, p, compat_mode);
+ /* Now check variable-length commands */
+#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
- return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ switch (EVIOC_MASK_SIZE(cmd)) {
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
- return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ case EVIOCGKEY(0):
+ return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
- return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ case EVIOCGLED(0):
+ return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
- return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ case EVIOCGSND(0):
+ return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
- return str_to_user(dev->name, _IOC_SIZE(cmd), p);
+ case EVIOCGSW(0):
+ return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
- return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
+ case EVIOCGNAME(0):
+ return str_to_user(dev->name, size, p);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
- return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
+ case EVIOCGPHYS(0):
+ return str_to_user(dev->phys, size, p);
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+ case EVIOCGUNIQ(0):
+ return str_to_user(dev->uniq, size, p);
- t = _IOC_NR(cmd) & ABS_MAX;
+ case EVIOC_MASK_SIZE(EVIOCSFF):
+ if (input_ff_effect_from_user(p, size, &effect))
+ return -EFAULT;
- abs.value = dev->abs[t];
- abs.minimum = dev->absmin[t];
- abs.maximum = dev->absmax[t];
- abs.fuzz = dev->absfuzz[t];
- abs.flat = dev->absflat[t];
- abs.resolution = dev->absres[t];
+ error = input_ff_upload(dev, &effect, file);
- if (copy_to_user(p, &abs, min_t(size_t,
- _IOC_SIZE(cmd),
- sizeof(struct input_absinfo))))
- return -EFAULT;
+ if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+ return -EFAULT;
- return 0;
- }
+ return error;
+ }
- }
+ /* Multi-number variable-length handlers */
+ if (_IOC_TYPE(cmd) != 'E')
+ return -EINVAL;
- if (_IOC_DIR(cmd) == _IOC_WRITE) {
+ if (_IOC_DIR(cmd) == _IOC_READ) {
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
+ if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
+ return handle_eviocgbit(dev,
+ _IOC_NR(cmd) & EV_MAX, size,
+ p, compat_mode);
- if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
- return -EFAULT;
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
- error = input_ff_upload(dev, &effect, file);
+ t = _IOC_NR(cmd) & ABS_MAX;
+ abs = dev->absinfo[t];
- if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
- return -EFAULT;
+ if (copy_to_user(p, &abs, min_t(size_t,
+ size, sizeof(struct input_absinfo))))
+ return -EFAULT;
- return error;
- }
+ return 0;
+ }
+ }
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+ if (_IOC_DIR(cmd) == _IOC_READ) {
- t = _IOC_NR(cmd) & ABS_MAX;
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
- if (copy_from_user(&abs, p, min_t(size_t,
- _IOC_SIZE(cmd),
- sizeof(struct input_absinfo))))
- return -EFAULT;
+ t = _IOC_NR(cmd) & ABS_MAX;
- /* We can't change number of reserved MT slots */
- if (t == ABS_MT_SLOT)
- return -EINVAL;
+ if (copy_from_user(&abs, p, min_t(size_t,
+ size, sizeof(struct input_absinfo))))
+ return -EFAULT;
- /*
- * Take event lock to ensure that we are not
- * changing device parameters in the middle
- * of event.
- */
- spin_lock_irq(&dev->event_lock);
+ if (size < sizeof(struct input_absinfo))
+ abs.resolution = 0;
- dev->abs[t] = abs.value;
- dev->absmin[t] = abs.minimum;
- dev->absmax[t] = abs.maximum;
- dev->absfuzz[t] = abs.fuzz;
- dev->absflat[t] = abs.flat;
- dev->absres[t] = _IOC_SIZE(cmd) < sizeof(struct input_absinfo) ?
- 0 : abs.resolution;
+ /* We can't change number of reserved MT slots */
+ if (t == ABS_MT_SLOT)
+ return -EINVAL;
- spin_unlock_irq(&dev->event_lock);
+ /*
+ * Take event lock to ensure that we are not
+ * changing device parameters in the middle
+ * of event.
+ */
+ spin_lock_irq(&dev->event_lock);
+ dev->absinfo[t] = abs;
+ spin_unlock_irq(&dev->event_lock);
- return 0;
- }
+ return 0;
}
}
+
return -EINVAL;
}
diff --git a/drivers/input/input.c b/drivers/input/input.c
index e1243b4..a9b025f 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -182,7 +182,7 @@ static int input_handle_abs_event(struct input_dev *dev,
is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST;
if (!is_mt_event) {
- pold = &dev->abs[code];
+ pold = &dev->absinfo[code].value;
} else if (dev->mt) {
struct input_mt_slot *mtslot = &dev->mt[dev->slot];
pold = &mtslot->abs[code - ABS_MT_FIRST];
@@ -196,7 +196,7 @@ static int input_handle_abs_event(struct input_dev *dev,
if (pold) {
*pval = input_defuzz_abs_event(*pval, *pold,
- dev->absfuzz[code]);
+ dev->absinfo[code].fuzz);
if (*pold == *pval)
return INPUT_IGNORE_EVENT;
@@ -204,8 +204,8 @@ static int input_handle_abs_event(struct input_dev *dev,
}
/* Flush pending "slot" event */
- if (is_mt_event && dev->slot != dev->abs[ABS_MT_SLOT]) {
- dev->abs[ABS_MT_SLOT] = dev->slot;
+ if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
+ input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
}
@@ -391,6 +391,43 @@ void input_inject_event(struct input_handle *handle,
EXPORT_SYMBOL(input_inject_event);
/**
+ * input_alloc_absinfo - allocates array of input_absinfo structs
+ * @dev: the input device emitting absolute events
+ *
+ * If the absinfo struct the caller asked for is already allocated, this
+ * functions will not do anything.
+ */
+void input_alloc_absinfo(struct input_dev *dev)
+{
+ if (!dev->absinfo)
+ dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo),
+ GFP_KERNEL);
+
+ WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__);
+}
+EXPORT_SYMBOL(input_alloc_absinfo);
+
+void input_set_abs_params(struct input_dev *dev, unsigned int axis,
+ int min, int max, int fuzz, int flat)
+{
+ struct input_absinfo *absinfo;
+
+ input_alloc_absinfo(dev);
+ if (!dev->absinfo)
+ return;
+
+ absinfo = &dev->absinfo[axis];
+ absinfo->minimum = min;
+ absinfo->maximum = max;
+ absinfo->fuzz = fuzz;
+ absinfo->flat = flat;
+
+ dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
+}
+EXPORT_SYMBOL(input_set_abs_params);
+
+
+/**
* input_grab_device - grabs device for exclusive use
* @handle: input handle that wants to own the device
*
@@ -1308,6 +1345,7 @@ static void input_dev_release(struct device *device)
input_ff_destroy(dev);
input_mt_destroy_slots(dev);
+ kfree(dev->absinfo);
kfree(dev);
module_put(THIS_MODULE);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 6383458..d85bd8a 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -530,7 +530,7 @@ static int joydev_ioctl_common(struct joydev *joydev,
{
struct input_dev *dev = joydev->handle.dev;
size_t len;
- int i, j;
+ int i;
const char *name;
/* Process fixed-sized commands. */
@@ -562,12 +562,11 @@ static int joydev_ioctl_common(struct joydev *joydev,
case JSIOCSCORR:
if (copy_from_user(joydev->corr, argp,
sizeof(joydev->corr[0]) * joydev->nabs))
- return -EFAULT;
+ return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
- j = joydev->abspam[i];
- joydev->abs[i] = joydev_correct(dev->abs[j],
- &joydev->corr[i]);
+ int val = input_abs_get_val(dev, joydev->abspam[i]);
+ joydev->abs[i] = joydev_correct(val, &joydev->corr[i]);
}
return 0;
@@ -848,25 +847,27 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
for (i = 0; i < joydev->nabs; i++) {
j = joydev->abspam[i];
- if (dev->absmax[j] == dev->absmin[j]) {
+ if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) {
joydev->corr[i].type = JS_CORR_NONE;
- joydev->abs[i] = dev->abs[j];
+ joydev->abs[i] = input_abs_get_val(dev, j);
continue;
}
joydev->corr[i].type = JS_CORR_BROKEN;
- joydev->corr[i].prec = dev->absfuzz[j];
- joydev->corr[i].coef[0] =
- (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
- joydev->corr[i].coef[1] =
- (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+ joydev->corr[i].prec = input_abs_get_fuzz(dev, j);
+
+ t = (input_abs_get_max(dev, j) + input_abs_get_min(dev, j)) / 2;
+ joydev->corr[i].coef[0] = t - input_abs_get_flat(dev, j);
+ joydev->corr[i].coef[1] = t + input_abs_get_flat(dev, j);
- t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j];
+ t = (input_abs_get_max(dev, j) - input_abs_get_min(dev, j)) / 2
+ - 2 * input_abs_get_flat(dev, j);
if (t) {
joydev->corr[i].coef[2] = (1 << 29) / t;
joydev->corr[i].coef[3] = (1 << 29) / t;
- joydev->abs[i] = joydev_correct(dev->abs[j],
- joydev->corr + i);
+ joydev->abs[i] =
+ joydev_correct(input_abs_get_val(dev, j),
+ joydev->corr + i);
}
}
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
index 6489f40..d259b41 100644
--- a/drivers/input/joystick/a3d.c
+++ b/drivers/input/joystick/a3d.c
@@ -342,7 +342,8 @@ static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv)
for (i = 0; i < 4; i++) {
if (i < 2)
- input_set_abs_params(input_dev, axes[i], 48, input_dev->abs[axes[i]] * 2 - 48, 0, 8);
+ input_set_abs_params(input_dev, axes[i],
+ 48, input_abs_get_val(input_dev, axes[i]) * 2 - 48, 0, 8);
else
input_set_abs_params(input_dev, axes[i], 2, 253, 0, 0);
input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
index 89c4c08..b992fbf 100644
--- a/drivers/input/joystick/adi.c
+++ b/drivers/input/joystick/adi.c
@@ -452,7 +452,7 @@ static void adi_init_center(struct adi *adi)
for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) {
t = adi->abs[i];
- x = adi->dev->abs[t];
+ x = input_abs_get_val(adi->dev, t);
if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE)
x = i < adi->axes10 ? 512 : 128;
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
index 05022f0..0bc8620 100644
--- a/drivers/input/joystick/amijoy.c
+++ b/drivers/input/joystick/amijoy.c
@@ -139,8 +139,8 @@ static int __init amijoy_init(void)
amijoy_dev[i]->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
for (j = 0; j < 2; j++) {
- amijoy_dev[i]->absmin[ABS_X + j] = -1;
- amijoy_dev[i]->absmax[ABS_X + j] = 1;
+ input_set_abs_params(amijoy_dev[i], ABS_X + j,
+ -1, 1, 0, 0);
}
err = input_register_device(amijoy_dev[i]);
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
index 45ac70e..0536b1b 100644
--- a/drivers/input/joystick/gf2k.c
+++ b/drivers/input/joystick/gf2k.c
@@ -318,11 +318,8 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
for (i = 0; i < gf2k_axes[gf2k->id]; i++)
set_bit(gf2k_abs[i], input_dev->absbit);
- for (i = 0; i < gf2k_hats[gf2k->id]; i++) {
- set_bit(ABS_HAT0X + i, input_dev->absbit);
- input_dev->absmin[ABS_HAT0X + i] = -1;
- input_dev->absmax[ABS_HAT0X + i] = 1;
- }
+ for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+ input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
for (i = 0; i < gf2k_joys[gf2k->id]; i++)
set_bit(gf2k_btn_joy[i], input_dev->keybit);
@@ -334,11 +331,14 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
gf2k_read(gf2k, data);
for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
- input_dev->absmax[gf2k_abs[i]] = (i < 2) ? input_dev->abs[gf2k_abs[i]] * 2 - 32 :
- input_dev->abs[gf2k_abs[0]] + input_dev->abs[gf2k_abs[1]] - 32;
- input_dev->absmin[gf2k_abs[i]] = 32;
- input_dev->absfuzz[gf2k_abs[i]] = 8;
- input_dev->absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0;
+ int max = i < 2 ?
+ input_abs_get_val(input_dev, gf2k_abs[i]) * 2 :
+ input_abs_get_val(input_dev, gf2k_abs[0]) +
+ input_abs_get_val(input_dev, gf2k_abs[1]);
+ int flat = i < 2 ? 24 : 0;
+
+ input_set_abs_params(input_dev, gf2k_abs[i],
+ 32, max - 32, 8, flat);
}
err = input_register_device(gf2k->dev);
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
index 2478289..16fb19d 100644
--- a/drivers/input/joystick/interact.c
+++ b/drivers/input/joystick/interact.c
@@ -270,18 +270,14 @@ static int interact_connect(struct gameport *gameport, struct gameport_driver *d
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
- set_bit(t, input_dev->absbit);
- if (i < interact_type[interact->type].b8) {
- input_dev->absmin[t] = 0;
- input_dev->absmax[t] = 255;
- } else {
- input_dev->absmin[t] = -1;
- input_dev->absmax[t] = 1;
- }
+ if (i < interact_type[interact->type].b8)
+ input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+ else
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
}
for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
- set_bit(t, input_dev->keybit);
+ __set_bit(t, input_dev->keybit);
err = input_register_device(interact->dev);
if (err)
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
index ca13a6b..b8d8611 100644
--- a/drivers/input/joystick/sidewinder.c
+++ b/drivers/input/joystick/sidewinder.c
@@ -761,17 +761,21 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
+ int min, max, fuzz, flat;
+
code = sw_abs[sw->type][j];
- set_bit(code, input_dev->absbit);
- input_dev->absmax[code] = (1 << bits) - 1;
- input_dev->absmin[code] = (bits == 1) ? -1 : 0;
- input_dev->absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0;
- if (code != ABS_THROTTLE)
- input_dev->absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0;
+ min = bits == 1 ? -1 : 0;
+ max = (1 << bits) - 1;
+ fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0;
+ flat = code == ABS_THROTTLE || bits < 5 ?
+ 0 : 1 << (bits - 5);
+
+ input_set_abs_params(input_dev, code,
+ min, max, fuzz, flat);
}
for (j = 0; (code = sw_btn[sw->type][j]); j++)
- set_bit(code, input_dev->keybit);
+ __set_bit(code, input_dev->keybit);
dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k);
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 269a846..f9fb7fa 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -148,6 +148,7 @@ static const struct xpad_device {
{ 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX },
{ 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX },
{ 0x0e6f, 0x0006, "Pelican 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
{ 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX },
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b171f63..9cc488d2 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -395,6 +395,16 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
+config KEYBOARD_STMPE
+ tristate "STMPE keypad support"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want to use the keypad controller on STMPE I/O
+ expanders.
+
+ To compile this driver as a module, choose M here: the module will be
+ called stmpe-keypad.
+
config KEYBOARD_DAVINCI
tristate "TI DaVinci Key Scan"
depends on ARCH_DAVINCI_DM365
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1a66d5f..504b591 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index a9fd147..6069abe 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -39,6 +39,8 @@ struct gpio_keys_drvdata {
struct input_dev *input;
struct mutex disable_lock;
unsigned int n_buttons;
+ int (*enable)(struct device *dev);
+ void (*disable)(struct device *dev);
struct gpio_button_data data[0];
};
@@ -423,6 +425,21 @@ fail2:
return error;
}
+static int gpio_keys_open(struct input_dev *input)
+{
+ struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
+
+ return ddata->enable ? ddata->enable(input->dev.parent) : 0;
+}
+
+static void gpio_keys_close(struct input_dev *input)
+{
+ struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
+
+ if (ddata->disable)
+ ddata->disable(input->dev.parent);
+}
+
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
@@ -444,13 +461,18 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
ddata->input = input;
ddata->n_buttons = pdata->nbuttons;
+ ddata->enable = pdata->enable;
+ ddata->disable = pdata->disable;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev, ddata);
+ input_set_drvdata(input, ddata);
input->name = pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
+ input->open = gpio_keys_open;
+ input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index c83f4b2..dcc86b9 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -232,15 +232,16 @@ static void hil_dev_handle_ptr_events(struct hil_dev *ptr)
if (absdev) {
val = lo + (hi << 8);
#ifdef TABLET_AUTOADJUST
- if (val < dev->absmin[ABS_X + i])
- dev->absmin[ABS_X + i] = val;
- if (val > dev->absmax[ABS_X + i])
- dev->absmax[ABS_X + i] = val;
+ if (val < input_abs_min(dev, ABS_X + i))
+ input_abs_set_min(dev, ABS_X + i, val);
+ if (val > input_abs_max(dev, ABS_X + i))
+ input_abs_set_max(dev, ABS_X + i, val);
#endif
- if (i%3) val = dev->absmax[ABS_X + i] - val;
+ if (i % 3)
+ val = input_abs_max(dev, ABS_X + i) - val;
input_report_abs(dev, ABS_X + i, val);
} else {
- val = (int) (((int8_t)lo) | ((int8_t)hi << 8));
+ val = (int) (((int8_t) lo) | ((int8_t) hi << 8));
if (i % 3)
val *= -1;
input_report_rel(dev, REL_X + i, val);
@@ -387,9 +388,11 @@ static void hil_dev_pointer_setup(struct hil_dev *ptr)
#ifdef TABLET_AUTOADJUST
for (i = 0; i < ABS_MAX; i++) {
- int diff = input_dev->absmax[ABS_X + i] / 10;
- input_dev->absmin[ABS_X + i] += diff;
- input_dev->absmax[ABS_X + i] -= diff;
+ int diff = input_abs_max(input_dev, ABS_X + i) / 10;
+ input_abs_set_min(input_dev, ABS_X + i,
+ input_abs_min(input_dev, ABS_X + i) + diff)
+ input_abs_set_max(input_dev, ABS_X + i,
+ input_abs_max(input_dev, ABS_X + i) - diff)
}
#endif
diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
new file mode 100644
index 0000000..ab7610c
--- /dev/null
+++ b/drivers/input/keyboard/stmpe-keypad.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/stmpe.h>
+
+/* These are at the same addresses in all STMPE variants */
+#define STMPE_KPC_COL 0x60
+#define STMPE_KPC_ROW_MSB 0x61
+#define STMPE_KPC_ROW_LSB 0x62
+#define STMPE_KPC_CTRL_MSB 0x63
+#define STMPE_KPC_CTRL_LSB 0x64
+#define STMPE_KPC_COMBI_KEY_0 0x65
+#define STMPE_KPC_COMBI_KEY_1 0x66
+#define STMPE_KPC_COMBI_KEY_2 0x67
+#define STMPE_KPC_DATA_BYTE0 0x68
+#define STMPE_KPC_DATA_BYTE1 0x69
+#define STMPE_KPC_DATA_BYTE2 0x6a
+#define STMPE_KPC_DATA_BYTE3 0x6b
+#define STMPE_KPC_DATA_BYTE4 0x6c
+
+#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0)
+#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1)
+#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4)
+
+#define STMPE_KPC_ROW_MSB_ROWS 0xff
+
+#define STMPE_KPC_DATA_UP (0x1 << 7)
+#define STMPE_KPC_DATA_ROW (0xf << 3)
+#define STMPE_KPC_DATA_COL (0x7 << 0)
+#define STMPE_KPC_DATA_NOKEY_MASK 0x78
+
+#define STMPE_KEYPAD_MAX_DEBOUNCE 127
+#define STMPE_KEYPAD_MAX_SCAN_COUNT 15
+
+#define STMPE_KEYPAD_MAX_ROWS 8
+#define STMPE_KEYPAD_MAX_COLS 8
+#define STMPE_KEYPAD_ROW_SHIFT 3
+#define STMPE_KEYPAD_KEYMAP_SIZE \
+ (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
+
+/**
+ * struct stmpe_keypad_variant - model-specific attributes
+ * @auto_increment: whether the KPC_DATA_BYTE register address
+ * auto-increments on multiple read
+ * @num_data: number of data bytes
+ * @num_normal_data: number of normal keys' data bytes
+ * @max_cols: maximum number of columns supported
+ * @max_rows: maximum number of rows supported
+ * @col_gpios: bitmask of gpios which can be used for columns
+ * @row_gpios: bitmask of gpios which can be used for rows
+ */
+struct stmpe_keypad_variant {
+ bool auto_increment;
+ int num_data;
+ int num_normal_data;
+ int max_cols;
+ int max_rows;
+ unsigned int col_gpios;
+ unsigned int row_gpios;
+};
+
+static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
+ [STMPE1601] = {
+ .auto_increment = true,
+ .num_data = 5,
+ .num_normal_data = 3,
+ .max_cols = 8,
+ .max_rows = 8,
+ .col_gpios = 0x000ff, /* GPIO 0 - 7 */
+ .row_gpios = 0x0ff00, /* GPIO 8 - 15 */
+ },
+ [STMPE2401] = {
+ .auto_increment = false,
+ .num_data = 3,
+ .num_normal_data = 2,
+ .max_cols = 8,
+ .max_rows = 12,
+ .col_gpios = 0x0000ff, /* GPIO 0 - 7*/
+ .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
+ },
+ [STMPE2403] = {
+ .auto_increment = true,
+ .num_data = 5,
+ .num_normal_data = 3,
+ .max_cols = 8,
+ .max_rows = 12,
+ .col_gpios = 0x0000ff, /* GPIO 0 - 7*/
+ .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
+ },
+};
+
+struct stmpe_keypad {
+ struct stmpe *stmpe;
+ struct input_dev *input;
+ const struct stmpe_keypad_variant *variant;
+ const struct stmpe_keypad_platform_data *plat;
+
+ unsigned int rows;
+ unsigned int cols;
+
+ unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE];
+};
+
+static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
+{
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ struct stmpe *stmpe = keypad->stmpe;
+ int ret;
+ int i;
+
+ if (variant->auto_increment)
+ return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
+ variant->num_data, data);
+
+ for (i = 0; i < variant->num_data; i++) {
+ ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
+ if (ret < 0)
+ return ret;
+
+ data[i] = ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
+{
+ struct stmpe_keypad *keypad = dev;
+ struct input_dev *input = keypad->input;
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ u8 fifo[variant->num_data];
+ int ret;
+ int i;
+
+ ret = stmpe_keypad_read_data(keypad, fifo);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < variant->num_normal_data; i++) {
+ u8 data = fifo[i];
+ int row = (data & STMPE_KPC_DATA_ROW) >> 3;
+ int col = data & STMPE_KPC_DATA_COL;
+ int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
+ bool up = data & STMPE_KPC_DATA_UP;
+
+ if ((data & STMPE_KPC_DATA_NOKEY_MASK)
+ == STMPE_KPC_DATA_NOKEY_MASK)
+ continue;
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], !up);
+ input_sync(input);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
+{
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ unsigned int col_gpios = variant->col_gpios;
+ unsigned int row_gpios = variant->row_gpios;
+ struct stmpe *stmpe = keypad->stmpe;
+ unsigned int pins = 0;
+ int i;
+
+ /*
+ * Figure out which pins need to be set to the keypad alternate
+ * function.
+ *
+ * {cols,rows}_gpios are bitmasks of which pins on the chip can be used
+ * for the keypad.
+ *
+ * keypad->{cols,rows} are a bitmask of which pins (of the ones useable
+ * for the keypad) are used on the board.
+ */
+
+ for (i = 0; i < variant->max_cols; i++) {
+ int num = __ffs(col_gpios);
+
+ if (keypad->cols & (1 << i))
+ pins |= 1 << num;
+
+ col_gpios &= ~(1 << num);
+ }
+
+ for (i = 0; i < variant->max_rows; i++) {
+ int num = __ffs(row_gpios);
+
+ if (keypad->rows & (1 << i))
+ pins |= 1 << num;
+
+ row_gpios &= ~(1 << num);
+ }
+
+ return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
+}
+
+static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
+{
+ const struct stmpe_keypad_platform_data *plat = keypad->plat;
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ struct stmpe *stmpe = keypad->stmpe;
+ int ret;
+
+ if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
+ return -EINVAL;
+
+ if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
+ return -EINVAL;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_keypad_altfunc_init(keypad);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
+ if (ret < 0)
+ return ret;
+
+ if (variant->max_rows > 8) {
+ ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
+ STMPE_KPC_ROW_MSB_ROWS,
+ keypad->rows >> 8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
+ STMPE_KPC_CTRL_MSB_SCAN_COUNT,
+ plat->scan_count << 4);
+ if (ret < 0)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
+ STMPE_KPC_CTRL_LSB_SCAN |
+ STMPE_KPC_CTRL_LSB_DEBOUNCE,
+ STMPE_KPC_CTRL_LSB_SCAN |
+ (plat->debounce_ms << 1));
+}
+
+static int __devinit stmpe_keypad_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_keypad_platform_data *plat;
+ struct stmpe_keypad *keypad;
+ struct input_dev *input;
+ int ret;
+ int irq;
+ int i;
+
+ plat = stmpe->pdata->keypad;
+ if (!plat)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL);
+ if (!keypad)
+ return -ENOMEM;
+
+ input = input_allocate_device();
+ if (!input) {
+ ret = -ENOMEM;
+ goto out_freekeypad;
+ }
+
+ input->name = "STMPE keypad";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ for (i = 0; i < plat->keymap_data->keymap_size; i++) {
+ unsigned int key = plat->keymap_data->keymap[i];
+
+ keypad->cols |= 1 << KEY_COL(key);
+ keypad->rows |= 1 << KEY_ROW(key);
+ }
+
+ keypad->stmpe = stmpe;
+ keypad->plat = plat;
+ keypad->input = input;
+ keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
+
+ ret = stmpe_keypad_chip_init(keypad);
+ if (ret < 0)
+ goto out_freeinput;
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", ret);
+ goto out_freeinput;
+ }
+
+ ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT,
+ "stmpe-keypad", keypad);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_unregisterinput;
+ }
+
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+out_unregisterinput:
+ input_unregister_device(input);
+ input = NULL;
+out_freeinput:
+ input_free_device(input);
+out_freekeypad:
+ kfree(keypad);
+ return ret;
+}
+
+static int __devexit stmpe_keypad_remove(struct platform_device *pdev)
+{
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = keypad->stmpe;
+ int irq = platform_get_irq(pdev, 0);
+
+ stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
+
+ free_irq(irq, keypad);
+ input_unregister_device(keypad->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(keypad);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_keypad_driver = {
+ .driver.name = "stmpe-keypad",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_keypad_probe,
+ .remove = __devexit_p(stmpe_keypad_remove),
+};
+
+static int __init stmpe_keypad_init(void)
+{
+ return platform_driver_register(&stmpe_keypad_driver);
+}
+module_init(stmpe_keypad_init);
+
+static void __exit stmpe_keypad_exit(void)
+{
+ platform_driver_unregister(&stmpe_keypad_driver);
+}
+module_exit(stmpe_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx keypad driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
index e2ca017..de5900d 100644
--- a/drivers/input/misc/adxl34x.c
+++ b/drivers/input/misc/adxl34x.c
@@ -724,7 +724,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
pdata = &ac->pdata;
ac->input = input_dev;
- ac->disabled = true;
ac->dev = dev;
ac->irq = irq;
ac->bops = bops;
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index e148749..2325765 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -38,7 +38,8 @@ enum {
};
static int ati_remote2_set_mask(const char *val,
- struct kernel_param *kp, unsigned int max)
+ const struct kernel_param *kp,
+ unsigned int max)
{
unsigned long mask;
int ret;
@@ -59,28 +60,31 @@ static int ati_remote2_set_mask(const char *val,
}
static int ati_remote2_set_channel_mask(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK);
}
-static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp)
+static int ati_remote2_get_channel_mask(char *buffer,
+ const struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
}
-static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp)
+static int ati_remote2_set_mode_mask(const char *val,
+ const struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK);
}
-static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp)
+static int ati_remote2_get_mode_mask(char *buffer,
+ const struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
@@ -89,15 +93,19 @@ static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp)
static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int)
-#define param_set_channel_mask ati_remote2_set_channel_mask
-#define param_get_channel_mask ati_remote2_get_channel_mask
+static struct kernel_param_ops param_ops_channel_mask = {
+ .set = ati_remote2_set_channel_mask,
+ .get = ati_remote2_get_channel_mask,
+};
module_param(channel_mask, channel_mask, 0644);
MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK;
#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int)
-#define param_set_mode_mask ati_remote2_set_mode_mask
-#define param_get_mode_mask ati_remote2_get_mode_mask
+static struct kernel_param_ops param_ops_mode_mask = {
+ .set = ati_remote2_set_mode_mask,
+ .get = ati_remote2_get_mode_mask,
+};
module_param(mode_mask, mode_mask, 0644);
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index f3bb92e..8e130bf 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -173,7 +173,7 @@ static int __devinit sparcspkr_probe(struct device *dev)
return 0;
}
-static int sparcspkr_shutdown(struct of_device *dev)
+static int sparcspkr_shutdown(struct platform_device *dev)
{
struct sparcspkr_state *state = dev_get_drvdata(&dev->dev);
struct input_dev *input_dev = state->input_dev;
@@ -184,7 +184,7 @@ static int sparcspkr_shutdown(struct of_device *dev)
return 0;
}
-static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit bbc_beep_probe(struct platform_device *op, const struct of_device_id *match)
{
struct sparcspkr_state *state;
struct bbc_beep_info *info;
@@ -231,7 +231,7 @@ out_err:
return err;
}
-static int __devexit bbc_remove(struct of_device *op)
+static int __devexit bbc_remove(struct platform_device *op)
{
struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
struct input_dev *input_dev = state->input_dev;
@@ -269,7 +269,7 @@ static struct of_platform_driver bbc_beep_driver = {
.shutdown = sparcspkr_shutdown,
};
-static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit grover_beep_probe(struct platform_device *op, const struct of_device_id *match)
{
struct sparcspkr_state *state;
struct grover_beep_info *info;
@@ -312,7 +312,7 @@ out_err:
return err;
}
-static int __devexit grover_remove(struct of_device *op)
+static int __devexit grover_remove(struct platform_device *op)
{
struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
struct grover_beep_info *info = &state->u.grover;
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index b71eb55..bb53fd33 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -304,21 +304,25 @@ static int uinput_validate_absbits(struct input_dev *dev)
if (!test_bit(cnt, dev->absbit))
continue;
- if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
+ if (input_abs_get_max(dev, cnt) <= input_abs_get_min(dev, cnt)) {
printk(KERN_DEBUG
"%s: invalid abs[%02x] min:%d max:%d\n",
UINPUT_NAME, cnt,
- dev->absmin[cnt], dev->absmax[cnt]);
+ input_abs_get_min(dev, cnt),
+ input_abs_get_max(dev, cnt));
retval = -EINVAL;
break;
}
- if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
+ if (input_abs_get_flat(dev, cnt) >
+ input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
printk(KERN_DEBUG
- "%s: absflat[%02x] out of range: %d "
+ "%s: abs_flat #%02x out of range: %d "
"(min:%d/max:%d)\n",
- UINPUT_NAME, cnt, dev->absflat[cnt],
- dev->absmin[cnt], dev->absmax[cnt]);
+ UINPUT_NAME, cnt,
+ input_abs_get_flat(dev, cnt),
+ input_abs_get_min(dev, cnt),
+ input_abs_get_max(dev, cnt));
retval = -EINVAL;
break;
}
@@ -343,7 +347,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
struct uinput_user_dev *user_dev;
struct input_dev *dev;
char *name;
- int size;
+ int i, size;
int retval;
if (count != sizeof(struct uinput_user_dev))
@@ -387,11 +391,12 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
dev->id.product = user_dev->id.product;
dev->id.version = user_dev->id.version;
- size = sizeof(int) * ABS_CNT;
- memcpy(dev->absmax, user_dev->absmax, size);
- memcpy(dev->absmin, user_dev->absmin, size);
- memcpy(dev->absfuzz, user_dev->absfuzz, size);
- memcpy(dev->absflat, user_dev->absflat, size);
+ for (i = 0; i < ABS_CNT; i++) {
+ input_abs_set_max(dev, i, user_dev->absmax[i]);
+ input_abs_set_min(dev, i, user_dev->absmin[i]);
+ input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
+ input_abs_set_flat(dev, i, user_dev->absflat[i]);
+ }
/* check if absmin/absmax/absfuzz/absflat are filled as
* told in Documentation/input/input-programming.txt */
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index b18862b..4831120 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -185,7 +185,6 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
struct elantech_data *etd = psmouse->private;
unsigned char *packet = psmouse->packet;
int fingers;
- static int old_fingers;
if (etd->fw_version < 0x020000) {
/*
@@ -203,10 +202,13 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
}
if (etd->jumpy_cursor) {
- /* Discard packets that are likely to have bogus coordinates */
- if (fingers > old_fingers) {
+ if (fingers != 1) {
+ etd->single_finger_reports = 0;
+ } else if (etd->single_finger_reports < 2) {
+ /* Discard first 2 reports of one finger, bogus */
+ etd->single_finger_reports++;
elantech_debug("discarding packet\n");
- goto discard_packet_v1;
+ return;
}
}
@@ -238,9 +240,6 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
}
input_sync(dev);
-
- discard_packet_v1:
- old_fingers = fingers;
}
/*
@@ -258,6 +257,14 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOUCH, fingers != 0);
switch (fingers) {
+ case 3:
+ /*
+ * Same as one finger, except report of more than 3 fingers:
+ * byte 3: n4 . w1 w0 . . . .
+ */
+ if (packet[3] & 0x80)
+ fingers = 4;
+ /* pass through... */
case 1:
/*
* byte 1: . . . . . x10 x9 x8
@@ -310,6 +317,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+ input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
@@ -467,6 +475,7 @@ static void elantech_set_input_params(struct psmouse *psmouse)
break;
case 2:
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
@@ -733,13 +742,13 @@ int elantech_init(struct psmouse *psmouse)
etd->capabilities = param[0];
/*
- * This firmware seems to suffer from misreporting coordinates when
+ * This firmware suffers from misreporting coordinates when
* a touch action starts causing the mouse cursor or scrolled page
* to jump. Enable a workaround.
*/
- if (etd->fw_version == 0x020022) {
- pr_info("firmware version 2.0.34 detected, enabling jumpy cursor workaround\n");
- etd->jumpy_cursor = 1;
+ if (etd->fw_version == 0x020022 || etd->fw_version == 0x020600) {
+ pr_info("firmware version 2.0.34/2.6.0 detected, enabling jumpy cursor workaround\n");
+ etd->jumpy_cursor = true;
}
if (elantech_set_absolute_mode(psmouse)) {
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index ac57bde..aa4aac5 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -100,10 +100,11 @@ struct elantech_data {
unsigned char reg_26;
unsigned char debug;
unsigned char capabilities;
- unsigned char paritycheck;
- unsigned char jumpy_cursor;
+ bool paritycheck;
+ bool jumpy_cursor;
unsigned char hw_version;
- unsigned int fw_version;
+ unsigned int fw_version;
+ unsigned int single_finger_reports;
unsigned char parity[256];
};
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
index 3941f97..7b02b65 100644
--- a/drivers/input/mouse/pc110pad.c
+++ b/drivers/input/mouse/pc110pad.c
@@ -145,8 +145,8 @@ static int __init pc110pad_init(void)
pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- pc110pad_dev->absmax[ABS_X] = 0x1ff;
- pc110pad_dev->absmax[ABS_Y] = 0x0ff;
+ input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff);
+ input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff);
pc110pad_dev->open = pc110pad_open;
pc110pad_dev->close = pc110pad_close;
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 979c502..73a7af2 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -39,11 +39,13 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static unsigned int psmouse_max_proto = PSMOUSE_AUTO;
-static int psmouse_set_maxproto(const char *val, struct kernel_param *kp);
-static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp);
+static int psmouse_set_maxproto(const char *val, const struct kernel_param *);
+static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp);
+static struct kernel_param_ops param_ops_proto_abbrev = {
+ .set = psmouse_set_maxproto,
+ .get = psmouse_get_maxproto,
+};
#define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int)
-#define param_set_proto_abbrev psmouse_set_maxproto
-#define param_get_proto_abbrev psmouse_get_maxproto
module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644);
MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches.");
@@ -1679,7 +1681,7 @@ static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data,
}
-static int psmouse_set_maxproto(const char *val, struct kernel_param *kp)
+static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp)
{
const struct psmouse_protocol *proto;
@@ -1696,7 +1698,7 @@ static int psmouse_set_maxproto(const char *val, struct kernel_param *kp)
return 0;
}
-static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp)
+static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp)
{
int type = *((unsigned int *)kp->arg);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 8c32440..96b70a4 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -635,8 +635,8 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
- dev->absres[ABS_X] = priv->x_res;
- dev->absres[ABS_Y] = priv->y_res;
+ input_abs_set_res(dev, ABS_X, priv->x_res);
+ input_abs_set_res(dev, ABS_Y, priv->y_res);
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
/* Clickpads report only left button */
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index d8f68f7..83c24cc 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -22,6 +22,7 @@
#include <linux/random.h>
#include <linux/major.h>
#include <linux/device.h>
+#include <linux/kernel.h>
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
#include <linux/miscdevice.h>
#endif
@@ -134,11 +135,14 @@ static void mousedev_touchpad_event(struct input_dev *dev,
switch (code) {
case ABS_X:
+
fx(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) {
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ size = input_abs_get_min(dev, ABS_X) -
+ input_abs_get_max(dev, ABS_X);
if (size == 0)
size = 256 * 2;
+
tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
tmp += mousedev->frac_dx;
mousedev->packet.dx = tmp / FRACTION_DENOM;
@@ -150,10 +154,12 @@ static void mousedev_touchpad_event(struct input_dev *dev,
case ABS_Y:
fy(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) {
- /* use X size to keep the same scale */
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ /* use X size for ABS_Y to keep the same scale */
+ size = input_abs_get_min(dev, ABS_X) -
+ input_abs_get_max(dev, ABS_X);
if (size == 0)
size = 256 * 2;
+
tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
tmp += mousedev->frac_dy;
mousedev->packet.dy = tmp / FRACTION_DENOM;
@@ -167,33 +173,35 @@ static void mousedev_touchpad_event(struct input_dev *dev,
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
unsigned int code, int value)
{
- int size;
+ int min, max, size;
switch (code) {
case ABS_X:
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ min = input_abs_get_min(dev, ABS_X);
+ max = input_abs_get_max(dev, ABS_X);
+
+ size = max - min;
if (size == 0)
size = xres ? : 1;
- if (value > dev->absmax[ABS_X])
- value = dev->absmax[ABS_X];
- if (value < dev->absmin[ABS_X])
- value = dev->absmin[ABS_X];
- mousedev->packet.x =
- ((value - dev->absmin[ABS_X]) * xres) / size;
+
+ clamp(value, min, max);
+
+ mousedev->packet.x = ((value - min) * xres) / size;
mousedev->packet.abs_event = 1;
break;
case ABS_Y:
- size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
+ min = input_abs_get_min(dev, ABS_Y);
+ max = input_abs_get_max(dev, ABS_Y);
+
+ size = max - min;
if (size == 0)
size = yres ? : 1;
- if (value > dev->absmax[ABS_Y])
- value = dev->absmax[ABS_Y];
- if (value < dev->absmin[ABS_Y])
- value = dev->absmin[ABS_Y];
- mousedev->packet.y = yres -
- ((value - dev->absmin[ABS_Y]) * yres) / size;
+
+ clamp(value, min, max);
+
+ mousedev->packet.y = yres - ((value - min) * yres) / size;
mousedev->packet.abs_event = 1;
break;
}
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index cb2a24b..c5cc450 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -49,7 +49,7 @@ static inline void i8042_write_command(int val)
#define OBP_PS2MS_NAME1 "kdmouse"
#define OBP_PS2MS_NAME2 "mouse"
-static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit sparc_i8042_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -57,7 +57,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev
while (dp) {
if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
!strcmp(dp->name, OBP_PS2KBD_NAME2)) {
- struct of_device *kbd = of_find_device_by_node(dp);
+ struct platform_device *kbd = of_find_device_by_node(dp);
unsigned int irq = kbd->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
@@ -67,7 +67,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev
kbd_res = &kbd->resource[0];
} else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
!strcmp(dp->name, OBP_PS2MS_NAME2)) {
- struct of_device *ms = of_find_device_by_node(dp);
+ struct platform_device *ms = of_find_device_by_node(dp);
unsigned int irq = ms->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
@@ -80,7 +80,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev
return 0;
}
-static int __devexit sparc_i8042_remove(struct of_device *op)
+static int __devexit sparc_i8042_remove(struct platform_device *op)
{
of_iounmap(kbd_res, kbd_iobase, 8);
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 258b98b..46e4ba0 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -61,10 +61,6 @@ static bool i8042_noloop;
module_param_named(noloop, i8042_noloop, bool, 0);
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
-static unsigned int i8042_blink_frequency = 500;
-module_param_named(panicblink, i8042_blink_frequency, uint, 0600);
-MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics");
-
#ifdef CONFIG_X86
static bool i8042_dritek;
module_param_named(dritek, i8042_dritek, bool, 0);
@@ -1030,8 +1026,8 @@ static void i8042_controller_reset(void)
/*
- * i8042_panic_blink() will flash the keyboard LEDs and is called when
- * kernel panics. Flashing LEDs is useful for users running X who may
+ * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
+ * when kernel panics. Flashing LEDs is useful for users running X who may
* not see the console and will help distingushing panics from "real"
* lockups.
*
@@ -1041,22 +1037,12 @@ static void i8042_controller_reset(void)
#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
-static long i8042_panic_blink(long count)
+static long i8042_panic_blink(int state)
{
long delay = 0;
- static long last_blink;
- static char led;
-
- /*
- * We expect frequency to be about 1/2s. KDB uses about 1s.
- * Make sure they are different.
- */
- if (!i8042_blink_frequency)
- return 0;
- if (count - last_blink < i8042_blink_frequency)
- return 0;
+ char led;
- led ^= 0x01 | 0x04;
+ led = (state) ? 0x01 | 0x04 : 0;
while (i8042_read_status() & I8042_STR_IBF)
DELAY;
dbg("%02x -> i8042 (panic blink)", 0xed);
@@ -1069,7 +1055,6 @@ static long i8042_panic_blink(long count)
dbg("%02x -> i8042 (panic blink)", led);
i8042_write_data(led);
DELAY;
- last_blink = count;
return delay;
}
diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c
index e2c028d..bb14449 100644
--- a/drivers/input/serio/xilinx_ps2.c
+++ b/drivers/input/serio/xilinx_ps2.c
@@ -232,7 +232,7 @@ static void sxps2_close(struct serio *pserio)
* It returns 0, if the driver is bound to the PS/2 device, or a negative
* value if there is an error.
*/
-static int __devinit xps2_of_probe(struct of_device *ofdev,
+static int __devinit xps2_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct resource r_irq; /* Interrupt resources */
@@ -332,7 +332,7 @@ failed1:
* if the driver module is being unloaded. It frees any resources allocated to
* the device.
*/
-static int __devexit xps2_of_remove(struct of_device *of_dev)
+static int __devexit xps2_of_remove(struct platform_device *of_dev)
{
struct device *dev = &of_dev->dev;
struct xps2data *drvdata = dev_get_drvdata(dev);
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
index 51b80b0..57b25b8 100644
--- a/drivers/input/tablet/aiptek.c
+++ b/drivers/input/tablet/aiptek.c
@@ -987,20 +987,17 @@ static int aiptek_program_tablet(struct aiptek *aiptek)
/* Query getXextension */
if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
return ret;
- aiptek->inputdev->absmin[ABS_X] = 0;
- aiptek->inputdev->absmax[ABS_X] = ret - 1;
+ input_set_abs_params(aiptek->inputdev, ABS_X, 0, ret - 1, 0, 0);
/* Query getYextension */
if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
return ret;
- aiptek->inputdev->absmin[ABS_Y] = 0;
- aiptek->inputdev->absmax[ABS_Y] = ret - 1;
+ input_set_abs_params(aiptek->inputdev, ABS_Y, 0, ret - 1, 0, 0);
/* Query getPressureLevels */
if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
return ret;
- aiptek->inputdev->absmin[ABS_PRESSURE] = 0;
- aiptek->inputdev->absmax[ABS_PRESSURE] = ret - 1;
+ input_set_abs_params(aiptek->inputdev, ABS_PRESSURE, 0, ret - 1, 0, 0);
/* Depending on whether we are in absolute or relative mode, we will
* do a switchToTablet(absolute) or switchToMouse(relative) command.
@@ -1054,8 +1051,8 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr
struct aiptek *aiptek = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%dx%d\n",
- aiptek->inputdev->absmax[ABS_X] + 1,
- aiptek->inputdev->absmax[ABS_Y] + 1);
+ input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
+ input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
}
/* These structs define the sysfs files, param #1 is the name of the
@@ -1843,7 +1840,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
aiptek->curSetting.programmableDelay = speeds[i];
(void)aiptek_program_tablet(aiptek);
- if (aiptek->inputdev->absmax[ABS_X] > 0) {
+ if (input_abs_get_max(aiptek->inputdev, ABS_X) > 0) {
dev_info(&intf->dev,
"Aiptek using %d ms programming speed\n",
aiptek->curSetting.programmableDelay);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index ce0b460..40d77ba 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -687,10 +687,10 @@ static void wacom_tpc_finger_in(struct wacom_wac *wacom, char *data, int idx)
* protocol.
*/
if (wacom->last_finger != finger) {
- if (x == input->abs[ABS_X])
+ if (x == input_abs_get_val(input, ABS_X))
x++;
- if (y == input->abs[ABS_Y])
+ if (y == input_abs_get_val(input, ABS_Y))
y++;
}
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 61f3518..0069d97 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -628,4 +628,14 @@ config TOUCHSCREEN_TPS6507X
To compile this driver as a module, choose M here: the
module will be called tps6507x_ts.
+config TOUCHSCREEN_STMPE
+ tristate "STMicroelectronics STMPE touchscreens"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want support for STMicroelectronics
+ STMPE touchscreen controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmpe-ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index bd6f30b4..28217e1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index 4eb7df0..5ec0946 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -75,7 +75,7 @@ static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
unsigned char len, unsigned char *value)
{
struct i2c_client *client = tsc->client;
- unsigned int ret;
+ int ret;
unsigned char i2c_data[6];
BUG_ON(len > 5);
@@ -86,7 +86,7 @@ static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
ret = i2c_master_send(client, i2c_data, len + 1);
if (ret != 1) {
dev_err(&client->dev, "i2c write data cmd failed\n");
- return ret;
+ return ret ? ret : -EIO;
}
return 0;
@@ -96,7 +96,7 @@ static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,
unsigned char *data, unsigned char len, unsigned char cmd)
{
struct i2c_client *client = tsc->client;
- unsigned int ret;
+ int ret;
struct i2c_msg msg[2] = {
/* first write slave position to i2c devices */
{ client->addr, 0, 1, &cmd },
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
new file mode 100644
index 0000000..656148e
--- /dev/null
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -0,0 +1,397 @@
+/* STMicroelectronics STMPE811 Touchscreen Driver
+ *
+ * (C) 2010 Luotao Fu <l.fu@pengutronix.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/stmpe.h>
+
+/* Register layouts and functionalities are identical on all stmpexxx variants
+ * with touchscreen controller
+ */
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_TSC_CTRL 0x40
+#define STMPE_REG_TSC_CFG 0x41
+#define STMPE_REG_FIFO_TH 0x4A
+#define STMPE_REG_FIFO_STA 0x4B
+#define STMPE_REG_FIFO_SIZE 0x4C
+#define STMPE_REG_TSC_DATA_XYZ 0x52
+#define STMPE_REG_TSC_FRACTION_Z 0x56
+#define STMPE_REG_TSC_I_DRIVE 0x58
+
+#define OP_MOD_XYZ 0
+
+#define STMPE_TSC_CTRL_TSC_EN (1<<0)
+
+#define STMPE_FIFO_STA_RESET (1<<0)
+
+#define STMPE_IRQ_TOUCH_DET 0
+
+#define SAMPLE_TIME(x) ((x & 0xf) << 4)
+#define MOD_12B(x) ((x & 0x1) << 3)
+#define REF_SEL(x) ((x & 0x1) << 1)
+#define ADC_FREQ(x) (x & 0x3)
+#define AVE_CTRL(x) ((x & 0x3) << 6)
+#define DET_DELAY(x) ((x & 0x7) << 3)
+#define SETTLING(x) (x & 0x7)
+#define FRACTION_Z(x) (x & 0x7)
+#define I_DRIVE(x) (x & 0x1)
+#define OP_MODE(x) ((x & 0x7) << 1)
+
+#define STMPE_TS_NAME "stmpe-ts"
+#define XY_MASK 0xfff
+
+struct stmpe_touch {
+ struct stmpe *stmpe;
+ struct input_dev *idev;
+ struct delayed_work work;
+ struct device *dev;
+ u8 sample_time;
+ u8 mod_12b;
+ u8 ref_sel;
+ u8 adc_freq;
+ u8 ave_ctrl;
+ u8 touch_det_delay;
+ u8 settling;
+ u8 fraction_z;
+ u8 i_drive;
+};
+
+static int __stmpe_reset_fifo(struct stmpe *stmpe)
+{
+ int ret;
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, 0);
+}
+
+static void stmpe_work(struct work_struct *work)
+{
+ int int_sta;
+ u32 timeout = 40;
+
+ struct stmpe_touch *ts =
+ container_of(work, struct stmpe_touch, work.work);
+
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+
+ /*
+ * touch_det sometimes get desasserted or just get stuck. This appears
+ * to be a silicon bug, We still have to clearify this with the
+ * manufacture. As a workaround We release the key anyway if the
+ * touch_det keeps coming in after 4ms, while the FIFO contains no value
+ * during the whole time.
+ */
+ while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) {
+ timeout--;
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+ udelay(100);
+ }
+
+ /* reset the FIFO before we report release event */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ input_sync(ts->idev);
+}
+
+static irqreturn_t stmpe_ts_handler(int irq, void *data)
+{
+ u8 data_set[4];
+ int x, y, z;
+ struct stmpe_touch *ts = data;
+
+ /*
+ * Cancel scheduled polling for release if we have new value
+ * available. Wait if the polling is already running.
+ */
+ cancel_delayed_work_sync(&ts->work);
+
+ /*
+ * The FIFO sometimes just crashes and stops generating interrupts. This
+ * appears to be a silicon bug. We still have to clearify this with
+ * the manufacture. As a workaround we disable the TSC while we are
+ * collecting data and flush the FIFO after reading
+ */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+
+ stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set);
+
+ x = (data_set[0] << 4) | (data_set[1] >> 4);
+ y = ((data_set[1] & 0xf) << 8) | data_set[2];
+ z = data_set[3];
+
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, z);
+ input_sync(ts->idev);
+
+ /* flush the FIFO after we have read out our values. */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ /* reenable the tsc */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+
+ /* start polling for touch_det to detect release */
+ schedule_delayed_work(&ts->work, HZ / 50);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_init_hw(struct stmpe_touch *ts)
+{
+ int ret;
+ u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
+ struct stmpe *stmpe = ts->stmpe;
+ struct device *dev = ts->dev;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(dev, "Could not enable clock for ADC and TS\n");
+ return ret;
+ }
+
+ adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
+ REF_SEL(ts->ref_sel);
+ adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
+ adc_ctrl1_mask, adc_ctrl1);
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
+ ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
+ SETTLING(ts->settling);
+ tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
+ FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
+ I_DRIVE(0xff), I_DRIVE(ts->i_drive));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ /* set FIFO to 1 for single point reading */
+ ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1);
+ if (ret) {
+ dev_err(dev, "Could not set FIFO\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
+ OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
+ if (ret) {
+ dev_err(dev, "Could not set mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stmpe_ts_open(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+ int ret = 0;
+
+ ret = __stmpe_reset_fifo(ts->stmpe);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+}
+
+static void stmpe_ts_close(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&ts->work);
+
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+}
+
+static int __devinit stmpe_input_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_platform_data *pdata = stmpe->pdata;
+ struct stmpe_touch *ts;
+ struct input_dev *idev;
+ struct stmpe_ts_platform_data *ts_pdata = NULL;
+ int ret = 0;
+ int ts_irq;
+
+ ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+ if (ts_irq < 0)
+ return ts_irq;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ goto err_out;
+
+ idev = input_allocate_device();
+ if (!idev)
+ goto err_free_ts;
+
+ platform_set_drvdata(pdev, ts);
+ ts->stmpe = stmpe;
+ ts->idev = idev;
+ ts->dev = &pdev->dev;
+
+ if (pdata)
+ ts_pdata = pdata->ts;
+
+ if (ts_pdata) {
+ ts->sample_time = ts_pdata->sample_time;
+ ts->mod_12b = ts_pdata->mod_12b;
+ ts->ref_sel = ts_pdata->ref_sel;
+ ts->adc_freq = ts_pdata->adc_freq;
+ ts->ave_ctrl = ts_pdata->ave_ctrl;
+ ts->touch_det_delay = ts_pdata->touch_det_delay;
+ ts->settling = ts_pdata->settling;
+ ts->fraction_z = ts_pdata->fraction_z;
+ ts->i_drive = ts_pdata->i_drive;
+ }
+
+ INIT_DELAYED_WORK(&ts->work, stmpe_work);
+
+ ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler,
+ IRQF_ONESHOT, STMPE_TS_NAME, ts);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq);
+ goto err_free_input;
+ }
+
+ ret = stmpe_init_hw(ts);
+ if (ret)
+ goto err_free_irq;
+
+ idev->name = STMPE_TS_NAME;
+ idev->id.bustype = BUS_I2C;
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ idev->open = stmpe_ts_open;
+ idev->close = stmpe_ts_close;
+
+ input_set_drvdata(idev, ts);
+
+ input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register input device\n");
+ goto err_free_irq;
+ }
+
+ return ret;
+
+err_free_irq:
+ free_irq(ts_irq, ts);
+err_free_input:
+ input_free_device(idev);
+ platform_set_drvdata(pdev, NULL);
+err_free_ts:
+ kfree(ts);
+err_out:
+ return ret;
+}
+
+static int __devexit stmpe_ts_remove(struct platform_device *pdev)
+{
+ struct stmpe_touch *ts = platform_get_drvdata(pdev);
+ unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+
+ stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN);
+
+ free_irq(ts_irq, ts);
+
+ platform_set_drvdata(pdev, NULL);
+
+ input_unregister_device(ts->idev);
+ input_free_device(ts->idev);
+
+ kfree(ts);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_ts_driver = {
+ .driver = {
+ .name = STMPE_TS_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = stmpe_input_probe,
+ .remove = __devexit_p(stmpe_ts_remove),
+};
+
+static int __init stmpe_ts_init(void)
+{
+ return platform_driver_register(&stmpe_ts_driver);
+}
+
+module_init(stmpe_ts_init);
+
+static void __exit stmpe_ts_exit(void)
+{
+ platform_driver_unregister(&stmpe_ts_driver);
+}
+
+module_exit(stmpe_ts_exit);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" STMPE_TS_NAME);
diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
index 7715d32..d3530f6 100644
--- a/drivers/isdn/hardware/avm/c4.c
+++ b/drivers/isdn/hardware/avm/c4.c
@@ -1273,6 +1273,7 @@ static int __devinit c4_probe(struct pci_dev *dev,
if (retval != 0) {
printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
nr, param.port, param.irq, param.membase);
+ pci_disable_device(dev);
return -ENODEV;
}
return 0;
diff --git a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c
index 5a3f830..a79eb5a 100644
--- a/drivers/isdn/hardware/avm/t1pci.c
+++ b/drivers/isdn/hardware/avm/t1pci.c
@@ -210,6 +210,7 @@ static int __devinit t1pci_probe(struct pci_dev *dev,
if (retval != 0) {
printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
param.port, param.irq, param.membase);
+ pci_disable_device(dev);
return -ENODEV;
}
return 0;
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
index d2dd61d..af25e1f 100644
--- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c
+++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
@@ -1094,6 +1094,7 @@ inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pr_info("mISDN: do not have informations about adapter at %s\n",
pci_name(pdev));
kfree(card);
+ pci_disable_device(pdev);
return -EINVAL;
} else
pr_notice("mISDN: found adapter %s at %s\n",
@@ -1103,7 +1104,7 @@ inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, card);
err = setup_instance(card);
if (err) {
- pci_disable_device(card->pdev);
+ pci_disable_device(pdev);
kfree(card);
pci_set_drvdata(pdev, NULL);
} else if (ent->driver_data == INF_SCT_1) {
@@ -1114,6 +1115,7 @@ inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
if (!sc) {
release_card(card);
+ pci_disable_device(pdev);
return -ENOMEM;
}
sc->irq = card->irq;
@@ -1121,6 +1123,7 @@ inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
sc->ci = card->ci + i;
err = setup_instance(sc);
if (err) {
+ pci_disable_device(pdev);
kfree(sc);
release_card(card);
break;
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index cc22eee..ea57e05 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -224,7 +224,7 @@ struct gpio_led_of_platform_data {
struct gpio_led_data led_data[];
};
-static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
+static int __devinit of_gpio_leds_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node, *child;
@@ -283,7 +283,7 @@ err:
return ret;
}
-static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
+static int __devexit of_gpio_leds_remove(struct platform_device *ofdev)
{
struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
int i;
diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c
index 6024038..8eb40af 100644
--- a/drivers/macintosh/macio_sysfs.c
+++ b/drivers/macintosh/macio_sysfs.c
@@ -15,7 +15,7 @@ field##_show (struct device *dev, struct device_attribute *attr, \
static ssize_t
compatible_show (struct device *dev, struct device_attribute *attr, char *buf)
{
- struct of_device *of;
+ struct platform_device *of;
const char *compat;
int cplen;
int length = 0;
@@ -52,9 +52,9 @@ static ssize_t modalias_show (struct device *dev, struct device_attribute *attr,
static ssize_t devspec_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
- ofdev = to_of_device(dev);
+ ofdev = to_platform_device(dev);
return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name);
}
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 2506c95..e58c3d3 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -75,7 +75,7 @@ struct smu_cmd_buf {
struct smu_device {
spinlock_t lock;
struct device_node *of_node;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
int doorbell; /* doorbell gpio */
u32 __iomem *db_buf; /* doorbell buffer */
struct device_node *db_node;
@@ -645,7 +645,7 @@ static void smu_expose_childs(struct work_struct *unused)
static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs);
-static int smu_platform_probe(struct of_device* dev,
+static int smu_platform_probe(struct platform_device* dev,
const struct of_device_id *match)
{
if (!smu)
@@ -695,7 +695,7 @@ static int __init smu_init_sysfs(void)
device_initcall(smu_init_sysfs);
-struct of_device *smu_get_ofdev(void)
+struct platform_device *smu_get_ofdev(void)
{
if (!smu)
return NULL;
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index c42eeb4..d0d2213 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -84,7 +84,7 @@ struct thermostat {
static enum {ADT7460, ADT7467} therm_type;
static int therm_bus, therm_address;
-static struct of_device * of_dev;
+static struct platform_device * of_dev;
static struct thermostat* thermostat;
static struct task_struct *thread_therm = NULL;
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index e60605b..4454927 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -148,7 +148,7 @@
* Driver statics
*/
-static struct of_device * of_dev;
+static struct platform_device * of_dev;
static struct i2c_adapter * u3_0;
static struct i2c_adapter * u3_1;
static struct i2c_adapter * k2;
@@ -2210,7 +2210,7 @@ static void fcu_lookup_fans(struct device_node *fcu_node)
}
}
-static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match)
+static int fcu_of_probe(struct platform_device* dev, const struct of_device_id *match)
{
state = state_detached;
@@ -2221,7 +2221,7 @@ static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match)
return i2c_add_driver(&therm_pm72_driver);
}
-static int fcu_of_remove(struct of_device* dev)
+static int fcu_of_remove(struct platform_device* dev)
{
i2c_del_driver(&therm_pm72_driver);
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 5c9367a..c89f396 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -52,7 +52,7 @@ static struct {
struct task_struct *poll_task;
struct mutex lock;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
struct i2c_client *thermostat;
struct i2c_client *fan;
@@ -322,10 +322,10 @@ do_attach( struct i2c_adapter *adapter )
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE);
- i2c_new_probed_device(adapter, &info, scan_ds1775);
+ i2c_new_probed_device(adapter, &info, scan_ds1775, NULL);
strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE);
- i2c_new_probed_device(adapter, &info, scan_adm1030);
+ i2c_new_probed_device(adapter, &info, scan_adm1030, NULL);
if( x.thermostat && x.fan ) {
x.running = 1;
@@ -444,13 +444,13 @@ static struct i2c_driver g4fan_driver = {
/************************************************************************/
static int
-therm_of_probe( struct of_device *dev, const struct of_device_id *match )
+therm_of_probe( struct platform_device *dev, const struct of_device_id *match )
{
return i2c_add_driver( &g4fan_driver );
}
static int
-therm_of_remove( struct of_device *dev )
+therm_of_remove( struct platform_device *dev )
{
i2c_del_driver( &g4fan_driver );
return 0;
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 4a6feac..bf1a95e3 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -121,7 +121,7 @@ config MD_RAID10
config MD_RAID456
tristate "RAID-4/RAID-5/RAID-6 mode"
depends on BLK_DEV_MD
- select MD_RAID6_PQ
+ select RAID6_PQ
select ASYNC_MEMCPY
select ASYNC_XOR
select ASYNC_PQ
@@ -165,22 +165,6 @@ config MULTICORE_RAID456
If unsure, say N.
-config MD_RAID6_PQ
- tristate
-
-config ASYNC_RAID6_TEST
- tristate "Self test for hardware accelerated raid6 recovery"
- depends on MD_RAID6_PQ
- select ASYNC_RAID6_RECOV
- ---help---
- This is a one-shot self test that permutes through the
- recovery of all the possible two disk failure scenarios for a
- N-disk array. Recovery is performed with the asynchronous
- raid6 recovery routines, and will optionally use an offload
- engine if one is available.
-
- If unsure, say N.
-
config MD_MULTIPATH
tristate "Multipath I/O support"
depends on BLK_DEV_MD
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index e355e7f..5e3aac4 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -12,13 +12,6 @@ dm-log-userspace-y \
+= dm-log-userspace-base.o dm-log-userspace-transfer.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o
-raid6_pq-y += raid6algos.o raid6recov.o raid6tables.o \
- raid6int1.o raid6int2.o raid6int4.o \
- raid6int8.o raid6int16.o raid6int32.o \
- raid6altivec1.o raid6altivec2.o raid6altivec4.o \
- raid6altivec8.o \
- raid6mmx.o raid6sse1.o raid6sse2.o
-hostprogs-y += mktables
# Note: link order is important. All raid personalities
# and must come before md.o, as they each initialise
@@ -29,7 +22,6 @@ obj-$(CONFIG_MD_LINEAR) += linear.o
obj-$(CONFIG_MD_RAID0) += raid0.o
obj-$(CONFIG_MD_RAID1) += raid1.o
obj-$(CONFIG_MD_RAID10) += raid10.o
-obj-$(CONFIG_MD_RAID6_PQ) += raid6_pq.o
obj-$(CONFIG_MD_RAID456) += raid456.o
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_MD_FAULTY) += faulty.o
@@ -45,75 +37,6 @@ obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o
obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o
-quiet_cmd_unroll = UNROLL $@
- cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$(UNROLL) \
- < $< > $@ || ( rm -f $@ && exit 1 )
-
-ifeq ($(CONFIG_ALTIVEC),y)
-altivec_flags := -maltivec -mabi=altivec
-endif
-
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
endif
-
-targets += raid6int1.c
-$(obj)/raid6int1.c: UNROLL := 1
-$(obj)/raid6int1.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int2.c
-$(obj)/raid6int2.c: UNROLL := 2
-$(obj)/raid6int2.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int4.c
-$(obj)/raid6int4.c: UNROLL := 4
-$(obj)/raid6int4.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int8.c
-$(obj)/raid6int8.c: UNROLL := 8
-$(obj)/raid6int8.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int16.c
-$(obj)/raid6int16.c: UNROLL := 16
-$(obj)/raid6int16.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int32.c
-$(obj)/raid6int32.c: UNROLL := 32
-$(obj)/raid6int32.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec1.o += $(altivec_flags)
-targets += raid6altivec1.c
-$(obj)/raid6altivec1.c: UNROLL := 1
-$(obj)/raid6altivec1.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec2.o += $(altivec_flags)
-targets += raid6altivec2.c
-$(obj)/raid6altivec2.c: UNROLL := 2
-$(obj)/raid6altivec2.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec4.o += $(altivec_flags)
-targets += raid6altivec4.c
-$(obj)/raid6altivec4.c: UNROLL := 4
-$(obj)/raid6altivec4.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec8.o += $(altivec_flags)
-targets += raid6altivec8.c
-$(obj)/raid6altivec8.c: UNROLL := 8
-$(obj)/raid6altivec8.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-quiet_cmd_mktable = TABLE $@
- cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 )
-
-targets += raid6tables.c
-$(obj)/raid6tables.c: $(obj)/mktables FORCE
- $(call if_changed,mktable)
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 1742435..1ba1e12 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -13,7 +13,6 @@
* Still to do:
*
* flush after percent set rather than just time based. (maybe both).
- * wait if count gets too high, wake when it drops to half.
*/
#include <linux/blkdev.h>
@@ -30,6 +29,7 @@
#include "md.h"
#include "bitmap.h"
+#include <linux/dm-dirty-log.h>
/* debug macros */
#define DEBUG 0
@@ -51,9 +51,6 @@
#define INJECT_FATAL_FAULT_3 0 /* undef */
#endif
-//#define DPRINTK PRINTK /* set this NULL to avoid verbose debug output */
-#define DPRINTK(x...) do { } while(0)
-
#ifndef PRINTK
# if DEBUG > 0
# define PRINTK(x...) printk(KERN_DEBUG x)
@@ -62,12 +59,11 @@
# endif
#endif
-static inline char * bmname(struct bitmap *bitmap)
+static inline char *bmname(struct bitmap *bitmap)
{
return bitmap->mddev ? mdname(bitmap->mddev) : "mdX";
}
-
/*
* just a placeholder - calls kmalloc for bitmap pages
*/
@@ -78,7 +74,7 @@ static unsigned char *bitmap_alloc_page(struct bitmap *bitmap)
#ifdef INJECT_FAULTS_1
page = NULL;
#else
- page = kmalloc(PAGE_SIZE, GFP_NOIO);
+ page = kzalloc(PAGE_SIZE, GFP_NOIO);
#endif
if (!page)
printk("%s: bitmap_alloc_page FAILED\n", bmname(bitmap));
@@ -107,7 +103,8 @@ static void bitmap_free_page(struct bitmap *bitmap, unsigned char *page)
* if we find our page, we increment the page's refcount so that it stays
* allocated while we're using it
*/
-static int bitmap_checkpage(struct bitmap *bitmap, unsigned long page, int create)
+static int bitmap_checkpage(struct bitmap *bitmap,
+ unsigned long page, int create)
__releases(bitmap->lock)
__acquires(bitmap->lock)
{
@@ -121,7 +118,6 @@ __acquires(bitmap->lock)
return -EINVAL;
}
-
if (bitmap->bp[page].hijacked) /* it's hijacked, don't try to alloc */
return 0;
@@ -131,43 +127,34 @@ __acquires(bitmap->lock)
if (!create)
return -ENOENT;
- spin_unlock_irq(&bitmap->lock);
-
/* this page has not been allocated yet */
- if ((mappage = bitmap_alloc_page(bitmap)) == NULL) {
+ spin_unlock_irq(&bitmap->lock);
+ mappage = bitmap_alloc_page(bitmap);
+ spin_lock_irq(&bitmap->lock);
+
+ if (mappage == NULL) {
PRINTK("%s: bitmap map page allocation failed, hijacking\n",
bmname(bitmap));
/* failed - set the hijacked flag so that we can use the
* pointer as a counter */
- spin_lock_irq(&bitmap->lock);
if (!bitmap->bp[page].map)
bitmap->bp[page].hijacked = 1;
- goto out;
- }
-
- /* got a page */
-
- spin_lock_irq(&bitmap->lock);
-
- /* recheck the page */
-
- if (bitmap->bp[page].map || bitmap->bp[page].hijacked) {
+ } else if (bitmap->bp[page].map ||
+ bitmap->bp[page].hijacked) {
/* somebody beat us to getting the page */
bitmap_free_page(bitmap, mappage);
return 0;
- }
+ } else {
- /* no page was in place and we have one, so install it */
+ /* no page was in place and we have one, so install it */
- memset(mappage, 0, PAGE_SIZE);
- bitmap->bp[page].map = mappage;
- bitmap->missing_pages--;
-out:
+ bitmap->bp[page].map = mappage;
+ bitmap->missing_pages--;
+ }
return 0;
}
-
/* if page is completely empty, put it back on the free list, or dealloc it */
/* if page was hijacked, unmark the flag so it might get alloced next time */
/* Note: lock should be held when calling this */
@@ -183,26 +170,15 @@ static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
if (bitmap->bp[page].hijacked) { /* page was hijacked, undo this now */
bitmap->bp[page].hijacked = 0;
bitmap->bp[page].map = NULL;
- return;
+ } else {
+ /* normal case, free the page */
+ ptr = bitmap->bp[page].map;
+ bitmap->bp[page].map = NULL;
+ bitmap->missing_pages++;
+ bitmap_free_page(bitmap, ptr);
}
-
- /* normal case, free the page */
-
-#if 0
-/* actually ... let's not. We will probably need the page again exactly when
- * memory is tight and we are flusing to disk
- */
- return;
-#else
- ptr = bitmap->bp[page].map;
- bitmap->bp[page].map = NULL;
- bitmap->missing_pages++;
- bitmap_free_page(bitmap, ptr);
- return;
-#endif
}
-
/*
* bitmap file handling - read and write the bitmap file and its superblock
*/
@@ -220,11 +196,14 @@ static struct page *read_sb_page(mddev_t *mddev, loff_t offset,
mdk_rdev_t *rdev;
sector_t target;
+ int did_alloc = 0;
- if (!page)
+ if (!page) {
page = alloc_page(GFP_KERNEL);
- if (!page)
- return ERR_PTR(-ENOMEM);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+ did_alloc = 1;
+ }
list_for_each_entry(rdev, &mddev->disks, same_set) {
if (! test_bit(In_sync, &rdev->flags)
@@ -242,6 +221,8 @@ static struct page *read_sb_page(mddev_t *mddev, loff_t offset,
return page;
}
}
+ if (did_alloc)
+ put_page(page);
return ERR_PTR(-EIO);
}
@@ -286,49 +267,51 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
mddev_t *mddev = bitmap->mddev;
while ((rdev = next_active_rdev(rdev, mddev)) != NULL) {
- int size = PAGE_SIZE;
- loff_t offset = mddev->bitmap_info.offset;
- if (page->index == bitmap->file_pages-1)
- size = roundup(bitmap->last_page_size,
- bdev_logical_block_size(rdev->bdev));
- /* Just make sure we aren't corrupting data or
- * metadata
- */
- if (mddev->external) {
- /* Bitmap could be anywhere. */
- if (rdev->sb_start + offset + (page->index *(PAGE_SIZE/512)) >
- rdev->data_offset &&
- rdev->sb_start + offset <
- rdev->data_offset + mddev->dev_sectors +
- (PAGE_SIZE/512))
- goto bad_alignment;
- } else if (offset < 0) {
- /* DATA BITMAP METADATA */
- if (offset
- + (long)(page->index * (PAGE_SIZE/512))
- + size/512 > 0)
- /* bitmap runs in to metadata */
- goto bad_alignment;
- if (rdev->data_offset + mddev->dev_sectors
- > rdev->sb_start + offset)
- /* data runs in to bitmap */
- goto bad_alignment;
- } else if (rdev->sb_start < rdev->data_offset) {
- /* METADATA BITMAP DATA */
- if (rdev->sb_start
- + offset
- + page->index*(PAGE_SIZE/512) + size/512
- > rdev->data_offset)
- /* bitmap runs in to data */
- goto bad_alignment;
- } else {
- /* DATA METADATA BITMAP - no problems */
- }
- md_super_write(mddev, rdev,
- rdev->sb_start + offset
- + page->index * (PAGE_SIZE/512),
- size,
- page);
+ int size = PAGE_SIZE;
+ loff_t offset = mddev->bitmap_info.offset;
+ if (page->index == bitmap->file_pages-1)
+ size = roundup(bitmap->last_page_size,
+ bdev_logical_block_size(rdev->bdev));
+ /* Just make sure we aren't corrupting data or
+ * metadata
+ */
+ if (mddev->external) {
+ /* Bitmap could be anywhere. */
+ if (rdev->sb_start + offset + (page->index
+ * (PAGE_SIZE/512))
+ > rdev->data_offset
+ &&
+ rdev->sb_start + offset
+ < (rdev->data_offset + mddev->dev_sectors
+ + (PAGE_SIZE/512)))
+ goto bad_alignment;
+ } else if (offset < 0) {
+ /* DATA BITMAP METADATA */
+ if (offset
+ + (long)(page->index * (PAGE_SIZE/512))
+ + size/512 > 0)
+ /* bitmap runs in to metadata */
+ goto bad_alignment;
+ if (rdev->data_offset + mddev->dev_sectors
+ > rdev->sb_start + offset)
+ /* data runs in to bitmap */
+ goto bad_alignment;
+ } else if (rdev->sb_start < rdev->data_offset) {
+ /* METADATA BITMAP DATA */
+ if (rdev->sb_start
+ + offset
+ + page->index*(PAGE_SIZE/512) + size/512
+ > rdev->data_offset)
+ /* bitmap runs in to data */
+ goto bad_alignment;
+ } else {
+ /* DATA METADATA BITMAP - no problems */
+ }
+ md_super_write(mddev, rdev,
+ rdev->sb_start + offset
+ + page->index * (PAGE_SIZE/512),
+ size,
+ page);
}
if (wait)
@@ -364,10 +347,9 @@ static void write_page(struct bitmap *bitmap, struct page *page, int wait)
bh = bh->b_this_page;
}
- if (wait) {
+ if (wait)
wait_event(bitmap->write_wait,
atomic_read(&bitmap->pending_writes)==0);
- }
}
if (bitmap->flags & BITMAP_WRITE_ERROR)
bitmap_file_kick(bitmap);
@@ -424,7 +406,7 @@ static struct page *read_page(struct file *file, unsigned long index,
struct buffer_head *bh;
sector_t block;
- PRINTK("read bitmap file (%dB @ %Lu)\n", (int)PAGE_SIZE,
+ PRINTK("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
(unsigned long long)index << PAGE_SHIFT);
page = alloc_page(GFP_KERNEL);
@@ -478,7 +460,7 @@ static struct page *read_page(struct file *file, unsigned long index,
}
out:
if (IS_ERR(page))
- printk(KERN_ALERT "md: bitmap read error: (%dB @ %Lu): %ld\n",
+ printk(KERN_ALERT "md: bitmap read error: (%dB @ %llu): %ld\n",
(int)PAGE_SIZE,
(unsigned long long)index << PAGE_SHIFT,
PTR_ERR(page));
@@ -664,11 +646,14 @@ static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
sb = kmap_atomic(bitmap->sb_page, KM_USER0);
old = le32_to_cpu(sb->state) & bits;
switch (op) {
- case MASK_SET: sb->state |= cpu_to_le32(bits);
- break;
- case MASK_UNSET: sb->state &= cpu_to_le32(~bits);
- break;
- default: BUG();
+ case MASK_SET:
+ sb->state |= cpu_to_le32(bits);
+ break;
+ case MASK_UNSET:
+ sb->state &= cpu_to_le32(~bits);
+ break;
+ default:
+ BUG();
}
kunmap_atomic(sb, KM_USER0);
return old;
@@ -710,12 +695,14 @@ static inline unsigned long file_page_offset(struct bitmap *bitmap, unsigned lon
static inline struct page *filemap_get_page(struct bitmap *bitmap,
unsigned long chunk)
{
- if (file_page_index(bitmap, chunk) >= bitmap->file_pages) return NULL;
+ if (bitmap->filemap == NULL)
+ return NULL;
+ if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
+ return NULL;
return bitmap->filemap[file_page_index(bitmap, chunk)
- file_page_index(bitmap, 0)];
}
-
static void bitmap_file_unmap(struct bitmap *bitmap)
{
struct page **map, *sb_page;
@@ -766,7 +753,6 @@ static void bitmap_file_put(struct bitmap *bitmap)
}
}
-
/*
* bitmap_file_kick - if an error occurs while manipulating the bitmap file
* then it is no longer reliable, so we stop using it and we mark the file
@@ -785,7 +771,6 @@ static void bitmap_file_kick(struct bitmap *bitmap)
ptr = d_path(&bitmap->file->f_path, path,
PAGE_SIZE);
-
printk(KERN_ALERT
"%s: kicking failed bitmap file %s from array!\n",
bmname(bitmap), IS_ERR(ptr) ? "" : ptr);
@@ -803,27 +788,36 @@ static void bitmap_file_kick(struct bitmap *bitmap)
}
enum bitmap_page_attr {
- BITMAP_PAGE_DIRTY = 0, // there are set bits that need to be synced
- BITMAP_PAGE_CLEAN = 1, // there are bits that might need to be cleared
- BITMAP_PAGE_NEEDWRITE=2, // there are cleared bits that need to be synced
+ BITMAP_PAGE_DIRTY = 0, /* there are set bits that need to be synced */
+ BITMAP_PAGE_CLEAN = 1, /* there are bits that might need to be cleared */
+ BITMAP_PAGE_NEEDWRITE = 2, /* there are cleared bits that need to be synced */
};
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ if (page)
+ __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ else
+ __set_bit(attr, &bitmap->logattrs);
}
static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ if (page)
+ __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ else
+ __clear_bit(attr, &bitmap->logattrs);
}
static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ if (page)
+ return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ else
+ return test_bit(attr, &bitmap->logattrs);
}
/*
@@ -836,30 +830,32 @@ static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *p
static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
{
unsigned long bit;
- struct page *page;
+ struct page *page = NULL;
void *kaddr;
unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
if (!bitmap->filemap) {
- return;
- }
-
- page = filemap_get_page(bitmap, chunk);
- if (!page) return;
- bit = file_page_offset(bitmap, chunk);
+ struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
+ if (log)
+ log->type->mark_region(log, chunk);
+ } else {
- /* set the bit */
- kaddr = kmap_atomic(page, KM_USER0);
- if (bitmap->flags & BITMAP_HOSTENDIAN)
- set_bit(bit, kaddr);
- else
- ext2_set_bit(bit, kaddr);
- kunmap_atomic(kaddr, KM_USER0);
- PRINTK("set file bit %lu page %lu\n", bit, page->index);
+ page = filemap_get_page(bitmap, chunk);
+ if (!page)
+ return;
+ bit = file_page_offset(bitmap, chunk);
+ /* set the bit */
+ kaddr = kmap_atomic(page, KM_USER0);
+ if (bitmap->flags & BITMAP_HOSTENDIAN)
+ set_bit(bit, kaddr);
+ else
+ ext2_set_bit(bit, kaddr);
+ kunmap_atomic(kaddr, KM_USER0);
+ PRINTK("set file bit %lu page %lu\n", bit, page->index);
+ }
/* record page number so it gets flushed to disk when unplug occurs */
set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
-
}
/* this gets called when the md device is ready to unplug its underlying
@@ -874,6 +870,16 @@ void bitmap_unplug(struct bitmap *bitmap)
if (!bitmap)
return;
+ if (!bitmap->filemap) {
+ /* Must be using a dirty_log */
+ struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
+ dirty = test_and_clear_bit(BITMAP_PAGE_DIRTY, &bitmap->logattrs);
+ need_write = test_and_clear_bit(BITMAP_PAGE_NEEDWRITE, &bitmap->logattrs);
+ if (dirty || need_write)
+ if (log->type->flush(log))
+ bitmap->flags |= BITMAP_WRITE_ERROR;
+ goto out;
+ }
/* look at each page to see if there are any set bits that need to be
* flushed out to disk */
@@ -892,7 +898,7 @@ void bitmap_unplug(struct bitmap *bitmap)
wait = 1;
spin_unlock_irqrestore(&bitmap->lock, flags);
- if (dirty | need_write)
+ if (dirty || need_write)
write_page(bitmap, page, 0);
}
if (wait) { /* if any writes were performed, we need to wait on them */
@@ -902,9 +908,11 @@ void bitmap_unplug(struct bitmap *bitmap)
else
md_super_wait(bitmap->mddev);
}
+out:
if (bitmap->flags & BITMAP_WRITE_ERROR)
bitmap_file_kick(bitmap);
}
+EXPORT_SYMBOL(bitmap_unplug);
static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed);
/* * bitmap_init_from_disk -- called at bitmap_create time to initialize
@@ -943,12 +951,11 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
printk(KERN_INFO "%s: bitmap file is out of date, doing full "
"recovery\n", bmname(bitmap));
- bytes = (chunks + 7) / 8;
+ bytes = DIV_ROUND_UP(bitmap->chunks, 8);
if (!bitmap->mddev->bitmap_info.external)
bytes += sizeof(bitmap_super_t);
-
- num_pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
+ num_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
if (file && i_size_read(file->f_mapping->host) < bytes) {
printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
@@ -966,7 +973,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
/* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */
bitmap->filemap_attr = kzalloc(
- roundup( DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
+ roundup(DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
GFP_KERNEL);
if (!bitmap->filemap_attr)
goto err;
@@ -1021,7 +1028,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
if (outofdate) {
/*
* if bitmap is out of date, dirty the
- * whole page and write it out
+ * whole page and write it out
*/
paddr = kmap_atomic(page, KM_USER0);
memset(paddr + offset, 0xff,
@@ -1052,7 +1059,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
}
}
- /* everything went OK */
+ /* everything went OK */
ret = 0;
bitmap_mask_state(bitmap, BITMAP_STALE, MASK_UNSET);
@@ -1080,21 +1087,16 @@ void bitmap_write_all(struct bitmap *bitmap)
*/
int i;
- for (i=0; i < bitmap->file_pages; i++)
+ for (i = 0; i < bitmap->file_pages; i++)
set_page_attr(bitmap, bitmap->filemap[i],
BITMAP_PAGE_NEEDWRITE);
}
-
static void bitmap_count_page(struct bitmap *bitmap, sector_t offset, int inc)
{
sector_t chunk = offset >> CHUNK_BLOCK_SHIFT(bitmap);
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
bitmap->bp[page].count += inc;
-/*
- if (page == 0) printk("count page 0, offset %llu: %d gives %d\n",
- (unsigned long long)offset, inc, bitmap->bp[page].count);
-*/
bitmap_checkfree(bitmap, page);
}
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
@@ -1114,6 +1116,7 @@ void bitmap_daemon_work(mddev_t *mddev)
struct page *page = NULL, *lastpage = NULL;
int blocks;
void *paddr;
+ struct dm_dirty_log *log = mddev->bitmap_info.log;
/* Use a mutex to guard daemon_work against
* bitmap_destroy.
@@ -1138,11 +1141,12 @@ void bitmap_daemon_work(mddev_t *mddev)
spin_lock_irqsave(&bitmap->lock, flags);
for (j = 0; j < bitmap->chunks; j++) {
bitmap_counter_t *bmc;
- if (!bitmap->filemap)
- /* error or shutdown */
- break;
-
- page = filemap_get_page(bitmap, j);
+ if (!bitmap->filemap) {
+ if (!log)
+ /* error or shutdown */
+ break;
+ } else
+ page = filemap_get_page(bitmap, j);
if (page != lastpage) {
/* skip this page unless it's marked as needing cleaning */
@@ -1197,14 +1201,11 @@ void bitmap_daemon_work(mddev_t *mddev)
(sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
&blocks, 0);
if (bmc) {
-/*
- if (j < 100) printk("bitmap: j=%lu, *bmc = 0x%x\n", j, *bmc);
-*/
if (*bmc)
bitmap->allclean = 0;
if (*bmc == 2) {
- *bmc=1; /* maybe clear the bit next time */
+ *bmc = 1; /* maybe clear the bit next time */
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
} else if (*bmc == 1 && !bitmap->need_sync) {
/* we can clear the bit */
@@ -1214,14 +1215,17 @@ void bitmap_daemon_work(mddev_t *mddev)
-1);
/* clear the bit */
- paddr = kmap_atomic(page, KM_USER0);
- if (bitmap->flags & BITMAP_HOSTENDIAN)
- clear_bit(file_page_offset(bitmap, j),
- paddr);
- else
- ext2_clear_bit(file_page_offset(bitmap, j),
- paddr);
- kunmap_atomic(paddr, KM_USER0);
+ if (page) {
+ paddr = kmap_atomic(page, KM_USER0);
+ if (bitmap->flags & BITMAP_HOSTENDIAN)
+ clear_bit(file_page_offset(bitmap, j),
+ paddr);
+ else
+ ext2_clear_bit(file_page_offset(bitmap, j),
+ paddr);
+ kunmap_atomic(paddr, KM_USER0);
+ } else
+ log->type->clear_region(log, j);
}
} else
j |= PAGE_COUNTER_MASK;
@@ -1229,12 +1233,16 @@ void bitmap_daemon_work(mddev_t *mddev)
spin_unlock_irqrestore(&bitmap->lock, flags);
/* now sync the final page */
- if (lastpage != NULL) {
+ if (lastpage != NULL || log != NULL) {
spin_lock_irqsave(&bitmap->lock, flags);
if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
- write_page(bitmap, lastpage, 0);
+ if (lastpage)
+ write_page(bitmap, lastpage, 0);
+ else
+ if (log->type->flush(log))
+ bitmap->flags |= BITMAP_WRITE_ERROR;
} else {
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
@@ -1243,7 +1251,7 @@ void bitmap_daemon_work(mddev_t *mddev)
done:
if (bitmap->allclean == 0)
- bitmap->mddev->thread->timeout =
+ bitmap->mddev->thread->timeout =
bitmap->mddev->bitmap_info.daemon_sleep;
mutex_unlock(&mddev->bitmap_info.mutex);
}
@@ -1262,34 +1270,38 @@ __acquires(bitmap->lock)
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
unsigned long pageoff = (chunk & PAGE_COUNTER_MASK) << COUNTER_BYTE_SHIFT;
sector_t csize;
+ int err;
- if (bitmap_checkpage(bitmap, page, create) < 0) {
+ err = bitmap_checkpage(bitmap, page, create);
+
+ if (bitmap->bp[page].hijacked ||
+ bitmap->bp[page].map == NULL)
+ csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap) +
+ PAGE_COUNTER_SHIFT - 1);
+ else
csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
- *blocks = csize - (offset & (csize- 1));
+ *blocks = csize - (offset & (csize - 1));
+
+ if (err < 0)
return NULL;
- }
+
/* now locked ... */
if (bitmap->bp[page].hijacked) { /* hijacked pointer */
/* should we use the first or second counter field
* of the hijacked pointer? */
int hi = (pageoff > PAGE_COUNTER_MASK);
- csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap) +
- PAGE_COUNTER_SHIFT - 1);
- *blocks = csize - (offset & (csize- 1));
return &((bitmap_counter_t *)
&bitmap->bp[page].map)[hi];
- } else { /* page is allocated */
- csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
- *blocks = csize - (offset & (csize- 1));
+ } else /* page is allocated */
return (bitmap_counter_t *)
&(bitmap->bp[page].map[pageoff]);
- }
}
int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors, int behind)
{
- if (!bitmap) return 0;
+ if (!bitmap)
+ return 0;
if (behind) {
int bw;
@@ -1322,17 +1334,16 @@ int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sect
prepare_to_wait(&bitmap->overflow_wait, &__wait,
TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&bitmap->lock);
- blk_unplug(bitmap->mddev->queue);
+ md_unplug(bitmap->mddev);
schedule();
finish_wait(&bitmap->overflow_wait, &__wait);
continue;
}
- switch(*bmc) {
+ switch (*bmc) {
case 0:
bitmap_file_set_bit(bitmap, offset);
- bitmap_count_page(bitmap,offset, 1);
- blk_plug_device_unlocked(bitmap->mddev->queue);
+ bitmap_count_page(bitmap, offset, 1);
/* fall through */
case 1:
*bmc = 2;
@@ -1345,16 +1356,19 @@ int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sect
offset += blocks;
if (sectors > blocks)
sectors -= blocks;
- else sectors = 0;
+ else
+ sectors = 0;
}
bitmap->allclean = 0;
return 0;
}
+EXPORT_SYMBOL(bitmap_startwrite);
void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors,
int success, int behind)
{
- if (!bitmap) return;
+ if (!bitmap)
+ return;
if (behind) {
if (atomic_dec_and_test(&bitmap->behind_writes))
wake_up(&bitmap->behind_wait);
@@ -1381,7 +1395,7 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto
bitmap->events_cleared < bitmap->mddev->events) {
bitmap->events_cleared = bitmap->mddev->events;
bitmap->need_sync = 1;
- sysfs_notify_dirent(bitmap->sysfs_can_clear);
+ sysfs_notify_dirent_safe(bitmap->sysfs_can_clear);
}
if (!success && ! (*bmc & NEEDED_MASK))
@@ -1391,18 +1405,22 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto
wake_up(&bitmap->overflow_wait);
(*bmc)--;
- if (*bmc <= 2) {
+ if (*bmc <= 2)
set_page_attr(bitmap,
- filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
+ filemap_get_page(
+ bitmap,
+ offset >> CHUNK_BLOCK_SHIFT(bitmap)),
BITMAP_PAGE_CLEAN);
- }
+
spin_unlock_irqrestore(&bitmap->lock, flags);
offset += blocks;
if (sectors > blocks)
sectors -= blocks;
- else sectors = 0;
+ else
+ sectors = 0;
}
}
+EXPORT_SYMBOL(bitmap_endwrite);
static int __bitmap_start_sync(struct bitmap *bitmap, sector_t offset, int *blocks,
int degraded)
@@ -1455,14 +1473,14 @@ int bitmap_start_sync(struct bitmap *bitmap, sector_t offset, int *blocks,
}
return rv;
}
+EXPORT_SYMBOL(bitmap_start_sync);
void bitmap_end_sync(struct bitmap *bitmap, sector_t offset, int *blocks, int aborted)
{
bitmap_counter_t *bmc;
unsigned long flags;
-/*
- if (offset == 0) printk("bitmap_end_sync 0 (%d)\n", aborted);
-*/ if (bitmap == NULL) {
+
+ if (bitmap == NULL) {
*blocks = 1024;
return;
}
@@ -1471,26 +1489,23 @@ void bitmap_end_sync(struct bitmap *bitmap, sector_t offset, int *blocks, int ab
if (bmc == NULL)
goto unlock;
/* locked */
-/*
- if (offset == 0) printk("bitmap_end sync found 0x%x, blocks %d\n", *bmc, *blocks);
-*/
if (RESYNC(*bmc)) {
*bmc &= ~RESYNC_MASK;
if (!NEEDED(*bmc) && aborted)
*bmc |= NEEDED_MASK;
else {
- if (*bmc <= 2) {
+ if (*bmc <= 2)
set_page_attr(bitmap,
filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
BITMAP_PAGE_CLEAN);
- }
}
}
unlock:
spin_unlock_irqrestore(&bitmap->lock, flags);
bitmap->allclean = 0;
}
+EXPORT_SYMBOL(bitmap_end_sync);
void bitmap_close_sync(struct bitmap *bitmap)
{
@@ -1507,6 +1522,7 @@ void bitmap_close_sync(struct bitmap *bitmap)
sector += blocks;
}
}
+EXPORT_SYMBOL(bitmap_close_sync);
void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
{
@@ -1526,7 +1542,8 @@ void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
atomic_read(&bitmap->mddev->recovery_active) == 0);
bitmap->mddev->curr_resync_completed = bitmap->mddev->curr_resync;
- set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
+ if (bitmap->mddev->persistent)
+ set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
sector &= ~((1ULL << CHUNK_BLOCK_SHIFT(bitmap)) - 1);
s = 0;
while (s < sector && s < bitmap->mddev->resync_max_sectors) {
@@ -1536,6 +1553,7 @@ void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
bitmap->last_end_sync = jiffies;
sysfs_notify(&bitmap->mddev->kobj, NULL, "sync_completed");
}
+EXPORT_SYMBOL(bitmap_cond_end_sync);
static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed)
{
@@ -1552,9 +1570,9 @@ static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int n
spin_unlock_irq(&bitmap->lock);
return;
}
- if (! *bmc) {
+ if (!*bmc) {
struct page *page;
- *bmc = 1 | (needed?NEEDED_MASK:0);
+ *bmc = 1 | (needed ? NEEDED_MASK : 0);
bitmap_count_page(bitmap, offset, 1);
page = filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap));
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
@@ -1663,15 +1681,17 @@ int bitmap_create(mddev_t *mddev)
unsigned long pages;
struct file *file = mddev->bitmap_info.file;
int err;
- sector_t start;
- struct sysfs_dirent *bm;
+ struct sysfs_dirent *bm = NULL;
BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
- if (!file && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
+ if (!file
+ && !mddev->bitmap_info.offset
+ && !mddev->bitmap_info.log) /* bitmap disabled, nothing to do */
return 0;
BUG_ON(file && mddev->bitmap_info.offset);
+ BUG_ON(mddev->bitmap_info.offset && mddev->bitmap_info.log);
bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
if (!bitmap)
@@ -1685,7 +1705,8 @@ int bitmap_create(mddev_t *mddev)
bitmap->mddev = mddev;
- bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap");
+ if (mddev->kobj.sd)
+ bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap");
if (bm) {
bitmap->sysfs_can_clear = sysfs_get_dirent(bm, NULL, "can_clear");
sysfs_put(bm);
@@ -1719,9 +1740,9 @@ int bitmap_create(mddev_t *mddev)
bitmap->chunkshift = ffz(~mddev->bitmap_info.chunksize);
/* now that chunksize and chunkshift are set, we can use these macros */
- chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >>
+ chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >>
CHUNK_BLOCK_SHIFT(bitmap);
- pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
+ pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
BUG_ON(!pages);
@@ -1741,27 +1762,11 @@ int bitmap_create(mddev_t *mddev)
if (!bitmap->bp)
goto error;
- /* now that we have some pages available, initialize the in-memory
- * bitmap from the on-disk bitmap */
- start = 0;
- if (mddev->degraded == 0
- || bitmap->events_cleared == mddev->events)
- /* no need to keep dirty bits to optimise a re-add of a missing device */
- start = mddev->recovery_cp;
- err = bitmap_init_from_disk(bitmap, start);
-
- if (err)
- goto error;
-
printk(KERN_INFO "created bitmap (%lu pages) for device %s\n",
pages, bmname(bitmap));
mddev->bitmap = bitmap;
- mddev->thread->timeout = mddev->bitmap_info.daemon_sleep;
- md_wakeup_thread(mddev->thread);
-
- bitmap_update_sb(bitmap);
return (bitmap->flags & BITMAP_WRITE_ERROR) ? -EIO : 0;
@@ -1770,15 +1775,69 @@ int bitmap_create(mddev_t *mddev)
return err;
}
+int bitmap_load(mddev_t *mddev)
+{
+ int err = 0;
+ sector_t sector = 0;
+ struct bitmap *bitmap = mddev->bitmap;
+
+ if (!bitmap)
+ goto out;
+
+ /* Clear out old bitmap info first: Either there is none, or we
+ * are resuming after someone else has possibly changed things,
+ * so we should forget old cached info.
+ * All chunks should be clean, but some might need_sync.
+ */
+ while (sector < mddev->resync_max_sectors) {
+ int blocks;
+ bitmap_start_sync(bitmap, sector, &blocks, 0);
+ sector += blocks;
+ }
+ bitmap_close_sync(bitmap);
+
+ if (mddev->bitmap_info.log) {
+ unsigned long i;
+ struct dm_dirty_log *log = mddev->bitmap_info.log;
+ for (i = 0; i < bitmap->chunks; i++)
+ if (!log->type->in_sync(log, i, 1))
+ bitmap_set_memory_bits(bitmap,
+ (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
+ 1);
+ } else {
+ sector_t start = 0;
+ if (mddev->degraded == 0
+ || bitmap->events_cleared == mddev->events)
+ /* no need to keep dirty bits to optimise a
+ * re-add of a missing device */
+ start = mddev->recovery_cp;
+
+ err = bitmap_init_from_disk(bitmap, start);
+ }
+ if (err)
+ goto out;
+
+ mddev->thread->timeout = mddev->bitmap_info.daemon_sleep;
+ md_wakeup_thread(mddev->thread);
+
+ bitmap_update_sb(bitmap);
+
+ if (bitmap->flags & BITMAP_WRITE_ERROR)
+ err = -EIO;
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(bitmap_load);
+
static ssize_t
location_show(mddev_t *mddev, char *page)
{
ssize_t len;
- if (mddev->bitmap_info.file) {
+ if (mddev->bitmap_info.file)
len = sprintf(page, "file");
- } else if (mddev->bitmap_info.offset) {
+ else if (mddev->bitmap_info.offset)
len = sprintf(page, "%+lld", (long long)mddev->bitmap_info.offset);
- } else
+ else
len = sprintf(page, "none");
len += sprintf(page+len, "\n");
return len;
@@ -1867,7 +1926,7 @@ timeout_show(mddev_t *mddev, char *page)
ssize_t len;
unsigned long secs = mddev->bitmap_info.daemon_sleep / HZ;
unsigned long jifs = mddev->bitmap_info.daemon_sleep % HZ;
-
+
len = sprintf(page, "%lu", secs);
if (jifs)
len += sprintf(page+len, ".%03u", jiffies_to_msecs(jifs));
@@ -2049,12 +2108,3 @@ struct attribute_group md_bitmap_group = {
.attrs = md_bitmap_attrs,
};
-
-/* the bitmap API -- for raid personalities */
-EXPORT_SYMBOL(bitmap_startwrite);
-EXPORT_SYMBOL(bitmap_endwrite);
-EXPORT_SYMBOL(bitmap_start_sync);
-EXPORT_SYMBOL(bitmap_end_sync);
-EXPORT_SYMBOL(bitmap_unplug);
-EXPORT_SYMBOL(bitmap_close_sync);
-EXPORT_SYMBOL(bitmap_cond_end_sync);
diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h
index 3797dea..e872a7b 100644
--- a/drivers/md/bitmap.h
+++ b/drivers/md/bitmap.h
@@ -222,6 +222,10 @@ struct bitmap {
unsigned long file_pages; /* number of pages in the file */
int last_page_size; /* bytes in the last page */
+ unsigned long logattrs; /* used when filemap_attr doesn't exist
+ * because we are working with a dirty_log
+ */
+
unsigned long flags;
int allclean;
@@ -243,12 +247,14 @@ struct bitmap {
wait_queue_head_t behind_wait;
struct sysfs_dirent *sysfs_can_clear;
+
};
/* the bitmap API */
/* these are used only by md/bitmap */
int bitmap_create(mddev_t *mddev);
+int bitmap_load(mddev_t *mddev);
void bitmap_flush(mddev_t *mddev);
void bitmap_destroy(mddev_t *mddev);
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 3bdbb61..368e8e9 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -107,11 +107,10 @@ struct crypt_config {
struct workqueue_struct *io_queue;
struct workqueue_struct *crypt_queue;
- /*
- * crypto related data
- */
+ char *cipher;
+ char *cipher_mode;
+
struct crypt_iv_operations *iv_gen_ops;
- char *iv_mode;
union {
struct iv_essiv_private essiv;
struct iv_benbi_private benbi;
@@ -135,8 +134,6 @@ struct crypt_config {
unsigned int dmreq_start;
struct ablkcipher_request *req;
- char cipher[CRYPTO_MAX_ALG_NAME];
- char chainmode[CRYPTO_MAX_ALG_NAME];
struct crypto_ablkcipher *tfm;
unsigned long flags;
unsigned int key_size;
@@ -999,82 +996,135 @@ static int crypt_wipe_key(struct crypt_config *cc)
return crypto_ablkcipher_setkey(cc->tfm, cc->key, cc->key_size);
}
-/*
- * Construct an encryption mapping:
- * <cipher> <key> <iv_offset> <dev_path> <start>
- */
-static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+static void crypt_dtr(struct dm_target *ti)
{
- struct crypt_config *cc;
- struct crypto_ablkcipher *tfm;
- char *tmp;
- char *cipher;
- char *chainmode;
- char *ivmode;
- char *ivopts;
- unsigned int key_size;
- unsigned long long tmpll;
+ struct crypt_config *cc = ti->private;
- if (argc != 5) {
- ti->error = "Not enough arguments";
+ ti->private = NULL;
+
+ if (!cc)
+ return;
+
+ if (cc->io_queue)
+ destroy_workqueue(cc->io_queue);
+ if (cc->crypt_queue)
+ destroy_workqueue(cc->crypt_queue);
+
+ if (cc->bs)
+ bioset_free(cc->bs);
+
+ if (cc->page_pool)
+ mempool_destroy(cc->page_pool);
+ if (cc->req_pool)
+ mempool_destroy(cc->req_pool);
+ if (cc->io_pool)
+ mempool_destroy(cc->io_pool);
+
+ if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)
+ cc->iv_gen_ops->dtr(cc);
+
+ if (cc->tfm && !IS_ERR(cc->tfm))
+ crypto_free_ablkcipher(cc->tfm);
+
+ if (cc->dev)
+ dm_put_device(ti, cc->dev);
+
+ kzfree(cc->cipher);
+ kzfree(cc->cipher_mode);
+
+ /* Must zero key material before freeing */
+ kzfree(cc);
+}
+
+static int crypt_ctr_cipher(struct dm_target *ti,
+ char *cipher_in, char *key)
+{
+ struct crypt_config *cc = ti->private;
+ char *tmp, *cipher, *chainmode, *ivmode, *ivopts;
+ char *cipher_api = NULL;
+ int ret = -EINVAL;
+
+ /* Convert to crypto api definition? */
+ if (strchr(cipher_in, '(')) {
+ ti->error = "Bad cipher specification";
return -EINVAL;
}
- tmp = argv[0];
+ /*
+ * Legacy dm-crypt cipher specification
+ * cipher-mode-iv:ivopts
+ */
+ tmp = cipher_in;
cipher = strsep(&tmp, "-");
+
+ cc->cipher = kstrdup(cipher, GFP_KERNEL);
+ if (!cc->cipher)
+ goto bad_mem;
+
+ if (tmp) {
+ cc->cipher_mode = kstrdup(tmp, GFP_KERNEL);
+ if (!cc->cipher_mode)
+ goto bad_mem;
+ }
+
chainmode = strsep(&tmp, "-");
ivopts = strsep(&tmp, "-");
ivmode = strsep(&ivopts, ":");
if (tmp)
- DMWARN("Unexpected additional cipher options");
-
- key_size = strlen(argv[1]) >> 1;
-
- cc = kzalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL);
- if (cc == NULL) {
- ti->error =
- "Cannot allocate transparent encryption context";
- return -ENOMEM;
- }
+ DMWARN("Ignoring unexpected additional cipher options");
- /* Compatibility mode for old dm-crypt cipher strings */
- if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) {
+ /* Compatibility mode for old dm-crypt mappings */
+ if (!chainmode || (!strcmp(chainmode, "plain") && !ivmode)) {
+ kfree(cc->cipher_mode);
+ cc->cipher_mode = kstrdup("cbc-plain", GFP_KERNEL);
chainmode = "cbc";
ivmode = "plain";
}
if (strcmp(chainmode, "ecb") && !ivmode) {
- ti->error = "This chaining mode requires an IV mechanism";
- goto bad_cipher;
+ ti->error = "IV mechanism required";
+ return -EINVAL;
}
- if (snprintf(cc->cipher, CRYPTO_MAX_ALG_NAME, "%s(%s)",
- chainmode, cipher) >= CRYPTO_MAX_ALG_NAME) {
- ti->error = "Chain mode + cipher name is too long";
- goto bad_cipher;
+ cipher_api = kmalloc(CRYPTO_MAX_ALG_NAME, GFP_KERNEL);
+ if (!cipher_api)
+ goto bad_mem;
+
+ ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
+ "%s(%s)", chainmode, cipher);
+ if (ret < 0) {
+ kfree(cipher_api);
+ goto bad_mem;
}
- tfm = crypto_alloc_ablkcipher(cc->cipher, 0, 0);
- if (IS_ERR(tfm)) {
+ /* Allocate cipher */
+ cc->tfm = crypto_alloc_ablkcipher(cipher_api, 0, 0);
+ if (IS_ERR(cc->tfm)) {
+ ret = PTR_ERR(cc->tfm);
ti->error = "Error allocating crypto tfm";
- goto bad_cipher;
+ goto bad;
}
- strcpy(cc->cipher, cipher);
- strcpy(cc->chainmode, chainmode);
- cc->tfm = tfm;
-
- if (crypt_set_key(cc, argv[1]) < 0) {
+ /* Initialize and set key */
+ ret = crypt_set_key(cc, key);
+ if (ret < 0) {
ti->error = "Error decoding and setting key";
- goto bad_ivmode;
+ goto bad;
}
- /*
- * Choose ivmode. Valid modes: "plain", "essiv:<esshash>", "benbi".
- * See comments at iv code
- */
+ /* Initialize IV */
+ cc->iv_size = crypto_ablkcipher_ivsize(cc->tfm);
+ if (cc->iv_size)
+ /* at least a 64 bit sector number should fit in our buffer */
+ cc->iv_size = max(cc->iv_size,
+ (unsigned int)(sizeof(u64) / sizeof(u8)));
+ else if (ivmode) {
+ DMWARN("Selected cipher does not support IVs");
+ ivmode = NULL;
+ }
+ /* Choose ivmode, see comments at iv code. */
if (ivmode == NULL)
cc->iv_gen_ops = NULL;
else if (strcmp(ivmode, "plain") == 0)
@@ -1088,159 +1138,138 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
else if (strcmp(ivmode, "null") == 0)
cc->iv_gen_ops = &crypt_iv_null_ops;
else {
+ ret = -EINVAL;
ti->error = "Invalid IV mode";
- goto bad_ivmode;
+ goto bad;
}
- if (cc->iv_gen_ops && cc->iv_gen_ops->ctr &&
- cc->iv_gen_ops->ctr(cc, ti, ivopts) < 0)
- goto bad_ivmode;
-
- if (cc->iv_gen_ops && cc->iv_gen_ops->init &&
- cc->iv_gen_ops->init(cc) < 0) {
- ti->error = "Error initialising IV";
- goto bad_slab_pool;
+ /* Allocate IV */
+ if (cc->iv_gen_ops && cc->iv_gen_ops->ctr) {
+ ret = cc->iv_gen_ops->ctr(cc, ti, ivopts);
+ if (ret < 0) {
+ ti->error = "Error creating IV";
+ goto bad;
+ }
}
- cc->iv_size = crypto_ablkcipher_ivsize(tfm);
- if (cc->iv_size)
- /* at least a 64 bit sector number should fit in our buffer */
- cc->iv_size = max(cc->iv_size,
- (unsigned int)(sizeof(u64) / sizeof(u8)));
- else {
- if (cc->iv_gen_ops) {
- DMWARN("Selected cipher does not support IVs");
- if (cc->iv_gen_ops->dtr)
- cc->iv_gen_ops->dtr(cc);
- cc->iv_gen_ops = NULL;
+ /* Initialize IV (set keys for ESSIV etc) */
+ if (cc->iv_gen_ops && cc->iv_gen_ops->init) {
+ ret = cc->iv_gen_ops->init(cc);
+ if (ret < 0) {
+ ti->error = "Error initialising IV";
+ goto bad;
}
}
+ ret = 0;
+bad:
+ kfree(cipher_api);
+ return ret;
+
+bad_mem:
+ ti->error = "Cannot allocate cipher strings";
+ return -ENOMEM;
+}
+
+/*
+ * Construct an encryption mapping:
+ * <cipher> <key> <iv_offset> <dev_path> <start>
+ */
+static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ struct crypt_config *cc;
+ unsigned int key_size;
+ unsigned long long tmpll;
+ int ret;
+
+ if (argc != 5) {
+ ti->error = "Not enough arguments";
+ return -EINVAL;
+ }
+
+ key_size = strlen(argv[1]) >> 1;
+
+ cc = kzalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL);
+ if (!cc) {
+ ti->error = "Cannot allocate encryption context";
+ return -ENOMEM;
+ }
+
+ ti->private = cc;
+ ret = crypt_ctr_cipher(ti, argv[0], argv[1]);
+ if (ret < 0)
+ goto bad;
+
+ ret = -ENOMEM;
cc->io_pool = mempool_create_slab_pool(MIN_IOS, _crypt_io_pool);
if (!cc->io_pool) {
ti->error = "Cannot allocate crypt io mempool";
- goto bad_slab_pool;
+ goto bad;
}
cc->dmreq_start = sizeof(struct ablkcipher_request);
- cc->dmreq_start += crypto_ablkcipher_reqsize(tfm);
+ cc->dmreq_start += crypto_ablkcipher_reqsize(cc->tfm);
cc->dmreq_start = ALIGN(cc->dmreq_start, crypto_tfm_ctx_alignment());
- cc->dmreq_start += crypto_ablkcipher_alignmask(tfm) &
+ cc->dmreq_start += crypto_ablkcipher_alignmask(cc->tfm) &
~(crypto_tfm_ctx_alignment() - 1);
cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start +
sizeof(struct dm_crypt_request) + cc->iv_size);
if (!cc->req_pool) {
ti->error = "Cannot allocate crypt request mempool";
- goto bad_req_pool;
+ goto bad;
}
cc->req = NULL;
cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0);
if (!cc->page_pool) {
ti->error = "Cannot allocate page mempool";
- goto bad_page_pool;
+ goto bad;
}
cc->bs = bioset_create(MIN_IOS, 0);
if (!cc->bs) {
ti->error = "Cannot allocate crypt bioset";
- goto bad_bs;
+ goto bad;
}
+ ret = -EINVAL;
if (sscanf(argv[2], "%llu", &tmpll) != 1) {
ti->error = "Invalid iv_offset sector";
- goto bad_device;
+ goto bad;
}
cc->iv_offset = tmpll;
- if (sscanf(argv[4], "%llu", &tmpll) != 1) {
- ti->error = "Invalid device sector";
- goto bad_device;
- }
- cc->start = tmpll;
-
if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), &cc->dev)) {
ti->error = "Device lookup failed";
- goto bad_device;
+ goto bad;
}
- if (ivmode && cc->iv_gen_ops) {
- if (ivopts)
- *(ivopts - 1) = ':';
- cc->iv_mode = kmalloc(strlen(ivmode) + 1, GFP_KERNEL);
- if (!cc->iv_mode) {
- ti->error = "Error kmallocing iv_mode string";
- goto bad_ivmode_string;
- }
- strcpy(cc->iv_mode, ivmode);
- } else
- cc->iv_mode = NULL;
+ if (sscanf(argv[4], "%llu", &tmpll) != 1) {
+ ti->error = "Invalid device sector";
+ goto bad;
+ }
+ cc->start = tmpll;
+ ret = -ENOMEM;
cc->io_queue = create_singlethread_workqueue("kcryptd_io");
if (!cc->io_queue) {
ti->error = "Couldn't create kcryptd io queue";
- goto bad_io_queue;
+ goto bad;
}
cc->crypt_queue = create_singlethread_workqueue("kcryptd");
if (!cc->crypt_queue) {
ti->error = "Couldn't create kcryptd queue";
- goto bad_crypt_queue;
+ goto bad;
}
ti->num_flush_requests = 1;
- ti->private = cc;
return 0;
-bad_crypt_queue:
- destroy_workqueue(cc->io_queue);
-bad_io_queue:
- kfree(cc->iv_mode);
-bad_ivmode_string:
- dm_put_device(ti, cc->dev);
-bad_device:
- bioset_free(cc->bs);
-bad_bs:
- mempool_destroy(cc->page_pool);
-bad_page_pool:
- mempool_destroy(cc->req_pool);
-bad_req_pool:
- mempool_destroy(cc->io_pool);
-bad_slab_pool:
- if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)
- cc->iv_gen_ops->dtr(cc);
-bad_ivmode:
- crypto_free_ablkcipher(tfm);
-bad_cipher:
- /* Must zero key material before freeing */
- kzfree(cc);
- return -EINVAL;
-}
-
-static void crypt_dtr(struct dm_target *ti)
-{
- struct crypt_config *cc = (struct crypt_config *) ti->private;
-
- destroy_workqueue(cc->io_queue);
- destroy_workqueue(cc->crypt_queue);
-
- if (cc->req)
- mempool_free(cc->req, cc->req_pool);
-
- bioset_free(cc->bs);
- mempool_destroy(cc->page_pool);
- mempool_destroy(cc->req_pool);
- mempool_destroy(cc->io_pool);
-
- kfree(cc->iv_mode);
- if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)
- cc->iv_gen_ops->dtr(cc);
- crypto_free_ablkcipher(cc->tfm);
- dm_put_device(ti, cc->dev);
-
- /* Must zero key material before freeing */
- kzfree(cc);
+bad:
+ crypt_dtr(ti);
+ return ret;
}
static int crypt_map(struct dm_target *ti, struct bio *bio,
@@ -1255,7 +1284,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
return DM_MAPIO_REMAPPED;
}
- io = crypt_io_alloc(ti, bio, bio->bi_sector - ti->begin);
+ io = crypt_io_alloc(ti, bio, dm_target_offset(ti, bio->bi_sector));
if (bio_data_dir(io->base_bio) == READ)
kcryptd_queue_io(io);
@@ -1268,7 +1297,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
static int crypt_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen)
{
- struct crypt_config *cc = (struct crypt_config *) ti->private;
+ struct crypt_config *cc = ti->private;
unsigned int sz = 0;
switch (type) {
@@ -1277,11 +1306,10 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
break;
case STATUSTYPE_TABLE:
- if (cc->iv_mode)
- DMEMIT("%s-%s-%s ", cc->cipher, cc->chainmode,
- cc->iv_mode);
+ if (cc->cipher_mode)
+ DMEMIT("%s-%s ", cc->cipher, cc->cipher_mode);
else
- DMEMIT("%s-%s ", cc->cipher, cc->chainmode);
+ DMEMIT("%s ", cc->cipher);
if (cc->key_size > 0) {
if ((maxlen - sz) < ((cc->key_size << 1) + 1))
@@ -1378,7 +1406,7 @@ static int crypt_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return max_size;
bvm->bi_bdev = cc->dev->bdev;
- bvm->bi_sector = cc->start + bvm->bi_sector - ti->begin;
+ bvm->bi_sector = cc->start + dm_target_offset(ti, bvm->bi_sector);
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c
index 8520528..baa1191 100644
--- a/drivers/md/dm-delay.c
+++ b/drivers/md/dm-delay.c
@@ -198,6 +198,7 @@ out:
atomic_set(&dc->may_delay, 1);
ti->num_flush_requests = 1;
+ ti->num_discard_requests = 1;
ti->private = dc;
return 0;
@@ -281,14 +282,13 @@ static int delay_map(struct dm_target *ti, struct bio *bio,
bio->bi_bdev = dc->dev_write->bdev;
if (bio_sectors(bio))
bio->bi_sector = dc->start_write +
- (bio->bi_sector - ti->begin);
+ dm_target_offset(ti, bio->bi_sector);
return delay_bio(dc, dc->write_delay, bio);
}
bio->bi_bdev = dc->dev_read->bdev;
- bio->bi_sector = dc->start_read +
- (bio->bi_sector - ti->begin);
+ bio->bi_sector = dc->start_read + dm_target_offset(ti, bio->bi_sector);
return delay_bio(dc, dc->read_delay, bio);
}
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index 2b7907b..0bdb201 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -173,7 +173,9 @@ int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
/* Validate the chunk size against the device block size */
if (chunk_size %
- (bdev_logical_block_size(dm_snap_cow(store->snap)->bdev) >> 9)) {
+ (bdev_logical_block_size(dm_snap_cow(store->snap)->bdev) >> 9) ||
+ chunk_size %
+ (bdev_logical_block_size(dm_snap_origin(store->snap)->bdev) >> 9)) {
*error = "Chunk size is not a multiple of device blocksize";
return -EINVAL;
}
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index e8dfa06..0b25362 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -126,8 +126,9 @@ struct dm_exception_store {
};
/*
- * Obtain the cow device used by a given snapshot.
+ * Obtain the origin or cow device used by a given snapshot.
*/
+struct dm_dev *dm_snap_origin(struct dm_snapshot *snap);
struct dm_dev *dm_snap_cow(struct dm_snapshot *snap);
/*
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 10f457c..0590c75 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -356,7 +356,7 @@ static void dispatch_io(int rw, unsigned int num_regions,
BUG_ON(num_regions > DM_IO_MAX_REGIONS);
if (sync)
- rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
/*
* For multiple regions we need to be careful to rewind
@@ -364,7 +364,7 @@ static void dispatch_io(int rw, unsigned int num_regions,
*/
for (i = 0; i < num_regions; i++) {
*dp = old_pages;
- if (where[i].count || (rw & (1 << BIO_RW_BARRIER)))
+ if (where[i].count || (rw & REQ_HARDBARRIER))
do_region(rw, i, where + i, dp, io);
}
@@ -412,8 +412,8 @@ retry:
}
set_current_state(TASK_RUNNING);
- if (io->eopnotsupp_bits && (rw & (1 << BIO_RW_BARRIER))) {
- rw &= ~(1 << BIO_RW_BARRIER);
+ if (io->eopnotsupp_bits && (rw & REQ_HARDBARRIER)) {
+ rw &= ~REQ_HARDBARRIER;
goto retry;
}
@@ -479,8 +479,8 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
* New collapsed (a)synchronous interface.
*
* If the IO is asynchronous (i.e. it has notify.fn), you must either unplug
- * the queue with blk_unplug() some time later or set the BIO_RW_SYNC bit in
- * io_req->bi_rw. If you fail to do one of these, the IO will be submitted to
+ * the queue with blk_unplug() some time later or set REQ_SYNC in
+io_req->bi_rw. If you fail to do one of these, the IO will be submitted to
* the disk after q->unplug_delay, which defaults to 3ms in blk-settings.c.
*/
int dm_io(struct dm_io_request *io_req, unsigned num_regions,
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index d7500e1..3e39193 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -249,55 +249,66 @@ static void __hash_remove(struct hash_cell *hc)
static void dm_hash_remove_all(int keep_open_devices)
{
- int i, dev_skipped, dev_removed;
+ int i, dev_skipped;
struct hash_cell *hc;
- struct list_head *tmp, *n;
+ struct mapped_device *md;
+
+retry:
+ dev_skipped = 0;
down_write(&_hash_lock);
-retry:
- dev_skipped = dev_removed = 0;
for (i = 0; i < NUM_BUCKETS; i++) {
- list_for_each_safe (tmp, n, _name_buckets + i) {
- hc = list_entry(tmp, struct hash_cell, name_list);
+ list_for_each_entry(hc, _name_buckets + i, name_list) {
+ md = hc->md;
+ dm_get(md);
- if (keep_open_devices &&
- dm_lock_for_deletion(hc->md)) {
+ if (keep_open_devices && dm_lock_for_deletion(md)) {
+ dm_put(md);
dev_skipped++;
continue;
}
+
__hash_remove(hc);
- dev_removed = 1;
- }
- }
- /*
- * Some mapped devices may be using other mapped devices, so if any
- * still exist, repeat until we make no further progress.
- */
- if (dev_skipped) {
- if (dev_removed)
- goto retry;
+ up_write(&_hash_lock);
- DMWARN("remove_all left %d open device(s)", dev_skipped);
+ dm_put(md);
+ if (likely(keep_open_devices))
+ dm_destroy(md);
+ else
+ dm_destroy_immediate(md);
+
+ /*
+ * Some mapped devices may be using other mapped
+ * devices, so repeat until we make no further
+ * progress. If a new mapped device is created
+ * here it will also get removed.
+ */
+ goto retry;
+ }
}
up_write(&_hash_lock);
+
+ if (dev_skipped)
+ DMWARN("remove_all left %d open device(s)", dev_skipped);
}
-static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
- const char *new)
+static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
+ const char *new)
{
char *new_name, *old_name;
struct hash_cell *hc;
struct dm_table *table;
+ struct mapped_device *md;
/*
* duplicate new.
*/
new_name = kstrdup(new, GFP_KERNEL);
if (!new_name)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
down_write(&_hash_lock);
@@ -306,24 +317,24 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
*/
hc = __get_name_cell(new);
if (hc) {
- DMWARN("asked to rename to an already existing name %s -> %s",
- old, new);
+ DMWARN("asked to rename to an already-existing name %s -> %s",
+ param->name, new);
dm_put(hc->md);
up_write(&_hash_lock);
kfree(new_name);
- return -EBUSY;
+ return ERR_PTR(-EBUSY);
}
/*
* Is there such a device as 'old' ?
*/
- hc = __get_name_cell(old);
+ hc = __get_name_cell(param->name);
if (!hc) {
- DMWARN("asked to rename a non existent device %s -> %s",
- old, new);
+ DMWARN("asked to rename a non-existent device %s -> %s",
+ param->name, new);
up_write(&_hash_lock);
kfree(new_name);
- return -ENXIO;
+ return ERR_PTR(-ENXIO);
}
/*
@@ -345,13 +356,14 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
dm_table_put(table);
}
- if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie))
- *flags |= DM_UEVENT_GENERATED_FLAG;
+ if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr))
+ param->flags |= DM_UEVENT_GENERATED_FLAG;
- dm_put(hc->md);
+ md = hc->md;
up_write(&_hash_lock);
kfree(old_name);
- return 0;
+
+ return md;
}
/*-----------------------------------------------------------------
@@ -573,7 +585,7 @@ static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
* Fills in a dm_ioctl structure, ready for sending back to
* userland.
*/
-static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
+static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
{
struct gendisk *disk = dm_disk(md);
struct dm_table *table;
@@ -617,8 +629,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
dm_table_put(table);
}
}
-
- return 0;
}
static int dev_create(struct dm_ioctl *param, size_t param_size)
@@ -640,15 +650,17 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
if (r) {
dm_put(md);
+ dm_destroy(md);
return r;
}
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
- r = __dev_status(md, param);
+ __dev_status(md, param);
+
dm_put(md);
- return r;
+ return 0;
}
/*
@@ -742,6 +754,7 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
param->flags |= DM_UEVENT_GENERATED_FLAG;
dm_put(md);
+ dm_destroy(md);
return 0;
}
@@ -762,6 +775,7 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size)
{
int r;
char *new_name = (char *) param + param->data_start;
+ struct mapped_device *md;
if (new_name < param->data ||
invalid_str(new_name, (void *) param + param_size) ||
@@ -774,10 +788,14 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size)
if (r)
return r;
- param->data_size = 0;
+ md = dm_hash_rename(param, new_name);
+ if (IS_ERR(md))
+ return PTR_ERR(md);
+
+ __dev_status(md, param);
+ dm_put(md);
- return dm_hash_rename(param->event_nr, &param->flags, param->name,
- new_name);
+ return 0;
}
static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
@@ -818,8 +836,6 @@ static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
geometry.start = indata[3];
r = dm_set_geometry(md, &geometry);
- if (!r)
- r = __dev_status(md, param);
param->data_size = 0;
@@ -843,13 +859,17 @@ static int do_suspend(struct dm_ioctl *param)
if (param->flags & DM_NOFLUSH_FLAG)
suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
- if (!dm_suspended_md(md))
+ if (!dm_suspended_md(md)) {
r = dm_suspend(md, suspend_flags);
+ if (r)
+ goto out;
+ }
- if (!r)
- r = __dev_status(md, param);
+ __dev_status(md, param);
+out:
dm_put(md);
+
return r;
}
@@ -911,7 +931,7 @@ static int do_resume(struct dm_ioctl *param)
dm_table_destroy(old_map);
if (!r)
- r = __dev_status(md, param);
+ __dev_status(md, param);
dm_put(md);
return r;
@@ -935,16 +955,16 @@ static int dev_suspend(struct dm_ioctl *param, size_t param_size)
*/
static int dev_status(struct dm_ioctl *param, size_t param_size)
{
- int r;
struct mapped_device *md;
md = find_device(param);
if (!md)
return -ENXIO;
- r = __dev_status(md, param);
+ __dev_status(md, param);
dm_put(md);
- return r;
+
+ return 0;
}
/*
@@ -1019,7 +1039,7 @@ static void retrieve_status(struct dm_table *table,
*/
static int dev_wait(struct dm_ioctl *param, size_t param_size)
{
- int r;
+ int r = 0;
struct mapped_device *md;
struct dm_table *table;
@@ -1040,9 +1060,7 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
* changed to trigger the event, so we may as well tell
* him and save an ioctl.
*/
- r = __dev_status(md, param);
- if (r)
- goto out;
+ __dev_status(md, param);
table = dm_get_live_or_inactive_table(md, param);
if (table) {
@@ -1050,8 +1068,9 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
dm_table_put(table);
}
- out:
+out:
dm_put(md);
+
return r;
}
@@ -1112,28 +1131,9 @@ static int populate_table(struct dm_table *table,
next = spec->next;
}
- r = dm_table_set_type(table);
- if (r) {
- DMWARN("unable to set table type");
- return r;
- }
-
return dm_table_complete(table);
}
-static int table_prealloc_integrity(struct dm_table *t,
- struct mapped_device *md)
-{
- struct list_head *devices = dm_table_get_devices(t);
- struct dm_dev_internal *dd;
-
- list_for_each_entry(dd, devices, list)
- if (bdev_get_integrity(dd->dm_dev.bdev))
- return blk_integrity_register(dm_disk(md), NULL);
-
- return 0;
-}
-
static int table_load(struct dm_ioctl *param, size_t param_size)
{
int r;
@@ -1155,21 +1155,30 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
goto out;
}
- r = table_prealloc_integrity(t, md);
- if (r) {
- DMERR("%s: could not register integrity profile.",
- dm_device_name(md));
+ /* Protect md->type and md->queue against concurrent table loads. */
+ dm_lock_md_type(md);
+ if (dm_get_md_type(md) == DM_TYPE_NONE)
+ /* Initial table load: acquire type of table. */
+ dm_set_md_type(md, dm_table_get_type(t));
+ else if (dm_get_md_type(md) != dm_table_get_type(t)) {
+ DMWARN("can't change device type after initial table load.");
dm_table_destroy(t);
+ dm_unlock_md_type(md);
+ r = -EINVAL;
goto out;
}
- r = dm_table_alloc_md_mempools(t);
+ /* setup md->queue to reflect md's type (may block) */
+ r = dm_setup_md_queue(md);
if (r) {
- DMWARN("unable to allocate mempools for this table");
+ DMWARN("unable to set up device queue for new table.");
dm_table_destroy(t);
+ dm_unlock_md_type(md);
goto out;
}
+ dm_unlock_md_type(md);
+ /* stage inactive table */
down_write(&_hash_lock);
hc = dm_get_mdptr(md);
if (!hc || hc->md != md) {
@@ -1186,7 +1195,7 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
up_write(&_hash_lock);
param->flags |= DM_INACTIVE_PRESENT_FLAG;
- r = __dev_status(md, param);
+ __dev_status(md, param);
out:
dm_put(md);
@@ -1196,7 +1205,6 @@ out:
static int table_clear(struct dm_ioctl *param, size_t param_size)
{
- int r;
struct hash_cell *hc;
struct mapped_device *md;
@@ -1216,11 +1224,12 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
- r = __dev_status(hc->md, param);
+ __dev_status(hc->md, param);
md = hc->md;
up_write(&_hash_lock);
dm_put(md);
- return r;
+
+ return 0;
}
/*
@@ -1265,7 +1274,6 @@ static void retrieve_deps(struct dm_table *table,
static int table_deps(struct dm_ioctl *param, size_t param_size)
{
- int r = 0;
struct mapped_device *md;
struct dm_table *table;
@@ -1273,9 +1281,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;
- r = __dev_status(md, param);
- if (r)
- goto out;
+ __dev_status(md, param);
table = dm_get_live_or_inactive_table(md, param);
if (table) {
@@ -1283,9 +1289,9 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
dm_table_put(table);
}
- out:
dm_put(md);
- return r;
+
+ return 0;
}
/*
@@ -1294,7 +1300,6 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
*/
static int table_status(struct dm_ioctl *param, size_t param_size)
{
- int r;
struct mapped_device *md;
struct dm_table *table;
@@ -1302,9 +1307,7 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;
- r = __dev_status(md, param);
- if (r)
- goto out;
+ __dev_status(md, param);
table = dm_get_live_or_inactive_table(md, param);
if (table) {
@@ -1312,9 +1315,9 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
dm_table_put(table);
}
-out:
dm_put(md);
- return r;
+
+ return 0;
}
/*
@@ -1333,10 +1336,6 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
if (!md)
return -ENXIO;
- r = __dev_status(md, param);
- if (r)
- goto out;
-
if (tmsg < (struct dm_target_msg *) param->data ||
invalid_str(tmsg->message, (void *) param + param_size)) {
DMWARN("Invalid target message parameters.");
@@ -1593,18 +1592,22 @@ static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
#endif
static const struct file_operations _ctl_fops = {
+ .open = nonseekable_open,
.unlocked_ioctl = dm_ctl_ioctl,
.compat_ioctl = dm_compat_ctl_ioctl,
.owner = THIS_MODULE,
};
static struct miscdevice _dm_misc = {
- .minor = MISC_DYNAMIC_MINOR,
+ .minor = MAPPER_CTRL_MINOR,
.name = DM_NAME,
- .nodename = "mapper/control",
+ .nodename = DM_DIR "/" DM_CONTROL_NODE,
.fops = &_ctl_fops
};
+MODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR);
+MODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE);
+
/*
* Create misc character device and link to DM_DIR/control.
*/
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index addf834..d8587ba 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -345,7 +345,7 @@ static int run_io_job(struct kcopyd_job *job)
{
int r;
struct dm_io_request io_req = {
- .bi_rw = job->rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG),
+ .bi_rw = job->rw | REQ_SYNC | REQ_UNPLUG,
.mem.type = DM_IO_PAGE_LIST,
.mem.ptr.pl = job->pages,
.mem.offset = job->offset,
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 9200dbf..3921e3b 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -53,6 +53,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ti->num_flush_requests = 1;
+ ti->num_discard_requests = 1;
ti->private = lc;
return 0;
@@ -73,7 +74,7 @@ static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector)
{
struct linear_c *lc = ti->private;
- return lc->start + (bi_sector - ti->begin);
+ return lc->start + dm_target_offset(ti, bi_sector);
}
static void linear_map_bio(struct dm_target *ti, struct bio *bio)
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 826bce7..487ecda 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -706,6 +706,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
if (as->argc < nr_params) {
ti->error = "not enough path parameters";
+ r = -EINVAL;
goto bad;
}
@@ -892,6 +893,7 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
}
ti->num_flush_requests = 1;
+ ti->num_discard_requests = 1;
return 0;
@@ -1271,6 +1273,15 @@ static int do_end_io(struct multipath *m, struct request *clone,
if (error == -EOPNOTSUPP)
return error;
+ if (clone->cmd_flags & REQ_DISCARD)
+ /*
+ * Pass all discard request failures up.
+ * FIXME: only fail_path if the discard failed due to a
+ * transport problem. This requires precise understanding
+ * of the underlying failure (e.g. the SCSI sense).
+ */
+ return error;
+
if (mpio->pgpath)
fail_path(mpio->pgpath);
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index ddda531..7c081bc 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -445,7 +445,7 @@ static sector_t map_sector(struct mirror *m, struct bio *bio)
{
if (unlikely(!bio->bi_size))
return 0;
- return m->offset + (bio->bi_sector - m->ms->ti->begin);
+ return m->offset + dm_target_offset(m->ms->ti, bio->bi_sector);
}
static void map_bio(struct mirror *m, struct bio *bio)
@@ -1211,7 +1211,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio,
if (error == -EOPNOTSUPP)
goto out;
- if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD))
+ if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD))
goto out;
if (unlikely(error)) {
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index c097d8a..cc2bdb8 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -266,7 +266,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
*/
static chunk_t area_location(struct pstore *ps, chunk_t area)
{
- return 1 + ((ps->exceptions_per_area + 1) * area);
+ return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);
}
/*
@@ -780,8 +780,8 @@ static int persistent_commit_merge(struct dm_exception_store *store,
* ps->current_area does not get reduced by prepare_merge() until
* after commit_merge() has removed the nr_merged previous exceptions.
*/
- ps->next_free = (area_location(ps, ps->current_area) - 1) +
- (ps->current_committed + 1) + NUM_SNAPSHOT_HDR_CHUNKS;
+ ps->next_free = area_location(ps, ps->current_area) +
+ ps->current_committed + 1;
return 0;
}
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 5485377..5974d30 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -148,6 +148,12 @@ struct dm_snapshot {
#define RUNNING_MERGE 0
#define SHUTDOWN_MERGE 1
+struct dm_dev *dm_snap_origin(struct dm_snapshot *s)
+{
+ return s->origin;
+}
+EXPORT_SYMBOL(dm_snap_origin);
+
struct dm_dev *dm_snap_cow(struct dm_snapshot *s)
{
return s->cow;
@@ -1065,10 +1071,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
origin_mode = FMODE_WRITE;
}
- origin_path = argv[0];
- argv++;
- argc--;
-
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
ti->error = "Cannot allocate snapshot context private "
@@ -1077,6 +1079,16 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad;
}
+ origin_path = argv[0];
+ argv++;
+ argc--;
+
+ r = dm_get_device(ti, origin_path, origin_mode, &s->origin);
+ if (r) {
+ ti->error = "Cannot get origin device";
+ goto bad_origin;
+ }
+
cow_path = argv[0];
argv++;
argc--;
@@ -1097,12 +1109,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
argv += args_used;
argc -= args_used;
- r = dm_get_device(ti, origin_path, origin_mode, &s->origin);
- if (r) {
- ti->error = "Cannot get origin device";
- goto bad_origin;
- }
-
s->ti = ti;
s->valid = 1;
s->active = 0;
@@ -1212,15 +1218,15 @@ bad_kcopyd:
dm_exception_table_exit(&s->complete, exception_cache);
bad_hash_tables:
- dm_put_device(ti, s->origin);
-
-bad_origin:
dm_exception_store_destroy(s->store);
bad_store:
dm_put_device(ti, s->cow);
bad_cow:
+ dm_put_device(ti, s->origin);
+
+bad_origin:
kfree(s);
bad:
@@ -1314,12 +1320,12 @@ static void snapshot_dtr(struct dm_target *ti)
mempool_destroy(s->pending_pool);
- dm_put_device(ti, s->origin);
-
dm_exception_store_destroy(s->store);
dm_put_device(ti, s->cow);
+ dm_put_device(ti, s->origin);
+
kfree(s);
}
@@ -1686,7 +1692,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
chunk_t chunk;
if (unlikely(bio_empty_barrier(bio))) {
- if (!map_context->flush_request)
+ if (!map_context->target_request_nr)
bio->bi_bdev = s->origin->bdev;
else
bio->bi_bdev = s->cow->bdev;
@@ -1899,8 +1905,14 @@ static int snapshot_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct dm_snapshot *snap = ti->private;
+ int r;
+
+ r = fn(ti, snap->origin, 0, ti->len, data);
+
+ if (!r)
+ r = fn(ti, snap->cow, 0, get_dev_size(snap->cow->bdev), data);
- return fn(ti, snap->origin, 0, ti->len, data);
+ return r;
}
@@ -2159,6 +2171,21 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result,
return 0;
}
+static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct dm_dev *dev = ti->private;
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = dev->bdev;
+ bvm->bi_sector = bvm->bi_sector;
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
static int origin_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
@@ -2176,6 +2203,7 @@ static struct target_type origin_target = {
.map = origin_map,
.resume = origin_resume,
.status = origin_status,
+ .merge = origin_merge,
.iterate_devices = origin_iterate_devices,
};
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index e610725..c297f6d 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -25,6 +25,8 @@ struct stripe {
struct stripe_c {
uint32_t stripes;
+ int stripes_shift;
+ sector_t stripes_mask;
/* The size of this target / num. stripes */
sector_t stripe_width;
@@ -162,16 +164,22 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
/* Set pointer to dm target; used in trigger_event */
sc->ti = ti;
-
sc->stripes = stripes;
sc->stripe_width = width;
+
+ if (stripes & (stripes - 1))
+ sc->stripes_shift = -1;
+ else {
+ sc->stripes_shift = ffs(stripes) - 1;
+ sc->stripes_mask = ((sector_t) stripes) - 1;
+ }
+
ti->split_io = chunk_size;
ti->num_flush_requests = stripes;
+ ti->num_discard_requests = stripes;
+ sc->chunk_shift = ffs(chunk_size) - 1;
sc->chunk_mask = ((sector_t) chunk_size) - 1;
- for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
- chunk_size >>= 1;
- sc->chunk_shift--;
/*
* Get the stripe destinations.
@@ -207,26 +215,79 @@ static void stripe_dtr(struct dm_target *ti)
kfree(sc);
}
+static void stripe_map_sector(struct stripe_c *sc, sector_t sector,
+ uint32_t *stripe, sector_t *result)
+{
+ sector_t offset = dm_target_offset(sc->ti, sector);
+ sector_t chunk = offset >> sc->chunk_shift;
+
+ if (sc->stripes_shift < 0)
+ *stripe = sector_div(chunk, sc->stripes);
+ else {
+ *stripe = chunk & sc->stripes_mask;
+ chunk >>= sc->stripes_shift;
+ }
+
+ *result = (chunk << sc->chunk_shift) | (offset & sc->chunk_mask);
+}
+
+static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector,
+ uint32_t target_stripe, sector_t *result)
+{
+ uint32_t stripe;
+
+ stripe_map_sector(sc, sector, &stripe, result);
+ if (stripe == target_stripe)
+ return;
+ *result &= ~sc->chunk_mask; /* round down */
+ if (target_stripe < stripe)
+ *result += sc->chunk_mask + 1; /* next chunk */
+}
+
+static int stripe_map_discard(struct stripe_c *sc, struct bio *bio,
+ uint32_t target_stripe)
+{
+ sector_t begin, end;
+
+ stripe_map_range_sector(sc, bio->bi_sector, target_stripe, &begin);
+ stripe_map_range_sector(sc, bio->bi_sector + bio_sectors(bio),
+ target_stripe, &end);
+ if (begin < end) {
+ bio->bi_bdev = sc->stripe[target_stripe].dev->bdev;
+ bio->bi_sector = begin + sc->stripe[target_stripe].physical_start;
+ bio->bi_size = to_bytes(end - begin);
+ return DM_MAPIO_REMAPPED;
+ } else {
+ /* The range doesn't map to the target stripe */
+ bio_endio(bio, 0);
+ return DM_MAPIO_SUBMITTED;
+ }
+}
+
static int stripe_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
- struct stripe_c *sc = (struct stripe_c *) ti->private;
- sector_t offset, chunk;
+ struct stripe_c *sc = ti->private;
uint32_t stripe;
+ unsigned target_request_nr;
if (unlikely(bio_empty_barrier(bio))) {
- BUG_ON(map_context->flush_request >= sc->stripes);
- bio->bi_bdev = sc->stripe[map_context->flush_request].dev->bdev;
+ target_request_nr = map_context->target_request_nr;
+ BUG_ON(target_request_nr >= sc->stripes);
+ bio->bi_bdev = sc->stripe[target_request_nr].dev->bdev;
return DM_MAPIO_REMAPPED;
}
+ if (unlikely(bio->bi_rw & REQ_DISCARD)) {
+ target_request_nr = map_context->target_request_nr;
+ BUG_ON(target_request_nr >= sc->stripes);
+ return stripe_map_discard(sc, bio, target_request_nr);
+ }
- offset = bio->bi_sector - ti->begin;
- chunk = offset >> sc->chunk_shift;
- stripe = sector_div(chunk, sc->stripes);
+ stripe_map_sector(sc, bio->bi_sector, &stripe, &bio->bi_sector);
+ bio->bi_sector += sc->stripe[stripe].physical_start;
bio->bi_bdev = sc->stripe[stripe].dev->bdev;
- bio->bi_sector = sc->stripe[stripe].physical_start +
- (chunk << sc->chunk_shift) + (offset & sc->chunk_mask);
+
return DM_MAPIO_REMAPPED;
}
@@ -284,7 +345,7 @@ static int stripe_end_io(struct dm_target *ti, struct bio *bio,
if (!error)
return 0; /* I/O complete */
- if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD))
+ if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD))
return error;
if (error == -EOPNOTSUPP)
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 9924ea2..f9fc07d 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -54,6 +54,8 @@ struct dm_table {
sector_t *highs;
struct dm_target *targets;
+ unsigned discards_supported:1;
+
/*
* Indicates the rw permissions for the new logical
* device. This should be a combination of FMODE_READ
@@ -203,6 +205,7 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
INIT_LIST_HEAD(&t->devices);
atomic_set(&t->holders, 0);
+ t->discards_supported = 1;
if (!num_targets)
num_targets = KEYS_PER_NODE;
@@ -245,7 +248,7 @@ void dm_table_destroy(struct dm_table *t)
msleep(1);
smp_mb();
- /* free the indexes (see dm_table_complete) */
+ /* free the indexes */
if (t->depth >= 2)
vfree(t->index[t->depth - 2]);
@@ -770,6 +773,9 @@ int dm_table_add_target(struct dm_table *t, const char *type,
t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
+ if (!tgt->num_discard_requests)
+ t->discards_supported = 0;
+
return 0;
bad:
@@ -778,7 +784,7 @@ int dm_table_add_target(struct dm_table *t, const char *type,
return r;
}
-int dm_table_set_type(struct dm_table *t)
+static int dm_table_set_type(struct dm_table *t)
{
unsigned i;
unsigned bio_based = 0, request_based = 0;
@@ -900,7 +906,7 @@ static int setup_indexes(struct dm_table *t)
/*
* Builds the btree to index the map.
*/
-int dm_table_complete(struct dm_table *t)
+static int dm_table_build_index(struct dm_table *t)
{
int r = 0;
unsigned int leaf_nodes;
@@ -919,6 +925,55 @@ int dm_table_complete(struct dm_table *t)
return r;
}
+/*
+ * Register the mapped device for blk_integrity support if
+ * the underlying devices support it.
+ */
+static int dm_table_prealloc_integrity(struct dm_table *t, struct mapped_device *md)
+{
+ struct list_head *devices = dm_table_get_devices(t);
+ struct dm_dev_internal *dd;
+
+ list_for_each_entry(dd, devices, list)
+ if (bdev_get_integrity(dd->dm_dev.bdev))
+ return blk_integrity_register(dm_disk(md), NULL);
+
+ return 0;
+}
+
+/*
+ * Prepares the table for use by building the indices,
+ * setting the type, and allocating mempools.
+ */
+int dm_table_complete(struct dm_table *t)
+{
+ int r;
+
+ r = dm_table_set_type(t);
+ if (r) {
+ DMERR("unable to set table type");
+ return r;
+ }
+
+ r = dm_table_build_index(t);
+ if (r) {
+ DMERR("unable to build btrees");
+ return r;
+ }
+
+ r = dm_table_prealloc_integrity(t, t->md);
+ if (r) {
+ DMERR("could not register integrity profile.");
+ return r;
+ }
+
+ r = dm_table_alloc_md_mempools(t);
+ if (r)
+ DMERR("unable to allocate mempools");
+
+ return r;
+}
+
static DEFINE_MUTEX(_event_lock);
void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context)
@@ -1086,6 +1141,11 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
else
queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q);
+ if (!dm_table_supports_discards(t))
+ queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ else
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+
dm_table_set_integrity(t);
/*
@@ -1232,6 +1292,39 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
return t->md;
}
+static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && blk_queue_discard(q);
+}
+
+bool dm_table_supports_discards(struct dm_table *t)
+{
+ struct dm_target *ti;
+ unsigned i = 0;
+
+ if (!t->discards_supported)
+ return 0;
+
+ /*
+ * Ensure that at least one underlying device supports discards.
+ * t->devices includes internal dm devices such as mirror logs
+ * so we need to use iterate_devices here, which targets
+ * supporting discard must provide.
+ */
+ while (i < dm_table_get_num_targets(t)) {
+ ti = dm_table_get_target(t, i++);
+
+ if (ti->type->iterate_devices &&
+ ti->type->iterate_devices(ti, device_discard_capable, NULL))
+ return 1;
+ }
+
+ return 0;
+}
+
EXPORT_SYMBOL(dm_vcalloc);
EXPORT_SYMBOL(dm_get_device);
EXPORT_SYMBOL(dm_put_device);
diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c
index 11dea11..8da366c 100644
--- a/drivers/md/dm-target.c
+++ b/drivers/md/dm-target.c
@@ -113,6 +113,11 @@ void dm_unregister_target(struct target_type *tt)
*/
static int io_err_ctr(struct dm_target *tt, unsigned int argc, char **args)
{
+ /*
+ * Return error for discards instead of -EOPNOTSUPP
+ */
+ tt->num_discard_requests = 1;
+
return 0;
}
diff --git a/drivers/md/dm-zero.c b/drivers/md/dm-zero.c
index bbc9703..cc2b3cb 100644
--- a/drivers/md/dm-zero.c
+++ b/drivers/md/dm-zero.c
@@ -22,6 +22,11 @@ static int zero_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return -EINVAL;
}
+ /*
+ * Silently drop discards, avoiding -EOPNOTSUPP.
+ */
+ ti->num_discard_requests = 1;
+
return 0;
}
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index d21e128..ac384b2 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -15,10 +15,12 @@
#include <linux/blkpg.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
+#include <linux/smp_lock.h>
#include <linux/mempool.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/hdreg.h>
+#include <linux/delay.h>
#include <trace/events/block.h>
@@ -123,6 +125,10 @@ struct mapped_device {
unsigned long flags;
struct request_queue *queue;
+ unsigned type;
+ /* Protect queue and type against concurrent access. */
+ struct mutex type_lock;
+
struct gendisk *disk;
char name[16];
@@ -338,6 +344,7 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mapped_device *md;
+ lock_kernel();
spin_lock(&_minor_lock);
md = bdev->bd_disk->private_data;
@@ -355,6 +362,7 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode)
out:
spin_unlock(&_minor_lock);
+ unlock_kernel();
return md ? 0 : -ENXIO;
}
@@ -362,8 +370,12 @@ out:
static int dm_blk_close(struct gendisk *disk, fmode_t mode)
{
struct mapped_device *md = disk->private_data;
+
+ lock_kernel();
atomic_dec(&md->open_count);
dm_put(md);
+ unlock_kernel();
+
return 0;
}
@@ -614,7 +626,7 @@ static void dec_pending(struct dm_io *io, int error)
*/
spin_lock_irqsave(&md->deferred_lock, flags);
if (__noflush_suspending(md)) {
- if (!bio_rw_flagged(io->bio, BIO_RW_BARRIER))
+ if (!(io->bio->bi_rw & REQ_HARDBARRIER))
bio_list_add_head(&md->deferred,
io->bio);
} else
@@ -626,13 +638,19 @@ static void dec_pending(struct dm_io *io, int error)
io_error = io->error;
bio = io->bio;
- if (bio_rw_flagged(bio, BIO_RW_BARRIER)) {
+ if (bio->bi_rw & REQ_HARDBARRIER) {
/*
* There can be just one barrier request so we use
* a per-device variable for error reporting.
* Note that you can't touch the bio after end_io_acct
+ *
+ * We ignore -EOPNOTSUPP for empty flush reported by
+ * underlying devices. We assume that if the device
+ * doesn't support empty barriers, it doesn't need
+ * cache flushing commands.
*/
- if (!md->barrier_error && io_error != -EOPNOTSUPP)
+ if (!md->barrier_error &&
+ !(bio_empty_barrier(bio) && io_error == -EOPNOTSUPP))
md->barrier_error = io_error;
end_io_acct(io);
free_io(md, io);
@@ -792,12 +810,12 @@ static void dm_end_request(struct request *clone, int error)
{
int rw = rq_data_dir(clone);
int run_queue = 1;
- bool is_barrier = blk_barrier_rq(clone);
+ bool is_barrier = clone->cmd_flags & REQ_HARDBARRIER;
struct dm_rq_target_io *tio = clone->end_io_data;
struct mapped_device *md = tio->md;
struct request *rq = tio->orig;
- if (blk_pc_request(rq) && !is_barrier) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC && !is_barrier) {
rq->errors = clone->errors;
rq->resid_len = clone->resid_len;
@@ -844,7 +862,7 @@ void dm_requeue_unmapped_request(struct request *clone)
struct request_queue *q = rq->q;
unsigned long flags;
- if (unlikely(blk_barrier_rq(clone))) {
+ if (unlikely(clone->cmd_flags & REQ_HARDBARRIER)) {
/*
* Barrier clones share an original request.
* Leave it to dm_end_request(), which handles this special
@@ -943,7 +961,7 @@ static void dm_complete_request(struct request *clone, int error)
struct dm_rq_target_io *tio = clone->end_io_data;
struct request *rq = tio->orig;
- if (unlikely(blk_barrier_rq(clone))) {
+ if (unlikely(clone->cmd_flags & REQ_HARDBARRIER)) {
/*
* Barrier clones share an original request. So can't use
* softirq_done with the original.
@@ -972,7 +990,7 @@ void dm_kill_unmapped_request(struct request *clone, int error)
struct dm_rq_target_io *tio = clone->end_io_data;
struct request *rq = tio->orig;
- if (unlikely(blk_barrier_rq(clone))) {
+ if (unlikely(clone->cmd_flags & REQ_HARDBARRIER)) {
/*
* Barrier clones share an original request.
* Leave it to dm_end_request(), which handles this special
@@ -1012,17 +1030,27 @@ static void end_clone_request(struct request *clone, int error)
dm_complete_request(clone, error);
}
-static sector_t max_io_len(struct mapped_device *md,
- sector_t sector, struct dm_target *ti)
+/*
+ * Return maximum size of I/O possible at the supplied sector up to the current
+ * target boundary.
+ */
+static sector_t max_io_len_target_boundary(sector_t sector, struct dm_target *ti)
{
- sector_t offset = sector - ti->begin;
- sector_t len = ti->len - offset;
+ sector_t target_offset = dm_target_offset(ti, sector);
+
+ return ti->len - target_offset;
+}
+
+static sector_t max_io_len(sector_t sector, struct dm_target *ti)
+{
+ sector_t len = max_io_len_target_boundary(sector, ti);
/*
* Does the target need to split even further ?
*/
if (ti->split_io) {
sector_t boundary;
+ sector_t offset = dm_target_offset(ti, sector);
boundary = ((offset + ti->split_io) & ~(ti->split_io - 1))
- offset;
if (len > boundary)
@@ -1106,7 +1134,7 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector,
clone->bi_sector = sector;
clone->bi_bdev = bio->bi_bdev;
- clone->bi_rw = bio->bi_rw & ~(1 << BIO_RW_BARRIER);
+ clone->bi_rw = bio->bi_rw & ~REQ_HARDBARRIER;
clone->bi_vcnt = 1;
clone->bi_size = to_bytes(len);
clone->bi_io_vec->bv_offset = offset;
@@ -1133,7 +1161,7 @@ static struct bio *clone_bio(struct bio *bio, sector_t sector,
clone = bio_alloc_bioset(GFP_NOIO, bio->bi_max_vecs, bs);
__bio_clone(clone, bio);
- clone->bi_rw &= ~(1 << BIO_RW_BARRIER);
+ clone->bi_rw &= ~REQ_HARDBARRIER;
clone->bi_destructor = dm_bio_destructor;
clone->bi_sector = sector;
clone->bi_idx = idx;
@@ -1164,36 +1192,96 @@ static struct dm_target_io *alloc_tio(struct clone_info *ci,
return tio;
}
-static void __flush_target(struct clone_info *ci, struct dm_target *ti,
- unsigned flush_nr)
+static void __issue_target_request(struct clone_info *ci, struct dm_target *ti,
+ unsigned request_nr, sector_t len)
{
struct dm_target_io *tio = alloc_tio(ci, ti);
struct bio *clone;
- tio->info.flush_request = flush_nr;
+ tio->info.target_request_nr = request_nr;
- clone = bio_alloc_bioset(GFP_NOIO, 0, ci->md->bs);
+ /*
+ * Discard requests require the bio's inline iovecs be initialized.
+ * ci->bio->bi_max_vecs is BIO_INLINE_VECS anyway, for both flush
+ * and discard, so no need for concern about wasted bvec allocations.
+ */
+ clone = bio_alloc_bioset(GFP_NOIO, ci->bio->bi_max_vecs, ci->md->bs);
__bio_clone(clone, ci->bio);
clone->bi_destructor = dm_bio_destructor;
+ if (len) {
+ clone->bi_sector = ci->sector;
+ clone->bi_size = to_bytes(len);
+ }
__map_bio(ti, clone, tio);
}
+static void __issue_target_requests(struct clone_info *ci, struct dm_target *ti,
+ unsigned num_requests, sector_t len)
+{
+ unsigned request_nr;
+
+ for (request_nr = 0; request_nr < num_requests; request_nr++)
+ __issue_target_request(ci, ti, request_nr, len);
+}
+
static int __clone_and_map_empty_barrier(struct clone_info *ci)
{
- unsigned target_nr = 0, flush_nr;
+ unsigned target_nr = 0;
struct dm_target *ti;
while ((ti = dm_table_get_target(ci->map, target_nr++)))
- for (flush_nr = 0; flush_nr < ti->num_flush_requests;
- flush_nr++)
- __flush_target(ci, ti, flush_nr);
+ __issue_target_requests(ci, ti, ti->num_flush_requests, 0);
ci->sector_count = 0;
return 0;
}
+/*
+ * Perform all io with a single clone.
+ */
+static void __clone_and_map_simple(struct clone_info *ci, struct dm_target *ti)
+{
+ struct bio *clone, *bio = ci->bio;
+ struct dm_target_io *tio;
+
+ tio = alloc_tio(ci, ti);
+ clone = clone_bio(bio, ci->sector, ci->idx,
+ bio->bi_vcnt - ci->idx, ci->sector_count,
+ ci->md->bs);
+ __map_bio(ti, clone, tio);
+ ci->sector_count = 0;
+}
+
+static int __clone_and_map_discard(struct clone_info *ci)
+{
+ struct dm_target *ti;
+ sector_t len;
+
+ do {
+ ti = dm_table_find_target(ci->map, ci->sector);
+ if (!dm_target_is_valid(ti))
+ return -EIO;
+
+ /*
+ * Even though the device advertised discard support,
+ * reconfiguration might have changed that since the
+ * check was performed.
+ */
+ if (!ti->num_discard_requests)
+ return -EOPNOTSUPP;
+
+ len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
+
+ __issue_target_requests(ci, ti, ti->num_discard_requests, len);
+
+ ci->sector += len;
+ } while (ci->sector_count -= len);
+
+ return 0;
+}
+
static int __clone_and_map(struct clone_info *ci)
{
struct bio *clone, *bio = ci->bio;
@@ -1204,27 +1292,21 @@ static int __clone_and_map(struct clone_info *ci)
if (unlikely(bio_empty_barrier(bio)))
return __clone_and_map_empty_barrier(ci);
+ if (unlikely(bio->bi_rw & REQ_DISCARD))
+ return __clone_and_map_discard(ci);
+
ti = dm_table_find_target(ci->map, ci->sector);
if (!dm_target_is_valid(ti))
return -EIO;
- max = max_io_len(ci->md, ci->sector, ti);
-
- /*
- * Allocate a target io object.
- */
- tio = alloc_tio(ci, ti);
+ max = max_io_len(ci->sector, ti);
if (ci->sector_count <= max) {
/*
* Optimise for the simple case where we can do all of
* the remaining io with a single clone.
*/
- clone = clone_bio(bio, ci->sector, ci->idx,
- bio->bi_vcnt - ci->idx, ci->sector_count,
- ci->md->bs);
- __map_bio(ti, clone, tio);
- ci->sector_count = 0;
+ __clone_and_map_simple(ci, ti);
} else if (to_sector(bio->bi_io_vec[ci->idx].bv_len) <= max) {
/*
@@ -1245,6 +1327,7 @@ static int __clone_and_map(struct clone_info *ci)
len += bv_len;
}
+ tio = alloc_tio(ci, ti);
clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len,
ci->md->bs);
__map_bio(ti, clone, tio);
@@ -1267,13 +1350,12 @@ static int __clone_and_map(struct clone_info *ci)
if (!dm_target_is_valid(ti))
return -EIO;
- max = max_io_len(ci->md, ci->sector, ti);
-
- tio = alloc_tio(ci, ti);
+ max = max_io_len(ci->sector, ti);
}
len = min(remaining, max);
+ tio = alloc_tio(ci, ti);
clone = split_bvec(bio, ci->sector, ci->idx,
bv->bv_offset + offset, len,
ci->md->bs);
@@ -1301,7 +1383,7 @@ static void __split_and_process_bio(struct mapped_device *md, struct bio *bio)
ci.map = dm_get_live_table(md);
if (unlikely(!ci.map)) {
- if (!bio_rw_flagged(bio, BIO_RW_BARRIER))
+ if (!(bio->bi_rw & REQ_HARDBARRIER))
bio_io_error(bio);
else
if (!md->barrier_error)
@@ -1355,7 +1437,7 @@ static int dm_merge_bvec(struct request_queue *q,
/*
* Find maximum amount of I/O that won't need splitting
*/
- max_sectors = min(max_io_len(md, bvm->bi_sector, ti),
+ max_sectors = min(max_io_len(bvm->bi_sector, ti),
(sector_t) BIO_MAX_SECTORS);
max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size;
if (max_size < 0)
@@ -1414,7 +1496,7 @@ static int _dm_request(struct request_queue *q, struct bio *bio)
* we have to queue this io for later.
*/
if (unlikely(test_bit(DMF_QUEUE_IO_TO_THREAD, &md->flags)) ||
- unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
up_read(&md->io_lock);
if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) &&
@@ -1455,20 +1537,9 @@ static int dm_request(struct request_queue *q, struct bio *bio)
return _dm_request(q, bio);
}
-/*
- * Mark this request as flush request, so that dm_request_fn() can
- * recognize.
- */
-static void dm_rq_prepare_flush(struct request_queue *q, struct request *rq)
-{
- rq->cmd_type = REQ_TYPE_LINUX_BLOCK;
- rq->cmd[0] = REQ_LB_OP_FLUSH;
-}
-
static bool dm_rq_is_flush_request(struct request *rq)
{
- if (rq->cmd_type == REQ_TYPE_LINUX_BLOCK &&
- rq->cmd[0] == REQ_LB_OP_FLUSH)
+ if (rq->cmd_flags & REQ_FLUSH)
return true;
else
return false;
@@ -1849,6 +1920,28 @@ static const struct block_device_operations dm_blk_dops;
static void dm_wq_work(struct work_struct *work);
static void dm_rq_barrier_work(struct work_struct *work);
+static void dm_init_md_queue(struct mapped_device *md)
+{
+ /*
+ * Request-based dm devices cannot be stacked on top of bio-based dm
+ * devices. The type of this dm device has not been decided yet.
+ * The type is decided at the first table loading time.
+ * To prevent problematic device stacking, clear the queue flag
+ * for request stacking support until then.
+ *
+ * This queue is new, so no concurrency on the queue_flags.
+ */
+ queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue);
+
+ md->queue->queuedata = md;
+ md->queue->backing_dev_info.congested_fn = dm_any_congested;
+ md->queue->backing_dev_info.congested_data = md;
+ blk_queue_make_request(md->queue, dm_request);
+ blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
+ md->queue->unplug_fn = dm_unplug_all;
+ blk_queue_merge_bvec(md->queue, dm_merge_bvec);
+}
+
/*
* Allocate and initialise a blank device with a given minor.
*/
@@ -1874,8 +1967,10 @@ static struct mapped_device *alloc_dev(int minor)
if (r < 0)
goto bad_minor;
+ md->type = DM_TYPE_NONE;
init_rwsem(&md->io_lock);
mutex_init(&md->suspend_lock);
+ mutex_init(&md->type_lock);
spin_lock_init(&md->deferred_lock);
spin_lock_init(&md->barrier_error_lock);
rwlock_init(&md->map_lock);
@@ -1886,34 +1981,11 @@ static struct mapped_device *alloc_dev(int minor)
INIT_LIST_HEAD(&md->uevent_list);
spin_lock_init(&md->uevent_lock);
- md->queue = blk_init_queue(dm_request_fn, NULL);
+ md->queue = blk_alloc_queue(GFP_KERNEL);
if (!md->queue)
goto bad_queue;
- /*
- * Request-based dm devices cannot be stacked on top of bio-based dm
- * devices. The type of this dm device has not been decided yet,
- * although we initialized the queue using blk_init_queue().
- * The type is decided at the first table loading time.
- * To prevent problematic device stacking, clear the queue flag
- * for request stacking support until then.
- *
- * This queue is new, so no concurrency on the queue_flags.
- */
- queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue);
- md->saved_make_request_fn = md->queue->make_request_fn;
- md->queue->queuedata = md;
- md->queue->backing_dev_info.congested_fn = dm_any_congested;
- md->queue->backing_dev_info.congested_data = md;
- blk_queue_make_request(md->queue, dm_request);
- blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
- md->queue->unplug_fn = dm_unplug_all;
- blk_queue_merge_bvec(md->queue, dm_merge_bvec);
- blk_queue_softirq_done(md->queue, dm_softirq_done);
- blk_queue_prep_rq(md->queue, dm_prep_fn);
- blk_queue_lld_busy(md->queue, dm_lld_busy);
- blk_queue_ordered(md->queue, QUEUE_ORDERED_DRAIN_FLUSH,
- dm_rq_prepare_flush);
+ dm_init_md_queue(md);
md->disk = alloc_disk(1);
if (!md->disk)
@@ -2128,6 +2200,72 @@ int dm_create(int minor, struct mapped_device **result)
return 0;
}
+/*
+ * Functions to manage md->type.
+ * All are required to hold md->type_lock.
+ */
+void dm_lock_md_type(struct mapped_device *md)
+{
+ mutex_lock(&md->type_lock);
+}
+
+void dm_unlock_md_type(struct mapped_device *md)
+{
+ mutex_unlock(&md->type_lock);
+}
+
+void dm_set_md_type(struct mapped_device *md, unsigned type)
+{
+ md->type = type;
+}
+
+unsigned dm_get_md_type(struct mapped_device *md)
+{
+ return md->type;
+}
+
+/*
+ * Fully initialize a request-based queue (->elevator, ->request_fn, etc).
+ */
+static int dm_init_request_based_queue(struct mapped_device *md)
+{
+ struct request_queue *q = NULL;
+
+ if (md->queue->elevator)
+ return 1;
+
+ /* Fully initialize the queue */
+ q = blk_init_allocated_queue(md->queue, dm_request_fn, NULL);
+ if (!q)
+ return 0;
+
+ md->queue = q;
+ md->saved_make_request_fn = md->queue->make_request_fn;
+ dm_init_md_queue(md);
+ blk_queue_softirq_done(md->queue, dm_softirq_done);
+ blk_queue_prep_rq(md->queue, dm_prep_fn);
+ blk_queue_lld_busy(md->queue, dm_lld_busy);
+ blk_queue_ordered(md->queue, QUEUE_ORDERED_DRAIN_FLUSH);
+
+ elv_register_queue(md->queue);
+
+ return 1;
+}
+
+/*
+ * Setup the DM device's queue based on md's type
+ */
+int dm_setup_md_queue(struct mapped_device *md)
+{
+ if ((dm_get_md_type(md) == DM_TYPE_REQUEST_BASED) &&
+ !dm_init_request_based_queue(md)) {
+ DMWARN("Cannot initialize queue for request-based mapped device");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static struct mapped_device *dm_find_md(dev_t dev)
{
struct mapped_device *md;
@@ -2141,6 +2279,7 @@ static struct mapped_device *dm_find_md(dev_t dev)
md = idr_find(&_minor_idr, minor);
if (md && (md == MINOR_ALLOCED ||
(MINOR(disk_devt(dm_disk(md))) != minor) ||
+ dm_deleting_md(md) ||
test_bit(DMF_FREEING, &md->flags))) {
md = NULL;
goto out;
@@ -2175,6 +2314,7 @@ void dm_set_mdptr(struct mapped_device *md, void *ptr)
void dm_get(struct mapped_device *md)
{
atomic_inc(&md->holders);
+ BUG_ON(test_bit(DMF_FREEING, &md->flags));
}
const char *dm_device_name(struct mapped_device *md)
@@ -2183,27 +2323,55 @@ const char *dm_device_name(struct mapped_device *md)
}
EXPORT_SYMBOL_GPL(dm_device_name);
-void dm_put(struct mapped_device *md)
+static void __dm_destroy(struct mapped_device *md, bool wait)
{
struct dm_table *map;
- BUG_ON(test_bit(DMF_FREEING, &md->flags));
+ might_sleep();
- if (atomic_dec_and_lock(&md->holders, &_minor_lock)) {
- map = dm_get_live_table(md);
- idr_replace(&_minor_idr, MINOR_ALLOCED,
- MINOR(disk_devt(dm_disk(md))));
- set_bit(DMF_FREEING, &md->flags);
- spin_unlock(&_minor_lock);
- if (!dm_suspended_md(md)) {
- dm_table_presuspend_targets(map);
- dm_table_postsuspend_targets(map);
- }
- dm_sysfs_exit(md);
- dm_table_put(map);
- dm_table_destroy(__unbind(md));
- free_dev(md);
+ spin_lock(&_minor_lock);
+ map = dm_get_live_table(md);
+ idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md))));
+ set_bit(DMF_FREEING, &md->flags);
+ spin_unlock(&_minor_lock);
+
+ if (!dm_suspended_md(md)) {
+ dm_table_presuspend_targets(map);
+ dm_table_postsuspend_targets(map);
}
+
+ /*
+ * Rare, but there may be I/O requests still going to complete,
+ * for example. Wait for all references to disappear.
+ * No one should increment the reference count of the mapped_device,
+ * after the mapped_device state becomes DMF_FREEING.
+ */
+ if (wait)
+ while (atomic_read(&md->holders))
+ msleep(1);
+ else if (atomic_read(&md->holders))
+ DMWARN("%s: Forcibly removing mapped_device still in use! (%d users)",
+ dm_device_name(md), atomic_read(&md->holders));
+
+ dm_sysfs_exit(md);
+ dm_table_put(map);
+ dm_table_destroy(__unbind(md));
+ free_dev(md);
+}
+
+void dm_destroy(struct mapped_device *md)
+{
+ __dm_destroy(md, true);
+}
+
+void dm_destroy_immediate(struct mapped_device *md)
+{
+ __dm_destroy(md, false);
+}
+
+void dm_put(struct mapped_device *md)
+{
+ atomic_dec(&md->holders);
}
EXPORT_SYMBOL_GPL(dm_put);
@@ -2258,7 +2426,12 @@ static void process_barrier(struct mapped_device *md, struct bio *bio)
if (!bio_empty_barrier(bio)) {
__split_and_process_bio(md, bio);
- dm_flush(md);
+ /*
+ * If the request isn't supported, don't waste time with
+ * the second flush.
+ */
+ if (md->barrier_error != -EOPNOTSUPP)
+ dm_flush(md);
}
if (md->barrier_error != DM_ENDIO_REQUEUE)
@@ -2296,7 +2469,7 @@ static void dm_wq_work(struct work_struct *work)
if (dm_request_based(md))
generic_make_request(c);
else {
- if (bio_rw_flagged(c, BIO_RW_BARRIER))
+ if (c->bi_rw & REQ_HARDBARRIER)
process_barrier(md, c);
else
__split_and_process_bio(md, c);
@@ -2315,11 +2488,11 @@ static void dm_queue_flush(struct mapped_device *md)
queue_work(md->wq, &md->work);
}
-static void dm_rq_set_flush_nr(struct request *clone, unsigned flush_nr)
+static void dm_rq_set_target_request_nr(struct request *clone, unsigned request_nr)
{
struct dm_rq_target_io *tio = clone->end_io_data;
- tio->info.flush_request = flush_nr;
+ tio->info.target_request_nr = request_nr;
}
/* Issue barrier requests to targets and wait for their completion. */
@@ -2337,7 +2510,7 @@ static int dm_rq_barrier(struct mapped_device *md)
ti = dm_table_get_target(map, i);
for (j = 0; j < ti->num_flush_requests; j++) {
clone = clone_rq(md->flush_request, md, GFP_NOIO);
- dm_rq_set_flush_nr(clone, j);
+ dm_rq_set_target_request_nr(clone, j);
atomic_inc(&md->pending[rq_data_dir(clone)]);
map_request(ti, clone, md);
}
@@ -2403,13 +2576,6 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
goto out;
}
- /* cannot change the device type, once a table is bound */
- if (md->map &&
- (dm_table_get_type(md->map) != dm_table_get_type(table))) {
- DMWARN("can't change the device type after a table is bound");
- goto out;
- }
-
map = __bind(md, table, &limits);
out:
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index bad1724..0c2dd5f 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -59,13 +59,20 @@ void dm_table_postsuspend_targets(struct dm_table *t);
int dm_table_resume_targets(struct dm_table *t);
int dm_table_any_congested(struct dm_table *t, int bdi_bits);
int dm_table_any_busy_target(struct dm_table *t);
-int dm_table_set_type(struct dm_table *t);
unsigned dm_table_get_type(struct dm_table *t);
bool dm_table_request_based(struct dm_table *t);
+bool dm_table_supports_discards(struct dm_table *t);
int dm_table_alloc_md_mempools(struct dm_table *t);
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
+void dm_lock_md_type(struct mapped_device *md);
+void dm_unlock_md_type(struct mapped_device *md);
+void dm_set_md_type(struct mapped_device *md, unsigned type);
+unsigned dm_get_md_type(struct mapped_device *md);
+
+int dm_setup_md_queue(struct mapped_device *md);
+
/*
* To check the return value from dm_table_find_target().
*/
@@ -122,6 +129,11 @@ void dm_linear_exit(void);
int dm_stripe_init(void);
void dm_stripe_exit(void);
+/*
+ * mapped_device operations
+ */
+void dm_destroy(struct mapped_device *md);
+void dm_destroy_immediate(struct mapped_device *md);
int dm_open_count(struct mapped_device *md);
int dm_lock_for_deletion(struct mapped_device *md);
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 7e0e057..ba19060 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -294,7 +294,7 @@ static int linear_make_request (mddev_t *mddev, struct bio *bio)
dev_info_t *tmp_dev;
sector_t start_sector;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index cb20d0b..11567c7 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -36,6 +36,7 @@
#include <linux/blkdev.h>
#include <linux/sysctl.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/buffer_head.h> /* for invalidate_bdev */
#include <linux/poll.h>
#include <linux/ctype.h>
@@ -261,7 +262,7 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
* Once ->stop is called and completes, the module will be completely
* unused.
*/
-static void mddev_suspend(mddev_t *mddev)
+void mddev_suspend(mddev_t *mddev)
{
BUG_ON(mddev->suspended);
mddev->suspended = 1;
@@ -269,13 +270,15 @@ static void mddev_suspend(mddev_t *mddev)
wait_event(mddev->sb_wait, atomic_read(&mddev->active_io) == 0);
mddev->pers->quiesce(mddev, 1);
}
+EXPORT_SYMBOL_GPL(mddev_suspend);
-static void mddev_resume(mddev_t *mddev)
+void mddev_resume(mddev_t *mddev)
{
mddev->suspended = 0;
wake_up(&mddev->sb_wait);
mddev->pers->quiesce(mddev, 0);
}
+EXPORT_SYMBOL_GPL(mddev_resume);
int mddev_congested(mddev_t *mddev, int bits)
{
@@ -353,7 +356,7 @@ static void md_submit_barrier(struct work_struct *ws)
/* an empty barrier - all done */
bio_endio(bio, 0);
else {
- bio->bi_rw &= ~(1<<BIO_RW_BARRIER);
+ bio->bi_rw &= ~REQ_HARDBARRIER;
if (mddev->pers->make_request(mddev, bio))
generic_make_request(bio);
mddev->barrier = POST_REQUEST_BARRIER;
@@ -384,6 +387,51 @@ void md_barrier_request(mddev_t *mddev, struct bio *bio)
}
EXPORT_SYMBOL(md_barrier_request);
+/* Support for plugging.
+ * This mirrors the plugging support in request_queue, but does not
+ * require having a whole queue
+ */
+static void plugger_work(struct work_struct *work)
+{
+ struct plug_handle *plug =
+ container_of(work, struct plug_handle, unplug_work);
+ plug->unplug_fn(plug);
+}
+static void plugger_timeout(unsigned long data)
+{
+ struct plug_handle *plug = (void *)data;
+ kblockd_schedule_work(NULL, &plug->unplug_work);
+}
+void plugger_init(struct plug_handle *plug,
+ void (*unplug_fn)(struct plug_handle *))
+{
+ plug->unplug_flag = 0;
+ plug->unplug_fn = unplug_fn;
+ init_timer(&plug->unplug_timer);
+ plug->unplug_timer.function = plugger_timeout;
+ plug->unplug_timer.data = (unsigned long)plug;
+ INIT_WORK(&plug->unplug_work, plugger_work);
+}
+EXPORT_SYMBOL_GPL(plugger_init);
+
+void plugger_set_plug(struct plug_handle *plug)
+{
+ if (!test_and_set_bit(PLUGGED_FLAG, &plug->unplug_flag))
+ mod_timer(&plug->unplug_timer, jiffies + msecs_to_jiffies(3)+1);
+}
+EXPORT_SYMBOL_GPL(plugger_set_plug);
+
+int plugger_remove_plug(struct plug_handle *plug)
+{
+ if (test_and_clear_bit(PLUGGED_FLAG, &plug->unplug_flag)) {
+ del_timer(&plug->unplug_timer);
+ return 1;
+ } else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(plugger_remove_plug);
+
+
static inline mddev_t *mddev_get(mddev_t *mddev)
{
atomic_inc(&mddev->active);
@@ -416,7 +464,7 @@ static void mddev_put(mddev_t *mddev)
spin_unlock(&all_mddevs_lock);
}
-static void mddev_init(mddev_t *mddev)
+void mddev_init(mddev_t *mddev)
{
mutex_init(&mddev->open_mutex);
mutex_init(&mddev->reconfig_mutex);
@@ -436,6 +484,7 @@ static void mddev_init(mddev_t *mddev)
mddev->resync_max = MaxSector;
mddev->level = LEVEL_NONE;
}
+EXPORT_SYMBOL_GPL(mddev_init);
static mddev_t * mddev_find(dev_t unit)
{
@@ -532,25 +581,31 @@ static void mddev_unlock(mddev_t * mddev)
* an access to the files will try to take reconfig_mutex
* while holding the file unremovable, which leads to
* a deadlock.
- * So hold open_mutex instead - we are allowed to take
- * it while holding reconfig_mutex, and md_run can
- * use it to wait for the remove to complete.
+ * So hold set sysfs_active while the remove in happeing,
+ * and anything else which might set ->to_remove or my
+ * otherwise change the sysfs namespace will fail with
+ * -EBUSY if sysfs_active is still set.
+ * We set sysfs_active under reconfig_mutex and elsewhere
+ * test it under the same mutex to ensure its correct value
+ * is seen.
*/
struct attribute_group *to_remove = mddev->to_remove;
mddev->to_remove = NULL;
- mutex_lock(&mddev->open_mutex);
+ mddev->sysfs_active = 1;
mutex_unlock(&mddev->reconfig_mutex);
- if (to_remove != &md_redundancy_group)
- sysfs_remove_group(&mddev->kobj, to_remove);
- if (mddev->pers == NULL ||
- mddev->pers->sync_request == NULL) {
- sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
- if (mddev->sysfs_action)
- sysfs_put(mddev->sysfs_action);
- mddev->sysfs_action = NULL;
+ if (mddev->kobj.sd) {
+ if (to_remove != &md_redundancy_group)
+ sysfs_remove_group(&mddev->kobj, to_remove);
+ if (mddev->pers == NULL ||
+ mddev->pers->sync_request == NULL) {
+ sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
+ if (mddev->sysfs_action)
+ sysfs_put(mddev->sysfs_action);
+ mddev->sysfs_action = NULL;
+ }
}
- mutex_unlock(&mddev->open_mutex);
+ mddev->sysfs_active = 0;
} else
mutex_unlock(&mddev->reconfig_mutex);
@@ -675,11 +730,11 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
* if zero is reached.
* If an error occurred, call md_error
*
- * As we might need to resubmit the request if BIO_RW_BARRIER
+ * As we might need to resubmit the request if REQ_HARDBARRIER
* causes ENOTSUPP, we allocate a spare bio...
*/
struct bio *bio = bio_alloc(GFP_NOIO, 1);
- int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
+ int rw = REQ_WRITE | REQ_SYNC | REQ_UNPLUG;
bio->bi_bdev = rdev->bdev;
bio->bi_sector = sector;
@@ -691,7 +746,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
atomic_inc(&mddev->pending_writes);
if (!test_bit(BarriersNotsupp, &rdev->flags)) {
struct bio *rbio;
- rw |= (1<<BIO_RW_BARRIER);
+ rw |= REQ_HARDBARRIER;
rbio = bio_clone(bio, GFP_NOIO);
rbio->bi_private = bio;
rbio->bi_end_io = super_written_barrier;
@@ -736,7 +791,7 @@ int sync_page_io(struct block_device *bdev, sector_t sector, int size,
struct completion event;
int ret;
- rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
bio->bi_bdev = bdev;
bio->bi_sector = sector;
@@ -1811,11 +1866,9 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
goto fail;
ko = &part_to_dev(rdev->bdev->bd_part)->kobj;
- if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) {
- kobject_del(&rdev->kobj);
- goto fail;
- }
- rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, NULL, "state");
+ if (sysfs_create_link(&rdev->kobj, ko, "block"))
+ /* failure here is OK */;
+ rdev->sysfs_state = sysfs_get_dirent_safe(rdev->kobj.sd, "state");
list_add_rcu(&rdev->same_set, &mddev->disks);
bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk);
@@ -2334,8 +2387,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
set_bit(In_sync, &rdev->flags);
err = 0;
}
- if (!err && rdev->sysfs_state)
- sysfs_notify_dirent(rdev->sysfs_state);
+ if (!err)
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
return err ? err : len;
}
static struct rdev_sysfs_entry rdev_state =
@@ -2430,14 +2483,10 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
rdev->raid_disk = -1;
return err;
} else
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
- printk(KERN_WARNING
- "md: cannot register "
- "%s for %s\n",
- nm, mdname(rdev->mddev));
-
+ /* failure here is OK */;
/* don't wakeup anyone, leave that to userspace. */
} else {
if (slot >= rdev->mddev->raid_disks)
@@ -2447,7 +2496,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
clear_bit(Faulty, &rdev->flags);
clear_bit(WriteMostly, &rdev->flags);
set_bit(In_sync, &rdev->flags);
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
}
return len;
}
@@ -2695,6 +2744,24 @@ static struct kobj_type rdev_ktype = {
.default_attrs = rdev_default_attrs,
};
+void md_rdev_init(mdk_rdev_t *rdev)
+{
+ rdev->desc_nr = -1;
+ rdev->saved_raid_disk = -1;
+ rdev->raid_disk = -1;
+ rdev->flags = 0;
+ rdev->data_offset = 0;
+ rdev->sb_events = 0;
+ rdev->last_read_error.tv_sec = 0;
+ rdev->last_read_error.tv_nsec = 0;
+ atomic_set(&rdev->nr_pending, 0);
+ atomic_set(&rdev->read_errors, 0);
+ atomic_set(&rdev->corrected_errors, 0);
+
+ INIT_LIST_HEAD(&rdev->same_set);
+ init_waitqueue_head(&rdev->blocked_wait);
+}
+EXPORT_SYMBOL_GPL(md_rdev_init);
/*
* Import a device. If 'super_format' >= 0, then sanity check the superblock
*
@@ -2718,6 +2785,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
return ERR_PTR(-ENOMEM);
}
+ md_rdev_init(rdev);
if ((err = alloc_disk_sb(rdev)))
goto abort_free;
@@ -2727,18 +2795,6 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
kobject_init(&rdev->kobj, &rdev_ktype);
- rdev->desc_nr = -1;
- rdev->saved_raid_disk = -1;
- rdev->raid_disk = -1;
- rdev->flags = 0;
- rdev->data_offset = 0;
- rdev->sb_events = 0;
- rdev->last_read_error.tv_sec = 0;
- rdev->last_read_error.tv_nsec = 0;
- atomic_set(&rdev->nr_pending, 0);
- atomic_set(&rdev->read_errors, 0);
- atomic_set(&rdev->corrected_errors, 0);
-
size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
if (!size) {
printk(KERN_WARNING
@@ -2767,9 +2823,6 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
}
}
- INIT_LIST_HEAD(&rdev->same_set);
- init_waitqueue_head(&rdev->blocked_wait);
-
return rdev;
abort_free:
@@ -2960,7 +3013,9 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
* - new personality will access other array.
*/
- if (mddev->sync_thread || mddev->reshape_position != MaxSector)
+ if (mddev->sync_thread ||
+ mddev->reshape_position != MaxSector ||
+ mddev->sysfs_active)
return -EBUSY;
if (!mddev->pers->quiesce) {
@@ -3437,7 +3492,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
if (err)
return err;
else {
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
return len;
}
}
@@ -3735,7 +3790,7 @@ action_store(mddev_t *mddev, const char *page, size_t len)
}
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
return len;
}
@@ -4281,13 +4336,14 @@ static int md_alloc(dev_t dev, char *name)
disk->disk_name);
error = 0;
}
- if (sysfs_create_group(&mddev->kobj, &md_bitmap_group))
+ if (mddev->kobj.sd &&
+ sysfs_create_group(&mddev->kobj, &md_bitmap_group))
printk(KERN_DEBUG "pointless warning\n");
abort:
mutex_unlock(&disks_mutex);
- if (!error) {
+ if (!error && mddev->kobj.sd) {
kobject_uevent(&mddev->kobj, KOBJ_ADD);
- mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, NULL, "array_state");
+ mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd, "array_state");
}
mddev_put(mddev);
return error;
@@ -4325,14 +4381,14 @@ static void md_safemode_timeout(unsigned long data)
if (!atomic_read(&mddev->writes_pending)) {
mddev->safemode = 1;
if (mddev->external)
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
}
md_wakeup_thread(mddev->thread);
}
static int start_dirty_degraded;
-static int md_run(mddev_t *mddev)
+int md_run(mddev_t *mddev)
{
int err;
mdk_rdev_t *rdev;
@@ -4344,13 +4400,9 @@ static int md_run(mddev_t *mddev)
if (mddev->pers)
return -EBUSY;
-
- /* These two calls synchronise us with the
- * sysfs_remove_group calls in mddev_unlock,
- * so they must have completed.
- */
- mutex_lock(&mddev->open_mutex);
- mutex_unlock(&mddev->open_mutex);
+ /* Cannot run until previous stop completes properly */
+ if (mddev->sysfs_active)
+ return -EBUSY;
/*
* Analyze all RAID superblock(s)
@@ -4397,7 +4449,7 @@ static int md_run(mddev_t *mddev)
return -EINVAL;
}
}
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
}
spin_lock(&pers_lock);
@@ -4496,11 +4548,12 @@ static int md_run(mddev_t *mddev)
return err;
}
if (mddev->pers->sync_request) {
- if (sysfs_create_group(&mddev->kobj, &md_redundancy_group))
+ if (mddev->kobj.sd &&
+ sysfs_create_group(&mddev->kobj, &md_redundancy_group))
printk(KERN_WARNING
"md: cannot register extra attributes for %s\n",
mdname(mddev));
- mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, NULL, "sync_action");
+ mddev->sysfs_action = sysfs_get_dirent_safe(mddev->kobj.sd, "sync_action");
} else if (mddev->ro == 2) /* auto-readonly not meaningful */
mddev->ro = 0;
@@ -4518,8 +4571,7 @@ static int md_run(mddev_t *mddev)
char nm[20];
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
- printk("md: cannot register %s for %s\n",
- nm, mdname(mddev));
+ /* failure here is OK */;
}
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
@@ -4531,12 +4583,12 @@ static int md_run(mddev_t *mddev)
md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
md_new_event(mddev);
- sysfs_notify_dirent(mddev->sysfs_state);
- if (mddev->sysfs_action)
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
sysfs_notify(&mddev->kobj, NULL, "degraded");
return 0;
}
+EXPORT_SYMBOL_GPL(md_run);
static int do_md_run(mddev_t *mddev)
{
@@ -4545,7 +4597,11 @@ static int do_md_run(mddev_t *mddev)
err = md_run(mddev);
if (err)
goto out;
-
+ err = bitmap_load(mddev);
+ if (err) {
+ bitmap_destroy(mddev);
+ goto out;
+ }
set_capacity(mddev->gendisk, mddev->array_sectors);
revalidate_disk(mddev->gendisk);
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
@@ -4573,7 +4629,7 @@ static int restart_array(mddev_t *mddev)
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
md_wakeup_thread(mddev->sync_thread);
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
return 0;
}
@@ -4644,9 +4700,10 @@ static void md_clean(mddev_t *mddev)
mddev->bitmap_info.chunksize = 0;
mddev->bitmap_info.daemon_sleep = 0;
mddev->bitmap_info.max_write_behind = 0;
+ mddev->plug = NULL;
}
-static void md_stop_writes(mddev_t *mddev)
+void md_stop_writes(mddev_t *mddev)
{
if (mddev->sync_thread) {
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
@@ -4666,11 +4723,10 @@ static void md_stop_writes(mddev_t *mddev)
md_update_sb(mddev, 1);
}
}
+EXPORT_SYMBOL_GPL(md_stop_writes);
-static void md_stop(mddev_t *mddev)
+void md_stop(mddev_t *mddev)
{
- md_stop_writes(mddev);
-
mddev->pers->stop(mddev);
if (mddev->pers->sync_request && mddev->to_remove == NULL)
mddev->to_remove = &md_redundancy_group;
@@ -4678,6 +4734,7 @@ static void md_stop(mddev_t *mddev)
mddev->pers = NULL;
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
}
+EXPORT_SYMBOL_GPL(md_stop);
static int md_set_readonly(mddev_t *mddev, int is_open)
{
@@ -4697,7 +4754,7 @@ static int md_set_readonly(mddev_t *mddev, int is_open)
mddev->ro = 1;
set_disk_ro(mddev->gendisk, 1);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
err = 0;
}
out:
@@ -4711,26 +4768,29 @@ out:
*/
static int do_md_stop(mddev_t * mddev, int mode, int is_open)
{
- int err = 0;
struct gendisk *disk = mddev->gendisk;
mdk_rdev_t *rdev;
mutex_lock(&mddev->open_mutex);
- if (atomic_read(&mddev->openers) > is_open) {
+ if (atomic_read(&mddev->openers) > is_open ||
+ mddev->sysfs_active) {
printk("md: %s still in use.\n",mdname(mddev));
- err = -EBUSY;
- } else if (mddev->pers) {
+ mutex_unlock(&mddev->open_mutex);
+ return -EBUSY;
+ }
+ if (mddev->pers) {
if (mddev->ro)
set_disk_ro(disk, 0);
+ md_stop_writes(mddev);
md_stop(mddev);
mddev->queue->merge_bvec_fn = NULL;
mddev->queue->unplug_fn = NULL;
mddev->queue->backing_dev_info.congested_fn = NULL;
/* tell userspace to handle 'inactive' */
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
list_for_each_entry(rdev, &mddev->disks, same_set)
if (rdev->raid_disk >= 0) {
@@ -4740,21 +4800,17 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
}
set_capacity(disk, 0);
+ mutex_unlock(&mddev->open_mutex);
revalidate_disk(disk);
if (mddev->ro)
mddev->ro = 0;
-
- err = 0;
- }
- mutex_unlock(&mddev->open_mutex);
- if (err)
- return err;
+ } else
+ mutex_unlock(&mddev->open_mutex);
/*
* Free resources if final stop
*/
if (mode == 0) {
-
printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
bitmap_destroy(mddev);
@@ -4771,13 +4827,11 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
if (mddev->hold_active == UNTIL_STOP)
mddev->hold_active = 0;
-
}
- err = 0;
blk_integrity_unregister(disk);
md_new_event(mddev);
- sysfs_notify_dirent(mddev->sysfs_state);
- return err;
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
+ return 0;
}
#ifndef MODULE
@@ -5138,7 +5192,7 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
if (err)
export_rdev(rdev);
else
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
md_update_sb(mddev, 1);
if (mddev->degraded)
@@ -5331,8 +5385,11 @@ static int set_bitmap_file(mddev_t *mddev, int fd)
err = 0;
if (mddev->pers) {
mddev->pers->quiesce(mddev, 1);
- if (fd >= 0)
+ if (fd >= 0) {
err = bitmap_create(mddev);
+ if (!err)
+ err = bitmap_load(mddev);
+ }
if (fd < 0 || err) {
bitmap_destroy(mddev);
fd = -1; /* make sure to put the file */
@@ -5581,6 +5638,8 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
mddev->bitmap_info.default_offset;
mddev->pers->quiesce(mddev, 1);
rv = bitmap_create(mddev);
+ if (!rv)
+ rv = bitmap_load(mddev);
if (rv)
bitmap_destroy(mddev);
mddev->pers->quiesce(mddev, 0);
@@ -5813,7 +5872,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
if (_IOC_TYPE(cmd) == MD_MAJOR && mddev->ro && mddev->pers) {
if (mddev->ro == 2) {
mddev->ro = 0;
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
} else {
@@ -5902,6 +5961,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)
mddev_t *mddev = mddev_find(bdev->bd_dev);
int err;
+ lock_kernel();
if (mddev->gendisk != bdev->bd_disk) {
/* we are racing with mddev_put which is discarding this
* bd_disk.
@@ -5910,6 +5970,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)
/* Wait until bdev->bd_disk is definitely gone */
flush_scheduled_work();
/* Then retry the open from the top */
+ unlock_kernel();
return -ERESTARTSYS;
}
BUG_ON(mddev != bdev->bd_disk->private_data);
@@ -5923,6 +5984,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)
check_disk_size_change(mddev->gendisk, bdev);
out:
+ unlock_kernel();
return err;
}
@@ -5931,8 +5993,10 @@ static int md_release(struct gendisk *disk, fmode_t mode)
mddev_t *mddev = disk->private_data;
BUG_ON(!mddev);
+ lock_kernel();
atomic_dec(&mddev->openers);
mddev_put(mddev);
+ unlock_kernel();
return 0;
}
@@ -6059,10 +6123,12 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->pers->error_handler(mddev,rdev);
if (mddev->degraded)
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
+ if (mddev->event_work.func)
+ schedule_work(&mddev->event_work);
md_new_event_inintr(mddev);
}
@@ -6520,7 +6586,7 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
spin_unlock_irq(&mddev->write_lock);
}
if (did_change)
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
wait_event(mddev->sb_wait,
!test_bit(MD_CHANGE_CLEAN, &mddev->flags) &&
!test_bit(MD_CHANGE_PENDING, &mddev->flags));
@@ -6563,7 +6629,7 @@ int md_allow_write(mddev_t *mddev)
mddev->safemode = 1;
spin_unlock_irq(&mddev->write_lock);
md_update_sb(mddev, 0);
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
} else
spin_unlock_irq(&mddev->write_lock);
@@ -6574,6 +6640,14 @@ int md_allow_write(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_allow_write);
+void md_unplug(mddev_t *mddev)
+{
+ if (mddev->queue)
+ blk_unplug(mddev->queue);
+ if (mddev->plug)
+ mddev->plug->unplug_fn(mddev->plug);
+}
+
#define SYNC_MARKS 10
#define SYNC_MARK_STEP (3*HZ)
void md_do_sync(mddev_t *mddev)
@@ -6752,12 +6826,13 @@ void md_do_sync(mddev_t *mddev)
>= mddev->resync_max - mddev->curr_resync_completed
)) {
/* time to update curr_resync_completed */
- blk_unplug(mddev->queue);
+ md_unplug(mddev);
wait_event(mddev->recovery_wait,
atomic_read(&mddev->recovery_active) == 0);
mddev->curr_resync_completed =
mddev->curr_resync;
- set_bit(MD_CHANGE_CLEAN, &mddev->flags);
+ if (mddev->persistent)
+ set_bit(MD_CHANGE_CLEAN, &mddev->flags);
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
}
@@ -6829,7 +6904,7 @@ void md_do_sync(mddev_t *mddev)
* about not overloading the IO subsystem. (things like an
* e2fsck being done on the RAID array should execute fast)
*/
- blk_unplug(mddev->queue);
+ md_unplug(mddev);
cond_resched();
currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2
@@ -6848,7 +6923,7 @@ void md_do_sync(mddev_t *mddev)
* this also signals 'finished resyncing' to md_stop
*/
out:
- blk_unplug(mddev->queue);
+ md_unplug(mddev);
wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));
@@ -6950,10 +7025,7 @@ static int remove_and_add_spares(mddev_t *mddev)
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&mddev->kobj,
&rdev->kobj, nm))
- printk(KERN_WARNING
- "md: cannot register "
- "%s for %s\n",
- nm, mdname(mddev));
+ /* failure here is OK */;
spares++;
md_new_event(mddev);
set_bit(MD_CHANGE_DEVS, &mddev->flags);
@@ -7046,7 +7118,7 @@ void md_check_recovery(mddev_t *mddev)
mddev->safemode = 0;
spin_unlock_irq(&mddev->write_lock);
if (did_change)
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
}
if (mddev->flags)
@@ -7085,7 +7157,7 @@ void md_check_recovery(mddev_t *mddev)
mddev->recovery = 0;
/* flag recovery needed just to double check */
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
md_new_event(mddev);
goto unlock;
}
@@ -7147,7 +7219,7 @@ void md_check_recovery(mddev_t *mddev)
mddev->recovery = 0;
} else
md_wakeup_thread(mddev->sync_thread);
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
md_new_event(mddev);
}
unlock:
@@ -7156,7 +7228,7 @@ void md_check_recovery(mddev_t *mddev)
if (test_and_clear_bit(MD_RECOVERY_RECOVER,
&mddev->recovery))
if (mddev->sysfs_action)
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
}
mddev_unlock(mddev);
}
@@ -7164,7 +7236,7 @@ void md_check_recovery(mddev_t *mddev)
void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
{
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
wait_event_timeout(rdev->blocked_wait,
!test_bit(Blocked, &rdev->flags),
msecs_to_jiffies(5000));
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 10597bf..a953fe2 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -29,6 +29,26 @@
typedef struct mddev_s mddev_t;
typedef struct mdk_rdev_s mdk_rdev_t;
+/* generic plugging support - like that provided with request_queue,
+ * but does not require a request_queue
+ */
+struct plug_handle {
+ void (*unplug_fn)(struct plug_handle *);
+ struct timer_list unplug_timer;
+ struct work_struct unplug_work;
+ unsigned long unplug_flag;
+};
+#define PLUGGED_FLAG 1
+void plugger_init(struct plug_handle *plug,
+ void (*unplug_fn)(struct plug_handle *));
+void plugger_set_plug(struct plug_handle *plug);
+int plugger_remove_plug(struct plug_handle *plug);
+static inline void plugger_flush(struct plug_handle *plug)
+{
+ del_timer_sync(&plug->unplug_timer);
+ cancel_work_sync(&plug->unplug_work);
+}
+
/*
* MD's 'extended' device
*/
@@ -67,7 +87,7 @@ struct mdk_rdev_s
#define Faulty 1 /* device is known to have a fault */
#define In_sync 2 /* device is in_sync with rest of array */
#define WriteMostly 4 /* Avoid reading if at all possible */
-#define BarriersNotsupp 5 /* BIO_RW_BARRIER is not supported */
+#define BarriersNotsupp 5 /* REQ_HARDBARRIER is not supported */
#define AllReserved 6 /* If whole device is reserved for
* one array */
#define AutoDetected 7 /* added by auto-detect */
@@ -125,6 +145,10 @@ struct mddev_s
int suspended;
atomic_t active_io;
int ro;
+ int sysfs_active; /* set when sysfs deletes
+ * are happening, so run/
+ * takeover/stop are not safe
+ */
struct gendisk *gendisk;
@@ -254,7 +278,7 @@ struct mddev_s
* fails. Only supported
*/
struct bio *biolist; /* bios that need to be retried
- * because BIO_RW_BARRIER is not supported
+ * because REQ_HARDBARRIER is not supported
*/
atomic_t recovery_active; /* blocks scheduled, but not written */
@@ -297,9 +321,14 @@ struct mddev_s
* hot-adding a bitmap. It should
* eventually be settable by sysfs.
*/
+ /* When md is serving under dm, it might use a
+ * dirty_log to store the bits.
+ */
+ struct dm_dirty_log *log;
+
struct mutex mutex;
unsigned long chunksize;
- unsigned long daemon_sleep; /* how many seconds between updates? */
+ unsigned long daemon_sleep; /* how many jiffies between updates? */
unsigned long max_write_behind; /* write-behind mode */
int external;
} bitmap_info;
@@ -308,6 +337,8 @@ struct mddev_s
struct list_head all_mddevs;
struct attribute_group *to_remove;
+ struct plug_handle *plug; /* if used by personality */
+
/* Generic barrier handling.
* If there is a pending barrier request, all other
* writes are blocked while the devices are flushed.
@@ -318,6 +349,7 @@ struct mddev_s
struct bio *barrier;
atomic_t flush_pending;
struct work_struct barrier_work;
+ struct work_struct event_work; /* used by dm to report failure event */
};
@@ -382,6 +414,18 @@ struct md_sysfs_entry {
};
extern struct attribute_group md_bitmap_group;
+static inline struct sysfs_dirent *sysfs_get_dirent_safe(struct sysfs_dirent *sd, char *name)
+{
+ if (sd)
+ return sysfs_get_dirent(sd, NULL, name);
+ return sd;
+}
+static inline void sysfs_notify_dirent_safe(struct sysfs_dirent *sd)
+{
+ if (sd)
+ sysfs_notify_dirent(sd);
+}
+
static inline char * mdname (mddev_t * mddev)
{
return mddev->gendisk ? mddev->gendisk->disk_name : "mdX";
@@ -474,5 +518,14 @@ extern int md_integrity_register(mddev_t *mddev);
extern void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
extern void restore_bitmap_write_access(struct file *file);
+extern void md_unplug(mddev_t *mddev);
+
+extern void mddev_init(mddev_t *mddev);
+extern int md_run(mddev_t *mddev);
+extern void md_stop(mddev_t *mddev);
+extern void md_stop_writes(mddev_t *mddev);
+extern void md_rdev_init(mdk_rdev_t *rdev);
+extern void mddev_suspend(mddev_t *mddev);
+extern void mddev_resume(mddev_t *mddev);
#endif /* _MD_MD_H */
diff --git a/drivers/md/mktables.c b/drivers/md/mktables.c
deleted file mode 100644
index 3b15008..0000000
--- a/drivers/md/mktables.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
- *
- * This file is part of the Linux kernel, and is made available under
- * the terms of the GNU General Public License version 2 or (at your
- * option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * mktables.c
- *
- * Make RAID-6 tables. This is a host user space program to be run at
- * compile time.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <time.h>
-
-static uint8_t gfmul(uint8_t a, uint8_t b)
-{
- uint8_t v = 0;
-
- while (b) {
- if (b & 1)
- v ^= a;
- a = (a << 1) ^ (a & 0x80 ? 0x1d : 0);
- b >>= 1;
- }
-
- return v;
-}
-
-static uint8_t gfpow(uint8_t a, int b)
-{
- uint8_t v = 1;
-
- b %= 255;
- if (b < 0)
- b += 255;
-
- while (b) {
- if (b & 1)
- v = gfmul(v, a);
- a = gfmul(a, a);
- b >>= 1;
- }
-
- return v;
-}
-
-int main(int argc, char *argv[])
-{
- int i, j, k;
- uint8_t v;
- uint8_t exptbl[256], invtbl[256];
-
- printf("#include <linux/raid/pq.h>\n");
-
- /* Compute multiplication table */
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfmul[256][256] =\n"
- "{\n");
- for (i = 0; i < 256; i++) {
- printf("\t{\n");
- for (j = 0; j < 256; j += 8) {
- printf("\t\t");
- for (k = 0; k < 8; k++)
- printf("0x%02x,%c", gfmul(i, j + k),
- (k == 7) ? '\n' : ' ');
- }
- printf("\t},\n");
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfmul);\n");
- printf("#endif\n");
-
- /* Compute power-of-2 table (exponent) */
- v = 1;
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfexp[256] =\n" "{\n");
- for (i = 0; i < 256; i += 8) {
- printf("\t");
- for (j = 0; j < 8; j++) {
- exptbl[i + j] = v;
- printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
- v = gfmul(v, 2);
- if (v == 1)
- v = 0; /* For entry 255, not a real entry */
- }
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfexp);\n");
- printf("#endif\n");
-
- /* Compute inverse table x^-1 == x^254 */
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfinv[256] =\n" "{\n");
- for (i = 0; i < 256; i += 8) {
- printf("\t");
- for (j = 0; j < 8; j++) {
- invtbl[i + j] = v = gfpow(i + j, 254);
- printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
- }
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfinv);\n");
- printf("#endif\n");
-
- /* Compute inv(2^x + 1) (exponent-xor-inverse) table */
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfexi[256] =\n" "{\n");
- for (i = 0; i < 256; i += 8) {
- printf("\t");
- for (j = 0; j < 8; j++)
- printf("0x%02x,%c", invtbl[exptbl[i + j] ^ 1],
- (j == 7) ? '\n' : ' ');
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfexi);\n");
- printf("#endif\n");
-
- return 0;
-}
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 410fb60..0307d21 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -91,7 +91,7 @@ static void multipath_end_request(struct bio *bio, int error)
if (uptodate)
multipath_end_bh_io(mp_bh, 0);
- else if (!bio_rw_flagged(bio, BIO_RW_AHEAD)) {
+ else if (!(bio->bi_rw & REQ_RAHEAD)) {
/*
* oops, IO error:
*/
@@ -142,7 +142,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio)
struct multipath_bh * mp_bh;
struct multipath_info *multipath;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
@@ -163,7 +163,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio)
mp_bh->bio = *bio;
mp_bh->bio.bi_sector += multipath->rdev->data_offset;
mp_bh->bio.bi_bdev = multipath->rdev->bdev;
- mp_bh->bio.bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT);
+ mp_bh->bio.bi_rw |= REQ_FAILFAST_TRANSPORT;
mp_bh->bio.bi_end_io = multipath_end_request;
mp_bh->bio.bi_private = mp_bh;
generic_make_request(&mp_bh->bio);
@@ -398,7 +398,7 @@ static void multipathd (mddev_t *mddev)
*bio = *(mp_bh->master_bio);
bio->bi_sector += conf->multipaths[mp_bh->path].rdev->data_offset;
bio->bi_bdev = conf->multipaths[mp_bh->path].rdev->bdev;
- bio->bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT);
+ bio->bi_rw |= REQ_FAILFAST_TRANSPORT;
bio->bi_end_io = multipath_end_request;
bio->bi_private = mp_bh;
generic_make_request(bio);
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 563abed..6f7af46 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -483,7 +483,7 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio)
struct strip_zone *zone;
mdk_rdev_t *tmp_dev;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index a948da8..73cc74f 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -787,7 +787,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
struct bio_list bl;
struct page **behind_pages = NULL;
const int rw = bio_data_dir(bio);
- const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ const bool do_sync = (bio->bi_rw & REQ_SYNC);
bool do_barriers;
mdk_rdev_t *blocked_rdev;
@@ -822,7 +822,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
finish_wait(&conf->wait_barrier, &w);
}
if (unlikely(!mddev->barriers_work &&
- bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ (bio->bi_rw & REQ_HARDBARRIER))) {
if (rw == WRITE)
md_write_end(mddev);
bio_endio(bio, -EOPNOTSUPP);
@@ -877,7 +877,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset;
read_bio->bi_bdev = mirror->rdev->bdev;
read_bio->bi_end_io = raid1_end_read_request;
- read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ read_bio->bi_rw = READ | do_sync;
read_bio->bi_private = r1_bio;
generic_make_request(read_bio);
@@ -959,7 +959,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
atomic_set(&r1_bio->remaining, 0);
atomic_set(&r1_bio->behind_remaining, 0);
- do_barriers = bio_rw_flagged(bio, BIO_RW_BARRIER);
+ do_barriers = bio->bi_rw & REQ_HARDBARRIER;
if (do_barriers)
set_bit(R1BIO_Barrier, &r1_bio->state);
@@ -975,8 +975,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset;
mbio->bi_bdev = conf->mirrors[i].rdev->bdev;
mbio->bi_end_io = raid1_end_write_request;
- mbio->bi_rw = WRITE | (do_barriers << BIO_RW_BARRIER) |
- (do_sync << BIO_RW_SYNCIO);
+ mbio->bi_rw = WRITE | do_barriers | do_sync;
mbio->bi_private = r1_bio;
if (behind_pages) {
@@ -1633,7 +1632,7 @@ static void raid1d(mddev_t *mddev)
sync_request_write(mddev, r1_bio);
unplug = 1;
} else if (test_bit(R1BIO_BarrierRetry, &r1_bio->state)) {
- /* some requests in the r1bio were BIO_RW_BARRIER
+ /* some requests in the r1bio were REQ_HARDBARRIER
* requests which failed with -EOPNOTSUPP. Hohumm..
* Better resubmit without the barrier.
* We know which devices to resubmit for, because
@@ -1641,7 +1640,7 @@ static void raid1d(mddev_t *mddev)
* We already have a nr_pending reference on these rdevs.
*/
int i;
- const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO);
+ const bool do_sync = (r1_bio->master_bio->bi_rw & REQ_SYNC);
clear_bit(R1BIO_BarrierRetry, &r1_bio->state);
clear_bit(R1BIO_Barrier, &r1_bio->state);
for (i=0; i < conf->raid_disks; i++)
@@ -1662,8 +1661,7 @@ static void raid1d(mddev_t *mddev)
conf->mirrors[i].rdev->data_offset;
bio->bi_bdev = conf->mirrors[i].rdev->bdev;
bio->bi_end_io = raid1_end_write_request;
- bio->bi_rw = WRITE |
- (do_sync << BIO_RW_SYNCIO);
+ bio->bi_rw = WRITE | do_sync;
bio->bi_private = r1_bio;
r1_bio->bios[i] = bio;
generic_make_request(bio);
@@ -1698,7 +1696,7 @@ static void raid1d(mddev_t *mddev)
(unsigned long long)r1_bio->sector);
raid_end_bio_io(r1_bio);
} else {
- const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO);
+ const bool do_sync = r1_bio->master_bio->bi_rw & REQ_SYNC;
r1_bio->bios[r1_bio->read_disk] =
mddev->ro ? IO_BLOCKED : NULL;
r1_bio->read_disk = disk;
@@ -1715,7 +1713,7 @@ static void raid1d(mddev_t *mddev)
bio->bi_sector = r1_bio->sector + rdev->data_offset;
bio->bi_bdev = rdev->bdev;
bio->bi_end_io = raid1_end_read_request;
- bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ bio->bi_rw = READ | do_sync;
bio->bi_private = r1_bio;
unplug = 1;
generic_make_request(bio);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 42e64e4..a88aeb5 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -799,12 +799,12 @@ static int make_request(mddev_t *mddev, struct bio * bio)
int i;
int chunk_sects = conf->chunk_mask + 1;
const int rw = bio_data_dir(bio);
- const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ const bool do_sync = (bio->bi_rw & REQ_SYNC);
struct bio_list bl;
unsigned long flags;
mdk_rdev_t *blocked_rdev;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
@@ -825,11 +825,29 @@ static int make_request(mddev_t *mddev, struct bio * bio)
*/
bp = bio_split(bio,
chunk_sects - (bio->bi_sector & (chunk_sects - 1)) );
+
+ /* Each of these 'make_request' calls will call 'wait_barrier'.
+ * If the first succeeds but the second blocks due to the resync
+ * thread raising the barrier, we will deadlock because the
+ * IO to the underlying device will be queued in generic_make_request
+ * and will never complete, so will never reduce nr_pending.
+ * So increment nr_waiting here so no new raise_barriers will
+ * succeed, and so the second wait_barrier cannot block.
+ */
+ spin_lock_irq(&conf->resync_lock);
+ conf->nr_waiting++;
+ spin_unlock_irq(&conf->resync_lock);
+
if (make_request(mddev, &bp->bio1))
generic_make_request(&bp->bio1);
if (make_request(mddev, &bp->bio2))
generic_make_request(&bp->bio2);
+ spin_lock_irq(&conf->resync_lock);
+ conf->nr_waiting--;
+ wake_up(&conf->wait_barrier);
+ spin_unlock_irq(&conf->resync_lock);
+
bio_pair_release(bp);
return 0;
bad_map:
@@ -879,7 +897,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
mirror->rdev->data_offset;
read_bio->bi_bdev = mirror->rdev->bdev;
read_bio->bi_end_io = raid10_end_read_request;
- read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ read_bio->bi_rw = READ | do_sync;
read_bio->bi_private = r10_bio;
generic_make_request(read_bio);
@@ -947,7 +965,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
conf->mirrors[d].rdev->data_offset;
mbio->bi_bdev = conf->mirrors[d].rdev->bdev;
mbio->bi_end_io = raid10_end_write_request;
- mbio->bi_rw = WRITE | (do_sync << BIO_RW_SYNCIO);
+ mbio->bi_rw = WRITE | do_sync;
mbio->bi_private = r10_bio;
atomic_inc(&r10_bio->remaining);
@@ -1716,7 +1734,7 @@ static void raid10d(mddev_t *mddev)
raid_end_bio_io(r10_bio);
bio_put(bio);
} else {
- const bool do_sync = bio_rw_flagged(r10_bio->master_bio, BIO_RW_SYNCIO);
+ const bool do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC);
bio_put(bio);
rdev = conf->mirrors[mirror].rdev;
if (printk_ratelimit())
@@ -1730,7 +1748,7 @@ static void raid10d(mddev_t *mddev)
bio->bi_sector = r10_bio->devs[r10_bio->read_slot].addr
+ rdev->data_offset;
bio->bi_bdev = rdev->bdev;
- bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ bio->bi_rw = READ | do_sync;
bio->bi_private = r10_bio;
bio->bi_end_io = raid10_end_read_request;
unplug = 1;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 96c6902..866d4b5 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -201,11 +201,11 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
if (test_bit(STRIPE_HANDLE, &sh->state)) {
if (test_bit(STRIPE_DELAYED, &sh->state)) {
list_add_tail(&sh->lru, &conf->delayed_list);
- blk_plug_device(conf->mddev->queue);
+ plugger_set_plug(&conf->plug);
} else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
sh->bm_seq - conf->seq_write > 0) {
list_add_tail(&sh->lru, &conf->bitmap_list);
- blk_plug_device(conf->mddev->queue);
+ plugger_set_plug(&conf->plug);
} else {
clear_bit(STRIPE_BIT_DELAY, &sh->state);
list_add_tail(&sh->lru, &conf->handle_list);
@@ -434,7 +434,6 @@ static int has_failed(raid5_conf_t *conf)
}
static void unplug_slaves(mddev_t *mddev);
-static void raid5_unplug_device(struct request_queue *q);
static struct stripe_head *
get_active_stripe(raid5_conf_t *conf, sector_t sector,
@@ -464,7 +463,7 @@ get_active_stripe(raid5_conf_t *conf, sector_t sector,
< (conf->max_nr_stripes *3/4)
|| !conf->inactive_blocked),
conf->device_lock,
- raid5_unplug_device(conf->mddev->queue)
+ md_raid5_unplug_device(conf)
);
conf->inactive_blocked = 0;
} else
@@ -1337,10 +1336,14 @@ static int grow_stripes(raid5_conf_t *conf, int num)
struct kmem_cache *sc;
int devs = max(conf->raid_disks, conf->previous_raid_disks);
- sprintf(conf->cache_name[0],
- "raid%d-%s", conf->level, mdname(conf->mddev));
- sprintf(conf->cache_name[1],
- "raid%d-%s-alt", conf->level, mdname(conf->mddev));
+ if (conf->mddev->gendisk)
+ sprintf(conf->cache_name[0],
+ "raid%d-%s", conf->level, mdname(conf->mddev));
+ else
+ sprintf(conf->cache_name[0],
+ "raid%d-%p", conf->level, conf->mddev);
+ sprintf(conf->cache_name[1], "%s-alt", conf->cache_name[0]);
+
conf->active_name = 0;
sc = kmem_cache_create(conf->cache_name[conf->active_name],
sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev),
@@ -3614,7 +3617,7 @@ static void raid5_activate_delayed(raid5_conf_t *conf)
list_add_tail(&sh->lru, &conf->hold_list);
}
} else
- blk_plug_device(conf->mddev->queue);
+ plugger_set_plug(&conf->plug);
}
static void activate_bit_delay(raid5_conf_t *conf)
@@ -3655,36 +3658,44 @@ static void unplug_slaves(mddev_t *mddev)
rcu_read_unlock();
}
-static void raid5_unplug_device(struct request_queue *q)
+void md_raid5_unplug_device(raid5_conf_t *conf)
{
- mddev_t *mddev = q->queuedata;
- raid5_conf_t *conf = mddev->private;
unsigned long flags;
spin_lock_irqsave(&conf->device_lock, flags);
- if (blk_remove_plug(q)) {
+ if (plugger_remove_plug(&conf->plug)) {
conf->seq_flush++;
raid5_activate_delayed(conf);
}
- md_wakeup_thread(mddev->thread);
+ md_wakeup_thread(conf->mddev->thread);
spin_unlock_irqrestore(&conf->device_lock, flags);
- unplug_slaves(mddev);
+ unplug_slaves(conf->mddev);
}
+EXPORT_SYMBOL_GPL(md_raid5_unplug_device);
-static int raid5_congested(void *data, int bits)
+static void raid5_unplug(struct plug_handle *plug)
+{
+ raid5_conf_t *conf = container_of(plug, raid5_conf_t, plug);
+ md_raid5_unplug_device(conf);
+}
+
+static void raid5_unplug_queue(struct request_queue *q)
+{
+ mddev_t *mddev = q->queuedata;
+ md_raid5_unplug_device(mddev->private);
+}
+
+int md_raid5_congested(mddev_t *mddev, int bits)
{
- mddev_t *mddev = data;
raid5_conf_t *conf = mddev->private;
/* No difference between reads and writes. Just check
* how busy the stripe_cache is
*/
- if (mddev_congested(mddev, bits))
- return 1;
if (conf->inactive_blocked)
return 1;
if (conf->quiesce)
@@ -3694,6 +3705,15 @@ static int raid5_congested(void *data, int bits)
return 0;
}
+EXPORT_SYMBOL_GPL(md_raid5_congested);
+
+static int raid5_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+
+ return mddev_congested(mddev, bits) ||
+ md_raid5_congested(mddev, bits);
+}
/* We want read requests to align with chunks where possible,
* but write requests don't need to.
@@ -3958,7 +3978,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
const int rw = bio_data_dir(bi);
int remaining;
- if (unlikely(bio_rw_flagged(bi, BIO_RW_BARRIER))) {
+ if (unlikely(bi->bi_rw & REQ_HARDBARRIER)) {
/* Drain all pending writes. We only really need
* to ensure they have been submitted, but this is
* easier.
@@ -4075,7 +4095,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
* add failed due to overlap. Flush everything
* and wait a while
*/
- raid5_unplug_device(mddev->queue);
+ md_raid5_unplug_device(conf);
release_stripe(sh);
schedule();
goto retry;
@@ -4566,23 +4586,15 @@ raid5_show_stripe_cache_size(mddev_t *mddev, char *page)
return 0;
}
-static ssize_t
-raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
+int
+raid5_set_cache_size(mddev_t *mddev, int size)
{
raid5_conf_t *conf = mddev->private;
- unsigned long new;
int err;
- if (len >= PAGE_SIZE)
+ if (size <= 16 || size > 32768)
return -EINVAL;
- if (!conf)
- return -ENODEV;
-
- if (strict_strtoul(page, 10, &new))
- return -EINVAL;
- if (new <= 16 || new > 32768)
- return -EINVAL;
- while (new < conf->max_nr_stripes) {
+ while (size < conf->max_nr_stripes) {
if (drop_one_stripe(conf))
conf->max_nr_stripes--;
else
@@ -4591,11 +4603,32 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
err = md_allow_write(mddev);
if (err)
return err;
- while (new > conf->max_nr_stripes) {
+ while (size > conf->max_nr_stripes) {
if (grow_one_stripe(conf))
conf->max_nr_stripes++;
else break;
}
+ return 0;
+}
+EXPORT_SYMBOL(raid5_set_cache_size);
+
+static ssize_t
+raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
+{
+ raid5_conf_t *conf = mddev->private;
+ unsigned long new;
+ int err;
+
+ if (len >= PAGE_SIZE)
+ return -EINVAL;
+ if (!conf)
+ return -ENODEV;
+
+ if (strict_strtoul(page, 10, &new))
+ return -EINVAL;
+ err = raid5_set_cache_size(mddev, new);
+ if (err)
+ return err;
return len;
}
@@ -4958,7 +4991,7 @@ static int only_parity(int raid_disk, int algo, int raid_disks, int max_degraded
static int run(mddev_t *mddev)
{
raid5_conf_t *conf;
- int working_disks = 0, chunk_size;
+ int working_disks = 0;
int dirty_parity_disks = 0;
mdk_rdev_t *rdev;
sector_t reshape_offset = 0;
@@ -5144,42 +5177,47 @@ static int run(mddev_t *mddev)
"reshape");
}
- /* read-ahead size must cover two whole stripes, which is
- * 2 * (datadisks) * chunksize where 'n' is the number of raid devices
- */
- {
- int data_disks = conf->previous_raid_disks - conf->max_degraded;
- int stripe = data_disks *
- ((mddev->chunk_sectors << 9) / PAGE_SIZE);
- if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
- mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
- }
/* Ok, everything is just fine now */
if (mddev->to_remove == &raid5_attrs_group)
mddev->to_remove = NULL;
- else if (sysfs_create_group(&mddev->kobj, &raid5_attrs_group))
+ else if (mddev->kobj.sd &&
+ sysfs_create_group(&mddev->kobj, &raid5_attrs_group))
printk(KERN_WARNING
- "md/raid:%s: failed to create sysfs attributes.\n",
+ "raid5: failed to create sysfs attributes for %s\n",
mdname(mddev));
+ md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
- mddev->queue->queue_lock = &conf->device_lock;
+ plugger_init(&conf->plug, raid5_unplug);
+ mddev->plug = &conf->plug;
+ if (mddev->queue) {
+ int chunk_size;
+ /* read-ahead size must cover two whole stripes, which
+ * is 2 * (datadisks) * chunksize where 'n' is the
+ * number of raid devices
+ */
+ int data_disks = conf->previous_raid_disks - conf->max_degraded;
+ int stripe = data_disks *
+ ((mddev->chunk_sectors << 9) / PAGE_SIZE);
+ if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
+ mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
- mddev->queue->unplug_fn = raid5_unplug_device;
- mddev->queue->backing_dev_info.congested_data = mddev;
- mddev->queue->backing_dev_info.congested_fn = raid5_congested;
+ blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec);
- md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
+ mddev->queue->backing_dev_info.congested_data = mddev;
+ mddev->queue->backing_dev_info.congested_fn = raid5_congested;
+ mddev->queue->queue_lock = &conf->device_lock;
+ mddev->queue->unplug_fn = raid5_unplug_queue;
- blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec);
- chunk_size = mddev->chunk_sectors << 9;
- blk_queue_io_min(mddev->queue, chunk_size);
- blk_queue_io_opt(mddev->queue, chunk_size *
- (conf->raid_disks - conf->max_degraded));
+ chunk_size = mddev->chunk_sectors << 9;
+ blk_queue_io_min(mddev->queue, chunk_size);
+ blk_queue_io_opt(mddev->queue, chunk_size *
+ (conf->raid_disks - conf->max_degraded));
- list_for_each_entry(rdev, &mddev->disks, same_set)
- disk_stack_limits(mddev->gendisk, rdev->bdev,
- rdev->data_offset << 9);
+ list_for_each_entry(rdev, &mddev->disks, same_set)
+ disk_stack_limits(mddev->gendisk, rdev->bdev,
+ rdev->data_offset << 9);
+ }
return 0;
abort:
@@ -5200,8 +5238,9 @@ static int stop(mddev_t *mddev)
md_unregister_thread(mddev->thread);
mddev->thread = NULL;
- mddev->queue->backing_dev_info.congested_fn = NULL;
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
+ if (mddev->queue)
+ mddev->queue->backing_dev_info.congested_fn = NULL;
+ plugger_flush(&conf->plug); /* the unplug fn references 'conf'*/
free_conf(conf);
mddev->private = NULL;
mddev->to_remove = &raid5_attrs_group;
@@ -5545,10 +5584,7 @@ static int raid5_start_reshape(mddev_t *mddev)
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&mddev->kobj,
&rdev->kobj, nm))
- printk(KERN_WARNING
- "md/raid:%s: failed to create "
- " link %s\n",
- mdname(mddev), nm);
+ /* Failure here is OK */;
} else
break;
}
@@ -5603,7 +5639,7 @@ static void end_reshape(raid5_conf_t *conf)
/* read-ahead size must cover two whole stripes, which is
* 2 * (datadisks) * chunksize where 'n' is the number of raid devices
*/
- {
+ if (conf->mddev->queue) {
int data_disks = conf->raid_disks - conf->max_degraded;
int stripe = data_disks * ((conf->chunk_sectors << 9)
/ PAGE_SIZE);
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 0f86f5e..36eaed5 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -388,7 +388,7 @@ struct raid5_private_data {
* two caches.
*/
int active_name;
- char cache_name[2][20];
+ char cache_name[2][32];
struct kmem_cache *slab_cache; /* for allocating stripes */
int seq_flush, seq_write;
@@ -398,6 +398,9 @@ struct raid5_private_data {
* (fresh device added).
* Cleared when a sync completes.
*/
+
+ struct plug_handle plug;
+
/* per cpu variables */
struct raid5_percpu {
struct page *spare_page; /* Used when checking P/Q in raid6 */
@@ -497,4 +500,8 @@ static inline int algorithm_is_DDF(int layout)
{
return layout >= 8 && layout <= 10;
}
+
+extern int md_raid5_congested(mddev_t *mddev, int bits);
+extern void md_raid5_unplug_device(raid5_conf_t *conf);
+extern int raid5_set_cache_size(mddev_t *mddev, int size);
#endif
diff --git a/drivers/md/raid6algos.c b/drivers/md/raid6algos.c
deleted file mode 100644
index 1f8784b..0000000
--- a/drivers/md/raid6algos.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6algos.c
- *
- * Algorithm list and algorithm selection for RAID-6
- */
-
-#include <linux/raid/pq.h>
-#include <linux/gfp.h>
-#ifndef __KERNEL__
-#include <sys/mman.h>
-#include <stdio.h>
-#else
-#if !RAID6_USE_EMPTY_ZERO_PAGE
-/* In .bss so it's zeroed */
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
-EXPORT_SYMBOL(raid6_empty_zero_page);
-#endif
-#endif
-
-struct raid6_calls raid6_call;
-EXPORT_SYMBOL_GPL(raid6_call);
-
-const struct raid6_calls * const raid6_algos[] = {
- &raid6_intx1,
- &raid6_intx2,
- &raid6_intx4,
- &raid6_intx8,
-#if defined(__ia64__)
- &raid6_intx16,
- &raid6_intx32,
-#endif
-#if defined(__i386__) && !defined(__arch_um__)
- &raid6_mmxx1,
- &raid6_mmxx2,
- &raid6_sse1x1,
- &raid6_sse1x2,
- &raid6_sse2x1,
- &raid6_sse2x2,
-#endif
-#if defined(__x86_64__) && !defined(__arch_um__)
- &raid6_sse2x1,
- &raid6_sse2x2,
- &raid6_sse2x4,
-#endif
-#ifdef CONFIG_ALTIVEC
- &raid6_altivec1,
- &raid6_altivec2,
- &raid6_altivec4,
- &raid6_altivec8,
-#endif
- NULL
-};
-
-#ifdef __KERNEL__
-#define RAID6_TIME_JIFFIES_LG2 4
-#else
-/* Need more time to be stable in userspace */
-#define RAID6_TIME_JIFFIES_LG2 9
-#define time_before(x, y) ((x) < (y))
-#endif
-
-/* Try to pick the best algorithm */
-/* This code uses the gfmul table as convenient data set to abuse */
-
-int __init raid6_select_algo(void)
-{
- const struct raid6_calls * const * algo;
- const struct raid6_calls * best;
- char *syndromes;
- void *dptrs[(65536/PAGE_SIZE)+2];
- int i, disks;
- unsigned long perf, bestperf;
- int bestprefer;
- unsigned long j0, j1;
-
- disks = (65536/PAGE_SIZE)+2;
- for ( i = 0 ; i < disks-2 ; i++ ) {
- dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i;
- }
-
- /* Normal code - use a 2-page allocation to avoid D$ conflict */
- syndromes = (void *) __get_free_pages(GFP_KERNEL, 1);
-
- if ( !syndromes ) {
- printk("raid6: Yikes! No memory available.\n");
- return -ENOMEM;
- }
-
- dptrs[disks-2] = syndromes;
- dptrs[disks-1] = syndromes + PAGE_SIZE;
-
- bestperf = 0; bestprefer = 0; best = NULL;
-
- for ( algo = raid6_algos ; *algo ; algo++ ) {
- if ( !(*algo)->valid || (*algo)->valid() ) {
- perf = 0;
-
- preempt_disable();
- j0 = jiffies;
- while ( (j1 = jiffies) == j0 )
- cpu_relax();
- while (time_before(jiffies,
- j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
- (*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs);
- perf++;
- }
- preempt_enable();
-
- if ( (*algo)->prefer > bestprefer ||
- ((*algo)->prefer == bestprefer &&
- perf > bestperf) ) {
- best = *algo;
- bestprefer = best->prefer;
- bestperf = perf;
- }
- printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,
- (perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
- }
- }
-
- if (best) {
- printk("raid6: using algorithm %s (%ld MB/s)\n",
- best->name,
- (bestperf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
- raid6_call = *best;
- } else
- printk("raid6: Yikes! No algorithm found!\n");
-
- free_pages((unsigned long)syndromes, 1);
-
- return best ? 0 : -EINVAL;
-}
-
-static void raid6_exit(void)
-{
- do { } while (0);
-}
-
-subsys_initcall(raid6_select_algo);
-module_exit(raid6_exit);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
diff --git a/drivers/md/raid6altivec.uc b/drivers/md/raid6altivec.uc
deleted file mode 100644
index 2654d5c..0000000
--- a/drivers/md/raid6altivec.uc
+++ /dev/null
@@ -1,130 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6altivec$#.c
- *
- * $#-way unrolled portable integer math RAID-6 instruction set
- *
- * This file is postprocessed using unroll.awk
- *
- * <benh> hpa: in process,
- * you can just "steal" the vec unit with enable_kernel_altivec() (but
- * bracked this with preempt_disable/enable or in a lock)
- */
-
-#include <linux/raid/pq.h>
-
-#ifdef CONFIG_ALTIVEC
-
-#include <altivec.h>
-#ifdef __KERNEL__
-# include <asm/system.h>
-# include <asm/cputable.h>
-#endif
-
-/*
- * This is the C data type to use. We use a vector of
- * signed char so vec_cmpgt() will generate the right
- * instruction.
- */
-
-typedef vector signed char unative_t;
-
-#define NBYTES(x) ((vector signed char) {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x})
-#define NSIZE sizeof(unative_t)
-
-/*
- * The SHLBYTE() operation shifts each byte left by 1, *not*
- * rolling over into the next byte
- */
-static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
-{
- return vec_add(v,v);
-}
-
-/*
- * The MASK() operation returns 0xFF in any byte for which the high
- * bit is 1, 0x00 for any byte for which the high bit is 0.
- */
-static inline __attribute_const__ unative_t MASK(unative_t v)
-{
- unative_t zv = NBYTES(0);
-
- /* vec_cmpgt returns a vector bool char; thus the need for the cast */
- return (unative_t)vec_cmpgt(zv, v);
-}
-
-
-/* This is noinline to make damned sure that gcc doesn't move any of the
- Altivec code around the enable/disable code */
-static void noinline
-raid6_altivec$#_gen_syndrome_real(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
- unative_t x1d = NBYTES(0x1d);
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
- wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
- wp$$ = vec_xor(wp$$, wd$$);
- w2$$ = MASK(wq$$);
- w1$$ = SHLBYTE(wq$$);
- w2$$ = vec_and(w2$$, x1d);
- w1$$ = vec_xor(w1$$, w2$$);
- wq$$ = vec_xor(w1$$, wd$$);
- }
- *(unative_t *)&p[d+NSIZE*$$] = wp$$;
- *(unative_t *)&q[d+NSIZE*$$] = wq$$;
- }
-}
-
-static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- preempt_disable();
- enable_kernel_altivec();
-
- raid6_altivec$#_gen_syndrome_real(disks, bytes, ptrs);
-
- preempt_enable();
-}
-
-int raid6_have_altivec(void);
-#if $# == 1
-int raid6_have_altivec(void)
-{
- /* This assumes either all CPUs have Altivec or none does */
-# ifdef __KERNEL__
- return cpu_has_feature(CPU_FTR_ALTIVEC);
-# else
- return 1;
-# endif
-}
-#endif
-
-const struct raid6_calls raid6_altivec$# = {
- raid6_altivec$#_gen_syndrome,
- raid6_have_altivec,
- "altivecx$#",
- 0
-};
-
-#endif /* CONFIG_ALTIVEC */
diff --git a/drivers/md/raid6int.uc b/drivers/md/raid6int.uc
deleted file mode 100644
index d1e276a..0000000
--- a/drivers/md/raid6int.uc
+++ /dev/null
@@ -1,117 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6int$#.c
- *
- * $#-way unrolled portable integer math RAID-6 instruction set
- *
- * This file is postprocessed using unroll.awk
- */
-
-#include <linux/raid/pq.h>
-
-/*
- * This is the C data type to use
- */
-
-/* Change this from BITS_PER_LONG if there is something better... */
-#if BITS_PER_LONG == 64
-# define NBYTES(x) ((x) * 0x0101010101010101UL)
-# define NSIZE 8
-# define NSHIFT 3
-# define NSTRING "64"
-typedef u64 unative_t;
-#else
-# define NBYTES(x) ((x) * 0x01010101U)
-# define NSIZE 4
-# define NSHIFT 2
-# define NSTRING "32"
-typedef u32 unative_t;
-#endif
-
-
-
-/*
- * IA-64 wants insane amounts of unrolling. On other architectures that
- * is just a waste of space.
- */
-#if ($# <= 8) || defined(__ia64__)
-
-
-/*
- * These sub-operations are separate inlines since they can sometimes be
- * specially optimized using architecture-specific hacks.
- */
-
-/*
- * The SHLBYTE() operation shifts each byte left by 1, *not*
- * rolling over into the next byte
- */
-static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
-{
- unative_t vv;
-
- vv = (v << 1) & NBYTES(0xfe);
- return vv;
-}
-
-/*
- * The MASK() operation returns 0xFF in any byte for which the high
- * bit is 1, 0x00 for any byte for which the high bit is 0.
- */
-static inline __attribute_const__ unative_t MASK(unative_t v)
-{
- unative_t vv;
-
- vv = v & NBYTES(0x80);
- vv = (vv << 1) - (vv >> 7); /* Overflow on the top bit is OK */
- return vv;
-}
-
-
-static void raid6_int$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
- wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
- wp$$ ^= wd$$;
- w2$$ = MASK(wq$$);
- w1$$ = SHLBYTE(wq$$);
- w2$$ &= NBYTES(0x1d);
- w1$$ ^= w2$$;
- wq$$ = w1$$ ^ wd$$;
- }
- *(unative_t *)&p[d+NSIZE*$$] = wp$$;
- *(unative_t *)&q[d+NSIZE*$$] = wq$$;
- }
-}
-
-const struct raid6_calls raid6_intx$# = {
- raid6_int$#_gen_syndrome,
- NULL, /* always valid */
- "int" NSTRING "x$#",
- 0
-};
-
-#endif
diff --git a/drivers/md/raid6mmx.c b/drivers/md/raid6mmx.c
deleted file mode 100644
index e7f6c13..0000000
--- a/drivers/md/raid6mmx.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6mmx.c
- *
- * MMX implementation of RAID-6 syndrome functions
- */
-
-#if defined(__i386__) && !defined(__arch_um__)
-
-#include <linux/raid/pq.h>
-#include "raid6x86.h"
-
-/* Shared with raid6sse1.c */
-const struct raid6_mmx_constants {
- u64 x1d;
-} raid6_mmx_constants = {
- 0x1d1d1d1d1d1d1d1dULL,
-};
-
-static int raid6_have_mmx(void)
-{
- /* Not really "boot_cpu" but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX);
-}
-
-/*
- * Plain MMX implementation
- */
-static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 8 ) {
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm6,%mm2");
- asm volatile("pxor %mm6,%mm4");
- }
- asm volatile("movq %%mm2,%0" : "=m" (p[d]));
- asm volatile("pxor %mm2,%mm2");
- asm volatile("movq %%mm4,%0" : "=m" (q[d]));
- asm volatile("pxor %mm4,%mm4");
- }
-
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_mmxx1 = {
- raid6_mmx1_gen_syndrome,
- raid6_have_mmx,
- "mmxx1",
- 0
-};
-
-/*
- * Unrolled-by-2 MMX implementation
- */
-static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
- asm volatile("pxor %mm7,%mm7"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 16 ) {
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8]));
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- asm volatile("movq %mm3,%mm6"); /* Q[1] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("pcmpgtb %mm6,%mm7");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("paddb %mm6,%mm6");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pand %mm0,%mm7");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
- asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
- asm volatile("pxor %mm5,%mm2");
- asm volatile("pxor %mm7,%mm3");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm7,%mm7");
- }
- asm volatile("movq %%mm2,%0" : "=m" (p[d]));
- asm volatile("movq %%mm3,%0" : "=m" (p[d+8]));
- asm volatile("movq %%mm4,%0" : "=m" (q[d]));
- asm volatile("movq %%mm6,%0" : "=m" (q[d+8]));
- }
-
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_mmxx2 = {
- raid6_mmx2_gen_syndrome,
- raid6_have_mmx,
- "mmxx2",
- 0
-};
-
-#endif
diff --git a/drivers/md/raid6recov.c b/drivers/md/raid6recov.c
deleted file mode 100644
index 2609f00..0000000
--- a/drivers/md/raid6recov.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6recov.c
- *
- * RAID-6 data recovery in dual failure mode. In single failure mode,
- * use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct
- * the syndrome.)
- */
-
-#include <linux/raid/pq.h>
-
-/* Recover two failed data blocks. */
-void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
- void **ptrs)
-{
- u8 *p, *q, *dp, *dq;
- u8 px, qx, db;
- const u8 *pbmul; /* P multiplier table for B data */
- const u8 *qmul; /* Q multiplier table (for both) */
-
- p = (u8 *)ptrs[disks-2];
- q = (u8 *)ptrs[disks-1];
-
- /* Compute syndrome with zero for the missing data pages
- Use the dead data pages as temporary storage for
- delta p and delta q */
- dp = (u8 *)ptrs[faila];
- ptrs[faila] = (void *)raid6_empty_zero_page;
- ptrs[disks-2] = dp;
- dq = (u8 *)ptrs[failb];
- ptrs[failb] = (void *)raid6_empty_zero_page;
- ptrs[disks-1] = dq;
-
- raid6_call.gen_syndrome(disks, bytes, ptrs);
-
- /* Restore pointer table */
- ptrs[faila] = dp;
- ptrs[failb] = dq;
- ptrs[disks-2] = p;
- ptrs[disks-1] = q;
-
- /* Now, pick the proper data tables */
- pbmul = raid6_gfmul[raid6_gfexi[failb-faila]];
- qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]];
-
- /* Now do it... */
- while ( bytes-- ) {
- px = *p ^ *dp;
- qx = qmul[*q ^ *dq];
- *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
- *dp++ = db ^ px; /* Reconstructed A */
- p++; q++;
- }
-}
-EXPORT_SYMBOL_GPL(raid6_2data_recov);
-
-/* Recover failure of one data block plus the P block */
-void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs)
-{
- u8 *p, *q, *dq;
- const u8 *qmul; /* Q multiplier table */
-
- p = (u8 *)ptrs[disks-2];
- q = (u8 *)ptrs[disks-1];
-
- /* Compute syndrome with zero for the missing data page
- Use the dead data page as temporary storage for delta q */
- dq = (u8 *)ptrs[faila];
- ptrs[faila] = (void *)raid6_empty_zero_page;
- ptrs[disks-1] = dq;
-
- raid6_call.gen_syndrome(disks, bytes, ptrs);
-
- /* Restore pointer table */
- ptrs[faila] = dq;
- ptrs[disks-1] = q;
-
- /* Now, pick the proper data tables */
- qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]];
-
- /* Now do it... */
- while ( bytes-- ) {
- *p++ ^= *dq = qmul[*q ^ *dq];
- q++; dq++;
- }
-}
-EXPORT_SYMBOL_GPL(raid6_datap_recov);
-
-#ifndef __KERNEL__
-/* Testing only */
-
-/* Recover two failed blocks. */
-void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
-{
- if ( faila > failb ) {
- int tmp = faila;
- faila = failb;
- failb = tmp;
- }
-
- if ( failb == disks-1 ) {
- if ( faila == disks-2 ) {
- /* P+Q failure. Just rebuild the syndrome. */
- raid6_call.gen_syndrome(disks, bytes, ptrs);
- } else {
- /* data+Q failure. Reconstruct data from P,
- then rebuild syndrome. */
- /* NOT IMPLEMENTED - equivalent to RAID-5 */
- }
- } else {
- if ( failb == disks-2 ) {
- /* data+P failure. */
- raid6_datap_recov(disks, bytes, faila, ptrs);
- } else {
- /* data+data failure. */
- raid6_2data_recov(disks, bytes, faila, failb, ptrs);
- }
- }
-}
-
-#endif
diff --git a/drivers/md/raid6sse1.c b/drivers/md/raid6sse1.c
deleted file mode 100644
index b274dd5..0000000
--- a/drivers/md/raid6sse1.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6sse1.c
- *
- * SSE-1/MMXEXT implementation of RAID-6 syndrome functions
- *
- * This is really an MMX implementation, but it requires SSE-1 or
- * AMD MMXEXT for prefetch support and a few other features. The
- * support for nontemporal memory accesses is enough to make this
- * worthwhile as a separate implementation.
- */
-
-#if defined(__i386__) && !defined(__arch_um__)
-
-#include <linux/raid/pq.h>
-#include "raid6x86.h"
-
-/* Defined in raid6mmx.c */
-extern const struct raid6_mmx_constants {
- u64 x1d;
-} raid6_mmx_constants;
-
-static int raid6_have_sse1_or_mmxext(void)
-{
- /* Not really boot_cpu but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX) &&
- (boot_cpu_has(X86_FEATURE_XMM) ||
- boot_cpu_has(X86_FEATURE_MMXEXT));
-}
-
-/*
- * Plain SSE1 implementation
- */
-static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 8 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- asm volatile("movq %0,%%mm6" : : "m" (dptr[z0-1][d]));
- for ( z = z0-2 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm6,%mm2");
- asm volatile("pxor %mm6,%mm4");
- asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
- }
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm6,%mm2");
- asm volatile("pxor %mm6,%mm4");
-
- asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
- asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse1x1 = {
- raid6_sse11_gen_syndrome,
- raid6_have_sse1_or_mmxext,
- "sse1x1",
- 1 /* Has cache hints */
-};
-
-/*
- * Unrolled-by-2 SSE1 implementation
- */
-static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
- asm volatile("pxor %mm7,%mm7"); /* Zero temp */
-
- /* We uniformly assume a single prefetch covers at least 16 bytes */
- for ( d = 0 ; d < bytes ; d += 16 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8])); /* P[1] */
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- asm volatile("movq %mm3,%mm6"); /* Q[1] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("pcmpgtb %mm6,%mm7");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("paddb %mm6,%mm6");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pand %mm0,%mm7");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
- asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
- asm volatile("pxor %mm5,%mm2");
- asm volatile("pxor %mm7,%mm3");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm7,%mm7");
- }
- asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
- asm volatile("movntq %%mm3,%0" : "=m" (p[d+8]));
- asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
- asm volatile("movntq %%mm6,%0" : "=m" (q[d+8]));
- }
-
- asm volatile("sfence" : :: "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse1x2 = {
- raid6_sse12_gen_syndrome,
- raid6_have_sse1_or_mmxext,
- "sse1x2",
- 1 /* Has cache hints */
-};
-
-#endif
diff --git a/drivers/md/raid6sse2.c b/drivers/md/raid6sse2.c
deleted file mode 100644
index 6ed6c6c..0000000
--- a/drivers/md/raid6sse2.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6sse2.c
- *
- * SSE-2 implementation of RAID-6 syndrome functions
- *
- */
-
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
-
-#include <linux/raid/pq.h>
-#include "raid6x86.h"
-
-static const struct raid6_sse_constants {
- u64 x1d[2];
-} raid6_sse_constants __attribute__((aligned(16))) = {
- { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL },
-};
-
-static int raid6_have_sse2(void)
-{
- /* Not really boot_cpu but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX) &&
- boot_cpu_has(X86_FEATURE_FXSR) &&
- boot_cpu_has(X86_FEATURE_XMM) &&
- boot_cpu_has(X86_FEATURE_XMM2);
-}
-
-/*
- * Plain SSE2 implementation
- */
-static void raid6_sse21_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
- asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 16 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
- asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
- asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z0-1][d]));
- for ( z = z0-2 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm6,%xmm2");
- asm volatile("pxor %xmm6,%xmm4");
- asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z][d]));
- }
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm6,%xmm2");
- asm volatile("pxor %xmm6,%xmm4");
-
- asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
- asm volatile("pxor %xmm2,%xmm2");
- asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
- asm volatile("pxor %xmm4,%xmm4");
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse2x1 = {
- raid6_sse21_gen_syndrome,
- raid6_have_sse2,
- "sse2x1",
- 1 /* Has cache hints */
-};
-
-/*
- * Unrolled-by-2 SSE2 implementation
- */
-static void raid6_sse22_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
- asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
- asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
-
- /* We uniformly assume a single prefetch covers at least 32 bytes */
- for ( d = 0 ; d < bytes ; d += 32 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movdqa %0,%%xmm3" : : "m" (dptr[z0][d+16])); /* P[1] */
- asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
- asm volatile("movdqa %xmm3,%xmm6"); /* Q[1] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("pcmpgtb %xmm6,%xmm7");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("paddb %xmm6,%xmm6");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pand %xmm0,%xmm7");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("movdqa %0,%%xmm5" : : "m" (dptr[z][d]));
- asm volatile("movdqa %0,%%xmm7" : : "m" (dptr[z][d+16]));
- asm volatile("pxor %xmm5,%xmm2");
- asm volatile("pxor %xmm7,%xmm3");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm7,%xmm7");
- }
- asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
- asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
- asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
- asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse2x2 = {
- raid6_sse22_gen_syndrome,
- raid6_have_sse2,
- "sse2x2",
- 1 /* Has cache hints */
-};
-
-#endif
-
-#if defined(__x86_64__) && !defined(__arch_um__)
-
-/*
- * Unrolled-by-4 SSE2 implementation
- */
-static void raid6_sse24_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movdqa %0,%%xmm0" :: "m" (raid6_sse_constants.x1d[0]));
- asm volatile("pxor %xmm2,%xmm2"); /* P[0] */
- asm volatile("pxor %xmm3,%xmm3"); /* P[1] */
- asm volatile("pxor %xmm4,%xmm4"); /* Q[0] */
- asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
- asm volatile("pxor %xmm6,%xmm6"); /* Q[1] */
- asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
- asm volatile("pxor %xmm10,%xmm10"); /* P[2] */
- asm volatile("pxor %xmm11,%xmm11"); /* P[3] */
- asm volatile("pxor %xmm12,%xmm12"); /* Q[2] */
- asm volatile("pxor %xmm13,%xmm13"); /* Zero temp */
- asm volatile("pxor %xmm14,%xmm14"); /* Q[3] */
- asm volatile("pxor %xmm15,%xmm15"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 64 ) {
- for ( z = z0 ; z >= 0 ; z-- ) {
- /* The second prefetch seems to improve performance... */
- asm volatile("prefetchnta %0" :: "m" (dptr[z][d]));
- asm volatile("prefetchnta %0" :: "m" (dptr[z][d+32]));
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("pcmpgtb %xmm6,%xmm7");
- asm volatile("pcmpgtb %xmm12,%xmm13");
- asm volatile("pcmpgtb %xmm14,%xmm15");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("paddb %xmm6,%xmm6");
- asm volatile("paddb %xmm12,%xmm12");
- asm volatile("paddb %xmm14,%xmm14");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pand %xmm0,%xmm7");
- asm volatile("pand %xmm0,%xmm13");
- asm volatile("pand %xmm0,%xmm15");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("pxor %xmm13,%xmm12");
- asm volatile("pxor %xmm15,%xmm14");
- asm volatile("movdqa %0,%%xmm5" :: "m" (dptr[z][d]));
- asm volatile("movdqa %0,%%xmm7" :: "m" (dptr[z][d+16]));
- asm volatile("movdqa %0,%%xmm13" :: "m" (dptr[z][d+32]));
- asm volatile("movdqa %0,%%xmm15" :: "m" (dptr[z][d+48]));
- asm volatile("pxor %xmm5,%xmm2");
- asm volatile("pxor %xmm7,%xmm3");
- asm volatile("pxor %xmm13,%xmm10");
- asm volatile("pxor %xmm15,%xmm11");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("pxor %xmm13,%xmm12");
- asm volatile("pxor %xmm15,%xmm14");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm7,%xmm7");
- asm volatile("pxor %xmm13,%xmm13");
- asm volatile("pxor %xmm15,%xmm15");
- }
- asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
- asm volatile("pxor %xmm2,%xmm2");
- asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
- asm volatile("pxor %xmm3,%xmm3");
- asm volatile("movntdq %%xmm10,%0" : "=m" (p[d+32]));
- asm volatile("pxor %xmm10,%xmm10");
- asm volatile("movntdq %%xmm11,%0" : "=m" (p[d+48]));
- asm volatile("pxor %xmm11,%xmm11");
- asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
- asm volatile("pxor %xmm4,%xmm4");
- asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
- asm volatile("pxor %xmm6,%xmm6");
- asm volatile("movntdq %%xmm12,%0" : "=m" (q[d+32]));
- asm volatile("pxor %xmm12,%xmm12");
- asm volatile("movntdq %%xmm14,%0" : "=m" (q[d+48]));
- asm volatile("pxor %xmm14,%xmm14");
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse2x4 = {
- raid6_sse24_gen_syndrome,
- raid6_have_sse2,
- "sse2x4",
- 1 /* Has cache hints */
-};
-
-#endif
diff --git a/drivers/md/raid6test/Makefile b/drivers/md/raid6test/Makefile
deleted file mode 100644
index 2874cbe..0000000
--- a/drivers/md/raid6test/Makefile
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# This is a simple Makefile to test some of the RAID-6 code
-# from userspace.
-#
-
-CC = gcc
-OPTFLAGS = -O2 # Adjust as desired
-CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS)
-LD = ld
-AWK = awk
-AR = ar
-RANLIB = ranlib
-
-.c.o:
- $(CC) $(CFLAGS) -c -o $@ $<
-
-%.c: ../%.c
- cp -f $< $@
-
-%.uc: ../%.uc
- cp -f $< $@
-
-all: raid6.a raid6test
-
-raid6.a: raid6int1.o raid6int2.o raid6int4.o raid6int8.o raid6int16.o \
- raid6int32.o \
- raid6mmx.o raid6sse1.o raid6sse2.o \
- raid6altivec1.o raid6altivec2.o raid6altivec4.o raid6altivec8.o \
- raid6recov.o raid6algos.o \
- raid6tables.o
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
-
-raid6test: test.c raid6.a
- $(CC) $(CFLAGS) -o raid6test $^
-
-raid6altivec1.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < raid6altivec.uc > $@
-
-raid6altivec2.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < raid6altivec.uc > $@
-
-raid6altivec4.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < raid6altivec.uc > $@
-
-raid6altivec8.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < raid6altivec.uc > $@
-
-raid6int1.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < raid6int.uc > $@
-
-raid6int2.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < raid6int.uc > $@
-
-raid6int4.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < raid6int.uc > $@
-
-raid6int8.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < raid6int.uc > $@
-
-raid6int16.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=16 < raid6int.uc > $@
-
-raid6int32.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=32 < raid6int.uc > $@
-
-raid6tables.c: mktables
- ./mktables > raid6tables.c
-
-clean:
- rm -f *.o *.a mktables mktables.c raid6int.uc raid6*.c raid6test
-
-spotless: clean
- rm -f *~
diff --git a/drivers/md/raid6test/test.c b/drivers/md/raid6test/test.c
deleted file mode 100644
index 7a93031..0000000
--- a/drivers/md/raid6test/test.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
- *
- * This file is part of the Linux kernel, and is made available under
- * the terms of the GNU General Public License version 2 or (at your
- * option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6test.c
- *
- * Test RAID-6 recovery with various algorithms
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <linux/raid/pq.h>
-
-#define NDISKS 16 /* Including P and Q */
-
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
-struct raid6_calls raid6_call;
-
-char *dataptrs[NDISKS];
-char data[NDISKS][PAGE_SIZE];
-char recovi[PAGE_SIZE], recovj[PAGE_SIZE];
-
-static void makedata(void)
-{
- int i, j;
-
- for (i = 0; i < NDISKS; i++) {
- for (j = 0; j < PAGE_SIZE; j++)
- data[i][j] = rand();
-
- dataptrs[i] = data[i];
- }
-}
-
-static char disk_type(int d)
-{
- switch (d) {
- case NDISKS-2:
- return 'P';
- case NDISKS-1:
- return 'Q';
- default:
- return 'D';
- }
-}
-
-static int test_disks(int i, int j)
-{
- int erra, errb;
-
- memset(recovi, 0xf0, PAGE_SIZE);
- memset(recovj, 0xba, PAGE_SIZE);
-
- dataptrs[i] = recovi;
- dataptrs[j] = recovj;
-
- raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs);
-
- erra = memcmp(data[i], recovi, PAGE_SIZE);
- errb = memcmp(data[j], recovj, PAGE_SIZE);
-
- if (i < NDISKS-2 && j == NDISKS-1) {
- /* We don't implement the DQ failure scenario, since it's
- equivalent to a RAID-5 failure (XOR, then recompute Q) */
- erra = errb = 0;
- } else {
- printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n",
- raid6_call.name,
- i, disk_type(i),
- j, disk_type(j),
- (!erra && !errb) ? "OK" :
- !erra ? "ERRB" :
- !errb ? "ERRA" : "ERRAB");
- }
-
- dataptrs[i] = data[i];
- dataptrs[j] = data[j];
-
- return erra || errb;
-}
-
-int main(int argc, char *argv[])
-{
- const struct raid6_calls *const *algo;
- int i, j;
- int err = 0;
-
- makedata();
-
- for (algo = raid6_algos; *algo; algo++) {
- if (!(*algo)->valid || (*algo)->valid()) {
- raid6_call = **algo;
-
- /* Nuke syndromes */
- memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
-
- /* Generate assumed good syndrome */
- raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
- (void **)&dataptrs);
-
- for (i = 0; i < NDISKS-1; i++)
- for (j = i+1; j < NDISKS; j++)
- err += test_disks(i, j);
- }
- printf("\n");
- }
-
- printf("\n");
- /* Pick the best algorithm test */
- raid6_select_algo();
-
- if (err)
- printf("\n*** ERRORS FOUND ***\n");
-
- return err;
-}
diff --git a/drivers/md/raid6x86.h b/drivers/md/raid6x86.h
deleted file mode 100644
index 4c22c15..0000000
--- a/drivers/md/raid6x86.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6x86.h
- *
- * Definitions common to x86 and x86-64 RAID-6 code only
- */
-
-#ifndef LINUX_RAID_RAID6X86_H
-#define LINUX_RAID_RAID6X86_H
-
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
-
-#ifdef __KERNEL__ /* Real code */
-
-#include <asm/i387.h>
-
-#else /* Dummy code for user space testing */
-
-static inline void kernel_fpu_begin(void)
-{
-}
-
-static inline void kernel_fpu_end(void)
-{
-}
-
-#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
-#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions
- * (fast save and restore) */
-#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
-#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
-#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
-
-/* Should work well enough on modern CPUs for testing */
-static inline int boot_cpu_has(int flag)
-{
- u32 eax = (flag >> 5) ? 0x80000001 : 1;
- u32 edx;
-
- asm volatile("cpuid"
- : "+a" (eax), "=d" (edx)
- : : "ecx", "ebx");
-
- return (edx >> (flag & 31)) & 1;
-}
-
-#endif /* ndef __KERNEL__ */
-
-#endif
-#endif
diff --git a/drivers/md/unroll.awk b/drivers/md/unroll.awk
deleted file mode 100644
index c6aa036..0000000
--- a/drivers/md/unroll.awk
+++ /dev/null
@@ -1,20 +0,0 @@
-
-# This filter requires one command line option of form -vN=n
-# where n must be a decimal number.
-#
-# Repeat each input line containing $$ n times, replacing $$ with 0...n-1.
-# Replace each $# with n, and each $* with a single $.
-
-BEGIN {
- n = N + 0
-}
-{
- if (/\$\$/) { rep = n } else { rep = 1 }
- for (i = 0; i < rep; ++i) {
- tmp = $0
- gsub(/\$\$/, i, tmp)
- gsub(/\$\#/, n, tmp)
- gsub(/\$\*/, "$", tmp)
- print tmp
- }
-}
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index 999a825..490c57c 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -1,13 +1,22 @@
-config IR_CORE
- tristate
+menuconfig IR_CORE
+ tristate "Infrared remote controller adapters"
depends on INPUT
default INPUT
+ ---help---
+ Enable support for Remote Controllers on Linux. This is
+ needed in order to support several video capture adapters.
+
+ Enable this option if you have a video capture board even
+ if you don't need IR, as otherwise, you may not be able to
+ compile the driver for your adapter.
config VIDEO_IR
tristate
depends on IR_CORE
default IR_CORE
+if IR_CORE
+
config LIRC
tristate
default y
@@ -16,7 +25,7 @@ config LIRC
Enable this option to build the Linux Infrared Remote
Control (LIRC) core device interface driver. The LIRC
interface passes raw IR to and from userspace, where the
- LIRC daemon handles protocol decoding for IR reception ann
+ LIRC daemon handles protocol decoding for IR reception and
encoding for IR transmitting (aka "blasting").
source "drivers/media/IR/keymaps/Kconfig"
@@ -103,3 +112,31 @@ config IR_MCEUSB
To compile this driver as a module, choose M here: the
module will be called mceusb.
+
+config IR_ENE
+ tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
+ depends on PNP
+ depends on IR_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receiver
+ /transciever made by ENE.
+
+ You can see if you have it by looking at lspnp output.
+ Output should include ENE0100 ENE0200 or something similiar.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ene_ir.
+
+config IR_STREAMZAP
+ tristate "Streamzap PC Remote IR Receiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on IR_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use a Streamzap PC Remote
+ Infrared Receiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called streamzap.
+
+endif #IR_CORE
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index 2ae4f3a..5367683 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
diff --git a/drivers/media/IR/ene_ir.c b/drivers/media/IR/ene_ir.c
new file mode 100644
index 0000000..5447750
--- /dev/null
+++ b/drivers/media/IR/ene_ir.c
@@ -0,0 +1,1023 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@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/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+#include <media/ir-common.h>
+#include "ene_ir.h"
+
+
+static int sample_period = -1;
+static int enable_idle = 1;
+static int input = 1;
+static int debug;
+static int txsim;
+
+static int ene_irq_status(struct ene_device *dev);
+
+/* read a hardware register */
+static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+{
+ u8 retval;
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ retval = inb(dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x == %02x", reg, retval);
+ return retval;
+}
+
+/* write a hardware register */
+static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
+{
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ outb(value, dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x <- %02x", reg, value);
+}
+
+/* change specific bits in hardware register */
+static void ene_hw_write_reg_mask(struct ene_device *dev,
+ u16 reg, u8 value, u8 mask)
+{
+ u8 regvalue;
+
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+
+ regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
+ regvalue |= (value & mask);
+ outb(regvalue, dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
+}
+
+/* detect hardware features */
+static int ene_hw_detect(struct ene_device *dev)
+{
+ u8 chip_major, chip_minor;
+ u8 hw_revision, old_ver;
+ u8 tmp;
+ u8 fw_capabilities;
+ int pll_freq;
+
+ tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
+ ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
+
+ chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
+ chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
+
+ ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
+ hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
+ old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
+
+ pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
+ (ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
+
+ if (pll_freq != 1000)
+ dev->rx_period_adjust = 4;
+ else
+ dev->rx_period_adjust = 2;
+
+
+ ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
+
+ if (hw_revision == 0xFF) {
+
+ ene_printk(KERN_WARNING, "device seems to be disabled\n");
+ ene_printk(KERN_WARNING,
+ "send a mail to lirc-list@lists.sourceforge.net\n");
+ ene_printk(KERN_WARNING, "please attach output of acpidump\n");
+ return -ENODEV;
+ }
+
+ if (chip_major == 0x33) {
+ ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n");
+ return -ENODEV;
+ }
+
+ if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
+ dev->hw_revision = ENE_HW_C;
+ } else if (old_ver == 0x24 && hw_revision == 0xC0) {
+ dev->hw_revision = ENE_HW_B;
+ ene_printk(KERN_NOTICE, "KB3926B detected\n");
+ } else {
+ dev->hw_revision = ENE_HW_D;
+ ene_printk(KERN_WARNING,
+ "unknown ENE chip detected, assuming KB3926D\n");
+ ene_printk(KERN_WARNING,
+ "driver support might be not complete");
+
+ }
+
+ ene_printk(KERN_DEBUG,
+ "chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+ chip_major, chip_minor, old_ver, hw_revision);
+
+ /* detect features hardware supports */
+ if (dev->hw_revision < ENE_HW_C)
+ return 0;
+
+ fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
+ ene_dbg("Firmware capabilities: %02x", fw_capabilities);
+
+ dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
+ dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
+
+ dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
+ (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN);
+
+ ene_printk(KERN_NOTICE, "hardware features:\n");
+ ene_printk(KERN_NOTICE,
+ "learning and transmit %s, gpio40_learn %s, fan_in %s\n",
+ dev->hw_learning_and_tx_capable ? "on" : "off",
+ dev->hw_gpio40_learning ? "on" : "off",
+ dev->hw_fan_as_normal_input ? "on" : "off");
+
+ if (dev->hw_learning_and_tx_capable) {
+ ene_printk(KERN_WARNING,
+ "Device supports transmitting, but that support is\n");
+ ene_printk(KERN_WARNING,
+ "lightly tested. Please test it and mail\n");
+ ene_printk(KERN_WARNING,
+ "lirc-list@lists.sourceforge.net\n");
+ }
+ return 0;
+}
+
+/* this enables/disables IR input via gpio40*/
+static void ene_enable_gpio40_receive(struct ene_device *dev, int enable)
+{
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ?
+ 0 : ENE_CIR_CONF2_GPIO40DIS,
+ ENE_CIR_CONF2_GPIO40DIS);
+}
+
+/* this enables/disables IR via standard input */
+static void ene_enable_normal_receive(struct ene_device *dev, int enable)
+{
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0);
+}
+
+/* this enables/disables IR input via unused fan tachtometer input */
+static void ene_enable_fan_receive(struct ene_device *dev, int enable)
+{
+ if (!enable)
+ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
+ else {
+ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+ ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
+ }
+ dev->rx_fan_input_inuse = enable;
+}
+
+
+/* Sense current received carrier */
+static int ene_rx_sense_carrier(struct ene_device *dev)
+{
+ int period = ene_hw_read_reg(dev, ENE_RX_CARRIER);
+ int carrier;
+ ene_dbg("RX: hardware carrier period = %02x", period);
+
+ if (!(period & ENE_RX_CARRIER_VALID))
+ return 0;
+
+ period &= ~ENE_RX_CARRIER_VALID;
+
+ if (!period)
+ return 0;
+
+ carrier = 2000000 / period;
+ ene_dbg("RX: sensed carrier = %d Hz", carrier);
+ return carrier;
+}
+
+/* determine which input to use*/
+static void ene_rx_set_inputs(struct ene_device *dev)
+{
+ int learning_mode = dev->learning_enabled;
+
+ ene_dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+ ene_enable_normal_receive(dev, 1);
+
+ /* old hardware doesn't support learning mode for sure */
+ if (dev->hw_revision <= ENE_HW_B)
+ return;
+
+ /* receiver not learning capable, still set gpio40 correctly */
+ if (!dev->hw_learning_and_tx_capable) {
+ ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning);
+ return;
+ }
+
+ /* enable learning mode */
+ if (learning_mode) {
+ ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning);
+
+ /* fan input is not used for learning */
+ if (dev->hw_fan_as_normal_input)
+ ene_enable_fan_receive(dev, 0);
+
+ /* disable learning mode */
+ } else {
+ if (dev->hw_fan_as_normal_input) {
+ ene_enable_fan_receive(dev, 1);
+ ene_enable_normal_receive(dev, 0);
+ } else
+ ene_enable_gpio40_receive(dev,
+ !dev->hw_gpio40_learning);
+ }
+
+ /* set few additional settings for this mode */
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ?
+ ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
+
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ?
+ ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
+
+ if (dev->rx_fan_input_inuse) {
+ dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000;
+
+ dev->props->timeout =
+ ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000;
+ } else {
+ dev->props->rx_resolution = sample_period * 1000;
+ dev->props->timeout = ENE_MAXGAP * 1000;
+ }
+}
+
+/* Enable the device for receive */
+static void ene_rx_enable(struct ene_device *dev)
+{
+ u8 reg_value;
+
+ if (dev->hw_revision < ENE_HW_C) {
+ ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
+ ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
+ } else {
+ reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
+ reg_value |= ENEC_IRQ_UNK_EN;
+ reg_value &= ~ENEC_IRQ_STATUS;
+ reg_value |= (dev->irq & ENEC_IRQ_MASK);
+ ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
+ ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
+ }
+
+ ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
+ ene_rx_set_inputs(dev);
+
+ /* set sampling period */
+ ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
+
+ /* ack any pending irqs - just in case */
+ ene_irq_status(dev);
+
+ /* enable firmware bits */
+ ene_hw_write_reg_mask(dev, ENE_FW1,
+ ENE_FW1_ENABLE | ENE_FW1_IRQ,
+ ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+ /* enter idle mode */
+ ir_raw_event_set_idle(dev->idev, 1);
+ ir_raw_event_reset(dev->idev);
+
+}
+
+/* Disable the device receiver */
+static void ene_rx_disable(struct ene_device *dev)
+{
+ /* disable inputs */
+ ene_enable_normal_receive(dev, 0);
+
+ if (dev->hw_fan_as_normal_input)
+ ene_enable_fan_receive(dev, 0);
+
+ /* disable hardware IRQ and firmware flag */
+ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+ ir_raw_event_set_idle(dev->idev, 1);
+ ir_raw_event_reset(dev->idev);
+}
+
+
+/* prepare transmission */
+static void ene_tx_prepare(struct ene_device *dev)
+{
+ u8 conf1;
+
+ conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1);
+ dev->saved_conf1 = conf1;
+
+ if (dev->hw_revision == ENE_HW_C)
+ conf1 &= ~ENE_CIR_CONF1_TX_CLEAR;
+
+ /* Enable TX engine */
+ conf1 |= ENE_CIR_CONF1_TX_ON;
+
+ /* Set carrier */
+ if (dev->tx_period) {
+
+ /* NOTE: duty cycle handling is just a guess, it might
+ not be aviable. Default values were tested */
+ int tx_period_in500ns = dev->tx_period * 2;
+
+ int tx_pulse_width_in_500ns =
+ tx_period_in500ns / (100 / dev->tx_duty_cycle);
+
+ if (!tx_pulse_width_in_500ns)
+ tx_pulse_width_in_500ns = 1;
+
+ ene_dbg("TX: pulse distance = %d * 500 ns", tx_period_in500ns);
+ ene_dbg("TX: pulse width = %d * 500 ns",
+ tx_pulse_width_in_500ns);
+
+ ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT |
+ tx_period_in500ns);
+
+ ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE,
+ tx_pulse_width_in_500ns);
+
+ conf1 |= ENE_CIR_CONF1_TX_CARR;
+ } else
+ conf1 &= ~ENE_CIR_CONF1_TX_CARR;
+
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1);
+
+}
+
+/* end transmission */
+static void ene_tx_complete(struct ene_device *dev)
+{
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1);
+ dev->tx_buffer = NULL;
+}
+
+/* set transmit mask */
+static void ene_tx_hw_set_transmiter_mask(struct ene_device *dev)
+{
+ u8 txport1 = ene_hw_read_reg(dev, ENE_TX_PORT1) & ~ENE_TX_PORT1_EN;
+ u8 txport2 = ene_hw_read_reg(dev, ENE_TX_PORT2) & ~ENE_TX_PORT2_EN;
+
+ if (dev->transmitter_mask & 0x01)
+ txport1 |= ENE_TX_PORT1_EN;
+
+ if (dev->transmitter_mask & 0x02)
+ txport2 |= ENE_TX_PORT2_EN;
+
+ ene_hw_write_reg(dev, ENE_TX_PORT1, txport1);
+ ene_hw_write_reg(dev, ENE_TX_PORT2, txport2);
+}
+
+/* TX one sample - must be called with dev->hw_lock*/
+static void ene_tx_sample(struct ene_device *dev)
+{
+ u8 raw_tx;
+ u32 sample;
+
+ if (!dev->tx_buffer) {
+ ene_dbg("TX: attempt to transmit NULL buffer");
+ return;
+ }
+
+ /* Grab next TX sample */
+ if (!dev->tx_sample) {
+again:
+ if (dev->tx_pos == dev->tx_len + 1) {
+ if (!dev->tx_done) {
+ ene_dbg("TX: no more data to send");
+ dev->tx_done = 1;
+ goto exit;
+ } else {
+ ene_dbg("TX: last sample sent by hardware");
+ ene_tx_complete(dev);
+ complete(&dev->tx_complete);
+ return;
+ }
+ }
+
+ sample = dev->tx_buffer[dev->tx_pos++];
+ dev->tx_sample_pulse = !dev->tx_sample_pulse;
+
+ ene_dbg("TX: sample %8d (%s)", sample, dev->tx_sample_pulse ?
+ "pulse" : "space");
+
+ dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD);
+
+ /* guard against too short samples */
+ if (!dev->tx_sample)
+ goto again;
+ }
+
+ raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK);
+ dev->tx_sample -= raw_tx;
+
+ if (dev->tx_sample_pulse)
+ raw_tx |= ENE_TX_PULSE_MASK;
+
+ ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx);
+ dev->tx_reg = !dev->tx_reg;
+exit:
+ /* simulate TX done interrupt */
+ if (txsim)
+ mod_timer(&dev->tx_sim_timer, jiffies + HZ / 500);
+}
+
+/* timer to simulate tx done interrupt */
+static void ene_tx_irqsim(unsigned long data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_tx_sample(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+
+/* read irq status and ack it */
+static int ene_irq_status(struct ene_device *dev)
+{
+ u8 irq_status;
+ u8 fw_flags1, fw_flags2;
+ int cur_rx_pointer;
+ int retval = 0;
+
+ fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
+ cur_rx_pointer = !!(fw_flags2 & ENE_FW2_BUF_HIGH);
+
+ if (dev->hw_revision < ENE_HW_C) {
+ irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
+
+ if (!(irq_status & ENEB_IRQ_STATUS_IR))
+ return 0;
+
+ ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
+ irq_status & ~ENEB_IRQ_STATUS_IR);
+ dev->rx_pointer = cur_rx_pointer;
+ return ENE_IRQ_RX;
+ }
+
+ irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
+
+ if (!(irq_status & ENEC_IRQ_STATUS))
+ return 0;
+
+ /* original driver does that twice - a workaround ? */
+ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+
+ /* clear unknown flag in F8F9 */
+ if (fw_flags2 & ENE_FW2_IRQ_CLR)
+ ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
+
+ /* check if this is a TX interrupt */
+ fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
+ if (fw_flags1 & ENE_FW1_TXIRQ) {
+ ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
+ retval |= ENE_IRQ_TX;
+ }
+
+ /* Check if this is RX interrupt */
+ if (dev->rx_pointer != cur_rx_pointer) {
+ retval |= ENE_IRQ_RX;
+ dev->rx_pointer = cur_rx_pointer;
+
+ } else if (!(retval & ENE_IRQ_TX)) {
+ ene_dbg("RX: interrupt without change in RX pointer(%d)",
+ dev->rx_pointer);
+ retval |= ENE_IRQ_RX;
+ }
+
+ if ((retval & ENE_IRQ_RX) && (retval & ENE_IRQ_TX))
+ ene_dbg("both RX and TX interrupt at same time");
+
+ return retval;
+}
+
+/* interrupt handler */
+static irqreturn_t ene_isr(int irq, void *data)
+{
+ u16 hw_value;
+ int i, hw_sample;
+ int pulse;
+ int irq_status;
+ unsigned long flags;
+ int carrier = 0;
+ irqreturn_t retval = IRQ_NONE;
+ struct ene_device *dev = (struct ene_device *)data;
+ struct ir_raw_event ev;
+
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ irq_status = ene_irq_status(dev);
+
+ if (!irq_status)
+ goto unlock;
+
+ retval = IRQ_HANDLED;
+
+ if (irq_status & ENE_IRQ_TX) {
+
+ if (!dev->hw_learning_and_tx_capable) {
+ ene_dbg("TX interrupt on unsupported device!");
+ goto unlock;
+ }
+ ene_tx_sample(dev);
+ }
+
+ if (!(irq_status & ENE_IRQ_RX))
+ goto unlock;
+
+
+ if (dev->carrier_detect_enabled || debug)
+ carrier = ene_rx_sense_carrier(dev);
+#if 0
+ /* TODO */
+ if (dev->carrier_detect_enabled && carrier)
+ ir_raw_event_report_frequency(dev->idev, carrier);
+#endif
+
+ for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
+ hw_value = ene_hw_read_reg(dev,
+ ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i);
+
+ if (dev->rx_fan_input_inuse) {
+ /* read high part of the sample */
+ hw_value |= ene_hw_read_reg(dev,
+ ENE_SAMPLE_BUFFER_FAN +
+ dev->rx_pointer * 4 + i) << 8;
+ pulse = hw_value & ENE_FAN_SMPL_PULS_MSK;
+
+ /* clear space bit, and other unused bits */
+ hw_value &= ENE_FAN_VALUE_MASK;
+ hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
+
+ } else {
+ pulse = !(hw_value & ENE_SAMPLE_SPC_MASK);
+ hw_value &= ENE_SAMPLE_VALUE_MASK;
+ hw_sample = hw_value * sample_period;
+
+ if (dev->rx_period_adjust) {
+ hw_sample *= (100 - dev->rx_period_adjust);
+ hw_sample /= 100;
+ }
+ }
+ /* no more data */
+ if (!(hw_value))
+ break;
+
+ ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
+
+
+ ev.duration = hw_sample * 1000;
+ ev.pulse = pulse;
+ ir_raw_event_store_with_filter(dev->idev, &ev);
+ }
+
+ ir_raw_event_handle(dev->idev);
+unlock:
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return retval;
+}
+
+/* Initialize default settings */
+static void ene_setup_settings(struct ene_device *dev)
+{
+ dev->tx_period = 32;
+ dev->tx_duty_cycle = 25; /*%*/
+ dev->transmitter_mask = 3;
+
+ /* Force learning mode if (input == 2), otherwise
+ let user set it with LIRC_SET_REC_CARRIER */
+ dev->learning_enabled =
+ (input == 2 && dev->hw_learning_and_tx_capable);
+
+ dev->rx_pointer = -1;
+
+}
+
+/* outside interface: called on first open*/
+static int ene_open(void *data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->in_use = 1;
+ ene_setup_settings(dev);
+ ene_rx_enable(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface: called on device close*/
+static void ene_close(void *data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ ene_rx_disable(dev);
+ dev->in_use = 0;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* outside interface: set transmitter mask */
+static int ene_set_tx_mask(void *data, u32 tx_mask)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ ene_dbg("TX: attempt to set transmitter mask %02x", tx_mask);
+
+ /* invalid txmask */
+ if (!tx_mask || tx_mask & ~0x3) {
+ ene_dbg("TX: invalid mask");
+ /* return count of transmitters */
+ return 2;
+ }
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->transmitter_mask = tx_mask;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface : set tx carrier */
+static int ene_set_tx_carrier(void *data, u32 carrier)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ u32 period = 1000000 / carrier; /* (1 / freq) (* # usec in 1 sec) */
+
+ ene_dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+
+ if (period && (period > ENE_TX_PERIOD_MAX ||
+ period < ENE_TX_PERIOD_MIN)) {
+
+ ene_dbg("TX: out of range %d-%d carrier, "
+ "falling back to 32 kHz",
+ 1000 / ENE_TX_PERIOD_MIN,
+ 1000 / ENE_TX_PERIOD_MAX);
+
+ period = 32; /* this is just a coincidence!!! */
+ }
+ ene_dbg("TX: set carrier to %d kHz", carrier);
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->tx_period = period;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+
+/* outside interface: enable learning mode */
+static int ene_set_learning_mode(void *data, int enable)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ if (enable == dev->learning_enabled)
+ return 0;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->learning_enabled = enable;
+ ene_rx_set_inputs(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface: set rec carrier */
+static int ene_set_rec_carrier(void *data, u32 min, u32 max)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ ene_set_learning_mode(dev,
+ max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW);
+ return 0;
+}
+
+/* outside interface: enable or disable idle mode */
+static void ene_rx_set_idle(void *data, int idle)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ ene_dbg("%sabling idle mode", idle ? "en" : "dis");
+
+ ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
+ (enable_idle && idle) ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
+ ENE_CIR_SAMPLE_OVERFLOW);
+}
+
+
+/* outside interface: transmit */
+static int ene_transmit(void *data, int *buf, u32 n)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ dev->tx_buffer = buf;
+ dev->tx_len = n / sizeof(int);
+ dev->tx_pos = 0;
+ dev->tx_reg = 0;
+ dev->tx_done = 0;
+ dev->tx_sample = 0;
+ dev->tx_sample_pulse = 0;
+
+ ene_dbg("TX: %d samples", dev->tx_len);
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ ene_tx_hw_set_transmiter_mask(dev);
+ ene_tx_prepare(dev);
+
+ /* Transmit first two samples */
+ ene_tx_sample(dev);
+ ene_tx_sample(dev);
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) {
+ ene_dbg("TX: timeout");
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_tx_complete(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ } else
+ ene_dbg("TX: done");
+ return n;
+}
+
+
+/* probe entry */
+static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+{
+ int error = -ENOMEM;
+ struct ir_dev_props *ir_props;
+ struct input_dev *input_dev;
+ struct ene_device *dev;
+
+ /* allocate memory */
+ input_dev = input_allocate_device();
+ ir_props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
+
+ if (!input_dev || !ir_props || !dev)
+ goto error;
+
+ /* validate resources */
+ error = -ENODEV;
+
+ if (!pnp_port_valid(pnp_dev, 0) ||
+ pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
+ goto error;
+
+ if (!pnp_irq_valid(pnp_dev, 0))
+ goto error;
+
+ dev->hw_io = pnp_port_start(pnp_dev, 0);
+ dev->irq = pnp_irq(pnp_dev, 0);
+ spin_lock_init(&dev->hw_lock);
+
+ /* claim the resources */
+ error = -EBUSY;
+ if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
+ goto error;
+
+ if (request_irq(dev->irq, ene_isr,
+ IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
+ goto error;
+
+ pnp_set_drvdata(pnp_dev, dev);
+ dev->pnp_dev = pnp_dev;
+
+ /* detect hardware version and features */
+ error = ene_hw_detect(dev);
+ if (error)
+ goto error;
+
+ ene_setup_settings(dev);
+
+ if (!dev->hw_learning_and_tx_capable && txsim) {
+ dev->hw_learning_and_tx_capable = 1;
+ setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
+ (long unsigned int)dev);
+ ene_printk(KERN_WARNING,
+ "Simulation of TX activated\n");
+ }
+
+ ir_props->driver_type = RC_DRIVER_IR_RAW;
+ ir_props->allowed_protos = IR_TYPE_ALL;
+ ir_props->priv = dev;
+ ir_props->open = ene_open;
+ ir_props->close = ene_close;
+ ir_props->min_timeout = ENE_MINGAP * 1000;
+ ir_props->max_timeout = ENE_MAXGAP * 1000;
+ ir_props->timeout = ENE_MAXGAP * 1000;
+
+ if (dev->hw_revision == ENE_HW_B)
+ ir_props->s_idle = ene_rx_set_idle;
+
+
+ dev->props = ir_props;
+ dev->idev = input_dev;
+
+ /* don't allow too short/long sample periods */
+ if (sample_period < 5 || sample_period > 0x7F)
+ sample_period = -1;
+
+ /* choose default sample period */
+ if (sample_period == -1) {
+
+ sample_period = 50;
+
+ /* on revB, hardware idle mode eats first sample
+ if we set too low sample period */
+ if (dev->hw_revision == ENE_HW_B && enable_idle)
+ sample_period = 75;
+ }
+
+ ir_props->rx_resolution = sample_period * 1000;
+
+ if (dev->hw_learning_and_tx_capable) {
+
+ ir_props->s_learning_mode = ene_set_learning_mode;
+
+ if (input == 0)
+ ir_props->s_rx_carrier_range = ene_set_rec_carrier;
+
+ init_completion(&dev->tx_complete);
+ ir_props->tx_ir = ene_transmit;
+ ir_props->s_tx_mask = ene_set_tx_mask;
+ ir_props->s_tx_carrier = ene_set_tx_carrier;
+ ir_props->tx_resolution = ENE_TX_SMPL_PERIOD * 1000;
+ /* ir_props->s_carrier_report = ene_set_carrier_report; */
+ }
+
+
+ device_set_wakeup_capable(&pnp_dev->dev, 1);
+ device_set_wakeup_enable(&pnp_dev->dev, 1);
+
+ if (dev->hw_learning_and_tx_capable)
+ input_dev->name = "ENE eHome Infrared Remote Transceiver";
+ else
+ input_dev->name = "ENE eHome Infrared Remote Receiver";
+
+
+ error = -ENODEV;
+ if (ir_input_register(input_dev, RC_MAP_RC6_MCE, ir_props,
+ ENE_DRIVER_NAME))
+ goto error;
+
+
+ ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
+ return 0;
+error:
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->hw_io)
+ release_region(dev->hw_io, ENE_MAX_IO);
+
+ input_free_device(input_dev);
+ kfree(ir_props);
+ kfree(dev);
+ return error;
+}
+
+/* main unload function */
+static void ene_remove(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_rx_disable(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ free_irq(dev->irq, dev);
+ release_region(dev->hw_io, ENE_MAX_IO);
+ ir_input_unregister(dev->idev);
+ kfree(dev->props);
+ kfree(dev);
+}
+
+/* enable wake on IR (wakes on specific button on original remote) */
+static void ene_enable_wake(struct ene_device *dev, int enable)
+{
+ enable = enable && device_may_wakeup(&dev->pnp_dev->dev);
+
+ ene_dbg("wake on IR %s", enable ? "enabled" : "disabled");
+
+ ene_hw_write_reg_mask(dev, ENE_FW1, enable ?
+ ENE_FW1_WAKE : 0, ENE_FW1_WAKE);
+}
+
+#ifdef CONFIG_PM
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ ene_enable_wake(dev, 1);
+ return 0;
+}
+
+static int ene_resume(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ if (dev->in_use)
+ ene_rx_enable(dev);
+
+ ene_enable_wake(dev, 0);
+ return 0;
+}
+#endif
+
+static void ene_shutdown(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ ene_enable_wake(dev, 1);
+}
+
+static const struct pnp_device_id ene_ids[] = {
+ {.id = "ENE0100",},
+ {.id = "ENE0200",},
+ {.id = "ENE0201",},
+ {.id = "ENE0202",},
+ {},
+};
+
+static struct pnp_driver ene_driver = {
+ .name = ENE_DRIVER_NAME,
+ .id_table = ene_ids,
+ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+
+ .probe = ene_probe,
+ .remove = __devexit_p(ene_remove),
+#ifdef CONFIG_PM
+ .suspend = ene_suspend,
+ .resume = ene_resume,
+#endif
+ .shutdown = ene_shutdown,
+};
+
+static int __init ene_init(void)
+{
+ return pnp_register_driver(&ene_driver);
+}
+
+static void ene_exit(void)
+{
+ pnp_unregister_driver(&ene_driver);
+}
+
+module_param(sample_period, int, S_IRUGO);
+MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
+
+module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_idle,
+ "Enables turning off signal sampling after long inactivity time; "
+ "if disabled might help detecting input signal (default: enabled)"
+ " (KB3926B only)");
+
+module_param(input, bool, S_IRUGO);
+MODULE_PARM_DESC(input, "select which input to use "
+ "0 - auto, 1 - standard, 2 - wideband(KB3926C+)");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)");
+
+module_param(txsim, bool, S_IRUGO);
+MODULE_PARM_DESC(txsim,
+ "Simulate TX features on unsupported hardware (dangerous)");
+
+MODULE_DEVICE_TABLE(pnp, ene_ids);
+MODULE_DESCRIPTION
+ ("Infrared input driver for KB3926B/KB3926C/KB3926D "
+ "(aka ENE0100/ENE0200/ENE0201) CIR port");
+
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_LICENSE("GPL");
+
+module_init(ene_init);
+module_exit(ene_exit);
diff --git a/drivers/media/IR/ene_ir.h b/drivers/media/IR/ene_ir.h
new file mode 100644
index 0000000..54c76af
--- /dev/null
+++ b/drivers/media/IR/ene_ir.h
@@ -0,0 +1,235 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@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/spinlock.h>
+
+
+/* hardware address */
+#define ENE_STATUS 0 /* hardware status - unused */
+#define ENE_ADDR_HI 1 /* hi byte of register address */
+#define ENE_ADDR_LO 2 /* low byte of register address */
+#define ENE_IO 3 /* read/write window */
+#define ENE_MAX_IO 4
+
+/* 8 bytes of samples, divided in 2 halfs*/
+#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
+#define ENE_SAMPLE_SPC_MASK 0x80 /* sample is space */
+#define ENE_SAMPLE_VALUE_MASK 0x7F
+#define ENE_SAMPLE_OVERFLOW 0x7F
+#define ENE_SAMPLES_SIZE 4
+
+/* fan input sample buffer */
+#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
+ /* each sample of normal buffer */
+#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
+ /* if set, says that sample is pulse */
+#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
+
+/* first firmware register */
+#define ENE_FW1 0xF8F8
+#define ENE_FW1_ENABLE 0x01 /* enable fw processing */
+#define ENE_FW1_TXIRQ 0x02 /* TX interrupt pending */
+#define ENE_FW1_WAKE 0x40 /* enable wake from S3 */
+#define ENE_FW1_IRQ 0x80 /* enable interrupt */
+
+/* second firmware register */
+#define ENE_FW2 0xF8F9
+#define ENE_FW2_BUF_HIGH 0x01 /* which half of the buffer to read */
+#define ENE_FW2_IRQ_CLR 0x04 /* clear this on IRQ */
+#define ENE_FW2_GP40_AS_LEARN 0x08 /* normal input is used as */
+ /* learning input */
+#define ENE_FW2_FAN_AS_NRML_IN 0x40 /* fan is used as normal input */
+#define ENE_FW2_LEARNING 0x80 /* hardware supports learning and TX */
+
+/* transmitter ports */
+#define ENE_TX_PORT2 0xFC01 /* this enables one or both */
+#define ENE_TX_PORT2_EN 0x20 /* TX ports */
+#define ENE_TX_PORT1 0xFC08
+#define ENE_TX_PORT1_EN 0x02
+
+/* IRQ registers block (for revision B) */
+#define ENEB_IRQ 0xFD09 /* IRQ number */
+#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
+#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
+#define ENEB_IRQ_STATUS_IR 0x20 /* IR irq */
+
+/* fan as input settings - only if learning capable */
+#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
+#define ENE_FAN_AS_IN1_EN 0xCD
+#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
+#define ENE_FAN_AS_IN2_EN 0x03
+#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
+
+/* IRQ registers block (for revision C,D) */
+#define ENEC_IRQ 0xFE9B /* new irq settings register */
+#define ENEC_IRQ_MASK 0x0F /* irq number mask */
+#define ENEC_IRQ_UNK_EN 0x10 /* always enabled */
+#define ENEC_IRQ_STATUS 0x20 /* irq status and ACK */
+
+/* CIR block settings */
+#define ENE_CIR_CONF1 0xFEC0
+#define ENE_CIR_CONF1_TX_CLEAR 0x01 /* clear that on revC */
+ /* while transmitting */
+#define ENE_CIR_CONF1_RX_ON 0x07 /* normal receiver enabled */
+#define ENE_CIR_CONF1_LEARN1 0x08 /* enabled on learning mode */
+#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
+#define ENE_CIR_CONF1_TX_CARR 0x80 /* send TX carrier or not */
+
+#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
+#define ENE_CIR_CONF2_LEARN2 0x10 /* set on enable learning */
+#define ENE_CIR_CONF2_GPIO40DIS 0x20 /* disable input via gpio40 */
+
+#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
+#define ENE_CIR_SAMPLE_OVERFLOW 0x80 /* interrupt on overflows if set */
+
+
+/* Two byte tx buffer */
+#define ENE_TX_INPUT1 0xFEC9
+#define ENE_TX_INPUT2 0xFECA
+#define ENE_TX_PULSE_MASK 0x80 /* Transmitted sample is pulse */
+#define ENE_TX_SMLP_MASK 0x7F
+#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period - fixed */
+
+
+/* Unknown TX setting - TX sample period ??? */
+#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
+
+/* Current received carrier period */
+#define ENE_RX_CARRIER 0xFECC /* RX period (500 ns) */
+#define ENE_RX_CARRIER_VALID 0x80 /* Register content valid */
+
+
+/* TX period (1/carrier) */
+#define ENE_TX_PERIOD 0xFECE /* TX period (500 ns) */
+#define ENE_TX_PERIOD_UNKBIT 0x80 /* This bit set on transmit*/
+#define ENE_TX_PERIOD_PULSE 0xFECF /* TX pulse period (500 ns)*/
+
+/* Hardware versions */
+#define ENE_HW_VERSION 0xFF00 /* hardware revision */
+#define ENE_PLLFRH 0xFF16
+#define ENE_PLLFRL 0xFF17
+
+#define ENE_HW_UNK 0xFF1D
+#define ENE_HW_UNK_CLR 0x04
+#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
+#define ENE_HW_VER_MINOR 0xFF1F
+#define ENE_HW_VER_OLD 0xFD00
+
+/* Normal/Learning carrier ranges - only valid if we have learning input*/
+/* TODO: test */
+#define ENE_NORMAL_RX_LOW 34
+#define ENE_NORMAL_RX_HI 38
+
+/* Tx carrier range */
+/* Hardware might be able to do more, but this range is enough for
+ all purposes */
+#define ENE_TX_PERIOD_MAX 32 /* corresponds to 29.4 kHz */
+#define ENE_TX_PERIOD_MIN 16 /* corrsponds to 62.5 kHz */
+
+
+
+/* Minimal and maximal gaps */
+
+/* Normal case:
+ Minimal gap is 0x7F * sample period
+ Maximum gap depends on hardware.
+ For KB3926B, it is unlimited, for newer models its around
+ 250000, after which HW stops sending samples, and that is
+ not possible to change */
+
+/* Fan case:
+ Both minimal and maximal gaps are same, and equal to 0xFFF * 0x61
+ And there is nothing to change this setting
+*/
+
+#define ENE_MAXGAP 250000
+#define ENE_MINGAP (127 * sample_period)
+
+/******************************************************************************/
+
+#define ENE_DRIVER_NAME "ene_ir"
+
+#define ENE_IRQ_RX 1
+#define ENE_IRQ_TX 2
+
+#define ENE_HW_B 1 /* 3926B */
+#define ENE_HW_C 2 /* 3926C */
+#define ENE_HW_D 3 /* 3926D */
+
+#define ene_printk(level, text, ...) \
+ printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
+
+#define ene_dbg(text, ...) \
+ if (debug) \
+ printk(KERN_DEBUG \
+ ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+#define ene_dbg_verbose(text, ...) \
+ if (debug > 1) \
+ printk(KERN_DEBUG \
+ ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+
+struct ene_device {
+ struct pnp_dev *pnp_dev;
+ struct input_dev *idev;
+ struct ir_dev_props *props;
+ int in_use;
+
+ /* hw IO settings */
+ unsigned long hw_io;
+ int irq;
+ spinlock_t hw_lock;
+
+ /* HW features */
+ int hw_revision; /* hardware revision */
+ bool hw_learning_and_tx_capable; /* learning capable */
+ bool hw_gpio40_learning; /* gpio40 is learning */
+ bool hw_fan_as_normal_input; /* fan input is used as */
+ /* regular input */
+ /* HW state*/
+ int rx_pointer; /* hw pointer to rx buffer */
+ bool rx_fan_input_inuse; /* is fan input in use for rx*/
+ int tx_reg; /* current reg used for TX */
+ u8 saved_conf1; /* saved FEC0 reg */
+
+ /* TX sample handling */
+ unsigned int tx_sample; /* current sample for TX */
+ bool tx_sample_pulse; /* current sample is pulse */
+
+ /* TX buffer */
+ int *tx_buffer; /* input samples buffer*/
+ int tx_pos; /* position in that bufer */
+ int tx_len; /* current len of tx buffer */
+ int tx_done; /* done transmitting */
+ /* one more sample pending*/
+ struct completion tx_complete; /* TX completion */
+ struct timer_list tx_sim_timer;
+
+ /* TX settings */
+ int tx_period;
+ int tx_duty_cycle;
+ int transmitter_mask;
+
+ /* RX settings */
+ bool learning_enabled; /* learning input enabled */
+ bool carrier_detect_enabled; /* carrier detect enabled */
+ int rx_period_adjust;
+};
diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c
index 65c125e..c185422 100644
--- a/drivers/media/IR/imon.c
+++ b/drivers/media/IR/imon.c
@@ -87,7 +87,6 @@ static ssize_t lcd_write(struct file *file, const char *buf,
struct imon_context {
struct device *dev;
struct ir_dev_props *props;
- struct ir_input_dev *ir;
/* Newer devices have two interfaces */
struct usb_device *usbdev_intf0;
struct usb_device *usbdev_intf1;
@@ -1656,7 +1655,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
{
struct input_dev *idev;
struct ir_dev_props *props;
- struct ir_input_dev *ir;
int ret, i;
idev = input_allocate_device();
@@ -1671,12 +1669,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
goto props_alloc_failed;
}
- ir = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
- if (!ir) {
- dev_err(ictx->dev, "remote ir input dev allocation failed\n");
- goto ir_dev_alloc_failed;
- }
-
snprintf(ictx->name_idev, sizeof(ictx->name_idev),
"iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
idev->name = ictx->name_idev;
@@ -1706,14 +1698,9 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
props->change_protocol = imon_ir_change_protocol;
ictx->props = props;
- ictx->ir = ir;
- memcpy(&ir->dev, ictx->dev, sizeof(struct device));
-
usb_to_input_id(ictx->usbdev_intf0, &idev->id);
idev->dev.parent = ictx->dev;
- input_set_drvdata(idev, ir);
-
ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
if (ret < 0) {
dev_err(ictx->dev, "remote input dev register failed\n");
@@ -1723,8 +1710,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
return idev;
idev_register_failed:
- kfree(ir);
-ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
@@ -1944,7 +1929,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
urb_submit_failed:
ir_input_unregister(ictx->idev);
- input_free_device(ictx->idev);
idev_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
@@ -2014,10 +1998,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
return ictx;
urb_submit_failed:
- if (ictx->touch) {
+ if (ictx->touch)
input_unregister_device(ictx->touch);
- input_free_device(ictx->touch);
- }
touch_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h
index babd5206..a85a8c7 100644
--- a/drivers/media/IR/ir-core-priv.h
+++ b/drivers/media/IR/ir-core-priv.h
@@ -32,7 +32,7 @@ struct ir_raw_handler {
struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
- struct work_struct rx_work; /* for the rx decoding workqueue */
+ struct task_struct *thread;
struct kfifo kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
@@ -41,10 +41,13 @@ struct ir_raw_event_ctrl {
/* raw decoder state follows */
struct ir_raw_event prev_ev;
+ struct ir_raw_event this_ev;
struct nec_dec {
int state;
unsigned count;
u32 bits;
+ bool is_nec_x;
+ bool necx_repeat;
} nec;
struct rc5_dec {
int state;
@@ -76,7 +79,7 @@ struct ir_raw_event_ctrl {
struct lirc_codec {
struct ir_input_dev *ir_dev;
struct lirc_driver *drv;
- int lircdata;
+ int carrier_low;
} lirc;
};
@@ -104,10 +107,9 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration)
ev->duration -= duration;
}
-#define TO_US(duration) (((duration) + 500) / 1000)
+#define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000)
#define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space")
#define IS_RESET(ev) (ev.duration == 0)
-
/*
* Routines from ir-sysfs.c - Meant to be called only internally inside
* ir-core
@@ -126,7 +128,8 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
void ir_raw_init(void);
-
+int ir_rcmap_init(void);
+void ir_rcmap_cleanup(void);
/*
* Decoder initialization code
*
diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c
index 8894d8b..77a89c4 100644
--- a/drivers/media/IR/ir-jvc-decoder.c
+++ b/drivers/media/IR/ir-jvc-decoder.c
@@ -32,6 +32,7 @@ enum jvc_state {
STATE_BIT_SPACE,
STATE_TRAILER_PULSE,
STATE_TRAILER_SPACE,
+ STATE_CHECK_REPEAT,
};
/**
@@ -60,6 +61,7 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+again:
switch (data->state) {
case STATE_INACTIVE:
@@ -149,8 +151,18 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
}
data->count = 0;
- data->state = STATE_BIT_PULSE;
+ data->state = STATE_CHECK_REPEAT;
return 0;
+
+ case STATE_CHECK_REPEAT:
+ if (!ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+ data->state = STATE_INACTIVE;
+ else
+ data->state = STATE_BIT_PULSE;
+ goto again;
}
out:
diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c
index 15a0f19..7e82a9d 100644
--- a/drivers/media/IR/ir-keytable.c
+++ b/drivers/media/IR/ir-keytable.c
@@ -339,6 +339,8 @@ void ir_repeat(struct input_dev *dev)
spin_lock_irqsave(&ir->keylock, flags);
+ input_event(dev, EV_MSC, MSC_SCAN, ir->last_scancode);
+
if (!ir->keypressed)
goto out;
@@ -370,6 +372,8 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
spin_lock_irqsave(&ir->keylock, flags);
+ input_event(dev, EV_MSC, MSC_SCAN, scancode);
+
/* Repeat event? */
if (ir->keypressed &&
ir->last_scancode == scancode &&
@@ -383,9 +387,11 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
ir->last_toggle = toggle;
ir->last_keycode = keycode;
+
if (keycode == KEY_RESERVED)
goto out;
+
/* Register a keypress */
ir->keypressed = true;
IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
@@ -428,7 +434,7 @@ static void ir_close(struct input_dev *input_dev)
*/
int __ir_input_register(struct input_dev *input_dev,
const struct ir_scancode_table *rc_tab,
- const struct ir_dev_props *props,
+ struct ir_dev_props *props,
const char *driver_name)
{
struct ir_input_dev *ir_dev;
@@ -480,6 +486,8 @@ int __ir_input_register(struct input_dev *input_dev,
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
+ set_bit(EV_MSC, input_dev->evbit);
+ set_bit(MSC_SCAN, input_dev->mscbit);
if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
rc = -ENOMEM;
@@ -499,7 +507,8 @@ int __ir_input_register(struct input_dev *input_dev,
IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
driver_name, rc_tab->name,
- ir_dev->props->driver_type == RC_DRIVER_IR_RAW ? " in raw mode" : "");
+ (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ?
+ " in raw mode" : "");
return 0;
diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c
index 3ba482d..77b5946 100644
--- a/drivers/media/IR/ir-lirc-codec.c
+++ b/drivers/media/IR/ir-lirc-codec.c
@@ -32,6 +32,7 @@
static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
{
struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+ int sample;
if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
return 0;
@@ -39,18 +40,20 @@ static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
return -EINVAL;
+ if (IS_RESET(ev))
+ return 0;
+
IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
TO_US(ev.duration), TO_STR(ev.pulse));
- ir_dev->raw->lirc.lircdata += ev.duration / 1000;
+ sample = ev.duration / 1000;
if (ev.pulse)
- ir_dev->raw->lirc.lircdata |= PULSE_BIT;
+ sample |= PULSE_BIT;
lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
- (unsigned char *) &ir_dev->raw->lirc.lircdata);
+ (unsigned char *) &sample);
wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
- ir_dev->raw->lirc.lircdata = 0;
return 0;
}
@@ -92,13 +95,14 @@ out:
return ret;
}
-static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long __user arg)
{
struct lirc_codec *lirc;
struct ir_input_dev *ir_dev;
int ret = 0;
void *drv_data;
- unsigned long val;
+ unsigned long val = 0;
lirc = lirc_get_pdata(filep);
if (!lirc)
@@ -110,47 +114,106 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long ar
drv_data = ir_dev->props->priv;
- switch (cmd) {
- case LIRC_SET_TRANSMITTER_MASK:
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = get_user(val, (unsigned long *)arg);
if (ret)
return ret;
+ }
+
+ switch (cmd) {
- if (ir_dev->props && ir_dev->props->s_tx_mask)
+ /* legacy support */
+ case LIRC_GET_SEND_MODE:
+ val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+ return -EINVAL;
+ break;
+
+ /* TX settings */
+ case LIRC_SET_TRANSMITTER_MASK:
+ if (ir_dev->props->s_tx_mask)
ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
else
return -EINVAL;
break;
case LIRC_SET_SEND_CARRIER:
- ret = get_user(val, (unsigned long *)arg);
- if (ret)
- return ret;
-
- if (ir_dev->props && ir_dev->props->s_tx_carrier)
+ if (ir_dev->props->s_tx_carrier)
ir_dev->props->s_tx_carrier(drv_data, (u32)val);
else
return -EINVAL;
break;
- case LIRC_GET_SEND_MODE:
- val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
- ret = put_user(val, (unsigned long *)arg);
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ if (!ir_dev->props->s_tx_duty_cycle)
+ return -ENOSYS;
+
+ if (val <= 0 || val >= 100)
+ return -EINVAL;
+
+ ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val);
break;
- case LIRC_SET_SEND_MODE:
- ret = get_user(val, (unsigned long *)arg);
- if (ret)
- return ret;
+ /* RX settings */
+ case LIRC_SET_REC_CARRIER:
+ if (ir_dev->props->s_rx_carrier_range)
+ ret = ir_dev->props->s_rx_carrier_range(
+ ir_dev->props->priv,
+ ir_dev->raw->lirc.carrier_low, val);
+ else
+ return -ENOSYS;
- if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+ if (!ret)
+ ir_dev->raw->lirc.carrier_low = 0;
+ break;
+
+ case LIRC_SET_REC_CARRIER_RANGE:
+ if (val >= 0)
+ ir_dev->raw->lirc.carrier_low = val;
+ break;
+
+
+ case LIRC_GET_REC_RESOLUTION:
+ val = ir_dev->props->rx_resolution;
+ break;
+
+ case LIRC_SET_WIDEBAND_RECEIVER:
+ if (ir_dev->props->s_learning_mode)
+ return ir_dev->props->s_learning_mode(
+ ir_dev->props->priv, !!val);
+ else
+ return -ENOSYS;
+
+ /* Generic timeout support */
+ case LIRC_GET_MIN_TIMEOUT:
+ if (!ir_dev->props->max_timeout)
+ return -ENOSYS;
+ val = ir_dev->props->min_timeout / 1000;
+ break;
+
+ case LIRC_GET_MAX_TIMEOUT:
+ if (!ir_dev->props->max_timeout)
+ return -ENOSYS;
+ val = ir_dev->props->max_timeout / 1000;
+ break;
+
+ case LIRC_SET_REC_TIMEOUT:
+ if (val < ir_dev->props->min_timeout ||
+ val > ir_dev->props->max_timeout)
return -EINVAL;
+ ir_dev->props->timeout = val * 1000;
break;
default:
return lirc_dev_fop_ioctl(filep, cmd, arg);
}
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ ret = put_user(val, (unsigned long *)arg);
+
return ret;
}
@@ -196,13 +259,28 @@ static int ir_lirc_register(struct input_dev *input_dev)
features = LIRC_CAN_REC_MODE2;
if (ir_dev->props->tx_ir) {
+
features |= LIRC_CAN_SEND_PULSE;
if (ir_dev->props->s_tx_mask)
features |= LIRC_CAN_SET_TRANSMITTER_MASK;
if (ir_dev->props->s_tx_carrier)
features |= LIRC_CAN_SET_SEND_CARRIER;
+
+ if (ir_dev->props->s_tx_duty_cycle)
+ features |= LIRC_CAN_SET_REC_DUTY_CYCLE;
}
+ if (ir_dev->props->s_rx_carrier_range)
+ features |= LIRC_CAN_SET_REC_CARRIER |
+ LIRC_CAN_SET_REC_CARRIER_RANGE;
+
+ if (ir_dev->props->s_learning_mode)
+ features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
+
+ if (ir_dev->props->max_timeout)
+ features |= LIRC_CAN_SET_REC_TIMEOUT;
+
+
snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
ir_dev->driver_name);
drv->minor = -1;
@@ -224,8 +302,6 @@ static int ir_lirc_register(struct input_dev *input_dev)
ir_dev->raw->lirc.drv = drv;
ir_dev->raw->lirc.ir_dev = ir_dev;
- ir_dev->raw->lirc.lircdata = PULSE_MASK;
-
return 0;
lirc_register_failed:
diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c
index 52e0f37..d597421 100644
--- a/drivers/media/IR/ir-nec-decoder.c
+++ b/drivers/media/IR/ir-nec-decoder.c
@@ -20,12 +20,13 @@
#define NEC_HEADER_PULSE (16 * NEC_UNIT)
#define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */
#define NEC_HEADER_SPACE (8 * NEC_UNIT)
-#define NEC_REPEAT_SPACE (8 * NEC_UNIT)
+#define NEC_REPEAT_SPACE (4 * NEC_UNIT)
#define NEC_BIT_PULSE (1 * NEC_UNIT)
#define NEC_BIT_0_SPACE (1 * NEC_UNIT)
#define NEC_BIT_1_SPACE (3 * NEC_UNIT)
#define NEC_TRAILER_PULSE (1 * NEC_UNIT)
#define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */
+#define NECX_REPEAT_BITS 1
enum nec_state {
STATE_INACTIVE,
@@ -67,8 +68,12 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (!ev.pulse)
break;
- if (!eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2) &&
- !eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+ if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
+ data->is_nec_x = false;
+ data->necx_repeat = false;
+ } else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+ data->is_nec_x = true;
+ else
break;
data->count = 0;
@@ -105,6 +110,17 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (ev.pulse)
break;
+ if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
+ geq_margin(ev.duration,
+ NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
+ IR_dprintk(1, "Repeat last key\n");
+ ir_repeat(input_dev);
+ data->state = STATE_INACTIVE;
+ return 0;
+
+ } else if (data->count > NECX_REPEAT_BITS)
+ data->necx_repeat = false;
+
data->bits <<= 1;
if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
data->bits |= 1;
@@ -159,6 +175,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
}
+ if (data->is_nec_x)
+ data->necx_repeat = true;
+
ir_keydown(input_dev, scancode, 0);
data->state = STATE_INACTIVE;
return 0;
diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c
index 6f192ef..43094e7 100644
--- a/drivers/media/IR/ir-raw-event.c
+++ b/drivers/media/IR/ir-raw-event.c
@@ -12,9 +12,10 @@
* GNU General Public License for more details.
*/
-#include <linux/workqueue.h>
-#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
+#include <linux/freezer.h>
#include "ir-core-priv.h"
/* Define the max number of pulse/space transitions to buffer */
@@ -24,7 +25,7 @@
static LIST_HEAD(ir_raw_client_list);
/* Used to handle IR raw handler extensions */
-static DEFINE_SPINLOCK(ir_raw_handler_lock);
+static DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static u64 available_protocols;
@@ -33,20 +34,30 @@ static u64 available_protocols;
static struct work_struct wq_load;
#endif
-static void ir_raw_event_work(struct work_struct *work)
+static int ir_raw_event_thread(void *data)
{
struct ir_raw_event ev;
struct ir_raw_handler *handler;
- struct ir_raw_event_ctrl *raw =
- container_of(work, struct ir_raw_event_ctrl, rx_work);
-
- while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
- spin_lock(&ir_raw_handler_lock);
- list_for_each_entry(handler, &ir_raw_handler_list, list)
- handler->decode(raw->input_dev, ev);
- spin_unlock(&ir_raw_handler_lock);
- raw->prev_ev = ev;
+ struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+
+ while (!kthread_should_stop()) {
+ try_to_freeze();
+
+ mutex_lock(&ir_raw_handler_lock);
+
+ while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
+ list_for_each_entry(handler, &ir_raw_handler_list, list)
+ handler->decode(raw->input_dev, ev);
+ raw->prev_ev = ev;
+ }
+
+ mutex_unlock(&ir_raw_handler_lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
}
+
+ return 0;
}
/**
@@ -66,6 +77,9 @@ int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev)
if (!ir->raw)
return -EINVAL;
+ IR_dprintk(2, "sample: (05%dus %s)\n",
+ TO_US(ev->duration), TO_STR(ev->pulse));
+
if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
return -ENOMEM;
@@ -126,6 +140,90 @@ int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type typ
EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
/**
+ * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
+ * @input_dev: the struct input_dev device descriptor
+ * @type: the type of the event that has occurred
+ *
+ * This routine (which may be called from an interrupt context) works
+ * in similiar manner to ir_raw_event_store_edge.
+ * This routine is intended for devices with limited internal buffer
+ * It automerges samples of same type, and handles timeouts
+ */
+int ir_raw_event_store_with_filter(struct input_dev *input_dev,
+ struct ir_raw_event *ev)
+{
+ struct ir_input_dev *ir = input_get_drvdata(input_dev);
+ struct ir_raw_event_ctrl *raw = ir->raw;
+
+ if (!raw || !ir->props)
+ return -EINVAL;
+
+ /* Ignore spaces in idle mode */
+ if (ir->idle && !ev->pulse)
+ return 0;
+ else if (ir->idle)
+ ir_raw_event_set_idle(input_dev, 0);
+
+ if (!raw->this_ev.duration) {
+ raw->this_ev = *ev;
+ } else if (ev->pulse == raw->this_ev.pulse) {
+ raw->this_ev.duration += ev->duration;
+ } else {
+ ir_raw_event_store(input_dev, &raw->this_ev);
+ raw->this_ev = *ev;
+ }
+
+ /* Enter idle mode if nessesary */
+ if (!ev->pulse && ir->props->timeout &&
+ raw->this_ev.duration >= ir->props->timeout)
+ ir_raw_event_set_idle(input_dev, 1);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
+
+void ir_raw_event_set_idle(struct input_dev *input_dev, int idle)
+{
+ struct ir_input_dev *ir = input_get_drvdata(input_dev);
+ struct ir_raw_event_ctrl *raw = ir->raw;
+ ktime_t now;
+ u64 delta;
+
+ if (!ir->props)
+ return;
+
+ if (!ir->raw)
+ goto out;
+
+ if (idle) {
+ IR_dprintk(2, "enter idle mode\n");
+ raw->last_event = ktime_get();
+ } else {
+ IR_dprintk(2, "exit idle mode\n");
+
+ now = ktime_get();
+ delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
+
+ WARN_ON(raw->this_ev.pulse);
+
+ raw->this_ev.duration =
+ min(raw->this_ev.duration + delta,
+ (u64)IR_MAX_DURATION);
+
+ ir_raw_event_store(input_dev, &raw->this_ev);
+
+ if (raw->this_ev.duration == IR_MAX_DURATION)
+ ir_raw_event_reset(input_dev);
+
+ raw->this_ev.duration = 0;
+ }
+out:
+ if (ir->props->s_idle)
+ ir->props->s_idle(ir->props->priv, idle);
+ ir->idle = idle;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
+
+/**
* ir_raw_event_handle() - schedules the decoding of stored ir data
* @input_dev: the struct input_dev device descriptor
*
@@ -138,7 +236,7 @@ void ir_raw_event_handle(struct input_dev *input_dev)
if (!ir->raw)
return;
- schedule_work(&ir->raw->rx_work);
+ wake_up_process(ir->raw->thread);
}
EXPORT_SYMBOL_GPL(ir_raw_event_handle);
@@ -147,9 +245,9 @@ u64
ir_raw_get_allowed_protocols()
{
u64 protocols;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
protocols = available_protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return protocols;
}
@@ -167,7 +265,7 @@ int ir_raw_event_register(struct input_dev *input_dev)
return -ENOMEM;
ir->raw->input_dev = input_dev;
- INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
+
ir->raw->enabled_protocols = ~0;
rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
GFP_KERNEL);
@@ -177,12 +275,21 @@ int ir_raw_event_register(struct input_dev *input_dev)
return rc;
}
- spin_lock(&ir_raw_handler_lock);
+ ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw,
+ "rc%u", (unsigned int)ir->devno);
+
+ if (IS_ERR(ir->raw->thread)) {
+ kfree(ir->raw);
+ ir->raw = NULL;
+ return PTR_ERR(ir->raw->thread);
+ }
+
+ mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir->raw->list, &ir_raw_client_list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_register)
handler->raw_register(ir->raw->input_dev);
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return 0;
}
@@ -195,14 +302,14 @@ void ir_raw_event_unregister(struct input_dev *input_dev)
if (!ir->raw)
return;
- cancel_work_sync(&ir->raw->rx_work);
+ kthread_stop(ir->raw->thread);
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_del(&ir->raw->list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_unregister)
handler->raw_unregister(ir->raw->input_dev);
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
kfifo_free(&ir->raw->kfifo);
kfree(ir->raw);
@@ -217,13 +324,13 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
if (ir_raw_handler->raw_register)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_register(raw->input_dev);
available_protocols |= ir_raw_handler->protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return 0;
}
@@ -233,13 +340,13 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
if (ir_raw_handler->raw_unregister)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_unregister(raw->input_dev);
available_protocols &= ~ir_raw_handler->protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c
index 6273047..96dafc4 100644
--- a/drivers/media/IR/ir-sysfs.c
+++ b/drivers/media/IR/ir-sysfs.c
@@ -325,6 +325,7 @@ static int __init ir_core_init(void)
/* Initialize/load the decoders/keymap code that will be used */
ir_raw_init();
+ ir_rcmap_init();
return 0;
}
@@ -332,6 +333,7 @@ static int __init ir_core_init(void)
static void __exit ir_core_exit(void)
{
class_unregister(&ir_input_class);
+ ir_rcmap_cleanup();
}
module_init(ir_core_init);
diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile
index cbee062..950e5d9 100644
--- a/drivers/media/IR/keymaps/Makefile
+++ b/drivers/media/IR/keymaps/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-dm1105-nec.o \
rc-dntv-live-dvb-t.o \
rc-dntv-live-dvbt-pro.o \
- rc-empty.o \
rc-em-terratec.o \
rc-encore-enltv2.o \
rc-encore-enltv.o \
@@ -59,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-purpletv.o \
rc-pv951.o \
rc-rc5-hauppauge-new.o \
+ rc-rc5-streamzap.o \
rc-rc5-tv.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \
diff --git a/drivers/media/IR/keymaps/rc-empty.c b/drivers/media/IR/keymaps/rc-empty.c
deleted file mode 100644
index 3b338d8..0000000
--- a/drivers/media/IR/keymaps/rc-empty.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* empty.h - Keytable for empty Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 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; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <media/rc-map.h>
-
-/* empty keytable, can be used as placeholder for not-yet created keytables */
-
-static struct ir_scancode empty[] = {
- { 0x2a, KEY_COFFEE },
-};
-
-static struct rc_keymap empty_map = {
- .map = {
- .scan = empty,
- .size = ARRAY_SIZE(empty),
- .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
- .name = RC_MAP_EMPTY,
- }
-};
-
-static int __init init_rc_map_empty(void)
-{
- return ir_register_map(&empty_map);
-}
-
-static void __exit exit_rc_map_empty(void)
-{
- ir_unregister_map(&empty_map);
-}
-
-module_init(init_rc_map_empty)
-module_exit(exit_rc_map_empty)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-rc5-streamzap.c b/drivers/media/IR/keymaps/rc-rc5-streamzap.c
new file mode 100644
index 0000000..4c19c58
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-rc5-streamzap.c
@@ -0,0 +1,81 @@
+/* rc-rc5-streamzap.c - Keytable for Streamzap PC Remote, for use
+ * with the Streamzap PC Remote IR Receiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@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.
+ */
+
+#include <media/rc-map.h>
+
+static struct ir_scancode rc5_streamzap[] = {
+/*
+ * FIXME: The Streamzap remote isn't actually true RC-5, it has an extra
+ * bit in it, which presently throws the in-kernel RC-5 decoder for a loop.
+ * We either have to enhance the decoder to support it, add a new decoder,
+ * or just rely on lirc userspace decoding.
+ */
+ { 0x00, KEY_NUMERIC_0 },
+ { 0x01, KEY_NUMERIC_1 },
+ { 0x02, KEY_NUMERIC_2 },
+ { 0x03, KEY_NUMERIC_3 },
+ { 0x04, KEY_NUMERIC_4 },
+ { 0x05, KEY_NUMERIC_5 },
+ { 0x06, KEY_NUMERIC_6 },
+ { 0x07, KEY_NUMERIC_7 },
+ { 0x08, KEY_NUMERIC_8 },
+ { 0x0a, KEY_POWER },
+ { 0x0b, KEY_MUTE },
+ { 0x0c, KEY_CHANNELUP },
+ { 0x0d, KEY_VOLUMEUP },
+ { 0x0e, KEY_CHANNELDOWN },
+ { 0x0f, KEY_VOLUMEDOWN },
+ { 0x10, KEY_UP },
+ { 0x11, KEY_LEFT },
+ { 0x12, KEY_OK },
+ { 0x13, KEY_RIGHT },
+ { 0x14, KEY_DOWN },
+ { 0x15, KEY_MENU },
+ { 0x16, KEY_EXIT },
+ { 0x17, KEY_PLAY },
+ { 0x18, KEY_PAUSE },
+ { 0x19, KEY_STOP },
+ { 0x1a, KEY_BACK },
+ { 0x1b, KEY_FORWARD },
+ { 0x1c, KEY_RECORD },
+ { 0x1d, KEY_REWIND },
+ { 0x1e, KEY_FASTFORWARD },
+ { 0x20, KEY_RED },
+ { 0x21, KEY_GREEN },
+ { 0x22, KEY_YELLOW },
+ { 0x23, KEY_BLUE },
+
+};
+
+static struct rc_keymap rc5_streamzap_map = {
+ .map = {
+ .scan = rc5_streamzap,
+ .size = ARRAY_SIZE(rc5_streamzap),
+ .ir_type = IR_TYPE_RC5,
+ .name = RC_MAP_RC5_STREAMZAP,
+ }
+};
+
+static int __init init_rc_map_rc5_streamzap(void)
+{
+ return ir_register_map(&rc5_streamzap_map);
+}
+
+static void __exit exit_rc_map_rc5_streamzap(void)
+{
+ ir_unregister_map(&rc5_streamzap_map);
+}
+
+module_init(init_rc_map_rc5_streamzap)
+module_exit(exit_rc_map_rc5_streamzap)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-rc6-mce.c b/drivers/media/IR/keymaps/rc-rc6-mce.c
index c6726a8..64264f7 100644
--- a/drivers/media/IR/keymaps/rc-rc6-mce.c
+++ b/drivers/media/IR/keymaps/rc-rc6-mce.c
@@ -74,6 +74,8 @@ static struct ir_scancode rc6_mce[] = {
{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
{ 0x800f044d, KEY_TITLE },
+ { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
+
{ 0x800f040c, KEY_POWER },
{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c
index 78bf7f7..ac6bb2c 100644
--- a/drivers/media/IR/mceusb.c
+++ b/drivers/media/IR/mceusb.c
@@ -228,7 +228,6 @@ static struct usb_device_id std_tx_mask_list[] = {
/* data structure for each usb transceiver */
struct mceusb_dev {
/* ir-core bits */
- struct ir_input_dev *irdev;
struct ir_dev_props *props;
struct ir_raw_event rawir;
@@ -428,7 +427,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
}
}
-static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
{
struct mceusb_dev *ir;
int len;
@@ -477,7 +476,7 @@ static void mce_request_packet(struct mceusb_dev *ir,
/* outbound data */
usb_fill_int_urb(async_urb, ir->usbdev,
usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
- async_buf, size, (usb_complete_t) usb_async_callback,
+ async_buf, size, (usb_complete_t)mce_async_callback,
ir, ep->bInterval);
memcpy(async_buf, data, size);
@@ -739,7 +738,7 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
ir->send_flags = SEND_FLAG_COMPLETE;
- dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n",
+ dev_dbg(ir->dev, "setup answer received %d bytes\n",
buf_len);
}
@@ -861,7 +860,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
{
struct input_dev *idev;
struct ir_dev_props *props;
- struct ir_input_dev *irdev;
struct device *dev = ir->dev;
int ret = -ENODEV;
@@ -878,12 +876,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
goto props_alloc_failed;
}
- irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
- if (!irdev) {
- dev_err(dev, "remote ir input dev allocation failed\n");
- goto ir_dev_alloc_failed;
- }
-
snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
"Infrared Remote Transceiver (%04x:%04x)",
le16_to_cpu(ir->usbdev->descriptor.idVendor),
@@ -902,9 +894,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
props->tx_ir = mceusb_tx_ir;
ir->props = props;
- ir->irdev = irdev;
-
- input_set_drvdata(idev, irdev);
ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
if (ret < 0) {
@@ -915,8 +904,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
return idev;
irdev_failed:
- kfree(irdev);
-ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
@@ -932,7 +919,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
struct usb_endpoint_descriptor *ep = NULL;
struct usb_endpoint_descriptor *ep_in = NULL;
struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_host_config *config;
struct mceusb_dev *ir = NULL;
int pipe, maxp, i;
char buf[63], name[128] = "";
@@ -942,7 +928,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, ": %s called\n", __func__);
- config = dev->actconfig;
idesc = intf->cur_altsetting;
is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
diff --git a/drivers/media/IR/rc-map.c b/drivers/media/IR/rc-map.c
index 46a8f15..689143f 100644
--- a/drivers/media/IR/rc-map.c
+++ b/drivers/media/IR/rc-map.c
@@ -82,3 +82,26 @@ void ir_unregister_map(struct rc_keymap *map)
}
EXPORT_SYMBOL_GPL(ir_unregister_map);
+
+static struct ir_scancode empty[] = {
+ { 0x2a, KEY_COFFEE },
+};
+
+static struct rc_keymap empty_map = {
+ .map = {
+ .scan = empty,
+ .size = ARRAY_SIZE(empty),
+ .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
+ .name = RC_MAP_EMPTY,
+ }
+};
+
+int ir_rcmap_init(void)
+{
+ return ir_register_map(&empty_map);
+}
+
+void ir_rcmap_cleanup(void)
+{
+ ir_unregister_map(&empty_map);
+}
diff --git a/drivers/media/IR/streamzap.c b/drivers/media/IR/streamzap.c
new file mode 100644
index 0000000..058e29f
--- /dev/null
+++ b/drivers/media/IR/streamzap.c
@@ -0,0 +1,741 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * Ported to in-kernel ir-core interface by Jarod Wilson
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+
+#define DRIVER_VERSION "1.60"
+#define DRIVER_NAME "streamzap"
+#define DRIVER_DESC "Streamzap Remote Control driver"
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define USB_STREAMZAP_VENDOR_ID 0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID 0x0000
+
+/* table of devices that work with this driver */
+static struct usb_device_id streamzap_table[] = {
+ /* Streamzap Remote Control */
+ { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+ /* Terminating entry */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_TIMEOUT 0xff
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define SZ_BUF_LEN 128
+
+enum StreamzapDecoderState {
+ PulseSpace,
+ FullPulse,
+ FullSpace,
+ IgnorePulse
+};
+
+/* structure to hold our device specific stuff */
+struct streamzap_ir {
+
+ /* ir-core */
+ struct ir_dev_props *props;
+ struct ir_raw_event rawir;
+
+ /* core device info */
+ struct device *dev;
+ struct input_dev *idev;
+
+ /* usb */
+ struct usb_device *usbdev;
+ struct usb_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct urb *urb_in;
+
+ /* buffer & dma */
+ unsigned char *buf_in;
+ dma_addr_t dma_in;
+ unsigned int buf_in_len;
+
+ /* timer used to support delay buffering */
+ struct timer_list delay_timer;
+ bool timer_running;
+ spinlock_t timer_lock;
+ struct timer_list flush_timer;
+ bool flush;
+
+ /* delay buffer */
+ struct kfifo fifo;
+ bool fifo_initialized;
+
+ /* track what state we're in */
+ enum StreamzapDecoderState decoder_state;
+ /* tracks whether we are currently receiving some signal */
+ bool idle;
+ /* sum of signal lengths received since signal start */
+ unsigned long sum;
+ /* start time of signal; necessary for gap tracking */
+ struct timeval signal_last;
+ struct timeval signal_start;
+ /* bool timeout_enabled; */
+
+ char name[128];
+ char phys[64];
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void streamzap_callback(struct urb *urb);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver streamzap_driver = {
+ .name = DRIVER_NAME,
+ .probe = streamzap_probe,
+ .disconnect = streamzap_disconnect,
+ .suspend = streamzap_suspend,
+ .resume = streamzap_resume,
+ .id_table = streamzap_table,
+};
+
+static void streamzap_stop_timer(struct streamzap_ir *sz)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (sz->timer_running) {
+ sz->timer_running = false;
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+ del_timer_sync(&sz->delay_timer);
+ } else {
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+ }
+}
+
+static void streamzap_flush_timeout(unsigned long arg)
+{
+ struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+
+ dev_info(sz->dev, "%s: callback firing\n", __func__);
+
+ /* finally start accepting data */
+ sz->flush = false;
+}
+
+static void streamzap_delay_timeout(unsigned long arg)
+{
+ struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ unsigned long flags;
+ int len, ret;
+ static unsigned long delay;
+ bool wake = false;
+
+ /* deliver data every 10 ms */
+ delay = msecs_to_jiffies(10);
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+
+ if (kfifo_len(&sz->fifo) > 0) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ }
+
+ len = kfifo_len(&sz->fifo);
+ if (len > 0) {
+ while ((len < SZ_BUF_LEN / 2) &&
+ (len < SZ_BUF_LEN * sizeof(int))) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ len = kfifo_len(&sz->fifo);
+ }
+ if (sz->timer_running)
+ mod_timer(&sz->delay_timer, jiffies + delay);
+
+ } else {
+ sz->timer_running = false;
+ }
+
+ if (wake)
+ ir_raw_event_handle(sz->idev);
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void streamzap_flush_delay_buffer(struct streamzap_ir *sz)
+{
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ bool wake = false;
+ int ret;
+
+ while (kfifo_len(&sz->fifo) > 0) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ }
+
+ if (wake)
+ ir_raw_event_handle(sz->idev);
+}
+
+static void sz_push(struct streamzap_ir *sz)
+{
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (kfifo_len(&sz->fifo) >= sizeof(int) * SZ_BUF_LEN) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ }
+
+ kfifo_in(&sz->fifo, &sz->rawir, sizeof(rawir));
+
+ if (!sz->timer_running) {
+ sz->delay_timer.expires = jiffies + (HZ / 10);
+ add_timer(&sz->delay_timer);
+ sz->timer_running = true;
+ }
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void sz_push_full_pulse(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ if (sz->idle) {
+ long deltv;
+
+ sz->signal_last = sz->signal_start;
+ do_gettimeofday(&sz->signal_start);
+
+ deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
+ sz->rawir.pulse = false;
+ if (deltv > 15) {
+ /* really long time */
+ sz->rawir.duration = IR_MAX_DURATION;
+ } else {
+ sz->rawir.duration = (int)(deltv * 1000000 +
+ sz->signal_start.tv_usec -
+ sz->signal_last.tv_usec);
+ sz->rawir.duration -= sz->sum;
+ sz->rawir.duration *= 1000;
+ sz->rawir.duration &= IR_MAX_DURATION;
+ }
+ dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration);
+ sz_push(sz);
+
+ sz->idle = 0;
+ sz->sum = 0;
+ }
+
+ sz->rawir.pulse = true;
+ sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+ sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+ sz->sum += sz->rawir.duration;
+ sz->rawir.duration *= 1000;
+ sz->rawir.duration &= IR_MAX_DURATION;
+ dev_dbg(sz->dev, "p %u\n", sz->rawir.duration);
+ sz_push(sz);
+}
+
+static void sz_push_half_pulse(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4);
+}
+
+static void sz_push_full_space(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ sz->rawir.pulse = false;
+ sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+ sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+ sz->sum += sz->rawir.duration;
+ sz->rawir.duration *= 1000;
+ dev_dbg(sz->dev, "s %u\n", sz->rawir.duration);
+ sz_push(sz);
+}
+
+static void sz_push_half_space(struct streamzap_ir *sz,
+ unsigned long value)
+{
+ sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/**
+ * streamzap_callback - usb IRQ handler callback
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void streamzap_callback(struct urb *urb)
+{
+ struct streamzap_ir *sz;
+ unsigned int i;
+ int len;
+ #if 0
+ static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) &
+ IR_MAX_DURATION) | 0x03000000);
+ #endif
+
+ if (!urb)
+ return;
+
+ sz = urb->context;
+ len = urb->actual_length;
+
+ switch (urb->status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /*
+ * this urb is terminated, clean up.
+ * sz might already be invalid at this point
+ */
+ dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
+ return;
+ default:
+ break;
+ }
+
+ dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
+ if (!sz->flush) {
+ for (i = 0; i < urb->actual_length; i++) {
+ dev_dbg(sz->dev, "%d: %x\n", i,
+ (unsigned char)sz->buf_in[i]);
+ switch (sz->decoder_state) {
+ case PulseSpace:
+ if ((sz->buf_in[i] & STREAMZAP_PULSE_MASK) ==
+ STREAMZAP_PULSE_MASK) {
+ sz->decoder_state = FullPulse;
+ continue;
+ } else if ((sz->buf_in[i] & STREAMZAP_SPACE_MASK)
+ == STREAMZAP_SPACE_MASK) {
+ sz_push_half_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = FullSpace;
+ continue;
+ } else {
+ sz_push_half_pulse(sz, sz->buf_in[i]);
+ sz_push_half_space(sz, sz->buf_in[i]);
+ }
+ break;
+ case FullPulse:
+ sz_push_full_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = IgnorePulse;
+ break;
+ case FullSpace:
+ if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
+ sz->idle = 1;
+ streamzap_stop_timer(sz);
+ #if 0
+ if (sz->timeout_enabled) {
+ sz->rawir.pulse = false;
+ sz->rawir.duration = timeout;
+ sz->rawir.duration *= 1000;
+ sz_push(sz);
+ }
+ #endif
+ streamzap_flush_delay_buffer(sz);
+ } else
+ sz_push_full_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ case IgnorePulse:
+ if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+ STREAMZAP_SPACE_MASK) {
+ sz->decoder_state = FullSpace;
+ continue;
+ }
+ sz_push_half_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ }
+ }
+ }
+
+ usb_submit_urb(urb, GFP_ATOMIC);
+
+ return;
+}
+
+static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz)
+{
+ struct input_dev *idev;
+ struct ir_dev_props *props;
+ struct device *dev = sz->dev;
+ int ret;
+
+ idev = input_allocate_device();
+ if (!idev) {
+ dev_err(dev, "remote input dev allocation failed\n");
+ goto idev_alloc_failed;
+ }
+
+ props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+ if (!props) {
+ dev_err(dev, "remote ir dev props allocation failed\n");
+ goto props_alloc_failed;
+ }
+
+ snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared "
+ "Receiver (%04x:%04x)",
+ le16_to_cpu(sz->usbdev->descriptor.idVendor),
+ le16_to_cpu(sz->usbdev->descriptor.idProduct));
+
+ idev->name = sz->name;
+ usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
+ strlcat(sz->phys, "/input0", sizeof(sz->phys));
+ idev->phys = sz->phys;
+
+ props->priv = sz;
+ props->driver_type = RC_DRIVER_IR_RAW;
+ /* FIXME: not sure about supported protocols, check on this */
+ props->allowed_protos = IR_TYPE_RC5 | IR_TYPE_RC6;
+
+ sz->props = props;
+
+ ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME);
+ if (ret < 0) {
+ dev_err(dev, "remote input device register failed\n");
+ goto irdev_failed;
+ }
+
+ return idev;
+
+irdev_failed:
+ kfree(props);
+props_alloc_failed:
+ input_free_device(idev);
+idev_alloc_failed:
+ return NULL;
+}
+
+static int streamzap_delay_buf_init(struct streamzap_ir *sz)
+{
+ int ret;
+
+ ret = kfifo_alloc(&sz->fifo, sizeof(int) * SZ_BUF_LEN,
+ GFP_KERNEL);
+ if (ret == 0)
+ sz->fifo_initialized = 1;
+
+ return ret;
+}
+
+static void streamzap_start_flush_timer(struct streamzap_ir *sz)
+{
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = true;
+ add_timer(&sz->flush_timer);
+
+ sz->urb_in->dev = sz->usbdev;
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+ dev_err(sz->dev, "urb submit failed\n");
+}
+
+/**
+ * streamzap_probe
+ *
+ * Called by usb-core to associated with a candidate device
+ * On any failure the return value is the ERROR
+ * On success return 0
+ */
+static int __devinit streamzap_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct usb_host_interface *iface_host;
+ struct streamzap_ir *sz = NULL;
+ char buf[63], name[128] = "";
+ int retval = -ENOMEM;
+ int pipe, maxp;
+
+ /* Allocate space for device driver specific data */
+ sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
+ if (!sz)
+ return -ENOMEM;
+
+ sz->usbdev = usbdev;
+ sz->interface = intf;
+
+ /* Check to ensure endpoint information matches requirements */
+ iface_host = intf->cur_altsetting;
+
+ if (iface_host->desc.bNumEndpoints != 1) {
+ dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
+ __func__, iface_host->desc.bNumEndpoints);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ sz->endpoint = &(iface_host->endpoint[0].desc);
+ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ != USB_DIR_IN) {
+ dev_err(&intf->dev, "%s: endpoint doesn't match input device "
+ "02%02x\n", __func__, sz->endpoint->bEndpointAddress);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_INT) {
+ dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
+ "02%02x\n", __func__, sz->endpoint->bmAttributes);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
+
+ if (maxp == 0) {
+ dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
+ __func__);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ /* Allocate the USB buffer and IRQ URB */
+ sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
+ if (!sz->buf_in)
+ goto free_sz;
+
+ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!sz->urb_in)
+ goto free_buf_in;
+
+ sz->dev = &intf->dev;
+ sz->buf_in_len = maxp;
+
+ if (usbdev->descriptor.iManufacturer
+ && usb_string(usbdev, usbdev->descriptor.iManufacturer,
+ buf, sizeof(buf)) > 0)
+ strlcpy(name, buf, sizeof(name));
+
+ if (usbdev->descriptor.iProduct
+ && usb_string(usbdev, usbdev->descriptor.iProduct,
+ buf, sizeof(buf)) > 0)
+ snprintf(name + strlen(name), sizeof(name) - strlen(name),
+ " %s", buf);
+
+ retval = streamzap_delay_buf_init(sz);
+ if (retval) {
+ dev_err(&intf->dev, "%s: delay buffer init failed\n", __func__);
+ goto free_urb_in;
+ }
+
+ sz->idev = streamzap_init_input_dev(sz);
+ if (!sz->idev)
+ goto input_dev_fail;
+
+ sz->idle = true;
+ sz->decoder_state = PulseSpace;
+ #if 0
+ /* not yet supported, depends on patches from maxim */
+ /* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
+ sz->timeout_enabled = false;
+ sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+ sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+ #endif
+
+ init_timer(&sz->delay_timer);
+ sz->delay_timer.function = streamzap_delay_timeout;
+ sz->delay_timer.data = (unsigned long)sz;
+ spin_lock_init(&sz->timer_lock);
+
+ init_timer(&sz->flush_timer);
+ sz->flush_timer.function = streamzap_flush_timeout;
+ sz->flush_timer.data = (unsigned long)sz;
+
+ do_gettimeofday(&sz->signal_start);
+
+ /* Complete final initialisations */
+ usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
+ maxp, (usb_complete_t)streamzap_callback,
+ sz, sz->endpoint->bInterval);
+ sz->urb_in->transfer_dma = sz->dma_in;
+ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, sz);
+
+ streamzap_start_flush_timer(sz);
+
+ dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
+ usbdev->bus->busnum, usbdev->devnum);
+
+ return 0;
+
+input_dev_fail:
+ kfifo_free(&sz->fifo);
+free_urb_in:
+ usb_free_urb(sz->urb_in);
+free_buf_in:
+ usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
+free_sz:
+ kfree(sz);
+
+ return retval;
+}
+
+/**
+ * streamzap_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->usbdev. It is also supposed to terminate any currently
+ * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ * does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(interface);
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+
+ usb_set_intfdata(interface, NULL);
+
+ if (!sz)
+ return;
+
+ if (sz->flush) {
+ sz->flush = false;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ streamzap_stop_timer(sz);
+
+ sz->usbdev = NULL;
+ ir_input_unregister(sz->idev);
+ usb_kill_urb(sz->urb_in);
+ usb_free_urb(sz->urb_in);
+ usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+ kfree(sz);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+ if (sz->flush) {
+ sz->flush = false;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ streamzap_stop_timer(sz);
+
+ usb_kill_urb(sz->urb_in);
+
+ return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+ if (sz->fifo_initialized)
+ kfifo_reset(&sz->fifo);
+
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = true;
+ add_timer(&sz->flush_timer);
+
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+ dev_err(sz->dev, "Error sumbiting urb\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * streamzap_init
+ */
+static int __init streamzap_init(void)
+{
+ int ret;
+
+ /* register this driver with the USB subsystem */
+ ret = usb_register(&streamzap_driver);
+ if (ret < 0)
+ printk(KERN_ERR DRIVER_NAME ": usb register failed, "
+ "result = %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * streamzap_exit
+ */
+static void __exit streamzap_exit(void)
+{
+ usb_deregister(&streamzap_driver);
+}
+
+
+module_init(streamzap_init);
+module_exit(streamzap_exit);
+
+MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index 409a426..b3ed5da 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -34,7 +34,7 @@ config MEDIA_TUNER
menuconfig MEDIA_TUNER_CUSTOMISE
bool "Customize analog and hybrid tuner modules to build"
depends on MEDIA_TUNER
- default n
+ default y if EMBEDDED
help
This allows the user to deselect tuner drivers unnecessary
for their hardware from the build. Use this option with care
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
index 248a2a9..caa4e18 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -1763,7 +1763,15 @@ static struct dvb_frontend_ops dst_dvbt_ops = {
.frequency_min = 137000000,
.frequency_max = 858000000,
.frequency_stepsize = 166667,
- .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_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 |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO
},
.release = dst_release,
diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig
index 6952392..a6ceb08 100644
--- a/drivers/media/dvb/dm1105/Kconfig
+++ b/drivers/media/dvb/dm1105/Kconfig
@@ -9,7 +9,7 @@ config DVB_DM1105
select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_SI21XX if !DVB_FE_CUSTOMISE
select DVB_DS3000 if !DVB_FE_CUSTOMISE
- select VIDEO_IR
+ depends on VIDEO_IR
help
Support for cards based on the SDMC DM1105 PCI chip like
DvbWorld 2002
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 553b48a..fdc19bb 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -1,6 +1,6 @@
config DVB_USB
tristate "Support for various USB DVB devices"
- depends on DVB_CORE && USB && I2C && INPUT
+ depends on DVB_CORE && USB && I2C && IR_CORE
help
By enabling this you will be able to choose the various supported
USB1.1 and USB2.0 DVB devices.
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 51d578a..b5f6a04 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -1,7 +1,7 @@
config DVB_FE_CUSTOMISE
bool "Customise the frontend modules to build"
depends on DVB_CORE
- default N
+ default y if EMBEDDED
help
This allows the user to select/deselect frontend drivers for their
hardware from the build.
diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig
index 85a222c..e520bce 100644
--- a/drivers/media/dvb/siano/Kconfig
+++ b/drivers/media/dvb/siano/Kconfig
@@ -4,7 +4,7 @@
config SMS_SIANO_MDTV
tristate "Siano SMS1xxx based MDTV receiver"
- depends on DVB_CORE && INPUT && HAS_DMA
+ depends on DVB_CORE && IR_CORE && HAS_DMA
---help---
Choose Y or M here if you have MDTV receiver with a Siano chipset.
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index 7f2c94a1..d93468c 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -1113,9 +1113,11 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
*/
prepare_to_wait(&coredev->buffer_mng_waitq, &wait, TASK_INTERRUPTIBLE);
-
- if (list_empty(&coredev->buffers))
+ if (list_empty(&coredev->buffers)) {
+ spin_unlock_irqrestore(&coredev->bufferslock, flags);
schedule();
+ spin_lock_irqsave(&coredev->bufferslock, flags);
+ }
finish_wait(&coredev->buffer_mng_waitq, &wait);
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 32a7ec6..debea8d 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -98,7 +98,7 @@ config DVB_BUDGET_CI
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
- select VIDEO_IR
+ depends on VIDEO_IR
help
Support for simple SAA7146 based DVB cards
(so called Budget- or Nova-PCI cards) without onboard
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 2e15903..f6e4d04 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -83,7 +83,7 @@ config VIDEO_FIXED_MINOR_RANGES
config VIDEO_HELPER_CHIPS_AUTO
bool "Autoselect pertinent encoders/decoders and other helper chips"
- default y
+ default y if !EMBEDDED
---help---
Most video cards may require additional modules to encode or
decode audio/video standards. This option will autoselect
@@ -792,10 +792,11 @@ config SOC_CAMERA_MT9M001
and colour models.
config SOC_CAMERA_MT9M111
- tristate "mt9m111 and mt9m112 support"
+ tristate "mt9m111, mt9m112 and mt9m131 support"
depends on SOC_CAMERA && I2C
help
- This driver supports MT9M111 and MT9M112 cameras from Micron
+ This driver supports MT9M111, MT9M112 and MT9M131 cameras from
+ Micron/Aptina
config SOC_CAMERA_MT9T031
tristate "mt9t031 support"
@@ -1016,4 +1017,13 @@ config VIDEO_MEM2MEM_TESTDEV
This is a virtual test device for the memory-to-memory driver
framework.
+config VIDEO_SAMSUNG_S5P_FIMC
+ tristate "Samsung S5P FIMC (video postprocessor) driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for the S5P camera interface
+ (video postprocessor)
+
endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 1051ecc6..40f98fb 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
- v4l2-event.o
+ v4l2-event.o v4l2-ctrls.o
# V4L2 core modules
@@ -163,6 +163,7 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
index 3077c450..1a4a89f 100644
--- a/drivers/media/video/bt8xx/Kconfig
+++ b/drivers/media/video/bt8xx/Kconfig
@@ -4,7 +4,7 @@ config VIDEO_BT848
select I2C_ALGOBIT
select VIDEO_BTCX
select VIDEOBUF_DMA_SG
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 407fa61..685d659 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -411,7 +411,7 @@ void __devinit init_bttv_i2c_ir(struct bttv *btv)
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
- i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list);
+ i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
}
}
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index 3cc135a..cc9e84d 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -26,10 +26,10 @@
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
-#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
@@ -43,6 +43,21 @@ module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
+struct cs53l32a_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cs53l32a_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@@ -74,31 +89,20 @@ static int cs53l32a_s_routing(struct v4l2_subdev *sd,
return 0;
}
-static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
{
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
- return 0;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = (s8)cs53l32a_read(sd, 0x04);
- return 0;
-}
+ struct v4l2_subdev *sd = to_sd(ctrl);
-static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ cs53l32a_write(sd, 0x04, (u8)ctrl->val);
+ cs53l32a_write(sd, 0x05, (u8)ctrl->val);
return 0;
}
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 12 || ctrl->value < -96)
- return -EINVAL;
- cs53l32a_write(sd, 0x04, (u8) ctrl->value);
- cs53l32a_write(sd, 0x05, (u8) ctrl->value);
- return 0;
+ return -EINVAL;
}
static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@@ -111,23 +115,30 @@ static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_id
static int cs53l32a_log_status(struct v4l2_subdev *sd)
{
+ struct cs53l32a_state *state = to_state(sd);
u8 v = cs53l32a_read(sd, 0x01);
- u8 m = cs53l32a_read(sd, 0x03);
- s8 vol = cs53l32a_read(sd, 0x04);
- v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3,
- (m & 0xC0) ? " (muted)" : "");
- v4l2_info(sd, "Volume: %d dB\n", vol);
+ v4l2_info(sd, "Input: %d\n", (v >> 4) & 3);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
+ .s_ctrl = cs53l32a_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
.log_status = cs53l32a_log_status,
.g_chip_ident = cs53l32a_g_chip_ident,
- .g_ctrl = cs53l32a_g_ctrl,
- .s_ctrl = cs53l32a_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
@@ -151,6 +162,7 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {
static int cs53l32a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct cs53l32a_state *state;
struct v4l2_subdev *sd;
int i;
@@ -164,9 +176,10 @@ static int cs53l32a_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
- if (sd == NULL)
+ state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
+ if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
for (i = 1; i <= 7; i++) {
@@ -175,15 +188,29 @@ static int cs53l32a_probe(struct i2c_client *client,
v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
- cs53l32a_write(sd, 0x01, (u8) 0x21);
- cs53l32a_write(sd, 0x02, (u8) 0x29);
- cs53l32a_write(sd, 0x03, (u8) 0x30);
- cs53l32a_write(sd, 0x04, (u8) 0x00);
- cs53l32a_write(sd, 0x05, (u8) 0x00);
- cs53l32a_write(sd, 0x06, (u8) 0x00);
- cs53l32a_write(sd, 0x07, (u8) 0x00);
+ cs53l32a_write(sd, 0x01, 0x21);
+ cs53l32a_write(sd, 0x02, 0x29);
+ cs53l32a_write(sd, 0x03, 0x30);
+ cs53l32a_write(sd, 0x04, 0x00);
+ cs53l32a_write(sd, 0x05, 0x00);
+ cs53l32a_write(sd, 0x06, 0x00);
+ cs53l32a_write(sd, 0x07, 0x00);
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
@@ -198,9 +225,11 @@ static int cs53l32a_probe(struct i2c_client *client,
static int cs53l32a_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cs53l32a_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
index baf7e91..76c054d 100644
--- a/drivers/media/video/cx18/Kconfig
+++ b/drivers/media/video/cx18/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_CX18
depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
depends on INPUT # due to VIDEO_IR
select I2C_ALGOBIT
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index 809f7d3..73ce90c 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -117,7 +117,8 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
break;
}
- return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0;
+ return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
+ -1 : 0;
}
int cx18_i2c_register(struct cx18 *cx, unsigned idx)
diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
index 477d4ab..5ac7ece 100644
--- a/drivers/media/video/cx231xx/Kconfig
+++ b/drivers/media/video/cx231xx/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_CX231XX
depends on VIDEO_DEV && I2C && INPUT
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEOBUF_VMALLOC
select VIDEO_CX25840
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 2bf44ef..e5c3c8d 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -38,6 +38,145 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
+/********************** COMMON CODE *********************/
+
+/* definitions for audio properties bits 29-28 */
+#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
+#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
+#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
+
+static const char *cx2341x_get_name(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return "Spatial Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ return "Spatial Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Luma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Chroma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return "Temporal Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ return "Temporal Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return "Median Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ return "Median Luma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Luma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ return "Median Chroma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Chroma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return "Insert Navigation Packets";
+ }
+ return NULL;
+}
+
+static const char **cx2341x_get_menu(u32 id)
+{
+ static const char *cx2341x_video_spatial_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ "1D Vertical",
+ "2D H/V Separable",
+ "2D Symmetric non-separable",
+ NULL
+ };
+
+ static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ NULL
+ };
+
+ static const char *cx2341x_video_temporal_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_median_filter_type_menu[] = {
+ "Off",
+ "Horizontal",
+ "Vertical",
+ "Horizontal/Vertical",
+ "Diagonal",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return cx2341x_video_spatial_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_luma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_chroma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return cx2341x_video_temporal_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_video_median_filter_type_menu;
+ }
+ return NULL;
+}
+
+static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = cx2341x_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *type = V4L2_CTRL_TYPE_MENU;
+ *min = 0;
+ *step = 0;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ }
+}
+
+
+/********************** OLD CODE *********************/
+
/* Must be sorted from low to high control ID! */
const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CLASS,
@@ -134,8 +273,6 @@ static const struct cx2341x_mpeg_params default_params = {
.video_chroma_median_filter_top = 255,
.video_chroma_median_filter_bottom = 0,
};
-
-
/* Map the control ID to the correct field in the cx2341x_mpeg_params
struct. Return -EINVAL if the ID is unknown, else return 0. */
static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
@@ -415,83 +552,33 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
{
const char *name;
- qctrl->flags = 0;
switch (qctrl->id) {
/* MPEG controls */
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- name = "Spatial Filter Mode";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
- name = "Spatial Filter";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
- name = "Spatial Luma Filter Type";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
- name = "Spatial Chroma Filter Type";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- name = "Temporal Filter Mode";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
- name = "Temporal Filter";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- name = "Median Filter Type";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
- name = "Median Luma Filter Maximum";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
- name = "Median Luma Filter Minimum";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
- name = "Median Chroma Filter Maximum";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
- name = "Median Chroma Filter Minimum";
- break;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
- name = "Insert Navigation Packets";
- break;
+ cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
default:
return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
}
- switch (qctrl->id) {
- case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- qctrl->type = V4L2_CTRL_TYPE_MENU;
- min = 0;
- step = 1;
- break;
- case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
- qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
- min = 0;
- max = 1;
- step = 1;
- break;
- default:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- break;
- }
- switch (qctrl->id) {
- case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
- break;
- }
- qctrl->minimum = min;
- qctrl->maximum = max;
- qctrl->step = step;
- qctrl->default_value = def;
- qctrl->reserved[0] = qctrl->reserved[1] = 0;
- snprintf(qctrl->name, sizeof(qctrl->name), name);
- return 0;
}
int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
@@ -797,42 +884,6 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
NULL
};
- static const char *cx2341x_video_spatial_filter_mode_menu[] = {
- "Manual",
- "Auto",
- NULL
- };
-
- static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
- "Off",
- "1D Horizontal",
- "1D Vertical",
- "2D H/V Separable",
- "2D Symmetric non-separable",
- NULL
- };
-
- static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
- "Off",
- "1D Horizontal",
- NULL
- };
-
- static const char *cx2341x_video_temporal_filter_mode_menu[] = {
- "Manual",
- "Auto",
- NULL
- };
-
- static const char *cx2341x_video_median_filter_type_menu[] = {
- "Off",
- "Horizontal",
- "Vertical",
- "Horizontal/Vertical",
- "Diagonal",
- NULL
- };
-
switch (id) {
case V4L2_CID_MPEG_STREAM_TYPE:
return (p->capabilities & CX2341X_CAP_HAS_TS) ?
@@ -844,26 +895,17 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
return NULL;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- return cx2341x_video_spatial_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
- return cx2341x_video_luma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
- return cx2341x_video_chroma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- return cx2341x_video_temporal_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- return cx2341x_video_median_filter_type_menu;
+ return cx2341x_get_menu(id);
default:
return v4l2_ctrl_get_menu(id);
}
}
EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
-/* definitions for audio properties bits 29-28 */
-#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
-#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
-#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
-
static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
{
params->audio_properties =
@@ -1195,9 +1237,490 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
}
EXPORT_SYMBOL(cx2341x_log_status);
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+
+/********************** NEW CODE *********************/
+
+static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct cx2341x_handler, hdl);
+}
+
+static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
+ u32 cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i;
+
+ va_start(vargs, args);
+
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(vargs, int);
+ va_end(vargs);
+ return hdl->func(hdl->priv, cmd, args, 0, data);
+}
+
+/* ctrl->handler->lock is held, so it is safe to access cur.val */
+static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
+{
+ return ctrl && ctrl->val != ctrl->cur.val;
+}
+
+static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+ /* video gop cluster */
+ int b = val + 1;
+ int gop = hdl->video_gop_size->val;
+
+ gop = b * ((gop + b - 1) / b);
+
+ /* Max GOP size = 34 */
+ while (gop > 34)
+ gop -= b;
+ hdl->video_gop_size->val = gop;
+ break;
+ }
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ hdl->video_encoding->val =
+ (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ /* MPEG-1 implies CBR */
+ hdl->video_bitrate_mode->val =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ /* peak bitrate shall be >= normal bitrate */
+ if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
+ hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
+ break;
+ }
+ return 0;
+}
+
+static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const int mpeg_stream_type[] = {
+ 0, /* MPEG-2 PS */
+ 1, /* MPEG-2 TS */
+ 2, /* MPEG-1 SS */
+ 14, /* DVD */
+ 11, /* VCD */
+ 12, /* SVCD */
+ };
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+ u32 props;
+ int err;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
+ return hdl->ops->s_stream_vbi_fmt(hdl, val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
+
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
+
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
+
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ /* audio properties cluster */
+ props = (hdl->audio_sampling_freq->val << 0) |
+ (hdl->audio_mode->val << 8) |
+ (hdl->audio_mode_extension->val << 10) |
+ (hdl->audio_crc->val << 14);
+ if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ props |= 3 << 12;
+ else
+ props |= hdl->audio_emphasis->val << 12;
+
+ if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ props |=
+#if 1
+ /* Not sure if this MPEG Layer II setting is required */
+ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+#endif
+ (hdl->audio_ac3_bitrate->val << 4) |
+ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+ } else {
+ /* Assuming MPEG Layer II */
+ props |=
+ ((3 - hdl->audio_encoding->val) << 2) |
+ ((1 + hdl->audio_l2_bitrate->val) << 4);
+ }
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
+ if (err)
+ return err;
+
+ hdl->audio_properties = props;
+ if (hdl->audio_ac3_bitrate) {
+ int is_ac3 = hdl->audio_encoding->val ==
+ V4L2_MPEG_AUDIO_ENCODING_AC3;
+
+ v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
+ v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+ }
+ v4l2_ctrl_activate(hdl->audio_mode_extension,
+ hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
+ if (cx2341x_neq(hdl->audio_sampling_freq) &&
+ hdl->ops && hdl->ops->s_audio_sampling_freq)
+ return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
+ if (cx2341x_neq(hdl->audio_mode) &&
+ hdl->ops && hdl->ops->s_audio_mode)
+ return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ /* video gop cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+ hdl->video_gop_size->val,
+ hdl->video_b_frames->val + 1);
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
+ if (err)
+ return err;
+
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
+ hdl->video_bitrate_mode->val,
+ hdl->video_bitrate->val,
+ hdl->video_bitrate_peak->val / 400, 0, 0);
+ if (err)
+ return err;
+
+ v4l2_ctrl_activate(hdl->video_bitrate_mode,
+ hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
+ v4l2_ctrl_activate(hdl->video_bitrate_peak,
+ hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+ if (cx2341x_neq(hdl->video_encoding) &&
+ hdl->ops && hdl->ops->s_video_encoding)
+ return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ /* video mute cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
+ hdl->video_mute->val |
+ (hdl->video_mute_yuv->val << 8));
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
+ int active_filter;
+
+ /* video filter mode */
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
+ hdl->video_spatial_filter_mode->val |
+ (hdl->video_temporal_filter_mode->val << 1),
+ hdl->video_median_filter_type->val);
+ if (err)
+ return err;
+
+ active_filter = hdl->video_spatial_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+ active_filter = hdl->video_temporal_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+ active_filter = hdl->video_median_filter_type->val !=
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+ return 0;
+ }
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ /* video filter type cluster */
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
+ hdl->video_luma_spatial_filter_type->val,
+ hdl->video_chroma_spatial_filter_type->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ /* video filter cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
+ hdl->video_spatial_filter->val,
+ hdl->video_temporal_filter->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ /* video median cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
+ hdl->video_luma_median_filter_bottom->val,
+ hdl->video_luma_median_filter_top->val,
+ hdl->video_chroma_median_filter_bottom->val,
+ hdl->video_chroma_median_filter_top->val);
+ }
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops cx2341x_ops = {
+ .try_ctrl = cx2341x_try_ctrl,
+ .s_ctrl = cx2341x_s_ctrl,
+};
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ struct v4l2_ctrl_config cfg;
+
+ cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
+ cfg.ops = &cx2341x_ops;
+ cfg.id = id;
+ cfg.min = min;
+ cfg.max = max;
+ cfg.def = def;
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = step;
+ cfg.qmenu = cx2341x_get_menu(id);
+ } else {
+ cfg.step = step;
+ cfg.menu_skip_mask = 0;
+ }
+ return v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def);
+}
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+ unsigned nr_of_controls_hint)
+{
+ struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
+ u32 caps = cxhdl->capabilities;
+ int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
+ int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
+ int has_ts = caps & CX2341X_CAP_HAS_TS;
+
+ cxhdl->width = 720;
+ cxhdl->height = 480;
+
+ v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
+
+ /* Add controls in ascending control ID order for fastest
+ insertion time. */
+ cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+ cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+ cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+ cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+ cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff,
+ V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+ cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE,
+ V4L2_MPEG_AUDIO_MODE_MONO, 0,
+ V4L2_MPEG_AUDIO_MODE_STEREO);
+ cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+ cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_MPEG_AUDIO_CRC_CRC16, 0,
+ V4L2_MPEG_AUDIO_CRC_NONE);
+
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
+ if (has_ac3)
+ cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
+ cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+ cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
+ cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 1, 34, 1, cxhdl->is_50hz ? 12 : 15);
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
+ cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0, 27000000, 1, 6000000);
+ cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0, 27000000, 1, 8000000);
+ cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
+ cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
+ cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+ cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ 0, 15, 1, 0);
+ cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+ cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ 0, 31, 1, 8);
+ cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+ cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+ 0, 1, 1, 0);
+
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
+ v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
+ v4l2_ctrl_cluster(5, &cxhdl->stream_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_mute);
+ v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
+ v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
+ v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx2341x_handler_init);
+
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
+{
+ cxhdl->is_50hz = is_50hz;
+ cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
+}
+EXPORT_SYMBOL(cx2341x_handler_set_50hz);
+
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
+{
+ int h = cxhdl->height;
+ int w = cxhdl->width;
+ int err;
+
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
+ if (err)
+ return err;
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
+ if (err)
+ return err;
+
+ if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ w /= 2;
+ h /= 2;
+ }
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+ if (err)
+ return err;
+ return v4l2_ctrl_handler_setup(&cxhdl->hdl);
+}
+EXPORT_SYMBOL(cx2341x_handler_setup);
+
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
+{
+ v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
+ v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
+ v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
+ v4l2_ctrl_grab(cxhdl->stream_type, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+}
+EXPORT_SYMBOL(cx2341x_handler_set_busy);
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index bcdda9a..e1367b3 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -5,7 +5,7 @@ config VIDEO_CX23885
select VIDEO_BTCX
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_IR
+ depends on IR_CORE
select VIDEOBUF_DVB
select VIDEOBUF_DMA_SG
select VIDEO_CX25840
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index 5787ae2..e2ee95f 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -1,7 +1,8 @@
cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
- cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
- netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
+ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+ cx23885-f300.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
diff --git a/drivers/media/video/cx23885/cx23885-av.c b/drivers/media/video/cx23885/cx23885-av.c
new file mode 100644
index 0000000..134ebdd
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-av.c
@@ -0,0 +1,35 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx23885.h"
+
+void cx23885_av_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, cx25840_work);
+ bool handled;
+
+ v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
+ PCI_MSK_AV_CORE, &handled);
+ cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
+}
diff --git a/drivers/media/video/cx23885/cx23885-av.h b/drivers/media/video/cx23885/cx23885-av.h
new file mode 100644
index 0000000..d2915c3
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-av.h
@@ -0,0 +1,27 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX23885_AV_H_
+#define _CX23885_AV_H_
+void cx23885_av_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 2014dae..e76ce87 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -30,6 +30,16 @@
#include "netup-init.h"
#include "cx23888-ir.h"
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+ "Enable integrated IR controller for supported\n"
+ "\t\t CX2388[57] boards that are wired for it:\n"
+ "\t\t\tHVR-1250 (reported safe)\n"
+ "\t\t\tTeVii S470 (reported unsafe)\n"
+ "\t\t This can cause an interrupt storm with some cards.\n"
+ "\t\t Default: 0 [Disabled]");
+
/* ------------------------------------------------------------------ */
/* board config info */
@@ -626,6 +636,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
case 79101:
/* WinTV-HVR1250 (PCIe, Retail, IR, half height,
ATSC and Basic analog */
+ case 79501:
+ /* WinTV-HVR1250 (PCIe, No IR, half height,
+ ATSC [at least] and Basic analog) */
case 79561:
/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
ATSC and Basic analog */
@@ -959,9 +972,37 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
int cx23885_ir_init(struct cx23885_dev *dev)
{
+ static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }, {
+ .flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
+ .pin = CX23885_PIN_IR_TX_GPIO20,
+ .function = CX23885_PAD_IR_TX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+ static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
+
+ struct v4l2_subdev_ir_parameters params;
int ret = 0;
switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
@@ -979,7 +1020,41 @@ int cx23885_ir_init(struct cx23885_dev *dev)
if (ret)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
- dev->pci_irqmask |= PCI_MSK_IR;
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ /*
+ * For these boards we need to invert the Tx output via the
+ * IR controller to have the LED off while idle
+ */
+ v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
+ params.enable = false;
+ params.shutdown = false;
+ params.invert_level = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
@@ -994,11 +1069,16 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- dev->pci_irqmask &= ~PCI_MSK_IR;
- cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+ cx23885_irq_remove(dev, PCI_MSK_IR);
cx23888_ir_remove(dev);
dev->sd_ir = NULL;
break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
+ /* sd_ir is a duplicate pointer to the AV Core, just clear it */
+ dev->sd_ir = NULL;
+ break;
}
}
@@ -1007,8 +1087,13 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
- cx_set(PCI_INT_MSK, PCI_MSK_IR);
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_IR);
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
break;
}
}
@@ -1028,6 +1113,13 @@ void cx23885_card_setup(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ if (eeprom[0x80] != 0x84)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ else
+ hauppauge_eeprom(dev, eeprom+0x80);
+ }
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
@@ -1136,6 +1228,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
* loaded, ensure this happens.
*/
switch (dev->board) {
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Currently only enabled for the integrated IR controller */
+ if (!enable_885_ir)
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
@@ -1151,7 +1248,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
- v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ if (dev->sd_cx25840) {
+ dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+ v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ }
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index ff76f64..f6b62e7 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -34,6 +34,7 @@
#include "cimax2.h"
#include "cx23888-ir.h"
#include "cx23885-ir.h"
+#include "cx23885-av.h"
#include "cx23885-input.h"
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
@@ -299,6 +300,83 @@ static struct sram_channel cx23887_sram_channels[] = {
},
};
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+ cx_set(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = mask & dev->pci_irqmask;
+ if (v)
+ cx_set(PCI_INT_MSK, v);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask &= ~mask;
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = cx_read(PCI_INT_MSK);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+ return v;
+}
+
static int cx23885_risc_decode(u32 risc)
{
static char *instr[16] = {
@@ -548,7 +626,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev)
cx_write(UART_CTL, 0);
/* Disable Interrupts */
- cx_write(PCI_INT_MSK, 0);
+ cx23885_irq_disable_all(dev);
cx_write(VID_A_INT_MSK, 0);
cx_write(VID_B_INT_MSK, 0);
cx_write(VID_C_INT_MSK, 0);
@@ -774,6 +852,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
+ spin_lock_init(&dev->pci_irqmask_lock);
+
mutex_init(&dev->lock);
mutex_init(&dev->gpio_lock);
@@ -820,9 +900,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
- dev->pci_irqmask = 0x001f00;
+ cx23885_irq_add(dev, 0x001f00);
if (cx23885_boards[dev->board].cimax > 0)
- dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+ cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
/* External Master 1 Bus */
dev->i2c_bus[0].nr = 0;
@@ -1156,7 +1236,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__,
cx_read(DEV_CNTRL2));
dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__,
- cx_read(PCI_INT_MSK));
+ cx23885_irq_get_mask(dev));
dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__,
cx_read(AUDIO_INT_INT_MSK));
dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__,
@@ -1292,7 +1372,8 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_set(port->reg_dma_ctl, port->dma_ctl_val);
- cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
+ cx23885_irq_add(dev, port->pci_irqmask);
+ cx23885_irq_enable_all(dev);
break;
default:
BUG();
@@ -1650,10 +1731,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
- bool ir_handled = false;
+ bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
- pci_mask = cx_read(PCI_INT_MSK);
+ pci_mask = cx23885_irq_get_mask(dev);
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
@@ -1681,7 +1762,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A |
PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
PCI_MSK_GPIO0 | PCI_MSK_GPIO1 |
- PCI_MSK_IR)) {
+ PCI_MSK_AV_CORE | PCI_MSK_IR)) {
if (pci_status & PCI_MSK_RISC_RD)
dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
@@ -1731,6 +1812,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
PCI_MSK_GPIO1);
+ if (pci_status & PCI_MSK_AV_CORE)
+ dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n",
+ PCI_MSK_AV_CORE);
+
if (pci_status & PCI_MSK_IR)
dprintk(7, " (PCI_MSK_IR 0x%08x)\n",
PCI_MSK_IR);
@@ -1765,12 +1850,22 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
handled += cx23885_video_irq(dev, vida_status);
if (pci_status & PCI_MSK_IR) {
- v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
- pci_status, &ir_handled);
- if (ir_handled)
+ subdev_handled = false;
+ v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
+ pci_status, &subdev_handled);
+ if (subdev_handled)
handled++;
}
+ if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+ cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+ if (!schedule_work(&dev->cx25840_work))
+ printk(KERN_ERR "%s: failed to set up deferred work for"
+ " AV Core/IR interrupt. Interrupt is disabled"
+ " and won't be re-enabled\n", dev->name);
+ handled++;
+ }
+
if (handled)
cx_write(PCI_INT_STAT, pci_status);
out:
@@ -1788,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
dev = to_cx23885(sd->v4l2_dev);
switch (notification) {
- case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+ case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
- case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+ case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
@@ -1801,6 +1896,7 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
{
+ INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
@@ -1967,7 +2063,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
switch (dev->board) {
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+ cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index d4746e0..ed3d8f5 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -99,7 +99,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
- return -EIO;
+ return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
return 0;
@@ -120,11 +120,12 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
+ if (!i2c_slave_did_ack(i2c_adap)) {
+ retval = -ENXIO;
+ goto err;
+ }
if (i2c_debug) {
printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
if (!(ctrl & I2C_NOSTOP))
@@ -145,10 +146,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
@@ -185,7 +183,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
- return -EIO;
+ return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
@@ -209,11 +207,12 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
+ if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
+ retval = -ENXIO;
+ goto err;
+ }
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
@@ -365,17 +364,10 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
- /*
- * We can't call i2c_new_probed_device() because it uses
- * quick writes for probing and the IR receiver device only
- * replies to reads.
- */
- if (i2c_smbus_xfer(&bus->i2c_adap, addr_list[0], 0,
- I2C_SMBUS_READ, 0, I2C_SMBUS_QUICK,
- NULL) >= 0) {
- info.addr = addr_list[0];
- i2c_new_device(&bus->i2c_adap, &info);
- }
+ /* Use quick read command for probe, some IR chips don't
+ * support writes */
+ i2c_new_probed_device(&bus->i2c_adap, &info, addr_list,
+ i2c_probe_func_quick_read);
}
return bus->i2c_rc;
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index d0b1613..bb61870 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -44,40 +44,26 @@
#define MODULE_NAME "cx23885"
-static void convert_measurement(u32 x, struct ir_raw_event *y)
-{
- if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
- y->pulse = false;
- y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
- return;
- }
-
- y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false;
- y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
-}
-
static void cx23885_input_process_measurements(struct cx23885_dev *dev,
bool overrun)
{
struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
- struct ir_raw_event kernel_ir_event;
- u32 sd_ir_data[64];
ssize_t num;
int count, i;
bool handle = false;
+ struct ir_raw_event ir_core_event[64];
do {
num = 0;
- v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data,
- sizeof(sd_ir_data), &num);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+ sizeof(ir_core_event), &num);
- count = num / sizeof(u32);
+ count = num / sizeof(struct ir_raw_event);
for (i = 0; i < count; i++) {
- convert_measurement(sd_ir_data[i], &kernel_ir_event);
ir_raw_event_store(kernel_ir->inp_dev,
- &kernel_ir_event);
+ &ir_core_event[i]);
handle = true;
}
} while (num != 0);
@@ -99,8 +85,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
- * The only board we handle right now. However other boards
+ * The only boards we handle right now. However other boards
* using the CX2388x integrated IR controller should be similar
*/
break;
@@ -148,6 +136,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
@@ -170,7 +159,38 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
* mark is received as low logic level;
* falling edges are detected as rising edges; etc.
*/
- params.invert = true;
+ params.invert_level = true;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for a standard NEC protocol */
+ params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+ params.carrier_range_lower = 33000; /* Hz */
+ params.carrier_range_upper = 43000; /* Hz */
+ params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+ /*
+ * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+ * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+ */
+ params.max_pulse_width = 12378022; /* ns */
+
+ /*
+ * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+ * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+ */
+ params.noise_filter_min_width = 351648; /* ns */
+
+ params.modulation = false;
+ params.invert_level = true;
break;
}
v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
@@ -244,12 +264,20 @@ int cx23885_input_init(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- /* Integrated CX23888 IR controller */
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Integrated CX2388[58] IR controller */
driver_type = RC_DRIVER_IR_RAW;
allowed_protos = IR_TYPE_ALL;
/* The grey Hauppauge RC-5 remote */
rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
break;
+ case CX23885_BOARD_TEVII_S470:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = IR_TYPE_ALL;
+ /* A guess at the remote */
+ rc_map = RC_MAP_TEVII_NEC;
+ break;
default:
return -ENODEV;
}
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
index 6ceabd4..7125247 100644
--- a/drivers/media/video/cx23885/cx23885-ir.c
+++ b/drivers/media/video/cx23885/cx23885-ir.c
@@ -72,7 +72,7 @@ void cx23885_ir_tx_work_handler(struct work_struct *work)
}
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@@ -86,10 +86,18 @@ void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
- schedule_work(&dev->ir_rx_work);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_rx_work_handler(&dev->ir_rx_work);
+ else
+ schedule_work(&dev->ir_rx_work);
}
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@@ -97,5 +105,13 @@ void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
- schedule_work(&dev->ir_tx_work);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_tx_work_handler(&dev->ir_tx_work);
+ else
+ schedule_work(&dev->ir_tx_work);
}
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index c0bc9a0..a28772d 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -213,6 +213,7 @@ Channel manager Data Structure entry = 20 DWORD
#define DEV_CNTRL2 0x00040000
#define PCI_MSK_IR (1 << 28)
+#define PCI_MSK_AV_CORE (1 << 27)
#define PCI_MSK_GPIO1 (1 << 24)
#define PCI_MSK_GPIO0 (1 << 23)
#define PCI_MSK_APB_DMA (1 << 12)
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
index 708a8c7..c0b6038 100644
--- a/drivers/media/video/cx23885/cx23885-vbi.c
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -74,7 +74,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
q->count = 1;
/* enable irqs */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000022);
/* start dma */
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 4e44dcd..da66e5f 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -441,7 +441,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
q->count = 1;
/* enable irq */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000011);
/* start dma */
@@ -1205,6 +1205,21 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ printk(KERN_INFO
+ "%s/0: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ printk(KERN_INFO
+ "%s/0: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qctrl)
{
@@ -1410,6 +1425,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
+ .vidioc_log_status = vidioc_log_status,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
@@ -1449,7 +1465,7 @@ static const struct v4l2_file_operations radio_fops = {
void cx23885_video_unregister(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __func__);
- cx_clear(PCI_INT_MSK, 1);
+ cx23885_irq_remove(dev, 0x01);
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
@@ -1486,7 +1502,8 @@ int cx23885_video_register(struct cx23885_dev *dev)
VID_A_DMA_CTL, 0x11, 0x00);
/* Don't enable VBI yet */
- cx_set(PCI_INT_MSK, 1);
+
+ cx23885_irq_add_enable(dev, 0x01);
if (TUNER_ABSENT != dev->tuner_type) {
struct v4l2_subdev *sd = NULL;
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index a33f2b7..ed94b17 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -325,6 +325,7 @@ struct cx23885_dev {
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
+ spinlock_t pci_irqmask_lock; /* protects mask reg too */
int hwrevision;
/* This valud is board specific and is used to configure the
@@ -365,6 +366,7 @@ struct cx23885_dev {
unsigned char radio_addr;
unsigned int has_radio;
struct v4l2_subdev *sd_cx25840;
+ struct work_struct cx25840_work;
/* Infrared */
struct v4l2_subdev *sd_ir;
@@ -403,7 +405,8 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
#define call_all(dev, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
-#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
#define call_hw(dev, grpid, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
@@ -484,6 +487,10 @@ extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
int asoutput);
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
/* ----------------------------------------------------------- */
/* cx23885-cards.c */
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index f63d378..2502a0a 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -26,6 +26,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/ir-core.h>
#include "cx23885.h"
@@ -60,6 +61,8 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
#define CNTRL_CPL 0x00001000
#define CNTRL_LBM 0x00002000
#define CNTRL_R 0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO 0x00008000
#define CX23888_IR_TXCLK_REG 0x170004
#define TXCLK_TCD 0x0000FFFF
@@ -111,8 +114,18 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
-#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32))
-#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32))
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx23888_ir_fifo_rec {
+ u32 hw_fifo_data;
+ struct ir_raw_event ir_core_data;
+};
+
+#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
+#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
struct cx23888_ir_state {
struct v4l2_subdev sd;
@@ -423,6 +436,13 @@ static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
invert ? CNTRL_CPL : 0);
}
+static inline void control_tx_level_invert(struct cx23885_dev *dev,
+ bool invert)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
+ invert ? CNTRL_IVO : 0);
+}
+
/*
* IR Rx & Tx Clock Register helpers
*/
@@ -449,8 +469,8 @@ static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
{
u64 pulse_clocks;
- if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
- ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
pulse_clocks = ns_to_pulse_clocks(ns);
*divider = pulse_clocks_to_clock_divider(pulse_clocks);
cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
@@ -462,8 +482,8 @@ static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
{
u64 pulse_clocks;
- if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
- ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
pulse_clocks = ns_to_pulse_clocks(ns);
*divider = pulse_clocks_to_clock_divider(pulse_clocks);
cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
@@ -526,8 +546,8 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
- u32 rx_data[FIFO_RX_DEPTH];
- int i, j, k;
+ union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+ unsigned int i, j, k;
u32 events, v;
int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
@@ -588,11 +608,12 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
for (j = 0;
(v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
- rx_data[i++] = v & ~FIFO_RX_NDV;
+ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+ i++;
}
if (i == 0)
break;
- j = i * sizeof(u32);
+ j = i * sizeof(union cx23888_ir_fifo_rec);
k = kfifo_in_locked(&state->rx_kfifo,
(unsigned char *) rx_data, j,
&state->rx_kfifo_lock);
@@ -651,10 +672,11 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
u16 divider = (u16) atomic_read(&state->rxclk_divider);
unsigned int i, n;
- u32 *p;
- u32 u, v;
+ union cx23888_ir_fifo_rec *p;
+ unsigned u, v;
- n = count / sizeof(u32) * sizeof(u32);
+ n = count / sizeof(union cx23888_ir_fifo_rec)
+ * sizeof(union cx23888_ir_fifo_rec);
if (n == 0) {
*num = 0;
return 0;
@@ -662,26 +684,28 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
- n /= sizeof(u32);
- *num = n * sizeof(u32);
+ n /= sizeof(union cx23888_ir_fifo_rec);
+ *num = n * sizeof(union cx23888_ir_fifo_rec);
+
+ for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
- for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
- if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
- *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ /* Assume RTO was because of no IR light input */
+ u = 0;
v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
- continue;
+ } else {
+ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+ if (invert)
+ u = u ? 0 : 1;
}
- u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
- if (invert)
- u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+ v = (unsigned) pulse_width_count_to_ns(
+ (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+ if (v > IR_MAX_DURATION)
+ v = IR_MAX_DURATION;
- v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
- divider);
- if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
- v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
-
- *p = u | v;
+ p->ir_core_data.pulse = u;
+ p->ir_core_data.duration = v;
v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n",
v, u ? "mark" : "space");
@@ -740,7 +764,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
- o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+ o->bytes_per_data_element = p->bytes_per_data_element
+ = sizeof(union cx23888_ir_fifo_rec);
/* Before we tweak the hardware, we have to disable the receiver */
irqenable_rx(dev, 0);
@@ -762,12 +787,15 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
&p->carrier_range_upper);
o->carrier_range_lower = p->carrier_range_lower;
o->carrier_range_upper = p->carrier_range_upper;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
} else {
p->max_pulse_width =
rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
&rxclk_divider);
- o->max_pulse_width = p->max_pulse_width;
}
+ o->max_pulse_width = p->max_pulse_width;
atomic_set(&state->rxclk_divider, rxclk_divider);
p->noise_filter_min_width =
@@ -782,8 +810,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
- o->invert = p->invert;
- atomic_set(&state->rx_invert, p->invert);
+ o->invert_level = p->invert_level;
+ atomic_set(&state->rx_invert, p->invert_level);
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
@@ -864,7 +892,8 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
- o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+ o->bytes_per_data_element = p->bytes_per_data_element
+ = sizeof(union cx23888_ir_fifo_rec);
/* Before we tweak the hardware, we have to disable the transmitter */
irqenable_tx(dev, 0);
@@ -880,12 +909,15 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
o->duty_cycle = p->duty_cycle;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
} else {
p->max_pulse_width =
txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
&txclk_divider);
- o->max_pulse_width = p->max_pulse_width;
}
+ o->max_pulse_width = p->max_pulse_width;
atomic_set(&state->txclk_divider, txclk_divider);
p->resolution = clock_divider_to_resolution(txclk_divider);
@@ -894,8 +926,11 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
/* FIXME - make this dependent on resolution for better performance */
control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
- control_tx_polarity_invert(dev, p->invert);
- o->invert = p->invert;
+ control_tx_polarity_invert(dev, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ control_tx_level_invert(dev, p->invert_level);
+ o->invert_level = p->invert_level;
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
@@ -988,12 +1023,10 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
"-%1d/+%1d, %u to %u Hz\n", i, j,
clock_divider_to_freq(rxclk, 16 + j),
clock_divider_to_freq(rxclk, 16 - i));
- } else {
- v4l2_info(sd, "\tMax measurable pulse width: %u us, "
- "%llu ns\n",
- pulse_width_count_to_us(FIFO_RXTX, rxclk),
- pulse_width_count_to_ns(FIFO_RXTX, rxclk));
}
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
v4l2_info(sd, "\tLow pass filter: %s\n",
filtr ? "enabled" : "disabled");
if (filtr)
@@ -1025,19 +1058,20 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
cntrl & CNTRL_TFE ? "enabled" : "disabled");
v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
cntrl & CNTRL_TIC ? "not empty" : "half full or less");
- v4l2_info(sd, "\tSignal polarity: %s\n",
- cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
+ v4l2_info(sd, "\tOutput pin level inversion %s\n",
+ cntrl & CNTRL_IVO ? "yes" : "no");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
if (cntrl & CNTRL_MOD) {
v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
clock_divider_to_carrier_freq(txclk));
v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
cduty + 1);
- } else {
- v4l2_info(sd, "\tMax pulse width: %u us, "
- "%llu ns\n",
- pulse_width_count_to_us(FIFO_RXTX, txclk),
- pulse_width_count_to_ns(FIFO_RXTX, txclk));
}
+ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
v4l2_info(sd, "\tBusy: %s\n",
stats & STATS_TBY ? "yes" : "no");
v4l2_info(sd, "\tFIFO service requested: %s\n",
@@ -1111,11 +1145,10 @@ static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
.g_register = cx23888_ir_g_register,
.s_register = cx23888_ir_s_register,
#endif
+ .interrupt_service_routine = cx23888_ir_irq_handler,
};
static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
- .interrupt_service_routine = cx23888_ir_irq_handler,
-
.rx_read = cx23888_ir_rx_read,
.rx_g_parameters = cx23888_ir_rx_g_parameters,
.rx_s_parameters = cx23888_ir_rx_s_parameters,
@@ -1131,7 +1164,7 @@ static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
};
static const struct v4l2_subdev_ir_parameters default_rx_params = {
- .bytes_per_data_element = sizeof(u32),
+ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
.enable = false,
@@ -1146,11 +1179,11 @@ static const struct v4l2_subdev_ir_parameters default_rx_params = {
.noise_filter_min_width = 333333, /* ns */
.carrier_range_lower = 35000,
.carrier_range_upper = 37000,
- .invert = false,
+ .invert_level = false,
};
static const struct v4l2_subdev_ir_parameters default_tx_params = {
- .bytes_per_data_element = sizeof(u32),
+ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
.enable = false,
@@ -1160,7 +1193,8 @@ static const struct v4l2_subdev_ir_parameters default_tx_params = {
.modulation = true,
.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
.duty_cycle = 25, /* 25 % - RC-5 carrier */
- .invert = false,
+ .invert_level = false,
+ .invert_carrier_sense = false,
};
int cx23888_ir_probe(struct cx23885_dev *dev)
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
index 6e8665b..2ee96d3 100644
--- a/drivers/media/video/cx25840/Makefile
+++ b/drivers/media/video/cx25840/Makefile
@@ -1,5 +1,5 @@
cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
- cx25840-vbi.o
+ cx25840-vbi.o cx25840-ir.o
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 45608d50..6faad34 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -474,33 +474,10 @@ void cx25840_audio_set_path(struct i2c_client *client)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
-static int get_volume(struct i2c_client *client)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
- int vol;
-
- if (state->unmute_volume >= 0)
- return state->unmute_volume;
-
- /* Volume runs +18dB to -96dB in 1/2dB steps
- * change to fit the msp3400 -114dB to +12dB range */
-
- /* check PATH1_VOLUME */
- vol = 228 - cx25840_read(client, 0x8d4);
- vol = (vol / 2) + 23;
- return vol << 9;
-}
-
static void set_volume(struct i2c_client *client, int volume)
{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
- if (state->unmute_volume >= 0) {
- state->unmute_volume = volume;
- return;
- }
-
/* Convert the volume to msp3400 values (0-127) */
vol = volume >> 9;
@@ -517,52 +494,6 @@ static void set_volume(struct i2c_client *client, int volume)
cx25840_write(client, 0x8d4, 228 - (vol * 2));
}
-static int get_bass(struct i2c_client *client)
-{
- /* bass is 49 steps +12dB to -12dB */
-
- /* check PATH1_EQ_BASS_VOL */
- int bass = cx25840_read(client, 0x8d9) & 0x3f;
- bass = (((48 - bass) * 0xffff) + 47) / 48;
- return bass;
-}
-
-static void set_bass(struct i2c_client *client, int bass)
-{
- /* PATH1_EQ_BASS_VOL */
- cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
-}
-
-static int get_treble(struct i2c_client *client)
-{
- /* treble is 49 steps +12dB to -12dB */
-
- /* check PATH1_EQ_TREBLE_VOL */
- int treble = cx25840_read(client, 0x8db) & 0x3f;
- treble = (((48 - treble) * 0xffff) + 47) / 48;
- return treble;
-}
-
-static void set_treble(struct i2c_client *client, int treble)
-{
- /* PATH1_EQ_TREBLE_VOL */
- cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
-}
-
-static int get_balance(struct i2c_client *client)
-{
- /* balance is 7 bit, 0 to -96dB */
-
- /* check PATH1_BAL_LEVEL */
- int balance = cx25840_read(client, 0x8d5) & 0x7f;
- /* check PATH1_BAL_LEFT */
- if ((cx25840_read(client, 0x8d5) & 0x80) == 0)
- balance = 0x80 - balance;
- else
- balance = 0x80 + balance;
- return balance << 8;
-}
-
static void set_balance(struct i2c_client *client, int balance)
{
int bal = balance >> 8;
@@ -579,31 +510,6 @@ static void set_balance(struct i2c_client *client, int balance)
}
}
-static int get_mute(struct i2c_client *client)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
- return state->unmute_volume >= 0;
-}
-
-static void set_mute(struct i2c_client *client, int mute)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
- if (mute && state->unmute_volume == -1) {
- int vol = get_volume(client);
-
- set_volume(client, 0);
- state->unmute_volume = vol;
- }
- else if (!mute && state->unmute_volume != -1) {
- int vol = state->unmute_volume;
-
- state->unmute_volume = -1;
- set_volume(client, vol);
- }
-}
-
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -624,25 +530,31 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return retval;
}
-int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = get_volume(client);
+ if (state->mute->val)
+ set_volume(client, 0);
+ else
+ set_volume(client, state->volume->val);
break;
case V4L2_CID_AUDIO_BASS:
- ctrl->value = get_bass(client);
+ /* PATH1_EQ_BASS_VOL */
+ cx25840_and_or(client, 0x8d9, ~0x3f,
+ 48 - (ctrl->val * 48 / 0xffff));
break;
case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = get_treble(client);
+ /* PATH1_EQ_TREBLE_VOL */
+ cx25840_and_or(client, 0x8db, ~0x3f,
+ 48 - (ctrl->val * 48 / 0xffff));
break;
case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = get_balance(client);
- break;
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = get_mute(client);
+ set_balance(client, ctrl->val);
break;
default:
return -EINVAL;
@@ -650,28 +562,6 @@ int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return 0;
}
-int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- set_volume(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_BASS:
- set_bass(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_TREBLE:
- set_treble(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_BALANCE:
- set_balance(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_MUTE:
- set_mute(client, ctrl->value);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
+const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
+ .s_ctrl = cx25840_audio_s_ctrl,
+};
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index bb4872b..86ca8c2 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -15,6 +15,9 @@
*
* CX23885 support by Steven Toth <stoth@linuxtv.org>.
*
+ * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.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
@@ -48,6 +51,28 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
MODULE_LICENSE("GPL");
+#define CX25840_VID_INT_STAT_REG 0x410
+#define CX25840_VID_INT_STAT_BITS 0x0000ffff
+#define CX25840_VID_INT_MASK_BITS 0xffff0000
+#define CX25840_VID_INT_MASK_SHFT 16
+#define CX25840_VID_INT_MASK_REG 0x412
+
+#define CX23885_AUD_MC_INT_MASK_REG 0x80c
+#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000
+#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff
+#define CX23885_AUD_MC_INT_STAT_SHFT 16
+
+#define CX25840_AUD_INT_CTRL_REG 0x812
+#define CX25840_AUD_INT_STAT_REG 0x813
+
+#define CX23885_PIN_CTRL_IRQ_REG 0x123
+#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40
+#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20
+#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10
+
+#define CX25840_IR_STATS_REG 0x210
+#define CX25840_IR_IRQEN_REG 0x214
+
static int cx25840_debug;
module_param_named(debug,cx25840_debug, int, 0644);
@@ -80,33 +105,53 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
u8 cx25840_read(struct i2c_client * client, u16 addr)
{
- u8 buffer[2];
- buffer[0] = addr >> 8;
- buffer[1] = addr & 0xff;
-
- if (i2c_master_send(client, buffer, 2) < 2)
- return 0;
-
- if (i2c_master_recv(client, buffer, 1) < 1)
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[1];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
- return buffer[0];
+ return rx_buf[0];
}
u32 cx25840_read4(struct i2c_client * client, u16 addr)
{
- u8 buffer[4];
- buffer[0] = addr >> 8;
- buffer[1] = addr & 0xff;
-
- if (i2c_master_send(client, buffer, 2) < 2)
- return 0;
-
- if (i2c_master_recv(client, buffer, 4) < 4)
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[4];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from registers */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 4;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
- return (buffer[3] << 24) | (buffer[2] << 16) |
- (buffer[1] << 8) | buffer[0];
+ return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
+ rx_buf[0];
}
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
@@ -117,6 +162,14 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
or_value);
}
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value)
+{
+ return cx25840_write4(client, addr,
+ (cx25840_read4(client, addr) & and_mask) |
+ or_value);
+}
+
/* ----------------------------------------------------------------------- */
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
@@ -124,6 +177,158 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
/* ----------------------------------------------------------------------- */
+static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *p)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+ u32 pin_ctrl;
+ u8 gpio_oe, gpio_data, strength;
+
+ pin_ctrl = cx25840_read4(client, 0x120);
+ gpio_oe = cx25840_read(client, 0x160);
+ gpio_data = cx25840_read(client, 0x164);
+
+ for (i = 0; i < n; i++) {
+ strength = p[i].strength;
+ if (strength > CX25840_PIN_DRIVE_FAST)
+ strength = CX25840_PIN_DRIVE_FAST;
+
+ switch (p[i].pin) {
+ case CX23885_PIN_IRQ_N_GPIO16:
+ if (p[i].function != CX23885_PAD_IRQ_N) {
+ /* GPIO16 */
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ /* IRQ_N */
+ if (p[i].flags &
+ (V4L2_SUBDEV_IO_PIN_DISABLE |
+ V4L2_SUBDEV_IO_PIN_INPUT)) {
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ pin_ctrl |= (0x1 << 25);
+ }
+ if (p[i].flags &
+ V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) {
+ pin_ctrl &= ~(0x1 << 24);
+ } else {
+ pin_ctrl |= (0x1 << 24);
+ }
+ }
+ break;
+ case CX23885_PIN_IR_RX_GPIO19:
+ if (p[i].function != CX23885_PAD_GPIO19) {
+ /* IR_RX */
+ gpio_oe |= (0x1 << 0);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO19 */
+ gpio_oe &= ~(0x1 << 0);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 0);
+ gpio_data |= ((p[i].value & 0x1) << 0);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_IR_TX_GPIO20:
+ if (p[i].function != CX23885_PAD_GPIO20) {
+ /* IR_TX */
+ gpio_oe |= (0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE)
+ pin_ctrl &= ~(0x1 << 10);
+ else
+ pin_ctrl |= (0x1 << 10);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO20 */
+ gpio_oe &= ~(0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 1);
+ gpio_data |= ((p[i].value & 0x1) << 1);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_SDAT_GPIO21:
+ if (p[i].function != CX23885_PAD_GPIO21) {
+ /* I2S_SDAT */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 2);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO21 */
+ gpio_oe &= ~(0x1 << 2);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 2);
+ gpio_data |= ((p[i].value & 0x1) << 2);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_WCLK_GPIO22:
+ if (p[i].function != CX23885_PAD_GPIO22) {
+ /* I2S_WCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 3);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO22 */
+ gpio_oe &= ~(0x1 << 3);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 3);
+ gpio_data |= ((p[i].value & 0x1) << 3);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_BCLK_GPIO23:
+ if (p[i].function != CX23885_PAD_GPIO23) {
+ /* I2S_BCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 4);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO23 */
+ gpio_oe &= ~(0x1 << 4);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 4);
+ gpio_data |= ((p[i].value & 0x1) << 4);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ }
+ }
+
+ cx25840_write(client, 0x164, gpio_data);
+ cx25840_write(client, 0x160, gpio_oe);
+ cx25840_write4(client, 0x120, pin_ctrl);
+ return 0;
+}
+
+static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *pincfg)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx2388x(state))
+ return cx23885_s_io_pin_config(sd, n, pincfg);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
static void init_dll1(struct i2c_client *client)
{
/* This is the Hauppauge sequence used to
@@ -420,6 +625,13 @@ static void cx23885_initialize(struct i2c_client *client)
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* Disable and clear video interrupts - we don't use them */
+ cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
+
+ /* Disable and clear audio interrupts - we don't use them */
+ cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff);
+ cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff);
}
/* ----------------------------------------------------------------------- */
@@ -909,102 +1121,29 @@ static int set_v4lstd(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct cx25840_state *state = to_state(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
- case CX25840_CID_ENABLE_PVR150_WORKAROUND:
- state->pvr150_workaround = ctrl->value;
- set_input(client, state->vid_input, state->aud_input);
- break;
-
case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x414, ctrl->value - 128);
+ cx25840_write(client, 0x414, ctrl->val - 128);
break;
case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x415, ctrl->value << 1);
+ cx25840_write(client, 0x415, ctrl->val << 1);
break;
case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x420, ctrl->value << 1);
- cx25840_write(client, 0x421, ctrl->value << 1);
+ cx25840_write(client, 0x420, ctrl->val << 1);
+ cx25840_write(client, 0x421, ctrl->val << 1);
break;
case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x422, ctrl->value);
+ cx25840_write(client, 0x422, ctrl->val);
break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- if (is_cx2583x(state))
- return -EINVAL;
- return cx25840_audio_s_ctrl(sd, ctrl);
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct cx25840_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case CX25840_CID_ENABLE_PVR150_WORKAROUND:
- ctrl->value = state->pvr150_workaround;
- break;
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = (s8)cx25840_read(client, 0x414) + 128;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = cx25840_read(client, 0x415) >> 1;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = cx25840_read(client, 0x420) >> 1;
- break;
- case V4L2_CID_HUE:
- ctrl->value = (s8)cx25840_read(client, 0x422);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- if (is_cx2583x(state))
- return -EINVAL;
- return cx25840_audio_g_ctrl(sd, ctrl);
default:
return -EINVAL;
}
@@ -1163,8 +1302,6 @@ static void log_audio_status(struct i2c_client *client)
default: p = "not defined";
}
v4l_info(client, "Detected audio standard: %s\n", p);
- v4l_info(client, "Audio muted: %s\n",
- (state->unmute_volume >= 0) ? "yes" : "no");
v4l_info(client, "Audio microcontroller: %s\n",
(download_ctl & 0x10) ?
((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
@@ -1381,40 +1518,6 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct cx25840_state *state = to_state(sd);
-
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- default:
- break;
- }
- if (is_cx2583x(state))
- return -EINVAL;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- default:
- return -EINVAL;
- }
- return -EINVAL;
-}
-
static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct cx25840_state *state = to_state(sd);
@@ -1576,24 +1679,134 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
log_video_status(client);
if (!is_cx2583x(state))
log_audio_status(client);
+ cx25840_ir_log_status(sd);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (platform_data) {
+ struct cx25840_platform_data *pdata = platform_data;
+
+ state->pvr150_workaround = pdata->pvr150_workaround;
+ set_input(client, state->vid_input, state->aud_input);
+ }
return 0;
}
+static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en;
+ u32 vid_stat, aud_mc_stat;
+ bool block_handled;
+ int ret = 0;
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ if ((is_cx23885(state) || is_cx23887(state))) {
+ ir_stat = cx25840_read(c, CX25840_IR_STATS_REG);
+ ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core ir IRQ status: %#04x disables: %#04x\n",
+ ir_stat, ir_en);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) {
+ block_handled = false;
+ ret = cx25840_ir_irq_handler(sd,
+ status, &block_handled);
+ if (block_handled)
+ *handled = true;
+ }
+ }
+
+ aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG);
+ aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio IRQ status: %#04x disables: %#04x\n",
+ aud_stat, aud_en);
+ aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio MC IRQ status: %#06x enables: %#06x\n",
+ aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT,
+ aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) {
+ if (aud_stat) {
+ cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat);
+ *handled = true;
+ }
+ }
+
+ vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core video IRQ status: %#06x disables: %#06x\n",
+ vid_stat & CX25840_VID_INT_STAT_BITS,
+ vid_stat >> CX25840_VID_INT_MASK_SHFT);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) {
+ if (vid_stat & CX25840_VID_INT_STAT_BITS) {
+ cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat);
+ *handled = true;
+ }
+ }
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ return ret;
+}
+
+static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ *handled = false;
+
+ /* Only support the CX2388[578] AV Core for now */
+ if (is_cx2388x(state))
+ return cx23885_irq_handler(sd, status, handled);
+
+ return -ENODEV;
+}
+
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
+ .s_ctrl = cx25840_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.log_status = cx25840_log_status,
+ .s_config = cx25840_s_config,
.g_chip_ident = cx25840_g_chip_ident,
- .g_ctrl = cx25840_g_ctrl,
- .s_ctrl = cx25840_s_ctrl,
- .queryctrl = cx25840_queryctrl,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = cx25840_s_std,
.reset = cx25840_reset,
.load_fw = cx25840_load_fw,
+ .s_io_pin_config = common_s_io_pin_config,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
#endif
+ .interrupt_service_routine = cx25840_irq_handler,
};
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
@@ -1628,6 +1841,7 @@ static const struct v4l2_subdev_ops cx25840_ops = {
.audio = &cx25840_audio_ops,
.video = &cx25840_video_ops,
.vbi = &cx25840_vbi_ops,
+ .ir = &cx25840_ir_ops,
};
/* ----------------------------------------------------------------------- */
@@ -1675,6 +1889,7 @@ static int cx25840_probe(struct i2c_client *client,
{
struct cx25840_state *state;
struct v4l2_subdev *sd;
+ int default_volume;
u32 id = V4L2_IDENT_NONE;
u16 device_id;
@@ -1718,6 +1933,7 @@ static int cx25840_probe(struct i2c_client *client,
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+
switch (id) {
case V4L2_IDENT_CX23885_AV:
v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
@@ -1762,22 +1978,62 @@ static int cx25840_probe(struct i2c_client *client,
state->audclk_freq = 48000;
state->pvr150_workaround = 0;
state->audmode = V4L2_TUNER_MODE_LANG1;
- state->unmute_volume = -1;
- state->default_volume = 228 - cx25840_read(client, 0x8d4);
- state->default_volume = ((state->default_volume / 2) + 23) << 9;
state->vbi_line_offset = 8;
state->id = id;
state->rev = device_id;
+ v4l2_ctrl_handler_init(&state->hdl, 9);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ if (!is_cx2583x(state)) {
+ default_volume = 228 - cx25840_read(client, 0x8d4);
+ default_volume = ((default_volume / 2) + 23) << 9;
+
+ state->volume = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+ 0, 65335, 65535 / 100, default_volume);
+ state->mute = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BASS,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE,
+ 0, 65535, 65535 / 100, 32768);
+ }
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(&state->hdl);
+ cx25840_ir_probe(sd);
return 0;
}
static int cx25840_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ cx25840_ir_remove(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 04393b9..bd4ada2 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -24,19 +24,20 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <linux/i2c.h>
-/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
- present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
- certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
- audio autodetect fails on some channels for these models and the workaround
- is to select the audio standard explicitly. Many thanks to Hauppauge for
- providing this information. */
-#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
+struct cx25840_ir_state;
struct cx25840_state {
struct i2c_client *c;
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* volume cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ };
int pvr150_workaround;
int radio;
v4l2_std_id std;
@@ -44,14 +45,13 @@ struct cx25840_state {
enum cx25840_audio_input aud_input;
u32 audclk_freq;
int audmode;
- int unmute_volume; /* -1 if not muted */
- int default_volume;
int vbi_line_offset;
u32 id;
u32 rev;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
+ struct cx25840_ir_state *ir_state;
};
static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
@@ -59,6 +59,11 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct cx25840_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd;
+}
+
static inline bool is_cx2583x(struct cx25840_state *state)
{
return state->id == V4L2_IDENT_CX25836 ||
@@ -77,6 +82,21 @@ static inline bool is_cx2388x(struct cx25840_state *state)
state->id == V4L2_IDENT_CX23888_AV;
}
+static inline bool is_cx23885(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23885_AV;
+}
+
+static inline bool is_cx23887(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23887_AV;
+}
+
+static inline bool is_cx23888(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23888_AV;
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
@@ -84,6 +104,8 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
u8 cx25840_read(struct i2c_client *client, u16 addr);
u32 cx25840_read4(struct i2c_client *client, u16 addr);
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value);
void cx25840_std_setup(struct i2c_client *client);
/* ----------------------------------------------------------------------- */
@@ -94,8 +116,8 @@ int cx25840_loadfw(struct i2c_client *client);
/* cx25850-audio.c */
void cx25840_audio_set_path(struct i2c_client *client);
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
-int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;
/* ----------------------------------------------------------------------- */
/* cx25850-vbi.c */
@@ -104,4 +126,12 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
+/* ----------------------------------------------------------------------- */
+/* cx25850-ir.c */
+extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
+int cx25840_ir_log_status(struct v4l2_subdev *sd);
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
+int cx25840_ir_probe(struct v4l2_subdev *sd);
+int cx25840_ir_remove(struct v4l2_subdev *sd);
+
#endif
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
new file mode 100644
index 0000000..c2b4c14
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -0,0 +1,1279 @@
+/*
+ * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
+ *
+ * Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <media/cx25840.h>
+#include <media/ir-core.h>
+
+#include "cx25840-core.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
+
+#define CX25840_IR_REG_BASE 0x200
+
+#define CX25840_IR_CNTRL_REG 0x200
+#define CNTRL_WIN_3_3 0x00000000
+#define CNTRL_WIN_4_3 0x00000001
+#define CNTRL_WIN_3_4 0x00000002
+#define CNTRL_WIN_4_4 0x00000003
+#define CNTRL_WIN 0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG 0x0000000C
+#define CNTRL_DMD 0x00000010
+#define CNTRL_MOD 0x00000020
+#define CNTRL_RFE 0x00000040
+#define CNTRL_TFE 0x00000080
+#define CNTRL_RXE 0x00000100
+#define CNTRL_TXE 0x00000200
+#define CNTRL_RIC 0x00000400
+#define CNTRL_TIC 0x00000800
+#define CNTRL_CPL 0x00001000
+#define CNTRL_LBM 0x00002000
+#define CNTRL_R 0x00004000
+
+#define CX25840_IR_TXCLK_REG 0x204
+#define TXCLK_TCD 0x0000FFFF
+
+#define CX25840_IR_RXCLK_REG 0x208
+#define RXCLK_RCD 0x0000FFFF
+
+#define CX25840_IR_CDUTY_REG 0x20C
+#define CDUTY_CDC 0x0000000F
+
+#define CX25840_IR_STATS_REG 0x210
+#define STATS_RTO 0x00000001
+#define STATS_ROR 0x00000002
+#define STATS_RBY 0x00000004
+#define STATS_TBY 0x00000008
+#define STATS_RSR 0x00000010
+#define STATS_TSR 0x00000020
+
+#define CX25840_IR_IRQEN_REG 0x214
+#define IRQEN_RTE 0x00000001
+#define IRQEN_ROE 0x00000002
+#define IRQEN_RSE 0x00000010
+#define IRQEN_TSE 0x00000020
+#define IRQEN_MSK 0x00000033
+
+#define CX25840_IR_FILTR_REG 0x218
+#define FILTR_LPF 0x0000FFFF
+
+#define CX25840_IR_FIFO_REG 0x23C
+#define FIFO_RXTX 0x0000FFFF
+#define FIFO_RXTX_LVL 0x00010000
+#define FIFO_RXTX_RTO 0x0001FFFF
+#define FIFO_RX_NDV 0x00020000
+#define FIFO_RX_DEPTH 8
+#define FIFO_TX_DEPTH 8
+
+#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
+#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2)
+
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx25840_ir_fifo_rec {
+ u32 hw_fifo_data;
+ struct ir_raw_event ir_core_data;
+};
+
+#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+
+struct cx25840_ir_state {
+ struct i2c_client *c;
+
+ struct v4l2_subdev_ir_parameters rx_params;
+ struct mutex rx_params_lock; /* protects Rx parameter settings cache */
+ atomic_t rxclk_divider;
+ atomic_t rx_invert;
+
+ struct kfifo rx_kfifo;
+ spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
+
+ struct v4l2_subdev_ir_parameters tx_params;
+ struct mutex tx_params_lock; /* protects Tx parameter settings cache */
+ atomic_t txclk_divider;
+};
+
+static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ return state ? state->ir_state : NULL;
+}
+
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+ if (d > RXCLK_RCD + 1)
+ d = RXCLK_RCD;
+ else if (d < 2)
+ d = 1;
+ else
+ d--;
+ return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+ /* Period of the Rx or Tx clock in ns */
+ return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+ unsigned int rollovers)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+ unsigned int rollovers)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
+ (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+ if (d > FILTR_LPF)
+ d = FILTR_LPF;
+ else if (d < 4)
+ d = 0;
+ return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+ return count_to_lpf_count(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in ns */
+ return DIV_ROUND_CLOSEST(count * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in us */
+ return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+ /*
+ * Resolution is the duration of 1 tick of the readable portion of
+ * of the pulse width counter as read from the FIFO. The two lsb's are
+ * not readable, hence the << 2. This function returns ns.
+ */
+ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return n;
+}
+
+#if 0
+/* Keep as we will need this for Transmit functionality */
+static u16 ns_to_pulse_width_count(u32 ns, u16 divider)
+{
+ u64 n;
+ u32 d;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not accessable, hence
+ * the (1 << 2)
+ */
+ n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
+ d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */
+ rem = do_div(n, d);
+ if (rem >= d / 2)
+ n++;
+
+ if (n > FIFO_RXTX)
+ n = FIFO_RXTX;
+ else if (n == 0)
+ n = 1;
+ return (u16) n;
+}
+
+#endif
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+ u64 clocks;
+ u32 rem;
+ clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
+ rem = do_div(clocks, 1000); /* /1000 = cycles */
+ if (rem >= 1000 / 2)
+ clocks++;
+ return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+ u32 rem;
+
+ rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+ /* net result needs to be rounded down and decremented by 1 */
+ if (count > RXCLK_RCD + 1)
+ count = RXCLK_RCD;
+ else if (count < 2)
+ count = 1;
+ else
+ count--;
+ return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+ TX_FIFO_HALF_EMPTY = 0,
+ TX_FIFO_EMPTY = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+ RX_FIFO_HALF_FULL = 0,
+ RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct i2c_client *c,
+ enum tx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct i2c_client *c,
+ enum rx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+ enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+ enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD,
+ enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD,
+ enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct i2c_client *c,
+ u32 edge_types)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+ edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct i2c_client *c,
+ unsigned int carrier,
+ unsigned int *carrier_range_low,
+ unsigned int *carrier_range_high)
+{
+ u32 v;
+ unsigned int c16 = carrier * 16;
+
+ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+ v = CNTRL_WIN_3_4;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+ } else {
+ v = CNTRL_WIN_3_3;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+ }
+
+ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+ v |= CNTRL_WIN_4_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+ } else {
+ v |= CNTRL_WIN_3_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+ }
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct i2c_client *c,
+ bool invert)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL,
+ invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c,
+ unsigned int duty_cycle)
+{
+ u32 n;
+ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+ if (n != 0)
+ n--;
+ if (n > 15)
+ n = 15;
+ cx25840_write4(c, CX25840_IR_CDUTY_REG, n);
+ return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns)
+{
+ u32 count = ns_to_lpf_count(min_width_ns);
+ cx25840_write4(c, CX25840_IR_FILTR_REG, count);
+ return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG,
+ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= IRQEN_TSE;
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c = NULL;
+ unsigned long flags;
+
+ union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+ unsigned int i, j, k;
+ u32 events, v;
+ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+ u32 cntrl, irqen, stats;
+
+ *handled = false;
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+
+ /* Only support the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return -ENODEV;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+
+ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+ ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n",
+ tsr ? "tsr" : " ", rsr ? "rsr" : " ",
+ rto ? "rto" : " ", ror ? "ror" : " ",
+ stats & STATS_TBY ? "tby" : " ",
+ stats & STATS_RBY ? "rby" : " ");
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n",
+ tse ? "tse" : " ", rse ? "rse" : " ",
+ rte ? "rte" : " ", roe ? "roe" : " ");
+
+ /*
+ * Transmitter interrupt service
+ */
+ if (tse && tsr) {
+ /*
+ * TODO:
+ * Check the watermark threshold setting
+ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+ * Push the data to the hardware FIFO.
+ * If there was nothing more to send in the tx_kfifo, disable
+ * the TSR IRQ and notify the v4l2_device.
+ * If there was something in the tx_kfifo, check the tx_kfifo
+ * level and notify the v4l2_device, if it is low.
+ */
+ /* For now, inhibit TSR interrupt until Tx is implemented */
+ irqenable_tx(sd, 0);
+ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+ *handled = true;
+ }
+
+ /*
+ * Receiver interrupt service
+ */
+ kror = 0;
+ if ((rse && rsr) || (rte && rto)) {
+ /*
+ * Receive data on RSR to clear the STATS_RSR.
+ * Receive data on RTO, since we may not have yet hit the RSR
+ * watermark when we receive the RTO.
+ */
+ for (i = 0, v = FIFO_RX_NDV;
+ (v & FIFO_RX_NDV) && !kror; i = 0) {
+ for (j = 0;
+ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+ v = cx25840_read4(c, CX25840_IR_FIFO_REG);
+ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+ i++;
+ }
+ if (i == 0)
+ break;
+ j = i * sizeof(union cx25840_ir_fifo_rec);
+ k = kfifo_in_locked(&ir_state->rx_kfifo,
+ (unsigned char *) rx_data, j,
+ &ir_state->rx_kfifo_lock);
+ if (k != j)
+ kror++; /* rx_kfifo over run */
+ }
+ *handled = true;
+ }
+
+ events = 0;
+ v = 0;
+ if (kror) {
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver software FIFO overrun\n");
+ }
+ if (roe && ror) {
+ /*
+ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+ * the Rx FIFO Over Run status (STATS_ROR)
+ */
+ v |= CNTRL_RFE;
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+ }
+ if (rte && rto) {
+ /*
+ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+ * the Rx Pulse Width Timer Time Out (STATS_RTO)
+ */
+ v |= CNTRL_RXE;
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ }
+ if (v) {
+ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
+ *handled = true;
+ }
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+
+ if (events)
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+ return 0;
+}
+
+/* Receiver */
+static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ bool invert;
+ u16 divider;
+ unsigned int i, n;
+ union cx25840_ir_fifo_rec *p;
+ unsigned u, v;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ invert = (bool) atomic_read(&ir_state->rx_invert);
+ divider = (u16) atomic_read(&ir_state->rxclk_divider);
+
+ n = count / sizeof(union cx25840_ir_fifo_rec)
+ * sizeof(union cx25840_ir_fifo_rec);
+ if (n == 0) {
+ *num = 0;
+ return 0;
+ }
+
+ n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
+ &ir_state->rx_kfifo_lock);
+
+ n /= sizeof(union cx25840_ir_fifo_rec);
+ *num = n * sizeof(union cx25840_ir_fifo_rec);
+
+ for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
+
+ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ /* Assume RTO was because of no IR light input */
+ u = 0;
+ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+ } else {
+ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+ if (invert)
+ u = u ? 0 : 1;
+ }
+
+ v = (unsigned) pulse_width_count_to_ns(
+ (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+ if (v > IR_MAX_DURATION)
+ v = IR_MAX_DURATION;
+
+ p->ir_core_data.pulse = u;
+ p->ir_core_data.duration = v;
+
+ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n",
+ v, u ? "mark" : "space");
+ }
+ return 0;
+}
+
+static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->rx_params_lock);
+ memcpy(p, &ir_state->rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->rx_params_lock);
+
+ /* Disable or slow down all IR Rx circuits and counters */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+ control_rx_demodulation_enable(c, false);
+ control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
+ filter_rx_s_min_width(c, 0);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
+
+ ir_state->rx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 rxclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_rx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->rx_params;
+
+ mutex_lock(&ir_state->rx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the receiver */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+
+ control_rx_demodulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq,
+ &rxclk_divider);
+
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = 50;
+ o->duty_cycle = p->duty_cycle;
+
+ control_rx_s_carrier_window(c, p->carrier_freq,
+ &p->carrier_range_lower,
+ &p->carrier_range_upper);
+ o->carrier_range_lower = p->carrier_range_lower;
+ o->carrier_range_upper = p->carrier_range_upper;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
+ } else {
+ p->max_pulse_width =
+ rxclk_rx_s_max_pulse_width(c, p->max_pulse_width,
+ &rxclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&ir_state->rxclk_divider, rxclk_divider);
+
+ p->noise_filter_min_width =
+ filter_rx_s_min_width(c, p->noise_filter_min_width);
+ o->noise_filter_min_width = p->noise_filter_min_width;
+
+ p->resolution = clock_divider_to_resolution(rxclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_rx_irq_watermark(c, RX_FIFO_HALF_FULL);
+
+ control_rx_s_edge_detection(c, CNTRL_EDG_BOTH);
+
+ o->invert_level = p->invert_level;
+ atomic_set(&ir_state->rx_invert, p->invert_level);
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ kfifo_reset(&ir_state->rx_kfifo);
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+ if (p->interrupt_enable)
+ irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+ control_rx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+/* Transmitter */
+static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+#if 0
+ /*
+ * FIXME - the code below is an incomplete and untested sketch of what
+ * may need to be done. The critical part is to get 4 (or 8) pulses
+ * from the tx_kfifo, or converted from ns to the proper units from the
+ * input, and push them off to the hardware Tx FIFO right away, if the
+ * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in
+ * a less critical timeframe. Also watch out for overruning the
+ * tx_kfifo - don't let it happen and let the caller know not all his
+ * pulses were written.
+ */
+ u32 *ns_pulse = (u32 *) buf;
+ unsigned int n;
+ u32 fifo_pulse[FIFO_TX_DEPTH];
+ u32 mark;
+
+ /* Compute how much we can fit in the tx kfifo */
+ n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
+ n = min(n, (unsigned int) count);
+ n /= sizeof(u32);
+
+ /* FIXME - turn on Tx Fifo service interrupt
+ * check hardware fifo level, and other stuff
+ */
+ for (i = 0; i < n; ) {
+ for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
+ mark = ns_pulse[i] & LEVEL_MASK;
+ fifo_pulse[j] = ns_to_pulse_width_count(
+ ns_pulse[i] &
+ ~LEVEL_MASK,
+ ir_state->txclk_divider);
+ if (mark)
+ fifo_pulse[j] &= FIFO_RXTX_LVL;
+ i++;
+ }
+ kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
+ j * sizeof(u32));
+ }
+ *num = n * sizeof(u32);
+#else
+ /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+ irqenable_tx(sd, IRQEN_TSE);
+ *num = count;
+#endif
+ return 0;
+}
+
+static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->tx_params_lock);
+ memcpy(p, &ir_state->tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ /* Disable or slow down all IR Tx circuits and counters */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+ control_tx_modulation_enable(c, false);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
+
+ ir_state->tx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 txclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_tx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->tx_params;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the transmitter */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+
+ control_tx_modulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq,
+ &txclk_divider);
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle);
+ o->duty_cycle = p->duty_cycle;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
+ } else {
+ p->max_pulse_width =
+ txclk_tx_s_max_pulse_width(c, p->max_pulse_width,
+ &txclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&ir_state->txclk_divider, txclk_divider);
+
+ p->resolution = clock_divider_to_resolution(txclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY);
+
+ control_tx_polarity_invert(c, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ /*
+ * FIXME: we don't have hardware help for IO pin level inversion
+ * here like we have on the CX23888.
+ * Act on this with some mix of logical inversion of data levels,
+ * carrier polarity, and carrier duty cycle.
+ */
+ o->invert_level = p->invert_level;
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ /* reset tx_fifo here */
+ if (p->interrupt_enable)
+ irqenable_tx(sd, IRQEN_TSE);
+ control_tx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops support
+ */
+int cx25840_ir_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = state->c;
+ char *s;
+ int i, j;
+ u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr;
+
+ /* The CX23888 chip doesn't have an IR controller on the A/V core */
+ if (is_cx23888(state))
+ return 0;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD;
+ rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD;
+ cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF;
+
+ v4l2_info(sd, "IR Receiver:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_RXE ? "yes" : "no");
+ v4l2_info(sd, "\tDemodulation from a carrier: %s\n",
+ cntrl & CNTRL_DMD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_RFE ? "enabled" : "disabled");
+ switch (cntrl & CNTRL_EDG) {
+ case CNTRL_EDG_NONE:
+ s = "disabled";
+ break;
+ case CNTRL_EDG_FALL:
+ s = "falling edge";
+ break;
+ case CNTRL_EDG_RISE:
+ s = "rising edge";
+ break;
+ case CNTRL_EDG_BOTH:
+ s = "rising & falling edges";
+ break;
+ default:
+ s = "??? edge";
+ break;
+ }
+ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s);
+ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+ cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+ v4l2_info(sd, "\tLoopback mode: %s\n",
+ cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+ if (cntrl & CNTRL_DMD) {
+ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(rxclk));
+ switch (cntrl & CNTRL_WIN) {
+ case CNTRL_WIN_3_3:
+ i = 3;
+ j = 3;
+ break;
+ case CNTRL_WIN_4_3:
+ i = 4;
+ j = 3;
+ break;
+ case CNTRL_WIN_3_4:
+ i = 3;
+ j = 4;
+ break;
+ case CNTRL_WIN_4_4:
+ i = 4;
+ j = 4;
+ break;
+ default:
+ i = 0;
+ j = 0;
+ break;
+ }
+ v4l2_info(sd, "\tNext carrier edge window: 16 clocks "
+ "-%1d/+%1d, %u to %u Hz\n", i, j,
+ clock_divider_to_freq(rxclk, 16 + j),
+ clock_divider_to_freq(rxclk, 16 - i));
+ }
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+ v4l2_info(sd, "\tLow pass filter: %s\n",
+ filtr ? "enabled" : "disabled");
+ if (filtr)
+ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, "
+ "%u ns\n",
+ lpf_count_to_us(filtr),
+ lpf_count_to_ns(filtr));
+ v4l2_info(sd, "\tPulse width timer timed-out: %s\n",
+ stats & STATS_RTO ? "yes" : "no");
+ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n",
+ irqen & IRQEN_RTE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO overrun: %s\n",
+ stats & STATS_ROR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n",
+ irqen & IRQEN_ROE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_RBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_RSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+ v4l2_info(sd, "IR Transmitter:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_TXE ? "yes" : "no");
+ v4l2_info(sd, "\tModulation onto a carrier: %s\n",
+ cntrl & CNTRL_MOD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_TFE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
+ if (cntrl & CNTRL_MOD) {
+ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(txclk));
+ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
+ cduty + 1);
+ }
+ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_TBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_TSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+ return 0;
+}
+
+
+const struct v4l2_subdev_ir_ops cx25840_ir_ops = {
+ .rx_read = cx25840_ir_rx_read,
+ .rx_g_parameters = cx25840_ir_rx_g_parameters,
+ .rx_s_parameters = cx25840_ir_rx_s_parameters,
+
+ .tx_write = cx25840_ir_tx_write,
+ .tx_g_parameters = cx25840_ir_tx_g_parameters,
+ .tx_s_parameters = cx25840_ir_tx_s_parameters,
+};
+
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */
+
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ .noise_filter_min_width = 333333, /* ns */
+ .carrier_range_lower = 35000,
+ .carrier_range_upper = 37000,
+ .invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+ .duty_cycle = 25, /* 25 % - RC-5 carrier */
+ .invert_level = false,
+ .invert_carrier_sense = false,
+};
+
+int cx25840_ir_probe(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state;
+ struct v4l2_subdev_ir_parameters default_params;
+
+ /* Only init the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return 0;
+
+ ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL);
+ if (ir_state == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&ir_state->rx_kfifo_lock);
+ if (kfifo_alloc(&ir_state->rx_kfifo,
+ CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
+ kfree(ir_state);
+ return -ENOMEM;
+ }
+
+ ir_state->c = state->c;
+ state->ir_state = ir_state;
+
+ /* Ensure no interrupts arrive yet */
+ if (is_cx23885(state) || is_cx23887(state))
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK);
+ else
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0);
+
+ mutex_init(&ir_state->rx_params_lock);
+ memcpy(&default_params, &default_rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+ mutex_init(&ir_state->tx_params_lock);
+ memcpy(&default_params, &default_tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+
+ return 0;
+}
+
+int cx25840_ir_remove(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ cx25840_ir_rx_shutdown(sd);
+ cx25840_ir_tx_shutdown(sd);
+
+ kfifo_free(&ir_state->rx_kfifo);
+ kfree(ir_state);
+ state->ir_state = NULL;
+ return 0;
+}
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index c7e5851..99dbae1 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -6,7 +6,7 @@ config VIDEO_CX88
select VIDEOBUF_DMA_SG
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
---help---
This is a video4linux driver for Conexant 2388x based
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 375ad53..82db555 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -193,24 +193,13 @@ void cx88_i2c_init_ir(struct cx88_core *core)
0x18, 0x6b, 0x71,
I2C_CLIENT_END
};
- const unsigned short *addrp;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
- /*
- * We can't call i2c_new_probed_device() because it uses
- * quick writes for probing and at least some R receiver
- * devices only reply to reads.
- */
- for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) {
- if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0,
- I2C_SMBUS_READ, 0,
- I2C_SMBUS_QUICK, NULL) >= 0) {
- info.addr = *addrp;
- i2c_new_device(&core->i2c_adap, &info);
- break;
- }
- }
+ /* Use quick read command for probe, some IR chips don't
+ * support writes */
+ i2c_new_probed_device(&core->i2c_adap, &info, addr_list,
+ i2c_probe_func_quick_read);
}
}
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index c7be0e0..66aefd6 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_EM28XX
depends on VIDEO_DEV && I2C && INPUT
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEOBUF_VMALLOC
select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index ffbe544..e7efb4b 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -2385,7 +2385,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
if (dev->init_data.name)
info.platform_data = &dev->init_data;
- i2c_new_probed_device(&dev->i2c_adap, &info, addr_list);
+ i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL);
}
void em28xx_card_setup(struct em28xx *dev)
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
index 8f1c94f..43d208f 100644
--- a/drivers/media/video/fsl-viu.c
+++ b/drivers/media/video/fsl-viu.c
@@ -1418,7 +1418,7 @@ static struct video_device viu_template = {
.current_norm = V4L2_STD_NTSC_M,
};
-static int __devinit viu_of_probe(struct of_device *op,
+static int __devinit viu_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct viu_dev *viu_dev;
@@ -1549,7 +1549,7 @@ err:
return ret;
}
-static int __devexit viu_of_remove(struct of_device *op)
+static int __devexit viu_of_remove(struct platform_device *op)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
@@ -1570,7 +1570,7 @@ static int __devexit viu_of_remove(struct of_device *op)
}
#ifdef CONFIG_PM
-static int viu_suspend(struct of_device *op, pm_message_t state)
+static int viu_suspend(struct platform_device *op, pm_message_t state)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
@@ -1579,7 +1579,7 @@ static int viu_suspend(struct of_device *op, pm_message_t state)
return 0;
}
-static int viu_resume(struct of_device *op)
+static int viu_resume(struct platform_device *op)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index d951b0f..b984610 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 9, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 10, 0)
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
@@ -440,10 +440,15 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
frame->v4l2_buf.sequence = ++gspca_dev->sequence;
gspca_dev->image = frame->data;
gspca_dev->image_len = 0;
- } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
- if (packet_type == LAST_PACKET)
- gspca_dev->last_packet_type = packet_type;
- return;
+ } else {
+ switch (gspca_dev->last_packet_type) {
+ case DISCARD_PACKET:
+ if (packet_type == LAST_PACKET)
+ gspca_dev->last_packet_type = packet_type;
+ return;
+ case LAST_PACKET:
+ return;
+ }
}
/* append the packet to the frame buffer */
@@ -454,6 +459,12 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
gspca_dev->frsz);
packet_type = DISCARD_PACKET;
} else {
+/* !! image is NULL only when last pkt is LAST or DISCARD
+ if (gspca_dev->image == NULL) {
+ err("gspca_frame_add() image == NULL");
+ return;
+ }
+ */
memcpy(gspca_dev->image + gspca_dev->image_len,
data, len);
gspca_dev->image_len += len;
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index ee17b03..3705443 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -66,7 +66,11 @@ struct sd {
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
u8 sensor; /* Type of image sensor chip */
-enum {
+ u8 i2c_addr;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
SENSOR_ADCM1700,
SENSOR_GC0307,
SENSOR_HV7131R,
@@ -81,10 +85,6 @@ enum {
SENSOR_PO2030N,
SENSOR_SOI768,
SENSOR_SP80708,
-} sensors;
- u8 i2c_addr;
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
};
/* V4L2 controls supported by the driver */
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
index 37cee5e..7ae6522 100644
--- a/drivers/media/video/gspca/sq930x.c
+++ b/drivers/media/video/gspca/sq930x.c
@@ -23,7 +23,6 @@
#define MODULE_NAME "sq930x"
#include "gspca.h"
-#include "jpeg.h"
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
"Gerard Klaver <gerard at gkall dot hobby dot nl\n"
@@ -31,8 +30,6 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
MODULE_LICENSE("GPL");
-#define BULK_TRANSFER_LEN 5128
-
/* Structure to hold all of our device specific stuff */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
@@ -40,28 +37,20 @@ struct sd {
u16 expo;
u8 gain;
- u8 quality; /* webcam quality 0..3 */
-#define QUALITY_DEF 1
-
- u8 gpio[2];
-
- u8 eof_len;
u8 do_ctrl;
-
+ u8 gpio[2];
u8 sensor;
-enum {
+ u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
SENSOR_ICX098BQ,
SENSOR_LZ24BP,
SENSOR_MI0360,
- SENSOR_MT9V111,
+ SENSOR_MT9V111, /* = MI360SOC */
SENSOR_OV7660,
SENSOR_OV9630,
-} sensors;
- u8 type;
-#define Generic 0
-#define Creative_live_motion 1
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
};
static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
@@ -78,7 +67,7 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0x0001,
.maximum = 0x0fff,
.step = 1,
-#define EXPO_DEF 0x027d
+#define EXPO_DEF 0x0356
.default_value = EXPO_DEF,
},
.set = sd_setexpo,
@@ -92,7 +81,7 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0x01,
.maximum = 0xff,
.step = 1,
-#define GAIN_DEF 0x61
+#define GAIN_DEF 0x8d
.default_value = GAIN_DEF,
},
.set = sd_setgain,
@@ -101,30 +90,18 @@ static const struct ctrl sd_ctrls[] = {
};
static struct v4l2_pix_format vga_mode[] = {
- {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 5 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 320,
- .sizeimage = 320 * 240 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
};
-/* JPEG quality indexed by webcam quality */
-#define QUAL_0 90
-#define QUAL_1 85
-#define QUAL_2 75
-#define QUAL_3 70
-static const u8 quality_tb[4] = { QUAL_0, QUAL_1, QUAL_2, QUAL_3 };
-
/* sq930x registers */
#define SQ930_CTRL_UCBUS_IO 0x0001
#define SQ930_CTRL_I2C_IO 0x0002
@@ -302,7 +279,7 @@ static const struct i2c_write_cmd mt9v111_init_0[] = {
{0x01, 0x0001}, /* select IFP/SOC registers */
{0x06, 0x300c}, /* operating mode control */
{0x08, 0xcc00}, /* output format control (RGB) */
- {0x01, 0x0004}, /* select core registers */
+ {0x01, 0x0004}, /* select sensor core registers */
};
static const struct i2c_write_cmd mt9v111_init_1[] = {
{0x03, 0x01e5}, /* window height */
@@ -330,7 +307,8 @@ static const struct i2c_write_cmd mt9v111_init_3[] = {
{0x62, 0x0405},
};
static const struct i2c_write_cmd mt9v111_init_4[] = {
- {0x05, 0x00ce}, /* horizontal blanking */
+/* {0x05, 0x00ce}, */
+ {0x05, 0x005d}, /* horizontal blanking */
};
static const struct ucbus_write_cmd ov7660_start_0[] = {
@@ -343,78 +321,58 @@ static const struct ucbus_write_cmd ov9630_start_0[] = {
{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
};
+/* start parameters indexed by [sensor][mode] */
static const struct cap_s {
u8 cc_sizeid;
u8 cc_bytes[32];
-} capconfig[4][3] = {
+} capconfig[4][2] = {
[SENSOR_ICX098BQ] = {
- {0, /* JPEG, 160x120 */
+ {2, /* Bayer 320x240 */
+ {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
{0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
- 0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {2, /* JPEG, 320x240 */
- {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_LZ24BP] = {
- {0, /* JPEG, 160x120 */
- {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
- 0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {2, /* JPEG, 320x240 */
+ {2, /* Bayer 320x240 */
+ {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
{0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_MI0360] = {
- {0, /* JPEG, 160x120 */
- {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
- 0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
- 0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
- {2, /* JPEG, 320x240 */
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
-/*fixme 03 e3 */
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe3,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_MT9V111] = {
- {0, /* JPEG, 160x120 */
- {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
- 0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
- 0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
- {2, /* JPEG, 320x240 */
- {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
};
@@ -864,7 +822,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
buf[i++] = 0x35; /* reg = global gain */
buf[i++] = 0x00; /* val H */
buf[i++] = sensor->i2c_dum;
- buf[i++] = sd->gain; /* val L */
+ buf[i++] = 0x80 + sd->gain / 2; /* val L */
buf[i++] = 0x00;
buf[i++] = 0x00;
buf[i++] = 0x00;
@@ -889,10 +847,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(vga_mode);
cam->bulk = 1;
- cam->bulk_size = BULK_TRANSFER_LEN;
-/* cam->bulk_nurbs = 2; fixme: if no setexpo sync */
- sd->quality = QUALITY_DEF;
sd->gain = GAIN_DEF;
sd->expo = EXPO_DEF;
@@ -945,13 +900,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
if (sd->sensor == SENSOR_MI0360) {
/* no sensor probe for icam tracer */
- if (gspca_dev->usb_buf[5] == 0xf6) { /* if CMOS */
+ if (gspca_dev->usb_buf[5] == 0xf6) /* if CMOS */
sd->sensor = SENSOR_ICX098BQ;
- gspca_dev->cam.cam_mode = &vga_mode[1];
- gspca_dev->cam.nmodes = 1; /* only 320x240 */
- } else {
+ else
cmos_probe(gspca_dev);
- }
}
PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
@@ -960,51 +912,24 @@ static int sd_init(struct gspca_dev *gspca_dev)
return gspca_dev->usb_err;
}
-/* special function to create the quantization tables of the JPEG header */
-static void sd_jpeg_set_qual(u8 *jpeg_hdr,
- int quality)
-{
- int i, sc1, sc2;
-
- quality = quality_tb[quality]; /* convert to JPEG quality */
-/*
- * approximative qualities for Y and U/V:
- * quant = 0:94%/91% 1:91%/87% 2:82%/73% 3:69%/56%
- * should have:
- * quant = 0:94%/91% 1:91%/87.5% 2:81.5%/72% 3:69%/54.5%
- */
- sc1 = 200 - quality * 2;
- quality = quality * 7 / 5 - 40; /* UV quality */
- sc2 = 200 - quality * 2;
- for (i = 0; i < 64; i++) {
- jpeg_hdr[JPEG_QT0_OFFSET + i] =
- (jpeg_head[JPEG_QT0_OFFSET + i] * sc1 + 50) / 100;
- jpeg_hdr[JPEG_QT1_OFFSET + i] =
- (jpeg_head[JPEG_QT1_OFFSET + i] * sc2 + 50) / 100;
- }
-}
-
/* send the start/stop commands to the webcam */
static void send_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
const struct cap_s *cap;
- int mode, quality;
+ int mode;
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
cap = &capconfig[sd->sensor][mode];
- quality = sd->quality;
- reg_wb(gspca_dev, (quality << 12)
- | 0x0a00 /* 900 for Bayer */
- | SQ930_CTRL_CAP_START,
- 0x0500 /* a00 for Bayer */
- | cap->cc_sizeid,
+ reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
+ 0x0a00 | cap->cc_sizeid,
cap->cc_bytes, 32);
-};
+}
+
static void send_stop(struct gspca_dev *gspca_dev)
{
reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
-};
+}
/* function called at start time before URB creation */
static int sd_isoc_init(struct gspca_dev *gspca_dev)
@@ -1013,6 +938,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */
sd->do_ctrl = 0;
+ gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
return 0;
}
@@ -1022,11 +948,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int mode;
- /* initialize the JPEG header */
- jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
- 0x21); /* JPEG 422 */
- sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-
bridge_init(sd);
global_init(sd, 0);
msleep(100);
@@ -1071,7 +992,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
ARRAY_SIZE(lz24bp_start_2),
6);
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- lz24bp_ppl(sd, mode == 2 ? 0x0564 : 0x0310);
+ lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
msleep(10);
break;
case SENSOR_MI0360:
@@ -1095,7 +1016,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* 1st start */
send_start(gspca_dev);
msleep(60);
- reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+ send_stop(gspca_dev);
i2c_write(sd,
mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
@@ -1113,7 +1034,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
ARRAY_SIZE(mt9v111_init_2));
ucbus_write(gspca_dev, mt9v111_start_1,
ARRAY_SIZE(mt9v111_start_1),
- 8);
+ 5);
i2c_write(sd, mt9v111_init_3,
ARRAY_SIZE(mt9v111_init_3));
i2c_write(sd, mt9v111_init_4,
@@ -1125,8 +1046,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
out:
msleep(1000);
- sd->eof_len = 0; /* init packet scan */
-
if (sd->sensor == SENSOR_MT9V111)
gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
@@ -1166,94 +1085,17 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
msleep(100);
}
-/* move a packet adding 0x00 after 0xff */
-static void add_packet(struct gspca_dev *gspca_dev,
- u8 *data,
- int len)
-{
- int i;
-
- i = 0;
- do {
- if (data[i] == 0xff) {
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data, i + 1);
- len -= i;
- data += i;
- *data = 0x00;
- i = 0;
- }
- } while (++i < len);
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-/* end a frame and start a new one */
-static void eof_sof(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const u8 ffd9[] = {0xff, 0xd9};
-
- /* if control set, stop bulk transfer */
- if (sd->do_ctrl
- && gspca_dev->last_packet_type == INTER_PACKET)
- gspca_dev->cam.bulk_nurbs = 0;
- gspca_frame_add(gspca_dev, LAST_PACKET,
- ffd9, 2);
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
-}
-
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 *p;
- int l;
-
- len -= 8; /* ignore last 8 bytes (00 00 55 aa 55 aa 00 00) */
-
- /*
- * the end/start of frame is indicated by
- * 0x00 * 16 - 0xab * 8
- * aligned on 8 bytes boundary
- */
- if (sd->eof_len != 0) { /* if 'abababab' in previous pkt */
- if (*((u32 *) data) == 0xabababab) {
- /*fixme: should remove previous 0000ababab*/
- eof_sof(gspca_dev);
- data += 4;
- len -= 4;
- }
- sd->eof_len = 0;
- }
- p = data;
- l = len;
- for (;;) {
- if (*((u32 *) p) == 0xabababab) {
- if (l < 8) { /* (may be 4 only) */
- sd->eof_len = 1;
- break;
- }
- if (*((u32 *) p + 1) == 0xabababab) {
- add_packet(gspca_dev, data, p - data - 16);
- /* remove previous zeros */
- eof_sof(gspca_dev);
- p += 8;
- l -= 8;
- if (l <= 0)
- return;
- len = l;
- data = p;
- continue;
- }
- }
- p += 4;
- l -= 4;
- if (l <= 0)
- break;
- }
- add_packet(gspca_dev, data, len);
+
+ if (sd->do_ctrl)
+ gspca_dev->cam.bulk_nurbs = 0;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
}
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
@@ -1291,45 +1133,6 @@ static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int quality;
-
- if (jcomp->quality >= (QUAL_0 + QUAL_1) / 2)
- quality = 0;
- else if (jcomp->quality >= (QUAL_1 + QUAL_2) / 2)
- quality = 1;
- else if (jcomp->quality >= (QUAL_2 + QUAL_3) / 2)
- quality = 2;
- else
- quality = 3;
-
- if (quality != sd->quality) {
- sd->quality = quality;
- if (gspca_dev->streaming) {
- send_stop(gspca_dev);
- sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- msleep(70);
- send_start(gspca_dev);
- }
- }
- return gspca_dev->usb_err;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = quality_tb[sd->quality];
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
-}
-
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -1342,8 +1145,6 @@ static const struct sd_desc sd_desc = {
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_dq_callback,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* Table of supported USB devices */
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 2a0f12d..3b3b983 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -55,12 +55,12 @@ struct sd {
u8 effect;
u8 sensor;
-enum {
+};
+enum sensors {
SENSOR_OM6802,
SENSOR_OTHER,
SENSOR_TAS5130A,
SENSOR_LT168G, /* must verify if this is the actual model */
-} sensors;
};
/* V4L2 controls supported by the driver */
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 031266a..b16fd47 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -39,6 +39,10 @@ struct sd {
u8 vflip;
u8 lightfreq;
s8 sharpness;
+ u16 exposure;
+ u8 gain;
+ u8 autogain;
+ u8 backlight;
u8 image_offset;
@@ -77,6 +81,14 @@ 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_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_IDX 0
@@ -185,6 +197,66 @@ static const struct ctrl sd_ctrls[] = {
.set = sd_setsharpness,
.get = sd_getsharpness,
},
+#define GAIN_IDX 7
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 78,
+ .step = 1,
+#define GAIN_DEF 0
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+#define EXPOSURE_IDX 8
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+#define EXPOSURE_DEF 450
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+#define AUTOGAIN_IDX 9
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define BACKLIGHT_IDX 10
+ {
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Backlight Compensation",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define BACKLIGHT_DEF 15
+ .default_value = BACKLIGHT_DEF,
+ },
+ .set = sd_setbacklight,
+ .get = sd_getbacklight,
+ },
};
/* table of the disabled controls */
@@ -192,33 +264,51 @@ 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),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_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),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1310_SOC 2 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1320 3 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1320_SOC 4 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_OV7660 5 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_OV7670 6 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << SHARPNESS_IDX),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_PO1200 7 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX),
+ | (1 << LIGHTFREQ_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_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),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_POxxxx 9 */
(1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
};
@@ -2825,7 +2915,9 @@ static const u8 poxxxx_init_common[][4] = {
{0x00, 0x1e, 0xc6, 0xaa},
{0x00, 0x00, 0x40, 0xdd},
{0x00, 0x1d, 0x05, 0xaa},
-
+ {}
+};
+static const u8 poxxxx_gamma[][4] = {
{0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
{0x00, 0x73, 0x00, 0xaa},
{0x00, 0x74, 0x0a, 0xaa},
@@ -2867,19 +2959,9 @@ static const u8 poxxxx_init_common[][4] = {
{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},
+ {}
+};
+static const u8 poxxxx_init_start_3[][4] = {
{0x00, 0xb8, 0x28, 0xaa},
{0x00, 0xb9, 0x1e, 0xaa},
{0x00, 0xb6, 0x14, 0xaa},
@@ -2959,9 +3041,6 @@ static const u8 poxxxx_init_end_1[][4] = {
{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] = {
@@ -3312,6 +3391,33 @@ static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info >> 8;
+ sd->flags = id->driver_info & 0xff;
+
+ if (id->idVendor == 0x046d &&
+ (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+ sd->sensor = SENSOR_POxxxx; /* no probe */
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ sd->lightfreq = FREQ_DEF;
+ sd->sharpness = SHARPNESS_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->backlight = BACKLIGHT_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;
struct cam *cam;
int sensor;
static u8 npkt[] = { /* number of packets per ISOC message */
@@ -3327,14 +3433,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
128, /* POxxxx 9 */
};
- cam = &gspca_dev->cam;
- sd->bridge = id->driver_info >> 8;
- sd->flags = id->driver_info & 0xff;
- if (id->idVendor == 0x046d &&
- (id->idProduct == 0x0892 || id->idProduct == 0x0896))
- sensor = SENSOR_POxxxx;
- else
+ if (sd->sensor != SENSOR_POxxxx)
sensor = vc032x_probe_sensor(gspca_dev);
+ else
+ sensor = sd->sensor;
+
switch (sensor) {
case -1:
PDEBUG(D_PROBE, "Unknown sensor...");
@@ -3373,6 +3476,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
sd->sensor = sensor;
+ cam = &gspca_dev->cam;
if (sd->bridge == BRIDGE_VC0321) {
cam->cam_mode = vc0321_mode;
cam->nmodes = ARRAY_SIZE(vc0321_mode);
@@ -3401,28 +3505,11 @@ 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;
- sd->lightfreq = FREQ_DEF;
- sd->sharpness = SHARPNESS_DEF;
-
gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
if (sd->sensor == SENSOR_OV7670)
sd->flags |= FL_HFLIP | FL_VFLIP;
- 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;
-
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
@@ -3433,8 +3520,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_buf[0] != 0) {
reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
- reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
}
+ reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
}
}
return gspca_dev->usb_err;
@@ -3551,6 +3638,82 @@ static void setsharpness(struct gspca_dev *gspca_dev)
break;
}
}
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
+ return;
+ i2c_write(gspca_dev, 0x15, &sd->gain, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
+ return;
+ data = sd->exposure >> 8;
+ i2c_write(gspca_dev, 0x1a, &data, 1);
+ data = sd->exposure;
+ i2c_write(gspca_dev, 0x1b, &data, 1);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 data[2] = {0x28, 0x3c};
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
+ return;
+ i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do */
+ usb_exchange(gspca_dev, poxxxx_gamma);
+}
+
+static void setbacklight(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 v;
+ u8 data;
+
+ data = (sd->backlight << 4) | 0x0f;
+ i2c_write(gspca_dev, 0xaa, &data, 1);
+ v = 613 + 12 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc4, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc5, &data, 1);
+ v = 1093 - 12 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc6, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc7, &data, 1);
+ v = 342 + 9 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc8, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc9, &data, 1);
+ v = 702 - 9 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xca, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xcb, &data, 1);
+}
+
+static void setwb(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
+ static const u8 data[2] = {0x00, 0x00};
+
+ i2c_write(gspca_dev, 0x16, &data[0], 1);
+ i2c_write(gspca_dev, 0x18, &data[1], 1);
+}
static int sd_start(struct gspca_dev *gspca_dev)
{
@@ -3662,6 +3825,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
default:
/* case SENSOR_POxxxx: */
usb_exchange(gspca_dev, poxxxx_init_common);
+ setgamma(gspca_dev);
+ setbacklight(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ setsharpness(gspca_dev);
+ setautogain(gspca_dev);
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ usb_exchange(gspca_dev, poxxxx_init_start_3);
if (mode)
init = poxxxx_initQVGA;
else
@@ -3693,7 +3866,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
msleep(100);
- setsharpness(gspca_dev);
sethvflip(gspca_dev);
setlightfreq(gspca_dev);
}
@@ -3704,14 +3876,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
break;
case SENSOR_POxxxx:
- setcolors(gspca_dev);
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
-
- /* led on */
- msleep(80);
- reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
usb_exchange(gspca_dev, poxxxx_init_end_2);
+ setwb(gspca_dev);
+ msleep(80); /* led on */
+ reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
break;
}
return gspca_dev->usb_err;
@@ -3911,6 +4079,80 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (gspca_dev->streaming)
+ 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_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->backlight = val;
+ if (gspca_dev->streaming)
+ setbacklight(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->backlight;
+ return 0;
+}
+
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 4473f0f..0666038 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -21,7 +21,9 @@
#define MODULE_NAME "zc3xx"
+#ifdef CONFIG_INPUT
#include <linux/input.h>
+#endif
#include "gspca.h"
#include "jpeg.h"
@@ -50,33 +52,38 @@ struct sd {
#define QUALITY_MAX 80
#define QUALITY_DEF 70
+ u8 bridge;
u8 sensor; /* Type of image sensor chip */
-/* !! values used in different tables */
-#define SENSOR_ADCM2700 0
-#define SENSOR_CS2102 1
-#define SENSOR_CS2102K 2
-#define SENSOR_GC0305 3
-#define SENSOR_HDCS2020b 4
-#define SENSOR_HV7131B 5
-#define SENSOR_HV7131C 6
-#define SENSOR_ICM105A 7
-#define SENSOR_MC501CB 8
-#define SENSOR_MI0360SOC 9
-#define SENSOR_OV7620 10
-/*#define SENSOR_OV7648 10 - same values */
-#define SENSOR_OV7630C 11
-#define SENSOR_PAS106 12
-#define SENSOR_PAS202B 13
-#define SENSOR_PB0330 14 /* (MI0360) */
-#define SENSOR_PO2030 15
-#define SENSOR_TAS5130CK 16
-#define SENSOR_TAS5130CXX 17
-#define SENSOR_TAS5130C_VF0250 18
-#define SENSOR_MAX 19
- unsigned short chip_revision;
+ u16 chip_revision;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
+enum bridges {
+ BRIDGE_ZC301,
+ BRIDGE_ZC303,
+};
+enum sensors {
+ SENSOR_ADCM2700,
+ SENSOR_CS2102,
+ SENSOR_CS2102K,
+ SENSOR_GC0305,
+ SENSOR_HDCS2020b,
+ SENSOR_HV7131B,
+ SENSOR_HV7131R,
+ SENSOR_ICM105A,
+ SENSOR_MC501CB,
+ SENSOR_MT9V111_1, /* (mi360soc) zc301 */
+ SENSOR_MT9V111_3, /* (mi360soc) zc303 */
+ SENSOR_OV7620, /* OV7648 - same values */
+ SENSOR_OV7630C,
+ SENSOR_PAS106,
+ SENSOR_PAS202B,
+ SENSOR_PB0330,
+ SENSOR_PO2030,
+ SENSOR_TAS5130C,
+ SENSOR_TAS5130C_VF0250,
+ SENSOR_MAX
+};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
@@ -2074,6 +2081,7 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
{}
};
+/* from lPEPI264v.inf (hv7131b!) */
static const struct usb_action hv7131r_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
@@ -2081,8 +2089,8 @@ static const struct usb_action hv7131r_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -2095,6 +2103,8 @@ static const struct usb_action hv7131r_InitialScale[] = {
{0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x000c},
{0xaa, 0x11, 0x0000},
@@ -2103,10 +2113,10 @@ static const struct usb_action hv7131r_InitialScale[] = {
{0xaa, 0x15, 0x00e8},
{0xaa, 0x16, 0x0002},
{0xaa, 0x17, 0x0088},
-
+ {0xaa, 0x30, 0x000b},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0xc0, 0x019b},
@@ -2116,96 +2126,44 @@ static const struct usb_action hv7131r_InitialScale[] = {
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf0, ZC3XX_R10B_RGB01},
- {0xa0, 0xf0, ZC3XX_R10C_RGB02},
- {0xa0, 0xf0, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xf0, ZC3XX_R10F_RGB12},
- {0xa0, 0xf0, ZC3XX_R110_RGB20},
- {0xa0, 0xf0, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x0053},
- {0xaa, 0x27, 0x0000},
-
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-
static const struct usb_action hv7131r_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
-
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 1e0 */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
{0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x000c},
{0xaa, 0x11, 0x0000},
{0xaa, 0x13, 0x0000},
{0xaa, 0x14, 0x0001},
- {0xaa, 0x15, 0x00e8},
+ {0xaa, 0x15, 0x00e6},
{0xaa, 0x16, 0x0002},
- {0xaa, 0x17, 0x0088},
-
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00 */
-
+ {0xaa, 0x17, 0x0086},
+ {0xaa, 0x30, 0x000b},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0xc0, 0x019b},
@@ -2215,58 +2173,114 @@ static const struct usb_action hv7131r_Initial[] = {
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- /* read the i2c chips ident */
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf0, ZC3XX_R10B_RGB01},
- {0xa0, 0xf0, ZC3XX_R10C_RGB02},
- {0xa0, 0xf0, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xf0, ZC3XX_R10F_RGB12},
- {0xa0, 0xf0, ZC3XX_R110_RGB20},
- {0xa0, 0xf0, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action hv7131r_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x0053},
- {0xaa, 0x27, 0x0000},
-
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */
-
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
{0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
-
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
{}
};
@@ -3350,7 +3364,7 @@ static const struct usb_action ov7620_NoFliker[] = {
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc
- - if mode1 (320x240) */
+ * if mode1 (320x240) */
/* ?? was
{0xa0, 0x00, 0x0039}, * 00,00,00,dd *
{0xa1, 0x01, 0x0037}, */
@@ -3439,7 +3453,6 @@ static const struct usb_action ov7630c_InitialScale[] = {
{0xa0, 0xf8, ZC3XX_R110_RGB20},
{0xa0, 0xf8, ZC3XX_R111_RGB21},
{0xa0, 0x50, ZC3XX_R112_RGB22},
-/* 0x03, */
{0xa1, 0x01, 0x0008},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
{0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
@@ -3719,7 +3732,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
{0xaa, 0x0e, 0x0002},
{0xaa, 0x14, 0x0081},
-/* Other registors */
+/* Other registers */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
/* Frame retreiving */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
@@ -3730,7 +3743,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
/* Sharpness */
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registors */
+/* Other registers */
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
/* Auto exposure and white balance */
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -3837,7 +3850,7 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */
{0xaa, 0x0e, 0x0002},
{0xaa, 0x14, 0x0081},
-/* Other registors */
+/* Other registers */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
/* Frame retreiving */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
@@ -3848,7 +3861,7 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */
/* Sharpness */
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registors */
+/* Other registers */
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
/* Auto exposure and white balance */
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -4241,8 +4254,8 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
{}
};
-/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */
-static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
+/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
+static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
@@ -4253,14 +4266,14 @@ static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
-/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x0001},
{0xaa, 0x06, 0x0000},
{0xaa, 0x08, 0x0483},
@@ -4270,18 +4283,18 @@ static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
{0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
{0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
{0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
- {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/
+ {0xaa, 0x20, 0x5100},
+ {0xaa, 0x35, 0x507f},
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
{0xaa, 0x58, 0x0078},
{0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x0028},
+ {0xaa, 0x2b, 0x007f},
{0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
{0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
{0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -4291,12 +4304,12 @@ static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
{0xa0, 0x61, ZC3XX_R116_RGAIN},
{0xa0, 0x65, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
+static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
@@ -4307,14 +4320,14 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
-/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x0001},
{0xaa, 0x06, 0x0000},
{0xaa, 0x08, 0x0483},
@@ -4324,7 +4337,7 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
{0xaa, 0x03, 0x01e7},
{0xaa, 0x04, 0x0287},
{0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
+ {0xaa, 0x20, 0x5100},
{0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
@@ -4335,7 +4348,7 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
{0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
{0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -4345,12 +4358,12 @@ static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
{0xa0, 0x61, ZC3XX_R116_RGAIN},
{0xa0, 0x65, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action mi360soc_AE50HZ[] = {
+static const struct usb_action mt9v111_1_AE50HZ[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0562},
@@ -4373,7 +4386,7 @@ static const struct usb_action mi360soc_AE50HZ[] = {
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AE50HZScale[] = {
+static const struct usb_action mt9v111_1_AE50HZScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
@@ -4395,11 +4408,11 @@ static const struct usb_action mi360soc_AE50HZScale[] = {
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AE60HZ[] = {
+static const struct usb_action mt9v111_1_AE60HZ[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x053d},
- {0xbb, 0x01, 0x096e},
+ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x09, 0x016e},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
{0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
@@ -4418,7 +4431,7 @@ static const struct usb_action mi360soc_AE60HZ[] = {
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AE60HZScale[] = {
+static const struct usb_action mt9v111_1_AE60HZScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
@@ -4440,7 +4453,7 @@ static const struct usb_action mi360soc_AE60HZScale[] = {
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AENoFliker[] = {
+static const struct usb_action mt9v111_1_AENoFliker[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
@@ -4463,7 +4476,7 @@ static const struct usb_action mi360soc_AENoFliker[] = {
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AENoFlikerScale[] = {
+static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0534},
@@ -4486,6 +4499,251 @@ static const struct usb_action mi360soc_AENoFlikerScale[] = {
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
+/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
+static const struct usb_action mt9v111_3_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */
+ {0xaa, 0x06, 0x0000}, /* operating mode control */
+ {0xaa, 0x08, 0x0483}, /* output format control */
+ /* H red first, V red or blue first,
+ * raw Bayer, auto flicker */
+ {0xaa, 0x01, 0x0004}, /* select sensor core registers */
+ {0xaa, 0x08, 0x0006}, /* row start */
+ {0xaa, 0x02, 0x0011}, /* column start */
+ {0xaa, 0x03, 0x01e5}, /* window height - 1 */
+ {0xaa, 0x04, 0x0285}, /* window width - 1 */
+ {0xaa, 0x07, 0x3002}, /* output control */
+ {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */
+ {0xaa, 0x35, 0x007f}, /* global gain */
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f}, /* green1 gain */
+ {0xaa, 0x2c, 0x007f}, /* blue gain */
+ {0xaa, 0x2d, 0x007f}, /* red gain */
+ {0xaa, 0x2e, 0x007f}, /* green2 gain */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_3_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x35, 0x007f},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f},
+ {0xaa, 0x2c, 0x007f},
+ {0xaa, 0x2d, 0x007f},
+ {0xaa, 0x2e, 0x007f},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_3_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009}, /* horizontal blanking */
+ {0xaa, 0x09, 0x01ce}, /* shutter width */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x01ce},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
static const struct usb_action pb0330_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
@@ -4928,419 +5186,7 @@ static const struct usb_action po2030_NoFliker[] = {
{}
};
-/* TEST */
-static const struct usb_action tas5130cK_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x01, 0x003b},
- {0xa0, 0x0e, 0x003a},
- {0xa0, 0x01, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x00, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xE7, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x87, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2c, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */
- {0xa0, 0x51, ZC3XX_R121_GAMMA01},
- {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
- {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
- {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
- {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
- {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
- {0xa0, 0xed, ZC3XX_R129_GAMMA09},
- {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x12, ZC3XX_R130_GAMMA10},
- {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
- {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
- {0xa0, 0x15, ZC3XX_R134_GAMMA14},
- {0xa0, 0x12, ZC3XX_R135_GAMMA15},
- {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x09, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x34, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-
-static const struct usb_action tas5130cK_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x01, 0x003b},
- {0xa0, 0x0e, 0x003a},
- {0xa0, 0x01, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x00, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xe5, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x85, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x50, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2C, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */
- {0xa0, 0x51, ZC3XX_R121_GAMMA01},
- {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
- {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
- {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
- {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
- {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
- {0xa0, 0xed, ZC3XX_R129_GAMMA09},
- {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x12, ZC3XX_R130_GAMMA10},
- {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
- {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
- {0xa0, 0x15, ZC3XX_R134_GAMMA14},
- {0xa0, 0x12, ZC3XX_R135_GAMMA15},
- {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x62, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xaa, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x30, 0x0007},
- {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x00, 0x0007},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {}
-};
-
-static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
+static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5377,7 +5223,7 @@ static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
{0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{}
};
-static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
+static const struct usb_action tas5130c_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
@@ -5413,7 +5259,7 @@ static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
{0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{}
};
-static const struct usb_action tas5130cxx_50HZ[] = {
+static const struct usb_action tas5130c_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
@@ -5438,7 +5284,7 @@ static const struct usb_action tas5130cxx_50HZ[] = {
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_50HZScale[] = {
+static const struct usb_action tas5130c_50HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
@@ -5463,7 +5309,7 @@ static const struct usb_action tas5130cxx_50HZScale[] = {
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_60HZ[] = {
+static const struct usb_action tas5130c_60HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
@@ -5488,7 +5334,7 @@ static const struct usb_action tas5130cxx_60HZ[] = {
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_60HZScale[] = {
+static const struct usb_action tas5130c_60HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
@@ -5513,7 +5359,7 @@ static const struct usb_action tas5130cxx_60HZScale[] = {
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_NoFliker[] = {
+static const struct usb_action tas5130c_NoFliker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
@@ -5539,7 +5385,7 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
{}
};
-static const struct usb_action tas5130cxx_NoFlikerScale[] = {
+static const struct usb_action tas5130c_NoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
@@ -5840,13 +5686,22 @@ static const struct usb_action tas5130c_vf0250_NoFliker[] = {
static u8 reg_r_i(struct gspca_dev *gspca_dev,
u16 index)
{
- usb_control_msg(gspca_dev->dev,
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
0xa1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x01, /* value */
index, gspca_dev->usb_buf, 1,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_r_i err %d", ret);
+ gspca_dev->usb_err = ret;
+ return 0;
+ }
return gspca_dev->usb_buf[0];
}
@@ -5860,24 +5715,32 @@ static u8 reg_r(struct gspca_dev *gspca_dev,
return ret;
}
-static void reg_w_i(struct usb_device *dev,
+static void reg_w_i(struct gspca_dev *gspca_dev,
u8 value,
u16 index)
{
- usb_control_msg(dev,
- usb_sndctrlpipe(dev, 0),
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
0xa0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w_i err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
}
-static void reg_w(struct usb_device *dev,
+static void reg_w(struct gspca_dev *gspca_dev,
u8 value,
u16 index)
{
PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
- reg_w_i(dev, value, index);
+ reg_w_i(gspca_dev, value, index);
}
static u16 i2c_read(struct gspca_dev *gspca_dev,
@@ -5886,8 +5749,10 @@ static u16 i2c_read(struct gspca_dev *gspca_dev,
u8 retbyte;
u16 retval;
- reg_w_i(gspca_dev->dev, reg, 0x0092);
- reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w_i(gspca_dev, reg, 0x0092);
+ reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */
msleep(20);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
@@ -5906,10 +5771,12 @@ static u8 i2c_write(struct gspca_dev *gspca_dev,
{
u8 retbyte;
- reg_w_i(gspca_dev->dev, reg, 0x92);
- reg_w_i(gspca_dev->dev, valL, 0x93);
- reg_w_i(gspca_dev->dev, valH, 0x94);
- reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w_i(gspca_dev, reg, 0x92);
+ reg_w_i(gspca_dev, valL, 0x93);
+ reg_w_i(gspca_dev, valH, 0x94);
+ reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */
msleep(1);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
@@ -5925,7 +5792,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
while (action->req) {
switch (action->req) {
case 0xa0: /* write register */
- reg_w(gspca_dev->dev, action->val, action->idx);
+ reg_w(gspca_dev, action->val, action->idx);
break;
case 0xa1: /* read status */
reg_r(gspca_dev, action->idx);
@@ -5974,38 +5841,37 @@ static void setmatrix(struct gspca_dev *gspca_dev)
static const u8 vf0250_matrix[9] =
{0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
static const u8 *matrix_tb[SENSOR_MAX] = {
- adcm2700_matrix, /* SENSOR_ADCM2700 0 */
- ov7620_matrix, /* SENSOR_CS2102 1 */
- NULL, /* SENSOR_CS2102K 2 */
- gc0305_matrix, /* SENSOR_GC0305 3 */
- NULL, /* SENSOR_HDCS2020b 4 */
- NULL, /* SENSOR_HV7131B 5 */
- NULL, /* SENSOR_HV7131C 6 */
- NULL, /* SENSOR_ICM105A 7 */
- NULL, /* SENSOR_MC501CB 8 */
- gc0305_matrix, /* SENSOR_MI0360SOC 9 */
- ov7620_matrix, /* SENSOR_OV7620 10 */
- NULL, /* SENSOR_OV7630C 11 */
- NULL, /* SENSOR_PAS106 12 */
- pas202b_matrix, /* SENSOR_PAS202B 13 */
- gc0305_matrix, /* SENSOR_PB0330 14 */
- po2030_matrix, /* SENSOR_PO2030 15 */
- NULL, /* SENSOR_TAS5130CK 16 */
- tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */
- vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_ADCM2700] = adcm2700_matrix,
+ [SENSOR_CS2102] = ov7620_matrix,
+ [SENSOR_CS2102K] = NULL,
+ [SENSOR_GC0305] = gc0305_matrix,
+ [SENSOR_HDCS2020b] = NULL,
+ [SENSOR_HV7131B] = NULL,
+ [SENSOR_HV7131R] = NULL,
+ [SENSOR_ICM105A] = po2030_matrix,
+ [SENSOR_MC501CB] = NULL,
+ [SENSOR_MT9V111_1] = gc0305_matrix,
+ [SENSOR_MT9V111_3] = gc0305_matrix,
+ [SENSOR_OV7620] = ov7620_matrix,
+ [SENSOR_OV7630C] = NULL,
+ [SENSOR_PAS106] = NULL,
+ [SENSOR_PAS202B] = pas202b_matrix,
+ [SENSOR_PB0330] = gc0305_matrix,
+ [SENSOR_PO2030] = po2030_matrix,
+ [SENSOR_TAS5130C] = tas5130c_matrix,
+ [SENSOR_TAS5130C_VF0250] = vf0250_matrix,
};
matrix = matrix_tb[sd->sensor];
if (matrix == NULL)
return; /* matrix already loaded */
for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
- reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
+ reg_w(gspca_dev, matrix[i], 0x010a + i);
}
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] = {
{0x02, 0x03},
@@ -6015,17 +5881,16 @@ static void setsharpness(struct gspca_dev *gspca_dev)
};
sharpness = sd->sharpness;
- reg_w(dev, sharpness_tb[sharpness][0], 0x01c6);
+ reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6);
reg_r(gspca_dev, 0x01c8);
reg_r(gspca_dev, 0x01c9);
reg_r(gspca_dev, 0x01ca);
- reg_w(dev, sharpness_tb[sharpness][1], 0x01cb);
+ reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb);
}
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;
int g, i, brightness, contrast, adj, gp1, gp2;
u8 gr[16];
@@ -6063,7 +5928,7 @@ static void setcontrast(struct gspca_dev *gspca_dev)
g = 0xff;
else if (g < 0)
g = 0;
- reg_w(dev, g, 0x0120 + i); /* gamma */
+ reg_w(gspca_dev, g, 0x0120 + i); /* gamma */
if (contrast > 0)
adj--;
else if (contrast < 0)
@@ -6077,13 +5942,12 @@ static void setcontrast(struct gspca_dev *gspca_dev)
}
gr[15] = (0xff - gp2) / 2;
for (i = 0; i < 16; i++)
- reg_w(dev, gr[i], 0x0130 + i); /* gradient */
+ reg_w(gspca_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;
switch (sd->sensor) {
@@ -6096,9 +5960,9 @@ static void setquality(struct gspca_dev *gspca_dev)
return;
}
/*fixme: is it really 0008 0007 0018 for all other sensors? */
- reg_w(dev, QUANT_VAL, 0x0008);
+ reg_w(gspca_dev, QUANT_VAL, 0x0008);
frxt = 0x30;
- reg_w(dev, frxt, 0x0007);
+ reg_w(gspca_dev, frxt, 0x0007);
#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2
frxt = 0xff;
#elif QUANT_VAL == 3
@@ -6108,7 +5972,7 @@ static void setquality(struct gspca_dev *gspca_dev)
#else
frxt = 0x20;
#endif
- reg_w(dev, frxt, 0x0018);
+ reg_w(gspca_dev, frxt, 0x0018);
}
/* Matches the sensor's internal frame rate to the lighting frequency.
@@ -6116,87 +5980,86 @@ static void setquality(struct gspca_dev *gspca_dev)
* 50Hz, for European and Asian lighting (default)
* 60Hz, for American lighting
* 0 = No Fliker (for outdoore usage)
- * Returns: 0 for success
*/
-static int setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i, mode;
const struct usb_action *zc3_freq;
static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
-/* SENSOR_ADCM2700 0 */
+ [SENSOR_ADCM2700] =
{adcm2700_NoFliker, adcm2700_NoFliker,
adcm2700_50HZ, adcm2700_50HZ,
adcm2700_60HZ, adcm2700_60HZ},
-/* SENSOR_CS2102 1 */
+ [SENSOR_CS2102] =
{cs2102_NoFliker, cs2102_NoFlikerScale,
cs2102_50HZ, cs2102_50HZScale,
cs2102_60HZ, cs2102_60HZScale},
-/* SENSOR_CS2102K 2 */
+ [SENSOR_CS2102K] =
{cs2102_NoFliker, cs2102_NoFlikerScale,
NULL, NULL, /* currently disabled */
NULL, NULL},
-/* SENSOR_GC0305 3 */
+ [SENSOR_GC0305] =
{gc0305_NoFliker, gc0305_NoFliker,
gc0305_50HZ, gc0305_50HZ,
gc0305_60HZ, gc0305_60HZ},
-/* SENSOR_HDCS2020b 4 */
+ [SENSOR_HDCS2020b] =
{hdcs2020b_NoFliker, hdcs2020b_NoFliker,
hdcs2020b_50HZ, hdcs2020b_50HZ,
hdcs2020b_60HZ, hdcs2020b_60HZ},
-/* SENSOR_HV7131B 5 */
+ [SENSOR_HV7131B] =
{hv7131b_NoFliker, hv7131b_NoFlikerScale,
hv7131b_50HZ, hv7131b_50HZScale,
hv7131b_60HZ, hv7131b_60HZScale},
-/* SENSOR_HV7131C 6 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_ICM105A 7 */
+ [SENSOR_HV7131R] =
+ {hv7131r_NoFliker, hv7131r_NoFlikerScale,
+ hv7131r_50HZ, hv7131r_50HZScale,
+ hv7131r_60HZ, hv7131r_60HZScale},
+ [SENSOR_ICM105A] =
{icm105a_NoFliker, icm105a_NoFlikerScale,
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
-/* SENSOR_MC501CB 8 */
+ [SENSOR_MC501CB] =
{mc501cb_NoFliker, mc501cb_NoFlikerScale,
mc501cb_50HZ, mc501cb_50HZScale,
mc501cb_60HZ, mc501cb_60HZScale},
-/* SENSOR_MI0360SOC 9 */
- {mi360soc_AENoFliker, mi360soc_AENoFlikerScale,
- mi360soc_AE50HZ, mi360soc_AE50HZScale,
- mi360soc_AE60HZ, mi360soc_AE60HZScale},
-/* SENSOR_OV7620 10 */
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+ mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
+ mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+ mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
+ mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
+ [SENSOR_OV7620] =
{ov7620_NoFliker, ov7620_NoFliker,
ov7620_50HZ, ov7620_50HZ,
ov7620_60HZ, ov7620_60HZ},
-/* SENSOR_OV7630C 11 */
+ [SENSOR_OV7630C] =
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_PAS106 12 */
+ [SENSOR_PAS106] =
{pas106b_NoFliker, pas106b_NoFliker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
-/* SENSOR_PAS202B 13 */
+ [SENSOR_PAS202B] =
{pas202b_NoFliker, pas202b_NoFlikerScale,
pas202b_50HZ, pas202b_50HZScale,
pas202b_60HZ, pas202b_60HZScale},
-/* SENSOR_PB0330 14 */
+ [SENSOR_PB0330] =
{pb0330_NoFliker, pb0330_NoFlikerScale,
pb0330_50HZ, pb0330_50HZScale,
pb0330_60HZ, pb0330_60HZScale},
-/* SENSOR_PO2030 15 */
+ [SENSOR_PO2030] =
{po2030_NoFliker, po2030_NoFliker,
po2030_50HZ, po2030_50HZ,
po2030_60HZ, po2030_60HZ},
-/* SENSOR_TAS5130CK 16 */
- {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
- tas5130cxx_50HZ, tas5130cxx_50HZScale,
- tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130CXX 17 */
- {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
- tas5130cxx_50HZ, tas5130cxx_50HZScale,
- tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_TAS5130C] =
+ {tas5130c_NoFliker, tas5130c_NoFlikerScale,
+ tas5130c_50HZ, tas5130c_50HZScale,
+ tas5130c_60HZ, tas5130c_60HZScale},
+ [SENSOR_TAS5130C_VF0250] =
{tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
@@ -6207,29 +6070,28 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
if (mode)
i++; /* 320x240 */
zc3_freq = freq_tb[sd->sensor][i];
- if (zc3_freq != NULL) {
- usb_exchange(gspca_dev, zc3_freq);
- switch (sd->sensor) {
- case SENSOR_GC0305:
- if (mode /* if 320x240 */
- && sd->lightfreq == 1) /* and 50Hz */
- reg_w(gspca_dev->dev, 0x85, 0x018d);
- /* win: 0x80, 0x018d */
- break;
- case SENSOR_OV7620:
- if (!mode) { /* if 640x480 */
- if (sd->lightfreq != 0) /* and 50 or 60 Hz */
- reg_w(gspca_dev->dev, 0x40, 0x0002);
- else
- reg_w(gspca_dev->dev, 0x44, 0x0002);
- }
- break;
- case SENSOR_PAS202B:
- reg_w(gspca_dev->dev, 0x00, 0x01a7);
- break;
+ if (zc3_freq == NULL)
+ return;
+ usb_exchange(gspca_dev, zc3_freq);
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+ if (mode /* if 320x240 */
+ && sd->lightfreq == 1) /* and 50Hz */
+ reg_w(gspca_dev, 0x85, 0x018d);
+ /* win: 0x80, 0x018d */
+ break;
+ case SENSOR_OV7620:
+ if (!mode) { /* if 640x480 */
+ if (sd->lightfreq != 0) /* and 50 or 60 Hz */
+ reg_w(gspca_dev, 0x40, 0x0002);
+ else
+ reg_w(gspca_dev, 0x44, 0x0002);
}
+ break;
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x00, 0x01a7);
+ break;
}
- return 0;
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -6241,45 +6103,46 @@ static void setautogain(struct gspca_dev *gspca_dev)
autoval = 0x42;
else
autoval = 0x02;
- reg_w(gspca_dev->dev, autoval, 0x0180);
+ reg_w(gspca_dev, autoval, 0x0180);
}
-static void send_unknown(struct usb_device *dev, int sensor)
+static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
{
- reg_w(dev, 0x01, 0x0000); /* led off */
+ reg_w(gspca_dev, 0x01, 0x0000); /* led off */
switch (sensor) {
case SENSOR_PAS106:
- reg_w(dev, 0x03, 0x003a);
- reg_w(dev, 0x0c, 0x003b);
- reg_w(dev, 0x08, 0x0038);
+ reg_w(gspca_dev, 0x03, 0x003a);
+ reg_w(gspca_dev, 0x0c, 0x003b);
+ reg_w(gspca_dev, 0x08, 0x0038);
break;
case SENSOR_ADCM2700:
case SENSOR_GC0305:
case SENSOR_OV7620:
- case SENSOR_MI0360SOC:
+ case SENSOR_MT9V111_1:
+ case SENSOR_MT9V111_3:
case SENSOR_PB0330:
case SENSOR_PO2030:
- reg_w(dev, 0x0d, 0x003a);
- reg_w(dev, 0x02, 0x003b);
- reg_w(dev, 0x00, 0x0038);
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_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);
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
+ reg_w(gspca_dev, 0x0b, 0x0038);
break;
}
}
/* start probe 2 wires */
-static void start_2wr_probe(struct usb_device *dev, int sensor)
+static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
{
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, sensor, 0x0010);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, sensor, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
/* msleep(2); */
}
@@ -6287,14 +6150,14 @@ static int sif_probe(struct gspca_dev *gspca_dev)
{
u16 checkword;
- start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */
- reg_w(gspca_dev->dev, 0x08, 0x008d);
+ start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */
+ reg_w(gspca_dev, 0x08, 0x008d);
msleep(150);
checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
| ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
if (checkword == 0x0007) {
- send_unknown(gspca_dev->dev, SENSOR_PAS106);
+ send_unknown(gspca_dev, SENSOR_PAS106);
return 0x0f; /* PAS106 */
}
return -1;
@@ -6302,23 +6165,22 @@ static int sif_probe(struct gspca_dev *gspca_dev)
static int vga_2wr_probe(struct gspca_dev *gspca_dev)
{
- struct usb_device *dev = gspca_dev->dev;
u16 retword;
- start_2wr_probe(dev, 0x00); /* HV7131B */
+ start_2wr_probe(gspca_dev, 0x00); /* HV7131B */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
return 0x00; /* HV7131B */
- start_2wr_probe(dev, 0x04); /* CS2102 */
+ start_2wr_probe(gspca_dev, 0x04); /* CS2102 */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
return 0x04; /* CS2102 */
- start_2wr_probe(dev, 0x06); /* OmniVision */
- reg_w(dev, 0x08, 0x008d);
+ start_2wr_probe(gspca_dev, 0x06); /* OmniVision */
+ reg_w(gspca_dev, 0x08, 0x008d);
i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x11);
if (retword != 0) {
@@ -6327,14 +6189,14 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
goto ov_check;
}
- start_2wr_probe(dev, 0x08); /* HDCS2020 */
+ start_2wr_probe(gspca_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)
return 0x08; /* HDCS2020 */
- start_2wr_probe(dev, 0x0a); /* PB0330 */
+ start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */
i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
retword = i2c_read(gspca_dev, 0x07);
if (retword != 0)
@@ -6346,23 +6208,23 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
if (retword != 0)
return 0x0a; /* PB0330 ?? */
- start_2wr_probe(dev, 0x0c); /* ICM105A */
+ start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */
i2c_write(gspca_dev, 0x01, 0x11, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
return 0x0c; /* ICM105A */
- start_2wr_probe(dev, 0x0e); /* PAS202BCB */
- reg_w(dev, 0x08, 0x008d);
+ start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */
+ reg_w(gspca_dev, 0x08, 0x008d);
i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
msleep(50);
retword = i2c_read(gspca_dev, 0x03);
if (retword != 0) {
- send_unknown(dev, SENSOR_PAS202B);
+ send_unknown(gspca_dev, SENSOR_PAS202B);
return 0x0e; /* PAS202BCB */
}
- start_2wr_probe(dev, 0x02); /* TAS5130C */
+ start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
@@ -6371,20 +6233,20 @@ ov_check:
reg_r(gspca_dev, 0x0010); /* ?? */
reg_r(gspca_dev, 0x0010);
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x06, 0x0010); /* OmniVision */
- reg_w(dev, 0xa1, 0x008b);
- reg_w(dev, 0x08, 0x008d);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
msleep(500);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
retword = i2c_read(gspca_dev, 0x0a) << 8;
retword |= i2c_read(gspca_dev, 0x0b);
PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
switch (retword) {
case 0x7631: /* OV7630C */
- reg_w(dev, 0x06, 0x0010);
+ reg_w(gspca_dev, 0x06, 0x0010);
break;
case 0x7620: /* OV7620 */
case 0x7648: /* OV7648 */
@@ -6401,32 +6263,31 @@ struct sensor_by_chipset_revision {
};
static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
{0xc000, 0x12}, /* TAS5130C */
- {0xc001, 0x13}, /* MI0360SOC */
+ {0xc001, 0x13}, /* MT9V111 */
{0xe001, 0x13},
{0x8001, 0x13},
{0x8000, 0x14}, /* CS2102K */
- {0x8400, 0x15}, /* TAS5130K */
+ {0x8400, 0x15}, /* MT9V111 */
{0xe400, 0x15},
};
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;
u16 retword;
/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
- reg_w(dev, 0x02, 0x0010);
+ reg_w(gspca_dev, 0x02, 0x0010);
reg_r(gspca_dev, 0x0010);
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x00, 0x0010);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x91, 0x008b);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x91, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
retword = i2c_read(gspca_dev, 0x14);
if (retword != 0)
return 0x11; /* HV7131R */
@@ -6437,93 +6298,90 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
if (retword != 0)
return 0x11; /* HV7131R */
- reg_w(dev, 0x02, 0x0010);
+ reg_w(gspca_dev, 0x02, 0x0010);
retword = reg_r(gspca_dev, 0x000b) << 8;
retword |= reg_r(gspca_dev, 0x000a);
PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
reg_r(gspca_dev, 0x0010);
- /* value 0x4001 is meaningless */
- if (retword != 0x4001) {
- if ((retword & 0xff00) == 0x6400)
- return 0x02; /* TAS5130C */
- for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
- if (chipset_revision_sensor[i].revision == retword) {
- sd->chip_revision = retword;
- send_unknown(dev, SENSOR_PB0330);
- return chipset_revision_sensor[i]
- .internal_sensor_id;
- }
+ if ((retword & 0xff00) == 0x6400)
+ return 0x02; /* TAS5130C */
+ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+ if (chipset_revision_sensor[i].revision == retword) {
+ sd->chip_revision = retword;
+ send_unknown(gspca_dev, SENSOR_PB0330);
+ return chipset_revision_sensor[i]
+ .internal_sensor_id;
}
}
- reg_w(dev, 0x01, 0x0000); /* check PB0330 */
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0xdd, 0x008b);
- reg_w(dev, 0x0a, 0x0010);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xdd, 0x008b);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
retword = i2c_read(gspca_dev, 0x00);
if (retword != 0) {
- PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a");
return 0x0a; /* PB0330 */
}
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x98, 0x008b);
- reg_w(dev, 0x01, 0x0010);
- reg_w(dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x98, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
msleep(2);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
retword = i2c_read(gspca_dev, 0x00);
if (retword != 0) {
PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
if (retword == 0x0011) /* VF0250 */
return 0x0250;
if (retword == 0x0029) /* gc0305 */
- send_unknown(dev, SENSOR_GC0305);
+ send_unknown(gspca_dev, SENSOR_GC0305);
return retword;
}
- reg_w(dev, 0x01, 0x0000); /* check OmniVision */
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0xa1, 0x008b);
- reg_w(dev, 0x08, 0x008d);
- reg_w(dev, 0x06, 0x0010);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
+ reg_w(gspca_dev, 0x06, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
&& i2c_read(gspca_dev, 0x1d) == 0x00a2) {
- send_unknown(dev, SENSOR_OV7620);
+ send_unknown(gspca_dev, SENSOR_OV7620);
return 0x06; /* OmniVision confirm ? */
}
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x00, 0x0002);
- reg_w(dev, 0x01, 0x0010);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0xee, 0x008b);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0002);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xee, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */
PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
if (retword == 0x2030) {
retbyte = i2c_read(gspca_dev, 0x02); /* revision number */
PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
- send_unknown(dev, SENSOR_PO2030);
+ send_unknown(gspca_dev, SENSOR_PO2030);
return retword;
}
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x0a, 0x0010);
- reg_w(dev, 0xd3, 0x008b);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
- reg_w(dev, 0xd3, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0xd3, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0xd3, 0x008b);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0) {
PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
@@ -6560,54 +6418,74 @@ static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
+
+ if (id->idProduct == 0x301b)
+ sd->bridge = BRIDGE_ZC301;
+ else
+ sd->bridge = BRIDGE_ZC303;
+
+ /* define some sensors from the vendor/product */
+ sd->sensor = id->driver_info;
+
+ sd->sharpness = SHARPNESS_DEF;
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->lightfreq = FREQ_DEF;
+ sd->quality = QUALITY_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;
struct cam *cam;
int sensor;
static const u8 gamma[SENSOR_MAX] = {
- 4, /* SENSOR_ADCM2700 0 */
- 4, /* SENSOR_CS2102 1 */
- 5, /* SENSOR_CS2102K 2 */
- 4, /* SENSOR_GC0305 3 */
- 4, /* SENSOR_HDCS2020b 4 */
- 4, /* SENSOR_HV7131B 5 */
- 4, /* SENSOR_HV7131C 6 */
- 4, /* SENSOR_ICM105A 7 */
- 4, /* SENSOR_MC501CB 8 */
- 4, /* SENSOR_MI0360SOC 9 */
- 3, /* SENSOR_OV7620 10 */
- 4, /* SENSOR_OV7630C 11 */
- 4, /* SENSOR_PAS106 12 */
- 4, /* SENSOR_PAS202B 13 */
- 4, /* SENSOR_PB0330 14 */
- 4, /* SENSOR_PO2030 15 */
- 4, /* SENSOR_TAS5130CK 16 */
- 3, /* SENSOR_TAS5130CXX 17 */
- 3, /* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_ADCM2700] = 4,
+ [SENSOR_CS2102] = 4,
+ [SENSOR_CS2102K] = 5,
+ [SENSOR_GC0305] = 4,
+ [SENSOR_HDCS2020b] = 4,
+ [SENSOR_HV7131B] = 4,
+ [SENSOR_HV7131R] = 4,
+ [SENSOR_ICM105A] = 4,
+ [SENSOR_MC501CB] = 4,
+ [SENSOR_MT9V111_1] = 4,
+ [SENSOR_MT9V111_3] = 4,
+ [SENSOR_OV7620] = 3,
+ [SENSOR_OV7630C] = 4,
+ [SENSOR_PAS106] = 4,
+ [SENSOR_PAS202B] = 4,
+ [SENSOR_PB0330] = 4,
+ [SENSOR_PO2030] = 4,
+ [SENSOR_TAS5130C] = 3,
+ [SENSOR_TAS5130C_VF0250] = 3,
};
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 */
+ [SENSOR_ADCM2700] = 2,
+ [SENSOR_CS2102] = 1,
+ [SENSOR_CS2102K] = 1,
+ [SENSOR_GC0305] = 1,
+ [SENSOR_HDCS2020b] = 1,
+ [SENSOR_HV7131B] = 1,
+ [SENSOR_HV7131R] = 1,
+ [SENSOR_ICM105A] = 1,
+ [SENSOR_MC501CB] = 2,
+ [SENSOR_MT9V111_1] = 1,
+ [SENSOR_MT9V111_3] = 1,
+ [SENSOR_OV7620] = 2,
+ [SENSOR_OV7630C] = 1,
+ [SENSOR_PAS106] = 0,
+ [SENSOR_PAS202B] = 1,
+ [SENSOR_PB0330] = 1,
+ [SENSOR_PO2030] = 1,
+ [SENSOR_TAS5130C] = 1,
+ [SENSOR_TAS5130C_VF0250] = 1,
};
- /* define some sensors from the vendor/product */
- sd->sharpness = SHARPNESS_DEF;
- sd->sensor = id->driver_info;
sensor = zcxx_probeSensor(gspca_dev);
if (sensor >= 0)
PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
@@ -6626,8 +6504,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
default:
PDEBUG(D_PROBE,
- "Sensor UNKNOWN_0 force Tas5130");
- sd->sensor = SENSOR_TAS5130CXX;
+ "Unknown sensor - set to TAS5130C");
+ sd->sensor = SENSOR_TAS5130C;
}
break;
case 0:
@@ -6642,14 +6520,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
default:
/* case 2: * hv7131r */
- PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
- sd->sensor = SENSOR_HV7131C;
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
break;
}
break;
case 0x02:
PDEBUG(D_PROBE, "Sensor TAS5130C");
- sd->sensor = SENSOR_TAS5130CXX;
+ sd->sensor = SENSOR_TAS5130C;
break;
case 0x04:
PDEBUG(D_PROBE, "Find Sensor CS2102");
@@ -6681,17 +6559,20 @@ static int sd_config(struct gspca_dev *gspca_dev,
case 0x10:
case 0x12:
PDEBUG(D_PROBE, "Find Sensor TAS5130C");
- sd->sensor = SENSOR_TAS5130CXX;
+ sd->sensor = SENSOR_TAS5130C;
break;
case 0x11:
- PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
- sd->sensor = SENSOR_HV7131C;
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
break;
case 0x13:
+ case 0x15:
PDEBUG(D_PROBE,
- "Find Sensor MI0360SOC. Chip revision %x",
+ "Sensor MT9V111. Chip revision %04x",
sd->chip_revision);
- sd->sensor = SENSOR_MI0360SOC;
+ sd->sensor = sd->bridge == BRIDGE_ZC301
+ ? SENSOR_MT9V111_1
+ : SENSOR_MT9V111_3;
break;
case 0x14:
PDEBUG(D_PROBE,
@@ -6699,12 +6580,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->chip_revision);
sd->sensor = SENSOR_CS2102K;
break;
- case 0x15:
- PDEBUG(D_PROBE,
- "Find Sensor TAS5130CK?. Chip revision %x",
- sd->chip_revision);
- sd->sensor = SENSOR_TAS5130CK;
- break;
case 0x16:
PDEBUG(D_PROBE, "Find Sensor ADCM2700");
sd->sensor = SENSOR_ADCM2700;
@@ -6741,13 +6616,11 @@ 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);
+ reg_w(gspca_dev, 0x02, 0x0010);
reg_r(gspca_dev, 0x0010);
}
cam = &gspca_dev->cam;
-/*fixme:test*/
- gspca_dev->nbalt--;
switch (mode_tb[sd->sensor]) {
case 0:
cam->cam_mode = sif_mode;
@@ -6763,58 +6636,62 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(broken_vga_mode);
break;
}
- 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_HV7131B:
- case SENSOR_HV7131C:
case SENSOR_OV7630C:
gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
break;
}
- 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;
+ reg_w(gspca_dev, 0x01, 0x0000);
+ return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
int mode;
static const struct usb_action *init_tb[SENSOR_MAX][2] = {
- {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */
- {cs2102_Initial, cs2102_InitialScale}, /* 1 */
- {cs2102K_Initial, cs2102K_InitialScale}, /* 2 */
- {gc0305_Initial, gc0305_InitialScale}, /* 3 */
- {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_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_Initial, po2030_InitialScale}, /* 15 */
- {tas5130cK_Initial, tas5130cK_InitialScale}, /* 16 */
- {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */
+ [SENSOR_ADCM2700] =
+ {adcm2700_Initial, adcm2700_InitialScale},
+ [SENSOR_CS2102] =
+ {cs2102_Initial, cs2102_InitialScale},
+ [SENSOR_CS2102K] =
+ {cs2102K_Initial, cs2102K_InitialScale},
+ [SENSOR_GC0305] =
+ {gc0305_Initial, gc0305_InitialScale},
+ [SENSOR_HDCS2020b] =
+ {hdcs2020b_Initial, hdcs2020b_InitialScale},
+ [SENSOR_HV7131B] =
+ {hv7131b_Initial, hv7131b_InitialScale},
+ [SENSOR_HV7131R] =
+ {hv7131r_Initial, hv7131r_InitialScale},
+ [SENSOR_ICM105A] =
+ {icm105a_Initial, icm105a_InitialScale},
+ [SENSOR_MC501CB] =
+ {mc501cb_Initial, mc501cb_InitialScale},
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_Initial, mt9v111_1_InitialScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_Initial, mt9v111_3_InitialScale},
+ [SENSOR_OV7620] =
+ {ov7620_Initial, ov7620_InitialScale},
+ [SENSOR_OV7630C] =
+ {ov7630c_Initial, ov7630c_InitialScale},
+ [SENSOR_PAS106] =
+ {pas106b_Initial, pas106b_InitialScale},
+ [SENSOR_PAS202B] =
+ {pas202b_Initial, pas202b_InitialScale},
+ [SENSOR_PB0330] =
+ {pb0330_Initial, pb0330_InitialScale},
+ [SENSOR_PO2030] =
+ {po2030_Initial, po2030_InitialScale},
+ [SENSOR_TAS5130C] =
+ {tas5130c_Initial, tas5130c_InitialScale},
+ [SENSOR_TAS5130C_VF0250] =
{tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale},
- /* 18 */
};
/* create the JPEG header */
@@ -6824,7 +6701,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
switch (sd->sensor) {
- case SENSOR_HV7131C:
+ case SENSOR_HV7131R:
zcxx_probeSensor(gspca_dev);
break;
case SENSOR_PAS106:
@@ -6838,22 +6715,22 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_GC0305:
case SENSOR_OV7620:
case SENSOR_PO2030:
- case SENSOR_TAS5130CXX:
+ case SENSOR_TAS5130C:
case SENSOR_TAS5130C_VF0250:
/* msleep(100); * ?? */
reg_r(gspca_dev, 0x0002); /* --> 0x40 */
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- if (sd->sensor == SENSOR_TAS5130CXX)
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ if (sd->sensor == SENSOR_TAS5130C)
break;
- reg_w(dev, 0x0d, 0x003a);
- reg_w(dev, 0x02, 0x003b);
- reg_w(dev, 0x00, 0x0038);
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
break;
case SENSOR_PAS202B:
- reg_w(dev, 0x03, 0x003b);
- reg_w(dev, 0x0c, 0x003a);
- reg_w(dev, 0x0b, 0x0039);
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
break;
}
@@ -6862,15 +6739,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_ADCM2700:
case SENSOR_OV7620:
reg_r(gspca_dev, 0x0008);
- reg_w(dev, 0x00, 0x0008);
+ reg_w(gspca_dev, 0x00, 0x0008);
break;
case SENSOR_PAS202B:
case SENSOR_GC0305:
- case SENSOR_TAS5130CXX:
+ case SENSOR_TAS5130C:
reg_r(gspca_dev, 0x0008);
/* fall thru */
case SENSOR_PO2030:
- reg_w(dev, 0x03, 0x0008);
+ reg_w(gspca_dev, 0x03, 0x0008);
break;
}
setsharpness(gspca_dev);
@@ -6880,7 +6757,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_CS2102K: /* gamma set in xxx_Initial */
case SENSOR_HDCS2020b:
case SENSOR_OV7630C:
- case SENSOR_TAS5130CK:
break;
default:
setcontrast(gspca_dev);
@@ -6891,7 +6767,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_OV7620:
case SENSOR_PAS202B:
reg_r(gspca_dev, 0x0180); /* from win */
- reg_w(dev, 0x00, 0x0180);
+ reg_w(gspca_dev, 0x00, 0x0180);
break;
default:
setquality(gspca_dev);
@@ -6901,29 +6777,29 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_ADCM2700:
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- reg_w(dev, 0x02, 0x0180);
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ reg_w(gspca_dev, 0x02, 0x0180);
/* ms-win + */
- reg_w(dev, 0x40, 0x0117);
+ reg_w(gspca_dev, 0x40, 0x0117);
break;
case SENSOR_GC0305:
- case SENSOR_TAS5130CXX:
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
+ case SENSOR_TAS5130C:
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
/* fall thru */
case SENSOR_PAS202B:
case SENSOR_PO2030:
-/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
+/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
reg_r(gspca_dev, 0x0180);
break;
case SENSOR_OV7620:
- reg_w(dev, 0x09, 0x01ad);
- reg_w(dev, 0x15, 0x01ae);
+ reg_w(gspca_dev, 0x09, 0x01ad);
+ reg_w(gspca_dev, 0x15, 0x01ae);
i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
/*fixme: returned value to send? */
- reg_w(dev, 0x40, 0x0117);
+ reg_w(gspca_dev, 0x40, 0x0117);
reg_r(gspca_dev, 0x0180);
break;
}
@@ -6932,11 +6808,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_PO2030:
msleep(50);
- reg_w(dev, 0x00, 0x0007); /* (from win traces) */
- reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+ reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */
+ reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING);
break;
}
- return 0;
+ return gspca_dev->usb_err;
}
/* called on streamoff with alt 0 and on disconnect */
@@ -6946,7 +6822,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
if (!gspca_dev->present)
return;
- send_unknown(gspca_dev->dev, sd->sensor);
+ send_unknown(gspca_dev, sd->sensor);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -6981,7 +6857,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
sd->brightness = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -6999,7 +6875,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
sd->contrast = 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)
@@ -7017,7 +6893,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
sd->autogain = val;
if (gspca_dev->streaming)
setautogain(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7035,7 +6911,7 @@ static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
sd->gamma = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7053,7 +6929,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
sd->lightfreq = val;
if (gspca_dev->streaming)
setlightfreq(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7071,7 +6947,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
sd->sharpness = val;
if (gspca_dev->streaming)
setsharpness(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7116,7 +6992,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
sd->quality = jcomp->quality;
if (gspca_dev->streaming)
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -7220,7 +7096,6 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x10fd, 0x8050)},
{} /* end of entry */
};
-#undef DVNAME
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
index c46bfb1..be4af1f 100644
--- a/drivers/media/video/ivtv/Kconfig
+++ b/drivers/media/video/ivtv/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_IVTV
depends on VIDEO_V4L2 && PCI && I2C
depends on INPUT # due to VIDEO_IR
select I2C_ALGOBIT
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index b588e30..b31ee1bc 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -17,163 +17,14 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/kernel.h>
-#include <linux/slab.h>
#include "ivtv-driver.h"
-#include "ivtv-cards.h"
#include "ivtv-ioctl.h"
-#include "ivtv-routing.h"
-#include "ivtv-i2c.h"
-#include "ivtv-mailbox.h"
#include "ivtv-controls.h"
-/* Must be sorted from low to high control ID! */
-static const u32 user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
- V4L2_CID_AUDIO_BALANCE,
- V4L2_CID_AUDIO_BASS,
- V4L2_CID_AUDIO_TREBLE,
- V4L2_CID_AUDIO_MUTE,
- V4L2_CID_AUDIO_LOUDNESS,
- 0
-};
-
-static const u32 *ctrl_classes[] = {
- user_ctrls,
- cx2341x_mpeg_ctrls,
- NULL
-};
-
-
-int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
-{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- const char *name;
-
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (qctrl->id == 0)
- return -EINVAL;
-
- switch (qctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_USER_CLASS:
- return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-
- default:
- if (cx2341x_ctrl_query(&itv->params, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
- }
- strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
- qctrl->name[sizeof(qctrl->name) - 1] = 0;
- return 0;
-}
-
-int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
-{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- struct v4l2_queryctrl qctrl;
-
- qctrl.id = qmenu->id;
- ivtv_queryctrl(file, fh, &qctrl);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- cx2341x_ctrl_get_menu(&itv->params, qmenu->id));
-}
-
-static int ivtv_try_ctrl(struct file *file, void *fh,
- struct v4l2_ext_control *vctrl)
-{
- struct v4l2_queryctrl qctrl;
- const char **menu_items = NULL;
- int err;
-
- qctrl.id = vctrl->id;
- err = ivtv_queryctrl(file, fh, &qctrl);
- if (err)
- return err;
- if (qctrl.type == V4L2_CTRL_TYPE_MENU)
- menu_items = v4l2_ctrl_get_menu(qctrl.id);
- return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
-}
-
-static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
-{
- switch (vctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
-
- default:
- IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
{
- switch (vctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
- default:
- IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
-{
- if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
- return -EINVAL;
- if (atomic_read(&itv->capturing) > 0)
- return -EBUSY;
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
/* First try to allocate sliced VBI buffers if needed. */
if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
@@ -208,106 +59,43 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm
return 0;
}
-int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- struct v4l2_control ctrl;
-
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- ctrl.id = c->controls[i].id;
- ctrl.value = c->controls[i].value;
- err = ivtv_g_ctrl(itv, &ctrl);
- c->controls[i].value = ctrl.value;
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
- return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS);
- return -EINVAL;
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_mbus_framefmt fmt;
+
+ /* fix videodecoder resolution */
+ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+ fmt.height = cxhdl->height;
+ fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
+ return 0;
}
-int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- struct v4l2_control ctrl;
-
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- ctrl.id = c->controls[i].id;
- ctrl.value = c->controls[i].value;
- err = ivtv_s_ctrl(itv, &ctrl);
- c->controls[i].value = ctrl.value;
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
- static u32 freqs[3] = { 44100, 48000, 32000 };
- struct cx2341x_mpeg_params p = itv->params;
- int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS);
- unsigned idx;
-
- if (err)
- return err;
+ static const u32 freqs[3] = { 44100, 48000, 32000 };
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
- if (p.video_encoding != itv->params.video_encoding) {
- int is_mpeg1 = p.video_encoding ==
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
- struct v4l2_mbus_framefmt fmt;
-
- /* fix videodecoder resolution */
- fmt.width = itv->params.width / (is_mpeg1 ? 2 : 1);
- fmt.height = itv->params.height;
- fmt.code = V4L2_MBUS_FMT_FIXED;
- v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
- }
- err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
- if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
- err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
- itv->params = p;
- itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
- idx = p.audio_properties & 0x03;
- /* The audio clock of the digitizer must match the codec sample
- rate otherwise you get some very strange effects. */
- if (idx < ARRAY_SIZE(freqs))
- ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
- return err;
- }
- return -EINVAL;
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (idx < ARRAY_SIZE(freqs))
+ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
+ return 0;
}
-int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- err = ivtv_try_ctrl(file, fh, &c->controls[i]);
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
- return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS);
- return -EINVAL;
+ itv->dualwatch_stereo_mode = val;
+ return 0;
}
+
+struct cx2341x_handler_ops ivtv_cxhdl_ops = {
+ .s_audio_mode = ivtv_s_audio_mode,
+ .s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
+ .s_video_encoding = ivtv_s_video_encoding,
+ .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
+};
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
index 1c7721e..d12893d 100644
--- a/drivers/media/video/ivtv/ivtv-controls.h
+++ b/drivers/media/video/ivtv/ivtv-controls.h
@@ -21,10 +21,6 @@
#ifndef IVTV_CONTROLS_H
#define IVTV_CONTROLS_H
-int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
-int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
+extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
#endif
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 07c5c18..e421d15 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -53,6 +53,7 @@
#include "ivtv-cards.h"
#include "ivtv-vbi.h"
#include "ivtv-routing.h"
+#include "ivtv-controls.h"
#include "ivtv-gpio.h"
#include <media/tveeprom.h>
@@ -734,9 +735,8 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
itv->open_id = 1;
/* Initial settings */
- cx2341x_fill_defaults(&itv->params);
- itv->params.port = CX2341X_PORT_MEMORY;
- itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+ itv->cxhdl.port = CX2341X_PORT_MEMORY;
+ itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
init_waitqueue_head(&itv->eos_waitq);
init_waitqueue_head(&itv->event_waitq);
init_waitqueue_head(&itv->vsync_waitq);
@@ -1006,6 +1006,13 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
retval = -ENOMEM;
goto err;
}
+ retval = cx2341x_handler_init(&itv->cxhdl, 50);
+ if (retval)
+ goto err;
+ itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
+ itv->cxhdl.ops = &ivtv_cxhdl_ops;
+ itv->cxhdl.priv = itv;
+ itv->cxhdl.func = ivtv_api_func;
IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
@@ -1127,7 +1134,7 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
- itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
@@ -1269,15 +1276,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
IVTV_DEBUG_INFO("Getting firmware version..\n");
ivtv_firmware_versions(itv);
- if (itv->card->hw_all & IVTV_HW_CX25840) {
- struct v4l2_control ctrl;
-
+ if (itv->card->hw_all & IVTV_HW_CX25840)
v4l2_subdev_call(itv->sd_video, core, load_fw);
- /* CX25840_CID_ENABLE_PVR150_WORKAROUND */
- ctrl.id = V4L2_CID_PRIVATE_BASE;
- ctrl.value = itv->pvr150_workaround;
- v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
- }
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
@@ -1329,6 +1329,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
/* For cards with video out, this call needs interrupts enabled */
ivtv_s_std(NULL, &fh, &itv->tuner_std);
+ /* Setup initial controls */
+ cx2341x_handler_setup(&itv->cxhdl);
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 102071246..7580314 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -62,6 +62,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
@@ -631,6 +632,8 @@ struct ivtv {
struct ivtv_options options; /* user options */
struct v4l2_device v4l2_dev;
+ struct cx2341x_handler cxhdl;
+ struct v4l2_ctrl_handler hdl_gpio;
struct v4l2_subdev sd_gpio; /* GPIO sub-device */
u16 instance;
@@ -648,7 +651,6 @@ struct ivtv {
v4l2_std_id std_out; /* current TV output standard */
u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
- struct cx2341x_mpeg_params params; /* current encoder parameters */
/* Locking */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index a6a2cdb..d727485 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -150,12 +150,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
static void ivtv_dualwatch(struct ivtv *itv)
{
struct v4l2_tuner vt;
- u32 new_bitmap;
u32 new_stereo_mode;
- const u32 stereo_mask = 0x0300;
- const u32 dual = 0x0200;
+ const u32 dual = 0x02;
- new_stereo_mode = itv->params.audio_properties & stereo_mask;
+ new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
memset(&vt, 0, sizeof(vt));
ivtv_call_all(itv, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
@@ -164,16 +162,10 @@ static void ivtv_dualwatch(struct ivtv *itv)
if (new_stereo_mode == itv->dualwatch_stereo_mode)
return;
- new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
-
- IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
- itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
-
- if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
- itv->dualwatch_stereo_mode = new_stereo_mode;
- return;
- }
- IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+ IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+ itv->dualwatch_stereo_mode, new_stereo_mode);
+ if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
+ IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}
static void ivtv_update_pgm_info(struct ivtv *itv)
@@ -894,7 +886,8 @@ int ivtv_v4l2_close(struct file *filp)
if (atomic_read(&itv->capturing) > 0) {
/* Undo video mute */
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
- itv->params.video_mute | (itv->params.video_mute_yuv << 8));
+ v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
index d8bf2b0..4df0194 100644
--- a/drivers/media/video/ivtv/ivtv-firmware.c
+++ b/drivers/media/video/ivtv/ivtv-firmware.c
@@ -248,9 +248,9 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
volatile u8 __iomem *mem_offset;
data[0] = 0;
- data[1] = itv->params.width; /* YUV source width */
- data[2] = itv->params.height;
- data[3] = itv->params.audio_properties; /* Audio settings to use,
+ data[1] = itv->cxhdl.width; /* YUV source width */
+ data[2] = itv->cxhdl.height;
+ data[3] = itv->cxhdl.audio_properties; /* Audio settings to use,
bitmap. see docs. */
if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index aede061..8f0d077 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -24,6 +24,7 @@
#include "ivtv-gpio.h"
#include "tuner-xc2028.h"
#include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
/*
* GPIO assignment of Yuan MPG600/MPG160
@@ -149,16 +150,10 @@ static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
return container_of(sd, struct ivtv, sd_gpio);
}
-static struct v4l2_queryctrl gpio_ctrl_mute = {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
-};
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
+}
static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
@@ -262,40 +257,24 @@ static int subdev_s_audio_routing(struct v4l2_subdev *sd,
return 0;
}
-static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = itv->card->gpio_audio_mute.mute;
- ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
- return 0;
-}
-
-static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ivtv *itv = sd_to_ivtv(sd);
- u16 mask, data;
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
- if (mask)
- write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
+ (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+ }
+ return -EINVAL;
}
-static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- if (qc->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- *qc = gpio_ctrl_mute;
- return 0;
-}
static int subdev_log_status(struct v4l2_subdev *sd)
{
@@ -304,6 +283,7 @@ static int subdev_log_status(struct v4l2_subdev *sd)
IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
+ v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
return 0;
}
@@ -327,11 +307,19 @@ static int subdev_s_video_routing(struct v4l2_subdev *sd,
return 0;
}
+static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
+ .s_ctrl = subdev_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops subdev_core_ops = {
.log_status = subdev_log_status,
- .g_ctrl = subdev_g_ctrl,
- .s_ctrl = subdev_s_ctrl,
- .queryctrl = subdev_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
@@ -375,5 +363,12 @@ int ivtv_gpio_init(struct ivtv *itv)
v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
+ v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ if (itv->hdl_gpio.error)
+ return itv->hdl_gpio.error;
+ itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
+ v4l2_ctrl_handler_setup(&itv->hdl_gpio);
return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
}
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index a5b92d1..a74fa09 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -63,6 +63,7 @@
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
#include "ivtv-i2c.h"
+#include <media/cx25840.h>
/* i2c implementation for cx23415/6 chip, ivtv project.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
@@ -182,8 +183,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
return -1;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, type, I2C_NAME_SIZE);
- return i2c_new_probed_device(adap, &info, addr_list) == NULL
- ? -1 : 0;
+ return i2c_new_probed_device(adap, &info, addr_list, NULL)
+ == NULL ? -1 : 0;
}
/* Only allow one IR receiver to be registered per board */
@@ -220,7 +221,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
info.platform_data = init_data;
strlcpy(info.type, type, I2C_NAME_SIZE);
- return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0;
+ return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
+ -1 : 0;
}
/* Instantiate the IR receiver device using probing -- undesirable */
@@ -248,7 +250,7 @@ struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv)
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
- return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list);
+ return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list, NULL);
}
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
@@ -292,6 +294,12 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
+ } else if (hw == IVTV_HW_CX25840) {
+ struct cx25840_platform_data pdata;
+
+ pdata.pvr150_workaround = itv->pvr150_workaround;
+ sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev,
+ adap, mod, type, 0, &pdata, hw_addrs[idx], NULL);
} else {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, hw_addrs[idx], NULL);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 11ac2fa..4eed912 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -162,7 +162,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
data[1] = (speed < 0);
data[2] = speed < 0 ? 3 : 7;
- data[3] = itv->params.video_b_frames;
+ data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
data[5] = 0;
data[6] = 0;
@@ -339,8 +339,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
struct ivtv *itv = id->itv;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- pixfmt->width = itv->params.width;
- pixfmt->height = itv->params.height;
+ pixfmt->width = itv->cxhdl.width;
+ pixfmt->height = itv->cxhdl.height;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
pixfmt->field = V4L2_FIELD_INTERLACED;
pixfmt->priv = 0;
@@ -568,7 +568,6 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
{
struct ivtv_open_id *id = fh;
struct ivtv *itv = id->itv;
- struct cx2341x_mpeg_params *p = &itv->params;
struct v4l2_mbus_framefmt mbus_fmt;
int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
int w = fmt->fmt.pix.width;
@@ -577,15 +576,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
if (ret)
return ret;
- if (p->width == w && p->height == h)
+ if (itv->cxhdl.width == w && itv->cxhdl.height == h)
return 0;
if (atomic_read(&itv->capturing) > 0)
return -EBUSY;
- p->width = w;
- p->height = h;
- if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ itv->cxhdl.width = w;
+ itv->cxhdl.height = h;
+ if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
fmt->fmt.pix.width /= 2;
mbus_fmt.width = fmt->fmt.pix.width;
mbus_fmt.height = h;
@@ -1114,9 +1113,10 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
itv->std = *std;
itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
- itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
- itv->params.width = 720;
- itv->params.height = itv->is_50hz ? 576 : 480;
+ itv->is_50hz = !itv->is_60hz;
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+ itv->cxhdl.width = 720;
+ itv->cxhdl.height = itv->is_50hz ? 576 : 480;
itv->vbi.count = itv->is_50hz ? 18 : 12;
itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
@@ -1157,7 +1157,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
itv->main_rect.left = itv->main_rect.top = 0;
itv->main_rect.width = 720;
- itv->main_rect.height = itv->params.height;
+ itv->main_rect.height = itv->cxhdl.height;
ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
720, itv->main_rect.height, 0, 0);
yi->main_rect = itv->main_rect;
@@ -1554,7 +1554,7 @@ static int ivtv_log_status(struct file *file, void *fh)
}
IVTV_INFO("Tuner: %s\n",
test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
- cx2341x_log_status(&itv->params, itv->v4l2_dev.name);
+ v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
struct ivtv_stream *s = &itv->streams[i];
@@ -1942,11 +1942,6 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
.vidioc_s_register = ivtv_s_register,
#endif
.vidioc_default = ivtv_default,
- .vidioc_queryctrl = ivtv_queryctrl,
- .vidioc_querymenu = ivtv_querymenu,
- .vidioc_g_ext_ctrls = ivtv_g_ext_ctrls,
- .vidioc_s_ext_ctrls = ivtv_s_ext_ctrls,
- .vidioc_try_ext_ctrls = ivtv_try_ext_ctrls,
.vidioc_subscribe_event = ivtv_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 55df419..512607e 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -210,6 +210,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
s->vdev->num = num;
s->vdev->v4l2_dev = &itv->v4l2_dev;
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->fops = ivtv_stream_info[type].fops;
s->vdev->release = video_device_release;
s->vdev->tvnorms = V4L2_STD_ALL;
@@ -451,7 +452,6 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
- struct cx2341x_mpeg_params *p = &itv->params;
int captype = 0, subtype = 0;
int enable_passthrough = 0;
@@ -472,7 +472,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
}
itv->mpg_data_received = itv->vbi_data_inserted = 0;
itv->dualwatch_jiffies = jiffies;
- itv->dualwatch_stereo_mode = p->audio_properties & 0x0300;
+ itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
itv->search_pack_header = 0;
break;
@@ -560,12 +560,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
itv->pgm_info_offset, itv->pgm_info_num);
/* Setup API for Stream */
- cx2341x_update(itv, ivtv_api_func, NULL, p);
+ cx2341x_handler_setup(&itv->cxhdl);
/* mute if capturing radio */
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
- 1 | (p->video_mute_yuv << 8));
+ 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Vsync Setup */
@@ -581,6 +581,8 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
/* Initialize Digitizer for Capture */
/* Avoid tinny audio problem - ensure audio clocks are going */
v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
@@ -617,7 +619,6 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
- struct cx2341x_mpeg_params *p = &itv->params;
int datatype;
u16 width;
u16 height;
@@ -627,8 +628,8 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
- width = p->width;
- height = p->height;
+ width = itv->cxhdl.width;
+ height = itv->cxhdl.height;
/* set audio mode to left/stereo for dual/stereo mode. */
ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
@@ -668,7 +669,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
break;
}
if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
- width, height, p->audio_properties)) {
+ width, height, itv->cxhdl.audio_properties)) {
IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
}
@@ -847,6 +848,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
return 0;
}
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
/* Set the following Interrupt mask bits for capture */
ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
del_timer(&itv->dma_timer);
@@ -967,7 +970,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
/* Setup capture if not already done */
if (atomic_read(&itv->capturing) == 0) {
- cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+ cx2341x_handler_setup(&itv->cxhdl);
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
}
/* Start Passthrough Mode */
@@ -988,6 +992,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
itv->output_mode = OUT_NONE;
+ if (atomic_read(&itv->capturing) == 0)
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
return 0;
}
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index e9df3cb..0e41213 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
msp_write_dem(client, 0x40, state->i2s_mode);
}
-void msp_set_audio(struct i2c_client *client)
-{
- struct msp_state *state = to_state(i2c_get_clientdata(client));
- int bal = 0, bass, treble, loudness;
- int val = 0;
- int reallymuted = state->muted | state->scan_in_progress;
-
- if (!reallymuted)
- val = (state->volume * 0x7f / 65535) << 8;
-
- v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
- state->muted ? "on" : "off",
- state->scan_in_progress ? "yes" : "no",
- state->volume);
-
- msp_write_dsp(client, 0x0000, val);
- msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
- if (state->has_scart2_out_volume)
- msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
- if (state->has_headphones)
- msp_write_dsp(client, 0x0006, val);
- if (!state->has_sound_processing)
- return;
-
- if (val)
- bal = (u8)((state->balance / 256) - 128);
- bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
- treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
- loudness = state->loudness ? ((5 * 4) << 8) : 0;
-
- v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n",
- state->balance, state->bass, state->treble, state->loudness);
-
- msp_write_dsp(client, 0x0001, bal << 8);
- msp_write_dsp(client, 0x0002, bass);
- msp_write_dsp(client, 0x0003, treble);
- msp_write_dsp(client, 0x0004, loudness);
- if (!state->has_headphones)
- return;
- msp_write_dsp(client, 0x0030, bal << 8);
- msp_write_dsp(client, 0x0031, bass);
- msp_write_dsp(client, 0x0032, treble);
- msp_write_dsp(client, 0x0033, loudness);
-}
-
/* ------------------------------------------------------------------------ */
static void msp_wake_thread(struct i2c_client *client)
@@ -363,98 +318,73 @@ int msp_sleep(struct msp_state *state, int timeout)
/* ------------------------------------------------------------------------ */
-static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct msp_state *state = to_state(sd);
+ struct msp_state *state = ctrl_to_state(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+ int val = ctrl->val;
switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->volume;
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->muted;
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->balance;
- break;
-
- case V4L2_CID_AUDIO_BASS:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->bass;
+ case V4L2_CID_AUDIO_VOLUME: {
+ /* audio volume cluster */
+ int reallymuted = state->muted->val | state->scan_in_progress;
+
+ if (!reallymuted)
+ val = (val * 0x7f / 65535) << 8;
+
+ v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
+ state->muted->val ? "on" : "off",
+ state->scan_in_progress ? "yes" : "no",
+ state->volume->val);
+
+ msp_write_dsp(client, 0x0000, val);
+ msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_scart2_out_volume)
+ msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0006, val);
break;
-
- case V4L2_CID_AUDIO_TREBLE:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->treble;
- break;
-
- case V4L2_CID_AUDIO_LOUDNESS:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->loudness;
- break;
-
- default:
- return -EINVAL;
}
- return 0;
-}
-
-static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- state->volume = ctrl->value;
- if (state->volume == 0)
- state->balance = 32768;
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value < 0 || ctrl->value >= 2)
- return -ERANGE;
- state->muted = ctrl->value;
- break;
case V4L2_CID_AUDIO_BASS:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->bass = ctrl->value;
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0002, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0031, val);
break;
case V4L2_CID_AUDIO_TREBLE:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->treble = ctrl->value;
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0003, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0032, val);
break;
case V4L2_CID_AUDIO_LOUDNESS:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->loudness = ctrl->value;
+ val = val ? ((5 * 4) << 8) : 0;
+ msp_write_dsp(client, 0x0004, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0033, val);
break;
case V4L2_CID_AUDIO_BALANCE:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->balance = ctrl->value;
+ val = (u8)((val / 256) - 128);
+ msp_write_dsp(client, 0x0001, val << 8);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0030, val << 8);
break;
default:
return -EINVAL;
}
- msp_set_audio(client);
return 0;
}
+void msp_update_volume(struct msp_state *state)
+{
+ v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume));
+}
+
/* --- v4l2 ioctls --- */
static int msp_s_radio(struct v4l2_subdev *sd)
{
@@ -472,7 +402,7 @@ static int msp_s_radio(struct v4l2_subdev *sd)
msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
msp3400c_set_carrier(client, MSP_CARRIER(10.7),
MSP_CARRIER(10.7));
- msp_set_audio(client);
+ msp_update_volume(state);
break;
case OPMODE_AUTODETECT:
case OPMODE_AUTOSELECT:
@@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return 0;
}
-static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct msp_state *state = to_state(sd);
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- default:
- break;
- }
- if (!state->has_sound_processing)
- return -EINVAL;
- switch (qc->id) {
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct msp_state *state = to_state(sd);
@@ -633,19 +536,14 @@ static int msp_log_status(struct v4l2_subdev *sd)
struct msp_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
const char *p;
+ char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
if (state->opmode == OPMODE_AUTOSELECT)
msp_detect_stereo(client);
v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
client->name, state->rev1, state->rev2);
- v4l_info(client, "Audio: volume %d%s\n",
- state->volume, state->muted ? " (muted)" : "");
- if (state->has_sound_processing) {
- v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
- state->balance, state->bass,
- state->treble,
- state->loudness ? "on" : "off");
- }
+ snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name);
+ v4l2_ctrl_handler_log_status(&state->hdl, prefix);
switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
@@ -695,12 +593,20 @@ static int msp_resume(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops msp_ctrl_ops = {
+ .s_ctrl = msp_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops msp_core_ops = {
.log_status = msp_log_status,
.g_chip_ident = msp_g_chip_ident,
- .g_ctrl = msp_g_ctrl,
- .s_ctrl = msp_s_ctrl,
- .queryctrl = msp_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = msp_s_std,
};
@@ -728,6 +634,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct msp_state *state;
struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
int (*thread_func)(void *data) = NULL;
int msp_hard;
int msp_family;
@@ -752,13 +659,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
state->v4l2_std = V4L2_STD_NTSC;
state->audmode = V4L2_TUNER_MODE_STEREO;
- state->volume = 58880; /* 0db gain */
- state->balance = 32768; /* 0db gain */
- state->bass = 32768;
- state->treble = 32768;
- state->loudness = 0;
state->input = -1;
- state->muted = 0;
state->i2s_mode = 0;
init_waitqueue_head(&state->wq);
/* These are the reset input/output positions */
@@ -777,8 +678,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENODEV;
}
- msp_set_audio(client);
-
msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
msp_product = (state->rev2 >> 8) & 0xff;
msp_prod_hi = msp_product / 10;
@@ -849,6 +748,34 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
state->opmode = OPMODE_MANUAL;
}
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ if (state->has_sound_processing) {
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
+ }
+ state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(hdl);
+
/* hello world :-) */
v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
msp_family, msp_product,
@@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client)
}
msp_reset(client);
+ v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return 0;
}
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index d6b3e6d..32a478e 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -6,6 +6,7 @@
#include <media/msp3400.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
/* ---------------------------------------------------------------------- */
@@ -51,6 +52,7 @@ extern int msp_stereo_thresh;
struct msp_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
int rev1, rev2;
int ident;
u8 has_nicam;
@@ -87,9 +89,12 @@ struct msp_state {
int audmode;
int rxsubchans;
- int volume, muted;
- int balance, loudness;
- int bass, treble;
+ struct {
+ /* volume cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *muted;
+ };
+
int scan_in_progress;
/* thread */
@@ -104,6 +109,11 @@ static inline struct msp_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct msp_state, sd);
}
+static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct msp_state, hdl);
+}
+
/* msp3400-driver.c */
int msp_write_dem(struct i2c_client *client, int addr, int val);
int msp_write_dsp(struct i2c_client *client, int addr, int val);
@@ -111,7 +121,7 @@ int msp_read_dem(struct i2c_client *client, int addr);
int msp_read_dsp(struct i2c_client *client, int addr);
int msp_reset(struct i2c_client *client);
void msp_set_scart(struct i2c_client *client, int in, int out);
-void msp_set_audio(struct i2c_client *client);
+void msp_update_volume(struct msp_state *state);
int msp_sleep(struct msp_state *state, int timeout);
/* msp3400-kthreads.c */
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index d5a69c5..b376fcd 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -496,13 +496,13 @@ restart:
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
continue;
}
/* mute audio */
state->scan_in_progress = 1;
- msp_set_audio(client);
+ msp_update_volume(state);
msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
val1 = val2 = 0;
@@ -634,7 +634,7 @@ no_second:
/* unmute */
state->scan_in_progress = 0;
msp3400c_set_audmode(client);
- msp_set_audio(client);
+ msp_update_volume(state);
if (msp_debug)
msp3400c_print_mode(client);
@@ -679,13 +679,13 @@ restart:
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
continue;
}
/* mute audio */
state->scan_in_progress = 1;
- msp_set_audio(client);
+ msp_update_volume(state);
/* start autodetect. Note: autodetect is not supported for
NTSC-M and radio, hence we force the standard in those
@@ -797,7 +797,7 @@ restart:
/* unmute */
msp3400c_set_audmode(client);
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
/* monitor tv audio mode, the first time don't wait
so long to get a quick stereo/bilingual result */
@@ -974,7 +974,7 @@ restart:
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
continue;
}
@@ -1020,7 +1020,7 @@ unmute:
}
/* unmute: dispatch sound to scart output, set scart volume */
- msp_set_audio(client);
+ msp_update_volume(state);
/* restore ACB */
if (msp_write_dsp(client, 0x13, state->acb))
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index 31cc3d0..758a4db 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -1,5 +1,5 @@
/*
- * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron
+ * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
*
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
*
@@ -19,11 +19,14 @@
#include <media/soc_camera.h>
/*
- * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin)
+ * MT9M111, MT9M112 and MT9M131:
+ * i2c address is 0x48 or 0x5d (depending on SADDR pin)
* The platform has to define i2c_board_info and call i2c_register_board_info()
*/
-/* mt9m111: Sensor register addresses */
+/*
+ * Sensor core register addresses (0x000..0x0ff)
+ */
#define MT9M111_CHIP_VERSION 0x000
#define MT9M111_ROW_START 0x001
#define MT9M111_COLUMN_START 0x002
@@ -72,8 +75,9 @@
#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
+
/*
- * mt9m111: Colorpipe register addresses (0x100..0x1ff)
+ * Colorpipe register addresses (0x100..0x1ff)
*/
#define MT9M111_OPER_MODE_CTRL 0x106
#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
@@ -109,8 +113,9 @@
#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1)
#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1)
#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0)
+
/*
- * mt9m111: Camera control register addresses (0x200..0x2ff not implemented)
+ * Camera control register addresses (0x200..0x2ff not implemented)
*/
#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
@@ -160,7 +165,8 @@ enum mt9m111_context {
struct mt9m111 {
struct v4l2_subdev subdev;
- int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
+ int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
+ * from v4l2-chip-ident.h */
enum mt9m111_context context;
struct v4l2_rect rect;
const struct mt9m111_datafmt *fmt;
@@ -934,7 +940,7 @@ static int mt9m111_init(struct i2c_client *client)
if (!ret)
ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
if (ret)
- dev_err(&client->dev, "mt9m11x init failed: %d\n", ret);
+ dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
return ret;
}
@@ -963,27 +969,27 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
mt9m111->swap_rgb_even_odd = 1;
mt9m111->swap_rgb_red_blue = 1;
- ret = mt9m111_init(client);
- if (ret)
- goto ei2c;
-
data = reg_read(CHIP_VERSION);
switch (data) {
- case 0x143a: /* MT9M111 */
+ case 0x143a: /* MT9M111 or MT9M131 */
mt9m111->model = V4L2_IDENT_MT9M111;
+ dev_info(&client->dev,
+ "Detected a MT9M111/MT9M131 chip ID %x\n", data);
break;
case 0x148c: /* MT9M112 */
mt9m111->model = V4L2_IDENT_MT9M112;
+ dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
break;
default:
ret = -ENODEV;
dev_err(&client->dev,
- "No MT9M11x chip detected, register read %x\n", data);
+ "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
+ data);
goto ei2c;
}
- dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
+ ret = mt9m111_init(client);
ei2c:
return ret;
@@ -1034,13 +1040,13 @@ static int mt9m111_probe(struct i2c_client *client,
int ret;
if (!icd) {
- dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n");
+ dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
return -EINVAL;
}
icl = to_soc_camera_link(icd);
if (!icl) {
- dev_err(&client->dev, "MT9M11x driver needs platform data\n");
+ dev_err(&client->dev, "mt9m111: driver needs platform data\n");
return -EINVAL;
}
@@ -1114,6 +1120,6 @@ static void __exit mt9m111_mod_exit(void)
module_init(mt9m111_mod_init);
module_exit(mt9m111_mod_exit);
-MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver");
+MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
MODULE_AUTHOR("Robert Jarzmik");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 026bef0..66ff174 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -785,6 +785,8 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
if (ret < 0)
return ret;
+ if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
csicr1 |= CSICR1_INV_PCLK;
if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
@@ -1201,7 +1203,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
buf = list_entry(pcdev->capture.next,
struct mx2_buffer, vb.queue);
- buf->bufnum = bufnum;
+ buf->bufnum = !bufnum;
list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
index e9b11e1..4279ebb 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -94,8 +94,6 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
u32 *num_ptr)
{
u32 result = 0;
- u32 val;
- int ch;
int radix = 10;
if ((count >= 2) && (buf[0] == '0') &&
((buf[1] == 'x') || (buf[1] == 'X'))) {
@@ -107,17 +105,9 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
}
while (count--) {
- ch = *buf++;
- if ((ch >= '0') && (ch <= '9')) {
- val = ch - '0';
- } else if ((ch >= 'a') && (ch <= 'f')) {
- val = ch - 'a' + 10;
- } else if ((ch >= 'A') && (ch <= 'F')) {
- val = ch - 'A' + 10;
- } else {
+ int val = hex_to_bin(*buf++);
+ if (val < 0 || val >= radix)
return -EINVAL;
- }
- if (val >= radix) return -EINVAL;
result *= radix;
result += val;
}
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
new file mode 100644
index 0000000..0d9d541
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o
+s5p-fimc-y := fimc-core.o fimc-reg.o
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
new file mode 100644
index 0000000..b151c7b
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -0,0 +1,1586 @@
+/*
+ * S5P camera interface (video postprocessor) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#include "fimc-core.h"
+
+static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" };
+
+static struct fimc_fmt fimc_formats[] = {
+ {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .color = S5P_FIMC_RGB565,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "BGR666",
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .depth = 32,
+ .color = S5P_FIMC_RGB666,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "XRGB-8-8-8-8, 24 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 32,
+ .color = S5P_FIMC_RGB888,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .color = S5P_FIMC_YCBYCR422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .color = S5P_FIMC_CBYCRY422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ .color = S5P_FIMC_CRYCBY422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+ .color = S5P_FIMC_YCRYCB422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 12,
+ .color = S5P_FIMC_YCBCR422,
+ .buff_cnt = 1,
+ .planes_cnt = 3
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = 16,
+ .color = S5P_FIMC_YCBCR422,
+ .buff_cnt = 1,
+ .planes_cnt = 2
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .depth = 16,
+ .color = S5P_FIMC_RGB565,
+ .buff_cnt = 1,
+ .planes_cnt = 2
+ }, {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .color = S5P_FIMC_YCBCR420,
+ .buff_cnt = 1,
+ .planes_cnt = 3
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = 12,
+ .color = S5P_FIMC_YCBCR420,
+ .buff_cnt = 1,
+ .planes_cnt = 2
+ }
+ };
+
+static struct v4l2_queryctrl fimc_ctrls[] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_ROTATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rotation (CCW)",
+ .minimum = 0,
+ .maximum = 270,
+ .step = 90,
+ .default_value = 0,
+ },
+};
+
+
+static struct v4l2_queryctrl *get_ctrl(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i)
+ if (id == fimc_ctrls[i].id)
+ return &fimc_ctrls[i];
+ return NULL;
+}
+
+static int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f)
+{
+ if (r->width > f->width) {
+ if (f->width > (r->width * SCALER_MAX_HRATIO))
+ return -EINVAL;
+ } else {
+ if ((f->width * SCALER_MAX_HRATIO) < r->width)
+ return -EINVAL;
+ }
+
+ if (r->height > f->height) {
+ if (f->height > (r->height * SCALER_MAX_VRATIO))
+ return -EINVAL;
+ } else {
+ if ((f->height * SCALER_MAX_VRATIO) < r->height)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
+{
+ if (src >= tar * 64) {
+ return -EINVAL;
+ } else if (src >= tar * 32) {
+ *ratio = 32;
+ *shift = 5;
+ } else if (src >= tar * 16) {
+ *ratio = 16;
+ *shift = 4;
+ } else if (src >= tar * 8) {
+ *ratio = 8;
+ *shift = 3;
+ } else if (src >= tar * 4) {
+ *ratio = 4;
+ *shift = 2;
+ } else if (src >= tar * 2) {
+ *ratio = 2;
+ *shift = 1;
+ } else {
+ *ratio = 1;
+ *shift = 0;
+ }
+
+ return 0;
+}
+
+static int fimc_set_scaler_info(struct fimc_ctx *ctx)
+{
+ struct fimc_scaler *sc = &ctx->scaler;
+ struct fimc_frame *s_frame = &ctx->s_frame;
+ struct fimc_frame *d_frame = &ctx->d_frame;
+ int tx, ty, sx, sy;
+ int ret;
+
+ tx = d_frame->width;
+ ty = d_frame->height;
+ if (tx <= 0 || ty <= 0) {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "invalid target size: %d x %d", tx, ty);
+ return -EINVAL;
+ }
+
+ sx = s_frame->width;
+ sy = s_frame->height;
+ if (sx <= 0 || sy <= 0) {
+ err("invalid source size: %d x %d", sx, sy);
+ return -EINVAL;
+ }
+
+ sc->real_width = sx;
+ sc->real_height = sy;
+ dbg("sx= %d, sy= %d, tx= %d, ty= %d", sx, sy, tx, ty);
+
+ ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor);
+ if (ret)
+ return ret;
+
+ ret = fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor);
+ if (ret)
+ return ret;
+
+ sc->pre_dst_width = sx / sc->pre_hratio;
+ sc->pre_dst_height = sy / sc->pre_vratio;
+
+ sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+
+ sc->scaleup_h = (tx >= sx) ? 1 : 0;
+ sc->scaleup_v = (ty >= sy) ? 1 : 0;
+
+ /* check to see if input and output size/format differ */
+ if (s_frame->fmt->color == d_frame->fmt->color
+ && s_frame->width == d_frame->width
+ && s_frame->height == d_frame->height)
+ sc->copy_mode = 1;
+ else
+ sc->copy_mode = 0;
+
+ return 0;
+}
+
+
+static irqreturn_t fimc_isr(int irq, void *priv)
+{
+ struct fimc_vid_buffer *src_buf, *dst_buf;
+ struct fimc_dev *fimc = (struct fimc_dev *)priv;
+ struct fimc_ctx *ctx;
+
+ BUG_ON(!fimc);
+ fimc_hw_clear_irq(fimc);
+
+ spin_lock(&fimc->slock);
+
+ if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+ if (!ctx || !ctx->m2m_ctx)
+ goto isr_unlock;
+ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ if (src_buf && dst_buf) {
+ spin_lock(&fimc->irqlock);
+ src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
+ wake_up(&src_buf->vb.done);
+ wake_up(&dst_buf->vb.done);
+ spin_unlock(&fimc->irqlock);
+ v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+ }
+ }
+
+isr_unlock:
+ spin_unlock(&fimc->slock);
+ return IRQ_HANDLED;
+}
+
+/* The color format (planes_cnt, buff_cnt) must be already configured. */
+static int fimc_prepare_addr(struct fimc_ctx *ctx,
+ struct fimc_vid_buffer *buf, enum v4l2_buf_type type)
+{
+ struct fimc_frame *frame;
+ struct fimc_addr *paddr;
+ u32 pix_size;
+ int ret = 0;
+
+ frame = ctx_m2m_get_frame(ctx, type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+ paddr = &frame->paddr;
+
+ if (!buf)
+ return -EINVAL;
+
+ pix_size = frame->width * frame->height;
+
+ dbg("buff_cnt= %d, planes_cnt= %d, frame->size= %d, pix_size= %d",
+ frame->fmt->buff_cnt, frame->fmt->planes_cnt,
+ frame->size, pix_size);
+
+ if (frame->fmt->buff_cnt == 1) {
+ paddr->y = videobuf_to_dma_contig(&buf->vb);
+ switch (frame->fmt->planes_cnt) {
+ case 1:
+ paddr->cb = 0;
+ paddr->cr = 0;
+ break;
+ case 2:
+ /* decompose Y into Y/Cb */
+ paddr->cb = (u32)(paddr->y + pix_size);
+ paddr->cr = 0;
+ break;
+ case 3:
+ paddr->cb = (u32)(paddr->y + pix_size);
+ /* decompose Y into Y/Cb/Cr */
+ if (S5P_FIMC_YCBCR420 == frame->fmt->color)
+ paddr->cr = (u32)(paddr->cb
+ + (pix_size >> 2));
+ else /* 422 */
+ paddr->cr = (u32)(paddr->cb
+ + (pix_size >> 1));
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ dbg("PHYS_ADDR: type= %d, y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
+ type, paddr->y, paddr->cb, paddr->cr, ret);
+
+ return ret;
+}
+
+/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
+static void fimc_set_yuv_order(struct fimc_ctx *ctx)
+{
+ /* The one only mode supported in SoC. */
+ ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
+ ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
+
+ /* Set order for 1 plane input formats. */
+ switch (ctx->s_frame.fmt->color) {
+ case S5P_FIMC_YCRYCB422:
+ ctx->in_order_1p = S5P_FIMC_IN_YCRYCB;
+ break;
+ case S5P_FIMC_CBYCRY422:
+ ctx->in_order_1p = S5P_FIMC_IN_CBYCRY;
+ break;
+ case S5P_FIMC_CRYCBY422:
+ ctx->in_order_1p = S5P_FIMC_IN_CRYCBY;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ default:
+ ctx->in_order_1p = S5P_FIMC_IN_YCBYCR;
+ break;
+ }
+ dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
+
+ switch (ctx->d_frame.fmt->color) {
+ case S5P_FIMC_YCRYCB422:
+ ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB;
+ break;
+ case S5P_FIMC_CBYCRY422:
+ ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY;
+ break;
+ case S5P_FIMC_CRYCBY422:
+ ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ default:
+ ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR;
+ break;
+ }
+ dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
+}
+
+/**
+ * fimc_prepare_config - check dimensions, operation and color mode
+ * and pre-calculate offset and the scaling coefficients.
+ *
+ * @ctx: hardware context information
+ * @flags: flags indicating which parameters to check/update
+ *
+ * Return: 0 if dimensions are valid or non zero otherwise.
+ */
+static int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
+{
+ struct fimc_frame *s_frame, *d_frame;
+ struct fimc_vid_buffer *buf = NULL;
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ int ret = 0;
+
+ s_frame = &ctx->s_frame;
+ d_frame = &ctx->d_frame;
+
+ if (flags & FIMC_PARAMS) {
+ if ((ctx->out_path == FIMC_DMA) &&
+ (ctx->rotation == 90 || ctx->rotation == 270)) {
+ swap(d_frame->f_width, d_frame->f_height);
+ swap(d_frame->width, d_frame->height);
+ }
+
+ /* Prepare the output offset ratios for scaler. */
+ d_frame->dma_offset.y_h = d_frame->offs_h;
+ if (!variant->pix_hoff)
+ d_frame->dma_offset.y_h *= (d_frame->fmt->depth >> 3);
+
+ d_frame->dma_offset.y_v = d_frame->offs_v;
+
+ d_frame->dma_offset.cb_h = d_frame->offs_h;
+ d_frame->dma_offset.cb_v = d_frame->offs_v;
+
+ d_frame->dma_offset.cr_h = d_frame->offs_h;
+ d_frame->dma_offset.cr_v = d_frame->offs_v;
+
+ if (!variant->pix_hoff && d_frame->fmt->planes_cnt == 3) {
+ d_frame->dma_offset.cb_h >>= 1;
+ d_frame->dma_offset.cb_v >>= 1;
+ d_frame->dma_offset.cr_h >>= 1;
+ d_frame->dma_offset.cr_v >>= 1;
+ }
+
+ dbg("out offset: color= %d, y_h= %d, y_v= %d",
+ d_frame->fmt->color,
+ d_frame->dma_offset.y_h, d_frame->dma_offset.y_v);
+
+ /* Prepare the input offset ratios for scaler. */
+ s_frame->dma_offset.y_h = s_frame->offs_h;
+ if (!variant->pix_hoff)
+ s_frame->dma_offset.y_h *= (s_frame->fmt->depth >> 3);
+ s_frame->dma_offset.y_v = s_frame->offs_v;
+
+ s_frame->dma_offset.cb_h = s_frame->offs_h;
+ s_frame->dma_offset.cb_v = s_frame->offs_v;
+
+ s_frame->dma_offset.cr_h = s_frame->offs_h;
+ s_frame->dma_offset.cr_v = s_frame->offs_v;
+
+ if (!variant->pix_hoff && s_frame->fmt->planes_cnt == 3) {
+ s_frame->dma_offset.cb_h >>= 1;
+ s_frame->dma_offset.cb_v >>= 1;
+ s_frame->dma_offset.cr_h >>= 1;
+ s_frame->dma_offset.cr_v >>= 1;
+ }
+
+ dbg("in offset: color= %d, y_h= %d, y_v= %d",
+ s_frame->fmt->color, s_frame->dma_offset.y_h,
+ s_frame->dma_offset.y_v);
+
+ fimc_set_yuv_order(ctx);
+
+ /* Check against the scaler ratio. */
+ if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) ||
+ s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) {
+ err("out of scaler range");
+ return -EINVAL;
+ }
+ }
+
+ /* Input DMA mode is not allowed when the scaler is disabled. */
+ ctx->scaler.enabled = 1;
+
+ if (flags & FIMC_SRC_ADDR) {
+ buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, buf,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (ret)
+ return ret;
+ }
+
+ if (flags & FIMC_DST_ADDR) {
+ buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, buf,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ }
+
+ return ret;
+}
+
+static void fimc_dma_run(void *priv)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+ u32 ret;
+
+ if (WARN(!ctx, "null hardware context"))
+ return;
+
+ fimc = ctx->fimc_dev;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ set_bit(ST_M2M_PEND, &fimc->state);
+
+ ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
+ ret = fimc_prepare_config(ctx, ctx->state);
+ if (ret) {
+ err("general configuration error");
+ goto dma_unlock;
+ }
+
+ if (fimc->m2m.ctx != ctx)
+ ctx->state |= FIMC_PARAMS;
+
+ fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
+
+ if (ctx->state & FIMC_PARAMS) {
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_in_dma(ctx);
+ if (fimc_set_scaler_info(ctx)) {
+ err("scaler configuration error");
+ goto dma_unlock;
+ }
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_scaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx);
+ }
+
+ fimc_hw_set_output_path(ctx);
+ if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
+ fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr);
+
+ if (ctx->state & FIMC_PARAMS)
+ fimc_hw_set_out_dma(ctx);
+
+ if (ctx->scaler.enabled)
+ fimc_hw_start_scaler(fimc);
+ fimc_hw_en_capture(ctx);
+
+ ctx->state = 0;
+ fimc_hw_start_in_dma(fimc);
+
+ fimc->m2m.ctx = ctx;
+
+dma_unlock:
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static void fimc_job_abort(void *priv)
+{
+ /* Nothing done in job_abort. */
+}
+
+static void fimc_buf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct fimc_ctx *ctx = vq->priv_data;
+ struct fimc_frame *frame;
+
+ frame = ctx_m2m_get_frame(ctx, vq->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ *size = (frame->width * frame->height * frame->fmt->depth) >> 3;
+ if (0 == *count)
+ *count = 1;
+ return 0;
+}
+
+static int fimc_buf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct fimc_ctx *ctx = vq->priv_data;
+ struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+ struct fimc_frame *frame;
+ int ret;
+
+ frame = ctx_m2m_get_frame(ctx, vq->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ if (vb->baddr) {
+ if (vb->bsize < frame->size) {
+ v4l2_err(v4l2_dev,
+ "User-provided buffer too small (%d < %d)\n",
+ vb->bsize, frame->size);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ } else if (vb->state != VIDEOBUF_NEEDS_INIT
+ && vb->bsize < frame->size) {
+ return -EINVAL;
+ }
+
+ vb->width = frame->width;
+ vb->height = frame->height;
+ vb->bytesperline = (frame->width * frame->fmt->depth) >> 3;
+ vb->size = frame->size;
+ vb->field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret) {
+ v4l2_err(v4l2_dev, "Iolock failed\n");
+ fimc_buf_release(vq, vb);
+ return ret;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+}
+
+static void fimc_buf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct fimc_ctx *ctx = vq->priv_data;
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
+}
+
+static struct videobuf_queue_ops fimc_qops = {
+ .buf_setup = fimc_buf_setup,
+ .buf_prepare = fimc_buf_prepare,
+ .buf_queue = fimc_buf_queue,
+ .buf_release = fimc_buf_release,
+};
+
+static int fimc_m2m_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+
+ return 0;
+}
+
+static int fimc_m2m_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(fimc_formats))
+ return -EINVAL;
+
+ fmt = &fimc_formats[f->index];
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int fimc_m2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_frame *frame;
+
+ frame = ctx_m2m_get_frame(ctx, f->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ f->fmt.pix.width = frame->width;
+ f->fmt.pix.height = frame->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = frame->fmt->fourcc;
+
+ return 0;
+}
+
+static struct fimc_fmt *find_format(struct v4l2_format *f)
+{
+ struct fimc_fmt *fmt;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
+ fmt = &fimc_formats[i];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+ if (i == ARRAY_SIZE(fimc_formats))
+ return NULL;
+
+ return fmt;
+}
+
+static int fimc_m2m_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_fmt *fmt;
+ u32 max_width, max_height, mod_x, mod_y;
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct samsung_fimc_variant *variant = fimc->variant;
+
+ fmt = find_format(f);
+ if (!fmt) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "Fourcc format (0x%X) invalid.\n", pix->pixelformat);
+ return -EINVAL;
+ }
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != pix->field)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ max_width = variant->scaler_dis_w;
+ max_height = variant->scaler_dis_w;
+ mod_x = variant->min_inp_pixsize;
+ mod_y = variant->min_inp_pixsize;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ max_width = variant->out_rot_dis_w;
+ max_height = variant->out_rot_dis_w;
+ mod_x = variant->min_out_pixsize;
+ mod_y = variant->min_out_pixsize;
+ } else {
+ err("Wrong stream type (%d)", f->type);
+ return -EINVAL;
+ }
+
+ dbg("max_w= %d, max_h= %d", max_width, max_height);
+
+ if (pix->height > max_height)
+ pix->height = max_height;
+ if (pix->width > max_width)
+ pix->width = max_width;
+
+ if (tiled_fmt(fmt)) {
+ mod_x = 64; /* 64x32 tile */
+ mod_y = 32;
+ }
+
+ dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y);
+
+ pix->width = (pix->width == 0) ? mod_x : ALIGN(pix->width, mod_x);
+ pix->height = (pix->height == 0) ? mod_y : ALIGN(pix->height, mod_y);
+
+ if (pix->bytesperline == 0 ||
+ pix->bytesperline * 8 / fmt->depth > pix->width)
+ pix->bytesperline = (pix->width * fmt->depth) >> 3;
+
+ if (pix->sizeimage == 0)
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ dbg("pix->bytesperline= %d, fmt->depth= %d",
+ pix->bytesperline, fmt->depth);
+
+ return 0;
+}
+
+
+static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = priv;
+ struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+ struct videobuf_queue *src_vq, *dst_vq;
+ struct fimc_frame *frame;
+ struct v4l2_pix_format *pix;
+ unsigned long flags;
+ int ret = 0;
+
+ BUG_ON(!ctx);
+
+ ret = fimc_m2m_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ctx->fimc_dev->lock);
+
+ src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
+ dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
+
+ mutex_lock(&src_vq->vb_lock);
+ mutex_lock(&dst_vq->vb_lock);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (videobuf_queue_is_busy(src_vq)) {
+ v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto s_fmt_out;
+ }
+ frame = &ctx->s_frame;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_SRC_FMT;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (videobuf_queue_is_busy(dst_vq)) {
+ v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto s_fmt_out;
+ }
+ frame = &ctx->d_frame;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_DST_FMT;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ } else {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", f->type);
+ return -EINVAL;
+ }
+
+ pix = &f->fmt.pix;
+ frame->fmt = find_format(f);
+ if (!frame->fmt) {
+ ret = -EINVAL;
+ goto s_fmt_out;
+ }
+
+ frame->f_width = pix->bytesperline * 8 / frame->fmt->depth;
+ frame->f_height = pix->sizeimage/pix->bytesperline;
+ frame->width = pix->width;
+ frame->height = pix->height;
+ frame->o_width = pix->width;
+ frame->o_height = pix->height;
+ frame->offs_h = 0;
+ frame->offs_v = 0;
+ frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
+ src_vq->field = dst_vq->field = pix->field;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_PARAMS;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height);
+
+s_fmt_out:
+ mutex_unlock(&dst_vq->vb_lock);
+ mutex_unlock(&src_vq->vb_lock);
+ mutex_unlock(&ctx->fimc_dev->lock);
+ return ret;
+}
+
+static int fimc_m2m_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int fimc_m2m_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+int fimc_m2m_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct v4l2_queryctrl *c;
+ c = get_ctrl(qc->id);
+ if (!c)
+ return -EINVAL;
+ *qc = *c;
+ return 0;
+}
+
+int fimc_m2m_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct fimc_ctx *ctx = priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
+ break;
+ case V4L2_CID_VFLIP:
+ ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
+ break;
+ case V4L2_CID_ROTATE:
+ ctrl->value = ctx->rotation;
+ break;
+ default:
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+ dbg("ctrl->value= %d", ctrl->value);
+ return 0;
+}
+
+static int check_ctrl_val(struct fimc_ctx *ctx,
+ struct v4l2_control *ctrl)
+{
+ struct v4l2_queryctrl *c;
+ c = get_ctrl(ctrl->id);
+ if (!c)
+ return -EINVAL;
+
+ if (ctrl->value < c->minimum || ctrl->value > c->maximum
+ || (c->step != 0 && ctrl->value % c->step != 0)) {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "Invalid control value\n");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+int fimc_m2m_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct fimc_ctx *ctx = priv;
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ unsigned long flags;
+ int ret = 0;
+
+ ret = check_ctrl_val(ctx, ctrl);
+ if (ret)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ if (ctx->rotation != 0)
+ return 0;
+ if (ctrl->value)
+ ctx->flip |= FLIP_X_AXIS;
+ else
+ ctx->flip &= ~FLIP_X_AXIS;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if (ctx->rotation != 0)
+ return 0;
+ if (ctrl->value)
+ ctx->flip |= FLIP_Y_AXIS;
+ else
+ ctx->flip &= ~FLIP_Y_AXIS;
+ break;
+
+ case V4L2_CID_ROTATE:
+ if (ctrl->value == 90 || ctrl->value == 270) {
+ if (ctx->out_path == FIMC_LCDFIFO &&
+ !variant->has_inp_rot) {
+ return -EINVAL;
+ } else if (ctx->in_path == FIMC_DMA &&
+ !variant->has_out_rot) {
+ return -EINVAL;
+ }
+ }
+ ctx->rotation = ctrl->value;
+ if (ctrl->value == 180)
+ ctx->flip = FLIP_XY_AXIS;
+ break;
+
+ default:
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_PARAMS;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return 0;
+}
+
+
+static int fimc_m2m_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cr)
+{
+ struct fimc_frame *frame;
+ struct fimc_ctx *ctx = fh;
+
+ frame = ctx_m2m_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = frame->f_width;
+ cr->bounds.height = frame->f_height;
+ cr->defrect.left = frame->offs_h;
+ cr->defrect.top = frame->offs_v;
+ cr->defrect.width = frame->o_width;
+ cr->defrect.height = frame->o_height;
+ return 0;
+}
+
+static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_frame *frame;
+ struct fimc_ctx *ctx = file->private_data;
+
+ frame = ctx_m2m_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c.left = frame->offs_h;
+ cr->c.top = frame->offs_v;
+ cr->c.width = frame->width;
+ cr->c.height = frame->height;
+
+ return 0;
+}
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ unsigned long flags;
+ struct fimc_frame *f;
+ u32 min_size;
+ int ret = 0;
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+
+ if (cr->c.width <= 0 || cr->c.height <= 0) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "crop width and height must be greater than 0\n");
+ return -EINVAL;
+ }
+
+ f = ctx_m2m_get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ /* Adjust to required pixel boundary. */
+ min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
+ fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+
+ cr->c.width = round_down(cr->c.width, min_size);
+ cr->c.height = round_down(cr->c.height, min_size);
+ cr->c.left = round_down(cr->c.left + 1, min_size);
+ cr->c.top = round_down(cr->c.top + 1, min_size);
+
+ if ((cr->c.left + cr->c.width > f->o_width)
+ || (cr->c.top + cr->c.height > f->o_height)) {
+ v4l2_err(&fimc->m2m.v4l2_dev, "Error in S_CROP params\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ if ((ctx->state & FIMC_SRC_FMT) && (ctx->state & FIMC_DST_FMT)) {
+ /* Check for the pixel scaling ratio when cropping input img. */
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame);
+
+ if (ret) {
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range");
+ return -EINVAL;
+ }
+ }
+ ctx->state |= FIMC_PARAMS;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ f->offs_h = cr->c.left;
+ f->offs_v = cr->c.top;
+ f->width = cr->c.width;
+ f->height = cr->c.height;
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
+ .vidioc_querycap = fimc_m2m_querycap,
+
+ .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt,
+ .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt,
+
+ .vidioc_g_fmt_vid_cap = fimc_m2m_g_fmt,
+ .vidioc_g_fmt_vid_out = fimc_m2m_g_fmt,
+
+ .vidioc_try_fmt_vid_cap = fimc_m2m_try_fmt,
+ .vidioc_try_fmt_vid_out = fimc_m2m_try_fmt,
+
+ .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt,
+ .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt,
+
+ .vidioc_reqbufs = fimc_m2m_reqbufs,
+ .vidioc_querybuf = fimc_m2m_querybuf,
+
+ .vidioc_qbuf = fimc_m2m_qbuf,
+ .vidioc_dqbuf = fimc_m2m_dqbuf,
+
+ .vidioc_streamon = fimc_m2m_streamon,
+ .vidioc_streamoff = fimc_m2m_streamoff,
+
+ .vidioc_queryctrl = fimc_m2m_queryctrl,
+ .vidioc_g_ctrl = fimc_m2m_g_ctrl,
+ .vidioc_s_ctrl = fimc_m2m_s_ctrl,
+
+ .vidioc_g_crop = fimc_m2m_g_crop,
+ .vidioc_s_crop = fimc_m2m_s_crop,
+ .vidioc_cropcap = fimc_m2m_cropcap
+
+};
+
+static void queue_init(void *priv, struct videobuf_queue *vq,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ videobuf_queue_dma_contig_init(vq, &fimc_qops,
+ fimc->m2m.v4l2_dev.dev,
+ &fimc->irqlock, type, V4L2_FIELD_NONE,
+ sizeof(struct fimc_vid_buffer), priv);
+}
+
+static int fimc_m2m_open(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = NULL;
+ int err = 0;
+
+ mutex_lock(&fimc->lock);
+ fimc->m2m.refcnt++;
+ set_bit(ST_OUTDMA_RUN, &fimc->state);
+ mutex_unlock(&fimc->lock);
+
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->fimc_dev = fimc;
+ /* default format */
+ ctx->s_frame.fmt = &fimc_formats[0];
+ ctx->d_frame.fmt = &fimc_formats[0];
+ /* per user process device context initialization */
+ ctx->state = 0;
+ ctx->flags = 0;
+ ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL;
+ ctx->in_path = FIMC_DMA;
+ ctx->out_path = FIMC_DMA;
+ spin_lock_init(&ctx->slock);
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, fimc->m2m.m2m_dev, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ err = PTR_ERR(ctx->m2m_ctx);
+ kfree(ctx);
+ }
+ return err;
+}
+
+static int fimc_m2m_release(struct file *file)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx);
+ mutex_lock(&fimc->lock);
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_OUTDMA_RUN, &fimc->state);
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static unsigned int fimc_m2m_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+
+static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations fimc_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_m2m_open,
+ .release = fimc_m2m_release,
+ .poll = fimc_m2m_poll,
+ .ioctl = video_ioctl2,
+ .mmap = fimc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = fimc_dma_run,
+ .job_abort = fimc_job_abort,
+};
+
+
+static int fimc_register_m2m_device(struct fimc_dev *fimc)
+{
+ struct video_device *vfd;
+ struct platform_device *pdev;
+ struct v4l2_device *v4l2_dev;
+ int ret = 0;
+
+ if (!fimc)
+ return -ENODEV;
+
+ pdev = fimc->pdev;
+ v4l2_dev = &fimc->m2m.v4l2_dev;
+
+ /* set name if it is empty */
+ if (!v4l2_dev->name[0])
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+ "%s.m2m", dev_name(&pdev->dev));
+
+ ret = v4l2_device_register(&pdev->dev, v4l2_dev);
+ if (ret)
+ return ret;;
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+ goto err_m2m_r1;
+ }
+
+ vfd->fops = &fimc_m2m_fops;
+ vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->minor = -1;
+ vfd->release = video_device_release;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
+
+ video_set_drvdata(vfd, fimc);
+ platform_set_drvdata(pdev, fimc);
+
+ fimc->m2m.vfd = vfd;
+ fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(fimc->m2m.m2m_dev)) {
+ v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
+ ret = PTR_ERR(fimc->m2m.m2m_dev);
+ goto err_m2m_r2;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(v4l2_dev,
+ "%s(): failed to register video device\n", __func__);
+ goto err_m2m_r3;
+ }
+ v4l2_info(v4l2_dev,
+ "FIMC m2m driver registered as /dev/video%d\n", vfd->num);
+
+ return 0;
+
+err_m2m_r3:
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+err_m2m_r2:
+ video_device_release(fimc->m2m.vfd);
+err_m2m_r1:
+ v4l2_device_unregister(v4l2_dev);
+
+ return ret;
+}
+
+static void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+{
+ if (fimc) {
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+ video_unregister_device(fimc->m2m.vfd);
+ video_device_release(fimc->m2m.vfd);
+ v4l2_device_unregister(&fimc->m2m.v4l2_dev);
+ }
+}
+
+static void fimc_clk_release(struct fimc_dev *fimc)
+{
+ int i;
+ for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+ if (fimc->clock[i]) {
+ clk_disable(fimc->clock[i]);
+ clk_put(fimc->clock[i]);
+ }
+ }
+}
+
+static int fimc_clk_get(struct fimc_dev *fimc)
+{
+ int i;
+ for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+ fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clock_name[i]);
+ if (IS_ERR(fimc->clock[i])) {
+ dev_err(&fimc->pdev->dev,
+ "failed to get fimc clock: %s\n",
+ fimc_clock_name[i]);
+ return -ENXIO;
+ }
+ clk_enable(fimc->clock[i]);
+ }
+ return 0;
+}
+
+static int fimc_probe(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc;
+ struct resource *res;
+ struct samsung_fimc_driverdata *drv_data;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s():\n", __func__);
+
+ drv_data = (struct samsung_fimc_driverdata *)
+ platform_get_device_id(pdev)->driver_data;
+
+ if (pdev->id >= drv_data->devs_cnt) {
+ dev_err(&pdev->dev, "Invalid platform device id: %d\n",
+ pdev->id);
+ return -EINVAL;
+ }
+
+ fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
+ if (!fimc)
+ return -ENOMEM;
+
+ fimc->id = pdev->id;
+ fimc->variant = drv_data->variant[fimc->id];
+ fimc->pdev = pdev;
+ fimc->state = ST_IDLE;
+
+ spin_lock_init(&fimc->irqlock);
+ spin_lock_init(&fimc->slock);
+
+ mutex_init(&fimc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find the registers\n");
+ ret = -ENOENT;
+ goto err_info;
+ }
+
+ fimc->regs_res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!fimc->regs_res) {
+ dev_err(&pdev->dev, "failed to obtain register region\n");
+ ret = -ENOENT;
+ goto err_info;
+ }
+
+ fimc->regs = ioremap(res->start, resource_size(res));
+ if (!fimc->regs) {
+ dev_err(&pdev->dev, "failed to map registers\n");
+ ret = -ENXIO;
+ goto err_req_region;
+ }
+
+ ret = fimc_clk_get(fimc);
+ if (ret)
+ goto err_regs_unmap;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get IRQ resource\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+ fimc->irq = res->start;
+
+ fimc_hw_reset(fimc);
+
+ ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ fimc->work_queue = create_workqueue(dev_name(&fimc->pdev->dev));
+ if (!fimc->work_queue)
+ goto err_irq;
+
+ ret = fimc_register_m2m_device(fimc);
+ if (ret)
+ goto err_wq;
+
+ fimc_hw_en_lastirq(fimc, true);
+
+ dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
+ __func__, fimc->id);
+
+ return 0;
+
+err_wq:
+ destroy_workqueue(fimc->work_queue);
+err_irq:
+ free_irq(fimc->irq, fimc);
+err_clk:
+ fimc_clk_release(fimc);
+err_regs_unmap:
+ iounmap(fimc->regs);
+err_req_region:
+ release_resource(fimc->regs_res);
+ kfree(fimc->regs_res);
+err_info:
+ kfree(fimc);
+ dev_err(&pdev->dev, "failed to install\n");
+ return ret;
+}
+
+static int __devexit fimc_remove(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc =
+ (struct fimc_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&fimc->m2m.v4l2_dev, "Removing %s\n", pdev->name);
+
+ free_irq(fimc->irq, fimc);
+
+ fimc_hw_reset(fimc);
+
+ fimc_unregister_m2m_device(fimc);
+ fimc_clk_release(fimc);
+ iounmap(fimc->regs);
+ release_resource(fimc->regs_res);
+ kfree(fimc->regs_res);
+ kfree(fimc);
+ return 0;
+}
+
+static struct samsung_fimc_variant fimc01_variant_s5p = {
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+
+ .scaler_en_w = 3264,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5p = {
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+
+ .scaler_en_w = 4224,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+};
+
+static struct samsung_fimc_variant fimc01_variant_s5pv210 = {
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 32,
+
+ .scaler_en_w = 4224,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 32,
+
+ .scaler_en_w = 1920,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1280,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1280,
+ .out_rot_dis_w = 1920,
+};
+
+static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
+ .variant = {
+ [0] = &fimc01_variant_s5p,
+ [1] = &fimc01_variant_s5p,
+ [2] = &fimc2_variant_s5p,
+ },
+ .devs_cnt = 3
+};
+
+static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
+ .variant = {
+ [0] = &fimc01_variant_s5pv210,
+ [1] = &fimc01_variant_s5pv210,
+ [2] = &fimc2_variant_s5pv210,
+ },
+ .devs_cnt = 3
+};
+
+static struct platform_device_id fimc_driver_ids[] = {
+ {
+ .name = "s5p-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_s5p,
+ }, {
+ .name = "s5pv210-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_s5pv210,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+
+static struct platform_driver fimc_driver = {
+ .probe = fimc_probe,
+ .remove = __devexit_p(fimc_remove),
+ .id_table = fimc_driver_ids,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static char banner[] __initdata = KERN_INFO
+ "S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n";
+
+static int __init fimc_init(void)
+{
+ u32 ret;
+ printk(banner);
+
+ ret = platform_driver_register(&fimc_driver);
+ if (ret) {
+ printk(KERN_ERR "FIMC platform driver register failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __exit fimc_exit(void)
+{
+ platform_driver_unregister(&fimc_driver);
+}
+
+module_init(fimc_init);
+module_exit(fimc_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki, s.nawrocki@samsung.com");
+MODULE_DESCRIPTION("S3C/S5P FIMC (video postprocessor) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
new file mode 100644
index 0000000..6b3e0cd
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_CORE_H_
+#define FIMC_CORE_H_
+
+#include <linux/types.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <linux/videodev2.h>
+#include "regs-fimc.h"
+
+#define err(fmt, args...) \
+ printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+
+#ifdef DEBUG
+#define dbg(fmt, args...) \
+ printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+#define NUM_FIMC_CLOCKS 2
+#define MODULE_NAME "s5p-fimc"
+#define FIMC_MAX_DEVS 3
+#define FIMC_MAX_OUT_BUFS 4
+#define SCALER_MAX_HRATIO 64
+#define SCALER_MAX_VRATIO 64
+
+enum {
+ ST_IDLE,
+ ST_OUTDMA_RUN,
+ ST_M2M_PEND,
+};
+
+#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
+#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+
+enum fimc_datapath {
+ FIMC_ITU_CAM_A,
+ FIMC_ITU_CAM_B,
+ FIMC_MIPI_CAM,
+ FIMC_DMA,
+ FIMC_LCDFIFO,
+ FIMC_WRITEBACK
+};
+
+enum fimc_color_fmt {
+ S5P_FIMC_RGB565,
+ S5P_FIMC_RGB666,
+ S5P_FIMC_RGB888,
+ S5P_FIMC_YCBCR420,
+ S5P_FIMC_YCBCR422,
+ S5P_FIMC_YCBYCR422,
+ S5P_FIMC_YCRYCB422,
+ S5P_FIMC_CBYCRY422,
+ S5P_FIMC_CRYCBY422,
+ S5P_FIMC_RGB30_LOCAL,
+ S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_COLOR_MASK = 0x0F,
+};
+
+/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */
+#define S5P_FIMC_OUT_CRYCBY S5P_CIOCTRL_ORDER422_CRYCBY
+#define S5P_FIMC_OUT_CBYCRY S5P_CIOCTRL_ORDER422_YCRYCB
+#define S5P_FIMC_OUT_YCRYCB S5P_CIOCTRL_ORDER422_CBYCRY
+#define S5P_FIMC_OUT_YCBYCR S5P_CIOCTRL_ORDER422_YCBYCR
+
+/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */
+#define S5P_FIMC_IN_CRYCBY S5P_MSCTRL_ORDER422_CRYCBY
+#define S5P_FIMC_IN_CBYCRY S5P_MSCTRL_ORDER422_YCRYCB
+#define S5P_FIMC_IN_YCRYCB S5P_MSCTRL_ORDER422_CBYCRY
+#define S5P_FIMC_IN_YCBYCR S5P_MSCTRL_ORDER422_YCBYCR
+
+/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
+#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
+
+/* The embedded image effect selection */
+#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS
+#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY
+#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE
+#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE
+#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING
+#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE
+
+/* The hardware context state. */
+#define FIMC_PARAMS (1 << 0)
+#define FIMC_SRC_ADDR (1 << 1)
+#define FIMC_DST_ADDR (1 << 2)
+#define FIMC_SRC_FMT (1 << 3)
+#define FIMC_DST_FMT (1 << 4)
+
+/* Image conversion flags */
+#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
+#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0)
+#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1)
+#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1)
+#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2)
+#define FIMC_SCAN_MODE_INTERLACED (1 << 2)
+/* YCbCr data dynamic range for RGB-YUV color conversion. Y/Cb/Cr: (0 ~ 255) */
+#define FIMC_COLOR_RANGE_WIDE (0 << 3)
+/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
+#define FIMC_COLOR_RANGE_NARROW (1 << 3)
+
+#define FLIP_NONE 0
+#define FLIP_X_AXIS 1
+#define FLIP_Y_AXIS 2
+#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS)
+
+/**
+ * struct fimc_fmt - the driver's internal color format data
+ * @name: format description
+ * @fourcc: the fourcc code for this format
+ * @color: the corresponding fimc_color_fmt
+ * @depth: number of bits per pixel
+ * @buff_cnt: number of physically non-contiguous data planes
+ * @planes_cnt: number of physically contiguous data planes
+ */
+struct fimc_fmt {
+ char *name;
+ u32 fourcc;
+ u32 color;
+ u32 depth;
+ u16 buff_cnt;
+ u16 planes_cnt;
+};
+
+/**
+ * struct fimc_dma_offset - pixel offset information for DMA
+ * @y_h: y value horizontal offset
+ * @y_v: y value vertical offset
+ * @cb_h: cb value horizontal offset
+ * @cb_v: cb value vertical offset
+ * @cr_h: cr value horizontal offset
+ * @cr_v: cr value vertical offset
+ */
+struct fimc_dma_offset {
+ int y_h;
+ int y_v;
+ int cb_h;
+ int cb_v;
+ int cr_h;
+ int cr_v;
+};
+
+/**
+ * struct fimc_effect - the configuration data for the "Arbitrary" image effect
+ * @type: effect type
+ * @pat_cb: cr value when type is "arbitrary"
+ * @pat_cr: cr value when type is "arbitrary"
+ */
+struct fimc_effect {
+ u32 type;
+ u8 pat_cb;
+ u8 pat_cr;
+};
+
+/**
+ * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
+ *
+ * @enabled: the flag set when the scaler is used
+ * @hfactor: horizontal shift factor
+ * @vfactor: vertical shift factor
+ * @pre_hratio: horizontal ratio of the prescaler
+ * @pre_vratio: vertical ratio of the prescaler
+ * @pre_dst_width: the prescaler's destination width
+ * @pre_dst_height: the prescaler's destination height
+ * @scaleup_h: flag indicating scaling up horizontally
+ * @scaleup_v: flag indicating scaling up vertically
+ * @main_hratio: the main scaler's horizontal ratio
+ * @main_vratio: the main scaler's vertical ratio
+ * @real_width: source width - offset
+ * @real_height: source height - offset
+ * @copy_mode: flag set if one-to-one mode is used, i.e. no scaling
+ * and color format conversion
+ */
+struct fimc_scaler {
+ u32 enabled;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 scaleup_h;
+ u32 scaleup_v;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+ u32 copy_mode;
+};
+
+/**
+ * struct fimc_addr - the FIMC physical address set for DMA
+ *
+ * @y: luminance plane physical address
+ * @cb: Cb plane physical address
+ * @cr: Cr plane physical address
+ */
+struct fimc_addr {
+ u32 y;
+ u32 cb;
+ u32 cr;
+};
+
+/**
+ * struct fimc_vid_buffer - the driver's video buffer
+ * @vb: v4l videobuf buffer
+ */
+struct fimc_vid_buffer {
+ struct videobuf_buffer vb;
+};
+
+/**
+ * struct fimc_frame - input/output frame format properties
+ *
+ * @f_width: image full width (virtual screen size)
+ * @f_height: image full height (virtual screen size)
+ * @o_width: original image width as set by S_FMT
+ * @o_height: original image height as set by S_FMT
+ * @offs_h: image horizontal pixel offset
+ * @offs_v: image vertical pixel offset
+ * @width: image pixel width
+ * @height: image pixel weight
+ * @paddr: image frame buffer physical addresses
+ * @buf_cnt: number of buffers depending on a color format
+ * @size: image size in bytes
+ * @color: color format
+ * @dma_offset: DMA offset in bytes
+ */
+struct fimc_frame {
+ u32 f_width;
+ u32 f_height;
+ u32 o_width;
+ u32 o_height;
+ u32 offs_h;
+ u32 offs_v;
+ u32 width;
+ u32 height;
+ u32 size;
+ struct fimc_addr paddr;
+ struct fimc_dma_offset dma_offset;
+ struct fimc_fmt *fmt;
+};
+
+/**
+ * struct fimc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @v4l2_dev: v4l2 device for m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct fimc_m2m_device {
+ struct video_device *vfd;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct fimc_ctx *ctx;
+ int refcnt;
+};
+
+/**
+ * struct samsung_fimc_variant - camera interface variant information
+ *
+ * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
+ * @has_inp_rot: set if has input rotator
+ * @has_out_rot: set if has output rotator
+ * @min_inp_pixsize: minimum input pixel size
+ * @min_out_pixsize: minimum output pixel size
+ * @scaler_en_w: maximum input pixel width when the scaler is enabled
+ * @scaler_dis_w: maximum input pixel width when the scaler is disabled
+ * @in_rot_en_h: maximum input width when the input rotator is used
+ * @in_rot_dis_w: maximum input width when the input rotator is used
+ * @out_rot_en_w: maximum output width for the output rotator enabled
+ * @out_rot_dis_w: maximum output width for the output rotator enabled
+ */
+struct samsung_fimc_variant {
+ unsigned int pix_hoff:1;
+ unsigned int has_inp_rot:1;
+ unsigned int has_out_rot:1;
+
+ u16 min_inp_pixsize;
+ u16 min_out_pixsize;
+ u16 scaler_en_w;
+ u16 scaler_dis_w;
+ u16 in_rot_en_h;
+ u16 in_rot_dis_w;
+ u16 out_rot_en_w;
+ u16 out_rot_dis_w;
+};
+
+/**
+ * struct samsung_fimc_driverdata - per-device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @dev_cnt: number of fimc sub-devices available in SoC
+ */
+struct samsung_fimc_driverdata {
+ struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
+ int devs_cnt;
+};
+
+struct fimc_ctx;
+
+/**
+ * struct fimc_subdev - abstraction for a FIMC entity
+ *
+ * @slock: the spinlock protecting this data structure
+ * @lock: the mutex protecting this data structure
+ * @pdev: pointer to the FIMC platform device
+ * @id: FIMC device index (0..2)
+ * @clock[]: the clocks required for FIMC operation
+ * @regs: the mapped hardware registers
+ * @regs_res: the resource claimed for IO registers
+ * @irq: interrupt number of the FIMC subdevice
+ * @irqlock: spinlock protecting videbuffer queue
+ * @m2m: memory-to-memory V4L2 device information
+ * @state: the FIMC device state flags
+ */
+struct fimc_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ struct platform_device *pdev;
+ struct samsung_fimc_variant *variant;
+ int id;
+ struct clk *clock[NUM_FIMC_CLOCKS];
+ void __iomem *regs;
+ struct resource *regs_res;
+ int irq;
+ spinlock_t irqlock;
+ struct workqueue_struct *work_queue;
+ struct fimc_m2m_device m2m;
+ unsigned long state;
+};
+
+/**
+ * fimc_ctx - the device context data
+ *
+ * @lock: mutex protecting this data structure
+ * @s_frame: source frame properties
+ * @d_frame: destination frame properties
+ * @out_order_1p: output 1-plane YCBCR order
+ * @out_order_2p: output 2-plane YCBCR order
+ * @in_order_1p input 1-plane YCBCR order
+ * @in_order_2p: input 2-plane YCBCR order
+ * @in_path: input mode (DMA or camera)
+ * @out_path: output mode (DMA or FIFO)
+ * @scaler: image scaler properties
+ * @effect: image effect
+ * @rotation: image clockwise rotation in degrees
+ * @flip: image flip mode
+ * @flags: an additional flags for image conversion
+ * @state: flags to keep track of user configuration
+ * @fimc_dev: the FIMC device this context applies to
+ * @m2m_ctx: memory-to-memory device context
+ */
+struct fimc_ctx {
+ spinlock_t slock;
+ struct fimc_frame s_frame;
+ struct fimc_frame d_frame;
+ u32 out_order_1p;
+ u32 out_order_2p;
+ u32 in_order_1p;
+ u32 in_order_2p;
+ enum fimc_datapath in_path;
+ enum fimc_datapath out_path;
+ struct fimc_scaler scaler;
+ struct fimc_effect effect;
+ int rotation;
+ u32 flip;
+ u32 flags;
+ u32 state;
+ struct fimc_dev *fimc_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+
+static inline int tiled_fmt(struct fimc_fmt *fmt)
+{
+ return 0;
+}
+
+static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg |= S5P_CIGCTRL_IRQ_CLR;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+}
+
+static inline void fimc_hw_start_scaler(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg |= S5P_CISCCTRL_SCALERSTART;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_stop_scaler(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg &= ~S5P_CISCCTRL_SCALERSTART;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+ cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
+ writel(cfg, dev->regs + S5P_CIIMGCPT);
+}
+
+static inline void fimc_hw_start_in_dma(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg |= S5P_MSCTRL_ENVID;
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline void fimc_hw_stop_in_dma(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~S5P_MSCTRL_ENVID;
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline struct fimc_frame *ctx_m2m_get_frame(struct fimc_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ struct fimc_frame *frame;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
+ frame = &ctx->s_frame;
+ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
+ frame = &ctx->d_frame;
+ } else {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return frame;
+}
+
+/* -----------------------------------------------------*/
+/* fimc-reg.c */
+void fimc_hw_reset(struct fimc_dev *dev);
+void fimc_hw_set_rotation(struct fimc_ctx *ctx);
+void fimc_hw_set_target_format(struct fimc_ctx *ctx);
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable);
+void fimc_hw_en_irq(struct fimc_dev *dev, int enable);
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_set_scaler(struct fimc_ctx *ctx);
+void fimc_hw_en_capture(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
+void fimc_hw_set_input_path(struct fimc_ctx *ctx);
+void fimc_hw_set_output_path(struct fimc_ctx *ctx);
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+
+#endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
new file mode 100644
index 0000000..5570f1c
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -0,0 +1,527 @@
+/*
+ * Register interface file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, s.nawrocki@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/map.h>
+
+#include "fimc-core.h"
+
+
+void fimc_hw_reset(struct fimc_dev *dev)
+{
+ u32 cfg;
+
+ cfg = readl(dev->regs + S5P_CISRCFMT);
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ writel(cfg, dev->regs + S5P_CISRCFMT);
+
+ /* Software reset. */
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+ msleep(1);
+
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg &= ~S5P_CIGCTRL_SWRST;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+
+}
+
+void fimc_hw_set_rotation(struct fimc_ctx *ctx)
+{
+ u32 cfg, flip;
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ cfg = readl(dev->regs + S5P_CITRGFMT);
+ cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90);
+
+ flip = readl(dev->regs + S5P_MSCTRL);
+ flip &= ~S5P_MSCTRL_FLIP_MASK;
+
+ /*
+ * The input and output rotator cannot work simultaneously.
+ * Use the output rotator in output DMA mode or the input rotator
+ * in direct fifo output mode.
+ */
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ if (ctx->out_path == FIMC_LCDFIFO) {
+ cfg |= S5P_CITRGFMT_INROT90;
+ if (ctx->rotation == 270)
+ flip |= S5P_MSCTRL_FLIP_180;
+ } else {
+ cfg |= S5P_CITRGFMT_OUTROT90;
+ if (ctx->rotation == 270)
+ cfg |= S5P_CITRGFMT_FLIP_180;
+ }
+ } else if (ctx->rotation == 180) {
+ if (ctx->out_path == FIMC_LCDFIFO)
+ flip |= S5P_MSCTRL_FLIP_180;
+ else
+ cfg |= S5P_CITRGFMT_FLIP_180;
+ }
+ if (ctx->rotation == 180 || ctx->rotation == 270)
+ writel(flip, dev->regs + S5P_MSCTRL);
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+}
+
+static u32 fimc_hw_get_in_flip(u32 ctx_flip)
+{
+ u32 flip = S5P_MSCTRL_FLIP_NORMAL;
+
+ switch (ctx_flip) {
+ case FLIP_X_AXIS:
+ flip = S5P_MSCTRL_FLIP_X_MIRROR;
+ break;
+ case FLIP_Y_AXIS:
+ flip = S5P_MSCTRL_FLIP_Y_MIRROR;
+ break;
+ case FLIP_XY_AXIS:
+ flip = S5P_MSCTRL_FLIP_180;
+ break;
+ }
+
+ return flip;
+}
+
+static u32 fimc_hw_get_target_flip(u32 ctx_flip)
+{
+ u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
+
+ switch (ctx_flip) {
+ case FLIP_X_AXIS:
+ flip = S5P_CITRGFMT_FLIP_X_MIRROR;
+ break;
+ case FLIP_Y_AXIS:
+ flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
+ break;
+ case FLIP_XY_AXIS:
+ flip = S5P_CITRGFMT_FLIP_180;
+ break;
+ case FLIP_NONE:
+ break;
+
+ }
+ return flip;
+}
+
+void fimc_hw_set_target_format(struct fimc_ctx *ctx)
+{
+ u32 cfg;
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+
+ dbg("w= %d, h= %d color: %d", frame->width,
+ frame->height, frame->fmt->color);
+
+ cfg = readl(dev->regs + S5P_CITRGFMT);
+ cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
+ S5P_CITRGFMT_VSIZE_MASK);
+
+ switch (frame->fmt->color) {
+ case S5P_FIMC_RGB565:
+ case S5P_FIMC_RGB666:
+ case S5P_FIMC_RGB888:
+ cfg |= S5P_CITRGFMT_RGB;
+ break;
+ case S5P_FIMC_YCBCR420:
+ cfg |= S5P_CITRGFMT_YCBCR420;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ case S5P_FIMC_YCRYCB422:
+ case S5P_FIMC_CBYCRY422:
+ case S5P_FIMC_CRYCBY422:
+ if (frame->fmt->planes_cnt == 1)
+ cfg |= S5P_CITRGFMT_YCBCR422_1P;
+ else
+ cfg |= S5P_CITRGFMT_YCBCR422;
+ break;
+ default:
+ break;
+ }
+
+ cfg |= S5P_CITRGFMT_HSIZE(frame->width);
+ cfg |= S5P_CITRGFMT_VSIZE(frame->height);
+
+ if (ctx->rotation == 0) {
+ cfg &= ~S5P_CITRGFMT_FLIP_MASK;
+ cfg |= fimc_hw_get_target_flip(ctx->flip);
+ }
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+
+ cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
+ cfg |= (frame->width * frame->height);
+ writel(cfg, dev->regs + S5P_CITAREA);
+}
+
+static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ u32 cfg = 0;
+
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ cfg |= S5P_ORIG_SIZE_HOR(frame->f_height);
+ cfg |= S5P_ORIG_SIZE_VER(frame->f_width);
+ } else {
+ cfg |= S5P_ORIG_SIZE_HOR(frame->f_width);
+ cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
+ }
+ writel(cfg, dev->regs + S5P_ORGOSIZE);
+}
+
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
+{
+ u32 cfg;
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ struct fimc_dma_offset *offset = &frame->dma_offset;
+
+ /* Set the input dma offsets. */
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+ writel(cfg, dev->regs + S5P_CIOYOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+ writel(cfg, dev->regs + S5P_CIOCBOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+ writel(cfg, dev->regs + S5P_CIOCROFF);
+
+ fimc_hw_set_out_dma_size(ctx);
+
+ /* Configure chroma components order. */
+ cfg = readl(dev->regs + S5P_CIOCTRL);
+
+ cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
+ S5P_CIOCTRL_YCBCR_PLANE_MASK);
+
+ if (frame->fmt->planes_cnt == 1)
+ cfg |= ctx->out_order_1p;
+ else if (frame->fmt->planes_cnt == 2)
+ cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
+ else if (frame->fmt->planes_cnt == 3)
+ cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
+
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
+static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
+{
+ u32 cfg = readl(dev->regs + S5P_ORGISIZE);
+ if (enable)
+ cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ else
+ cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ writel(cfg, dev->regs + S5P_ORGISIZE);
+}
+
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
+{
+ unsigned long flags;
+ u32 cfg;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ cfg = readl(dev->regs + S5P_CIOCTRL);
+ if (enable)
+ cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
+ else
+ cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ u32 cfg = 0, shfactor;
+
+ shfactor = 10 - (sc->hfactor + sc->vfactor);
+
+ cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
+ cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
+ cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
+ writel(cfg, dev->regs + S5P_CISCPRERATIO);
+
+ cfg = 0;
+ cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
+ cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
+ writel(cfg, dev->regs + S5P_CISCPREDST);
+}
+
+void fimc_hw_set_scaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ struct fimc_frame *src_frame = &ctx->s_frame;
+ struct fimc_frame *dst_frame = &ctx->d_frame;
+ u32 cfg = 0;
+
+ if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
+ cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
+
+ if (!sc->enabled)
+ cfg |= S5P_CISCCTRL_SCALERBYPASS;
+
+ if (sc->scaleup_h)
+ cfg |= S5P_CISCCTRL_SCALEUP_H;
+
+ if (sc->scaleup_v)
+ cfg |= S5P_CISCCTRL_SCALEUP_V;
+
+ if (sc->copy_mode)
+ cfg |= S5P_CISCCTRL_ONE2ONE;
+
+
+ if (ctx->in_path == FIMC_DMA) {
+ if (src_frame->fmt->color == S5P_FIMC_RGB565)
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
+ else if (src_frame->fmt->color == S5P_FIMC_RGB666)
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
+ else if (src_frame->fmt->color == S5P_FIMC_RGB888)
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
+ }
+
+ if (ctx->out_path == FIMC_DMA) {
+ if (dst_frame->fmt->color == S5P_FIMC_RGB565)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
+ else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
+ else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+ } else {
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+
+ if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
+ cfg |= S5P_CISCCTRL_INTERLACE;
+ }
+
+ dbg("main_hratio= 0x%X main_vratio= 0x%X",
+ sc->main_hratio, sc->main_vratio);
+
+ cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio);
+ cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio);
+
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_en_capture(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ u32 cfg;
+
+ cfg = readl(dev->regs + S5P_CIIMGCPT);
+ /* One shot mode for output DMA or freerun for FIFO. */
+ if (ctx->out_path == FIMC_DMA)
+ cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
+ else
+ cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
+
+ if (ctx->scaler.enabled)
+ cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
+
+ writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
+}
+
+void fimc_hw_set_effect(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_effect *effect = &ctx->effect;
+ u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
+
+ cfg |= effect->type;
+
+ if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+ cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+ cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ }
+
+ writel(cfg, dev->regs + S5P_CIIMGEFF);
+}
+
+static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->s_frame;
+ u32 cfg_o = 0;
+ u32 cfg_r = 0;
+
+ if (FIMC_LCDFIFO == ctx->out_path)
+ cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+
+ cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
+ cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
+ cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
+ cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
+
+ writel(cfg_o, dev->regs + S5P_ORGISIZE);
+ writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->s_frame;
+ struct fimc_dma_offset *offset = &frame->dma_offset;
+ u32 cfg = 0;
+
+ /* Set the pixel offsets. */
+ cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+ writel(cfg, dev->regs + S5P_CIIYOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+ writel(cfg, dev->regs + S5P_CIICBOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+ writel(cfg, dev->regs + S5P_CIICROFF);
+
+ /* Input original and real size. */
+ fimc_hw_set_in_dma_size(ctx);
+
+ /* Autoload is used currently only in FIFO mode. */
+ fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
+
+ /* Set the input DMA to process single frame only. */
+ cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~(S5P_MSCTRL_FLIP_MASK
+ | S5P_MSCTRL_INFORMAT_MASK
+ | S5P_MSCTRL_IN_BURST_COUNT_MASK
+ | S5P_MSCTRL_INPUT_MASK
+ | S5P_MSCTRL_C_INT_IN_MASK
+ | S5P_MSCTRL_2P_IN_ORDER_MASK);
+
+ cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
+
+ switch (frame->fmt->color) {
+ case S5P_FIMC_RGB565:
+ case S5P_FIMC_RGB666:
+ case S5P_FIMC_RGB888:
+ cfg |= S5P_MSCTRL_INFORMAT_RGB;
+ break;
+ case S5P_FIMC_YCBCR420:
+ cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
+
+ if (frame->fmt->planes_cnt == 2)
+ cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
+ else
+ cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+
+ break;
+ case S5P_FIMC_YCBYCR422:
+ case S5P_FIMC_YCRYCB422:
+ case S5P_FIMC_CBYCRY422:
+ case S5P_FIMC_CRYCBY422:
+ if (frame->fmt->planes_cnt == 1) {
+ cfg |= ctx->in_order_1p
+ | S5P_MSCTRL_INFORMAT_YCBCR422_1P;
+ } else {
+ cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
+
+ if (frame->fmt->planes_cnt == 2)
+ cfg |= ctx->in_order_2p
+ | S5P_MSCTRL_C_INT_IN_2PLANE;
+ else
+ cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Input DMA flip mode (and rotation).
+ * Do not allow simultaneous rotation and flipping.
+ */
+ if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO)
+ cfg |= fimc_hw_get_in_flip(ctx->flip);
+
+ writel(cfg, dev->regs + S5P_MSCTRL);
+
+ /* Input/output DMA linear/tiled mode. */
+ cfg = readl(dev->regs + S5P_CIDMAPARAM);
+ cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
+
+ if (tiled_fmt(ctx->s_frame.fmt))
+ cfg |= S5P_CIDMAPARAM_R_64X32;
+
+ if (tiled_fmt(ctx->d_frame.fmt))
+ cfg |= S5P_CIDMAPARAM_W_64X32;
+
+ writel(cfg, dev->regs + S5P_CIDMAPARAM);
+}
+
+
+void fimc_hw_set_input_path(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~S5P_MSCTRL_INPUT_MASK;
+
+ if (ctx->in_path == FIMC_DMA)
+ cfg |= S5P_MSCTRL_INPUT_MEMORY;
+ else
+ cfg |= S5P_MSCTRL_INPUT_EXTCAM;
+
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+void fimc_hw_set_output_path(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
+ if (ctx->out_path == FIMC_LCDFIFO)
+ cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+ u32 cfg = 0;
+
+ cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
+ cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+
+ writel(paddr->y, dev->regs + S5P_CIIYSA0);
+ writel(paddr->cb, dev->regs + S5P_CIICBSA0);
+ writel(paddr->cr, dev->regs + S5P_CIICRSA0);
+
+ cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+ int i;
+ /* Set all the output register sets to point to single video buffer. */
+ for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) {
+ writel(paddr->y, dev->regs + S5P_CIOYSA(i));
+ writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
+ writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
+ }
+}
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
new file mode 100644
index 0000000..a3cfe82
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -0,0 +1,293 @@
+/*
+ * Register definition file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef REGS_FIMC_H_
+#define REGS_FIMC_H_
+
+#define S5P_CIOYSA(__x) (0x18 + (__x) * 4)
+#define S5P_CIOCBSA(__x) (0x28 + (__x) * 4)
+#define S5P_CIOCRSA(__x) (0x38 + (__x) * 4)
+
+/* Input source format */
+#define S5P_CISRCFMT 0x00
+#define S5P_CISRCFMT_ITU601_8BIT (1 << 31)
+#define S5P_CISRCFMT_ITU601_16BIT (1 << 29)
+#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14)
+#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14)
+#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14)
+#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14)
+#define S5P_CISRCFMT_HSIZE(x) ((x) << 16)
+#define S5P_CISRCFMT_VSIZE(x) ((x) << 0)
+
+/* Window offset */
+#define S5P_CIWDOFST 0x04
+#define S5P_CIWDOFST_WINOFSEN (1 << 31)
+#define S5P_CIWDOFST_CLROVFIY (1 << 30)
+#define S5P_CIWDOFST_CLROVRLB (1 << 29)
+#define S5P_CIWDOFST_WINHOROFST_MASK (0x7ff << 16)
+#define S5P_CIWDOFST_CLROVFICB (1 << 15)
+#define S5P_CIWDOFST_CLROVFICR (1 << 14)
+#define S5P_CIWDOFST_WINHOROFST(x) ((x) << 16)
+#define S5P_CIWDOFST_WINVEROFST(x) ((x) << 0)
+#define S5P_CIWDOFST_WINVEROFST_MASK (0xfff << 0)
+
+/* Global control */
+#define S5P_CIGCTRL 0x08
+#define S5P_CIGCTRL_SWRST (1 << 31)
+#define S5P_CIGCTRL_CAMRST_A (1 << 30)
+#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29)
+#define S5P_CIGCTRL_SELCAM_ITU_MASK (1 << 29)
+#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27)
+#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27)
+#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27)
+#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_SHIFT (27)
+#define S5P_CIGCTRL_INVPOLPCLK (1 << 26)
+#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25)
+#define S5P_CIGCTRL_INVPOLHREF (1 << 24)
+#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22)
+#define S5P_CIGCTRL_HREF_MASK (1 << 21)
+#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20)
+#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
+#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
+#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
+#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
+#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
+#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
+#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
+#define S5P_CIGCTRL_INTERLACE (1 << 0)
+
+/* Window offset 2 */
+#define S5P_CIWDOFST2 0x14
+#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16)
+#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0)
+#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16)
+#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0)
+
+/* Output DMA Y plane start address */
+#define S5P_CIOYSA1 0x18
+#define S5P_CIOYSA2 0x1c
+#define S5P_CIOYSA3 0x20
+#define S5P_CIOYSA4 0x24
+
+/* Output DMA Cb plane start address */
+#define S5P_CIOCBSA1 0x28
+#define S5P_CIOCBSA2 0x2c
+#define S5P_CIOCBSA3 0x30
+#define S5P_CIOCBSA4 0x34
+
+/* Output DMA Cr plane start address */
+#define S5P_CIOCRSA1 0x38
+#define S5P_CIOCRSA2 0x3c
+#define S5P_CIOCRSA3 0x40
+#define S5P_CIOCRSA4 0x44
+
+/* Target image format */
+#define S5P_CITRGFMT 0x48
+#define S5P_CITRGFMT_INROT90 (1 << 31)
+#define S5P_CITRGFMT_YCBCR420 (0 << 29)
+#define S5P_CITRGFMT_YCBCR422 (1 << 29)
+#define S5P_CITRGFMT_YCBCR422_1P (2 << 29)
+#define S5P_CITRGFMT_RGB (3 << 29)
+#define S5P_CITRGFMT_FMT_MASK (3 << 29)
+#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16)
+#define S5P_CITRGFMT_FLIP_SHIFT (14)
+#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14)
+#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14)
+#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14)
+#define S5P_CITRGFMT_FLIP_180 (3 << 14)
+#define S5P_CITRGFMT_FLIP_MASK (3 << 14)
+#define S5P_CITRGFMT_OUTROT90 (1 << 13)
+#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0)
+#define S5P_CITRGFMT_HSIZE(x) ((x) << 16)
+#define S5P_CITRGFMT_VSIZE(x) ((x) << 0)
+
+/* Output DMA control */
+#define S5P_CIOCTRL 0x4c
+#define S5P_CIOCTRL_ORDER422_MASK (3 << 0)
+#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0)
+#define S5P_CIOCTRL_ORDER422_YCRYCB (1 << 0)
+#define S5P_CIOCTRL_ORDER422_CBYCRY (2 << 0)
+#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0)
+#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
+#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3)
+#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3)
+#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3)
+#define S5P_CIOCTRL_ORDER2P_SHIFT (24)
+#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24)
+#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
+
+/* Pre-scaler control 1 */
+#define S5P_CISCPRERATIO 0x50
+#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28)
+#define S5P_CISCPRERATIO_HOR(x) ((x) << 16)
+#define S5P_CISCPRERATIO_VER(x) ((x) << 0)
+
+#define S5P_CISCPREDST 0x54
+#define S5P_CISCPREDST_WIDTH(x) ((x) << 16)
+#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0)
+
+/* Main scaler control */
+#define S5P_CISCCTRL 0x58
+#define S5P_CISCCTRL_SCALERBYPASS (1 << 31)
+#define S5P_CISCCTRL_SCALEUP_H (1 << 30)
+#define S5P_CISCCTRL_SCALEUP_V (1 << 29)
+#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28)
+#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27)
+#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26)
+#define S5P_CISCCTRL_INTERLACE (1 << 25)
+#define S5P_CISCCTRL_SCALERSTART (1 << 15)
+#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
+#define S5P_CISCCTRL_RGB_EXT (1 << 10)
+#define S5P_CISCCTRL_ONE2ONE (1 << 9)
+#define S5P_CISCCTRL_SC_HORRATIO(x) ((x) << 16)
+#define S5P_CISCCTRL_SC_VERRATIO(x) ((x) << 0)
+
+/* Target area */
+#define S5P_CITAREA 0x5c
+#define S5P_CITAREA_MASK 0x0fffffff
+
+/* General status */
+#define S5P_CISTATUS 0x64
+#define S5P_CISTATUS_OVFIY (1 << 31)
+#define S5P_CISTATUS_OVFICB (1 << 30)
+#define S5P_CISTATUS_OVFICR (1 << 29)
+#define S5P_CISTATUS_VSYNC (1 << 28)
+#define S5P_CISTATUS_WINOFF_EN (1 << 25)
+#define S5P_CISTATUS_IMGCPT_EN (1 << 22)
+#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21)
+#define S5P_CISTATUS_VSYNC_A (1 << 20)
+#define S5P_CISTATUS_VSYNC_B (1 << 19)
+#define S5P_CISTATUS_OVRLB (1 << 18)
+#define S5P_CISTATUS_FRAME_END (1 << 17)
+#define S5P_CISTATUS_LASTCAPT_END (1 << 16)
+#define S5P_CISTATUS_VVALID_A (1 << 15)
+#define S5P_CISTATUS_VVALID_B (1 << 14)
+
+/* Image capture control */
+#define S5P_CIIMGCPT 0xc0
+#define S5P_CIIMGCPT_IMGCPTEN (1 << 31)
+#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30)
+#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25)
+#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
+
+/* Frame capture sequence */
+#define S5P_CICPTSEQ 0xc4
+
+/* Image effect */
+#define S5P_CIIMGEFF 0xd0
+#define S5P_CIIMGEFF_IE_DISABLE (0 << 30)
+#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
+#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
+#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
+#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26)
+#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26)
+#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26)
+#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26)
+#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26)
+#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
+#define S5P_CIIMGEFF_FIN_MASK (7 << 26)
+#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0))
+#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13)
+#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0)
+
+/* Input DMA Y/Cb/Cr plane start address 0 */
+#define S5P_CIIYSA0 0xd4
+#define S5P_CIICBSA0 0xd8
+#define S5P_CIICRSA0 0xdc
+
+/* Real input DMA image size */
+#define S5P_CIREAL_ISIZE 0xf8
+#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31)
+#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30)
+#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16)
+#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0)
+
+
+/* Input DMA control */
+#define S5P_MSCTRL 0xfc
+#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24)
+#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
+#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16
+#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15)
+#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15)
+#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15)
+#define S5P_MSCTRL_FLIP_SHIFT 13
+#define S5P_MSCTRL_FLIP_MASK (3 << 13)
+#define S5P_MSCTRL_FLIP_NORMAL (0 << 13)
+#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13)
+#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13)
+#define S5P_MSCTRL_FLIP_180 (3 << 13)
+#define S5P_MSCTRL_ORDER422_SHIFT 4
+#define S5P_MSCTRL_ORDER422_CRYCBY (0 << 4)
+#define S5P_MSCTRL_ORDER422_YCRYCB (1 << 4)
+#define S5P_MSCTRL_ORDER422_CBYCRY (2 << 4)
+#define S5P_MSCTRL_ORDER422_YCBYCR (3 << 4)
+#define S5P_MSCTRL_ORDER422_MASK (3 << 4)
+#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3)
+#define S5P_MSCTRL_INPUT_MEMORY (1 << 3)
+#define S5P_MSCTRL_INPUT_MASK (1 << 3)
+#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1)
+#define S5P_MSCTRL_INFORMAT_RGB (3 << 1)
+#define S5P_MSCTRL_INFORMAT_MASK (3 << 1)
+#define S5P_MSCTRL_ENVID (1 << 0)
+#define S5P_MSCTRL_FRAME_COUNT(x) ((x) << 24)
+
+/* Input DMA Y/Cb/Cr plane start address 1 */
+#define S5P_CIIYSA1 0x144
+#define S5P_CIICBSA1 0x148
+#define S5P_CIICRSA1 0x14c
+
+/* Output DMA Y/Cb/Cr offset */
+#define S5P_CIOYOFF 0x168
+#define S5P_CIOCBOFF 0x16c
+#define S5P_CIOCROFF 0x170
+
+/* Input DMA Y/Cb/Cr offset */
+#define S5P_CIIYOFF 0x174
+#define S5P_CIICBOFF 0x178
+#define S5P_CIICROFF 0x17c
+
+#define S5P_CIO_OFFS_VER(x) ((x) << 16)
+#define S5P_CIO_OFFS_HOR(x) ((x) << 0)
+
+/* Input DMA original image size */
+#define S5P_ORGISIZE 0x180
+
+/* Output DMA original image size */
+#define S5P_ORGOSIZE 0x184
+
+#define S5P_ORIG_SIZE_VER(x) ((x) << 16)
+#define S5P_ORIG_SIZE_HOR(x) ((x) << 0)
+
+/* Real output DMA image size (extension register) */
+#define S5P_CIEXTEN 0x188
+
+#define S5P_CIDMAPARAM 0x18c
+#define S5P_CIDMAPARAM_R_LINEAR (0 << 29)
+#define S5P_CIDMAPARAM_R_64X32 (3 << 29)
+#define S5P_CIDMAPARAM_W_LINEAR (0 << 13)
+#define S5P_CIDMAPARAM_W_64X32 (3 << 13)
+#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13))
+
+/* MIPI CSI image format */
+#define S5P_CSIIMGFMT 0x194
+
+#endif /* REGS_FIMC_H_ */
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 76da743..ee963f4 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -45,6 +45,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/saa7115.h>
@@ -65,16 +66,19 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct saa711x_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ struct {
+ /* chroma gain control cluster */
+ struct v4l2_ctrl *agc;
+ struct v4l2_ctrl *gain;
+ };
+
v4l2_std_id std;
int input;
int output;
int enable;
int radio;
- int bright;
- int contrast;
- int hue;
- int sat;
- int chroma_agc;
int width;
int height;
u32 ident;
@@ -90,6 +94,11 @@ static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct saa711x_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@@ -741,96 +750,53 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return 0;
}
-static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct saa711x_state *state = to_state(sd);
- u8 val;
switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->bright = ctrl->value;
- saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
- break;
-
- case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->contrast = ctrl->value;
- saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
- break;
-
- case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->sat = ctrl->value;
- saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
- break;
-
- case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->hue = ctrl->value;
- saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
- break;
case V4L2_CID_CHROMA_AGC:
- val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
- state->chroma_agc = ctrl->value;
- if (ctrl->value)
- val &= 0x7f;
- else
- val |= 0x80;
- saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+ /* chroma gain cluster */
+ if (state->agc->cur.val)
+ state->gain->cur.val =
+ saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
break;
- case V4L2_CID_CHROMA_GAIN:
- /* Chroma gain cannot be set when AGC is enabled */
- if (state->chroma_agc == 1)
- return -EINVAL;
- saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
- break;
- default:
- return -EINVAL;
}
-
return 0;
}
-static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->bright;
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
break;
+
case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
break;
+
case V4L2_CID_SATURATION:
- ctrl->value = state->sat;
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
break;
+
case V4L2_CID_HUE:
- ctrl->value = state->hue;
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
break;
+
case V4L2_CID_CHROMA_AGC:
- ctrl->value = state->chroma_agc;
- break;
- case V4L2_CID_CHROMA_GAIN:
- ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+ /* chroma gain cluster */
+ if (state->agc->val)
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
+ else
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+ v4l2_ctrl_activate(state->gain, !state->agc->val);
break;
+
default:
return -EINVAL;
}
@@ -1223,25 +1189,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
}
-static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- case V4L2_CID_CHROMA_AGC:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_CHROMA_GAIN:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
- default:
- return -EINVAL;
- }
-}
-
static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct saa711x_state *state = to_state(sd);
@@ -1518,17 +1465,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
break;
}
v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
+ .s_ctrl = saa711x_s_ctrl,
+ .g_volatile_ctrl = saa711x_g_volatile_ctrl,
+};
+
static const struct v4l2_subdev_core_ops saa711x_core_ops = {
.log_status = saa711x_log_status,
.g_chip_ident = saa711x_g_chip_ident,
- .g_ctrl = saa711x_g_ctrl,
- .s_ctrl = saa711x_s_ctrl,
- .queryctrl = saa711x_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = saa711x_s_std,
.reset = saa711x_reset,
.s_gpio = saa711x_s_gpio,
@@ -1579,8 +1536,9 @@ static int saa711x_probe(struct i2c_client *client,
{
struct saa711x_state *state;
struct v4l2_subdev *sd;
- int i;
- char name[17];
+ struct v4l2_ctrl_handler *hdl;
+ int i;
+ char name[17];
char chip_id;
int autodetect = !id || id->driver_data == 1;
@@ -1619,15 +1577,38 @@ static int saa711x_probe(struct i2c_client *client,
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+ state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
+ state->gain->is_volatile = 1;
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+ state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
+ v4l2_ctrl_cluster(2, &state->agc);
+
state->input = -1;
state->output = SAA7115_IPORT_ON;
state->enable = 1;
state->radio = 0;
- state->bright = 128;
- state->contrast = 64;
- state->hue = 0;
- state->sat = 64;
- state->chroma_agc = 1;
switch (chip_id) {
case '1':
state->ident = V4L2_IDENT_SAA7111;
@@ -1675,6 +1656,7 @@ static int saa711x_probe(struct i2c_client *client,
if (state->ident > V4L2_IDENT_SAA7111A)
saa711x_writeregs(sd, saa7115_init_misc);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
+ v4l2_ctrl_handler_setup(hdl);
v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
@@ -1689,6 +1671,7 @@ static int saa711x_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index 22bfd62..fda005e 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -2,7 +2,7 @@ config VIDEO_SAA7134
tristate "Philips SAA7134 support"
depends on VIDEO_DEV && PCI && I2C && INPUT
select VIDEOBUF_DMA_SG
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select CRC32
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index 78d6995..45f8bfc 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -38,6 +38,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
@@ -55,14 +56,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct saa717x_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
v4l2_std_id std;
int input;
int enable;
int radio;
- int bright;
- int contrast;
- int hue;
- int sat;
int playback;
int audio;
int tuner_audio_mode;
@@ -81,6 +79,11 @@ static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct saa717x_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
/* for audio mode */
@@ -774,29 +777,6 @@ static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
}
-/* write regs to video output level (bright,contrast,hue,sat) */
-static void set_video_output_level_regs(struct v4l2_subdev *sd,
- struct saa717x_state *decoder)
-{
- /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
- saa717x_write(sd, 0x10a, decoder->bright);
-
- /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
- 0h (luminance off) 40: i2c dump
- c0h (-1.0 inverse chrominance)
- 80h (-2.0 inverse chrominance) */
- saa717x_write(sd, 0x10b, decoder->contrast);
-
- /* saturation? 7fh(max)-40h(ITU)-0h(color off)
- c0h (-1.0 inverse chrominance)
- 80h (-2.0 inverse chrominance) */
- saa717x_write(sd, 0x10c, decoder->sat);
-
- /* color hue (phase) control
- 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
- saa717x_write(sd, 0x10d, decoder->hue);
-}
-
/* write regs to set audio volume, bass and treble */
static int set_audio_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
@@ -829,9 +809,9 @@ static int set_audio_regs(struct v4l2_subdev *sd,
saa717x_write(sd, 0x480, val);
- /* bass and treble; go to another function */
/* set bass and treble */
- val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
+ val = decoder->audio_main_bass & 0x1f;
+ val |= (decoder->audio_main_treble & 0x1f) << 5;
saa717x_write(sd, 0x488, val);
return 0;
}
@@ -893,218 +873,55 @@ static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
}
-static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct saa717x_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->bright = ctrl->value;
- v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
- saa717x_write(sd, 0x10a, state->bright);
- break;
-
- case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->contrast = ctrl->value;
- v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
- saa717x_write(sd, 0x10b, state->contrast);
- break;
-
- case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->sat = ctrl->value;
- v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
- saa717x_write(sd, 0x10c, state->sat);
- break;
-
- case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->hue = ctrl->value;
- v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
- saa717x_write(sd, 0x10d, state->hue);
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- state->audio_main_mute = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_VOLUME:
- state->audio_main_volume = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- state->audio_main_balance = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_TREBLE:
- state->audio_main_treble = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_BASS:
- state->audio_main_bass = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->bright;
- break;
+ saa717x_write(sd, 0x10a, ctrl->val);
+ return 0;
case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
- break;
+ saa717x_write(sd, 0x10b, ctrl->val);
+ return 0;
case V4L2_CID_SATURATION:
- ctrl->value = state->sat;
- break;
+ saa717x_write(sd, 0x10c, ctrl->val);
+ return 0;
case V4L2_CID_HUE:
- ctrl->value = state->hue;
- break;
+ saa717x_write(sd, 0x10d, ctrl->val);
+ return 0;
case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->audio_main_mute;
+ state->audio_main_mute = ctrl->val;
break;
case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->audio_main_volume;
+ state->audio_main_volume = ctrl->val;
break;
case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = state->audio_main_balance;
+ state->audio_main_balance = ctrl->val;
break;
case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = state->audio_main_treble;
+ state->audio_main_treble = ctrl->val;
break;
case V4L2_CID_AUDIO_BASS:
- ctrl->value = state->audio_main_bass;
+ state->audio_main_bass = ctrl->val;
break;
default:
- return -EINVAL;
+ return 0;
}
-
+ set_audio_regs(sd, state);
return 0;
}
-static struct v4l2_queryctrl saa717x_qctrl[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- .flags = 0,
- }, {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 64,
- .flags = 0,
- }, {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 64,
- .flags = 0,
- }, {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_VOLUME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 58880,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 32768,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_BASS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Bass",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 32768,
- }, {
- .id = V4L2_CID_AUDIO_TREBLE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Treble",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 32768,
- },
-};
-
static int saa717x_s_video_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
@@ -1158,18 +975,6 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,
return 0;
}
-static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
- if (qc->id && qc->id == saa717x_qctrl[i].id) {
- memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
@@ -1386,17 +1191,34 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
}
+static int saa717x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *state = to_state(sd);
+
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops saa717x_ctrl_ops = {
+ .s_ctrl = saa717x_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops saa717x_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = saa717x_g_register,
.s_register = saa717x_s_register,
#endif
- .queryctrl = saa717x_queryctrl,
- .g_ctrl = saa717x_g_ctrl,
- .s_ctrl = saa717x_s_ctrl,
.s_std = saa717x_s_std,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .log_status = saa717x_log_status,
};
static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
@@ -1432,6 +1254,7 @@ static int saa717x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct saa717x_state *decoder;
+ struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
u8 id = 0;
char *p = "";
@@ -1467,16 +1290,41 @@ static int saa717x_probe(struct i2c_client *client,
p = "saa7171";
v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
client->addr << 1, client->adapter->name);
+
+ hdl = &decoder->hdl;
+ v4l2_ctrl_handler_init(hdl, 9);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 68);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, -16, 15, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(decoder);
+ return err;
+ }
+
decoder->std = V4L2_STD_NTSC;
decoder->input = -1;
decoder->enable = 1;
- /* tune these parameters */
- decoder->bright = 0x80;
- decoder->contrast = 0x44;
- decoder->sat = 0x40;
- decoder->hue = 0x00;
-
/* FIXME!! */
decoder->playback = 0; /* initially capture mode used */
decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
@@ -1487,23 +1335,13 @@ static int saa717x_probe(struct i2c_client *client,
/* set volume, bass and treble */
decoder->audio_main_vol_l = 6;
decoder->audio_main_vol_r = 6;
- decoder->audio_main_bass = 0;
- decoder->audio_main_treble = 0;
- decoder->audio_main_mute = 0;
- decoder->audio_main_balance = 32768;
- /* normalize (24 to -40 (not -84) -> 65535 to 0) */
- decoder->audio_main_volume =
- (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
v4l2_dbg(1, debug, sd, "writing init values\n");
/* FIXME!! */
saa717x_write_regs(sd, reg_init_initialize);
- set_video_output_level_regs(sd, decoder);
- /* set bass,treble to 0db 20041101 K.Ohta */
- decoder->audio_main_bass = 0;
- decoder->audio_main_treble = 0;
- set_audio_regs(sd, decoder);
+
+ v4l2_ctrl_handler_setup(hdl);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
@@ -1515,6 +1353,7 @@ static int saa717x_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index f203293..a499cac 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -779,9 +779,12 @@ static int soc_camera_s_crop(struct file *file, void *fh,
ret = ici->ops->get_crop(icd, &current_crop);
/* Prohibit window size change with initialised buffers */
- if (icf->vb_vidq.bufs[0] && !ret &&
- (a->c.width != current_crop.c.width ||
- a->c.height != current_crop.c.height)) {
+ if (ret < 0) {
+ dev_err(&icd->dev,
+ "S_CROP denied: getting current crop failed\n");
+ } else if (icf->vb_vidq.bufs[0] &&
+ (a->c.width != current_crop.c.width ||
+ a->c.height != current_crop.c.height)) {
dev_err(&icd->dev,
"S_CROP denied: queue initialised and sizes differ\n");
ret = -EBUSY;
diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig
index 2c29ec6..1686ebf 100644
--- a/drivers/media/video/tlg2300/Kconfig
+++ b/drivers/media/video/tlg2300/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_TLG2300
depends on VIDEO_DEV && I2C && INPUT && SND && DVB_CORE
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_IR
+ depends on VIDEO_IR
select VIDEOBUF_VMALLOC
select SND_PCM
select VIDEOBUF_DVB
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index 8085ac3..48f5c76 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -179,7 +179,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = {
/* 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_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
@@ -223,7 +223,7 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = {
/* 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_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
@@ -245,7 +245,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
/* 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_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
@@ -289,7 +289,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
/* 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_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
@@ -311,7 +311,7 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = {
/* 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_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index 5ac37c6..f1fcf97 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -282,19 +282,15 @@ static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
};
unsigned short digit;
int ix, iy;
+ int value;
if ((uvd == NULL) || (frame == NULL))
return;
- if (ch >= '0' && ch <= '9')
- ch -= '0';
- else if (ch >= 'A' && ch <= 'F')
- ch = 10 + (ch - 'A');
- else if (ch >= 'a' && ch <= 'f')
- ch = 10 + (ch - 'a');
- else
+ value = hex_to_bin(ch);
+ if (value < 0)
return;
- digit = digits[ch];
+ digit = digits[value];
for (iy=0; iy < 5; iy++) {
for (ix=0; ix < 3; ix++) {
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 7eaf99b..8bdd940 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -2145,6 +2145,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Miricle 307K */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17dc,
+ .idProduct = 0x0202,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Lenovo Thinkpad SL400/SL500 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 133c78d1..e9928a4 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -78,12 +78,14 @@
*
*/
-void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+ int drop_corrupted)
{
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->mainqueue);
INIT_LIST_HEAD(&queue->irqqueue);
+ queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
queue->type = type;
}
@@ -435,8 +437,10 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
uvc_queue_cancel(queue, 0);
INIT_LIST_HEAD(&queue->mainqueue);
- for (i = 0; i < queue->count; ++i)
+ for (i = 0; i < queue->count; ++i) {
+ queue->buffer[i].error = 0;
queue->buffer[i].state = UVC_BUF_STATE_IDLE;
+ }
queue->flags &= ~UVC_QUEUE_STREAMING;
}
@@ -488,8 +492,8 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *nextbuf;
unsigned long flags;
- if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
- buf->buf.length != buf->buf.bytesused) {
+ if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
+ buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
buf->buf.bytesused = 0;
return buf;
@@ -497,6 +501,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
+ buf->error = 0;
buf->state = UVC_BUF_STATE_DONE;
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 53f3ef4..e27cf0d 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -555,6 +555,9 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
if (urb->iso_frame_desc[i].status < 0) {
uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
"lost (%d).\n", urb->iso_frame_desc[i].status);
+ /* Mark the buffer as faulty. */
+ if (buf != NULL)
+ buf->error = 1;
continue;
}
@@ -579,8 +582,14 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
uvc_video_decode_end(stream, buf, mem,
urb->iso_frame_desc[i].actual_length);
- if (buf->state == UVC_BUF_STATE_READY)
+ if (buf->state == UVC_BUF_STATE_READY) {
+ if (buf->buf.length != buf->buf.bytesused &&
+ !(stream->cur_format->flags &
+ UVC_FMT_FLAG_COMPRESSED))
+ buf->error = 1;
+
buf = uvc_queue_next_buffer(&stream->queue, buf);
+ }
}
}
@@ -1104,7 +1113,7 @@ int uvc_video_init(struct uvc_streaming *stream)
atomic_set(&stream->active, 0);
/* Initialize the video buffers queue. */
- uvc_queue_init(&stream->queue, stream->type);
+ uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
@@ -1197,12 +1206,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
return 0;
}
- if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
- uvc_no_drop_param)
- stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
- else
- stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
-
ret = uvc_queue_enable(&stream->queue, 1);
if (ret < 0)
return ret;
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index ac27245..bdacf3b 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -379,11 +379,12 @@ struct uvc_buffer {
struct list_head queue;
wait_queue_head_t wait;
enum uvc_buffer_state state;
+ unsigned int error;
};
#define UVC_QUEUE_STREAMING (1 << 0)
#define UVC_QUEUE_DISCONNECTED (1 << 1)
-#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
+#define UVC_QUEUE_DROP_CORRUPTED (1 << 2)
struct uvc_video_queue {
enum v4l2_buf_type type;
@@ -562,7 +563,7 @@ extern struct uvc_driver uvc_driver;
/* Video buffers queue management. */
extern void uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type);
+ enum v4l2_buf_type type, int drop_corrupted);
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
unsigned int nbuffers, unsigned int buflength);
extern int uvc_free_buffers(struct uvc_video_queue *queue);
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 4e53b0b..8ee1179 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -62,6 +62,7 @@
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -172,487 +173,17 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
}
EXPORT_SYMBOL(v4l2_ctrl_check);
-/* Returns NULL or a character pointer array containing the menu for
- the given control ID. The pointer array ends with a NULL pointer.
- An empty string signifies a menu entry that is invalid. This allows
- drivers to disable certain options if it is not supported. */
-const char **v4l2_ctrl_get_menu(u32 id)
-{
- static const char *mpeg_audio_sampling_freq[] = {
- "44.1 kHz",
- "48 kHz",
- "32 kHz",
- NULL
- };
- static const char *mpeg_audio_encoding[] = {
- "MPEG-1/2 Layer I",
- "MPEG-1/2 Layer II",
- "MPEG-1/2 Layer III",
- "MPEG-2/4 AAC",
- "AC-3",
- NULL
- };
- static const char *mpeg_audio_l1_bitrate[] = {
- "32 kbps",
- "64 kbps",
- "96 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "288 kbps",
- "320 kbps",
- "352 kbps",
- "384 kbps",
- "416 kbps",
- "448 kbps",
- NULL
- };
- static const char *mpeg_audio_l2_bitrate[] = {
- "32 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- NULL
- };
- static const char *mpeg_audio_l3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- NULL
- };
- static const char *mpeg_audio_ac3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- "448 kbps",
- "512 kbps",
- "576 kbps",
- "640 kbps",
- NULL
- };
- static const char *mpeg_audio_mode[] = {
- "Stereo",
- "Joint Stereo",
- "Dual",
- "Mono",
- NULL
- };
- static const char *mpeg_audio_mode_extension[] = {
- "Bound 4",
- "Bound 8",
- "Bound 12",
- "Bound 16",
- NULL
- };
- static const char *mpeg_audio_emphasis[] = {
- "No Emphasis",
- "50/15 us",
- "CCITT J17",
- NULL
- };
- static const char *mpeg_audio_crc[] = {
- "No CRC",
- "16-bit CRC",
- NULL
- };
- static const char *mpeg_video_encoding[] = {
- "MPEG-1",
- "MPEG-2",
- "MPEG-4 AVC",
- NULL
- };
- static const char *mpeg_video_aspect[] = {
- "1x1",
- "4x3",
- "16x9",
- "2.21x1",
- NULL
- };
- static const char *mpeg_video_bitrate_mode[] = {
- "Variable Bitrate",
- "Constant Bitrate",
- NULL
- };
- static const char *mpeg_stream_type[] = {
- "MPEG-2 Program Stream",
- "MPEG-2 Transport Stream",
- "MPEG-1 System Stream",
- "MPEG-2 DVD-compatible Stream",
- "MPEG-1 VCD-compatible Stream",
- "MPEG-2 SVCD-compatible Stream",
- NULL
- };
- static const char *mpeg_stream_vbi_fmt[] = {
- "No VBI",
- "Private packet, IVTV format",
- NULL
- };
- static const char *camera_power_line_frequency[] = {
- "Disabled",
- "50 Hz",
- "60 Hz",
- NULL
- };
- static const char *camera_exposure_auto[] = {
- "Auto Mode",
- "Manual Mode",
- "Shutter Priority Mode",
- "Aperture Priority Mode",
- NULL
- };
- static const char *colorfx[] = {
- "None",
- "Black & White",
- "Sepia",
- "Negative",
- "Emboss",
- "Sketch",
- "Sky blue",
- "Grass green",
- "Skin whiten",
- "Vivid",
- NULL
- };
- static const char *tune_preemphasis[] = {
- "No preemphasis",
- "50 useconds",
- "75 useconds",
- NULL,
- };
-
- switch (id) {
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- return mpeg_audio_sampling_freq;
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- return mpeg_audio_encoding;
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- return mpeg_audio_l1_bitrate;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- return mpeg_audio_l2_bitrate;
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- return mpeg_audio_l3_bitrate;
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- return mpeg_audio_ac3_bitrate;
- case V4L2_CID_MPEG_AUDIO_MODE:
- return mpeg_audio_mode;
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- return mpeg_audio_mode_extension;
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- return mpeg_audio_emphasis;
- case V4L2_CID_MPEG_AUDIO_CRC:
- return mpeg_audio_crc;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return mpeg_video_encoding;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- return mpeg_video_aspect;
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- return mpeg_video_bitrate_mode;
- case V4L2_CID_MPEG_STREAM_TYPE:
- return mpeg_stream_type;
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- return mpeg_stream_vbi_fmt;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- return camera_power_line_frequency;
- case V4L2_CID_EXPOSURE_AUTO:
- return camera_exposure_auto;
- case V4L2_CID_COLORFX:
- return colorfx;
- case V4L2_CID_TUNE_PREEMPHASIS:
- return tune_preemphasis;
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_menu);
-
-/* Return the control name. */
-const char *v4l2_ctrl_get_name(u32 id)
-{
- switch (id) {
- /* USER controls */
- case V4L2_CID_USER_CLASS: return "User Controls";
- case V4L2_CID_BRIGHTNESS: return "Brightness";
- case V4L2_CID_CONTRAST: return "Contrast";
- case V4L2_CID_SATURATION: return "Saturation";
- case V4L2_CID_HUE: return "Hue";
- case V4L2_CID_AUDIO_VOLUME: return "Volume";
- case V4L2_CID_AUDIO_BALANCE: return "Balance";
- case V4L2_CID_AUDIO_BASS: return "Bass";
- case V4L2_CID_AUDIO_TREBLE: return "Treble";
- case V4L2_CID_AUDIO_MUTE: return "Mute";
- case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
- case V4L2_CID_BLACK_LEVEL: return "Black Level";
- case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
- case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
- case V4L2_CID_RED_BALANCE: return "Red Balance";
- case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
- case V4L2_CID_GAMMA: return "Gamma";
- case V4L2_CID_EXPOSURE: return "Exposure";
- case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
- case V4L2_CID_GAIN: return "Gain";
- case V4L2_CID_HFLIP: return "Horizontal Flip";
- case V4L2_CID_VFLIP: return "Vertical Flip";
- case V4L2_CID_HCENTER: return "Horizontal Center";
- case V4L2_CID_VCENTER: return "Vertical Center";
- case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
- case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
- case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
- case V4L2_CID_SHARPNESS: return "Sharpness";
- case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
- case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
- case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
- case V4L2_CID_COLOR_KILLER: return "Color Killer";
- case V4L2_CID_COLORFX: return "Color Effects";
- case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
- case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
- case V4L2_CID_ROTATE: return "Rotate";
- case V4L2_CID_BG_COLOR: return "Background Color";
-
- /* MPEG controls */
- case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
- case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
- case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
- case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
- case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
- case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
- case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
- case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
- case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
- case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
- case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
- case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
- case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
- case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
- case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
- case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
- case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
- case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
- case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
- case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
- case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
- case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
- case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
-
- /* CAMERA controls */
- case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
- case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
- case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
- case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
- case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
- case V4L2_CID_PAN_RESET: return "Pan, Reset";
- case V4L2_CID_TILT_RESET: return "Tilt, Reset";
- case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
- case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
- case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
- case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
- case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
- case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
- case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
- case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
- case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
- case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
- case V4L2_CID_PRIVACY: return "Privacy";
-
- /* FM Radio Modulator control */
- case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
- case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
- case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
- case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
- case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
- case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
- case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
- case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
- case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
- case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
- case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
- case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
- case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
- case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
-
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_name);
-
/* Fill in a struct v4l2_queryctrl */
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
{
- const char *name = v4l2_ctrl_get_name(qctrl->id);
+ const char *name;
+
+ v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
- qctrl->flags = 0;
if (name == NULL)
return -EINVAL;
- switch (qctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_LOUDNESS:
- case V4L2_CID_AUTO_WHITE_BALANCE:
- case V4L2_CID_AUTOGAIN:
- case V4L2_CID_HFLIP:
- case V4L2_CID_VFLIP:
- case V4L2_CID_HUE_AUTO:
- case V4L2_CID_CHROMA_AGC:
- case V4L2_CID_COLOR_KILLER:
- case V4L2_CID_MPEG_AUDIO_MUTE:
- case V4L2_CID_MPEG_VIDEO_MUTE:
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- case V4L2_CID_MPEG_VIDEO_PULLDOWN:
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
- case V4L2_CID_FOCUS_AUTO:
- case V4L2_CID_PRIVACY:
- case V4L2_CID_AUDIO_LIMITER_ENABLED:
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
- case V4L2_CID_PILOT_TONE_ENABLED:
- qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
- min = 0;
- max = step = 1;
- break;
- case V4L2_CID_PAN_RESET:
- case V4L2_CID_TILT_RESET:
- qctrl->type = V4L2_CTRL_TYPE_BUTTON;
- qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- min = max = step = def = 0;
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- case V4L2_CID_MPEG_AUDIO_CRC:
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_STREAM_TYPE:
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_COLORFX:
- case V4L2_CID_TUNE_PREEMPHASIS:
- qctrl->type = V4L2_CTRL_TYPE_MENU;
- step = 1;
- break;
- case V4L2_CID_RDS_TX_PS_NAME:
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- qctrl->type = V4L2_CTRL_TYPE_STRING;
- break;
- case V4L2_CID_USER_CLASS:
- case V4L2_CID_CAMERA_CLASS:
- case V4L2_CID_MPEG_CLASS:
- case V4L2_CID_FM_TX_CLASS:
- qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
- qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- min = max = step = def = 0;
- break;
- case V4L2_CID_BG_COLOR:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- step = 1;
- min = 0;
- /* Max is calculated as RGB888 that is 2^24 */
- max = 0xFFFFFF;
- break;
- default:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- break;
- }
- switch (qctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_VIDEO_B_FRAMES:
- case V4L2_CID_MPEG_STREAM_TYPE:
- qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- case V4L2_CID_RED_BALANCE:
- case V4L2_CID_BLUE_BALANCE:
- case V4L2_CID_GAMMA:
- case V4L2_CID_SHARPNESS:
- case V4L2_CID_CHROMA_GAIN:
- case V4L2_CID_RDS_TX_DEVIATION:
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
- case V4L2_CID_AUDIO_LIMITER_DEVIATION:
- case V4L2_CID_AUDIO_COMPRESSION_GAIN:
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
- case V4L2_CID_PILOT_TONE_DEVIATION:
- case V4L2_CID_PILOT_TONE_FREQUENCY:
- case V4L2_CID_TUNE_POWER_LEVEL:
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- break;
- case V4L2_CID_PAN_RELATIVE:
- case V4L2_CID_TILT_RELATIVE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_IRIS_RELATIVE:
- case V4L2_CID_ZOOM_RELATIVE:
- qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- break;
- }
qctrl->minimum = min;
qctrl->maximum = max;
qctrl->step = step;
@@ -850,7 +381,8 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
- client = i2c_new_probed_device(adapter, info, probe_addrs);
+ client = i2c_new_probed_device(adapter, info, probe_addrs,
+ NULL);
else
client = i2c_new_device(adapter, info);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index d2f20c2..073f013 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -228,11 +228,6 @@ static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (file->f_op->unlocked_ioctl)
ret = file->f_op->unlocked_ioctl(file, cmd, arg);
- else if (file->f_op->ioctl) {
- lock_kernel();
- ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
- unlock_kernel();
- }
return ret;
}
@@ -973,7 +968,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret = -ENOIOCTLCMD;
- if (!file->f_op->ioctl && !file->f_op->unlocked_ioctl)
+ if (!file->f_op->unlocked_ioctl)
return ret;
switch (cmd) {
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
new file mode 100644
index 0000000..ea8d32c
--- /dev/null
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -0,0 +1,1852 @@
+/*
+ V4L2 controls framework implementation.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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/ctype.h>
+#include <linux/slab.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct ctrl_helper {
+ /* The control corresponding to the v4l2_ext_control ID field. */
+ struct v4l2_ctrl *ctrl;
+ /* Used internally to mark whether this control was already
+ processed. */
+ bool handled;
+};
+
+/* Returns NULL or a character pointer array containing the menu for
+ the given control ID. The pointer array ends with a NULL pointer.
+ An empty string signifies a menu entry that is invalid. This allows
+ drivers to disable certain options if it is not supported. */
+const char **v4l2_ctrl_get_menu(u32 id)
+{
+ static const char *mpeg_audio_sampling_freq[] = {
+ "44.1 kHz",
+ "48 kHz",
+ "32 kHz",
+ NULL
+ };
+ static const char *mpeg_audio_encoding[] = {
+ "MPEG-1/2 Layer I",
+ "MPEG-1/2 Layer II",
+ "MPEG-1/2 Layer III",
+ "MPEG-2/4 AAC",
+ "AC-3",
+ NULL
+ };
+ static const char *mpeg_audio_l1_bitrate[] = {
+ "32 kbps",
+ "64 kbps",
+ "96 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "288 kbps",
+ "320 kbps",
+ "352 kbps",
+ "384 kbps",
+ "416 kbps",
+ "448 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_l2_bitrate[] = {
+ "32 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_l3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_ac3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ "448 kbps",
+ "512 kbps",
+ "576 kbps",
+ "640 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_mode[] = {
+ "Stereo",
+ "Joint Stereo",
+ "Dual",
+ "Mono",
+ NULL
+ };
+ static const char *mpeg_audio_mode_extension[] = {
+ "Bound 4",
+ "Bound 8",
+ "Bound 12",
+ "Bound 16",
+ NULL
+ };
+ static const char *mpeg_audio_emphasis[] = {
+ "No Emphasis",
+ "50/15 us",
+ "CCITT J17",
+ NULL
+ };
+ static const char *mpeg_audio_crc[] = {
+ "No CRC",
+ "16-bit CRC",
+ NULL
+ };
+ static const char *mpeg_video_encoding[] = {
+ "MPEG-1",
+ "MPEG-2",
+ "MPEG-4 AVC",
+ NULL
+ };
+ static const char *mpeg_video_aspect[] = {
+ "1x1",
+ "4x3",
+ "16x9",
+ "2.21x1",
+ NULL
+ };
+ static const char *mpeg_video_bitrate_mode[] = {
+ "Variable Bitrate",
+ "Constant Bitrate",
+ NULL
+ };
+ static const char *mpeg_stream_type[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+ static const char *mpeg_stream_vbi_fmt[] = {
+ "No VBI",
+ "Private packet, IVTV format",
+ NULL
+ };
+ static const char *camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ NULL
+ };
+ static const char *camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
+ static const char *colorfx[] = {
+ "None",
+ "Black & White",
+ "Sepia",
+ "Negative",
+ "Emboss",
+ "Sketch",
+ "Sky blue",
+ "Grass green",
+ "Skin whiten",
+ "Vivid",
+ NULL
+ };
+ static const char *tune_preemphasis[] = {
+ "No preemphasis",
+ "50 useconds",
+ "75 useconds",
+ NULL,
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return mpeg_audio_sampling_freq;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return mpeg_audio_encoding;
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ return mpeg_audio_l1_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return mpeg_audio_l2_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return mpeg_audio_l3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return mpeg_audio_ac3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return mpeg_audio_mode;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ return mpeg_audio_mode_extension;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return mpeg_audio_emphasis;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return mpeg_audio_crc;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return mpeg_video_encoding;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return mpeg_video_aspect;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return mpeg_video_bitrate_mode;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return mpeg_stream_type;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
+ case V4L2_CID_COLORFX:
+ return colorfx;
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ return tune_preemphasis;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+ switch (id) {
+ /* USER controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
+ case V4L2_CID_COLORFX: return "Color Effects";
+ case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
+ case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
+ case V4L2_CID_ROTATE: return "Rotate";
+ case V4L2_CID_BG_COLOR: return "Background Color";
+ case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
+
+ /* MPEG controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+ case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
+ case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+ case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+ case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
+ case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
+ case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
+ case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
+ case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
+ case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
+ case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
+ case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+ case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
+
+ /* CAMERA controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+ case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
+ case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
+
+ /* FM Radio Modulator control */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
+ case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
+ case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
+ case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
+ case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
+ case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+ case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
+ case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
+ case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
+ case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
+ case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = v4l2_ctrl_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
+ case V4L2_CID_CHROMA_AGC:
+ case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_FOCUS_AUTO:
+ case V4L2_CID_PRIVACY:
+ case V4L2_CID_AUDIO_LIMITER_ENABLED:
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ case V4L2_CID_PAN_RESET:
+ case V4L2_CID_TILT_RESET:
+ *type = V4L2_CTRL_TYPE_BUTTON;
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
+ case V4L2_CID_COLORFX:
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ *type = V4L2_CTRL_TYPE_MENU;
+ break;
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ *type = V4L2_CTRL_TYPE_STRING;
+ break;
+ case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
+ case V4L2_CID_MPEG_CLASS:
+ case V4L2_CID_FM_TX_CLASS:
+ *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ /* You can neither read not write these */
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_BG_COLOR:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *step = 1;
+ *min = 0;
+ /* Max is calculated as RGB888 that is 2^24 */
+ *max = 0xFFFFFF;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case V4L2_CID_GAMMA:
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_CHROMA_GAIN:
+ case V4L2_CID_RDS_TX_DEVIATION:
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ case V4L2_CID_PILOT_TONE_FREQUENCY:
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ case V4L2_CID_TILT_RELATIVE:
+ case V4L2_CID_FOCUS_RELATIVE:
+ case V4L2_CID_IRIS_RELATIVE:
+ case V4L2_CID_ZOOM_RELATIVE:
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ break;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+ VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ case V4L2_CTRL_TYPE_STRING:
+ /* Nope, these need v4l2_ext_control */
+ return false;
+ default:
+ return true;
+ }
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->cur.string);
+ if (c->size < len + 1) {
+ c->size = len + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->cur.string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->cur.val64;
+ break;
+ default:
+ c->value = ctrl->cur.val;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ int ret;
+ u32 size;
+
+ ctrl->has_new = 1;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = c->value64;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ size = c->size;
+ if (size == 0)
+ return -ERANGE;
+ if (size > ctrl->maximum + 1)
+ size = ctrl->maximum + 1;
+ ret = copy_from_user(ctrl->string, c->string, size);
+ if (!ret) {
+ char last = ctrl->string[size - 1];
+
+ ctrl->string[size - 1] = 0;
+ /* If the string was longer than ctrl->maximum,
+ then return an error. */
+ if (strlen(ctrl->string) == ctrl->maximum && last)
+ return -ERANGE;
+ }
+ return ret ? -EFAULT : 0;
+ default:
+ ctrl->val = c->value;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->string);
+ if (c->size < len + 1) {
+ c->size = ctrl->maximum + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->val64;
+ break;
+ default:
+ c->value = ctrl->val;
+ break;
+ }
+ return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->cur.string, ctrl->string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->cur.val64 = ctrl->val64;
+ break;
+ default:
+ ctrl->cur.val = ctrl->val;
+ break;
+ }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = ctrl->cur.val64;
+ break;
+ default:
+ ctrl->val = ctrl->cur.val;
+ break;
+ }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+ value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+ int diff = 0;
+ int i;
+
+ for (i = 0; !diff && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ /* Button controls are always 'different' */
+ return 1;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ diff = strcmp(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ diff = ctrl->val64 != ctrl->cur.val64;
+ break;
+ default:
+ diff = ctrl->val != ctrl->cur.val;
+ break;
+ }
+ }
+ return diff;
+}
+
+/* Validate a new control */
+static int validate_new(struct v4l2_ctrl *ctrl)
+{
+ s32 val = ctrl->val;
+ char *s = ctrl->string;
+ u32 offset;
+ size_t len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ /* Round towards the closest legal value */
+ val += ctrl->step / 2;
+ if (val < ctrl->minimum)
+ val = ctrl->minimum;
+ if (val > ctrl->maximum)
+ val = ctrl->maximum;
+ offset = val - ctrl->minimum;
+ offset = ctrl->step * (offset / ctrl->step);
+ val = ctrl->minimum + offset;
+ ctrl->val = val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ ctrl->val = !!ctrl->val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (val < ctrl->minimum || val > ctrl->maximum)
+ return -ERANGE;
+ if (ctrl->qmenu[val][0] == '\0' ||
+ (ctrl->menu_skip_mask & (1 << val)))
+ return -EINVAL;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ ctrl->val64 = 0;
+ return 0;
+
+ case V4L2_CTRL_TYPE_INTEGER64:
+ return 0;
+
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(s);
+ if (len < ctrl->minimum)
+ return -ERANGE;
+ if ((len - ctrl->minimum) % ctrl->step)
+ return -ERANGE;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+ return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+ if (hdl->error == 0)
+ hdl->error = err;
+ return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint)
+{
+ mutex_init(&hdl->lock);
+ INIT_LIST_HEAD(&hdl->ctrls);
+ INIT_LIST_HEAD(&hdl->ctrl_refs);
+ hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+ hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets,
+ GFP_KERNEL);
+ hdl->error = hdl->buckets ? 0 : -ENOMEM;
+ return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl_ref *ref, *next_ref;
+ struct v4l2_ctrl *ctrl, *next_ctrl;
+
+ if (hdl == NULL || hdl->buckets == NULL)
+ return;
+
+ mutex_lock(&hdl->lock);
+ /* Free all nodes */
+ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+ list_del(&ref->node);
+ kfree(ref);
+ }
+ /* Free all controls owned by the handler */
+ list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+ list_del(&ctrl->node);
+ kfree(ctrl);
+ }
+ kfree(hdl->buckets);
+ hdl->buckets = NULL;
+ hdl->cached = NULL;
+ hdl->error = 0;
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+ be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+ with applications that do not use the NEXT_CTRL flag.
+
+ We just find the n-th private user control. It's O(N), but that should not
+ be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+
+ id -= V4L2_CID_PRIVATE_BASE;
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ /* Search for private user controls that are compatible with
+ VIDIOC_G/S_CTRL. */
+ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+ V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
+ if (!type_is_int(ref->ctrl))
+ continue;
+ if (id == 0)
+ return ref;
+ id--;
+ }
+ }
+ return NULL;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+ int bucket;
+
+ id &= V4L2_CTRL_ID_MASK;
+
+ /* Old-style private controls need special handling */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return find_private_ref(hdl, id);
+ bucket = id % hdl->nr_of_buckets;
+
+ /* Simple optimization: cache the last control found */
+ if (hdl->cached && hdl->cached->ctrl->id == id)
+ return hdl->cached;
+
+ /* Not in cache, search the hash */
+ ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+ while (ref && ref->ctrl->id != id)
+ ref = ref->next;
+
+ if (ref)
+ hdl->cached = ref; /* cache it! */
+ return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = NULL;
+
+ if (hdl) {
+ mutex_lock(&hdl->lock);
+ ref = find_ref(hdl, id);
+ mutex_unlock(&hdl->lock);
+ }
+ return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+ return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl_ref *new_ref;
+ u32 id = ctrl->id;
+ u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+ int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
+
+ /* Automatically add the control class if it is not yet present. */
+ if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+ if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+ return hdl->error;
+
+ if (hdl->error)
+ return hdl->error;
+
+ new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+ if (!new_ref)
+ return handler_set_err(hdl, -ENOMEM);
+ new_ref->ctrl = ctrl;
+ if (ctrl->handler == hdl) {
+ /* By default each control starts in a cluster of its own.
+ new_ref->ctrl is basically a cluster array with one
+ element, so that's perfect to use as the cluster pointer.
+ But only do this for the handler that owns the control. */
+ ctrl->cluster = &new_ref->ctrl;
+ ctrl->ncontrols = 1;
+ }
+
+ INIT_LIST_HEAD(&new_ref->node);
+
+ mutex_lock(&hdl->lock);
+
+ /* Add immediately at the end of the list if the list is empty, or if
+ the last element in the list has a lower ID.
+ This ensures that when elements are added in ascending order the
+ insertion is an O(1) operation. */
+ if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+ list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+ goto insert_in_hash;
+ }
+
+ /* Find insert position in sorted list */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ if (ref->ctrl->id < id)
+ continue;
+ /* Don't add duplicates */
+ if (ref->ctrl->id == id) {
+ kfree(new_ref);
+ goto unlock;
+ }
+ list_add(&new_ref->node, ref->node.prev);
+ break;
+ }
+
+insert_in_hash:
+ /* Insert the control node in the hash */
+ new_ref->next = hdl->buckets[bucket];
+ hdl->buckets[bucket] = new_ref;
+
+unlock:
+ mutex_unlock(&hdl->lock);
+ return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, const char *name, enum v4l2_ctrl_type type,
+ s32 min, s32 max, u32 step, s32 def,
+ u32 flags, const char **qmenu, void *priv)
+{
+ struct v4l2_ctrl *ctrl;
+ unsigned sz_extra = 0;
+
+ if (hdl->error)
+ return NULL;
+
+ /* Sanity checks */
+ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+ def < min || def > max || max < min ||
+ (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+ (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+ (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+
+ if (type == V4L2_CTRL_TYPE_BUTTON)
+ flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ else if (type == V4L2_CTRL_TYPE_STRING)
+ sz_extra += 2 * (max + 1);
+
+ ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -ENOMEM);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&ctrl->node);
+ ctrl->handler = hdl;
+ ctrl->ops = ops;
+ ctrl->id = id;
+ ctrl->name = name;
+ ctrl->type = type;
+ ctrl->flags = flags;
+ ctrl->minimum = min;
+ ctrl->maximum = max;
+ ctrl->step = step;
+ ctrl->qmenu = qmenu;
+ ctrl->priv = priv;
+ ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+ ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+ ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+ if (ctrl->minimum)
+ memset(ctrl->cur.string, ' ', ctrl->minimum);
+ }
+ if (handler_new_ref(hdl, ctrl)) {
+ kfree(ctrl);
+ return NULL;
+ }
+ mutex_lock(&hdl->lock);
+ list_add_tail(&ctrl->node, &hdl->ctrls);
+ mutex_unlock(&hdl->lock);
+ return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv)
+{
+ bool is_menu;
+ struct v4l2_ctrl *ctrl;
+ const char *name = cfg->name;
+ const char **qmenu = cfg->qmenu;
+ enum v4l2_ctrl_type type = cfg->type;
+ u32 flags = cfg->flags;
+ s32 min = cfg->min;
+ s32 max = cfg->max;
+ u32 step = cfg->step;
+ s32 def = cfg->def;
+
+ if (name == NULL)
+ v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
+ &def, &flags);
+
+ is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU);
+ if (is_menu)
+ WARN_ON(step);
+ else
+ WARN_ON(cfg->menu_skip_mask);
+ if (is_menu && qmenu == NULL)
+ qmenu = v4l2_ctrl_get_menu(cfg->id);
+
+ ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+ type, min, max,
+ is_menu ? cfg->menu_skip_mask : step,
+ def, flags, qmenu, priv);
+ if (ctrl) {
+ ctrl->is_private = cfg->is_private;
+ ctrl->is_volatile = cfg->is_volatile;
+ }
+ return ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard non-menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type == V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ min, max, step, def, flags, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ const char **qmenu = v4l2_ctrl_get_menu(id);
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 step;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type != V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ 0, max, mask, def, flags, qmenu, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ if (hdl == NULL || hdl->error)
+ return NULL;
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ if (ctrl->handler == hdl)
+ return ctrl;
+ return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
+
+/* Add the controls from another handler to our own. */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ /* Do nothing if either handler is NULL or if they are the same */
+ if (!hdl || !add || hdl == add)
+ return 0;
+ if (hdl->error)
+ return hdl->error;
+ mutex_lock(&add->lock);
+ list_for_each_entry(ctrl, &add->ctrls, node) {
+ /* Skip handler-private controls. */
+ if (ctrl->is_private)
+ continue;
+ ret = handler_new_ref(hdl, ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&add->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+ int i;
+
+ /* The first control is the master control and it must not be NULL */
+ BUG_ON(controls[0] == NULL);
+
+ for (i = 0; i < ncontrols; i++) {
+ if (controls[i]) {
+ controls[i]->cluster = controls;
+ controls[i]->ncontrols = ncontrols;
+ }
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+ if (ctrl == NULL)
+ return;
+
+ if (!active)
+ /* set V4L2_CTRL_FLAG_INACTIVE */
+ set_bit(4, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_INACTIVE */
+ clear_bit(4, &ctrl->flags);
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+ Typically used when streaming starts and you want to grab controls,
+ preventing the user from changing them.
+
+ Just call this and the framework will block any attempts to change
+ these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+ if (ctrl == NULL)
+ return;
+
+ if (grabbed)
+ /* set V4L2_CTRL_FLAG_GRABBED */
+ set_bit(1, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_GRABBED */
+ clear_bit(1, &ctrl->flags);
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+ const char *prefix, const char *colon)
+{
+ int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE;
+ int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED;
+
+ if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+ return;
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return;
+
+ printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ printk(KERN_CONT "%d", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ printk(KERN_CONT "%lld", ctrl->cur.val64);
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ printk(KERN_CONT "%s", ctrl->cur.string);
+ break;
+ default:
+ printk(KERN_CONT "unknown type %d", ctrl->type);
+ break;
+ }
+ if (fl_inact && fl_grabbed)
+ printk(KERN_CONT " (inactive, grabbed)\n");
+ else if (fl_inact)
+ printk(KERN_CONT " (inactive)\n");
+ else if (fl_grabbed)
+ printk(KERN_CONT " (grabbed)\n");
+ else
+ printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix)
+{
+ struct v4l2_ctrl *ctrl;
+ const char *colon = "";
+ int len;
+
+ if (hdl == NULL)
+ return;
+ if (prefix == NULL)
+ prefix = "";
+ len = strlen(prefix);
+ if (len && prefix[len - 1] != ' ')
+ colon = ": ";
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+ log_ctrl(ctrl, prefix, colon);
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ if (hdl == NULL)
+ return 0;
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ ctrl->done = false;
+
+ list_for_each_entry(ctrl, &hdl->ctrls, node) {
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int i;
+
+ /* Skip if this control was already handled by a cluster. */
+ if (ctrl->done)
+ continue;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+
+ /* Skip button controls and read-only controls. */
+ if (ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+ continue;
+ ret = master->ops->s_ctrl(master);
+ if (ret)
+ break;
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->done = true;
+ }
+ mutex_unlock(&hdl->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+ u32 id = qc->id & V4L2_CTRL_ID_MASK;
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hdl->lock);
+
+ /* Try to find it */
+ ref = find_ref(hdl, id);
+
+ if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+ /* Find the next control with ID > qc->id */
+
+ /* Did we reach the end of the control list? */
+ if (id >= node2id(hdl->ctrl_refs.prev)) {
+ ref = NULL; /* Yes, so there is no next control */
+ } else if (ref) {
+ /* We found a control with the given ID, so just get
+ the next one in the list. */
+ ref = list_entry(ref->node.next, typeof(*ref), node);
+ } else {
+ /* No control with the given ID exists, so start
+ searching for the next largest ID. We know there
+ is one, otherwise the first 'if' above would have
+ been true. */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node)
+ if (id < ref->ctrl->id)
+ break;
+ }
+ }
+ mutex_unlock(&hdl->lock);
+ if (!ref)
+ return -EINVAL;
+
+ ctrl = ref->ctrl;
+ memset(qc, 0, sizeof(*qc));
+ qc->id = ctrl->id;
+ strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+ qc->minimum = ctrl->minimum;
+ qc->maximum = ctrl->maximum;
+ qc->default_value = ctrl->default_value;
+ if (qc->type == V4L2_CTRL_TYPE_MENU)
+ qc->step = 1;
+ else
+ qc->step = ctrl->step;
+ qc->flags = ctrl->flags;
+ qc->type = ctrl->type;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_queryctrl);
+
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_subdev_queryctrl);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 i = qm->index;
+
+ ctrl = v4l2_ctrl_find(hdl, qm->id);
+ if (!ctrl)
+ return -EINVAL;
+
+ qm->reserved = 0;
+ /* Sanity checks */
+ if (ctrl->qmenu == NULL ||
+ i < ctrl->minimum || i > ctrl->maximum)
+ return -EINVAL;
+ /* Use mask to see if this menu item should be skipped */
+ if (ctrl->menu_skip_mask & (1 << i))
+ return -EINVAL;
+ /* Empty menu items should also be skipped */
+ if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+ return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+ It is not a fully atomic operation, just best-effort only. After all, if
+ multiple controls have to be set through multiple i2c writes (for example)
+ then some initial writes may succeed while others fail. Thus leaving the
+ system in an inconsistent state. The question is how much effort you are
+ willing to spend on trying to make something atomic that really isn't.
+
+ From the point of view of an application the main requirement is that
+ when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+ error should be returned without actually affecting any controls.
+
+ If all the values are correct, then it is acceptable to just give up
+ in case of low-level errors.
+
+ It is important though that the application can tell when only a partial
+ configuration was done. The way we do that is through the error_idx field
+ of struct v4l2_ext_controls: if that is equal to the count field then no
+ controls were affected. Otherwise all controls before that index were
+ successful in performing their 'get' or 'set' operation, the control at
+ the given index failed, and you don't know what happened with the controls
+ after the failed one. Since if they were part of a control cluster they
+ could have been successfully processed (if a cluster member was encountered
+ at index < error_idx), they could have failed (if a cluster member was at
+ error_idx), or they may not have been processed yet (if the first cluster
+ member appeared after error_idx).
+
+ It is all fairly theoretical, though. In practice all you can do is to
+ bail out. If error_idx == count, then it is an application bug. If
+ error_idx < count then it is only an application bug if the error code was
+ EBUSY. That usually means that something started streaming just when you
+ tried to set the controls. In all other cases it is a driver/hardware
+ problem and all you can do is to retry or bail out.
+
+ Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+ never modifies controls the error_idx is just set to whatever control
+ has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+ Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ bool try)
+{
+ u32 i;
+
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ext_control *c = &cs->controls[i];
+ struct v4l2_ctrl *ctrl;
+ u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+ if (try)
+ cs->error_idx = i;
+
+ if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+ return -EINVAL;
+
+ /* Old-style private controls are not allowed for
+ extended controls */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return -EINVAL;
+ ctrl = v4l2_ctrl_find(hdl, id);
+ if (ctrl == NULL)
+ return -EINVAL;
+ if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+
+ helpers[i].ctrl = ctrl;
+ helpers[i].handled = false;
+ }
+ return 0;
+}
+
+typedef int (*cluster_func)(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl);
+
+/* Walk over all controls in v4l2_ext_controls belonging to the same cluster
+ and call the provided function. */
+static int cluster_walk(unsigned from,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ cluster_func f)
+{
+ struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+ int ret = 0;
+ int i;
+
+ /* Find any controls from the same cluster and call the function */
+ for (i = from; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ if (!helpers[i].handled && ctrl->cluster == cluster)
+ ret = f(&cs->controls[i], ctrl);
+ }
+ return ret;
+}
+
+static void cluster_done(unsigned from,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers)
+{
+ struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+ int i;
+
+ /* Find any controls from the same cluster and mark them as handled */
+ for (i = from; i < cs->count; i++)
+ if (helpers[i].ctrl->cluster == cluster)
+ helpers[i].handled = true;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+ specified control class exists. If that class ID is 0, then it checks
+ whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+ if (ctrl_class == 0)
+ return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+ return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ struct ctrl_helper helper[4];
+ struct ctrl_helper *helpers = helper;
+ int ret;
+ int i;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (helpers == NULL)
+ return -ENOMEM;
+ }
+
+ ret = prepare_ext_ctrls(hdl, cs, helpers, false);
+
+ for (i = 0; !ret && i < cs->count; i++)
+ if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ ret = -EACCES;
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ if (helpers[i].handled)
+ continue;
+
+ cs->error_idx = i;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
+ ret = master->ops->g_volatile_ctrl(master);
+ /* If OK, then copy the current control values to the caller */
+ if (!ret)
+ ret = cluster_walk(i, cs, helpers, cur_to_user);
+ v4l2_ctrl_unlock(master);
+ cluster_done(i, cs, helpers);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ return -EACCES;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
+ ret = master->ops->g_volatile_ctrl(master);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(master);
+ return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+ return get_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ s32 val = 0;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ get_ctrl(ctrl, &val);
+ return val;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+ copied to the current value on a set.
+ Must be called with ctrl->handler->lock held. */
+static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set)
+{
+ bool try = !set;
+ int ret = 0;
+ int i;
+
+ /* Go through the cluster and either validate the new value or
+ (if no new value was set), copy the current value to the new
+ value, ensuring a consistent view for the control ops when
+ called. */
+ for (i = 0; !ret && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+
+ if (ctrl->has_new) {
+ /* Double check this: it may have changed since the
+ last check in try_or_set_ext_ctrls(). */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+
+ /* Validate if required */
+ if (!set)
+ ret = validate_new(ctrl);
+ continue;
+ }
+ /* No new value was set, so copy the current and force
+ a call to try_ctrl later, since the values for the cluster
+ may now have changed and the end result might be invalid. */
+ try = true;
+ cur_to_new(ctrl);
+ }
+
+ /* For larger clusters you have to call try_ctrl again to
+ verify that the controls are still valid after the
+ 'cur_to_new' above. */
+ if (!ret && master->ops->try_ctrl && try)
+ ret = master->ops->try_ctrl(master);
+
+ /* Don't set if there is no change */
+ if (!ret && set && cluster_changed(master)) {
+ ret = master->ops->s_ctrl(master);
+ /* If OK, then make the new values permanent. */
+ if (!ret)
+ for (i = 0; i < master->ncontrols; i++)
+ new_to_cur(master->cluster[i]);
+ }
+ return ret;
+}
+
+/* Try or set controls. */
+static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ bool set)
+{
+ unsigned i, j;
+ int ret = 0;
+
+ cs->error_idx = cs->count;
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ if (!set)
+ cs->error_idx = i;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+ /* This test is also done in try_set_control_cluster() which
+ is called in atomic context, so that has the final say,
+ but it makes sense to do an up-front check as well. Once
+ an error occurs in try_set_control_cluster() some other
+ controls may have been set already and we want to do a
+ best-effort to avoid that. */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ }
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ cs->error_idx = i;
+
+ if (helpers[i].handled)
+ continue;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'has_new' flags of the cluster */
+ for (j = 0; j < master->ncontrols; j++)
+ if (master->cluster[j])
+ master->cluster[j]->has_new = 0;
+
+ /* Copy the new caller-supplied control values.
+ user_to_new() sets 'has_new' to 1. */
+ ret = cluster_walk(i, cs, helpers, user_to_new);
+
+ if (!ret)
+ ret = try_or_set_control_cluster(master, set);
+
+ /* Copy the new values back to userspace. */
+ if (!ret)
+ ret = cluster_walk(i, cs, helpers, new_to_user);
+
+ v4l2_ctrl_unlock(ctrl);
+ cluster_done(i, cs, helpers);
+ }
+ return ret;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ bool set)
+{
+ struct ctrl_helper helper[4];
+ struct ctrl_helper *helpers = helper;
+ int ret;
+ int i;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (!helpers)
+ return -ENOMEM;
+ }
+ ret = prepare_ext_ctrls(hdl, cs, helpers, !set);
+ if (ret)
+ goto free;
+
+ /* First 'try' all controls and abort on error */
+ ret = try_or_set_ext_ctrls(hdl, cs, helpers, false);
+ /* If this is a 'set' operation and the initial 'try' failed,
+ then set error_idx to count to tell the application that no
+ controls changed value yet. */
+ if (set)
+ cs->error_idx = cs->count;
+ if (!ret && set) {
+ /* Reset 'handled' state */
+ for (i = 0; i < cs->count; i++)
+ helpers[i].handled = false;
+ ret = try_or_set_ext_ctrls(hdl, cs, helpers, true);
+ }
+
+free:
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret;
+ int i;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'has_new' flags of the cluster */
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->has_new = 0;
+
+ ctrl->val = *val;
+ ctrl->has_new = 1;
+ ret = try_or_set_control_cluster(master, false);
+ if (!ret)
+ ret = try_or_set_control_cluster(master, true);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(ctrl);
+ return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+
+ return set_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_s_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ return set_ctrl(ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 9e89bf61..cb77197 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -215,28 +216,24 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
return vdev->fops->poll(filp, poll);
}
-static int v4l2_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
+ int ret;
- if (!vdev->fops->ioctl)
- return -ENOTTY;
/* Allow ioctl to continue even if the device was unregistered.
Things like dequeueing buffers might still be useful. */
- return vdev->fops->ioctl(filp, cmd, arg);
-}
-
-static long v4l2_unlocked_ioctl(struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- struct video_device *vdev = video_devdata(filp);
+ if (vdev->fops->unlocked_ioctl) {
+ ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ } else if (vdev->fops->ioctl) {
+ /* TODO: convert all drivers to unlocked_ioctl */
+ lock_kernel();
+ ret = vdev->fops->ioctl(filp, cmd, arg);
+ unlock_kernel();
+ } else
+ ret = -ENOTTY;
- if (!vdev->fops->unlocked_ioctl)
- return -ENOTTY;
- /* Allow ioctl to continue even if the device was unregistered.
- Things like dequeueing buffers might still be useful. */
- return vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ return ret;
}
#ifdef CONFIG_MMU
@@ -307,22 +304,6 @@ static int v4l2_release(struct inode *inode, struct file *filp)
return ret;
}
-static const struct file_operations v4l2_unlocked_fops = {
- .owner = THIS_MODULE,
- .read = v4l2_read,
- .write = v4l2_write,
- .open = v4l2_open,
- .get_unmapped_area = v4l2_get_unmapped_area,
- .mmap = v4l2_mmap,
- .unlocked_ioctl = v4l2_unlocked_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = v4l2_compat_ioctl32,
-#endif
- .release = v4l2_release,
- .poll = v4l2_poll,
- .llseek = no_llseek,
-};
-
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
@@ -330,7 +311,7 @@ static const struct file_operations v4l2_fops = {
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
- .ioctl = v4l2_ioctl,
+ .unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
@@ -447,8 +428,12 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->vfl_type = type;
vdev->cdev = NULL;
- if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
- vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->v4l2_dev) {
+ if (vdev->v4l2_dev->dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ }
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
@@ -521,10 +506,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
ret = -ENOMEM;
goto cleanup;
}
- if (vdev->fops->unlocked_ioctl)
- vdev->cdev->ops = &v4l2_unlocked_fops;
- else
- vdev->cdev->ops = &v4l2_fops;
+ vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = vdev->fops->owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index 5a7dc4a..0b08f96 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -26,6 +26,7 @@
#endif
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
@@ -115,6 +116,8 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister);
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
+ int err;
+
/* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL;
@@ -122,6 +125,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
WARN_ON(sd->v4l2_dev != NULL);
if (!try_module_get(sd->owner))
return -ENODEV;
+ /* This just returns 0 if either of the two args is NULL */
+ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
+ if (err)
+ return err;
sd->v4l2_dev = v4l2_dev;
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 0eeceae..dd9283f 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -26,6 +26,7 @@
#endif
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-chip-ident.h>
@@ -1259,9 +1260,12 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_queryctrl *p = arg;
- if (!ops->vidioc_queryctrl)
+ if (vfd->ctrl_handler)
+ ret = v4l2_queryctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_queryctrl)
+ ret = ops->vidioc_queryctrl(file, fh, p);
+ else
break;
- ret = ops->vidioc_queryctrl(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
"step=%d, default=%d, flags=0x%08x\n",
@@ -1276,7 +1280,9 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_control *p = arg;
- if (ops->vidioc_g_ctrl)
+ if (vfd->ctrl_handler)
+ ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ctrl)
ret = ops->vidioc_g_ctrl(file, fh, p);
else if (ops->vidioc_g_ext_ctrls) {
struct v4l2_ext_controls ctrls;
@@ -1306,11 +1312,16 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
- if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
+ if (!vfd->ctrl_handler &&
+ !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+ if (vfd->ctrl_handler) {
+ ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
+ break;
+ }
if (ops->vidioc_s_ctrl) {
ret = ops->vidioc_s_ctrl(file, fh, p);
break;
@@ -1332,10 +1343,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_g_ext_ctrls)
- break;
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
ret = ops->vidioc_g_ext_ctrls(file, fh, p);
+ else
+ break;
v4l_print_ext_ctrls(cmd, vfd, p, !ret);
break;
}
@@ -1344,10 +1357,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_s_ext_ctrls)
+ if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_s_ext_ctrls(file, fh, p);
break;
}
@@ -1356,10 +1371,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_try_ext_ctrls)
+ if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_try_ext_ctrls(file, fh, p);
break;
}
@@ -1367,9 +1384,12 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_querymenu *p = arg;
- if (!ops->vidioc_querymenu)
+ if (vfd->ctrl_handler)
+ ret = v4l2_querymenu(vfd->ctrl_handler, p);
+ else if (ops->vidioc_querymenu)
+ ret = ops->vidioc_querymenu(file, fh, p);
+ else
break;
- ret = ops->vidioc_querymenu(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
p->id, p->index, p->name);
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index a11b99b..d596554 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -27,11 +27,11 @@
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
-#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("wm8739 driver");
MODULE_AUTHOR("T. Adachi, Hans Verkuil");
@@ -54,12 +54,14 @@ enum {
struct wm8739_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* audio cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *balance;
+ };
u32 clock_freq;
- u8 muted;
- u16 volume;
- u16 balance;
- u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
- u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
};
static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
@@ -67,6 +69,11 @@ static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct wm8739_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd;
+}
+
/* ------------------------------------------------------------------------ */
static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
@@ -89,58 +96,17 @@ static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
return -1;
}
-/* write regs to set audio volume etc */
-static void wm8739_set_audio(struct v4l2_subdev *sd)
-{
- struct wm8739_state *state = to_state(sd);
- u16 mute = state->muted ? 0x80 : 0;
-
- /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
- * Default setting: 0x17 = 0 dB
- */
- wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
- wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
-}
-
-static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct wm8739_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->muted;
- break;
-
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->volume;
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = state->balance;
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct wm8739_state *state = to_state(sd);
unsigned int work_l, work_r;
+ u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+ u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+ u16 mute;
switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- state->muted = ctrl->value;
- break;
-
case V4L2_CID_AUDIO_VOLUME:
- state->volume = ctrl->value;
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- state->balance = ctrl->value;
break;
default:
@@ -148,52 +114,25 @@ static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
}
/* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */
- work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768;
- work_r = (min(state->balance, (u16)32768) * state->volume) / 32768;
+ work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768;
+ work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768;
- state->vol_l = (long)work_l * 31 / 65535;
- state->vol_r = (long)work_r * 31 / 65535;
+ vol_l = (long)work_l * 31 / 65535;
+ vol_r = (long)work_r * 31 / 65535;
/* set audio volume etc. */
- wm8739_set_audio(sd);
+ mute = state->mute->val ? 0x80 : 0;
+
+ /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
+ * Default setting: 0x17 = 0 dB
+ */
+ wm8739_write(sd, R0, (vol_l & 0x1f) | mute);
+ wm8739_write(sd, R1, (vol_r & 0x1f) | mute);
return 0;
}
/* ------------------------------------------------------------------------ */
-static struct v4l2_queryctrl wm8739_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 58880,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }, {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }, {
- .id = V4L2_CID_AUDIO_BALANCE,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }
-};
-
-/* ------------------------------------------------------------------------ */
-
static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
{
struct wm8739_state *state = to_state(sd);
@@ -222,18 +161,6 @@ static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
return 0;
}
-static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
- if (qc->id && qc->id == wm8739_qctrl[i].id) {
- memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
-}
-
static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -246,21 +173,26 @@ static int wm8739_log_status(struct v4l2_subdev *sd)
struct wm8739_state *state = to_state(sd);
v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
- v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f,
- state->muted ? " (muted)" : "");
- v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f,
- state->muted ? " (muted)" : "");
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops wm8739_ctrl_ops = {
+ .s_ctrl = wm8739_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops wm8739_core_ops = {
.log_status = wm8739_log_status,
.g_chip_ident = wm8739_g_chip_ident,
- .queryctrl = wm8739_queryctrl,
- .g_ctrl = wm8739_g_ctrl,
- .s_ctrl = wm8739_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
@@ -289,17 +221,28 @@ static int wm8739_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
- state->vol_l = 0x17; /* 0dB */
- state->vol_r = 0x17; /* 0dB */
- state->muted = 0;
- state->balance = 32768;
- /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
- state->volume = ((long)state->vol_l + 1) * 65535 / 31;
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736);
+ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_cluster(3, &state->volume);
+
state->clock_freq = 48000;
/* Initialize wm8739 */
@@ -318,15 +261,17 @@ static int wm8739_probe(struct i2c_client *client,
/* activate */
wm8739_write(sd, R9, 0x001);
/* set volume/mute */
- wm8739_set_audio(sd);
+ v4l2_ctrl_handler_setup(&state->hdl);
return 0;
}
static int wm8739_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 5c2ba59..23bad3f 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -35,6 +35,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("wm8775 driver");
@@ -53,8 +54,9 @@ enum {
struct wm8775_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *mute;
u8 input; /* Last selected input (0-0xf) */
- u8 muted;
};
static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
@@ -62,6 +64,11 @@ static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct wm8775_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
+}
+
static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -95,7 +102,7 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
return -EINVAL;
}
state->input = input;
- if (state->muted)
+ if (!v4l2_ctrl_g_ctrl(state->mute))
return 0;
wm8775_write(sd, R21, 0x0c0);
wm8775_write(sd, R14, 0x1d4);
@@ -104,29 +111,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
return 0;
}
-static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct wm8775_state *state = to_state(sd);
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
- return 0;
-}
-
-static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct wm8775_state *state = to_state(sd);
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- wm8775_write(sd, R21, 0x0c0);
- wm8775_write(sd, R14, 0x1d4);
- wm8775_write(sd, R15, 0x1d4);
- if (!state->muted)
- wm8775_write(sd, R21, 0x100 + state->input);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ if (!ctrl->val)
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+ }
+ return -EINVAL;
}
static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@@ -140,8 +139,8 @@ static int wm8775_log_status(struct v4l2_subdev *sd)
{
struct wm8775_state *state = to_state(sd);
- v4l2_info(sd, "Input: %d%s\n", state->input,
- state->muted ? " (muted)" : "");
+ v4l2_info(sd, "Input: %d\n", state->input);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
@@ -162,11 +161,20 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
+ .s_ctrl = wm8775_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops wm8775_core_ops = {
.log_status = wm8775_log_status,
.g_chip_ident = wm8775_g_chip_ident,
- .g_ctrl = wm8775_g_ctrl,
- .s_ctrl = wm8775_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
@@ -205,13 +213,24 @@ static int wm8775_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
- state->muted = 0;
+
+ v4l2_ctrl_handler_init(&state->hdl, 1);
+ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
/* Initialize wm8775 */
@@ -248,9 +267,11 @@ static int wm8775_probe(struct i2c_client *client,
static int wm8775_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wm8775_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index 8327e24..d3f1a08 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -18,6 +18,7 @@
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/memstick.h>
#define DRIVER_NAME "mspro_block"
@@ -179,6 +180,7 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode)
struct mspro_block_data *msb = disk->private_data;
int rc = -ENXIO;
+ lock_kernel();
mutex_lock(&mspro_block_disk_lock);
if (msb && msb->card) {
@@ -190,6 +192,7 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode)
}
mutex_unlock(&mspro_block_disk_lock);
+ unlock_kernel();
return rc;
}
@@ -221,7 +224,11 @@ static int mspro_block_disk_release(struct gendisk *disk)
static int mspro_block_bd_release(struct gendisk *disk, fmode_t mode)
{
- return mspro_block_disk_release(disk);
+ int ret;
+ lock_kernel();
+ ret = mspro_block_disk_release(disk);
+ unlock_kernel();
+ return ret;
}
static int mspro_block_bd_getgeo(struct block_device *bdev,
@@ -805,7 +812,8 @@ static void mspro_block_start(struct memstick_dev *card)
static int mspro_block_prepare_req(struct request_queue *q, struct request *req)
{
- if (!blk_fs_request(req) && !blk_pc_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS &&
+ req->cmd_type != REQ_TYPE_BLOCK_PC) {
blk_dump_rq_flags(req, "MSPro unsupported request");
return BLKPREP_KILL;
}
@@ -1040,6 +1048,7 @@ static int mspro_block_read_attributes(struct memstick_dev *card)
snprintf(s_attr->name, sizeof(s_attr->name),
"attr_x%02x", attr->entries[cnt].id);
+ sysfs_attr_init(&s_attr->dev_attr.attr);
s_attr->dev_attr.attr.name = s_attr->name;
s_attr->dev_attr.attr.mode = S_IRUGO;
s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
@@ -1330,13 +1339,14 @@ static void mspro_block_remove(struct memstick_dev *card)
struct mspro_block_data *msb = memstick_get_drvdata(card);
unsigned long flags;
- del_gendisk(msb->disk);
- dev_dbg(&card->dev, "mspro block remove\n");
spin_lock_irqsave(&msb->q_lock, flags);
msb->eject = 1;
blk_start_queue(msb->queue);
spin_unlock_irqrestore(&msb->q_lock, flags);
+ del_gendisk(msb->disk);
+ dev_dbg(&card->dev, "mspro block remove\n");
+
blk_cleanup_queue(msb->queue);
msb->queue = NULL;
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index b88a244..6837a8e 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -50,6 +50,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pci.h>
@@ -109,8 +110,7 @@ MODULE_PARM_DESC(mpt_debug_level, " debug level - refer to mptdebug.h \
int mpt_fwfault_debug;
EXPORT_SYMBOL(mpt_fwfault_debug);
-module_param_call(mpt_fwfault_debug, param_set_int, param_get_int,
- &mpt_fwfault_debug, 0600);
+module_param(mpt_fwfault_debug, int, 0600);
MODULE_PARM_DESC(mpt_fwfault_debug, "Enable detection of Firmware fault"
" and halt Firmware on fault - (default=0)");
@@ -200,12 +200,9 @@ static int mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_valu
static int mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init);
#ifdef CONFIG_PROC_FS
-static int procmpt_summary_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int procmpt_version_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int procmpt_iocinfo_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
+static const struct file_operations mpt_summary_proc_fops;
+static const struct file_operations mpt_version_proc_fops;
+static const struct file_operations mpt_iocinfo_proc_fops;
#endif
static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
@@ -629,6 +626,7 @@ mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
* mpt_register - Register protocol-specific main callback handler.
* @cbfunc: callback function pointer
* @dclass: Protocol driver's class (%MPT_DRIVER_CLASS enum value)
+ * @func_name: call function's name
*
* This routine is called by a protocol-specific driver (SCSI host,
* LAN, SCSI target) to register its reply callback routine. Each
@@ -1725,7 +1723,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
u8 pcixcmd;
static int mpt_ids = 0;
#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *dent, *ent;
+ struct proc_dir_entry *dent;
#endif
ioc = kzalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC);
@@ -1980,16 +1978,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
*/
dent = proc_mkdir(ioc->name, mpt_proc_root_dir);
if (dent) {
- ent = create_proc_entry("info", S_IFREG|S_IRUGO, dent);
- if (ent) {
- ent->read_proc = procmpt_iocinfo_read;
- ent->data = ioc;
- }
- ent = create_proc_entry("summary", S_IFREG|S_IRUGO, dent);
- if (ent) {
- ent->read_proc = procmpt_summary_read;
- ent->data = ioc;
- }
+ proc_create_data("info", S_IRUGO, dent, &mpt_iocinfo_proc_fops, ioc);
+ proc_create_data("summary", S_IRUGO, dent, &mpt_summary_proc_fops, ioc);
}
#endif
@@ -6546,20 +6536,12 @@ mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
static int
procmpt_create(void)
{
- struct proc_dir_entry *ent;
-
mpt_proc_root_dir = proc_mkdir(MPT_PROCFS_MPTBASEDIR, NULL);
if (mpt_proc_root_dir == NULL)
return -ENOTDIR;
- ent = create_proc_entry("summary", S_IFREG|S_IRUGO, mpt_proc_root_dir);
- if (ent)
- ent->read_proc = procmpt_summary_read;
-
- ent = create_proc_entry("version", S_IFREG|S_IRUGO, mpt_proc_root_dir);
- if (ent)
- ent->read_proc = procmpt_version_read;
-
+ proc_create("summary", S_IRUGO, mpt_proc_root_dir, &mpt_summary_proc_fops);
+ proc_create("version", S_IRUGO, mpt_proc_root_dir, &mpt_version_proc_fops);
return 0;
}
@@ -6578,71 +6560,47 @@ procmpt_destroy(void)
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_summary_read - Handle read request of a summary file
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
+/*
* Handles read request from /proc/mpt/summary or /proc/mpt/iocN/summary.
- * Returns number of characters written to process performing the read.
*/
-static int
-procmpt_summary_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
-{
- MPT_ADAPTER *ioc;
- char *out = buf;
- int len;
-
- if (data) {
- int more = 0;
+static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan);
- ioc = data;
- mpt_print_ioc_summary(ioc, out, &more, 0, 1);
+static int mpt_summary_proc_show(struct seq_file *m, void *v)
+{
+ MPT_ADAPTER *ioc = m->private;
- out += more;
+ if (ioc) {
+ seq_mpt_print_ioc_summary(ioc, m, 1);
} else {
list_for_each_entry(ioc, &ioc_list, list) {
- int more = 0;
-
- mpt_print_ioc_summary(ioc, out, &more, 0, 1);
-
- out += more;
- if ((out-buf) >= request)
- break;
+ seq_mpt_print_ioc_summary(ioc, m, 1);
}
}
- len = out - buf;
+ return 0;
+}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+static int mpt_summary_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_summary_proc_show, PDE(inode)->data);
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_version_read - Handle read request from /proc/mpt/version.
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
- * Returns number of characters written to process performing the read.
- */
-static int
-procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+static const struct file_operations mpt_summary_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_summary_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mpt_version_proc_show(struct seq_file *m, void *v)
{
u8 cb_idx;
int scsi, fc, sas, lan, ctl, targ, dmp;
char *drvname;
- int len;
- len = sprintf(buf, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
- len += sprintf(buf+len, " Fusion MPT base driver\n");
+ seq_printf(m, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
+ seq_printf(m, " Fusion MPT base driver\n");
scsi = fc = sas = lan = ctl = targ = dmp = 0;
for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
@@ -6670,98 +6628,97 @@ procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eo
}
if (drvname)
- len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname);
+ seq_printf(m, " Fusion MPT %s driver\n", drvname);
}
}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+ return 0;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_iocinfo_read - Handle read request from /proc/mpt/iocN/info.
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
- * Returns number of characters written to process performing the read.
- */
-static int
-procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+static int mpt_version_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_version_proc_show, NULL);
+}
+
+static const struct file_operations mpt_version_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_version_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mpt_iocinfo_proc_show(struct seq_file *m, void *v)
{
- MPT_ADAPTER *ioc = data;
- int len;
+ MPT_ADAPTER *ioc = m->private;
char expVer[32];
int sz;
int p;
mpt_get_fw_exp_ver(expVer, ioc);
- len = sprintf(buf, "%s:", ioc->name);
+ seq_printf(m, "%s:", ioc->name);
if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT)
- len += sprintf(buf+len, " (f/w download boot flag set)");
+ seq_printf(m, " (f/w download boot flag set)");
// if (ioc->facts.IOCExceptions & MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL)
-// len += sprintf(buf+len, " CONFIG_CHECKSUM_FAIL!");
+// seq_printf(m, " CONFIG_CHECKSUM_FAIL!");
- len += sprintf(buf+len, "\n ProductID = 0x%04x (%s)\n",
+ seq_printf(m, "\n ProductID = 0x%04x (%s)\n",
ioc->facts.ProductID,
ioc->prod_name);
- len += sprintf(buf+len, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
+ seq_printf(m, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
if (ioc->facts.FWImageSize)
- len += sprintf(buf+len, " (fw_size=%d)", ioc->facts.FWImageSize);
- len += sprintf(buf+len, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
- len += sprintf(buf+len, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
- len += sprintf(buf+len, " EventState = 0x%02x\n", ioc->facts.EventState);
+ seq_printf(m, " (fw_size=%d)", ioc->facts.FWImageSize);
+ seq_printf(m, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
+ seq_printf(m, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
+ seq_printf(m, " EventState = 0x%02x\n", ioc->facts.EventState);
- len += sprintf(buf+len, " CurrentHostMfaHighAddr = 0x%08x\n",
+ seq_printf(m, " CurrentHostMfaHighAddr = 0x%08x\n",
ioc->facts.CurrentHostMfaHighAddr);
- len += sprintf(buf+len, " CurrentSenseBufferHighAddr = 0x%08x\n",
+ seq_printf(m, " CurrentSenseBufferHighAddr = 0x%08x\n",
ioc->facts.CurrentSenseBufferHighAddr);
- len += sprintf(buf+len, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
- len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
+ seq_printf(m, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
+ seq_printf(m, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
- len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
+ seq_printf(m, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
(void *)ioc->req_frames, (void *)(ulong)ioc->req_frames_dma);
/*
* Rounding UP to nearest 4-kB boundary here...
*/
sz = (ioc->req_sz * ioc->req_depth) + 128;
sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000;
- len += sprintf(buf+len, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
+ seq_printf(m, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz);
- len += sprintf(buf+len, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
+ seq_printf(m, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
4*ioc->facts.RequestFrameSize,
ioc->facts.GlobalCredits);
- len += sprintf(buf+len, " Frames @ 0x%p (Dma @ 0x%p)\n",
+ seq_printf(m, " Frames @ 0x%p (Dma @ 0x%p)\n",
(void *)ioc->alloc, (void *)(ulong)ioc->alloc_dma);
sz = (ioc->reply_sz * ioc->reply_depth) + 128;
- len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
+ seq_printf(m, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz);
- len += sprintf(buf+len, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
+ seq_printf(m, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
ioc->facts.CurReplyFrameSize,
ioc->facts.ReplyQueueDepth);
- len += sprintf(buf+len, " MaxDevices = %d\n",
+ seq_printf(m, " MaxDevices = %d\n",
(ioc->facts.MaxDevices==0) ? 255 : ioc->facts.MaxDevices);
- len += sprintf(buf+len, " MaxBuses = %d\n", ioc->facts.MaxBuses);
+ seq_printf(m, " MaxBuses = %d\n", ioc->facts.MaxBuses);
/* per-port info */
for (p=0; p < ioc->facts.NumberOfPorts; p++) {
- len += sprintf(buf+len, " PortNumber = %d (of %d)\n",
+ seq_printf(m, " PortNumber = %d (of %d)\n",
p+1,
ioc->facts.NumberOfPorts);
if (ioc->bus_type == FC) {
if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) {
u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
- len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ seq_printf(m, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
a[5], a[4], a[3], a[2], a[1], a[0]);
}
- len += sprintf(buf+len, " WWN = %08X%08X:%08X%08X\n",
+ seq_printf(m, " WWN = %08X%08X:%08X%08X\n",
ioc->fc_port_page0[p].WWNN.High,
ioc->fc_port_page0[p].WWNN.Low,
ioc->fc_port_page0[p].WWPN.High,
@@ -6769,9 +6726,21 @@ procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eo
}
}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+ return 0;
}
+static int mpt_iocinfo_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_iocinfo_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations mpt_iocinfo_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_iocinfo_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif /* CONFIG_PROC_FS } */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -6837,6 +6806,39 @@ mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int sh
*size = y;
}
+
+static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan)
+{
+ char expVer[32];
+
+ mpt_get_fw_exp_ver(expVer, ioc);
+
+ /*
+ * Shorter summary of attached ioc's...
+ */
+ seq_printf(m, "%s: %s, %s%08xh%s, Ports=%d, MaxQ=%d",
+ ioc->name,
+ ioc->prod_name,
+ MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */
+ ioc->facts.FWVersion.Word,
+ expVer,
+ ioc->facts.NumberOfPorts,
+ ioc->req_depth);
+
+ if (showlan && (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN)) {
+ u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
+ seq_printf(m, ", LanAddr=%02X:%02X:%02X:%02X:%02X:%02X",
+ a[5], a[4], a[3], a[2], a[1], a[0]);
+ }
+
+ seq_printf(m, ", IRQ=%d", ioc->pci_irq);
+
+ if (!ioc->active)
+ seq_printf(m, " (disabled)");
+
+ seq_putc(m, '\n');
+}
+
/**
* mpt_set_taskmgmt_in_progress_flag - set flags associated with task management
* @ioc: Pointer to MPT_ADAPTER structure
@@ -6922,7 +6924,6 @@ EXPORT_SYMBOL(mpt_halt_firmware);
* mpt_SoftResetHandler - Issues a less expensive reset
* @ioc: Pointer to MPT_ADAPTER structure
* @sleepFlag: Indicates if sleep or schedule must be called.
-
*
* Returns 0 for SUCCESS or -1 if FAILED.
*
@@ -7067,7 +7068,6 @@ mpt_SoftResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
* mpt_Soft_Hard_ResetHandler - Try less expensive reset
* @ioc: Pointer to MPT_ADAPTER structure
* @sleepFlag: Indicates if sleep or schedule must be called.
-
*
* Returns 0 for SUCCESS or -1 if FAILED.
* Try for softreset first, only if it fails go for expensive
@@ -8004,6 +8004,7 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 log_info)
* mpt_sas_log_info - Log information returned from SAS IOC.
* @ioc: Pointer to MPT_ADAPTER structure
* @log_info: U32 LogInfo reply word from the IOC
+ * @cb_idx: callback function's handle
*
* Refer to lsi/mpi_log_sas.h.
**/
@@ -8050,7 +8051,7 @@ union loginfo_type {
code_desc = ir_code_str[sas_loginfo.dw.code];
if (sas_loginfo.dw.subcode >=
ARRAY_SIZE(raid_sub_code_str))
- break;
+ break;
if (sas_loginfo.dw.code == 0)
sub_code_desc =
raid_sub_code_str[sas_loginfo.dw.subcode];
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 23ed3de..f71f229 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -420,31 +420,6 @@ typedef struct _VirtDevice {
#define MPT_TARGET_FLAGS_LED_ON 0x80
/*
- * /proc/mpt interface
- */
-typedef struct {
- const char *name;
- mode_t mode;
- int pad;
- read_proc_t *read_proc;
- write_proc_t *write_proc;
-} mpt_proc_entry_t;
-
-#define MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len) \
-do { \
- len -= offset; \
- if (len < request) { \
- *eof = 1; \
- if (len <= 0) \
- return 0; \
- } else \
- len = request; \
- *start = buf + offset; \
- return len; \
-} while (0)
-
-
-/*
* IOCTL structure and associated defines
*/
diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c
index 06c655c..a3970e5 100644
--- a/drivers/message/i2o/exec-osm.c
+++ b/drivers/message/i2o/exec-osm.c
@@ -389,12 +389,16 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)
dev = &c->pdev->dev;
if (i2o_dma_realloc(dev, &c->dlct,
- le32_to_cpu(sb->expected_lct_size)))
+ le32_to_cpu(sb->expected_lct_size))) {
+ mutex_unlock(&c->lct_lock);
return -ENOMEM;
+ }
msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
- if (IS_ERR(msg))
+ if (IS_ERR(msg)) {
+ mutex_unlock(&c->lct_lock);
return PTR_ERR(msg);
+ }
msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
msg->u.head[1] = cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index fc593fb..f0f1e66 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -53,6 +53,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2o.h>
+#include <linux/smp_lock.h>
#include <linux/mempool.h>
@@ -577,6 +578,7 @@ static int i2o_block_open(struct block_device *bdev, fmode_t mode)
if (!dev->i2o_dev)
return -ENODEV;
+ lock_kernel();
if (dev->power > 0x1f)
i2o_block_device_power(dev, 0x02);
@@ -585,6 +587,7 @@ static int i2o_block_open(struct block_device *bdev, fmode_t mode)
i2o_block_device_lock(dev->i2o_dev, -1);
osm_debug("Ready.\n");
+ unlock_kernel();
return 0;
};
@@ -615,6 +618,7 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode)
if (!dev->i2o_dev)
return 0;
+ lock_kernel();
i2o_block_device_flush(dev->i2o_dev);
i2o_block_device_unlock(dev->i2o_dev, -1);
@@ -625,6 +629,7 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode)
operation = 0x24;
i2o_block_device_power(dev, operation);
+ unlock_kernel();
return 0;
}
@@ -652,30 +657,40 @@ static int i2o_block_ioctl(struct block_device *bdev, fmode_t mode,
{
struct gendisk *disk = bdev->bd_disk;
struct i2o_block_device *dev = disk->private_data;
+ int ret = -ENOTTY;
/* Anyone capable of this syscall can do *real bad* things */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+ lock_kernel();
switch (cmd) {
case BLKI2OGRSTRAT:
- return put_user(dev->rcache, (int __user *)arg);
+ ret = put_user(dev->rcache, (int __user *)arg);
+ break;
case BLKI2OGWSTRAT:
- return put_user(dev->wcache, (int __user *)arg);
+ ret = put_user(dev->wcache, (int __user *)arg);
+ break;
case BLKI2OSRSTRAT:
+ ret = -EINVAL;
if (arg < 0 || arg > CACHE_SMARTFETCH)
- return -EINVAL;
+ break;
dev->rcache = arg;
+ ret = 0;
break;
case BLKI2OSWSTRAT:
+ ret = -EINVAL;
if (arg != 0
&& (arg < CACHE_WRITETHROUGH || arg > CACHE_SMARTBACK))
- return -EINVAL;
+ break;
dev->wcache = arg;
+ ret = 0;
break;
}
- return -ENOTTY;
+ unlock_kernel();
+
+ return ret;
};
/**
@@ -712,7 +727,7 @@ static int i2o_block_transfer(struct request *req)
{
struct i2o_block_device *dev = req->rq_disk->private_data;
struct i2o_controller *c;
- u32 tid = dev->i2o_dev->lct_data.tid;
+ u32 tid;
struct i2o_message *msg;
u32 *mptr;
struct i2o_block_request *ireq = req->special;
@@ -728,6 +743,7 @@ static int i2o_block_transfer(struct request *req)
goto exit;
}
+ tid = dev->i2o_dev->lct_data.tid;
c = dev->i2o_dev->iop;
msg = i2o_msg_get(c);
@@ -883,7 +899,7 @@ static void i2o_block_request_fn(struct request_queue *q)
if (!req)
break;
- if (blk_fs_request(req)) {
+ if (req->cmd_type == REQ_TYPE_FS) {
struct i2o_block_delayed_request *dreq;
struct i2o_block_request *ireq = req->special;
unsigned int queue_depth;
@@ -930,7 +946,8 @@ static const struct block_device_operations i2o_block_fops = {
.owner = THIS_MODULE,
.open = i2o_block_open,
.release = i2o_block_release,
- .locked_ioctl = i2o_block_ioctl,
+ .ioctl = i2o_block_ioctl,
+ .compat_ioctl = i2o_block_ioctl,
.getgeo = i2o_block_getgeo,
.media_changed = i2o_block_media_changed
};
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index c4b117f..068ba07 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -111,11 +111,11 @@ static int i2o_cfg_gethrt(unsigned long arg)
len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
- /* We did a get user...so assuming mem is ok...is this bad? */
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
- if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
+ else if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
ret = -EFAULT;
return ret;
@@ -147,8 +147,9 @@ static int i2o_cfg_getlct(unsigned long arg)
lct = (i2o_lct *) c->lct;
len = (unsigned int)lct->table_size << 2;
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
else if (copy_to_user(kcmd.resbuf, lct, len))
ret = -EFAULT;
@@ -208,8 +209,9 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
return -EAGAIN;
}
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
else if (copy_to_user(kcmd.resbuf, res, len))
ret = -EFAULT;
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index 3d45817..ea6b219 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -528,7 +528,6 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt,
* Do the incoming paperwork
*/
i2o_dev = SCpnt->device->hostdata;
- c = i2o_dev->iop;
SCpnt->scsi_done = done;
@@ -538,7 +537,7 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt,
done(SCpnt);
goto exit;
}
-
+ c = i2o_dev->iop;
tid = i2o_dev->lct_data.tid;
osm_debug("qcmd: Tid = %03x\n", tid);
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 2c65a2c57..07933f3 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -74,12 +74,12 @@ static struct mfd_cell backlight_devs[] = {
}
static struct resource led_resources[] = {
- PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B),
- PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C),
- PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D),
- PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B),
- PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C),
- PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D),
+ PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B),
+ PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C),
+ PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D),
+ PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B),
+ PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C),
+ PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D),
};
#define PM8606_LED_DEVS(_i) \
@@ -428,52 +428,44 @@ static int __devinit device_gpadc_init(struct pm860x_chip *chip,
{
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
- int use_gpadc = 0, data, ret;
+ int data;
+ int ret;
/* initialize GPADC without activating it */
- if (pdata && pdata->touch) {
- /* set GPADC MISC1 register */
- data = 0;
- data |= (pdata->touch->gpadc_prebias << 1)
- & PM8607_GPADC_PREBIAS_MASK;
- data |= (pdata->touch->slot_cycle << 3)
- & PM8607_GPADC_SLOT_CYCLE_MASK;
- data |= (pdata->touch->off_scale << 5)
- & PM8607_GPADC_OFF_SCALE_MASK;
- data |= (pdata->touch->sw_cal << 7)
- & PM8607_GPADC_SW_CAL_MASK;
- if (data) {
- ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
- if (ret < 0)
- goto out;
- }
- /* set tsi prebias time */
- if (pdata->touch->tsi_prebias) {
- data = pdata->touch->tsi_prebias;
- ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
- if (ret < 0)
- goto out;
- }
- /* set prebias & prechg time of pen detect */
- data = 0;
- data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
- data |= (pdata->touch->pen_prechg << 5)
- & PM8607_PD_PRECHG_MASK;
- if (data) {
- ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
- if (ret < 0)
- goto out;
- }
+ if (!pdata || !pdata->touch)
+ return -EINVAL;
- use_gpadc = 1;
+ /* set GPADC MISC1 register */
+ data = 0;
+ data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
+ data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
+ data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
+ data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
+ if (ret < 0)
+ goto out;
}
-
- /* turn on GPADC */
- if (use_gpadc) {
- ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
- PM8607_GPADC_EN, PM8607_GPADC_EN);
+ /* set tsi prebias time */
+ if (pdata->touch->tsi_prebias) {
+ data = pdata->touch->tsi_prebias;
+ ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
+ if (ret < 0)
+ goto out;
}
+ /* set prebias & prechg time of pen detect */
+ data = 0;
+ data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
+ data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
+ PM8607_GPADC_EN, PM8607_GPADC_EN);
out:
return ret;
}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9da0e50..db51ea1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -7,7 +7,16 @@ menuconfig MFD_SUPPORT
depends on HAS_IOMEM
default y
help
- Configure MFD device drivers.
+ Multifunction devices embed several functions (e.g. GPIOs,
+ touchscreens, keyboards, current regulators, power management chips,
+ etc...) in one single integrated circuit. They usually talk to the
+ main CPU through one or more IRQ lines and low speed data busses (SPI,
+ I2C, etc..). They appear as one single device to the main system
+ through the data bus and the MFD framework allows for sub devices
+ (a.k.a. functions) to appear as discrete platform devices.
+ MFDs are typically found on embedded platforms.
+
+ This option alone does not add any kernel code.
if MFD_SUPPORT
@@ -177,6 +186,38 @@ config TWL4030_CODEC
select MFD_CORE
default n
+config TWL6030_PWM
+ tristate "TWL6030 PWM (Pulse Width Modulator) Support"
+ depends on TWL4030_CORE
+ select HAVE_PWM
+ default n
+ help
+ Say yes here if you want support for TWL6030 PWM.
+ This is used to control charging LED brightness.
+
+config MFD_STMPE
+ bool "Support STMicroelectronics STMPE"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Support for the STMPE family of I/O Expanders from
+ STMicroelectronics.
+
+ Currently supported devices are:
+
+ STMPE811: GPIO, Touchscreen
+ STMPE1601: GPIO, Keypad
+ STMPE2401: GPIO, Keypad
+ STMPE2403: GPIO, Keypad
+
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device. Currently available sub drivers are:
+
+ GPIO: stmpe-gpio
+ Keypad: stmpe-keypad
+ Touchscreen: stmpe-ts
+
config MFD_TC35892
bool "Support Toshiba TC35892"
depends on I2C=y && GENERIC_HARDIRQS
@@ -252,6 +293,16 @@ config MFD_MAX8925
accessing the device, additional drivers must be enabled in order
to use the functionality of the device.
+config MFD_MAX8998
+ bool "Maxim Semiconductor MAX8998 PMIC Support"
+ depends on I2C=y
+ select MFD_CORE
+ help
+ Say yes here to support for Maxim Semiconductor MAX8998. This is
+ a Power Management IC. This driver provies common support for
+ accessing the device, additional drivers must be enabled in order
+ to use the functionality of the device.
+
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
@@ -482,6 +533,28 @@ config MFD_JANZ_CMODIO
host many different types of MODULbus daughterboards, including
CAN and GPIO controllers.
+config MFD_JZ4740_ADC
+ tristate "Support for the JZ4740 SoC ADC core"
+ select MFD_CORE
+ depends on MACH_JZ4740
+ help
+ Say yes here if you want support for the ADC unit in the JZ4740 SoC.
+ This driver is necessary for jz4740-battery and jz4740-hwmon driver.
+
+config MFD_TPS6586X
+ tristate "TPS6586x Power Management chips"
+ depends on I2C && GPIOLIB
+ select MFD_CORE
+ help
+ If you say yes here you get support for the TPS6586X series of
+ Power Management chips.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps6586x.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index fb503e7..feaeeae 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
+obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_MFD_TC35892) += tc35892.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
@@ -36,6 +37,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
+obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
@@ -56,6 +58,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
+obj-$(CONFIG_MFD_MAX8998) += max8998.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
@@ -71,3 +74,5 @@ obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
+obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
+obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index 63d2b72..8440010 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = ab3100_otp_read(otp);
if (err)
- return err;
+ goto err_otp_read;
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
@@ -208,21 +208,21 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = device_create_file(&pdev->dev,
&ab3100_otp_attrs[i]);
if (err)
- goto out_no_sysfs;
+ goto err_create_file;
}
/* debugfs entries */
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
if (err)
- goto out_no_debugfs;
+ goto err_init_debugfs;
return 0;
-out_no_sysfs:
- for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
- device_remove_file(&pdev->dev,
- &ab3100_otp_attrs[i]);
-out_no_debugfs:
+err_init_debugfs:
+err_create_file:
+ while (--i >= 0)
+ device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
+err_otp_read:
kfree(otp);
return err;
}
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
index f54ab62..8a98739 100644
--- a/drivers/mfd/ab3550-core.c
+++ b/drivers/mfd/ab3550-core.c
@@ -589,16 +589,16 @@ static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
}
/*
- * The exported register access functionality.
+ * The register access functionality.
*/
-int ab3550_get_chip_id(struct device *dev)
+static int ab3550_get_chip_id(struct device *dev)
{
struct ab3550 *ab = dev_get_drvdata(dev->parent);
return (int)ab->chip_id;
}
-int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 bitmask, u8 bitvalues)
+static int ab3550_mask_and_set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
{
struct ab3550 *ab;
struct platform_device *pdev = to_platform_device(dev);
@@ -612,15 +612,15 @@ int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
bitmask, bitvalues);
}
-int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 value)
+static int ab3550_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 value)
{
return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
value);
}
-int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 *value)
+static int ab3550_get_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
{
struct ab3550 *ab;
struct platform_device *pdev = to_platform_device(dev);
@@ -633,7 +633,7 @@ int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
return get_register_interruptible(ab, bank, reg, value);
}
-int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
+static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
u8 first_reg, u8 *regvals, u8 numregs)
{
struct ab3550 *ab;
@@ -649,7 +649,8 @@ int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
numregs);
}
-int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
+static int ab3550_event_registers_startup_state_get(struct device *dev,
+ u8 *event)
{
struct ab3550 *ab;
@@ -661,7 +662,7 @@ int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
return 0;
}
-int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
+static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
{
struct ab3550 *ab;
struct ab3550_platform_data *plf_data;
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index f3d26fa..defa786 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ab8500.h>
+#include <linux/regulator/ab8500.h>
/*
* Interrupt register offsets
@@ -352,6 +353,7 @@ static struct mfd_cell ab8500_devs[] = {
{ .name = "ab8500-audio", },
{ .name = "ab8500-usb", },
{ .name = "ab8500-pwm", },
+ { .name = "ab8500-regulator", },
};
int __devinit ab8500_init(struct ab8500 *ab8500)
@@ -411,7 +413,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
goto out_removeirq;
}
- ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs,
+ ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
ab8500->irq_base);
if (ret)
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
index b81d4f7..e1c8b62 100644
--- a/drivers/mfd/ab8500-spi.c
+++ b/drivers/mfd/ab8500-spi.c
@@ -68,7 +68,12 @@ static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
ret = spi_sync(spi, &msg);
if (!ret)
- ret = ab8500->rx_buf[0];
+ /*
+ * Only the 8 lowermost bytes are
+ * defined with value, the rest may
+ * vary depending on chip/board noise.
+ */
+ ret = ab8500->rx_buf[0] & 0xFFU;
return ret;
}
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
index 3b3b97e..f12720d 100644
--- a/drivers/mfd/abx500-core.c
+++ b/drivers/mfd/abx500-core.c
@@ -36,7 +36,7 @@ int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
struct abx500_device_entry *dev_entry;
dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
- if (IS_ERR(dev_entry)) {
+ if (!dev_entry) {
dev_err(dev, "register_ops kzalloc failed");
return -ENOMEM;
}
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index 3e75f02..33c923d 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -94,7 +94,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto fail4;
}
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
@@ -104,7 +105,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto fail4;
}
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index 9ed6307..36a166b 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/mfd/core.h>
#include <linux/mfd/janz.h>
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
new file mode 100644
index 0000000..3ad492c
--- /dev/null
+++ b/drivers/mfd/jz4740-adc.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * JZ4740 SoC ADC driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This driver synchronizes access to the JZ4740 ADC core between the
+ * JZ4740 battery and hwmon drivers.
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/clk.h>
+#include <linux/mfd/core.h>
+
+#include <linux/jz4740-adc.h>
+
+
+#define JZ_REG_ADC_ENABLE 0x00
+#define JZ_REG_ADC_CFG 0x04
+#define JZ_REG_ADC_CTRL 0x08
+#define JZ_REG_ADC_STATUS 0x0c
+
+#define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10
+#define JZ_REG_ADC_BATTERY_BASE 0x1c
+#define JZ_REG_ADC_HWMON_BASE 0x20
+
+#define JZ_ADC_ENABLE_TOUCH BIT(2)
+#define JZ_ADC_ENABLE_BATTERY BIT(1)
+#define JZ_ADC_ENABLE_ADCIN BIT(0)
+
+enum {
+ JZ_ADC_IRQ_ADCIN = 0,
+ JZ_ADC_IRQ_BATTERY,
+ JZ_ADC_IRQ_TOUCH,
+ JZ_ADC_IRQ_PENUP,
+ JZ_ADC_IRQ_PENDOWN,
+};
+
+struct jz4740_adc {
+ struct resource *mem;
+ void __iomem *base;
+
+ int irq;
+ int irq_base;
+
+ struct clk *clk;
+ atomic_t clk_ref;
+
+ spinlock_t lock;
+};
+
+static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq,
+ bool masked)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ irq -= adc->irq_base;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_CTRL);
+ if (masked)
+ val |= BIT(irq);
+ else
+ val &= ~BIT(irq);
+ writeb(val, adc->base + JZ_REG_ADC_CTRL);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void jz4740_adc_irq_mask(unsigned int irq)
+{
+ struct jz4740_adc *adc = get_irq_chip_data(irq);
+ jz4740_adc_irq_set_masked(adc, irq, true);
+}
+
+static void jz4740_adc_irq_unmask(unsigned int irq)
+{
+ struct jz4740_adc *adc = get_irq_chip_data(irq);
+ jz4740_adc_irq_set_masked(adc, irq, false);
+}
+
+static void jz4740_adc_irq_ack(unsigned int irq)
+{
+ struct jz4740_adc *adc = get_irq_chip_data(irq);
+
+ irq -= adc->irq_base;
+ writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
+}
+
+static struct irq_chip jz4740_adc_irq_chip = {
+ .name = "jz4740-adc",
+ .mask = jz4740_adc_irq_mask,
+ .unmask = jz4740_adc_irq_unmask,
+ .ack = jz4740_adc_irq_ack,
+};
+
+static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+ struct jz4740_adc *adc = get_irq_desc_data(desc);
+ uint8_t status;
+ unsigned int i;
+
+ status = readb(adc->base + JZ_REG_ADC_STATUS);
+
+ for (i = 0; i < 5; ++i) {
+ if (status & BIT(i))
+ generic_handle_irq(adc->irq_base + i);
+ }
+}
+
+
+/* Refcounting for the ADC clock is done in here instead of in the clock
+ * framework, because it is the only clock which is shared between multiple
+ * devices and thus is the only clock which needs refcounting */
+static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
+{
+ if (atomic_inc_return(&adc->clk_ref) == 1)
+ clk_enable(adc->clk);
+}
+
+static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
+{
+ if (atomic_dec_return(&adc->clk_ref) == 0)
+ clk_disable(adc->clk);
+}
+
+static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
+ bool enabled)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_ENABLE);
+ if (enabled)
+ val |= BIT(engine);
+ else
+ val &= BIT(engine);
+ writeb(val, adc->base + JZ_REG_ADC_ENABLE);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static int jz4740_adc_cell_enable(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
+
+ jz4740_adc_clk_enable(adc);
+ jz4740_adc_set_enabled(adc, pdev->id, true);
+
+ return 0;
+}
+
+static int jz4740_adc_cell_disable(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
+
+ jz4740_adc_set_enabled(adc, pdev->id, false);
+ jz4740_adc_clk_disable(adc);
+
+ return 0;
+}
+
+int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(dev);
+ unsigned long flags;
+ uint32_t cfg;
+
+ if (!adc)
+ return -ENODEV;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ cfg = readl(adc->base + JZ_REG_ADC_CFG);
+
+ cfg &= ~mask;
+ cfg |= val;
+
+ writel(cfg, adc->base + JZ_REG_ADC_CFG);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(jz4740_adc_set_config);
+
+static struct resource jz4740_hwmon_resources[] = {
+ {
+ .start = JZ_ADC_IRQ_ADCIN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = JZ_REG_ADC_HWMON_BASE,
+ .end = JZ_REG_ADC_HWMON_BASE + 3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource jz4740_battery_resources[] = {
+ {
+ .start = JZ_ADC_IRQ_BATTERY,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = JZ_REG_ADC_BATTERY_BASE,
+ .end = JZ_REG_ADC_BATTERY_BASE + 3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+const struct mfd_cell jz4740_adc_cells[] = {
+ {
+ .id = 0,
+ .name = "jz4740-hwmon",
+ .num_resources = ARRAY_SIZE(jz4740_hwmon_resources),
+ .resources = jz4740_hwmon_resources,
+ .platform_data = (void *)&jz4740_adc_cells[0],
+ .data_size = sizeof(struct mfd_cell),
+
+ .enable = jz4740_adc_cell_enable,
+ .disable = jz4740_adc_cell_disable,
+ },
+ {
+ .id = 1,
+ .name = "jz4740-battery",
+ .num_resources = ARRAY_SIZE(jz4740_battery_resources),
+ .resources = jz4740_battery_resources,
+ .platform_data = (void *)&jz4740_adc_cells[1],
+ .data_size = sizeof(struct mfd_cell),
+
+ .enable = jz4740_adc_cell_enable,
+ .disable = jz4740_adc_cell_disable,
+ },
+};
+
+static int __devinit jz4740_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_adc *adc;
+ struct resource *mem_base;
+ int irq;
+
+ adc = kmalloc(sizeof(*adc), GFP_KERNEL);
+ if (!adc) {
+ dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+ return -ENOMEM;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0) {
+ ret = adc->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+ goto err_free;
+ }
+
+ adc->irq_base = platform_get_irq(pdev, 1);
+ if (adc->irq_base < 0) {
+ ret = adc->irq_base;
+ dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
+ goto err_free;
+ }
+
+ mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_base) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
+ goto err_free;
+ }
+
+ /* Only request the shared registers for the MFD driver */
+ adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
+ pdev->name);
+ if (!adc->mem) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ goto err_free;
+ }
+
+ adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
+ if (!adc->base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ goto err_release_mem_region;
+ }
+
+ adc->clk = clk_get(&pdev->dev, "adc");
+ if (IS_ERR(adc->clk)) {
+ ret = PTR_ERR(adc->clk);
+ dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ spin_lock_init(&adc->lock);
+ atomic_set(&adc->clk_ref, 0);
+
+ platform_set_drvdata(pdev, adc);
+
+ for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
+ set_irq_chip_data(irq, adc);
+ set_irq_chip_and_handler(irq, &jz4740_adc_irq_chip,
+ handle_level_irq);
+ }
+
+ set_irq_data(adc->irq, adc);
+ set_irq_chained_handler(adc->irq, jz4740_adc_irq_demux);
+
+ writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
+ writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
+
+ ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
+ ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base);
+ if (ret < 0)
+ goto err_clk_put;
+
+ return 0;
+
+err_clk_put:
+ clk_put(adc->clk);
+err_iounmap:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(adc->base);
+err_release_mem_region:
+ release_mem_region(adc->mem->start, resource_size(adc->mem));
+err_free:
+ kfree(adc);
+
+ return ret;
+}
+
+static int __devexit jz4740_adc_remove(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+
+ set_irq_data(adc->irq, NULL);
+ set_irq_chained_handler(adc->irq, NULL);
+
+ iounmap(adc->base);
+ release_mem_region(adc->mem->start, resource_size(adc->mem));
+
+ clk_put(adc->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(adc);
+
+ return 0;
+}
+
+struct platform_driver jz4740_adc_driver = {
+ .probe = jz4740_adc_probe,
+ .remove = __devexit_p(jz4740_adc_remove),
+ .driver = {
+ .name = "jz4740-adc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_adc_init(void)
+{
+ return platform_driver_register(&jz4740_adc_driver);
+}
+module_init(jz4740_adc_init);
+
+static void __exit jz4740_adc_exit(void)
+{
+ platform_driver_unregister(&jz4740_adc_driver);
+}
+module_exit(jz4740_adc_exit);
+
+MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-adc");
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index f621bce..04028a9 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -90,6 +90,24 @@ static struct mfd_cell rtc_devs[] = {
},
};
+static struct resource onkey_resources[] = {
+ {
+ .name = "max8925-onkey",
+ .start = MAX8925_IRQ_GPM_SW_3SEC,
+ .end = MAX8925_IRQ_GPM_SW_3SEC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell onkey_devs[] = {
+ {
+ .name = "max8925-onkey",
+ .num_resources = 1,
+ .resources = &onkey_resources[0],
+ .id = -1,
+ },
+};
+
#define MAX8925_REG_RESOURCE(_start, _end) \
{ \
.start = MAX8925_##_start, \
@@ -596,6 +614,15 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
}
+
+ ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+ ARRAY_SIZE(onkey_devs),
+ &onkey_resources[0], 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to add onkey subdev\n");
+ goto out_dev;
+ }
+
if (pdata && pdata->regulator[0]) {
ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
ARRAY_SIZE(regulator_devs),
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
new file mode 100644
index 0000000..73e6f5c
--- /dev/null
+++ b/drivers/mfd/max8998.c
@@ -0,0 +1,158 @@
+/*
+ * max8698.c - mfd core driver for the Maxim 8998
+ *
+ * Copyright (C) 2009-2010 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+static struct mfd_cell max8998_devs[] = {
+ {
+ .name = "max8998-pmic",
+ }
+};
+
+static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest)
+{
+ struct i2c_client *client = max8998->i2c_client;
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ mutex_unlock(&max8998->iolock);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+ *dest = ret;
+ return 0;
+}
+
+static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value)
+{
+ struct i2c_client *client = max8998->i2c_client;
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_write_byte_data(client, reg, value);
+ mutex_unlock(&max8998->iolock);
+ return ret;
+}
+
+static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg,
+ u8 val, u8 mask)
+{
+ struct i2c_client *client = max8998->i2c_client;
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+ u8 old_val = ret & 0xff;
+ u8 new_val = (val & mask) | (old_val & (~mask));
+ ret = i2c_smbus_write_byte_data(client, reg, new_val);
+ if (ret >= 0)
+ ret = 0;
+ }
+ mutex_unlock(&max8998->iolock);
+ return ret;
+}
+
+static int max8998_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max8998_dev *max8998;
+ int ret = 0;
+
+ max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL);
+ if (max8998 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max8998);
+ max8998->dev = &i2c->dev;
+ max8998->i2c_client = i2c;
+ max8998->dev_read = max8998_i2c_device_read;
+ max8998->dev_write = max8998_i2c_device_write;
+ max8998->dev_update = max8998_i2c_device_update;
+ mutex_init(&max8998->iolock);
+
+ ret = mfd_add_devices(max8998->dev, -1,
+ max8998_devs, ARRAY_SIZE(max8998_devs),
+ NULL, 0);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ mfd_remove_devices(max8998->dev);
+ kfree(max8998);
+ return ret;
+}
+
+static int max8998_i2c_remove(struct i2c_client *i2c)
+{
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max8998->dev);
+ kfree(max8998);
+
+ return 0;
+}
+
+static const struct i2c_device_id max8998_i2c_id[] = {
+ { "max8998", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
+
+static struct i2c_driver max8998_i2c_driver = {
+ .driver = {
+ .name = "max8998",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8998_i2c_probe,
+ .remove = max8998_i2c_remove,
+ .id_table = max8998_i2c_id,
+};
+
+static int __init max8998_i2c_init(void)
+{
+ return i2c_add_driver(&max8998_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max8998_i2c_init);
+
+static void __exit max8998_i2c_exit(void)
+{
+ i2c_del_driver(&max8998_i2c_driver);
+}
+module_exit(max8998_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 8998 multi-function core driver");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index fecf38a..6df3498 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -11,9 +11,31 @@
*/
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
+
+struct mc13783 {
+ struct spi_device *spidev;
+ struct mutex lock;
+ int irq;
+ int flags;
+
+ irq_handler_t irqhandler[MC13783_NUM_IRQ];
+ void *irqdata[MC13783_NUM_IRQ];
+
+ /* XXX these should go as platformdata to the regulator subdevice */
+ struct mc13783_regulator_init_data *regulators;
+ int num_regulators;
+};
+
+#define MC13783_REG_REVISION 7
+#define MC13783_REG_ADC_0 43
+#define MC13783_REG_ADC_1 44
+#define MC13783_REG_ADC_2 45
#define MC13783_IRQSTAT0 0
#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
@@ -226,6 +248,12 @@ int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
}
EXPORT_SYMBOL(mc13783_reg_rmw);
+int mc13783_get_flags(struct mc13783 *mc13783)
+{
+ return mc13783->flags;
+}
+EXPORT_SYMBOL(mc13783_get_flags);
+
int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
{
int ret;
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index a3fb4bc..4ba85bb 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -128,6 +128,39 @@
#define MENELAUS_RESERVED14_IRQ 14 /* Reserved */
#define MENELAUS_RESERVED15_IRQ 15 /* Reserved */
+/* VCORE_CTRL1 register */
+#define VCORE_CTRL1_BYP_COMP (1 << 5)
+#define VCORE_CTRL1_HW_NSW (1 << 7)
+
+/* GPIO_CTRL register */
+#define GPIO_CTRL_SLOTSELEN (1 << 5)
+#define GPIO_CTRL_SLPCTLEN (1 << 6)
+#define GPIO1_DIR_INPUT (1 << 0)
+#define GPIO2_DIR_INPUT (1 << 1)
+#define GPIO3_DIR_INPUT (1 << 2)
+
+/* MCT_CTRL1 register */
+#define MCT_CTRL1_S1_CMD_OD (1 << 2)
+#define MCT_CTRL1_S2_CMD_OD (1 << 3)
+
+/* MCT_CTRL2 register */
+#define MCT_CTRL2_VS2_SEL_D0 (1 << 0)
+#define MCT_CTRL2_VS2_SEL_D1 (1 << 1)
+#define MCT_CTRL2_S1CD_BUFEN (1 << 4)
+#define MCT_CTRL2_S2CD_BUFEN (1 << 5)
+#define MCT_CTRL2_S1CD_DBEN (1 << 6)
+#define MCT_CTRL2_S2CD_BEN (1 << 7)
+
+/* MCT_CTRL3 register */
+#define MCT_CTRL3_SLOT1_EN (1 << 0)
+#define MCT_CTRL3_SLOT2_EN (1 << 1)
+#define MCT_CTRL3_S1_AUTO_EN (1 << 2)
+#define MCT_CTRL3_S2_AUTO_EN (1 << 3)
+
+/* MCT_PIN_ST register */
+#define MCT_PIN_ST_S1_CD_ST (1 << 0)
+#define MCT_PIN_ST_S2_CD_ST (1 << 1)
+
static void menelaus_work(struct work_struct *_menelaus);
struct menelaus_chip {
@@ -249,10 +282,10 @@ static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
return;
if (!(reg & 0x1))
- card_mask |= (1 << 0);
+ card_mask |= MCT_PIN_ST_S1_CD_ST;
if (!(reg & 0x2))
- card_mask |= (1 << 1);
+ card_mask |= MCT_PIN_ST_S2_CD_ST;
if (menelaus_hw->mmc_callback)
menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
@@ -277,14 +310,14 @@ int menelaus_set_mmc_opendrain(int slot, int enable)
val = ret;
if (slot == 1) {
if (enable)
- val |= 1 << 2;
+ val |= MCT_CTRL1_S1_CMD_OD;
else
- val &= ~(1 << 2);
+ val &= ~MCT_CTRL1_S1_CMD_OD;
} else {
if (enable)
- val |= 1 << 3;
+ val |= MCT_CTRL1_S2_CMD_OD;
else
- val &= ~(1 << 3);
+ val &= ~MCT_CTRL1_S2_CMD_OD;
}
ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
mutex_unlock(&the_menelaus->lock);
@@ -301,11 +334,11 @@ int menelaus_set_slot_sel(int enable)
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
if (ret < 0)
goto out;
- ret |= 0x02;
+ ret |= GPIO2_DIR_INPUT;
if (enable)
- ret |= 1 << 5;
+ ret |= GPIO_CTRL_SLOTSELEN;
else
- ret &= ~(1 << 5);
+ ret &= ~GPIO_CTRL_SLOTSELEN;
ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
out:
mutex_unlock(&the_menelaus->lock);
@@ -330,14 +363,14 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
val = ret;
if (slot == 1) {
if (cd_en)
- val |= (1 << 4) | (1 << 6);
+ val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN;
else
- val &= ~((1 << 4) | (1 << 6));
+ val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN);
} else {
if (cd_en)
- val |= (1 << 5) | (1 << 7);
+ val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN;
else
- val &= ~((1 << 5) | (1 << 7));
+ val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN);
}
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
if (ret < 0)
@@ -349,25 +382,25 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
val = ret;
if (slot == 1) {
if (enable)
- val |= 1 << 0;
+ val |= MCT_CTRL3_SLOT1_EN;
else
- val &= ~(1 << 0);
+ val &= ~MCT_CTRL3_SLOT1_EN;
} else {
int b;
if (enable)
- ret |= 1 << 1;
+ val |= MCT_CTRL3_SLOT2_EN;
else
- ret &= ~(1 << 1);
+ val &= ~MCT_CTRL3_SLOT2_EN;
b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
- b &= ~0x03;
+ b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1);
b |= power;
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
if (ret < 0)
goto out;
}
/* Disable autonomous shutdown */
- val &= ~(0x03 << 2);
+ val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN);
ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
out:
mutex_unlock(&the_menelaus->lock);
@@ -552,7 +585,7 @@ int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
if (!the_menelaus->vcore_hw_mode) {
val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
/* HW mode, turn OFF byte comparator */
- val |= ((1 << 7) | (1 << 5));
+ val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP);
ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
the_menelaus->vcore_hw_mode = 1;
}
@@ -749,7 +782,7 @@ int menelaus_set_regulator_sleep(int enable, u32 val)
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
if (ret < 0)
goto out;
- t = ((1 << 6) | 0x04);
+ t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT);
if (enable)
ret |= t;
else
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 7dd76bc..1823a57 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -70,7 +70,9 @@ static int mfd_add_device(struct device *parent, int id,
goto fail_res;
}
- platform_device_add_resources(pdev, res, cell->num_resources);
+ ret = platform_device_add_resources(pdev, res, cell->num_resources);
+ if (ret)
+ goto fail_res;
ret = platform_device_add(pdev);
if (ret)
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
new file mode 100644
index 0000000..0754c5e9
--- /dev/null
+++ b/drivers/mfd/stmpe.c
@@ -0,0 +1,985 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stmpe.h>
+#include "stmpe.h"
+
+static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+ return stmpe->variant->enable(stmpe, blocks, true);
+}
+
+static int __stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+ return stmpe->variant->enable(stmpe, blocks, false);
+}
+
+static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(stmpe->i2c, reg);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to read reg %#x: %d\n",
+ reg, ret);
+
+ dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret);
+
+ return ret;
+}
+
+static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+ int ret;
+
+ dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val);
+
+ ret = i2c_smbus_write_byte_data(stmpe->i2c, reg, val);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to write reg %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+static int __stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ ret = __stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~mask;
+ ret |= val;
+
+ return __stmpe_reg_write(stmpe, reg, ret);
+}
+
+static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
+ u8 *values)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(stmpe->i2c, reg, length, values);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to read regs %#x: %d\n",
+ reg, ret);
+
+ dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret);
+ stmpe_dump_bytes("stmpe rd: ", values, length);
+
+ return ret;
+}
+
+static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length);
+ stmpe_dump_bytes("stmpe wr: ", values, length);
+
+ ret = i2c_smbus_write_i2c_block_data(stmpe->i2c, reg, length,
+ values);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to write regs %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+/**
+ * stmpe_enable - enable blocks on an STMPE device
+ * @stmpe: Device to work on
+ * @blocks: Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_enable(stmpe, blocks);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_enable);
+
+/**
+ * stmpe_disable - disable blocks on an STMPE device
+ * @stmpe: Device to work on
+ * @blocks: Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_disable(stmpe, blocks);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_disable);
+
+/**
+ * stmpe_reg_read() - read a single STMPE register
+ * @stmpe: Device to read from
+ * @reg: Register to read
+ */
+int stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_reg_read(stmpe, reg);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_read);
+
+/**
+ * stmpe_reg_write() - write a single STMPE register
+ * @stmpe: Device to write to
+ * @reg: Register to write
+ * @val: Value to write
+ */
+int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_reg_write(stmpe, reg, val);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_write);
+
+/**
+ * stmpe_set_bits() - set the value of a bitfield in a STMPE register
+ * @stmpe: Device to write to
+ * @reg: Register to write
+ * @mask: Mask of bits to set
+ * @val: Value to set
+ */
+int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_set_bits(stmpe, reg, mask, val);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_bits);
+
+/**
+ * stmpe_block_read() - read multiple STMPE registers
+ * @stmpe: Device to read from
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Buffer to write to
+ */
+int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_block_read(stmpe, reg, length, values);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_read);
+
+/**
+ * stmpe_block_write() - write multiple STMPE registers
+ * @stmpe: Device to write to
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Values to write
+ */
+int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_block_write(stmpe, reg, length, values);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_write);
+
+/**
+ * stmpe_set_altfunc: set the alternate function for STMPE pins
+ * @stmpe: Device to configure
+ * @pins: Bitmask of pins to affect
+ * @block: block to enable alternate functions for
+ *
+ * @pins is assumed to have a bit set for each of the bits whose alternate
+ * function is to be changed, numbered according to the GPIOXY numbers.
+ *
+ * If the GPIO module is not enabled, this function automatically enables it in
+ * order to perform the change.
+ */
+int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB];
+ int af_bits = variant->af_bits;
+ int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8);
+ int afperreg = 8 / af_bits;
+ int mask = (1 << af_bits) - 1;
+ u8 regs[numregs];
+ int af;
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+
+ ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret < 0)
+ goto out;
+
+ ret = __stmpe_block_read(stmpe, regaddr, numregs, regs);
+ if (ret < 0)
+ goto out;
+
+ af = variant->get_altfunc(stmpe, block);
+
+ while (pins) {
+ int pin = __ffs(pins);
+ int regoffset = numregs - (pin / afperreg) - 1;
+ int pos = (pin % afperreg) * (8 / afperreg);
+
+ regs[regoffset] &= ~(mask << pos);
+ regs[regoffset] |= af << pos;
+
+ pins &= ~(1 << pin);
+ }
+
+ ret = __stmpe_block_write(stmpe, regaddr, numregs, regs);
+
+out:
+ mutex_unlock(&stmpe->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_altfunc);
+
+/*
+ * GPIO (all variants)
+ */
+
+static struct resource stmpe_gpio_resources[] = {
+ /* Start and end filled dynamically */
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_gpio_cell = {
+ .name = "stmpe-gpio",
+ .resources = stmpe_gpio_resources,
+ .num_resources = ARRAY_SIZE(stmpe_gpio_resources),
+};
+
+/*
+ * Keypad (1601, 2401, 2403)
+ */
+
+static struct resource stmpe_keypad_resources[] = {
+ {
+ .name = "KEYPAD",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "KEYPAD_OVER",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_keypad_cell = {
+ .name = "stmpe-keypad",
+ .resources = stmpe_keypad_resources,
+ .num_resources = ARRAY_SIZE(stmpe_keypad_resources),
+};
+
+/*
+ * Touchscreen (STMPE811)
+ */
+
+static struct resource stmpe_ts_resources[] = {
+ {
+ .name = "TOUCH_DET",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "FIFO_TH",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_ts_cell = {
+ .name = "stmpe-ts",
+ .resources = stmpe_ts_resources,
+ .num_resources = ARRAY_SIZE(stmpe_ts_resources),
+};
+
+/*
+ * STMPE811
+ */
+
+static const u8 stmpe811_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL,
+ [STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN,
+ [STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA,
+ [STMPE_IDX_GPMR_LSB] = STMPE811_REG_GPIO_MP_STA,
+ [STMPE_IDX_GPSR_LSB] = STMPE811_REG_GPIO_SET_PIN,
+ [STMPE_IDX_GPCR_LSB] = STMPE811_REG_GPIO_CLR_PIN,
+ [STMPE_IDX_GPDR_LSB] = STMPE811_REG_GPIO_DIR,
+ [STMPE_IDX_GPRER_LSB] = STMPE811_REG_GPIO_RE,
+ [STMPE_IDX_GPFER_LSB] = STMPE811_REG_GPIO_FE,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA,
+ [STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED,
+};
+
+static struct stmpe_variant_block stmpe811_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE811_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_ts_cell,
+ .irq = STMPE811_IRQ_TOUCH_DET,
+ .block = STMPE_BLOCK_TOUCHSCREEN,
+ },
+};
+
+static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE811_SYS_CTRL2_GPIO_OFF;
+
+ if (blocks & STMPE_BLOCK_ADC)
+ mask |= STMPE811_SYS_CTRL2_ADC_OFF;
+
+ if (blocks & STMPE_BLOCK_TOUCHSCREEN)
+ mask |= STMPE811_SYS_CTRL2_TSC_OFF;
+
+ return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask,
+ enable ? 0 : mask);
+}
+
+static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ /* 0 for touchscreen, 1 for GPIO */
+ return block != STMPE_BLOCK_TOUCHSCREEN;
+}
+
+static struct stmpe_variant_info stmpe811 = {
+ .name = "stmpe811",
+ .id_val = 0x0811,
+ .id_mask = 0xffff,
+ .num_gpios = 8,
+ .af_bits = 1,
+ .regs = stmpe811_regs,
+ .blocks = stmpe811_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe811_blocks),
+ .num_irqs = STMPE811_NR_INTERNAL_IRQS,
+ .enable = stmpe811_enable,
+ .get_altfunc = stmpe811_get_altfunc,
+};
+
+/*
+ * STMPE1601
+ */
+
+static const u8 stmpe1601_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB,
+ [STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB,
+ [STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB,
+ [STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB,
+ [STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB,
+ [STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB,
+ [STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB,
+ [STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB,
+};
+
+static struct stmpe_variant_block stmpe1601_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE24XX_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE24XX_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+/* supported autosleep timeout delay (in msecs) */
+static const int stmpe_autosleep_delay[] = {
+ 4, 16, 32, 64, 128, 256, 512, 1024,
+};
+
+static int stmpe_round_timeout(int timeout)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stmpe_autosleep_delay); i++) {
+ if (stmpe_autosleep_delay[i] >= timeout)
+ return i;
+ }
+
+ /*
+ * requests for delays longer than supported should not return the
+ * longest supported delay
+ */
+ return -EINVAL;
+}
+
+static int stmpe_autosleep(struct stmpe *stmpe, int autosleep_timeout)
+{
+ int ret;
+
+ if (!stmpe->variant->enable_autosleep)
+ return -ENOSYS;
+
+ mutex_lock(&stmpe->lock);
+ ret = stmpe->variant->enable_autosleep(stmpe, autosleep_timeout);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+
+/*
+ * Both stmpe 1601/2403 support same layout for autosleep
+ */
+static int stmpe1601_autosleep(struct stmpe *stmpe,
+ int autosleep_timeout)
+{
+ int ret, timeout;
+
+ /* choose the best available timeout */
+ timeout = stmpe_round_timeout(autosleep_timeout);
+ if (timeout < 0) {
+ dev_err(stmpe->dev, "invalid timeout\n");
+ return timeout;
+ }
+
+ ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ STMPE1601_AUTOSLEEP_TIMEOUT_MASK,
+ timeout);
+ if (ret < 0)
+ return ret;
+
+ return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ STPME1601_AUTOSLEEP_ENABLE,
+ STPME1601_AUTOSLEEP_ENABLE);
+}
+
+static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe1601_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ switch (block) {
+ case STMPE_BLOCK_PWM:
+ return 2;
+
+ case STMPE_BLOCK_KEYPAD:
+ return 1;
+
+ case STMPE_BLOCK_GPIO:
+ default:
+ return 0;
+ }
+}
+
+static struct stmpe_variant_info stmpe1601 = {
+ .name = "stmpe1601",
+ .id_val = 0x0210,
+ .id_mask = 0xfff0, /* at least 0x0210 and 0x0212 */
+ .num_gpios = 16,
+ .af_bits = 2,
+ .regs = stmpe1601_regs,
+ .blocks = stmpe1601_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe1601_blocks),
+ .num_irqs = STMPE1601_NR_INTERNAL_IRQS,
+ .enable = stmpe1601_enable,
+ .get_altfunc = stmpe1601_get_altfunc,
+ .enable_autosleep = stmpe1601_autosleep,
+};
+
+/*
+ * STMPE24XX
+ */
+
+static const u8 stmpe24xx_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB,
+ [STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB,
+ [STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB,
+ [STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB,
+ [STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB,
+ [STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB,
+ [STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB,
+ [STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB,
+};
+
+static struct stmpe_variant_block stmpe24xx_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE24XX_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE24XX_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE24XX_SYS_CTRL_ENABLE_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ switch (block) {
+ case STMPE_BLOCK_ROTATOR:
+ return 2;
+
+ case STMPE_BLOCK_KEYPAD:
+ return 1;
+
+ case STMPE_BLOCK_GPIO:
+ default:
+ return 0;
+ }
+}
+
+static struct stmpe_variant_info stmpe2401 = {
+ .name = "stmpe2401",
+ .id_val = 0x0101,
+ .id_mask = 0xffff,
+ .num_gpios = 24,
+ .af_bits = 2,
+ .regs = stmpe24xx_regs,
+ .blocks = stmpe24xx_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
+ .num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
+ .enable = stmpe24xx_enable,
+ .get_altfunc = stmpe24xx_get_altfunc,
+};
+
+static struct stmpe_variant_info stmpe2403 = {
+ .name = "stmpe2403",
+ .id_val = 0x0120,
+ .id_mask = 0xffff,
+ .num_gpios = 24,
+ .af_bits = 2,
+ .regs = stmpe24xx_regs,
+ .blocks = stmpe24xx_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
+ .num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
+ .enable = stmpe24xx_enable,
+ .get_altfunc = stmpe24xx_get_altfunc,
+ .enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */
+};
+
+static struct stmpe_variant_info *stmpe_variant_info[] = {
+ [STMPE811] = &stmpe811,
+ [STMPE1601] = &stmpe1601,
+ [STMPE2401] = &stmpe2401,
+ [STMPE2403] = &stmpe2403,
+};
+
+static irqreturn_t stmpe_irq(int irq, void *data)
+{
+ struct stmpe *stmpe = data;
+ struct stmpe_variant_info *variant = stmpe->variant;
+ int num = DIV_ROUND_UP(variant->num_irqs, 8);
+ u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
+ u8 isr[num];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, israddr, num, isr);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < num; i++) {
+ int bank = num - i - 1;
+ u8 status = isr[i];
+ u8 clear;
+
+ status &= stmpe->ier[bank];
+ if (!status)
+ continue;
+
+ clear = status;
+ while (status) {
+ int bit = __ffs(status);
+ int line = bank * 8 + bit;
+
+ handle_nested_irq(stmpe->irq_base + line);
+ status &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, israddr + i, clear);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void stmpe_irq_lock(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+
+ mutex_lock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_sync_unlock(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ struct stmpe_variant_info *variant = stmpe->variant;
+ int num = DIV_ROUND_UP(variant->num_irqs, 8);
+ int i;
+
+ for (i = 0; i < num; i++) {
+ u8 new = stmpe->ier[i];
+ u8 old = stmpe->oldier[i];
+
+ if (new == old)
+ continue;
+
+ stmpe->oldier[i] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new);
+ }
+
+ mutex_unlock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_mask(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ int offset = irq - stmpe->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe->ier[regoffset] &= ~mask;
+}
+
+static void stmpe_irq_unmask(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ int offset = irq - stmpe->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe->ier[regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_irq_chip = {
+ .name = "stmpe",
+ .bus_lock = stmpe_irq_lock,
+ .bus_sync_unlock = stmpe_irq_sync_unlock,
+ .mask = stmpe_irq_mask,
+ .unmask = stmpe_irq_unmask,
+};
+
+static int __devinit stmpe_irq_init(struct stmpe *stmpe)
+{
+ int num_irqs = stmpe->variant->num_irqs;
+ int base = stmpe->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
+ set_irq_chip_data(irq, stmpe);
+ set_irq_chip_and_handler(irq, &stmpe_irq_chip,
+ handle_edge_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void stmpe_irq_remove(struct stmpe *stmpe)
+{
+ int num_irqs = stmpe->variant->num_irqs;
+ int base = stmpe->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit stmpe_chip_init(struct stmpe *stmpe)
+{
+ unsigned int irq_trigger = stmpe->pdata->irq_trigger;
+ int autosleep_timeout = stmpe->pdata->autosleep_timeout;
+ struct stmpe_variant_info *variant = stmpe->variant;
+ u8 icr = STMPE_ICR_LSB_GIM;
+ unsigned int id;
+ u8 data[2];
+ int ret;
+
+ ret = stmpe_block_read(stmpe, stmpe->regs[STMPE_IDX_CHIP_ID],
+ ARRAY_SIZE(data), data);
+ if (ret < 0)
+ return ret;
+
+ id = (data[0] << 8) | data[1];
+ if ((id & variant->id_mask) != variant->id_val) {
+ dev_err(stmpe->dev, "unknown chip id: %#x\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(stmpe->dev, "%s detected, chip id: %#x\n", variant->name, id);
+
+ /* Disable all modules -- subdrivers should enable what they need. */
+ ret = stmpe_disable(stmpe, ~0);
+ if (ret)
+ return ret;
+
+ if (irq_trigger == IRQF_TRIGGER_FALLING ||
+ irq_trigger == IRQF_TRIGGER_RISING)
+ icr |= STMPE_ICR_LSB_EDGE;
+
+ if (irq_trigger == IRQF_TRIGGER_RISING ||
+ irq_trigger == IRQF_TRIGGER_HIGH)
+ icr |= STMPE_ICR_LSB_HIGH;
+
+ if (stmpe->pdata->irq_invert_polarity)
+ icr ^= STMPE_ICR_LSB_HIGH;
+
+ if (stmpe->pdata->autosleep) {
+ ret = stmpe_autosleep(stmpe, autosleep_timeout);
+ if (ret)
+ return ret;
+ }
+
+ return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);
+}
+
+static int __devinit stmpe_add_device(struct stmpe *stmpe,
+ struct mfd_cell *cell, int irq)
+{
+ return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
+ NULL, stmpe->irq_base + irq);
+}
+
+static int __devinit stmpe_devices_init(struct stmpe *stmpe)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ unsigned int platform_blocks = stmpe->pdata->blocks;
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < variant->num_blocks; i++) {
+ struct stmpe_variant_block *block = &variant->blocks[i];
+
+ if (!(platform_blocks & block->block))
+ continue;
+
+ platform_blocks &= ~block->block;
+ ret = stmpe_add_device(stmpe, block->cell, block->irq);
+ if (ret)
+ return ret;
+ }
+
+ if (platform_blocks)
+ dev_warn(stmpe->dev,
+ "platform wants blocks (%#x) not present on variant",
+ platform_blocks);
+
+ return ret;
+}
+
+static int __devinit stmpe_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct stmpe_platform_data *pdata = i2c->dev.platform_data;
+ struct stmpe *stmpe;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL);
+ if (!stmpe)
+ return -ENOMEM;
+
+ mutex_init(&stmpe->irq_lock);
+ mutex_init(&stmpe->lock);
+
+ stmpe->dev = &i2c->dev;
+ stmpe->i2c = i2c;
+
+ stmpe->pdata = pdata;
+ stmpe->irq_base = pdata->irq_base;
+
+ stmpe->partnum = id->driver_data;
+ stmpe->variant = stmpe_variant_info[stmpe->partnum];
+ stmpe->regs = stmpe->variant->regs;
+ stmpe->num_gpios = stmpe->variant->num_gpios;
+
+ i2c_set_clientdata(i2c, stmpe);
+
+ ret = stmpe_chip_init(stmpe);
+ if (ret)
+ goto out_free;
+
+ ret = stmpe_irq_init(stmpe);
+ if (ret)
+ goto out_free;
+
+ ret = request_threaded_irq(stmpe->i2c->irq, NULL, stmpe_irq,
+ pdata->irq_trigger | IRQF_ONESHOT,
+ "stmpe", stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = stmpe_devices_init(stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to add children\n");
+ goto out_removedevs;
+ }
+
+ return 0;
+
+out_removedevs:
+ mfd_remove_devices(stmpe->dev);
+ free_irq(stmpe->i2c->irq, stmpe);
+out_removeirq:
+ stmpe_irq_remove(stmpe);
+out_free:
+ kfree(stmpe);
+ return ret;
+}
+
+static int __devexit stmpe_remove(struct i2c_client *client)
+{
+ struct stmpe *stmpe = i2c_get_clientdata(client);
+
+ mfd_remove_devices(stmpe->dev);
+
+ free_irq(stmpe->i2c->irq, stmpe);
+ stmpe_irq_remove(stmpe);
+
+ kfree(stmpe);
+
+ return 0;
+}
+
+static const struct i2c_device_id stmpe_id[] = {
+ { "stmpe811", STMPE811 },
+ { "stmpe1601", STMPE1601 },
+ { "stmpe2401", STMPE2401 },
+ { "stmpe2403", STMPE2403 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, stmpe_id);
+
+static struct i2c_driver stmpe_driver = {
+ .driver.name = "stmpe",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_probe,
+ .remove = __devexit_p(stmpe_remove),
+ .id_table = stmpe_id,
+};
+
+static int __init stmpe_init(void)
+{
+ return i2c_add_driver(&stmpe_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+ i2c_del_driver(&stmpe_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD core driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
new file mode 100644
index 0000000..0dbdc4e
--- /dev/null
+++ b/drivers/mfd/stmpe.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#ifndef __STMPE_H
+#define __STMPE_H
+
+#ifdef STMPE_DUMP_BYTES
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+ size_t len)
+{
+ print_hex_dump_bytes(str, DUMP_PREFIX_OFFSET, buf, len);
+}
+#else
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+ size_t len)
+{
+}
+#endif
+
+/**
+ * struct stmpe_variant_block - information about block
+ * @cell: base mfd cell
+ * @irq: interrupt number to be added to each IORESOURCE_IRQ
+ * in the cell
+ * @block: block id; used for identification with platform data and for
+ * enable and altfunc callbacks
+ */
+struct stmpe_variant_block {
+ struct mfd_cell *cell;
+ int irq;
+ enum stmpe_block block;
+};
+
+/**
+ * struct stmpe_variant_info - variant-specific information
+ * @name: part name
+ * @id_val: content of CHIPID register
+ * @id_mask: bits valid in CHIPID register for comparison with id_val
+ * @num_gpios: number of GPIOS
+ * @af_bits: number of bits used to specify the alternate function
+ * @blocks: list of blocks present on this device
+ * @num_blocks: number of blocks present on this device
+ * @num_irqs: number of internal IRQs available on this device
+ * @enable: callback to enable the specified blocks.
+ * Called with the I/O lock held.
+ * @get_altfunc: callback to get the alternate function number for the
+ * specific block
+ * @enable_autosleep: callback to configure autosleep with specified timeout
+ */
+struct stmpe_variant_info {
+ const char *name;
+ u16 id_val;
+ u16 id_mask;
+ int num_gpios;
+ int af_bits;
+ const u8 *regs;
+ struct stmpe_variant_block *blocks;
+ int num_blocks;
+ int num_irqs;
+ int (*enable)(struct stmpe *stmpe, unsigned int blocks, bool enable);
+ int (*get_altfunc)(struct stmpe *stmpe, enum stmpe_block block);
+ int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout);
+};
+
+#define STMPE_ICR_LSB_HIGH (1 << 2)
+#define STMPE_ICR_LSB_EDGE (1 << 1)
+#define STMPE_ICR_LSB_GIM (1 << 0)
+
+/*
+ * STMPE811
+ */
+
+#define STMPE811_IRQ_TOUCH_DET 0
+#define STMPE811_IRQ_FIFO_TH 1
+#define STMPE811_IRQ_FIFO_OFLOW 2
+#define STMPE811_IRQ_FIFO_FULL 3
+#define STMPE811_IRQ_FIFO_EMPTY 4
+#define STMPE811_IRQ_TEMP_SENS 5
+#define STMPE811_IRQ_ADC 6
+#define STMPE811_IRQ_GPIOC 7
+#define STMPE811_NR_INTERNAL_IRQS 8
+
+#define STMPE811_REG_CHIP_ID 0x00
+#define STMPE811_REG_SYS_CTRL2 0x04
+#define STMPE811_REG_INT_CTRL 0x09
+#define STMPE811_REG_INT_EN 0x0A
+#define STMPE811_REG_INT_STA 0x0B
+#define STMPE811_REG_GPIO_INT_EN 0x0C
+#define STMPE811_REG_GPIO_INT_STA 0x0D
+#define STMPE811_REG_GPIO_SET_PIN 0x10
+#define STMPE811_REG_GPIO_CLR_PIN 0x11
+#define STMPE811_REG_GPIO_MP_STA 0x12
+#define STMPE811_REG_GPIO_DIR 0x13
+#define STMPE811_REG_GPIO_ED 0x14
+#define STMPE811_REG_GPIO_RE 0x15
+#define STMPE811_REG_GPIO_FE 0x16
+#define STMPE811_REG_GPIO_AF 0x17
+
+#define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0)
+#define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1)
+#define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2)
+#define STMPE811_SYS_CTRL2_TS_OFF (1 << 3)
+
+/*
+ * STMPE1601
+ */
+
+#define STMPE1601_IRQ_GPIOC 8
+#define STMPE1601_IRQ_PWM3 7
+#define STMPE1601_IRQ_PWM2 6
+#define STMPE1601_IRQ_PWM1 5
+#define STMPE1601_IRQ_PWM0 4
+#define STMPE1601_IRQ_KEYPAD_OVER 2
+#define STMPE1601_IRQ_KEYPAD 1
+#define STMPE1601_IRQ_WAKEUP 0
+#define STMPE1601_NR_INTERNAL_IRQS 9
+
+#define STMPE1601_REG_SYS_CTRL 0x02
+#define STMPE1601_REG_SYS_CTRL2 0x03
+#define STMPE1601_REG_ICR_LSB 0x11
+#define STMPE1601_REG_IER_LSB 0x13
+#define STMPE1601_REG_ISR_MSB 0x14
+#define STMPE1601_REG_CHIP_ID 0x80
+#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17
+#define STMPE1601_REG_INT_STA_GPIO_MSB 0x18
+#define STMPE1601_REG_GPIO_MP_LSB 0x87
+#define STMPE1601_REG_GPIO_SET_LSB 0x83
+#define STMPE1601_REG_GPIO_CLR_LSB 0x85
+#define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89
+#define STMPE1601_REG_GPIO_ED_MSB 0x8A
+#define STMPE1601_REG_GPIO_RE_LSB 0x8D
+#define STMPE1601_REG_GPIO_FE_LSB 0x8F
+#define STMPE1601_REG_GPIO_AF_U_MSB 0x92
+
+#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
+#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1)
+#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0)
+
+/* The 1601/2403 share the same masks */
+#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)
+#define STPME1601_AUTOSLEEP_ENABLE (1 << 3)
+
+/*
+ * STMPE24xx
+ */
+
+#define STMPE24XX_IRQ_GPIOC 8
+#define STMPE24XX_IRQ_PWM2 7
+#define STMPE24XX_IRQ_PWM1 6
+#define STMPE24XX_IRQ_PWM0 5
+#define STMPE24XX_IRQ_ROT_OVER 4
+#define STMPE24XX_IRQ_ROT 3
+#define STMPE24XX_IRQ_KEYPAD_OVER 2
+#define STMPE24XX_IRQ_KEYPAD 1
+#define STMPE24XX_IRQ_WAKEUP 0
+#define STMPE24XX_NR_INTERNAL_IRQS 9
+
+#define STMPE24XX_REG_SYS_CTRL 0x02
+#define STMPE24XX_REG_ICR_LSB 0x11
+#define STMPE24XX_REG_IER_LSB 0x13
+#define STMPE24XX_REG_ISR_MSB 0x14
+#define STMPE24XX_REG_CHIP_ID 0x80
+#define STMPE24XX_REG_IEGPIOR_LSB 0x18
+#define STMPE24XX_REG_ISGPIOR_MSB 0x19
+#define STMPE24XX_REG_GPMR_LSB 0xA5
+#define STMPE24XX_REG_GPSR_LSB 0x85
+#define STMPE24XX_REG_GPCR_LSB 0x88
+#define STMPE24XX_REG_GPDR_LSB 0x8B
+#define STMPE24XX_REG_GPEDR_MSB 0x8C
+#define STMPE24XX_REG_GPRER_LSB 0x91
+#define STMPE24XX_REG_GPFER_LSB 0x94
+#define STMPE24XX_REG_GPAFR_U_MSB 0x9B
+
+#define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3)
+#define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2)
+#define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1)
+#define STMPE24XX_SYSCON_ENABLE_ROT (1 << 0)
+
+#endif
diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
index 5041d33..006c121 100644
--- a/drivers/mfd/t7l66xb.c
+++ b/drivers/mfd/t7l66xb.c
@@ -350,7 +350,6 @@ static int t7l66xb_probe(struct platform_device *dev)
t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
if (IS_ERR(t7l66xb->clk48m)) {
ret = PTR_ERR(t7l66xb->clk48m);
- clk_put(t7l66xb->clk32k);
goto err_clk48m_get;
}
@@ -425,6 +424,8 @@ static int t7l66xb_remove(struct platform_device *dev)
ret = pdata->disable(dev);
clk_disable(t7l66xb->clk48m);
clk_put(t7l66xb->clk48m);
+ clk_disable(t7l66xb->clk32k);
+ clk_put(t7l66xb->clk32k);
t7l66xb_detach_irq(dev);
iounmap(t7l66xb->scr);
release_resource(&t7l66xb->rscr);
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index 517f9bc..6315f63 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -137,7 +137,7 @@ static struct mfd_cell tc6387xb_cells[] = {
},
};
-static int tc6387xb_probe(struct platform_device *dev)
+static int __devinit tc6387xb_probe(struct platform_device *dev)
{
struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
struct resource *iomem, *rscr;
@@ -201,6 +201,7 @@ static int tc6387xb_probe(struct platform_device *dev)
if (!ret)
return 0;
+ iounmap(tc6387xb->scr);
err_ioremap:
release_resource(&tc6387xb->rscr);
err_resource:
@@ -211,14 +212,17 @@ err_no_irq:
return ret;
}
-static int tc6387xb_remove(struct platform_device *dev)
+static int __devexit tc6387xb_remove(struct platform_device *dev)
{
- struct clk *clk32k = platform_get_drvdata(dev);
+ struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
mfd_remove_devices(&dev->dev);
- clk_disable(clk32k);
- clk_put(clk32k);
+ iounmap(tc6387xb->scr);
+ release_resource(&tc6387xb->rscr);
+ clk_disable(tc6387xb->clk32k);
+ clk_put(tc6387xb->clk32k);
platform_set_drvdata(dev, NULL);
+ kfree(tc6387xb);
return 0;
}
@@ -229,7 +233,7 @@ static struct platform_driver tc6387xb_platform_driver = {
.name = "tc6387xb",
},
.probe = tc6387xb_probe,
- .remove = tc6387xb_remove,
+ .remove = __devexit_p(tc6387xb_remove),
.suspend = tc6387xb_suspend,
.resume = tc6387xb_resume,
};
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index fcf9068..ef6c42c 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -732,9 +732,9 @@ err_gpio_add:
if (tc6393xb->gpio.base != -1)
temp = gpiochip_remove(&tc6393xb->gpio);
tcpd->disable(dev);
-err_clk_enable:
- clk_disable(tc6393xb->clk);
err_enable:
+ clk_disable(tc6393xb->clk);
+err_clk_enable:
iounmap(tc6393xb->scr);
err_ioremap:
release_resource(&tc6393xb->rscr);
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index d859dff..fc01976 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -89,10 +89,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
int ret = 0;
tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
- if (tps6507x == NULL) {
- kfree(i2c);
+ if (tps6507x == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(i2c, tps6507x);
tps6507x->dev = &i2c->dev;
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
new file mode 100644
index 0000000..4cde31e
--- /dev/null
+++ b/drivers/mfd/tps6586x.c
@@ -0,0 +1,375 @@
+/*
+ * Core driver for TI TPS6586x PMIC family
+ *
+ * Copyright (c) 2010 CompuLab Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on da903x.c.
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6586x.h>
+
+/* GPIO control registers */
+#define TPS6586X_GPIOSET1 0x5d
+#define TPS6586X_GPIOSET2 0x5e
+
+/* device id */
+#define TPS6586X_VERSIONCRC 0xcd
+#define TPS658621A_VERSIONCRC 0x15
+
+struct tps6586x {
+ struct mutex lock;
+ struct device *dev;
+ struct i2c_client *client;
+
+ struct gpio_chip gpio;
+};
+
+static inline int __tps6586x_read(struct i2c_client *client,
+ int reg, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+ return ret;
+ }
+
+ *val = (uint8_t)ret;
+
+ return 0;
+}
+
+static inline int __tps6586x_reads(struct i2c_client *client, int reg,
+ int len, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int __tps6586x_write(struct i2c_client *client,
+ int reg, uint8_t val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+ val, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int __tps6586x_writes(struct i2c_client *client, int reg,
+ int len, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+int tps6586x_write(struct device *dev, int reg, uint8_t val)
+{
+ return __tps6586x_write(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_write);
+
+int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
+{
+ return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_writes);
+
+int tps6586x_read(struct device *dev, int reg, uint8_t *val)
+{
+ return __tps6586x_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_read);
+
+int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
+{
+ return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_reads);
+
+int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&tps6586x->lock);
+
+ ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if ((reg_val & bit_mask) == 0) {
+ reg_val |= bit_mask;
+ ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
+ }
+out:
+ mutex_unlock(&tps6586x->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_set_bits);
+
+int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&tps6586x->lock);
+
+ ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if (reg_val & bit_mask) {
+ reg_val &= ~bit_mask;
+ ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
+ }
+out:
+ mutex_unlock(&tps6586x->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
+
+int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&tps6586x->lock);
+
+ ret = __tps6586x_read(tps6586x->client, reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if ((reg_val & mask) != val) {
+ reg_val = (reg_val & ~mask) | val;
+ ret = __tps6586x_write(tps6586x->client, reg, reg_val);
+ }
+out:
+ mutex_unlock(&tps6586x->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_update);
+
+static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
+ uint8_t val;
+ int ret;
+
+ ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & (1 << offset));
+}
+
+
+static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
+
+ __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2,
+ value << offset);
+}
+
+static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
+ uint8_t val, mask;
+
+ tps6586x_gpio_set(gc, offset, value);
+
+ val = 0x1 << (offset * 2);
+ mask = 0x3 << (offset * 2);
+
+ return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
+}
+
+static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
+{
+ int ret;
+
+ if (!gpio_base)
+ return;
+
+ tps6586x->gpio.owner = THIS_MODULE;
+ tps6586x->gpio.label = tps6586x->client->name;
+ tps6586x->gpio.dev = tps6586x->dev;
+ tps6586x->gpio.base = gpio_base;
+ tps6586x->gpio.ngpio = 4;
+ tps6586x->gpio.can_sleep = 1;
+
+ /* FIXME: add handling of GPIOs as dedicated inputs */
+ tps6586x->gpio.direction_output = tps6586x_gpio_output;
+ tps6586x->gpio.set = tps6586x_gpio_set;
+ tps6586x->gpio.get = tps6586x_gpio_get;
+
+ ret = gpiochip_add(&tps6586x->gpio);
+ if (ret)
+ dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret);
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
+static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
+{
+ return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
+}
+
+static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
+ struct tps6586x_platform_data *pdata)
+{
+ struct tps6586x_subdev_info *subdev;
+ struct platform_device *pdev;
+ int i, ret = 0;
+
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ subdev = &pdata->subdevs[i];
+
+ pdev = platform_device_alloc(subdev->name, subdev->id);
+
+ pdev->dev.parent = tps6586x->dev;
+ pdev->dev.platform_data = subdev->platform_data;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto failed;
+ }
+ return 0;
+
+failed:
+ tps6586x_remove_subdevs(tps6586x);
+ return ret;
+}
+
+static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps6586x_platform_data *pdata = client->dev.platform_data;
+ struct tps6586x *tps6586x;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&client->dev, "tps6586x requires platform data\n");
+ return -ENOTSUPP;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
+ if (ret < 0) {
+ dev_err(&client->dev, "Chip ID read failed: %d\n", ret);
+ return -EIO;
+ }
+
+ if (ret != TPS658621A_VERSIONCRC) {
+ dev_err(&client->dev, "Unsupported chip ID: %x\n", ret);
+ return -ENODEV;
+ }
+
+ tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
+ if (tps6586x == NULL)
+ return -ENOMEM;
+
+ tps6586x->client = client;
+ tps6586x->dev = &client->dev;
+ i2c_set_clientdata(client, tps6586x);
+
+ mutex_init(&tps6586x->lock);
+
+ ret = tps6586x_add_subdevs(tps6586x, pdata);
+ if (ret) {
+ dev_err(&client->dev, "add devices failed: %d\n", ret);
+ goto err_add_devs;
+ }
+
+ tps6586x_gpio_init(tps6586x, pdata->gpio_base);
+
+ return 0;
+
+err_add_devs:
+ kfree(tps6586x);
+ return ret;
+}
+
+static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id tps6586x_id_table[] = {
+ { "tps6586x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
+
+static struct i2c_driver tps6586x_driver = {
+ .driver = {
+ .name = "tps6586x",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps6586x_i2c_probe,
+ .remove = __devexit_p(tps6586x_i2c_remove),
+ .id_table = tps6586x_id_table,
+};
+
+static int __init tps6586x_init(void)
+{
+ return i2c_add_driver(&tps6586x_driver);
+}
+subsys_initcall(tps6586x_init);
+
+static void __exit tps6586x_exit(void)
+{
+ i2c_del_driver(&tps6586x_driver);
+}
+module_exit(tps6586x_exit);
+
+MODULE_DESCRIPTION("TPS6586X core driver");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
new file mode 100644
index 0000000..5d25bdc
--- /dev/null
+++ b/drivers/mfd/twl6030-pwm.c
@@ -0,0 +1,163 @@
+/*
+ * twl6030_pwm.c
+ * Driver for PHOENIX (TWL6030) Pulse Width Modulator
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/slab.h>
+
+#define LED_PWM_CTRL1 0xF4
+#define LED_PWM_CTRL2 0xF5
+
+/* Max value for CTRL1 register */
+#define PWM_CTRL1_MAX 255
+
+/* Pull down disable */
+#define PWM_CTRL2_DIS_PD (1 << 6)
+
+/* Current control 2.5 milli Amps */
+#define PWM_CTRL2_CURR_02 (2 << 4)
+
+/* LED supply source */
+#define PWM_CTRL2_SRC_VAC (1 << 2)
+
+/* LED modes */
+#define PWM_CTRL2_MODE_HW (0 << 0)
+#define PWM_CTRL2_MODE_SW (1 << 0)
+#define PWM_CTRL2_MODE_DIS (2 << 0)
+
+#define PWM_CTRL2_MODE_MASK 0x3
+
+struct pwm_device {
+ const char *label;
+ unsigned int pwm_id;
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ u8 duty_cycle;
+ int ret;
+
+ if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1);
+
+ if (ret < 0) {
+ pr_err("%s: Failed to configure PWM, Error %d\n",
+ pwm->label, ret);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ u8 val;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+ return ret;
+ }
+
+ /* Change mode to software control */
+ val &= ~PWM_CTRL2_MODE_MASK;
+ val |= PWM_CTRL2_MODE_SW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+ return ret;
+ }
+
+ twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+ return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ u8 val;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ return;
+ }
+
+ val &= ~PWM_CTRL2_MODE_MASK;
+ val |= PWM_CTRL2_MODE_HW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ return;
+ }
+ return;
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ u8 val;
+ int ret;
+ struct pwm_device *pwm;
+
+ pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+ if (pwm == NULL) {
+ pr_err("%s: failed to allocate memory\n", label);
+ return NULL;
+ }
+
+ pwm->label = label;
+ pwm->pwm_id = pwm_id;
+
+ /* Configure PWM */
+ val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
+ PWM_CTRL2_MODE_HW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+
+ if (ret < 0) {
+ pr_err("%s: Failed to configure PWM, Error %d\n",
+ pwm->label, ret);
+
+ kfree(pwm);
+ return NULL;
+ }
+
+ return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ pwm_disable(pwm);
+ kfree(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c
index dbe2801..d73f84b 100644
--- a/drivers/mfd/ucb1400_core.c
+++ b/drivers/mfd/ucb1400_core.c
@@ -114,7 +114,7 @@ static int ucb1400_core_probe(struct device *dev)
err3:
platform_device_put(ucb->ucb1400_ts);
err2:
- platform_device_unregister(ucb->ucb1400_gpio);
+ platform_device_del(ucb->ucb1400_gpio);
err1:
platform_device_put(ucb->ucb1400_gpio);
err0:
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 1a968f3..1e7aaaf 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -95,6 +95,7 @@ enum wm831x_parent {
WM8311 = 0x8311,
WM8312 = 0x8312,
WM8320 = 0x8320,
+ WM8321 = 0x8321,
};
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
@@ -1533,6 +1534,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
break;
+ case WM8321:
+ parent = WM8321;
+ wm831x->num_gpio = 12;
+ dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
+ break;
+
default:
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
ret = -EINVAL;
@@ -1607,6 +1614,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
NULL, 0);
break;
+ case WM8321:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8320_devs, ARRAY_SIZE(wm8320_devs),
+ NULL, 0);
+ break;
+
default:
/* If this happens the bus probe function is buggy */
BUG();
@@ -1744,10 +1757,8 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
struct wm831x *wm831x;
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
- if (wm831x == NULL) {
- kfree(i2c);
+ if (wm831x == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(i2c, wm831x);
wm831x->dev = &i2c->dev;
@@ -1779,6 +1790,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8311", WM8311 },
{ "wm8312", WM8312 },
{ "wm8320", WM8320 },
+ { "wm8321", WM8321 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index b580748..e81cc31 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -536,6 +536,7 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
}
out:
+ kfree(wm8350->reg_cache);
return ret;
}
@@ -700,7 +701,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
ret = wm8350_irq_init(wm8350, irq, pdata);
if (ret < 0)
- goto err;
+ goto err_free;
if (wm8350->irq_base) {
ret = request_threaded_irq(wm8350->irq_base +
@@ -738,8 +739,9 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
err_irq:
wm8350_irq_exit(wm8350);
-err:
+err_free:
kfree(wm8350->reg_cache);
+err:
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_device_init);
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index ec71c936..b3b2aaf8 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -326,8 +326,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
ARRAY_SIZE(wm8994_main_supplies),
GFP_KERNEL);
- if (!wm8994->supplies)
+ if (!wm8994->supplies) {
+ ret = -ENOMEM;
goto err;
+ }
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
wm8994->supplies[i].supply = wm8994_main_supplies[i];
@@ -495,10 +497,8 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
struct wm8994 *wm8994;
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
- if (wm8994 == NULL) {
- kfree(i2c);
+ if (wm8994 == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(i2c, wm8994);
wm8994->dev = &i2c->dev;
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c
index 2d44b33..6f62180 100644
--- a/drivers/misc/cs5535-mfgpt.c
+++ b/drivers/misc/cs5535-mfgpt.c
@@ -211,6 +211,17 @@ EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer);
*/
void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer)
{
+ unsigned long flags;
+ uint16_t val;
+
+ /* timer can be made available again only if never set up */
+ val = cs5535_mfgpt_read(timer, MFGPT_REG_SETUP);
+ if (!(val & MFGPT_SETUP_SETUP)) {
+ spin_lock_irqsave(&timer->chip->lock, flags);
+ __set_bit(timer->nr, timer->chip->avail);
+ spin_unlock_irqrestore(&timer->chip->lock, flags);
+ }
+
kfree(timer);
}
EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer);
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c
index 5bfb2a2..ef34de7 100644
--- a/drivers/misc/lkdtm.c
+++ b/drivers/misc/lkdtm.c
@@ -124,9 +124,9 @@ static int count = DEFAULT_COUNT;
module_param(recur_count, int, 0644);
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\
"default is 10");
-module_param(cpoint_name, charp, 0644);
+module_param(cpoint_name, charp, 0444);
MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
-module_param(cpoint_type, charp, 0644);
+module_param(cpoint_type, charp, 0444);
MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\
"hitting the crash point");
module_param(cpoint_count, int, 0644);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index cb9fbc8..d545f79 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -29,6 +29,7 @@
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
@@ -107,6 +108,7 @@ static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
int ret = -ENXIO;
+ lock_kernel();
if (md) {
if (md->usage == 2)
check_disk_change(bdev);
@@ -117,6 +119,7 @@ static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
ret = -EROFS;
}
}
+ unlock_kernel();
return ret;
}
@@ -125,7 +128,9 @@ static int mmc_blk_release(struct gendisk *disk, fmode_t mode)
{
struct mmc_blk_data *md = disk->private_data;
+ lock_kernel();
mmc_blk_put(md);
+ unlock_kernel();
return 0;
}
@@ -242,7 +247,76 @@ static u32 get_card_status(struct mmc_card *card, struct request *req)
return cmd.resp[0];
}
-static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ mmc_claim_host(card->host);
+
+ if (!mmc_can_erase(card)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card))
+ arg = MMC_TRIM_ARG;
+ else
+ arg = MMC_ERASE_ARG;
+
+ err = mmc_erase(card, from, nr, arg);
+out:
+ spin_lock_irq(&md->lock);
+ __blk_end_request(req, err, blk_rq_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ mmc_release_host(card->host);
+
+ return err ? 0 : 1;
+}
+
+static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ mmc_claim_host(card->host);
+
+ if (!mmc_can_secure_erase_trim(card)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+ arg = MMC_SECURE_TRIM1_ARG;
+ else
+ arg = MMC_SECURE_ERASE_ARG;
+
+ err = mmc_erase(card, from, nr, arg);
+ if (!err && arg == MMC_SECURE_TRIM1_ARG)
+ err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
+out:
+ spin_lock_irq(&md->lock);
+ __blk_end_request(req, err, blk_rq_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ mmc_release_host(card->host);
+
+ return err ? 0 : 1;
+}
+
+static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
@@ -470,6 +544,17 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
return 0;
}
+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+ if (req->cmd_flags & REQ_DISCARD) {
+ if (req->cmd_flags & REQ_SECURE)
+ return mmc_blk_issue_secdiscard_rq(mq, req);
+ else
+ return mmc_blk_issue_discard_rq(mq, req);
+ } else {
+ return mmc_blk_issue_rw_rq(mq, req);
+ }
+}
static inline int mmc_blk_readonly(struct mmc_card *card)
{
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 445d7db..5dd8576 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/scatterlist.h>
+#include <linux/swap.h> /* For nr_free_buffer_pages() */
#define RESULT_OK 0
#define RESULT_FAIL 1
@@ -25,6 +26,60 @@
#define BUFFER_ORDER 2
#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
+/*
+ * Limit the test area size to the maximum MMC HC erase group size. Note that
+ * the maximum SD allocation unit size is just 4MiB.
+ */
+#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024)
+
+/**
+ * struct mmc_test_pages - pages allocated by 'alloc_pages()'.
+ * @page: first page in the allocation
+ * @order: order of the number of pages allocated
+ */
+struct mmc_test_pages {
+ struct page *page;
+ unsigned int order;
+};
+
+/**
+ * struct mmc_test_mem - allocated memory.
+ * @arr: array of allocations
+ * @cnt: number of allocations
+ */
+struct mmc_test_mem {
+ struct mmc_test_pages *arr;
+ unsigned int cnt;
+};
+
+/**
+ * struct mmc_test_area - information for performance tests.
+ * @max_sz: test area size (in bytes)
+ * @dev_addr: address on card at which to do performance tests
+ * @max_segs: maximum segments in scatterlist @sg
+ * @blocks: number of (512 byte) blocks currently mapped by @sg
+ * @sg_len: length of currently mapped scatterlist @sg
+ * @mem: allocated memory
+ * @sg: scatterlist
+ */
+struct mmc_test_area {
+ unsigned long max_sz;
+ unsigned int dev_addr;
+ unsigned int max_segs;
+ unsigned int blocks;
+ unsigned int sg_len;
+ struct mmc_test_mem *mem;
+ struct scatterlist *sg;
+};
+
+/**
+ * struct mmc_test_card - test information.
+ * @card: card under test
+ * @scratch: transfer buffer
+ * @buffer: transfer buffer
+ * @highmem: buffer for highmem tests
+ * @area: information for performance tests
+ */
struct mmc_test_card {
struct mmc_card *card;
@@ -33,6 +88,7 @@ struct mmc_test_card {
#ifdef CONFIG_HIGHMEM
struct page *highmem;
#endif
+ struct mmc_test_area area;
};
/*******************************************************************/
@@ -97,6 +153,12 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
mmc_set_data_timeout(mrq->data, test->card);
}
+static int mmc_test_busy(struct mmc_command *cmd)
+{
+ return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(cmd->resp[0]) == 7);
+}
+
/*
* Wait for the card to finish the busy state
*/
@@ -117,13 +179,13 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
if (ret)
break;
- if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
+ if (!busy && mmc_test_busy(&cmd)) {
busy = 1;
printk(KERN_INFO "%s: Warning: Host did not "
"wait for busy state to end.\n",
mmc_hostname(test->card->host));
}
- } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+ } while (mmc_test_busy(&cmd));
return ret;
}
@@ -170,6 +232,248 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test,
return 0;
}
+static void mmc_test_free_mem(struct mmc_test_mem *mem)
+{
+ if (!mem)
+ return;
+ while (mem->cnt--)
+ __free_pages(mem->arr[mem->cnt].page,
+ mem->arr[mem->cnt].order);
+ kfree(mem->arr);
+ kfree(mem);
+}
+
+/*
+ * Allocate a lot of memory, preferrably max_sz but at least min_sz. In case
+ * there isn't much memory do not exceed 1/16th total lowmem pages.
+ */
+static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
+ unsigned long max_sz)
+{
+ unsigned long max_page_cnt = DIV_ROUND_UP(max_sz, PAGE_SIZE);
+ unsigned long min_page_cnt = DIV_ROUND_UP(min_sz, PAGE_SIZE);
+ unsigned long page_cnt = 0;
+ unsigned long limit = nr_free_buffer_pages() >> 4;
+ struct mmc_test_mem *mem;
+
+ if (max_page_cnt > limit)
+ max_page_cnt = limit;
+ if (max_page_cnt < min_page_cnt)
+ max_page_cnt = min_page_cnt;
+
+ mem = kzalloc(sizeof(struct mmc_test_mem), GFP_KERNEL);
+ if (!mem)
+ return NULL;
+
+ mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_page_cnt,
+ GFP_KERNEL);
+ if (!mem->arr)
+ goto out_free;
+
+ while (max_page_cnt) {
+ struct page *page;
+ unsigned int order;
+ gfp_t flags = GFP_KERNEL | GFP_DMA | __GFP_NOWARN |
+ __GFP_NORETRY;
+
+ order = get_order(max_page_cnt << PAGE_SHIFT);
+ while (1) {
+ page = alloc_pages(flags, order);
+ if (page || !order)
+ break;
+ order -= 1;
+ }
+ if (!page) {
+ if (page_cnt < min_page_cnt)
+ goto out_free;
+ break;
+ }
+ mem->arr[mem->cnt].page = page;
+ mem->arr[mem->cnt].order = order;
+ mem->cnt += 1;
+ if (max_page_cnt <= (1UL << order))
+ break;
+ max_page_cnt -= 1UL << order;
+ page_cnt += 1UL << order;
+ }
+
+ return mem;
+
+out_free:
+ mmc_test_free_mem(mem);
+ return NULL;
+}
+
+/*
+ * Map memory into a scatterlist. Optionally allow the same memory to be
+ * mapped more than once.
+ */
+static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz,
+ struct scatterlist *sglist, int repeat,
+ unsigned int max_segs, unsigned int *sg_len)
+{
+ struct scatterlist *sg = NULL;
+ unsigned int i;
+
+ sg_init_table(sglist, max_segs);
+
+ *sg_len = 0;
+ do {
+ for (i = 0; i < mem->cnt; i++) {
+ unsigned long len = PAGE_SIZE << mem->arr[i].order;
+
+ if (sz < len)
+ len = sz;
+ if (sg)
+ sg = sg_next(sg);
+ else
+ sg = sglist;
+ if (!sg)
+ return -EINVAL;
+ sg_set_page(sg, mem->arr[i].page, len, 0);
+ sz -= len;
+ *sg_len += 1;
+ if (!sz)
+ break;
+ }
+ } while (sz && repeat);
+
+ if (sz)
+ return -EINVAL;
+
+ if (sg)
+ sg_mark_end(sg);
+
+ return 0;
+}
+
+/*
+ * Map memory into a scatterlist so that no pages are contiguous. Allow the
+ * same memory to be mapped more than once.
+ */
+static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
+ unsigned long sz,
+ struct scatterlist *sglist,
+ unsigned int max_segs,
+ unsigned int *sg_len)
+{
+ struct scatterlist *sg = NULL;
+ unsigned int i = mem->cnt, cnt;
+ unsigned long len;
+ void *base, *addr, *last_addr = NULL;
+
+ sg_init_table(sglist, max_segs);
+
+ *sg_len = 0;
+ while (sz && i) {
+ base = page_address(mem->arr[--i].page);
+ cnt = 1 << mem->arr[i].order;
+ while (sz && cnt) {
+ addr = base + PAGE_SIZE * --cnt;
+ if (last_addr && last_addr + PAGE_SIZE == addr)
+ continue;
+ last_addr = addr;
+ len = PAGE_SIZE;
+ if (sz < len)
+ len = sz;
+ if (sg)
+ sg = sg_next(sg);
+ else
+ sg = sglist;
+ if (!sg)
+ return -EINVAL;
+ sg_set_page(sg, virt_to_page(addr), len, 0);
+ sz -= len;
+ *sg_len += 1;
+ }
+ }
+
+ if (sg)
+ sg_mark_end(sg);
+
+ return 0;
+}
+
+/*
+ * Calculate transfer rate in bytes per second.
+ */
+static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts)
+{
+ uint64_t ns;
+
+ ns = ts->tv_sec;
+ ns *= 1000000000;
+ ns += ts->tv_nsec;
+
+ bytes *= 1000000000;
+
+ while (ns > UINT_MAX) {
+ bytes >>= 1;
+ ns >>= 1;
+ }
+
+ if (!ns)
+ return 0;
+
+ do_div(bytes, (uint32_t)ns);
+
+ return bytes;
+}
+
+/*
+ * Print the transfer rate.
+ */
+static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
+ struct timespec *ts1, struct timespec *ts2)
+{
+ unsigned int rate, sectors = bytes >> 9;
+ struct timespec ts;
+
+ ts = timespec_sub(*ts2, *ts1);
+
+ rate = mmc_test_rate(bytes, &ts);
+
+ printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
+ "seconds (%u kB/s, %u KiB/s)\n",
+ mmc_hostname(test->card->host), sectors, sectors >> 1,
+ (sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
+ (unsigned long)ts.tv_nsec, rate / 1000, rate / 1024);
+}
+
+/*
+ * Print the average transfer rate.
+ */
+static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
+ unsigned int count, struct timespec *ts1,
+ struct timespec *ts2)
+{
+ unsigned int rate, sectors = bytes >> 9;
+ uint64_t tot = bytes * count;
+ struct timespec ts;
+
+ ts = timespec_sub(*ts2, *ts1);
+
+ rate = mmc_test_rate(tot, &ts);
+
+ printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
+ "%lu.%09lu seconds (%u kB/s, %u KiB/s)\n",
+ mmc_hostname(test->card->host), count, sectors, count,
+ sectors >> 1, (sectors == 1 ? ".5" : ""),
+ (unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec,
+ rate / 1000, rate / 1024);
+}
+
+/*
+ * Return the card size in sectors.
+ */
+static unsigned int mmc_test_capacity(struct mmc_card *card)
+{
+ if (!mmc_card_sd(card) && mmc_card_blockaddr(card))
+ return card->ext_csd.sectors;
+ else
+ return card->csd.capacity << (card->csd.read_blkbits - 9);
+}
+
/*******************************************************************/
/* Test preparation and cleanup */
/*******************************************************************/
@@ -893,8 +1197,419 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
return 0;
}
+#else
+
+static int mmc_test_no_highmem(struct mmc_test_card *test)
+{
+ printk(KERN_INFO "%s: Highmem not configured - test skipped\n",
+ mmc_hostname(test->card->host));
+ return 0;
+}
+
#endif /* CONFIG_HIGHMEM */
+/*
+ * Map sz bytes so that it can be transferred.
+ */
+static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
+ int max_scatter)
+{
+ struct mmc_test_area *t = &test->area;
+
+ t->blocks = sz >> 9;
+
+ if (max_scatter) {
+ return mmc_test_map_sg_max_scatter(t->mem, sz, t->sg,
+ t->max_segs, &t->sg_len);
+ } else {
+ return mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs,
+ &t->sg_len);
+ }
+}
+
+/*
+ * Transfer bytes mapped by mmc_test_area_map().
+ */
+static int mmc_test_area_transfer(struct mmc_test_card *test,
+ unsigned int dev_addr, int write)
+{
+ struct mmc_test_area *t = &test->area;
+
+ return mmc_test_simple_transfer(test, t->sg, t->sg_len, dev_addr,
+ t->blocks, 512, write);
+}
+
+/*
+ * Map and transfer bytes.
+ */
+static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
+ unsigned int dev_addr, int write, int max_scatter,
+ int timed)
+{
+ struct timespec ts1, ts2;
+ int ret;
+
+ ret = mmc_test_area_map(test, sz, max_scatter);
+ if (ret)
+ return ret;
+
+ if (timed)
+ getnstimeofday(&ts1);
+
+ ret = mmc_test_area_transfer(test, dev_addr, write);
+ if (ret)
+ return ret;
+
+ if (timed)
+ getnstimeofday(&ts2);
+
+ if (timed)
+ mmc_test_print_rate(test, sz, &ts1, &ts2);
+
+ return 0;
+}
+
+/*
+ * Write the test area entirely.
+ */
+static int mmc_test_area_fill(struct mmc_test_card *test)
+{
+ return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr,
+ 1, 0, 0);
+}
+
+/*
+ * Erase the test area entirely.
+ */
+static int mmc_test_area_erase(struct mmc_test_card *test)
+{
+ struct mmc_test_area *t = &test->area;
+
+ if (!mmc_can_erase(test->card))
+ return 0;
+
+ return mmc_erase(test->card, t->dev_addr, test->area.max_sz >> 9,
+ MMC_ERASE_ARG);
+}
+
+/*
+ * Cleanup struct mmc_test_area.
+ */
+static int mmc_test_area_cleanup(struct mmc_test_card *test)
+{
+ struct mmc_test_area *t = &test->area;
+
+ kfree(t->sg);
+ mmc_test_free_mem(t->mem);
+
+ return 0;
+}
+
+/*
+ * Initialize an area for testing large transfers. The size of the area is the
+ * preferred erase size which is a good size for optimal transfer speed. Note
+ * that is typically 4MiB for modern cards. The test area is set to the middle
+ * of the card because cards may have different charateristics at the front
+ * (for FAT file system optimization). Optionally, the area is erased (if the
+ * card supports it) which may improve write performance. Optionally, the area
+ * is filled with data for subsequent read tests.
+ */
+static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
+{
+ struct mmc_test_area *t = &test->area;
+ unsigned long min_sz = 64 * 1024;
+ int ret;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ if (test->card->pref_erase > TEST_AREA_MAX_SIZE >> 9)
+ t->max_sz = TEST_AREA_MAX_SIZE;
+ else
+ t->max_sz = (unsigned long)test->card->pref_erase << 9;
+ /*
+ * Try to allocate enough memory for the whole area. Less is OK
+ * because the same memory can be mapped into the scatterlist more than
+ * once.
+ */
+ t->mem = mmc_test_alloc_mem(min_sz, t->max_sz);
+ if (!t->mem)
+ return -ENOMEM;
+
+ t->max_segs = DIV_ROUND_UP(t->max_sz, PAGE_SIZE);
+ t->sg = kmalloc(sizeof(struct scatterlist) * t->max_segs, GFP_KERNEL);
+ if (!t->sg) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ t->dev_addr = mmc_test_capacity(test->card) / 2;
+ t->dev_addr -= t->dev_addr % (t->max_sz >> 9);
+
+ if (erase) {
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ goto out_free;
+ }
+
+ if (fill) {
+ ret = mmc_test_area_fill(test);
+ if (ret)
+ goto out_free;
+ }
+
+ return 0;
+
+out_free:
+ mmc_test_area_cleanup(test);
+ return ret;
+}
+
+/*
+ * Prepare for large transfers. Do not erase the test area.
+ */
+static int mmc_test_area_prepare(struct mmc_test_card *test)
+{
+ return mmc_test_area_init(test, 0, 0);
+}
+
+/*
+ * Prepare for large transfers. Do erase the test area.
+ */
+static int mmc_test_area_prepare_erase(struct mmc_test_card *test)
+{
+ return mmc_test_area_init(test, 1, 0);
+}
+
+/*
+ * Prepare for large transfers. Erase and fill the test area.
+ */
+static int mmc_test_area_prepare_fill(struct mmc_test_card *test)
+{
+ return mmc_test_area_init(test, 1, 1);
+}
+
+/*
+ * Test best-case performance. Best-case performance is expected from
+ * a single large transfer.
+ *
+ * An additional option (max_scatter) allows the measurement of the same
+ * transfer but with no contiguous pages in the scatter list. This tests
+ * the efficiency of DMA to handle scattered pages.
+ */
+static int mmc_test_best_performance(struct mmc_test_card *test, int write,
+ int max_scatter)
+{
+ return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr,
+ write, max_scatter, 1);
+}
+
+/*
+ * Best-case read performance.
+ */
+static int mmc_test_best_read_performance(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 0, 0);
+}
+
+/*
+ * Best-case write performance.
+ */
+static int mmc_test_best_write_performance(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 1, 0);
+}
+
+/*
+ * Best-case read performance into scattered pages.
+ */
+static int mmc_test_best_read_perf_max_scatter(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 0, 1);
+}
+
+/*
+ * Best-case write performance from scattered pages.
+ */
+static int mmc_test_best_write_perf_max_scatter(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 1, 1);
+}
+
+/*
+ * Single read performance by transfer size.
+ */
+static int mmc_test_profile_read_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr;
+ int ret;
+
+ for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
+ dev_addr = test->area.dev_addr + (sz >> 9);
+ ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
+ if (ret)
+ return ret;
+ }
+ dev_addr = test->area.dev_addr;
+ return mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
+}
+
+/*
+ * Single write performance by transfer size.
+ */
+static int mmc_test_profile_write_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr;
+ int ret;
+
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
+ dev_addr = test->area.dev_addr + (sz >> 9);
+ ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
+ if (ret)
+ return ret;
+ }
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ dev_addr = test->area.dev_addr;
+ return mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
+}
+
+/*
+ * Single trim performance by transfer size.
+ */
+static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr;
+ struct timespec ts1, ts2;
+ int ret;
+
+ if (!mmc_can_trim(test->card))
+ return RESULT_UNSUP_CARD;
+
+ if (!mmc_can_erase(test->card))
+ return RESULT_UNSUP_HOST;
+
+ for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
+ dev_addr = test->area.dev_addr + (sz >> 9);
+ getnstimeofday(&ts1);
+ ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
+ if (ret)
+ return ret;
+ getnstimeofday(&ts2);
+ mmc_test_print_rate(test, sz, &ts1, &ts2);
+ }
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
+ if (ret)
+ return ret;
+ getnstimeofday(&ts2);
+ mmc_test_print_rate(test, sz, &ts1, &ts2);
+ return 0;
+}
+
+/*
+ * Consecutive read performance by transfer size.
+ */
+static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr, i, cnt;
+ struct timespec ts1, ts2;
+ int ret;
+
+ for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
+ cnt = test->area.max_sz / sz;
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ for (i = 0; i < cnt; i++) {
+ ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
+ if (ret)
+ return ret;
+ dev_addr += (sz >> 9);
+ }
+ getnstimeofday(&ts2);
+ mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
+ }
+ return 0;
+}
+
+/*
+ * Consecutive write performance by transfer size.
+ */
+static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr, i, cnt;
+ struct timespec ts1, ts2;
+ int ret;
+
+ for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ cnt = test->area.max_sz / sz;
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ for (i = 0; i < cnt; i++) {
+ ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
+ if (ret)
+ return ret;
+ dev_addr += (sz >> 9);
+ }
+ getnstimeofday(&ts2);
+ mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
+ }
+ return 0;
+}
+
+/*
+ * Consecutive trim performance by transfer size.
+ */
+static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr, i, cnt;
+ struct timespec ts1, ts2;
+ int ret;
+
+ if (!mmc_can_trim(test->card))
+ return RESULT_UNSUP_CARD;
+
+ if (!mmc_can_erase(test->card))
+ return RESULT_UNSUP_HOST;
+
+ for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ ret = mmc_test_area_fill(test);
+ if (ret)
+ return ret;
+ cnt = test->area.max_sz / sz;
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ for (i = 0; i < cnt; i++) {
+ ret = mmc_erase(test->card, dev_addr, sz >> 9,
+ MMC_TRIM_ARG);
+ if (ret)
+ return ret;
+ dev_addr += (sz >> 9);
+ }
+ getnstimeofday(&ts2);
+ mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
+ }
+ return 0;
+}
+
static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (no data verification)",
@@ -1040,8 +1755,100 @@ static const struct mmc_test_case mmc_test_cases[] = {
.cleanup = mmc_test_cleanup,
},
+#else
+
+ {
+ .name = "Highmem write",
+ .run = mmc_test_no_highmem,
+ },
+
+ {
+ .name = "Highmem read",
+ .run = mmc_test_no_highmem,
+ },
+
+ {
+ .name = "Multi-block highmem write",
+ .run = mmc_test_no_highmem,
+ },
+
+ {
+ .name = "Multi-block highmem read",
+ .run = mmc_test_no_highmem,
+ },
+
#endif /* CONFIG_HIGHMEM */
+ {
+ .name = "Best-case read performance",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_best_read_performance,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Best-case write performance",
+ .prepare = mmc_test_area_prepare_erase,
+ .run = mmc_test_best_write_performance,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Best-case read performance into scattered pages",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_best_read_perf_max_scatter,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Best-case write performance from scattered pages",
+ .prepare = mmc_test_area_prepare_erase,
+ .run = mmc_test_best_write_perf_max_scatter,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Single read performance by transfer size",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_profile_read_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Single write performance by transfer size",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_write_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Single trim performance by transfer size",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_profile_trim_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Consecutive read performance by transfer size",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_profile_seq_read_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Consecutive write performance by transfer size",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_seq_write_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Consecutive trim performance by transfer size",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_seq_trim_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
};
static DEFINE_MUTEX(mmc_test_lock);
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d6ded24..e876678 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -30,9 +30,9 @@
static int mmc_prep_request(struct request_queue *q, struct request *req)
{
/*
- * We only like normal block requests.
+ * We only like normal block requests and discards.
*/
- if (!blk_fs_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS && !(req->cmd_flags & REQ_DISCARD)) {
blk_dump_rq_flags(req, "MMC bad request");
return BLKPREP_KILL;
}
@@ -128,8 +128,23 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
mq->req = NULL;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
- blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
+ if (mmc_can_erase(card)) {
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mq->queue);
+ mq->queue->limits.max_discard_sectors = UINT_MAX;
+ if (card->erased_byte == 0)
+ mq->queue->limits.discard_zeroes_data = 1;
+ if (!mmc_can_trim(card) && is_power_of_2(card->erase_size)) {
+ mq->queue->limits.discard_granularity =
+ card->erase_size << 9;
+ mq->queue->limits.discard_alignment =
+ card->erase_size << 9;
+ }
+ if (mmc_can_secure_erase_trim(card))
+ queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD,
+ mq->queue);
+ }
#ifdef CONFIG_MMC_BLOCK_BOUNCE
if (host->max_hw_segs == 1) {
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 49d9dca..7cd9749 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev,
return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n");
+ case MMC_TYPE_SD_COMBO:
+ return sprintf(buf, "SDcombo\n");
default:
return -EFAULT;
}
@@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SDcombo";
+ break;
default:
type = NULL;
}
@@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card)
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SD-combo";
+ if (mmc_card_blockaddr(card))
+ type = "SDHC-combo";
default:
type = "?";
break;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 569e94d..5db49b1 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1050,6 +1050,352 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
EXPORT_SYMBOL(mmc_detect_change);
+void mmc_init_erase(struct mmc_card *card)
+{
+ unsigned int sz;
+
+ if (is_power_of_2(card->erase_size))
+ card->erase_shift = ffs(card->erase_size) - 1;
+ else
+ card->erase_shift = 0;
+
+ /*
+ * It is possible to erase an arbitrarily large area of an SD or MMC
+ * card. That is not desirable because it can take a long time
+ * (minutes) potentially delaying more important I/O, and also the
+ * timeout calculations become increasingly hugely over-estimated.
+ * Consequently, 'pref_erase' is defined as a guide to limit erases
+ * to that size and alignment.
+ *
+ * For SD cards that define Allocation Unit size, limit erases to one
+ * Allocation Unit at a time. For MMC cards that define High Capacity
+ * Erase Size, whether it is switched on or not, limit to that size.
+ * Otherwise just have a stab at a good value. For modern cards it
+ * will end up being 4MiB. Note that if the value is too small, it
+ * can end up taking longer to erase.
+ */
+ if (mmc_card_sd(card) && card->ssr.au) {
+ card->pref_erase = card->ssr.au;
+ card->erase_shift = ffs(card->ssr.au) - 1;
+ } else if (card->ext_csd.hc_erase_size) {
+ card->pref_erase = card->ext_csd.hc_erase_size;
+ } else {
+ sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
+ if (sz < 128)
+ card->pref_erase = 512 * 1024 / 512;
+ else if (sz < 512)
+ card->pref_erase = 1024 * 1024 / 512;
+ else if (sz < 1024)
+ card->pref_erase = 2 * 1024 * 1024 / 512;
+ else
+ card->pref_erase = 4 * 1024 * 1024 / 512;
+ if (card->pref_erase < card->erase_size)
+ card->pref_erase = card->erase_size;
+ else {
+ sz = card->pref_erase % card->erase_size;
+ if (sz)
+ card->pref_erase += card->erase_size - sz;
+ }
+ }
+}
+
+static void mmc_set_mmc_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd,
+ unsigned int arg, unsigned int qty)
+{
+ unsigned int erase_timeout;
+
+ if (card->ext_csd.erase_group_def & 1) {
+ /* High Capacity Erase Group Size uses HC timeouts */
+ if (arg == MMC_TRIM_ARG)
+ erase_timeout = card->ext_csd.trim_timeout;
+ else
+ erase_timeout = card->ext_csd.hc_erase_timeout;
+ } else {
+ /* CSD Erase Group Size uses write timeout */
+ unsigned int mult = (10 << card->csd.r2w_factor);
+ unsigned int timeout_clks = card->csd.tacc_clks * mult;
+ unsigned int timeout_us;
+
+ /* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */
+ if (card->csd.tacc_ns < 1000000)
+ timeout_us = (card->csd.tacc_ns * mult) / 1000;
+ else
+ timeout_us = (card->csd.tacc_ns / 1000) * mult;
+
+ /*
+ * ios.clock is only a target. The real clock rate might be
+ * less but not that much less, so fudge it by multiplying by 2.
+ */
+ timeout_clks <<= 1;
+ timeout_us += (timeout_clks * 1000) /
+ (card->host->ios.clock / 1000);
+
+ erase_timeout = timeout_us / 1000;
+
+ /*
+ * Theoretically, the calculation could underflow so round up
+ * to 1ms in that case.
+ */
+ if (!erase_timeout)
+ erase_timeout = 1;
+ }
+
+ /* Multiplier for secure operations */
+ if (arg & MMC_SECURE_ARGS) {
+ if (arg == MMC_SECURE_ERASE_ARG)
+ erase_timeout *= card->ext_csd.sec_erase_mult;
+ else
+ erase_timeout *= card->ext_csd.sec_trim_mult;
+ }
+
+ erase_timeout *= qty;
+
+ /*
+ * Ensure at least a 1 second timeout for SPI as per
+ * 'mmc_set_data_timeout()'
+ */
+ if (mmc_host_is_spi(card->host) && erase_timeout < 1000)
+ erase_timeout = 1000;
+
+ cmd->erase_timeout = erase_timeout;
+}
+
+static void mmc_set_sd_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd, unsigned int arg,
+ unsigned int qty)
+{
+ if (card->ssr.erase_timeout) {
+ /* Erase timeout specified in SD Status Register (SSR) */
+ cmd->erase_timeout = card->ssr.erase_timeout * qty +
+ card->ssr.erase_offset;
+ } else {
+ /*
+ * Erase timeout not specified in SD Status Register (SSR) so
+ * use 250ms per write block.
+ */
+ cmd->erase_timeout = 250 * qty;
+ }
+
+ /* Must not be less than 1 second */
+ if (cmd->erase_timeout < 1000)
+ cmd->erase_timeout = 1000;
+}
+
+static void mmc_set_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd, unsigned int arg,
+ unsigned int qty)
+{
+ if (mmc_card_sd(card))
+ mmc_set_sd_erase_timeout(card, cmd, arg, qty);
+ else
+ mmc_set_mmc_erase_timeout(card, cmd, arg, qty);
+}
+
+static int mmc_do_erase(struct mmc_card *card, unsigned int from,
+ unsigned int to, unsigned int arg)
+{
+ struct mmc_command cmd;
+ unsigned int qty = 0;
+ int err;
+
+ /*
+ * qty is used to calculate the erase timeout which depends on how many
+ * erase groups (or allocation units in SD terminology) are affected.
+ * We count erasing part of an erase group as one erase group.
+ * For SD, the allocation units are always a power of 2. For MMC, the
+ * erase group size is almost certainly also power of 2, but it does not
+ * seem to insist on that in the JEDEC standard, so we fall back to
+ * division in that case. SD may not specify an allocation unit size,
+ * in which case the timeout is based on the number of write blocks.
+ *
+ * Note that the timeout for secure trim 2 will only be correct if the
+ * number of erase groups specified is the same as the total of all
+ * preceding secure trim 1 commands. Since the power may have been
+ * lost since the secure trim 1 commands occurred, it is generally
+ * impossible to calculate the secure trim 2 timeout correctly.
+ */
+ if (card->erase_shift)
+ qty += ((to >> card->erase_shift) -
+ (from >> card->erase_shift)) + 1;
+ else if (mmc_card_sd(card))
+ qty += to - from + 1;
+ else
+ qty += ((to / card->erase_size) -
+ (from / card->erase_size)) + 1;
+
+ if (!mmc_card_blockaddr(card)) {
+ from <<= 9;
+ to <<= 9;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ if (mmc_card_sd(card))
+ cmd.opcode = SD_ERASE_WR_BLK_START;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_START;
+ cmd.arg = from;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: group start error %d, "
+ "status %#x\n", err, cmd.resp[0]);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ if (mmc_card_sd(card))
+ cmd.opcode = SD_ERASE_WR_BLK_END;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_END;
+ cmd.arg = to;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n",
+ err, cmd.resp[0]);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_ERASE;
+ cmd.arg = arg;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ mmc_set_erase_timeout(card, &cmd, arg, qty);
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
+ err, cmd.resp[0]);
+ err = -EIO;
+ goto out;
+ }
+
+ if (mmc_host_is_spi(card->host))
+ goto out;
+
+ do {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ /* Do not retry else we can't see errors */
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err || (cmd.resp[0] & 0xFDF92000)) {
+ printk(KERN_ERR "error %d requesting status %#x\n",
+ err, cmd.resp[0]);
+ err = -EIO;
+ goto out;
+ }
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
+ R1_CURRENT_STATE(cmd.resp[0]) == 7);
+out:
+ return err;
+}
+
+/**
+ * mmc_erase - erase sectors.
+ * @card: card to erase
+ * @from: first sector to erase
+ * @nr: number of sectors to erase
+ * @arg: erase command argument (SD supports only %MMC_ERASE_ARG)
+ *
+ * Caller must claim host before calling this function.
+ */
+int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg)
+{
+ unsigned int rem, to = from + nr;
+
+ if (!(card->host->caps & MMC_CAP_ERASE) ||
+ !(card->csd.cmdclass & CCC_ERASE))
+ return -EOPNOTSUPP;
+
+ if (!card->erase_size)
+ return -EOPNOTSUPP;
+
+ if (mmc_card_sd(card) && arg != MMC_ERASE_ARG)
+ return -EOPNOTSUPP;
+
+ if ((arg & MMC_SECURE_ARGS) &&
+ !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN))
+ return -EOPNOTSUPP;
+
+ if ((arg & MMC_TRIM_ARGS) &&
+ !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN))
+ return -EOPNOTSUPP;
+
+ if (arg == MMC_SECURE_ERASE_ARG) {
+ if (from % card->erase_size || nr % card->erase_size)
+ return -EINVAL;
+ }
+
+ if (arg == MMC_ERASE_ARG) {
+ rem = from % card->erase_size;
+ if (rem) {
+ rem = card->erase_size - rem;
+ from += rem;
+ if (nr > rem)
+ nr -= rem;
+ else
+ return 0;
+ }
+ rem = nr % card->erase_size;
+ if (rem)
+ nr -= rem;
+ }
+
+ if (nr == 0)
+ return 0;
+
+ to = from + nr;
+
+ if (to <= from)
+ return -EINVAL;
+
+ /* 'from' and 'to' are inclusive */
+ to -= 1;
+
+ return mmc_do_erase(card, from, to, arg);
+}
+EXPORT_SYMBOL(mmc_erase);
+
+int mmc_can_erase(struct mmc_card *card)
+{
+ if ((card->host->caps & MMC_CAP_ERASE) &&
+ (card->csd.cmdclass & CCC_ERASE) && card->erase_size)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_erase);
+
+int mmc_can_trim(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_trim);
+
+int mmc_can_secure_erase_trim(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_secure_erase_trim);
+
+int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
+ unsigned int nr)
+{
+ if (!card->erase_size)
+ return 0;
+ if (from % card->erase_size || nr % card->erase_size)
+ return 0;
+ return 1;
+}
+EXPORT_SYMBOL(mmc_erase_group_aligned);
void mmc_rescan(struct work_struct *work)
{
@@ -1057,6 +1403,17 @@ void mmc_rescan(struct work_struct *work)
container_of(work, struct mmc_host, detect.work);
u32 ocr;
int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->rescan_disable) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
mmc_bus_get(host);
@@ -1099,8 +1456,15 @@ void mmc_rescan(struct work_struct *work)
*/
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
- if (mmc_attach_sdio(host, ocr))
- mmc_power_off(host);
+ if (mmc_attach_sdio(host, ocr)) {
+ mmc_claim_host(host);
+ /* try SDMEM (but not MMC) even if SDIO is broken */
+ if (mmc_send_app_op_cond(host, 0, &ocr))
+ goto out_fail;
+
+ if (mmc_attach_sd(host, ocr))
+ mmc_power_off(host);
+ }
goto out;
}
@@ -1124,6 +1488,7 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+out_fail:
mmc_release_host(host);
mmc_power_off(host);
@@ -1266,19 +1631,6 @@ int mmc_suspend_host(struct mmc_host *host)
if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend)
err = host->bus_ops->suspend(host);
- if (err == -ENOSYS || !host->bus_ops->resume) {
- /*
- * We simply "remove" the card in this case.
- * It will be redetected on resume.
- */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- host->pm_flags = 0;
- err = 0;
- }
}
mmc_bus_put(host);
@@ -1310,28 +1662,61 @@ int mmc_resume_host(struct mmc_host *host)
printk(KERN_WARNING "%s: error %d during resume "
"(card was removed?)\n",
mmc_hostname(host), err);
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- /* no need to bother upper layers */
err = 0;
}
}
mmc_bus_put(host);
- /*
- * We add a slight delay here so that resume can progress
- * in parallel.
- */
- mmc_detect_change(host, 1);
-
return err;
}
-
EXPORT_SYMBOL(mmc_resume_host);
+/* Do the card removal on suspend if card is assumed removeable
+ * Do that in pm notifier while userspace isn't yet frozen, so we will be able
+ to sync the card.
+*/
+int mmc_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ struct mmc_host *host = container_of(
+ notify_block, struct mmc_host, pm_notify);
+ unsigned long flags;
+
+
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->rescan_disable = 1;
+ spin_unlock_irqrestore(&host->lock, flags);
+ cancel_delayed_work_sync(&host->detect);
+
+ if (!host->bus_ops || host->bus_ops->suspend)
+ break;
+
+ mmc_claim_host(host);
+
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ host->pm_flags = 0;
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->rescan_disable = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+ mmc_detect_change(host, 0);
+
+ }
+
+ return 0;
+}
#endif
static int __init mmc_init(void)
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a811c52..9d9eef5 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -29,6 +29,8 @@ struct mmc_bus_ops {
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);
+void mmc_init_erase(struct mmc_card *card);
+
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 4735390..0efe631 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -17,6 +17,7 @@
#include <linux/pagemap.h>
#include <linux/leds.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/mmc/host.h>
@@ -85,6 +86,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
+ host->pm_notify.notifier_call = mmc_pm_notify;
/*
* By default, hosts do not support SGIO or large requests.
@@ -133,6 +135,7 @@ int mmc_add_host(struct mmc_host *host)
#endif
mmc_start_host(host);
+ register_pm_notifier(&host->pm_notify);
return 0;
}
@@ -149,6 +152,7 @@ EXPORT_SYMBOL(mmc_add_host);
*/
void mmc_remove_host(struct mmc_host *host)
{
+ unregister_pm_notifier(&host->pm_notify);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 89f7a25..6909a54 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -108,23 +108,34 @@ static int mmc_decode_cid(struct mmc_card *card)
return 0;
}
+static void mmc_set_erase_size(struct mmc_card *card)
+{
+ if (card->ext_csd.erase_group_def & 1)
+ card->erase_size = card->ext_csd.hc_erase_size;
+ else
+ card->erase_size = card->csd.erase_size;
+
+ mmc_init_erase(card);
+}
+
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
static int mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
- unsigned int e, m, csd_struct;
+ unsigned int e, m, a, b;
u32 *resp = card->raw_csd;
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
+ * We also support eMMC v4.4 & v4.41.
*/
- csd_struct = UNSTUFF_BITS(resp, 126, 2);
- if (csd_struct != 1 && csd_struct != 2) {
+ csd->structure = UNSTUFF_BITS(resp, 126, 2);
+ if (csd->structure == 0) {
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
- mmc_hostname(card->host), csd_struct);
+ mmc_hostname(card->host), csd->structure);
return -EINVAL;
}
@@ -151,6 +162,13 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
+ if (csd->write_blkbits >= 9) {
+ a = UNSTUFF_BITS(resp, 42, 5);
+ b = UNSTUFF_BITS(resp, 37, 5);
+ csd->erase_size = (a + 1) * (b + 1);
+ csd->erase_size <<= csd->write_blkbits - 9;
+ }
+
return 0;
}
@@ -207,11 +225,22 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}
+ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
+ if (card->csd.structure == 3) {
+ int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
+ if (ext_csd_struct > 2) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ "version %d\n", mmc_hostname(card->host),
+ ext_csd_struct);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 5) {
- printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
- "version %d\n", mmc_hostname(card->host),
- card->ext_csd.rev);
+ printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
+ mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}
@@ -222,7 +251,9 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
- if (card->ext_csd.sectors)
+
+ /* Cards with density > 2GiB are sector addressed */
+ if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
mmc_card_set_blockaddr(card);
}
@@ -247,8 +278,30 @@ static int mmc_read_ext_csd(struct mmc_card *card)
if (sa_shift > 0 && sa_shift <= 0x17)
card->ext_csd.sa_timeout =
1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
+ card->ext_csd.erase_group_def =
+ ext_csd[EXT_CSD_ERASE_GROUP_DEF];
+ card->ext_csd.hc_erase_timeout = 300 *
+ ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
+ card->ext_csd.hc_erase_size =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10;
}
+ if (card->ext_csd.rev >= 4) {
+ card->ext_csd.sec_trim_mult =
+ ext_csd[EXT_CSD_SEC_TRIM_MULT];
+ card->ext_csd.sec_erase_mult =
+ ext_csd[EXT_CSD_SEC_ERASE_MULT];
+ card->ext_csd.sec_feature_support =
+ ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
+ card->ext_csd.trim_timeout = 300 *
+ ext_csd[EXT_CSD_TRIM_MULT];
+ }
+
+ if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+
out:
kfree(ext_csd);
@@ -260,6 +313,8 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
+MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
@@ -271,6 +326,8 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_date.attr,
+ &dev_attr_erase_size.attr,
+ &dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
@@ -407,6 +464,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_read_ext_csd(card);
if (err)
goto free_card;
+ /* Erase size depends on CSD and Extended CSD */
+ mmc_set_erase_size(card);
}
/*
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5eac21d..0f52410 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -59,7 +59,7 @@ static const unsigned int tacc_mant[] = {
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
-static void mmc_decode_cid(struct mmc_card *card)
+void mmc_decode_cid(struct mmc_card *card)
{
u32 *resp = card->raw_cid;
@@ -119,6 +119,13 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
+
+ if (UNSTUFF_BITS(resp, 46, 1)) {
+ csd->erase_size = 1;
+ } else if (csd->write_blkbits >= 9) {
+ csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1;
+ csd->erase_size <<= csd->write_blkbits - 9;
+ }
break;
case 1:
/*
@@ -147,6 +154,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->r2w_factor = 4; /* Unused */
csd->write_blkbits = 9;
csd->write_partial = 0;
+ csd->erase_size = 1;
break;
default:
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
@@ -154,6 +162,8 @@ static int mmc_decode_csd(struct mmc_card *card)
return -EINVAL;
}
+ card->erase_size = csd->erase_size;
+
return 0;
}
@@ -179,10 +189,68 @@ static int mmc_decode_scr(struct mmc_card *card)
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
+ if (UNSTUFF_BITS(resp, 55, 1))
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+
return 0;
}
/*
+ * Fetch and process SD Status register.
+ */
+static int mmc_read_ssr(struct mmc_card *card)
+{
+ unsigned int au, es, et, eo;
+ int err, i;
+ u32 *ssr;
+
+ if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
+ printk(KERN_WARNING "%s: card lacks mandatory SD Status "
+ "function.\n", mmc_hostname(card->host));
+ return 0;
+ }
+
+ ssr = kmalloc(64, GFP_KERNEL);
+ if (!ssr)
+ return -ENOMEM;
+
+ err = mmc_app_sd_status(card, ssr);
+ if (err) {
+ printk(KERN_WARNING "%s: problem reading SD Status "
+ "register.\n", mmc_hostname(card->host));
+ err = 0;
+ goto out;
+ }
+
+ for (i = 0; i < 16; i++)
+ ssr[i] = be32_to_cpu(ssr[i]);
+
+ /*
+ * UNSTUFF_BITS only works with four u32s so we have to offset the
+ * bitfield positions accordingly.
+ */
+ au = UNSTUFF_BITS(ssr, 428 - 384, 4);
+ if (au > 0 || au <= 9) {
+ card->ssr.au = 1 << (au + 4);
+ es = UNSTUFF_BITS(ssr, 408 - 384, 16);
+ et = UNSTUFF_BITS(ssr, 402 - 384, 6);
+ eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
+ if (es && et) {
+ card->ssr.erase_timeout = (et * 1000) / es;
+ card->ssr.erase_offset = eo * 1000;
+ }
+ } else {
+ printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit "
+ "size.\n", mmc_hostname(card->host));
+ }
+out:
+ kfree(ssr);
+ return err;
+}
+
+/*
* Fetches and decodes switch information
*/
static int mmc_read_switch(struct mmc_card *card)
@@ -238,7 +306,7 @@ out:
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
-static int mmc_switch_hs(struct mmc_card *card)
+int mmc_sd_switch_hs(struct mmc_card *card)
{
int err;
u8 *status;
@@ -272,9 +340,9 @@ static int mmc_switch_hs(struct mmc_card *card)
printk(KERN_WARNING "%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(card->host));
+ err = 0;
} else {
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ err = 1;
}
out:
@@ -289,6 +357,8 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
+MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
@@ -302,6 +372,8 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_csd.attr,
&dev_attr_scr.attr,
&dev_attr_date.attr,
+ &dev_attr_erase_size.attr,
+ &dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
@@ -320,26 +392,16 @@ static const struct attribute_group *sd_attr_groups[] = {
NULL,
};
-static struct device_type sd_type = {
+struct device_type sd_type = {
.groups = sd_attr_groups,
};
/*
- * Handle the detection and initialisation of a card.
- *
- * In the case of a resume, "oldcard" will contain the card
- * we're trying to reinitialise.
+ * Fetch CID from card.
*/
-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
- struct mmc_card *oldcard)
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
{
- struct mmc_card *card;
int err;
- u32 cid[4];
- unsigned int max_dtr;
-
- BUG_ON(!host);
- WARN_ON(!host->claimed);
/*
* Since we're changing the OCR value, we seem to
@@ -361,92 +423,67 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_app_op_cond(host, ocr, NULL);
if (err)
- goto err;
+ return err;
- /*
- * Fetch CID from card.
- */
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
- if (err)
- goto err;
- if (oldcard) {
- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
- err = -ENOENT;
- goto err;
- }
-
- card = oldcard;
- } else {
- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host, &sd_type);
- if (IS_ERR(card)) {
- err = PTR_ERR(card);
- goto err;
- }
+ return err;
+}
- card->type = MMC_TYPE_SD;
- memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
- }
+int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
+{
+ int err;
/*
- * For native busses: get card RCA and quit open drain mode.
+ * Fetch CSD from card.
*/
- if (!mmc_host_is_spi(host)) {
- err = mmc_send_relative_addr(host, &card->rca);
- if (err)
- goto free_card;
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err)
+ return err;
- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
- }
+ err = mmc_decode_csd(card);
+ if (err)
+ return err;
- if (!oldcard) {
+ return 0;
+}
+
+int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
+ bool reinit)
+{
+ int err;
+
+ if (!reinit) {
/*
- * Fetch CSD from card.
+ * Fetch SCR from card.
*/
- err = mmc_send_csd(card, card->raw_csd);
- if (err)
- goto free_card;
-
- err = mmc_decode_csd(card);
+ err = mmc_app_send_scr(card, card->raw_scr);
if (err)
- goto free_card;
-
- mmc_decode_cid(card);
- }
+ return err;
- /*
- * Select card, as all following commands rely on that.
- */
- if (!mmc_host_is_spi(host)) {
- err = mmc_select_card(card);
+ err = mmc_decode_scr(card);
if (err)
- goto free_card;
- }
+ return err;
- if (!oldcard) {
/*
- * Fetch SCR from card.
+ * Fetch and process SD Status register.
*/
- err = mmc_app_send_scr(card, card->raw_scr);
+ err = mmc_read_ssr(card);
if (err)
- goto free_card;
+ return err;
- err = mmc_decode_scr(card);
- if (err < 0)
- goto free_card;
+ /* Erase init depends on CSD and SSR */
+ mmc_init_erase(card);
/*
* Fetch switch information from card.
*/
err = mmc_read_switch(card);
if (err)
- goto free_card;
+ return err;
}
/*
@@ -458,20 +495,34 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
- goto free_card;
+ return err;
}
/*
- * Attempt to change to high-speed (if supported)
+ * Check if read-only switch is active.
*/
- err = mmc_switch_hs(card);
- if (err)
- goto free_card;
+ if (!reinit) {
+ int ro = -1;
- /*
- * Compute bus speed.
- */
- max_dtr = (unsigned int)-1;
+ if (host->ops->get_ro)
+ ro = host->ops->get_ro(host);
+
+ if (ro < 0) {
+ printk(KERN_WARNING "%s: host does not "
+ "support reading read-only "
+ "switch. assuming write-enable.\n",
+ mmc_hostname(host));
+ } else if (ro > 0) {
+ mmc_card_set_readonly(card);
+ }
+ }
+
+ return 0;
+}
+
+unsigned mmc_sd_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr = (unsigned int)-1;
if (mmc_card_highspeed(card)) {
if (max_dtr > card->sw_caps.hs_max_dtr)
@@ -480,7 +531,97 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
max_dtr = card->csd.max_dtr;
}
- mmc_set_clock(host, max_dtr);
+ return max_dtr;
+}
+
+void mmc_sd_go_highspeed(struct mmc_card *card)
+{
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+}
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "oldcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+{
+ struct mmc_card *card;
+ int err;
+ u32 cid[4];
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ err = mmc_sd_get_cid(host, ocr, cid);
+ if (err)
+ return err;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+ return -ENOENT;
+
+ card = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host, &sd_type);
+ if (IS_ERR(card))
+ return PTR_ERR(card);
+
+ card->type = MMC_TYPE_SD;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
+
+ /*
+ * For native busses: get card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ return err;
+
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+
+ if (!oldcard) {
+ err = mmc_sd_get_csd(host, card);
+ if (err)
+ return err;
+
+ mmc_decode_cid(card);
+ }
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ return err;
+ }
+
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ if (err)
+ goto free_card;
+
+ /*
+ * Attempt to change to high-speed (if supported)
+ */
+ err = mmc_sd_switch_hs(card);
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
+ goto free_card;
+
+ /*
+ * Set bus speed.
+ */
+ mmc_set_clock(host, mmc_sd_get_max_clock(card));
/*
* Switch to wider bus (if supported).
@@ -494,30 +635,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
- /*
- * Check if read-only switch is active.
- */
- if (!oldcard) {
- if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {
- printk(KERN_WARNING "%s: host does not "
- "support reading read-only "
- "switch. assuming write-enable.\n",
- mmc_hostname(host));
- } else {
- if (host->ops->get_ro(host) > 0)
- mmc_card_set_readonly(card);
- }
- }
-
- if (!oldcard)
- host->card = card;
-
+ host->card = card;
return 0;
free_card:
if (!oldcard)
mmc_remove_card(card);
-err:
return err;
}
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
new file mode 100644
index 0000000..3d8800f
--- /dev/null
+++ b/drivers/mmc/core/sd.h
@@ -0,0 +1,17 @@
+#ifndef _MMC_CORE_SD_H
+#define _MMC_CORE_SD_H
+
+#include <linux/mmc/card.h>
+
+extern struct device_type sd_type;
+
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid);
+int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
+void mmc_decode_cid(struct mmc_card *card);
+int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
+ bool reinit);
+unsigned mmc_sd_get_max_clock(struct mmc_card *card);
+int mmc_sd_switch_hs(struct mmc_card *card);
+void mmc_sd_go_highspeed(struct mmc_card *card);
+
+#endif
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 63772e7..797cdb5 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -346,3 +346,51 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
return 0;
}
+int mmc_app_sd_status(struct mmc_card *card, void *ssr)
+{
+ int err;
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+ BUG_ON(!ssr);
+
+ /* NOTE: caller guarantees ssr is heap-allocated */
+
+ err = mmc_app_cmd(card->host, card);
+ if (err)
+ return err;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = SD_APP_SD_STATUS;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, ssr, 64);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index 9742d8a..ffc2305 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -19,6 +19,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
+int mmc_app_sd_status(struct mmc_card *card, void *ssr);
#endif
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index b9dee28..bd2755e 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -18,6 +18,7 @@
#include "core.h"
#include "bus.h"
+#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -62,13 +63,19 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn)
func->num = fn;
- ret = sdio_read_fbr(func);
- if (ret)
- goto fail;
+ if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) {
+ ret = sdio_read_fbr(func);
+ if (ret)
+ goto fail;
- ret = sdio_read_func_cis(func);
- if (ret)
- goto fail;
+ ret = sdio_read_func_cis(func);
+ if (ret)
+ goto fail;
+ } else {
+ func->vendor = func->card->cis.vendor;
+ func->device = func->card->cis.device;
+ func->max_blksize = func->card->cis.blksize;
+ }
card->sdio_func[fn - 1] = func;
@@ -159,9 +166,7 @@ static int sdio_enable_wide(struct mmc_card *card)
if (ret)
return ret;
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-
- return 0;
+ return 1;
}
/*
@@ -221,10 +226,34 @@ static int sdio_disable_wide(struct mmc_card *card)
return 0;
}
+
+static int sdio_enable_4bit_bus(struct mmc_card *card)
+{
+ int err;
+
+ if (card->type == MMC_TYPE_SDIO)
+ return sdio_enable_wide(card);
+
+ if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ return err;
+ } else
+ return 0;
+
+ err = sdio_enable_wide(card);
+ if (err <= 0)
+ mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
+
+ return err;
+}
+
+
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
-static int sdio_enable_hs(struct mmc_card *card)
+static int mmc_sdio_switch_hs(struct mmc_card *card, int enable)
{
int ret;
u8 speed;
@@ -239,16 +268,56 @@ static int sdio_enable_hs(struct mmc_card *card)
if (ret)
return ret;
- speed |= SDIO_SPEED_EHS;
+ if (enable)
+ speed |= SDIO_SPEED_EHS;
+ else
+ speed &= ~SDIO_SPEED_EHS;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (ret)
return ret;
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ return 1;
+}
- return 0;
+/*
+ * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported.
+ */
+static int sdio_enable_hs(struct mmc_card *card)
+{
+ int ret;
+
+ ret = mmc_sdio_switch_hs(card, true);
+ if (ret <= 0 || card->type == MMC_TYPE_SDIO)
+ return ret;
+
+ ret = mmc_sd_switch_hs(card);
+ if (ret <= 0)
+ mmc_sdio_switch_hs(card, false);
+
+ return ret;
+}
+
+static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr;
+
+ if (mmc_card_highspeed(card)) {
+ /*
+ * The SDIO specification doesn't mention how
+ * the CIS transfer speed register relates to
+ * high-speed, but it seems that 50 MHz is
+ * mandatory.
+ */
+ max_dtr = 50000000;
+ } else {
+ max_dtr = card->cis.max_dtr;
+ }
+
+ if (card->type == MMC_TYPE_SD_COMBO)
+ max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
+
+ return max_dtr;
}
/*
@@ -293,7 +362,24 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err;
}
- card->type = MMC_TYPE_SDIO;
+ err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid);
+
+ if (!err) {
+ card->type = MMC_TYPE_SD_COMBO;
+
+ if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
+ memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
+ mmc_remove_card(card);
+ return -ENOENT;
+ }
+ } else {
+ card->type = MMC_TYPE_SDIO;
+
+ if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
+ mmc_remove_card(card);
+ return -ENOENT;
+ }
+ }
/*
* Call the optional HC's init_card function to handle quirks.
@@ -313,6 +399,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Read CSD, before selecting the card
+ */
+ if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_get_csd(host, card);
+ if (err)
+ return err;
+
+ mmc_decode_cid(card);
+ }
+
+ /*
* Select card, as all following commands rely on that.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
@@ -321,6 +418,23 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto remove;
}
+ if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {
+ /*
+ * This is non-standard SDIO device, meaning it doesn't
+ * have any CIA (Common I/O area) registers present.
+ * It's host's responsibility to fill cccr and cis
+ * structures in init_card().
+ */
+ mmc_set_clock(host, card->cis.max_dtr);
+
+ if (card->cccr.high_speed) {
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ }
+
+ goto finish;
+ }
+
/*
* Read the common registers.
*/
@@ -339,43 +453,57 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device);
mmc_remove_card(card);
- if (!same) {
- err = -ENOENT;
- goto err;
- }
+ if (!same)
+ return -ENOENT;
+
card = oldcard;
return 0;
}
+ if (card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ /* handle as SDIO-only card if memory init failed */
+ if (err) {
+ mmc_go_idle(host);
+ if (mmc_host_is_spi(host))
+ /* should not fail, as it worked previously */
+ mmc_spi_set_crc(host, use_spi_crc);
+ card->type = MMC_TYPE_SDIO;
+ } else
+ card->dev.type = &sd_type;
+ }
+
+ /*
+ * If needed, disconnect card detection pull-up resistor.
+ */
+ err = sdio_disable_cd(card);
+ if (err)
+ goto remove;
+
/*
* Switch to high-speed (if supported).
*/
err = sdio_enable_hs(card);
- if (err)
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
goto remove;
/*
* Change to the card's maximum speed.
*/
- if (mmc_card_highspeed(card)) {
- /*
- * The SDIO specification doesn't mention how
- * the CIS transfer speed register relates to
- * high-speed, but it seems that 50 MHz is
- * mandatory.
- */
- mmc_set_clock(host, 50000000);
- } else {
- mmc_set_clock(host, card->cis.max_dtr);
- }
+ mmc_set_clock(host, mmc_sdio_get_max_clock(card));
/*
* Switch to wider bus (if supported).
*/
- err = sdio_enable_wide(card);
- if (err)
+ err = sdio_enable_4bit_bus(card);
+ if (err > 0)
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ else if (err)
goto remove;
+finish:
if (!oldcard)
host->card = card;
return 0;
@@ -487,9 +615,14 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_claim_host(host);
err = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER));
- if (!err)
+ if (!err) {
/* We may have switched to 1-bit mode during suspend. */
- err = sdio_enable_wide(host->card);
+ err = sdio_enable_4bit_bus(host->card);
+ if (err > 0) {
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ err = 0;
+ }
+ }
if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host);
mmc_release_host(host);
@@ -574,13 +707,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
card->sdio_funcs = 0;
/*
- * If needed, disconnect card detection pull-up resistor.
- */
- err = sdio_disable_cd(card);
- if (err)
- goto remove;
-
- /*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index d25e22c..283190b 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -121,6 +121,15 @@ config MMC_SDHCI_PLTFM
If unsure, say N.
+config MMC_SDHCI_CNS3XXX
+ bool "SDHCI support on the Cavium Networks CNS3xxx SoC"
+ depends on ARCH_CNS3XXX
+ depends on MMC_SDHCI_PLTFM
+ help
+ This selects the SDHCI support for CNS3xxx System-on-Chip devices.
+
+ If unsure, say N.
+
config MMC_SDHCI_S3C
tristate "SDHCI support on Samsung S3C SoC"
depends on MMC_SDHCI && (PLAT_S3C24XX || PLAT_S3C64XX)
@@ -247,12 +256,13 @@ config MMC_IMX
If unsure, say N.
-config MMC_MSM7X00A
- tristate "Qualcomm MSM 7X00A SDCC Controller Support"
- depends on MMC && ARCH_MSM && !ARCH_MSM7X30
+config MMC_MSM
+ tristate "Qualcomm SDCC Controller Support"
+ depends on MMC && ARCH_MSM
help
This provides support for the SD/MMC cell found in the
- MSM 7X00A controllers from Qualcomm.
+ MSM and QSD SOCs from Qualcomm. The controller also has
+ support for SDIO devices.
config MMC_MXC
tristate "Freescale i.MX2/3 Multimedia Card Interface support"
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f4e53c9..840bcb5 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -12,7 +12,6 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
-obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
@@ -22,7 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
-obj-$(CONFIG_MMC_MSM7X00A) += msm_sdcc.o
+obj-$(CONFIG_MMC_MSM) += msm_sdcc.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
@@ -38,6 +37,10 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
+obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
+sdhci-platform-y := sdhci-pltfm.o
+sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
+
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
sdhci-of-y := sdhci-of-core.o
sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 7b0f3ef..62a3582 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -182,7 +182,7 @@ mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
host->data_dma, sizeof(*host->data),
DMA_FROM_DEVICE);
- status = spi_sync(host->spi, &host->readback);
+ status = spi_sync_locked(host->spi, &host->readback);
if (host->dma_dev)
dma_sync_single_for_cpu(host->dma_dev,
@@ -541,7 +541,7 @@ mmc_spi_command_send(struct mmc_spi_host *host,
host->data_dma, sizeof(*host->data),
DMA_BIDIRECTIONAL);
}
- status = spi_sync(host->spi, &host->m);
+ status = spi_sync_locked(host->spi, &host->m);
if (host->dma_dev)
dma_sync_single_for_cpu(host->dma_dev,
@@ -685,7 +685,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
host->data_dma, sizeof(*scratch),
DMA_BIDIRECTIONAL);
- status = spi_sync(spi, &host->m);
+ status = spi_sync_locked(spi, &host->m);
if (status != 0) {
dev_dbg(&spi->dev, "write error (%d)\n", status);
@@ -822,7 +822,7 @@ mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t,
DMA_FROM_DEVICE);
}
- status = spi_sync(spi, &host->m);
+ status = spi_sync_locked(spi, &host->m);
if (host->dma_dev) {
dma_sync_single_for_cpu(host->dma_dev,
@@ -1018,7 +1018,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
host->data_dma, sizeof(*scratch),
DMA_BIDIRECTIONAL);
- tmp = spi_sync(spi, &host->m);
+ tmp = spi_sync_locked(spi, &host->m);
if (host->dma_dev)
dma_sync_single_for_cpu(host->dma_dev,
@@ -1084,6 +1084,9 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
}
#endif
+ /* request exclusive bus access */
+ spi_bus_lock(host->spi->master);
+
/* issue command; then optionally data and stop */
status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
if (status == 0 && mrq->data) {
@@ -1094,6 +1097,9 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
mmc_cs_off(host);
}
+ /* release the bus */
+ spi_bus_unlock(host->spi->master);
+
mmc_request_done(host->mmc, mrq);
}
@@ -1290,23 +1296,6 @@ mmc_spi_detect_irq(int irq, void *mmc)
return IRQ_HANDLED;
}
-struct count_children {
- unsigned n;
- struct bus_type *bus;
-};
-
-static int maybe_count_child(struct device *dev, void *c)
-{
- struct count_children *ccp = c;
-
- if (dev->bus == ccp->bus) {
- if (ccp->n)
- return -EBUSY;
- ccp->n++;
- }
- return 0;
-}
-
static int mmc_spi_probe(struct spi_device *spi)
{
void *ones;
@@ -1338,32 +1327,6 @@ static int mmc_spi_probe(struct spi_device *spi)
return status;
}
- /* We can use the bus safely iff nobody else will interfere with us.
- * Most commands consist of one SPI message to issue a command, then
- * several more to collect its response, then possibly more for data
- * transfer. Clocking access to other devices during that period will
- * corrupt the command execution.
- *
- * Until we have software primitives which guarantee non-interference,
- * we'll aim for a hardware-level guarantee.
- *
- * REVISIT we can't guarantee another device won't be added later...
- */
- if (spi->master->num_chipselect > 1) {
- struct count_children cc;
-
- cc.n = 0;
- cc.bus = spi->dev.bus;
- status = device_for_each_child(spi->dev.parent, &cc,
- maybe_count_child);
- if (status < 0) {
- dev_err(&spi->dev, "can't share SPI bus\n");
- return status;
- }
-
- dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n");
- }
-
/* We need a supply of ones to transmit. This is the only time
* the CPU touches these, so cache coherency isn't a concern.
*
@@ -1536,6 +1499,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi)
#if defined(CONFIG_OF)
static struct of_device_id mmc_spi_of_match_table[] __devinitdata = {
{ .compatible = "mmc-spi-slot", },
+ {},
};
#endif
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 24e0945..ff77523 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -160,18 +160,7 @@ msmsdcc_stop_data(struct msmsdcc_host *host)
uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
{
- switch (host->pdev_id) {
- case 1:
- return MSM_SDC1_PHYS + MMCIFIFO;
- case 2:
- return MSM_SDC2_PHYS + MMCIFIFO;
- case 3:
- return MSM_SDC3_PHYS + MMCIFIFO;
- case 4:
- return MSM_SDC4_PHYS + MMCIFIFO;
- }
- BUG();
- return 0;
+ return host->memres->start + MMCIFIFO;
}
static inline void
@@ -1057,26 +1046,10 @@ msmsdcc_init_dma(struct msmsdcc_host *host)
return 0;
}
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
-static void
-do_resume_work(struct work_struct *work)
-{
- struct msmsdcc_host *host =
- container_of(work, struct msmsdcc_host, resume_task);
- struct mmc_host *mmc = host->mmc;
-
- if (mmc) {
- mmc_resume_host(mmc);
- if (host->stat_irq)
- enable_irq(host->stat_irq);
- }
-}
-#endif
-
static int
msmsdcc_probe(struct platform_device *pdev)
{
- struct mmc_platform_data *plat = pdev->dev.platform_data;
+ struct msm_mmc_platform_data *plat = pdev->dev.platform_data;
struct msmsdcc_host *host;
struct mmc_host *mmc;
struct resource *cmd_irqres = NULL;
@@ -1145,15 +1118,6 @@ msmsdcc_probe(struct platform_device *pdev)
host->dmares = dmares;
spin_lock_init(&host->lock);
-#ifdef CONFIG_MMC_EMBEDDED_SDIO
- if (plat->embedded_sdio)
- mmc_set_embedded_sdio_data(mmc,
- &plat->embedded_sdio->cis,
- &plat->embedded_sdio->cccr,
- plat->embedded_sdio->funcs,
- plat->embedded_sdio->num_funcs);
-#endif
-
/*
* Setup DMA
*/
@@ -1314,6 +1278,24 @@ msmsdcc_probe(struct platform_device *pdev)
return ret;
}
+#ifdef CONFIG_PM
+#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
+static void
+do_resume_work(struct work_struct *work)
+{
+ struct msmsdcc_host *host =
+ container_of(work, struct msmsdcc_host, resume_task);
+ struct mmc_host *mmc = host->mmc;
+
+ if (mmc) {
+ mmc_resume_host(mmc);
+ if (host->stat_irq)
+ enable_irq(host->stat_irq);
+ }
+}
+#endif
+
+
static int
msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
{
@@ -1358,6 +1340,10 @@ msmsdcc_resume(struct platform_device *dev)
}
return 0;
}
+#else
+#define msmsdcc_suspend 0
+#define msmsdcc_resume 0
+#endif
static struct platform_driver msmsdcc_driver = {
.probe = msmsdcc_probe,
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index da0039c..ff2b0f7 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -225,7 +225,7 @@ struct msmsdcc_host {
u32 pwr;
u32 saved_irq0mask; /* MMCIMASK0 reg value */
- struct mmc_platform_data *plat;
+ struct msm_mmc_platform_data *plat;
struct timer_list timer;
unsigned int oldstat;
@@ -235,10 +235,6 @@ struct msmsdcc_host {
int cmdpoll;
struct msmsdcc_stats stats;
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
- struct work_struct resume_task;
-#endif
-
/* Command parameters */
unsigned int cmd_timeout;
unsigned int cmd_pio_irqmask;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b032828..4a8776f 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -28,6 +28,7 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
@@ -78,6 +79,7 @@
#define INT_EN_MASK 0x307F0033
#define BWR_ENABLE (1 << 4)
#define BRR_ENABLE (1 << 5)
+#define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
@@ -523,7 +525,8 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}
-static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
+static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
+ struct mmc_command *cmd)
{
unsigned int irq_mask;
@@ -532,6 +535,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
else
irq_mask = INT_EN_MASK;
+ /* Disable timeout for erases */
+ if (cmd->opcode == MMC_ERASE)
+ irq_mask &= ~DTO_ENABLE;
+
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
@@ -782,7 +789,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;
- omap_hsmmc_enable_irq(host);
+ omap_hsmmc_enable_irq(host, cmd);
host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -1273,8 +1280,11 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
struct mmc_data *data = host->mrq->data;
int dma_ch, req_in_progress;
- if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
- dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
+ if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+ dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
+ ch_status);
+ return;
+ }
spin_lock(&host->irq_lock);
if (host->dma_ch < 0) {
@@ -1598,6 +1608,14 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc)
return mmc_slot(host).get_ro(host->dev, 0);
}
+static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+
+ if (mmc_slot(host).init_card)
+ mmc_slot(host).init_card(card);
+}
+
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;
@@ -1869,6 +1887,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
@@ -1879,6 +1898,7 @@ static const struct mmc_host_ops omap_hsmmc_ps_ops = {
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
@@ -2094,12 +2114,25 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->max_seg_size = mmc->max_req_size;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_WAIT_WHILE_BUSY;
+ MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
- if (mmc_slot(host).wires >= 8)
+ switch (mmc_slot(host).wires) {
+ case 8:
mmc->caps |= MMC_CAP_8_BIT_DATA;
- else if (mmc_slot(host).wires >= 4)
+ /* Fall through */
+ case 4:
mmc->caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 1:
+ /* Nothing to crib here */
+ case 0:
+ /* Assuming nothing was given by board, Core use's 1-Bit */
+ break;
+ default:
+ /* Completely unexpected.. Core goes with 1-Bit Width */
+ dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!"
+ "using 1 instead\n", mmc_slot(host).wires);
+ }
if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
new file mode 100644
index 0000000..b7050b3
--- /dev/null
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -0,0 +1,97 @@
+/*
+ * SDHCI support for CNS3xxx SoC
+ *
+ * Copyright 2008 Cavium Networks
+ * Copyright 2010 MontaVista Software, LLC.
+ *
+ * Authors: Scott Shu
+ * Anton Vorontsov <avorontsov@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mmc/host.h>
+#include <linux/sdhci-pltfm.h>
+#include <mach/cns3xxx.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host)
+{
+ return 150000000;
+}
+
+static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct device *dev = mmc_dev(host->mmc);
+ int div = 1;
+ u16 clk;
+ unsigned long timeout;
+
+ if (clock == host->clock)
+ return;
+
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+ goto out;
+
+ while (host->max_clk / div > clock) {
+ /*
+ * On CNS3xxx divider grows linearly up to 4, and then
+ * exponentially up to 256.
+ */
+ if (div < 4)
+ div += 1;
+ else if (div < 256)
+ div *= 2;
+ else
+ break;
+ }
+
+ dev_dbg(dev, "desired SD clock: %d, actual: %d\n",
+ clock, host->max_clk / div);
+
+ /* Divide by 3 is special. */
+ if (div != 3)
+ div >>= 1;
+
+ clk = div << SDHCI_DIVIDER_SHIFT;
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ timeout = 20;
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ dev_warn(dev, "clock is unstable");
+ break;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+out:
+ host->clock = clock;
+}
+
+static struct sdhci_ops sdhci_cns3xxx_ops = {
+ .get_max_clock = sdhci_cns3xxx_get_max_clk,
+ .set_clock = sdhci_cns3xxx_set_clock,
+};
+
+struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
+ .ops = &sdhci_cns3xxx_ops,
+ .quirks = SDHCI_QUIRK_BROKEN_DMA |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_NONSTANDARD_CLOCK,
+};
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
index a2e9820..c51b711 100644
--- a/drivers/mmc/host/sdhci-of-core.c
+++ b/drivers/mmc/host/sdhci-of-core.c
@@ -85,14 +85,14 @@ void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
#ifdef CONFIG_PM
-static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+static int sdhci_of_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
return mmc_suspend_host(host->mmc);
}
-static int sdhci_of_resume(struct of_device *ofdev)
+static int sdhci_of_resume(struct platform_device *ofdev)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
@@ -115,7 +115,7 @@ static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
}
-static int __devinit sdhci_of_probe(struct of_device *ofdev,
+static int __devinit sdhci_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -154,6 +154,10 @@ static int __devinit sdhci_of_probe(struct of_device *ofdev,
host->ops = &sdhci_of_data->ops;
}
+ if (of_get_property(np, "sdhci,auto-cmd12", NULL))
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+
+
if (of_get_property(np, "sdhci,1-bit-only", NULL))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
@@ -179,7 +183,7 @@ err_addr_map:
return ret;
}
-static int __devexit sdhci_of_remove(struct of_device *ofdev)
+static int __devexit sdhci_of_remove(struct platform_device *ofdev)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 65483fd..e8aa99d 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -17,6 +17,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/mmc/host.h>
@@ -84,7 +85,30 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG ||
chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY)
chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET;
+ return 0;
+}
+
+static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->caps =
+ ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
+ & SDHCI_TIMEOUT_CLK_MASK) |
+
+ ((0x21 << SDHCI_CLOCK_BASE_SHIFT)
+ & SDHCI_CLOCK_BASE_MASK) |
+ SDHCI_TIMEOUT_CLK_UNIT |
+ SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_DO_SDMA;
+ return 0;
+}
+
+static int ricoh_mmc_resume(struct sdhci_pci_chip *chip)
+{
+ /* Apply a delay to allow controller to settle */
+ /* Otherwise it becomes confused if card state changed
+ during suspend */
+ msleep(500);
return 0;
}
@@ -95,6 +119,15 @@ static const struct sdhci_pci_fixes sdhci_ricoh = {
SDHCI_QUIRK_CLOCK_BEFORE_RESET,
};
+static const struct sdhci_pci_fixes sdhci_ricoh_mmc = {
+ .probe_slot = ricoh_mmc_probe_slot,
+ .resume = ricoh_mmc_resume,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_MISSING_CAPS
+};
+
static const struct sdhci_pci_fixes sdhci_ene_712 = {
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_BROKEN_DMA,
@@ -374,6 +407,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
},
{
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = 0x843,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = 0xe822,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB712_SD,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index b6ee0d7..e045e3c 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/highmem.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
@@ -32,6 +33,7 @@
#include <linux/sdhci-pltfm.h>
#include "sdhci.h"
+#include "sdhci-pltfm.h"
/*****************************************************************************\
* *
@@ -51,10 +53,14 @@ static struct sdhci_ops sdhci_pltfm_ops = {
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
struct sdhci_host *host;
struct resource *iomem;
int ret;
+ if (!pdata && platid && platid->driver_data)
+ pdata = (void *)platid->driver_data;
+
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
@@ -150,6 +156,15 @@ static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id sdhci_pltfm_ids[] = {
+ { "sdhci", },
+#ifdef CONFIG_MMC_SDHCI_CNS3XXX
+ { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata },
+#endif
+ { },
+};
+MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
+
static struct platform_driver sdhci_pltfm_driver = {
.driver = {
.name = "sdhci",
@@ -157,6 +172,7 @@ static struct platform_driver sdhci_pltfm_driver = {
},
.probe = sdhci_pltfm_probe,
.remove = __devexit_p(sdhci_pltfm_remove),
+ .id_table = sdhci_pltfm_ids,
};
/*****************************************************************************\
@@ -181,4 +197,3 @@ module_exit(sdhci_drv_exit);
MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:sdhci");
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
new file mode 100644
index 0000000..900f329
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2010 MontaVista Software, LLC.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
+#define _DRIVERS_MMC_SDHCI_PLTFM_H
+
+#include <linux/sdhci-pltfm.h>
+
+extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata;
+
+#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index ad30f07..0a7f261 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/gpio.h>
#include <linux/mmc/host.h>
@@ -44,6 +45,8 @@ struct sdhci_s3c {
struct resource *ioarea;
struct s3c_sdhci_platdata *pdata;
unsigned int cur_clk;
+ int ext_cd_irq;
+ int ext_cd_gpio;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
@@ -110,11 +113,6 @@ static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
return max;
}
-static unsigned int sdhci_s3c_get_timeout_clk(struct sdhci_host *host)
-{
- return sdhci_s3c_get_max_clk(host) / 1000000;
-}
-
/**
* sdhci_s3c_consider_clock - consider one the bus clocks for current setting
* @ourhost: Our SDHCI instance.
@@ -188,7 +186,6 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
ourhost->cur_clk = best_src;
host->max_clk = clk_get_rate(clk);
- host->timeout_clk = sdhci_s3c_get_timeout_clk(host);
ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
@@ -209,12 +206,93 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
}
}
+/**
+ * sdhci_s3c_get_min_clock - callback to get minimal supported clock value
+ * @host: The SDHCI host being queried
+ *
+ * To init mmc host properly a minimal clock value is needed. For high system
+ * bus clock's values the standard formula gives values out of allowed range.
+ * The clock still can be set to lower values, if clock source other then
+ * system bus is selected.
+*/
+static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
+{
+ struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned int delta, min = UINT_MAX;
+ int src;
+
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ delta = sdhci_s3c_consider_clock(ourhost, src, 0);
+ if (delta == UINT_MAX)
+ continue;
+ /* delta is a negative value in this case */
+ if (-delta < min)
+ min = -delta;
+ }
+ return min;
+}
+
static struct sdhci_ops sdhci_s3c_ops = {
.get_max_clock = sdhci_s3c_get_max_clk,
- .get_timeout_clock = sdhci_s3c_get_timeout_clk,
.set_clock = sdhci_s3c_set_clock,
+ .get_min_clock = sdhci_s3c_get_min_clock,
};
+static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
+{
+ struct sdhci_host *host = platform_get_drvdata(dev);
+ if (host) {
+ mutex_lock(&host->lock);
+ if (state) {
+ dev_dbg(&dev->dev, "card inserted.\n");
+ host->flags &= ~SDHCI_DEVICE_DEAD;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ } else {
+ dev_dbg(&dev->dev, "card removed.\n");
+ host->flags |= SDHCI_DEVICE_DEAD;
+ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+ sdhci_card_detect(host);
+ mutex_unlock(&host->lock);
+ }
+}
+
+static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id)
+{
+ struct sdhci_s3c *sc = dev_id;
+ int status = gpio_get_value(sc->ext_cd_gpio);
+ if (sc->pdata->ext_cd_gpio_invert)
+ status = !status;
+ sdhci_s3c_notify_change(sc->pdev, status);
+ return IRQ_HANDLED;
+}
+
+static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
+{
+ struct s3c_sdhci_platdata *pdata = sc->pdata;
+ struct device *dev = &sc->pdev->dev;
+
+ if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
+ sc->ext_cd_gpio = pdata->ext_cd_gpio;
+ sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
+ if (sc->ext_cd_irq &&
+ request_threaded_irq(sc->ext_cd_irq, NULL,
+ sdhci_s3c_gpio_card_detect_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ dev_name(dev), sc) == 0) {
+ int status = gpio_get_value(sc->ext_cd_gpio);
+ if (pdata->ext_cd_gpio_invert)
+ status = !status;
+ sdhci_s3c_notify_change(sc->pdev, status);
+ } else {
+ dev_warn(dev, "cannot request irq for card detect\n");
+ sc->ext_cd_irq = 0;
+ }
+ } else {
+ dev_err(dev, "cannot request gpio for card detect\n");
+ }
+}
+
static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
{
struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
@@ -252,6 +330,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
sc->host = host;
sc->pdev = pdev;
sc->pdata = pdata;
+ sc->ext_cd_gpio = -1; /* invalid gpio number */
platform_set_drvdata(pdev, host);
@@ -318,6 +397,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
/* Setup quirks for the controller */
host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
#ifndef CONFIG_MMC_SDHCI_S3C_DMA
@@ -332,15 +412,34 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
* SDHCI block, or a missing configuration that needs to be set. */
host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ;
+ if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
+ pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
+ if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+ host->mmc->caps = MMC_CAP_NONREMOVABLE;
+
host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE);
+ /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
+ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(dev, "sdhci_add_host() failed\n");
goto err_add_host;
}
+ /* The following two methods of card detection might call
+ sdhci_s3c_notify_change() immediately, so they can be called
+ only after sdhci_add_host(). Setup errors are ignored. */
+ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init)
+ pdata->ext_cd_init(&sdhci_s3c_notify_change);
+ if (pdata->cd_type == S3C_SDHCI_CD_GPIO &&
+ gpio_is_valid(pdata->ext_cd_gpio))
+ sdhci_s3c_setup_card_detect_gpio(sc);
+
return 0;
err_add_host:
@@ -365,10 +464,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
{
+ struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_s3c *sc = sdhci_priv(host);
int ptr;
+ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
+ pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);
+
+ if (sc->ext_cd_irq)
+ free_irq(sc->ext_cd_irq, sc);
+
+ if (gpio_is_valid(sc->ext_cd_gpio))
+ gpio_free(sc->ext_cd_gpio);
+
sdhci_remove_host(host, 1);
for (ptr = 0; ptr < 3; ptr++) {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c6d1bd8..7855121 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -19,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
+#include <linux/regulator/consumer.h>
#include <linux/leds.h>
@@ -817,8 +818,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
WARN_ON(!host->data);
mode = SDHCI_TRNS_BLK_CNT_EN;
- if (data->blocks > 1)
- mode |= SDHCI_TRNS_MULTI;
+ if (data->blocks > 1) {
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+ mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
+ else
+ mode |= SDHCI_TRNS_MULTI;
+ }
if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
if (host->flags & SDHCI_REQ_USE_DMA)
@@ -1108,6 +1113,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
#ifndef SDHCI_USE_LEDS_CLASS
sdhci_activate_led(host);
#endif
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
+ if (mrq->stop) {
+ mrq->data->stop = NULL;
+ mrq->stop = NULL;
+ }
+ }
host->mrq = mrq;
@@ -1159,6 +1170,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ if (ios->bus_width == MMC_BUS_WIDTH_8)
+ ctrl |= SDHCI_CTRL_8BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
+
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
else
@@ -1603,7 +1619,10 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
free_irq(host->irq, host);
- return 0;
+ if (host->vmmc)
+ ret = regulator_disable(host->vmmc);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(sdhci_suspend_host);
@@ -1612,6 +1631,13 @@ int sdhci_resume_host(struct sdhci_host *host)
{
int ret;
+ if (host->vmmc) {
+ int ret = regulator_enable(host->vmmc);
+ if (ret)
+ return ret;
+ }
+
+
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
@@ -1687,7 +1713,8 @@ int sdhci_add_host(struct sdhci_host *host)
host->version);
}
- caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
+ sdhci_readl(host, SDHCI_CAPABILITIES);
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
@@ -1785,13 +1812,12 @@ int sdhci_add_host(struct sdhci_host *host)
* Set host parameters.
*/
mmc->ops = &sdhci_ops;
- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK &&
- host->ops->set_clock && host->ops->get_min_clock)
+ if (host->ops->get_min_clock)
mmc->f_min = host->ops->get_min_clock(host);
else
mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk;
- mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
@@ -1884,6 +1910,14 @@ int sdhci_add_host(struct sdhci_host *host)
if (ret)
goto untasklet;
+ host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
+ if (IS_ERR(host->vmmc)) {
+ printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
+ host->vmmc = NULL;
+ } else {
+ regulator_enable(host->vmmc);
+ }
+
sdhci_init(host, 0);
#ifdef CONFIG_MMC_DEBUG
@@ -1968,6 +2002,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
+ if (host->vmmc) {
+ regulator_disable(host->vmmc);
+ regulator_put(host->vmmc);
+ }
+
kfree(host->adma_desc);
kfree(host->align_buffer);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c846813..036cfae 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -72,6 +72,7 @@
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
+#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_POWER_CONTROL 0x29
#define SDHCI_POWER_ON 0x01
@@ -240,12 +241,18 @@ struct sdhci_host {
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
/* Controller cannot support End Attribute in NOP ADMA descriptor */
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
+/* Controller is missing device caps. Use caps provided by host */
+#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
+/* Controller uses Auto CMD12 command to stop the transfer */
+#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
const struct sdhci_ops *ops; /* Low level hw interface */
+ struct regulator *vmmc; /* Power regulator */
+
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
u64 dma_mask; /* custom DMA mask */
@@ -292,6 +299,8 @@ struct sdhci_host {
struct timer_list timer; /* Timer for timeouts */
+ unsigned int caps; /* Alternative capabilities */
+
unsigned long private[0] ____cacheline_aligned;
};
@@ -407,6 +416,7 @@ static inline void *sdhci_priv(struct sdhci_host *host)
return (void *)host->private;
}
+extern void sdhci_card_detect(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 6ac5f9f..00af55d 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -43,7 +43,7 @@ struct of_flash {
#ifdef CONFIG_MTD_PARTITIONS
#define OF_FLASH_PARTS(info) ((info)->parts)
-static int parse_obsolete_partitions(struct of_device *dev,
+static int parse_obsolete_partitions(struct platform_device *dev,
struct of_flash *info,
struct device_node *dp)
{
@@ -93,7 +93,7 @@ static int parse_obsolete_partitions(struct of_device *dev,
#define parse_partitions(info, dev) (0)
#endif /* MTD_PARTITIONS */
-static int of_flash_remove(struct of_device *dev)
+static int of_flash_remove(struct platform_device *dev)
{
struct of_flash *info;
int i;
@@ -140,7 +140,7 @@ static int of_flash_remove(struct of_device *dev)
/* Helper function to handle probing of the obsolete "direct-mapped"
* compatible binding, which has an extra "probe-type" property
* describing the type of flash probe necessary. */
-static struct mtd_info * __devinit obsolete_probe(struct of_device *dev,
+static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev,
struct map_info *map)
{
struct device_node *dp = dev->dev.of_node;
@@ -215,7 +215,7 @@ static void __devinit of_free_probes(const char **probes)
}
#endif
-static int __devinit of_flash_probe(struct of_device *dev,
+static int __devinit of_flash_probe(struct platform_device *dev,
const struct of_device_id *match)
{
#ifdef CONFIG_MTD_PARTITIONS
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 8984236..3582ba1 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -48,7 +48,7 @@ struct map_info uflash_map_templ = {
.bankwidth = UFLASH_BUSWIDTH,
};
-int uflash_devinit(struct of_device *op, struct device_node *dp)
+int uflash_devinit(struct platform_device *op, struct device_node *dp)
{
struct uflash_dev *up;
@@ -108,7 +108,7 @@ int uflash_devinit(struct of_device *op, struct device_node *dp)
return 0;
}
-static int __devinit uflash_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit uflash_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -121,7 +121,7 @@ static int __devinit uflash_probe(struct of_device *op, const struct of_device_i
return uflash_devinit(op, dp);
}
-static int __devexit uflash_remove(struct of_device *op)
+static int __devexit uflash_remove(struct platform_device *op)
{
struct uflash_dev *up = dev_get_drvdata(&op->dev);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 1d2144d..62e6870 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -29,6 +29,7 @@
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/mutex.h>
@@ -87,14 +88,14 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
buf = req->buffer;
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
return -EIO;
if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
get_capacity(req->rq_disk))
return -EIO;
- if (blk_discard_rq(req))
+ if (req->cmd_flags & REQ_DISCARD)
return tr->discard(dev, block, nsect);
switch(rq_data_dir(req)) {
@@ -178,8 +179,9 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
int ret;
if (!dev)
- return -ERESTARTSYS;
+ return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
+ lock_kernel();
mutex_lock(&dev->lock);
if (!dev->mtd) {
@@ -196,6 +198,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
+ unlock_kernel();
return ret;
}
@@ -207,6 +210,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode)
if (!dev)
return ret;
+ lock_kernel();
mutex_lock(&dev->lock);
/* Release one reference, we sure its not the last one here*/
@@ -219,6 +223,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode)
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
+ unlock_kernel();
return ret;
}
@@ -251,6 +256,7 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
if (!dev)
return ret;
+ lock_kernel();
mutex_lock(&dev->lock);
if (!dev->mtd)
@@ -265,6 +271,7 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
}
unlock:
mutex_unlock(&dev->lock);
+ unlock_kernel();
blktrans_dev_put(dev);
return ret;
}
@@ -273,7 +280,7 @@ static const struct block_device_operations mtd_blktrans_ops = {
.owner = THIS_MODULE,
.open = blktrans_open,
.release = blktrans_release,
- .locked_ioctl = blktrans_ioctl,
+ .ioctl = blktrans_ioctl,
.getgeo = blktrans_getgeo,
};
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 5084cc5..80de0bf 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -958,7 +958,7 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
return 0;
}
-static int fsl_elbc_ctrl_remove(struct of_device *ofdev)
+static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
{
struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
int i;
@@ -1013,7 +1013,7 @@ static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
* in the chip probe function.
*/
-static int __devinit fsl_elbc_ctrl_probe(struct of_device *ofdev,
+static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *child;
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index 1312eda..4eff8b2 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -217,7 +217,7 @@ err:
return ret;
}
-static int __devinit fun_probe(struct of_device *ofdev,
+static int __devinit fun_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct fsl_upm_nand *fun;
@@ -335,7 +335,7 @@ err1:
return ret;
}
-static int __devexit fun_remove(struct of_device *ofdev)
+static int __devexit fun_remove(struct platform_device *ofdev)
{
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
int i;
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 0a130dc..df0c1da 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -647,7 +647,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
iounmap(prv->csreg);
}
-static int __devinit mpc5121_nfc_probe(struct of_device *op,
+static int __devinit mpc5121_nfc_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *rootnode, *dn = op->dev.of_node;
@@ -869,7 +869,7 @@ error:
return retval;
}
-static int __devexit mpc5121_nfc_remove(struct of_device *op)
+static int __devexit mpc5121_nfc_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct mtd_info *mtd = dev_get_drvdata(dev);
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 98fd2bd..510554e 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -35,7 +35,7 @@
struct ndfc_controller {
- struct of_device *ofdev;
+ struct platform_device *ofdev;
void __iomem *ndfcbase;
struct mtd_info mtd;
struct nand_chip chip;
@@ -225,7 +225,7 @@ err:
return ret;
}
-static int __devinit ndfc_probe(struct of_device *ofdev,
+static int __devinit ndfc_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
@@ -277,7 +277,7 @@ static int __devinit ndfc_probe(struct of_device *ofdev,
return 0;
}
-static int __devexit ndfc_remove(struct of_device *ofdev)
+static int __devexit ndfc_remove(struct platform_device *ofdev)
{
struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index f02af24..6ddb246 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -89,7 +89,7 @@ int pasemi_device_ready(struct mtd_info *mtd)
return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR);
}
-static int __devinit pasemi_nand_probe(struct of_device *ofdev,
+static int __devinit pasemi_nand_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct pci_dev *pdev;
@@ -185,7 +185,7 @@ static int __devinit pasemi_nand_probe(struct of_device *ofdev,
return err;
}
-static int __devexit pasemi_nand_remove(struct of_device *ofdev)
+static int __devexit pasemi_nand_remove(struct platform_device *ofdev)
{
struct nand_chip *chip;
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
index cc728b1..a8e403e 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/socrates_nand.c
@@ -162,7 +162,7 @@ static const char *part_probes[] = { "cmdlinepart", NULL };
/*
* Probe for the NAND device.
*/
-static int __devinit socrates_nand_probe(struct of_device *ofdev,
+static int __devinit socrates_nand_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct socrates_nand_host *host;
@@ -276,7 +276,7 @@ out:
/*
* Remove a NAND device.
*/
-static int __devexit socrates_nand_remove(struct of_device *ofdev)
+static int __devexit socrates_nand_remove(struct platform_device *ofdev)
{
struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
struct mtd_info *mtd = &host->mtd;
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 4f1cc71..6028226 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -241,7 +241,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
static spinlock_t mdio_lock;
static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
-struct mii_bus *mdio_bus;
+static struct mii_bus *mdio_bus;
static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
diff --git a/drivers/net/caif/caif_spi_slave.c b/drivers/net/caif/caif_spi_slave.c
index 077ccf8..2111dbf 100644
--- a/drivers/net/caif/caif_spi_slave.c
+++ b/drivers/net/caif/caif_spi_slave.c
@@ -22,13 +22,13 @@
#include <net/caif/caif_spi.h>
#ifndef CONFIG_CAIF_SPI_SYNC
-#define SPI_DATA_POS SPI_CMD_SZ
+#define SPI_DATA_POS 0
static inline int forward_to_spi_cmd(struct cfspi *cfspi)
{
return cfspi->rx_cpck_len;
}
#else
-#define SPI_DATA_POS 0
+#define SPI_DATA_POS SPI_CMD_SZ
static inline int forward_to_spi_cmd(struct cfspi *cfspi)
{
return 0;
diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c
index af75393..b1bdc90 100644
--- a/drivers/net/can/mscan/mpc5xxx_can.c
+++ b/drivers/net/can/mscan/mpc5xxx_can.c
@@ -38,7 +38,7 @@
struct mpc5xxx_can_data {
unsigned int type;
- u32 (*get_clock)(struct of_device *ofdev, const char *clock_name,
+ u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name,
int *mscan_clksrc);
};
@@ -48,7 +48,7 @@ static struct of_device_id __devinitdata mpc52xx_cdm_ids[] = {
{}
};
-static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -101,7 +101,7 @@ static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev,
return freq;
}
#else /* !CONFIG_PPC_MPC52xx */
-static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -129,7 +129,7 @@ static struct of_device_id __devinitdata mpc512x_clock_ids[] = {
{}
};
-static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -239,7 +239,7 @@ exit_unmap:
return freq;
}
#else /* !CONFIG_PPC_MPC512x */
-static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -247,7 +247,7 @@ static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev,
}
#endif /* CONFIG_PPC_MPC512x */
-static int __devinit mpc5xxx_can_probe(struct of_device *ofdev,
+static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct mpc5xxx_can_data *data = (struct mpc5xxx_can_data *)id->data;
@@ -317,7 +317,7 @@ exit_unmap_mem:
return err;
}
-static int __devexit mpc5xxx_can_remove(struct of_device *ofdev)
+static int __devexit mpc5xxx_can_remove(struct platform_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct mscan_priv *priv = netdev_priv(dev);
@@ -334,7 +334,7 @@ static int __devexit mpc5xxx_can_remove(struct of_device *ofdev)
#ifdef CONFIG_PM
static struct mscan_regs saved_regs;
-static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state)
+static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct mscan_priv *priv = netdev_priv(dev);
@@ -345,7 +345,7 @@ static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int mpc5xxx_can_resume(struct of_device *ofdev)
+static int mpc5xxx_can_resume(struct platform_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct mscan_priv *priv = netdev_priv(dev);
diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c
index ac1a83d..5bfccfd 100644
--- a/drivers/net/can/sja1000/sja1000_of_platform.c
+++ b/drivers/net/can/sja1000/sja1000_of_platform.c
@@ -67,7 +67,7 @@ static void sja1000_ofp_write_reg(const struct sja1000_priv *priv,
out_8(priv->reg_base + reg, val);
}
-static int __devexit sja1000_ofp_remove(struct of_device *ofdev)
+static int __devexit sja1000_ofp_remove(struct platform_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct sja1000_priv *priv = netdev_priv(dev);
@@ -87,7 +87,7 @@ static int __devexit sja1000_ofp_remove(struct of_device *ofdev)
return 0;
}
-static int __devinit sja1000_ofp_probe(struct of_device *ofdev,
+static int __devinit sja1000_ofp_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct device_node *np = ofdev->dev.of_node;
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 0060e42..99a9299 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -413,7 +413,7 @@ struct ehea_port_res {
struct ehea_adapter {
u64 handle;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct ehea_port *port[EHEA_MAX_PORTS];
struct ehea_eq *neq; /* notification event queue */
struct tasklet_struct neq_tasklet;
@@ -465,7 +465,7 @@ struct ehea_port {
struct net_device *netdev;
struct net_device_stats stats;
struct ehea_port_res port_res[EHEA_MAX_PORT_RES];
- struct of_device ofdev; /* Open Firmware Device */
+ struct platform_device ofdev; /* Open Firmware Device */
struct ehea_mc_list *mc_list; /* Multicast MAC addresses */
struct vlan_group *vgrp;
struct ehea_eq *qp_eq;
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 3beba70..897719b 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -107,10 +107,10 @@ struct ehea_fw_handle_array ehea_fw_handles;
struct ehea_bcmc_reg_array ehea_bcmc_regs;
-static int __devinit ehea_probe_adapter(struct of_device *dev,
+static int __devinit ehea_probe_adapter(struct platform_device *dev,
const struct of_device_id *id);
-static int __devexit ehea_remove(struct of_device *dev);
+static int __devexit ehea_remove(struct platform_device *dev);
static struct of_device_id ehea_device_table[] = {
{
@@ -3376,7 +3376,7 @@ static ssize_t ehea_remove_port(struct device *dev,
static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port);
static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port);
-int ehea_create_device_sysfs(struct of_device *dev)
+int ehea_create_device_sysfs(struct platform_device *dev)
{
int ret = device_create_file(&dev->dev, &dev_attr_probe_port);
if (ret)
@@ -3387,13 +3387,13 @@ out:
return ret;
}
-void ehea_remove_device_sysfs(struct of_device *dev)
+void ehea_remove_device_sysfs(struct platform_device *dev)
{
device_remove_file(&dev->dev, &dev_attr_probe_port);
device_remove_file(&dev->dev, &dev_attr_remove_port);
}
-static int __devinit ehea_probe_adapter(struct of_device *dev,
+static int __devinit ehea_probe_adapter(struct platform_device *dev,
const struct of_device_id *id)
{
struct ehea_adapter *adapter;
@@ -3492,7 +3492,7 @@ out:
return ret;
}
-static int __devexit ehea_remove(struct of_device *dev)
+static int __devexit ehea_remove(struct platform_device *dev)
{
struct ehea_adapter *adapter = dev_get_drvdata(&dev->dev);
int i;
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index d1a5b17..e3e10b4 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -850,7 +850,7 @@ static const struct net_device_ops mpc52xx_fec_netdev_ops = {
/* ======================================================================== */
static int __devinit
-mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
+mpc52xx_fec_probe(struct platform_device *op, const struct of_device_id *match)
{
int rv;
struct net_device *ndev;
@@ -995,7 +995,7 @@ err_netdev:
}
static int
-mpc52xx_fec_remove(struct of_device *op)
+mpc52xx_fec_remove(struct platform_device *op)
{
struct net_device *ndev;
struct mpc52xx_fec_priv *priv;
@@ -1025,7 +1025,7 @@ mpc52xx_fec_remove(struct of_device *op)
}
#ifdef CONFIG_PM
-static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
+static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state)
{
struct net_device *dev = dev_get_drvdata(&op->dev);
@@ -1035,7 +1035,7 @@ static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
return 0;
}
-static int mpc52xx_fec_of_resume(struct of_device *op)
+static int mpc52xx_fec_of_resume(struct platform_device *op)
{
struct net_device *dev = dev_get_drvdata(&op->dev);
diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c
index dbaf72c..0b4cb6f 100644
--- a/drivers/net/fec_mpc52xx_phy.c
+++ b/drivers/net/fec_mpc52xx_phy.c
@@ -61,7 +61,7 @@ static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg,
data | FEC_MII_WRITE_FRAME);
}
-static int mpc52xx_fec_mdio_probe(struct of_device *of,
+static int mpc52xx_fec_mdio_probe(struct platform_device *of,
const struct of_device_id *match)
{
struct device *dev = &of->dev;
@@ -122,7 +122,7 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of,
return err;
}
-static int mpc52xx_fec_mdio_remove(struct of_device *of)
+static int mpc52xx_fec_mdio_remove(struct platform_device *of)
{
struct device *dev = &of->dev;
struct mii_bus *bus = dev_get_drvdata(dev);
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index f08cff9..d6e3111 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -997,7 +997,7 @@ static const struct net_device_ops fs_enet_netdev_ops = {
#endif
};
-static int __devinit fs_enet_probe(struct of_device *ofdev,
+static int __devinit fs_enet_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct net_device *ndev;
@@ -1105,7 +1105,7 @@ out_free_fpi:
return ret;
}
-static int fs_enet_remove(struct of_device *ofdev)
+static int fs_enet_remove(struct platform_device *ofdev)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct fs_enet_private *fep = netdev_priv(ndev);
diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c
index 48e91b6..7a84e45 100644
--- a/drivers/net/fs_enet/mac-fcc.c
+++ b/drivers/net/fs_enet/mac-fcc.c
@@ -84,7 +84,7 @@ static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op)
static int do_pd_setup(struct fs_enet_private *fep)
{
- struct of_device *ofdev = to_of_device(fep->dev);
+ struct platform_device *ofdev = to_platform_device(fep->dev);
struct fs_platform_info *fpi = fep->fpi;
int ret = -EINVAL;
diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c
index 7ca1642..61035fc 100644
--- a/drivers/net/fs_enet/mac-fec.c
+++ b/drivers/net/fs_enet/mac-fec.c
@@ -96,7 +96,7 @@ static int whack_reset(struct fec __iomem *fecp)
static int do_pd_setup(struct fs_enet_private *fep)
{
- struct of_device *ofdev = to_of_device(fep->dev);
+ struct platform_device *ofdev = to_platform_device(fep->dev);
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
if (fep->interrupt == NO_IRQ)
diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c
index a3c4454..22a02a7 100644
--- a/drivers/net/fs_enet/mac-scc.c
+++ b/drivers/net/fs_enet/mac-scc.c
@@ -96,7 +96,7 @@ static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op)
static int do_pd_setup(struct fs_enet_private *fep)
{
- struct of_device *ofdev = to_of_device(fep->dev);
+ struct platform_device *ofdev = to_platform_device(fep->dev);
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
if (fep->interrupt == NO_IRQ)
diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c
index 3607340..3cda2b5 100644
--- a/drivers/net/fs_enet/mii-bitbang.c
+++ b/drivers/net/fs_enet/mii-bitbang.c
@@ -150,7 +150,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus,
return 0;
}
-static int __devinit fs_enet_mdio_probe(struct of_device *ofdev,
+static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct mii_bus *new_bus;
@@ -200,7 +200,7 @@ out:
return ret;
}
-static int fs_enet_mdio_remove(struct of_device *ofdev)
+static int fs_enet_mdio_remove(struct platform_device *ofdev)
{
struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
struct bb_info *bitbang = bus->priv;
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c
index bddffd1..dbb9c48 100644
--- a/drivers/net/fs_enet/mii-fec.c
+++ b/drivers/net/fs_enet/mii-fec.c
@@ -101,7 +101,7 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus)
return 0;
}
-static int __devinit fs_enet_mdio_probe(struct of_device *ofdev,
+static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct resource res;
@@ -192,7 +192,7 @@ out:
return ret;
}
-static int fs_enet_mdio_remove(struct of_device *ofdev)
+static int fs_enet_mdio_remove(struct platform_device *ofdev)
{
struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
struct fec_info *fec = bus->priv;
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c
index f53f850..d4bf91a 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/fsl_pq_mdio.c
@@ -265,7 +265,7 @@ static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id)
#endif
-static int fsl_pq_mdio_probe(struct of_device *ofdev,
+static int fsl_pq_mdio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -425,7 +425,7 @@ err_free_priv:
}
-static int fsl_pq_mdio_remove(struct of_device *ofdev)
+static int fsl_pq_mdio_remove(struct platform_device *ofdev)
{
struct device *device = &ofdev->dev;
struct mii_bus *bus = dev_get_drvdata(device);
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index a1b6301..4f7c3f3 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -122,9 +122,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id);
static void adjust_link(struct net_device *dev);
static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev);
-static int gfar_probe(struct of_device *ofdev,
+static int gfar_probe(struct platform_device *ofdev,
const struct of_device_id *match);
-static int gfar_remove(struct of_device *ofdev);
+static int gfar_remove(struct platform_device *ofdev);
static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
@@ -605,7 +605,7 @@ static int gfar_parse_group(struct device_node *np,
return 0;
}
-static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
+static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
@@ -959,7 +959,7 @@ static void gfar_detect_errata(struct gfar_private *priv)
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
-static int gfar_probe(struct of_device *ofdev,
+static int gfar_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
u32 tempval;
@@ -1238,7 +1238,7 @@ register_fail:
return err;
}
-static int gfar_remove(struct of_device *ofdev)
+static int gfar_remove(struct platform_device *ofdev)
{
struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 710810e..68984eb 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -1054,7 +1054,7 @@ struct gfar_private {
struct device_node *node;
struct net_device *ndev;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
enum gfar_errata errata;
struct gfar_priv_grp gfargrp[MAXGROUPS];
diff --git a/drivers/net/greth.c b/drivers/net/greth.c
index 4d09eab..f15c64f 100644
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1373,7 +1373,7 @@ error:
}
/* Initialize the GRETH MAC */
-static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_device_id *match)
+static int __devinit greth_of_probe(struct platform_device *ofdev, const struct of_device_id *match)
{
struct net_device *dev;
struct greth_private *greth;
@@ -1412,7 +1412,7 @@ static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_dev
}
regs = (struct greth_regs *) greth->regs;
- greth->irq = ofdev->irqs[0];
+ greth->irq = ofdev->archdata.irqs[0];
dev_set_drvdata(greth->dev, dev);
SET_NETDEV_DEV(dev, greth->dev);
@@ -1572,7 +1572,7 @@ error1:
return err;
}
-static int __devexit greth_of_remove(struct of_device *of_dev)
+static int __devexit greth_of_remove(struct platform_device *of_dev)
{
struct net_device *ndev = dev_get_drvdata(&of_dev->dev);
struct greth_private *greth = netdev_priv(ndev);
diff --git a/drivers/net/greth.h b/drivers/net/greth.h
index 973388d..03ad903 100644
--- a/drivers/net/greth.h
+++ b/drivers/net/greth.h
@@ -118,7 +118,7 @@ struct greth_private {
int irq;
- struct device *dev; /* Pointer to of_device->dev */
+ struct device *dev; /* Pointer to platform_device->dev */
struct net_device *netdev;
struct napi_struct napi;
spinlock_t devlock;
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index eeec7bc..3506fd6 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -2245,7 +2245,7 @@ static int emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
struct emac_depentry {
u32 phandle;
struct device_node *node;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
void *drvdata;
};
@@ -2719,7 +2719,7 @@ static const struct net_device_ops emac_gige_netdev_ops = {
.ndo_change_mtu = emac_change_mtu,
};
-static int __devinit emac_probe(struct of_device *ofdev,
+static int __devinit emac_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct net_device *ndev;
@@ -2941,7 +2941,7 @@ static int __devinit emac_probe(struct of_device *ofdev,
return err;
}
-static int __devexit emac_remove(struct of_device *ofdev)
+static int __devexit emac_remove(struct platform_device *ofdev)
{
struct emac_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/core.h b/drivers/net/ibm_newemac/core.h
index b1cbe6f..9e37e3d 100644
--- a/drivers/net/ibm_newemac/core.h
+++ b/drivers/net/ibm_newemac/core.h
@@ -170,12 +170,12 @@ struct emac_instance {
struct net_device *ndev;
struct resource rsrc_regs;
struct emac_regs __iomem *emacp;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct device_node **blist; /* bootlist entry */
/* MAL linkage */
u32 mal_ph;
- struct of_device *mal_dev;
+ struct platform_device *mal_dev;
u32 mal_rx_chan;
u32 mal_tx_chan;
struct mal_instance *mal;
@@ -196,24 +196,24 @@ struct emac_instance {
/* Shared MDIO if any */
u32 mdio_ph;
- struct of_device *mdio_dev;
+ struct platform_device *mdio_dev;
struct emac_instance *mdio_instance;
struct mutex mdio_lock;
/* ZMII infos if any */
u32 zmii_ph;
u32 zmii_port;
- struct of_device *zmii_dev;
+ struct platform_device *zmii_dev;
/* RGMII infos if any */
u32 rgmii_ph;
u32 rgmii_port;
- struct of_device *rgmii_dev;
+ struct platform_device *rgmii_dev;
/* TAH infos if any */
u32 tah_ph;
u32 tah_port;
- struct of_device *tah_dev;
+ struct platform_device *tah_dev;
/* IRQs */
int wol_irq;
diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ibm_newemac/mal.c
index fcff9e0..d5717e2 100644
--- a/drivers/net/ibm_newemac/mal.c
+++ b/drivers/net/ibm_newemac/mal.c
@@ -517,7 +517,7 @@ void *mal_dump_regs(struct mal_instance *mal, void *buf)
return regs + 1;
}
-static int __devinit mal_probe(struct of_device *ofdev,
+static int __devinit mal_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct mal_instance *mal;
@@ -730,7 +730,7 @@ static int __devinit mal_probe(struct of_device *ofdev,
return err;
}
-static int __devexit mal_remove(struct of_device *ofdev)
+static int __devexit mal_remove(struct platform_device *ofdev)
{
struct mal_instance *mal = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/mal.h b/drivers/net/ibm_newemac/mal.h
index 9ededfb..6608421 100644
--- a/drivers/net/ibm_newemac/mal.h
+++ b/drivers/net/ibm_newemac/mal.h
@@ -210,7 +210,7 @@ struct mal_instance {
dma_addr_t bd_dma;
struct mal_descriptor *bd_virt;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
int index;
spinlock_t lock;
diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c
index 108919b..dd61798 100644
--- a/drivers/net/ibm_newemac/rgmii.c
+++ b/drivers/net/ibm_newemac/rgmii.c
@@ -93,7 +93,7 @@ static inline u32 rgmii_mode_mask(int mode, int input)
}
}
-int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode)
+int __devinit rgmii_attach(struct platform_device *ofdev, int input, int mode)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -122,7 +122,7 @@ int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode)
return 0;
}
-void rgmii_set_speed(struct of_device *ofdev, int input, int speed)
+void rgmii_set_speed(struct platform_device *ofdev, int input, int speed)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -144,7 +144,7 @@ void rgmii_set_speed(struct of_device *ofdev, int input, int speed)
mutex_unlock(&dev->lock);
}
-void rgmii_get_mdio(struct of_device *ofdev, int input)
+void rgmii_get_mdio(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -165,7 +165,7 @@ void rgmii_get_mdio(struct of_device *ofdev, int input)
DBG2(dev, " fer = 0x%08x\n", fer);
}
-void rgmii_put_mdio(struct of_device *ofdev, int input)
+void rgmii_put_mdio(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -186,7 +186,7 @@ void rgmii_put_mdio(struct of_device *ofdev, int input)
mutex_unlock(&dev->lock);
}
-void rgmii_detach(struct of_device *ofdev, int input)
+void rgmii_detach(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p;
@@ -206,13 +206,13 @@ void rgmii_detach(struct of_device *ofdev, int input)
mutex_unlock(&dev->lock);
}
-int rgmii_get_regs_len(struct of_device *ofdev)
+int rgmii_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct rgmii_regs);
}
-void *rgmii_dump_regs(struct of_device *ofdev, void *buf)
+void *rgmii_dump_regs(struct platform_device *ofdev, void *buf)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct emac_ethtool_regs_subhdr *hdr = buf;
@@ -228,7 +228,7 @@ void *rgmii_dump_regs(struct of_device *ofdev, void *buf)
}
-static int __devinit rgmii_probe(struct of_device *ofdev,
+static int __devinit rgmii_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -293,7 +293,7 @@ static int __devinit rgmii_probe(struct of_device *ofdev,
return rc;
}
-static int __devexit rgmii_remove(struct of_device *ofdev)
+static int __devexit rgmii_remove(struct platform_device *ofdev)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/rgmii.h b/drivers/net/ibm_newemac/rgmii.h
index c4a4b35..d697990 100644
--- a/drivers/net/ibm_newemac/rgmii.h
+++ b/drivers/net/ibm_newemac/rgmii.h
@@ -51,20 +51,20 @@ struct rgmii_instance {
int users;
/* OF device instance */
- struct of_device *ofdev;
+ struct platform_device *ofdev;
};
#ifdef CONFIG_IBM_NEW_EMAC_RGMII
extern int rgmii_init(void);
extern void rgmii_exit(void);
-extern int rgmii_attach(struct of_device *ofdev, int input, int mode);
-extern void rgmii_detach(struct of_device *ofdev, int input);
-extern void rgmii_get_mdio(struct of_device *ofdev, int input);
-extern void rgmii_put_mdio(struct of_device *ofdev, int input);
-extern void rgmii_set_speed(struct of_device *ofdev, int input, int speed);
-extern int rgmii_get_regs_len(struct of_device *ofdev);
-extern void *rgmii_dump_regs(struct of_device *ofdev, void *buf);
+extern int rgmii_attach(struct platform_device *ofdev, int input, int mode);
+extern void rgmii_detach(struct platform_device *ofdev, int input);
+extern void rgmii_get_mdio(struct platform_device *ofdev, int input);
+extern void rgmii_put_mdio(struct platform_device *ofdev, int input);
+extern void rgmii_set_speed(struct platform_device *ofdev, int input, int speed);
+extern int rgmii_get_regs_len(struct platform_device *ofdev);
+extern void *rgmii_dump_regs(struct platform_device *ofdev, void *buf);
#else
diff --git a/drivers/net/ibm_newemac/tah.c b/drivers/net/ibm_newemac/tah.c
index 0446371..299aa49 100644
--- a/drivers/net/ibm_newemac/tah.c
+++ b/drivers/net/ibm_newemac/tah.c
@@ -23,7 +23,7 @@
#include "emac.h"
#include "core.h"
-int __devinit tah_attach(struct of_device *ofdev, int channel)
+int __devinit tah_attach(struct platform_device *ofdev, int channel)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -35,7 +35,7 @@ int __devinit tah_attach(struct of_device *ofdev, int channel)
return 0;
}
-void tah_detach(struct of_device *ofdev, int channel)
+void tah_detach(struct platform_device *ofdev, int channel)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -44,7 +44,7 @@ void tah_detach(struct of_device *ofdev, int channel)
mutex_unlock(&dev->lock);
}
-void tah_reset(struct of_device *ofdev)
+void tah_reset(struct platform_device *ofdev)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
struct tah_regs __iomem *p = dev->base;
@@ -66,13 +66,13 @@ void tah_reset(struct of_device *ofdev)
TAH_MR_DIG);
}
-int tah_get_regs_len(struct of_device *ofdev)
+int tah_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct tah_regs);
}
-void *tah_dump_regs(struct of_device *ofdev, void *buf)
+void *tah_dump_regs(struct platform_device *ofdev, void *buf)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
struct emac_ethtool_regs_subhdr *hdr = buf;
@@ -87,7 +87,7 @@ void *tah_dump_regs(struct of_device *ofdev, void *buf)
return regs + 1;
}
-static int __devinit tah_probe(struct of_device *ofdev,
+static int __devinit tah_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -139,7 +139,7 @@ static int __devinit tah_probe(struct of_device *ofdev,
return rc;
}
-static int __devexit tah_remove(struct of_device *ofdev)
+static int __devexit tah_remove(struct platform_device *ofdev)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/tah.h b/drivers/net/ibm_newemac/tah.h
index a068b56..61dbeca 100644
--- a/drivers/net/ibm_newemac/tah.h
+++ b/drivers/net/ibm_newemac/tah.h
@@ -48,7 +48,7 @@ struct tah_instance {
int users;
/* OF device instance */
- struct of_device *ofdev;
+ struct platform_device *ofdev;
};
@@ -74,11 +74,11 @@ struct tah_instance {
extern int tah_init(void);
extern void tah_exit(void);
-extern int tah_attach(struct of_device *ofdev, int channel);
-extern void tah_detach(struct of_device *ofdev, int channel);
-extern void tah_reset(struct of_device *ofdev);
-extern int tah_get_regs_len(struct of_device *ofdev);
-extern void *tah_dump_regs(struct of_device *ofdev, void *buf);
+extern int tah_attach(struct platform_device *ofdev, int channel);
+extern void tah_detach(struct platform_device *ofdev, int channel);
+extern void tah_reset(struct platform_device *ofdev);
+extern int tah_get_regs_len(struct platform_device *ofdev);
+extern void *tah_dump_regs(struct platform_device *ofdev, void *buf);
#else
diff --git a/drivers/net/ibm_newemac/zmii.c b/drivers/net/ibm_newemac/zmii.c
index 046dcd0..34ed6ee 100644
--- a/drivers/net/ibm_newemac/zmii.c
+++ b/drivers/net/ibm_newemac/zmii.c
@@ -82,7 +82,7 @@ static inline u32 zmii_mode_mask(int mode, int input)
}
}
-int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode)
+int __devinit zmii_attach(struct platform_device *ofdev, int input, int *mode)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct zmii_regs __iomem *p = dev->base;
@@ -148,7 +148,7 @@ int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode)
return 0;
}
-void zmii_get_mdio(struct of_device *ofdev, int input)
+void zmii_get_mdio(struct platform_device *ofdev, int input)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
u32 fer;
@@ -161,7 +161,7 @@ void zmii_get_mdio(struct of_device *ofdev, int input)
out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input));
}
-void zmii_put_mdio(struct of_device *ofdev, int input)
+void zmii_put_mdio(struct platform_device *ofdev, int input)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -170,7 +170,7 @@ void zmii_put_mdio(struct of_device *ofdev, int input)
}
-void zmii_set_speed(struct of_device *ofdev, int input, int speed)
+void zmii_set_speed(struct platform_device *ofdev, int input, int speed)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
u32 ssr;
@@ -191,7 +191,7 @@ void zmii_set_speed(struct of_device *ofdev, int input, int speed)
mutex_unlock(&dev->lock);
}
-void zmii_detach(struct of_device *ofdev, int input)
+void zmii_detach(struct platform_device *ofdev, int input)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -210,13 +210,13 @@ void zmii_detach(struct of_device *ofdev, int input)
mutex_unlock(&dev->lock);
}
-int zmii_get_regs_len(struct of_device *ofdev)
+int zmii_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct zmii_regs);
}
-void *zmii_dump_regs(struct of_device *ofdev, void *buf)
+void *zmii_dump_regs(struct platform_device *ofdev, void *buf)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct emac_ethtool_regs_subhdr *hdr = buf;
@@ -231,7 +231,7 @@ void *zmii_dump_regs(struct of_device *ofdev, void *buf)
return regs + 1;
}
-static int __devinit zmii_probe(struct of_device *ofdev,
+static int __devinit zmii_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -286,7 +286,7 @@ static int __devinit zmii_probe(struct of_device *ofdev,
return rc;
}
-static int __devexit zmii_remove(struct of_device *ofdev)
+static int __devexit zmii_remove(struct platform_device *ofdev)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/zmii.h b/drivers/net/ibm_newemac/zmii.h
index 6c9beba..1333fa2 100644
--- a/drivers/net/ibm_newemac/zmii.h
+++ b/drivers/net/ibm_newemac/zmii.h
@@ -48,20 +48,20 @@ struct zmii_instance {
u32 fer_save;
/* OF device instance */
- struct of_device *ofdev;
+ struct platform_device *ofdev;
};
#ifdef CONFIG_IBM_NEW_EMAC_ZMII
extern int zmii_init(void);
extern void zmii_exit(void);
-extern int zmii_attach(struct of_device *ofdev, int input, int *mode);
-extern void zmii_detach(struct of_device *ofdev, int input);
-extern void zmii_get_mdio(struct of_device *ofdev, int input);
-extern void zmii_put_mdio(struct of_device *ofdev, int input);
-extern void zmii_set_speed(struct of_device *ofdev, int input, int speed);
-extern int zmii_get_regs_len(struct of_device *ocpdev);
-extern void *zmii_dump_regs(struct of_device *ofdev, void *buf);
+extern int zmii_attach(struct platform_device *ofdev, int input, int *mode);
+extern void zmii_detach(struct platform_device *ofdev, int input);
+extern void zmii_get_mdio(struct platform_device *ofdev, int input);
+extern void zmii_put_mdio(struct platform_device *ofdev, int input);
+extern void zmii_set_speed(struct platform_device *ofdev, int input, int speed);
+extern int zmii_get_regs_len(struct platform_device *ocpdev);
+extern void *zmii_dump_regs(struct platform_device *ofdev, void *buf);
#else
# define zmii_init() 0
diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c
index edd5666..9e3f4f5 100644
--- a/drivers/net/irda/sh_irda.c
+++ b/drivers/net/irda/sh_irda.c
@@ -748,7 +748,6 @@ static int __devinit sh_irda_probe(struct platform_device *pdev)
struct net_device *ndev;
struct sh_irda_self *self;
struct resource *res;
- char clk_name[8];
int irq;
int err = -ENOMEM;
@@ -775,10 +774,9 @@ static int __devinit sh_irda_probe(struct platform_device *pdev)
if (err)
goto err_mem_2;
- snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id);
- self->clk = clk_get(&pdev->dev, clk_name);
+ self->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(self->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ dev_err(&pdev->dev, "cannot get irda clock\n");
goto err_mem_3;
}
diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
index 4eea3f7..c7b6247 100644
--- a/drivers/net/ll_temac_main.c
+++ b/drivers/net/ll_temac_main.c
@@ -159,7 +159,7 @@ static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value)
* temac_dcr_setup - If the DMA is DCR based, then setup the address and
* I/O functions
*/
-static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
+static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op,
struct device_node *np)
{
unsigned int dcrs;
@@ -184,7 +184,7 @@ static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
* temac_dcr_setup - This is a stub for when DCR is not supported,
* such as with MicroBlaze
*/
-static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
+static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op,
struct device_node *np)
{
return -1;
@@ -952,7 +952,7 @@ static const struct attribute_group temac_attr_group = {
};
static int __init
-temac_of_probe(struct of_device *op, const struct of_device_id *match)
+temac_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *np;
struct temac_local *lp;
@@ -1094,7 +1094,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
return rc;
}
-static int __devexit temac_of_remove(struct of_device *op)
+static int __devexit temac_of_remove(struct platform_device *op)
{
struct net_device *ndev = dev_get_drvdata(&op->dev);
struct temac_local *lp = netdev_priv(ndev);
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index d771d16..fb2c092 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -239,6 +239,7 @@ struct myri10ge_priv {
int watchdog_resets;
int watchdog_pause;
int pause;
+ bool fw_name_allocated;
char *fw_name;
char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE];
char *product_code_string;
@@ -271,6 +272,7 @@ MODULE_FIRMWARE("myri10ge_eth_z8e.dat");
MODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat");
MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat");
+/* Careful: must be accessed under kparam_block_sysfs_write */
static char *myri10ge_fw_name = NULL;
module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name");
@@ -376,6 +378,14 @@ static inline void put_be32(__be32 val, __be32 __iomem * p)
static struct net_device_stats *myri10ge_get_stats(struct net_device *dev);
+static void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated)
+{
+ if (mgp->fw_name_allocated)
+ kfree(mgp->fw_name);
+ mgp->fw_name = name;
+ mgp->fw_name_allocated = allocated;
+}
+
static int
myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd,
struct myri10ge_cmd *data, int atomic)
@@ -747,7 +757,7 @@ static int myri10ge_load_firmware(struct myri10ge_priv *mgp, int adopt)
dev_warn(&mgp->pdev->dev, "via hotplug\n");
}
- mgp->fw_name = "adopted";
+ set_fw_name(mgp, "adopted", false);
mgp->tx_boundary = 2048;
myri10ge_dummy_rdma(mgp, 1);
status = myri10ge_get_firmware_capabilities(mgp);
@@ -3233,7 +3243,7 @@ static void myri10ge_firmware_probe(struct myri10ge_priv *mgp)
* load the optimized firmware (which assumes aligned PCIe
* completions) in order to see if it works on this host.
*/
- mgp->fw_name = myri10ge_fw_aligned;
+ set_fw_name(mgp, myri10ge_fw_aligned, false);
status = myri10ge_load_firmware(mgp, 1);
if (status != 0) {
goto abort;
@@ -3261,7 +3271,7 @@ static void myri10ge_firmware_probe(struct myri10ge_priv *mgp)
abort:
/* fall back to using the unaligned firmware */
mgp->tx_boundary = 2048;
- mgp->fw_name = myri10ge_fw_unaligned;
+ set_fw_name(mgp, myri10ge_fw_unaligned, false);
}
@@ -3284,7 +3294,7 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
dev_info(&mgp->pdev->dev, "PCIE x%d Link\n",
link_width);
mgp->tx_boundary = 4096;
- mgp->fw_name = myri10ge_fw_aligned;
+ set_fw_name(mgp, myri10ge_fw_aligned, false);
} else {
myri10ge_firmware_probe(mgp);
}
@@ -3293,22 +3303,29 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
dev_info(&mgp->pdev->dev,
"Assuming aligned completions (forced)\n");
mgp->tx_boundary = 4096;
- mgp->fw_name = myri10ge_fw_aligned;
+ set_fw_name(mgp, myri10ge_fw_aligned, false);
} else {
dev_info(&mgp->pdev->dev,
"Assuming unaligned completions (forced)\n");
mgp->tx_boundary = 2048;
- mgp->fw_name = myri10ge_fw_unaligned;
+ set_fw_name(mgp, myri10ge_fw_unaligned, false);
}
}
+
+ kparam_block_sysfs_write(myri10ge_fw_name);
if (myri10ge_fw_name != NULL) {
- overridden = 1;
- mgp->fw_name = myri10ge_fw_name;
+ char *fw_name = kstrdup(myri10ge_fw_name, GFP_KERNEL);
+ if (fw_name) {
+ overridden = 1;
+ set_fw_name(mgp, fw_name, true);
+ }
}
+ kparam_unblock_sysfs_write(myri10ge_fw_name);
+
if (mgp->board_number < MYRI10GE_MAX_BOARDS &&
myri10ge_fw_names[mgp->board_number] != NULL &&
strlen(myri10ge_fw_names[mgp->board_number])) {
- mgp->fw_name = myri10ge_fw_names[mgp->board_number];
+ set_fw_name(mgp, myri10ge_fw_names[mgp->board_number], false);
overridden = 1;
}
if (overridden)
@@ -3660,6 +3677,7 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp)
struct myri10ge_cmd cmd;
struct pci_dev *pdev = mgp->pdev;
char *old_fw;
+ bool old_allocated;
int i, status, ncpus, msix_cap;
mgp->num_slices = 1;
@@ -3672,17 +3690,23 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp)
/* try to load the slice aware rss firmware */
old_fw = mgp->fw_name;
+ old_allocated = mgp->fw_name_allocated;
+ /* don't free old_fw if we override it. */
+ mgp->fw_name_allocated = false;
+
if (myri10ge_fw_name != NULL) {
dev_info(&mgp->pdev->dev, "overriding rss firmware to %s\n",
myri10ge_fw_name);
- mgp->fw_name = myri10ge_fw_name;
+ set_fw_name(mgp, myri10ge_fw_name, false);
} else if (old_fw == myri10ge_fw_aligned)
- mgp->fw_name = myri10ge_fw_rss_aligned;
+ set_fw_name(mgp, myri10ge_fw_rss_aligned, false);
else
- mgp->fw_name = myri10ge_fw_rss_unaligned;
+ set_fw_name(mgp, myri10ge_fw_rss_unaligned, false);
status = myri10ge_load_firmware(mgp, 0);
if (status != 0) {
dev_info(&pdev->dev, "Rss firmware not found\n");
+ if (old_allocated)
+ kfree(old_fw);
return;
}
@@ -3747,6 +3771,8 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp)
mgp->num_slices);
if (status == 0) {
pci_disable_msix(pdev);
+ if (old_allocated)
+ kfree(old_fw);
return;
}
if (status > 0)
@@ -3763,7 +3789,7 @@ disable_msix:
abort_with_fw:
mgp->num_slices = 1;
- mgp->fw_name = old_fw;
+ set_fw_name(mgp, old_fw, old_allocated);
myri10ge_load_firmware(mgp, 0);
}
@@ -3993,6 +4019,7 @@ abort_with_enabled:
pci_disable_device(pdev);
abort_with_netdev:
+ set_fw_name(mgp, NULL, false);
free_netdev(netdev);
return status;
}
@@ -4037,6 +4064,7 @@ static void myri10ge_remove(struct pci_dev *pdev)
dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd),
mgp->cmd, mgp->cmd_bus);
+ set_fw_name(mgp, NULL, false);
free_netdev(netdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index 04e552a..617f898 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -926,7 +926,7 @@ static const struct net_device_ops myri_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit myri_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit myri_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
static unsigned version_printed;
@@ -1124,7 +1124,7 @@ err:
return -ENODEV;
}
-static int __devexit myri_sbus_remove(struct of_device *op)
+static int __devexit myri_sbus_remove(struct platform_device *op)
{
struct myri_eth *mp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = mp->dev;
diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h
index ff363e9..80a2fa5 100644
--- a/drivers/net/myri_sbus.h
+++ b/drivers/net/myri_sbus.h
@@ -288,7 +288,7 @@ struct myri_eth {
struct myri_eeprom eeprom; /* Local copy of EEPROM. */
unsigned int reg_size; /* Size of register space. */
unsigned int shmem_base; /* Offset to shared ram. */
- struct of_device *myri_op; /* Our OF device struct. */
+ struct platform_device *myri_op; /* Our OF device struct. */
};
/* We use this to acquire receive skb's that we can DMA directly into. */
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index 404f2d55..bc695d5 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -9103,7 +9103,7 @@ retry:
static int __devinit niu_n2_irq_init(struct niu *np, u8 *ldg_num_map)
{
#ifdef CONFIG_SPARC64
- struct of_device *op = np->op;
+ struct platform_device *op = np->op;
const u32 *int_prop;
int i;
@@ -9688,7 +9688,7 @@ static void __devinit niu_driver_version(void)
static struct net_device * __devinit niu_alloc_and_init(
struct device *gen_dev, struct pci_dev *pdev,
- struct of_device *op, const struct niu_ops *ops,
+ struct platform_device *op, const struct niu_ops *ops,
u8 port)
{
struct net_device *dev;
@@ -10064,7 +10064,7 @@ static const struct niu_ops niu_phys_ops = {
.unmap_single = niu_phys_unmap_single,
};
-static int __devinit niu_of_probe(struct of_device *op,
+static int __devinit niu_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
union niu_parent_id parent_id;
@@ -10179,7 +10179,7 @@ err_out:
return err;
}
-static int __devexit niu_of_remove(struct of_device *op)
+static int __devexit niu_of_remove(struct platform_device *op)
{
struct net_device *dev = dev_get_drvdata(&op->dev);
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index 4eb6f98..f5819526 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -808,6 +808,7 @@ xirc2ps_config(struct pcmcia_device * link)
}
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
+ link->io_lines = 10;
if (local->modem) {
int pass;
@@ -839,7 +840,6 @@ xirc2ps_config(struct pcmcia_device * link)
}
printk(KNOT_XIRC "no ports available\n");
} else {
- link->io_lines = 10;
link->resource[0]->end = 16;
for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
link->resource[0]->start = ioaddr;
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index a527e37..eb799b3 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -5,7 +5,7 @@
menuconfig PHYLIB
tristate "PHY Device support and infrastructure"
depends on !S390
- depends on NET_ETHERNET
+ depends on NETDEVICES
help
Ethernet controllers are usually attached to PHY
devices. This option provides infrastructure for
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index fc5fef2..f62c7b7 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -188,7 +188,7 @@ static int __devexit mdio_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_OF_GPIO
-static int __devinit mdio_ofgpio_probe(struct of_device *ofdev,
+static int __devinit mdio_ofgpio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct mdio_gpio_platform_data *pdata;
@@ -224,7 +224,7 @@ out_free:
return -ENODEV;
}
-static int __devexit mdio_ofgpio_remove(struct of_device *ofdev)
+static int __devexit mdio_ofgpio_remove(struct platform_device *ofdev)
{
mdio_gpio_bus_destroy(&ofdev->dev);
kfree(ofdev->dev.platform_data);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 5130db8..1bb16cb7 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -301,7 +301,7 @@ EXPORT_SYMBOL(phy_ethtool_gset);
/**
* phy_mii_ioctl - generic PHY MII ioctl interface
* @phydev: the phy_device struct
- * @mii_data: MII ioctl data
+ * @ifr: &struct ifreq for socket ioctl's
* @cmd: ioctl cmd to execute
*
* Note that this function is currently incompatible with the
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c
index b9615bd..bf6d87a 100644
--- a/drivers/net/qlcnic/qlcnic_main.c
+++ b/drivers/net/qlcnic/qlcnic_main.c
@@ -473,48 +473,58 @@ qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter)
static int
qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
{
- struct qlcnic_pci_info pci_info[QLCNIC_MAX_PCI_FUNC];
+ struct qlcnic_pci_info *pci_info;
int i, ret = 0, err;
u8 pfn;
- if (!adapter->npars)
- adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) *
- QLCNIC_MAX_PCI_FUNC, GFP_KERNEL);
- if (!adapter->npars)
+ pci_info = kcalloc(QLCNIC_MAX_PCI_FUNC, sizeof(*pci_info), GFP_KERNEL);
+ if (!pci_info)
return -ENOMEM;
- if (!adapter->eswitch)
- adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
+ adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) *
+ QLCNIC_MAX_PCI_FUNC, GFP_KERNEL);
+ if (!adapter->npars) {
+ err = -ENOMEM;
+ goto err_pci_info;
+ }
+
+ adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
QLCNIC_NIU_MAX_XG_PORTS, GFP_KERNEL);
if (!adapter->eswitch) {
err = -ENOMEM;
- goto err_eswitch;
+ goto err_npars;
}
ret = qlcnic_get_pci_info(adapter, pci_info);
- if (!ret) {
- for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
- pfn = pci_info[i].id;
- if (pfn > QLCNIC_MAX_PCI_FUNC)
- return QL_STATUS_INVALID_PARAM;
- adapter->npars[pfn].active = pci_info[i].active;
- adapter->npars[pfn].type = pci_info[i].type;
- adapter->npars[pfn].phy_port = pci_info[i].default_port;
- adapter->npars[pfn].mac_learning = DEFAULT_MAC_LEARN;
- adapter->npars[pfn].min_bw = pci_info[i].tx_min_bw;
- adapter->npars[pfn].max_bw = pci_info[i].tx_max_bw;
- }
-
- for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
- adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
+ if (ret)
+ goto err_eswitch;
- return ret;
+ for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+ pfn = pci_info[i].id;
+ if (pfn > QLCNIC_MAX_PCI_FUNC)
+ return QL_STATUS_INVALID_PARAM;
+ adapter->npars[pfn].active = pci_info[i].active;
+ adapter->npars[pfn].type = pci_info[i].type;
+ adapter->npars[pfn].phy_port = pci_info[i].default_port;
+ adapter->npars[pfn].mac_learning = DEFAULT_MAC_LEARN;
+ adapter->npars[pfn].min_bw = pci_info[i].tx_min_bw;
+ adapter->npars[pfn].max_bw = pci_info[i].tx_max_bw;
}
+ for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
+ adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
+
+ kfree(pci_info);
+ return 0;
+
+err_eswitch:
kfree(adapter->eswitch);
adapter->eswitch = NULL;
-err_eswitch:
+err_npars:
kfree(adapter->npars);
+ adapter->npars = NULL;
+err_pci_info:
+ kfree(pci_info);
return ret;
}
@@ -3361,15 +3371,21 @@ qlcnic_sysfs_read_pci_config(struct file *file, struct kobject *kobj,
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
struct qlcnic_pci_func_cfg pci_cfg[QLCNIC_MAX_PCI_FUNC];
- struct qlcnic_pci_info pci_info[QLCNIC_MAX_PCI_FUNC];
+ struct qlcnic_pci_info *pci_info;
int i, ret;
if (size != sizeof(pci_cfg))
return QL_STATUS_INVALID_PARAM;
+ pci_info = kcalloc(QLCNIC_MAX_PCI_FUNC, sizeof(*pci_info), GFP_KERNEL);
+ if (!pci_info)
+ return -ENOMEM;
+
ret = qlcnic_get_pci_info(adapter, pci_info);
- if (ret)
+ if (ret) {
+ kfree(pci_info);
return ret;
+ }
for (i = 0; i < QLCNIC_MAX_PCI_FUNC ; i++) {
pci_cfg[i].pci_func = pci_info[i].id;
@@ -3380,8 +3396,8 @@ qlcnic_sysfs_read_pci_config(struct file *file, struct kobject *kobj,
memcpy(&pci_cfg[i].def_mac_addr, &pci_info[i].mac, ETH_ALEN);
}
memcpy(buf, &pci_cfg, size);
+ kfree(pci_info);
return size;
-
}
static struct bin_attribute bin_attr_npar_config = {
.attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)},
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 09c071b..618643e 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -97,7 +97,7 @@ static int qec_global_reset(void __iomem *gregs)
static void qec_init(struct bigmac *bp)
{
- struct of_device *qec_op = bp->qec_op;
+ struct platform_device *qec_op = bp->qec_op;
void __iomem *gregs = bp->gregs;
u8 bsizes = bp->bigmac_bursts;
u32 regval;
@@ -1083,8 +1083,8 @@ static const struct net_device_ops bigmac_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit bigmac_ether_init(struct of_device *op,
- struct of_device *qec_op)
+static int __devinit bigmac_ether_init(struct platform_device *op,
+ struct platform_device *qec_op)
{
static int version_printed;
struct net_device *dev;
@@ -1242,25 +1242,25 @@ fail_and_cleanup:
/* QEC can be the parent of either QuadEthernet or a BigMAC. We want
* the latter.
*/
-static int __devinit bigmac_sbus_probe(struct of_device *op,
+static int __devinit bigmac_sbus_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device *parent = op->dev.parent;
- struct of_device *qec_op;
+ struct platform_device *qec_op;
- qec_op = to_of_device(parent);
+ qec_op = to_platform_device(parent);
return bigmac_ether_init(op, qec_op);
}
-static int __devexit bigmac_sbus_remove(struct of_device *op)
+static int __devexit bigmac_sbus_remove(struct platform_device *op)
{
struct bigmac *bp = dev_get_drvdata(&op->dev);
struct device *parent = op->dev.parent;
struct net_device *net_dev = bp->dev;
- struct of_device *qec_op;
+ struct platform_device *qec_op;
- qec_op = to_of_device(parent);
+ qec_op = to_platform_device(parent);
unregister_netdev(net_dev);
diff --git a/drivers/net/sunbmac.h b/drivers/net/sunbmac.h
index 8840bc0..8db8894 100644
--- a/drivers/net/sunbmac.h
+++ b/drivers/net/sunbmac.h
@@ -329,8 +329,8 @@ struct bigmac {
unsigned int timer_ticks;
struct net_device_stats enet_stats;
- struct of_device *qec_op;
- struct of_device *bigmac_op;
+ struct platform_device *qec_op;
+ struct platform_device *bigmac_op;
struct net_device *dev;
};
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index eec443f..bd0df1c 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1591,7 +1591,7 @@ static int happy_meal_init(struct happy_meal *hp)
*/
#ifdef CONFIG_SBUS
if ((hp->happy_flags & HFLAG_PCI) == 0) {
- struct of_device *op = hp->happy_dev;
+ struct platform_device *op = hp->happy_dev;
if (sbus_can_dma_64bit()) {
sbus_set_sbus64(&op->dev,
hp->happy_bursts);
@@ -2480,7 +2480,7 @@ static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info
#ifdef CONFIG_SBUS
else {
const struct linux_prom_registers *regs;
- struct of_device *op = hp->happy_dev;
+ struct platform_device *op = hp->happy_dev;
regs = of_get_property(op->dev.of_node, "regs", NULL);
if (regs)
sprintf(info->bus_info, "SBUS:%d",
@@ -2515,13 +2515,13 @@ static int hme_version_printed;
*
* Return NULL on failure.
*/
-static struct quattro * __devinit quattro_sbus_find(struct of_device *child)
+static struct quattro * __devinit quattro_sbus_find(struct platform_device *child)
{
struct device *parent = child->dev.parent;
- struct of_device *op;
+ struct platform_device *op;
struct quattro *qp;
- op = to_of_device(parent);
+ op = to_platform_device(parent);
qp = dev_get_drvdata(&op->dev);
if (qp)
return qp;
@@ -2551,7 +2551,7 @@ static int __init quattro_sbus_register_irqs(void)
struct quattro *qp;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- struct of_device *op = qp->quattro_dev;
+ struct platform_device *op = qp->quattro_dev;
int err, qfe_slot, skip = 0;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) {
@@ -2580,7 +2580,7 @@ static void quattro_sbus_free_irqs(void)
struct quattro *qp;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- struct of_device *op = qp->quattro_dev;
+ struct platform_device *op = qp->quattro_dev;
int qfe_slot, skip = 0;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) {
@@ -2639,7 +2639,7 @@ static const struct net_device_ops hme_netdev_ops = {
};
#ifdef CONFIG_SBUS
-static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
+static int __devinit happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe)
{
struct device_node *dp = op->dev.of_node, *sbus_dp;
struct quattro *qp = NULL;
@@ -2648,7 +2648,7 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
int i, qfe_slot = -1;
int err = -ENODEV;
- sbus_dp = to_of_device(op->dev.parent)->dev.of_node;
+ sbus_dp = op->dev.parent->of_node;
/* We can match PCI devices too, do not accept those here. */
if (strcmp(sbus_dp->name, "sbus"))
@@ -3235,7 +3235,7 @@ static void happy_meal_pci_exit(void)
#endif
#ifdef CONFIG_SBUS
-static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit hme_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
const char *model = of_get_property(dp, "model", NULL);
@@ -3247,7 +3247,7 @@ static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device
return happy_meal_sbus_probe_one(op, is_qfe);
}
-static int __devexit hme_sbus_remove(struct of_device *op)
+static int __devexit hme_sbus_remove(struct platform_device *op)
{
struct happy_meal *hp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = hp->dev;
diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h
index efd2ca0..756b5bf 100644
--- a/drivers/net/sunhme.h
+++ b/drivers/net/sunhme.h
@@ -407,7 +407,7 @@ struct happy_meal {
void (*write_rxd)(struct happy_meal_rxd *, u32, u32);
#endif
- /* This is either an of_device or a pci_dev. */
+ /* This is either an platform_device or a pci_dev. */
void *happy_dev;
struct device *dma_dev;
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index ee364fa..8dcb858 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -250,7 +250,7 @@ struct lance_private {
int rx_new, tx_new;
int rx_old, tx_old;
- struct of_device *ledma; /* If set this points to ledma */
+ struct platform_device *ledma; /* If set this points to ledma */
char tpe; /* cable-selection is TPE */
char auto_select; /* cable-selection by carrier */
char burst_sizes; /* ledma SBus burst sizes */
@@ -265,8 +265,8 @@ struct lance_private {
char *name;
dma_addr_t init_block_dvma;
struct net_device *dev; /* Backpointer */
- struct of_device *op;
- struct of_device *lebuffer;
+ struct platform_device *op;
+ struct platform_device *lebuffer;
struct timer_list multicast_timer;
};
@@ -1272,7 +1272,7 @@ static void lance_free_hwresources(struct lance_private *lp)
if (lp->lregs)
of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE);
if (lp->dregs) {
- struct of_device *ledma = lp->ledma;
+ struct platform_device *ledma = lp->ledma;
of_iounmap(&ledma->resource[0], lp->dregs,
resource_size(&ledma->resource[0]));
@@ -1319,9 +1319,9 @@ static const struct net_device_ops sparc_lance_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit sparc_lance_probe_one(struct of_device *op,
- struct of_device *ledma,
- struct of_device *lebuffer)
+static int __devinit sparc_lance_probe_one(struct platform_device *op,
+ struct platform_device *ledma,
+ struct platform_device *lebuffer)
{
struct device_node *dp = op->dev.of_node;
static unsigned version_printed;
@@ -1503,9 +1503,9 @@ fail:
return -ENODEV;
}
-static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit sunlance_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
- struct of_device *parent = to_of_device(op->dev.parent);
+ struct platform_device *parent = to_platform_device(op->dev.parent);
struct device_node *parent_dp = parent->dev.of_node;
int err;
@@ -1519,7 +1519,7 @@ static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_d
return err;
}
-static int __devexit sunlance_sbus_remove(struct of_device *op)
+static int __devexit sunlance_sbus_remove(struct platform_device *op)
{
struct lance_private *lp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = lp->dev;
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index 5f84a5d..72e65d4 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -689,7 +689,7 @@ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
const struct linux_prom_registers *regs;
struct sunqe *qep = netdev_priv(dev);
- struct of_device *op;
+ struct platform_device *op;
strcpy(info->driver, "sunqe");
strcpy(info->version, "3.0");
@@ -720,7 +720,7 @@ static const struct ethtool_ops qe_ethtool_ops = {
};
/* This is only called once at boot time for each card probed. */
-static void qec_init_once(struct sunqec *qecp, struct of_device *op)
+static void qec_init_once(struct sunqec *qecp, struct platform_device *op)
{
u8 bsizes = qecp->qec_bursts;
@@ -770,9 +770,9 @@ static u8 __devinit qec_get_burst(struct device_node *dp)
return bsizes;
}
-static struct sunqec * __devinit get_qec(struct of_device *child)
+static struct sunqec * __devinit get_qec(struct platform_device *child)
{
- struct of_device *op = to_of_device(child->dev.parent);
+ struct platform_device *op = to_platform_device(child->dev.parent);
struct sunqec *qecp;
qecp = dev_get_drvdata(&op->dev);
@@ -836,7 +836,7 @@ static const struct net_device_ops qec_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit qec_ether_init(struct of_device *op)
+static int __devinit qec_ether_init(struct platform_device *op)
{
static unsigned version_printed;
struct net_device *dev;
@@ -941,12 +941,12 @@ fail:
return res;
}
-static int __devinit qec_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit qec_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
return qec_ether_init(op);
}
-static int __devexit qec_sbus_remove(struct of_device *op)
+static int __devexit qec_sbus_remove(struct platform_device *op)
{
struct sunqe *qp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = qp->dev;
@@ -997,7 +997,7 @@ static void __exit qec_exit(void)
while (root_qec_dev) {
struct sunqec *next = root_qec_dev->next_module;
- struct of_device *op = root_qec_dev->op;
+ struct platform_device *op = root_qec_dev->op;
free_irq(op->archdata.irqs[0], (void *) root_qec_dev);
of_iounmap(&op->resource[0], root_qec_dev->gregs,
diff --git a/drivers/net/sunqe.h b/drivers/net/sunqe.h
index 5813a7b..581781b 100644
--- a/drivers/net/sunqe.h
+++ b/drivers/net/sunqe.h
@@ -314,7 +314,7 @@ struct sunqec {
void __iomem *gregs; /* QEC Global Registers */
struct sunqe *qes[4]; /* Each child MACE */
unsigned int qec_bursts; /* Support burst sizes */
- struct of_device *op; /* QEC's OF device */
+ struct platform_device *op; /* QEC's OF device */
struct sunqec *next_module; /* List of all QECs in system */
};
@@ -342,7 +342,7 @@ struct sunqe {
__u32 buffers_dvma; /* DVMA visible address. */
struct sunqec *parent;
u8 mconfig; /* Base MACE mconfig value */
- struct of_device *op; /* QE's OF device struct */
+ struct platform_device *op; /* QE's OF device struct */
struct net_device *dev; /* QE's netdevice struct */
int channel; /* Who am I? */
};
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 8d532f9..a4c3f57 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -3601,7 +3601,7 @@ static void ucc_geth_timeout(struct net_device *dev)
#ifdef CONFIG_PM
-static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state)
+static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
@@ -3629,7 +3629,7 @@ static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int ucc_geth_resume(struct of_device *ofdev)
+static int ucc_geth_resume(struct platform_device *ofdev)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
@@ -3732,7 +3732,7 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
#endif
};
-static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match)
+static int ucc_geth_probe(struct platform_device* ofdev, const struct of_device_id *match)
{
struct device *device = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
@@ -3954,7 +3954,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
return 0;
}
-static int ucc_geth_remove(struct of_device* ofdev)
+static int ucc_geth_remove(struct platform_device* ofdev)
{
struct device *device = &ofdev->dev;
struct net_device *dev = dev_get_drvdata(device);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 7f62e2d..ca7fc9d 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -315,7 +315,7 @@ EXPORT_SYMBOL_GPL(usbnet_defer_kevent);
static void rx_complete (struct urb *urb);
-static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
+static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
struct sk_buff *skb;
struct skb_data *entry;
@@ -327,7 +327,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
usb_free_urb (urb);
- return;
+ return -ENOMEM;
}
skb_reserve (skb, NET_IP_ALIGN);
@@ -357,6 +357,9 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
netif_dbg(dev, ifdown, dev->net, "device gone\n");
netif_device_detach (dev->net);
break;
+ case -EHOSTUNREACH:
+ retval = -ENOLINK;
+ break;
default:
netif_dbg(dev, rx_err, dev->net,
"rx submit, %d\n", retval);
@@ -374,6 +377,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
dev_kfree_skb_any (skb);
usb_free_urb (urb);
}
+ return retval;
}
@@ -912,6 +916,7 @@ fail_halt:
/* tasklet could resubmit itself forever if memory is tight */
if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
struct urb *urb = NULL;
+ int resched = 1;
if (netif_running (dev->net))
urb = usb_alloc_urb (0, GFP_KERNEL);
@@ -922,10 +927,12 @@ fail_halt:
status = usb_autopm_get_interface(dev->intf);
if (status < 0)
goto fail_lowmem;
- rx_submit (dev, urb, GFP_KERNEL);
+ if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK)
+ resched = 0;
usb_autopm_put_interface(dev->intf);
fail_lowmem:
- tasklet_schedule (&dev->bh);
+ if (resched)
+ tasklet_schedule (&dev->bh);
}
}
@@ -1175,8 +1182,11 @@ static void usbnet_bh (unsigned long param)
// don't refill the queue all at once
for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
urb = usb_alloc_urb (0, GFP_ATOMIC);
- if (urb != NULL)
- rx_submit (dev, urb, GFP_ATOMIC);
+ if (urb != NULL) {
+ if (rx_submit (dev, urb, GFP_ATOMIC) ==
+ -ENOLINK)
+ return;
+ }
}
if (temp != dev->rxq.qlen)
netif_dbg(dev, link, dev->net,
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index ad7719f..e050bd6 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -885,20 +885,21 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port,
* Receive a frame through the DMA
*/
static inline void
-fst_rx_dma(struct fst_card_info *card, unsigned char *skb,
- unsigned char *mem, int len)
+fst_rx_dma(struct fst_card_info *card, dma_addr_t skb,
+ dma_addr_t mem, int len)
{
/*
* This routine will setup the DMA and start it
*/
- dbg(DBG_RX, "In fst_rx_dma %p %p %d\n", skb, mem, len);
+ dbg(DBG_RX, "In fst_rx_dma %lx %lx %d\n",
+ (unsigned long) skb, (unsigned long) mem, len);
if (card->dmarx_in_progress) {
dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n");
}
- outl((unsigned long) skb, card->pci_conf + DMAPADR0); /* Copy to here */
- outl((unsigned long) mem, card->pci_conf + DMALADR0); /* from here */
+ outl(skb, card->pci_conf + DMAPADR0); /* Copy to here */
+ outl(mem, card->pci_conf + DMALADR0); /* from here */
outl(len, card->pci_conf + DMASIZ0); /* for this length */
outl(0x00000000c, card->pci_conf + DMADPR0); /* In this direction */
@@ -1309,8 +1310,8 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port)
card->dma_port_rx = port;
card->dma_len_rx = len;
card->dma_rxpos = rxp;
- fst_rx_dma(card, (char *) card->rx_dma_handle_card,
- (char *) BUF_OFFSET(rxBuffer[pi][rxp][0]), len);
+ fst_rx_dma(card, card->rx_dma_handle_card,
+ BUF_OFFSET(rxBuffer[pi][rxp][0]), len);
}
if (rxp != port->rxpos) {
dbg(DBG_ASS, "About to increment rxpos by more than 1\n");
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index 88e3630..6c571e1 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -396,7 +396,7 @@ static void hss_config(struct port *port)
msg.cmd = PORT_CONFIG_WRITE;
msg.hss_port = port->id;
msg.index = HSS_CONFIG_TX_PCR;
- msg.data32 = PCR_FRM_SYNC_OUTPUT_RISING | PCR_MSB_ENDIAN |
+ msg.data32 = PCR_FRM_PULSE_DISABLED | PCR_MSB_ENDIAN |
PCR_TX_DATA_ENABLE | PCR_SOF_NO_FBIT;
if (port->clock_type == CLOCK_INT)
msg.data32 |= PCR_SYNC_CLK_DIR_OUTPUT;
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 8848333..fec0262 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -260,7 +260,7 @@ struct iwl_cfg iwl1000_bgn_cfg = {
.shadow_ram_support = false,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index a07310f..6950a78 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -769,22 +769,6 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv,
rts_retry_limit = data_retry_limit;
tx_cmd->rts_retry_limit = rts_retry_limit;
- if (ieee80211_is_mgmt(fc)) {
- switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
- case cpu_to_le16(IEEE80211_STYPE_AUTH):
- case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
- case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
- case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
- if (tx_flags & TX_CMD_FLG_RTS_MSK) {
- tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- tx_flags |= TX_CMD_FLG_CTS_MSK;
- }
- break;
- default:
- break;
- }
- }
-
tx_cmd->rate = rate;
tx_cmd->tx_flags = tx_flags;
@@ -2717,7 +2701,7 @@ static struct iwl_lib_ops iwl3945_lib = {
static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = {
.get_hcmd_size = iwl3945_get_hcmd_size,
.build_addsta_hcmd = iwl3945_build_addsta_hcmd,
- .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag,
+ .tx_cmd_protection = iwlcore_tx_cmd_protection,
.request_scan = iwl3945_request_scan,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index d6531ad..d6da356 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2223,7 +2223,7 @@ static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = {
.build_addsta_hcmd = iwl4965_build_addsta_hcmd,
.chain_noise_reset = iwl4965_chain_noise_reset,
.gain_computation = iwl4965_gain_computation,
- .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag,
+ .tx_cmd_protection = iwlcore_tx_cmd_protection,
.calc_rssi = iwl4965_calc_rssi,
.request_scan = iwlagn_request_scan,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 8093ce2..aacf377 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -506,7 +506,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
.use_bsm = false,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
@@ -537,7 +537,7 @@ struct iwl_cfg iwl5100_bgn_cfg = {
.use_bsm = false,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
@@ -597,7 +597,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
.use_bsm = false,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
@@ -628,7 +628,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
.use_bsm = false,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
@@ -659,7 +659,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
.use_bsm = false,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 5827052..af4fd50 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -381,7 +381,7 @@ struct iwl_cfg iwl6000g2a_2agn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -489,7 +489,7 @@ struct iwl_cfg iwl6000g2b_2agn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -563,7 +563,7 @@ struct iwl_cfg iwl6000g2b_2bgn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -637,7 +637,7 @@ struct iwl_cfg iwl6000g2b_bgn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -714,7 +714,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -821,7 +821,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -859,7 +859,7 @@ struct iwl_cfg iwl6050g2_bgn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
@@ -933,7 +933,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .use_rts_for_aggregation = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
index a7216dd..75b901b3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
@@ -211,10 +211,21 @@ static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
}
}
-static void iwlagn_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
- __le32 *tx_flags)
+static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags)
{
- *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS ||
+ info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
+ return;
+ }
+
+ if (priv->cfg->use_rts_for_aggregation &&
+ info->flags & IEEE80211_TX_CTL_AMPDU) {
+ *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK;
+ return;
+ }
}
/* Calc max signal level (dBm) among 3 possible receivers */
@@ -268,7 +279,7 @@ struct iwl_hcmd_utils_ops iwlagn_hcmd_utils = {
.build_addsta_hcmd = iwlagn_build_addsta_hcmd,
.gain_computation = iwlagn_gain_computation,
.chain_noise_reset = iwlagn_chain_noise_reset,
- .rts_tx_cmd_flag = iwlagn_rts_tx_cmd_flag,
+ .tx_cmd_protection = iwlagn_tx_cmd_protection,
.calc_rssi = iwlagn_calc_rssi,
.request_scan = iwlagn_request_scan,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index d04502d..69155aa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -379,10 +379,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- priv->cfg->ops->utils->rts_tx_cmd_flag(info, &tx_flags);
-
- if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK))
- tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+ priv->cfg->ops->utils->tx_cmd_protection(priv, info, fc, &tx_flags);
tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
if (ieee80211_is_mgmt(fc)) {
@@ -456,21 +453,6 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
rate_flags |= RATE_MCS_CCK_MSK;
- /* Set up RTS and CTS flags for certain packets */
- switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
- case cpu_to_le16(IEEE80211_STYPE_AUTH):
- case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
- case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
- case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
- if (tx_cmd->tx_flags & TX_CMD_FLG_RTS_MSK) {
- tx_cmd->tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- tx_cmd->tx_flags |= TX_CMD_FLG_CTS_MSK;
- }
- break;
- default:
- break;
- }
-
/* Set up antennas */
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
priv->hw_params.valid_tx_ant);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 35337b1e..c1882fd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -202,13 +202,6 @@ int iwl_commit_rxon(struct iwl_priv *priv)
priv->start_calib = 0;
if (new_assoc) {
- /*
- * allow CTS-to-self if possible for new association.
- * this is relevant only for 5000 series and up,
- * but will not damage 4965
- */
- priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN;
-
/* Apply the new configuration
* RXON assoc doesn't clear the station table in uCode,
*/
@@ -1618,45 +1611,9 @@ static ssize_t store_tx_power(struct device *d,
static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power);
-static ssize_t show_rts_ht_protection(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
-
- return sprintf(buf, "%s\n",
- priv->cfg->use_rts_for_ht ? "RTS/CTS" : "CTS-to-self");
-}
-
-static ssize_t store_rts_ht_protection(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
- unsigned long val;
- int ret;
-
- ret = strict_strtoul(buf, 10, &val);
- if (ret)
- IWL_INFO(priv, "Input is not in decimal form.\n");
- else {
- if (!iwl_is_associated(priv))
- priv->cfg->use_rts_for_ht = val ? true : false;
- else
- IWL_ERR(priv, "Sta associated with AP - "
- "Change protection mechanism is not allowed\n");
- ret = count;
- }
- return ret;
-}
-
-static DEVICE_ATTR(rts_ht_protection, S_IWUSR | S_IRUGO,
- show_rts_ht_protection, store_rts_ht_protection);
-
-
static struct attribute *iwl_sysfs_entries[] = {
&dev_attr_temperature.attr,
&dev_attr_tx_power.attr,
- &dev_attr_rts_ht_protection.attr,
#ifdef CONFIG_IWLWIFI_DEBUG
&dev_attr_debug_level.attr,
#endif
@@ -3464,25 +3421,6 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
return ret;
}
-/*
- * switch to RTS/CTS for TX
- */
-static void iwl_enable_rts_cts(struct iwl_priv *priv)
-{
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- priv->staging_rxon.flags &= ~RXON_FLG_SELF_CTS_EN;
- if (!test_bit(STATUS_SCANNING, &priv->status)) {
- IWL_DEBUG_INFO(priv, "use RTS/CTS protection\n");
- iwlcore_commit_rxon(priv);
- } else {
- /* scanning, defer the request until scan completed */
- IWL_DEBUG_INFO(priv, "defer setting RTS/CTS protection\n");
- }
-}
-
static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
@@ -3529,14 +3467,33 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
}
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
ret = 0;
+ if (priv->cfg->use_rts_for_aggregation) {
+ struct iwl_station_priv *sta_priv =
+ (void *) sta->drv_priv;
+ /*
+ * switch off RTS/CTS if it was previously enabled
+ */
+
+ sta_priv->lq_sta.lq.general_params.flags &=
+ ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
+ iwl_send_lq_cmd(priv, &sta_priv->lq_sta.lq,
+ CMD_ASYNC, false);
+ }
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
- if (priv->cfg->use_rts_for_ht) {
+ if (priv->cfg->use_rts_for_aggregation) {
+ struct iwl_station_priv *sta_priv =
+ (void *) sta->drv_priv;
+
/*
* switch to RTS/CTS if it is the prefer protection
* method for HT traffic
*/
- iwl_enable_rts_cts(priv);
+
+ sta_priv->lq_sta.lq.general_params.flags |=
+ LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
+ iwl_send_lq_cmd(priv, &sta_priv->lq_sta.lq,
+ CMD_ASYNC, false);
}
ret = 0;
break;
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 8ccb6d2..2c03c6e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -401,21 +401,38 @@ void iwlcore_free_geos(struct iwl_priv *priv)
EXPORT_SYMBOL(iwlcore_free_geos);
/*
- * iwlcore_rts_tx_cmd_flag: Set rts/cts. 3945 and 4965 only share this
+ * iwlcore_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this
* function.
*/
-void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
- __le32 *tx_flags)
+void iwlcore_tx_cmd_protection(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags)
{
if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
*tx_flags |= TX_CMD_FLG_RTS_MSK;
*tx_flags &= ~TX_CMD_FLG_CTS_MSK;
+ *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+
+ if (!ieee80211_is_mgmt(fc))
+ return;
+
+ switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
+ case cpu_to_le16(IEEE80211_STYPE_AUTH):
+ case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
+ case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
+ case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
+ *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+ *tx_flags |= TX_CMD_FLG_CTS_MSK;
+ break;
+ }
} else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
*tx_flags &= ~TX_CMD_FLG_RTS_MSK;
*tx_flags |= TX_CMD_FLG_CTS_MSK;
+ *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
}
}
-EXPORT_SYMBOL(iwlcore_rts_tx_cmd_flag);
+EXPORT_SYMBOL(iwlcore_tx_cmd_protection);
+
static bool is_single_rx_stream(struct iwl_priv *priv)
{
@@ -1869,6 +1886,10 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
priv->staging_rxon.flags |= RXON_FLG_TGG_PROTECT_MSK;
else
priv->staging_rxon.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
+ if (bss_conf->use_cts_prot)
+ priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN;
+ else
+ priv->staging_rxon.flags &= ~RXON_FLG_SELF_CTS_EN;
}
if (changes & BSS_CHANGED_BASIC_RATES) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index e9d23f2..4a71dfb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -104,8 +104,9 @@ struct iwl_hcmd_utils_ops {
u32 min_average_noise,
u8 default_chain);
void (*chain_noise_reset)(struct iwl_priv *priv);
- void (*rts_tx_cmd_flag)(struct ieee80211_tx_info *info,
- __le32 *tx_flags);
+ void (*tx_cmd_protection)(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags);
int (*calc_rssi)(struct iwl_priv *priv,
struct iwl_rx_phy_res *rx_resp);
void (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
@@ -249,7 +250,7 @@ struct iwl_mod_params {
* @led_compensation: compensate on the led on/off time per HW according
* to the deviation to achieve the desired led frequency.
* The detail algorithm is described in iwl-led.c
- * @use_rts_for_ht: use rts/cts protection for HT traffic
+ * @use_rts_for_aggregation: use rts/cts protection for HT traffic
* @chain_noise_num_beacons: number of beacons used to compute chain noise
* @adv_thermal_throttle: support advance thermal throttle
* @support_ct_kill_exit: support ct kill exit condition
@@ -318,7 +319,7 @@ struct iwl_cfg {
const bool ht_greenfield_support;
u16 led_compensation;
const bool broken_powersave;
- bool use_rts_for_ht;
+ bool use_rts_for_aggregation;
int chain_noise_num_beacons;
const bool supports_idle;
bool adv_thermal_throttle;
@@ -390,8 +391,9 @@ void iwl_config_ap(struct iwl_priv *priv, struct ieee80211_vif *vif);
void iwl_mac_reset_tsf(struct ieee80211_hw *hw);
int iwl_alloc_txq_mem(struct iwl_priv *priv);
void iwl_free_txq_mem(struct iwl_priv *priv);
-void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
- __le32 *tx_flags);
+void iwlcore_tx_cmd_protection(struct iwl_priv *priv,
+ struct ieee80211_tx_info *info,
+ __le16 fc, __le32 *tx_flags);
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_alloc_traffic_mem(struct iwl_priv *priv);
void iwl_free_traffic_mem(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index d24eb47..70c4b8f 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -435,10 +435,7 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv,
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- priv->cfg->ops->utils->rts_tx_cmd_flag(info, &tx_flags);
-
- if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK))
- tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+ priv->cfg->ops->utils->tx_cmd_protection(priv, info, fc, &tx_flags);
tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
if (ieee80211_is_mgmt(fc)) {
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 8e9fbfd..3e82f16 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -6,7 +6,10 @@
*
*/
+#include <linux/sched.h>
+#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/sched.h>
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 6e71346..ba854c7 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -125,6 +125,8 @@ struct if_sdio_card {
const char *helper;
const char *firmware;
+ bool helper_allocated;
+ bool firmware_allocated;
u8 buffer[65536];
@@ -984,16 +986,34 @@ static int if_sdio_probe(struct sdio_func *func,
card->helper = if_sdio_models[i].helper;
card->firmware = if_sdio_models[i].firmware;
+ kparam_block_sysfs_write(helper_name);
if (lbs_helper_name) {
+ char *helper = kstrdup(lbs_helper_name, GFP_KERNEL);
+ if (!helper) {
+ kparam_unblock_sysfs_write(helper_name);
+ ret = -ENOMEM;
+ goto free;
+ }
lbs_deb_sdio("overriding helper firmware: %s\n",
lbs_helper_name);
- card->helper = lbs_helper_name;
+ card->helper = helper;
+ card->helper_allocated = true;
}
+ kparam_unblock_sysfs_write(helper_name);
+ kparam_block_sysfs_write(fw_name);
if (lbs_fw_name) {
+ char *fw_name = kstrdup(lbs_fw_name, GFP_KERNEL);
+ if (!fw_name) {
+ kparam_unblock_sysfs_write(fw_name);
+ ret = -ENOMEM;
+ goto free;
+ }
lbs_deb_sdio("overriding firmware: %s\n", lbs_fw_name);
- card->firmware = lbs_fw_name;
+ card->firmware = fw_name;
+ card->firmware_allocated = true;
}
+ kparam_unblock_sysfs_write(fw_name);
sdio_claim_host(func);
@@ -1127,6 +1147,10 @@ free:
kfree(packet);
}
+ if (card->helper_allocated)
+ kfree(card->helper);
+ if (card->firmware_allocated)
+ kfree(card->firmware);
kfree(card);
goto out;
@@ -1177,6 +1201,10 @@ static void if_sdio_remove(struct sdio_func *func)
kfree(packet);
}
+ if (card->helper_allocated)
+ kfree(card->helper);
+ if (card->firmware_allocated)
+ kfree(card->firmware);
kfree(card);
lbs_deb_leave(LBS_DEB_SDIO);
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 07ece9d..3ff6106 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -289,10 +289,13 @@ static int if_usb_probe(struct usb_interface *intf,
}
/* Upload firmware */
+ kparam_block_sysfs_write(fw_name);
if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) {
+ kparam_unblock_sysfs_write(fw_name);
lbs_deb_usbd(&udev->dev, "FW upload failed\n");
goto err_prog_firmware;
}
+ kparam_unblock_sysfs_write(fw_name);
if (!(priv = lbs_add_card(cardp, &udev->dev)))
goto err_prog_firmware;
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c
index b172f5d..41a4f21 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/libertas_tf/if_usb.c
@@ -811,12 +811,15 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp)
lbtf_deb_enter(LBTF_DEB_USB);
+ kparam_block_sysfs_write(fw_name);
ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev);
if (ret < 0) {
pr_err("request_firmware() failed with %#x\n", ret);
pr_err("firmware %s not found\n", lbtf_fw_name);
+ kparam_unblock_sysfs_write(fw_name);
goto done;
}
+ kparam_unblock_sysfs_write(fw_name);
if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
goto release_fw;
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index 71a101f..822f8dc 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -43,8 +43,6 @@ static DEFINE_PCI_DEVICE_TABLE(p54p_table) = {
{ PCI_DEVICE(0x1260, 0x3886) },
/* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */
{ PCI_DEVICE(0x1260, 0xffff) },
- /* Standard Microsystems Corp SMC2802W Wireless PCI */
- { PCI_DEVICE(0x10b8, 0x2802) },
{ },
};
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index b2c2f39..ecbbb68 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -1086,7 +1086,7 @@ static void xemaclite_remove_ndev(struct net_device *ndev)
*
* Return: Value of the parameter if the parameter is found, or 0 otherwise
*/
-static bool get_bool(struct of_device *ofdev, const char *s)
+static bool get_bool(struct platform_device *ofdev, const char *s)
{
u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL);
@@ -1115,7 +1115,7 @@ static struct net_device_ops xemaclite_netdev_ops;
* Return: 0, if the driver is bound to the Emaclite device, or
* a negative error if there is failure.
*/
-static int __devinit xemaclite_of_probe(struct of_device *ofdev,
+static int __devinit xemaclite_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct resource r_irq; /* Interrupt resources */
@@ -1240,7 +1240,7 @@ error2:
*
* Return: 0, always.
*/
-static int __devexit xemaclite_of_remove(struct of_device *of_dev)
+static int __devexit xemaclite_of_remove(struct platform_device *of_dev)
{
struct device *dev = &of_dev->dev;
struct net_device *ndev = dev_get_drvdata(dev);
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 0d8a064..92de0eb 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -14,7 +14,7 @@
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
- * Used by a driver to check whether an of_device present in the
+ * Used by a driver to check whether an platform_device present in the
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index 40e208d..f01e264 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -342,7 +342,6 @@ static int __devinit parport_register (struct pci_dev *dev,
dev_dbg(&dev->dev,
"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
io_lo, io_hi, irq);
- irq = PARPORT_IRQ_NONE;
}
port = parport_pc_probe_port (io_lo, io_hi, irq,
PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c
index 210a644..55ba118 100644
--- a/drivers/parport/parport_sunbpp.c
+++ b/drivers/parport/parport_sunbpp.c
@@ -286,7 +286,7 @@ static struct parport_operations parport_sunbpp_ops =
.owner = THIS_MODULE,
};
-static int __devinit bpp_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit bpp_probe(struct platform_device *op, const struct of_device_id *match)
{
struct parport_operations *ops;
struct bpp_regs __iomem *regs;
@@ -351,7 +351,7 @@ out_unmap:
return err;
}
-static int __devexit bpp_remove(struct of_device *op)
+static int __devexit bpp_remove(struct platform_device *op)
{
struct parport *p = dev_get_drvdata(&op->dev);
struct parport_operations *ops = p->ops;
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index c988514..c80a7a6 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -215,7 +215,7 @@ config PCMCIA_PXA2XX
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \
- || MACH_VPAC270)
+ || MACH_VPAC270 || MACH_BALLOON3)
select PCMCIA_SOC_COMMON
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 7a2b160..8d9386a 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -69,6 +69,7 @@ pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o
+pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c
index f94d828..546d302 100644
--- a/drivers/pcmcia/electra_cf.c
+++ b/drivers/pcmcia/electra_cf.c
@@ -44,7 +44,7 @@ struct electra_cf_socket {
unsigned present:1;
unsigned active:1;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
unsigned long mem_phys;
void __iomem * mem_base;
unsigned long mem_size;
@@ -181,7 +181,7 @@ static struct pccard_operations electra_cf_ops = {
.set_mem_map = electra_cf_set_mem_map,
};
-static int __devinit electra_cf_probe(struct of_device *ofdev,
+static int __devinit electra_cf_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device *device = &ofdev->dev;
@@ -325,7 +325,7 @@ fail1:
}
-static int __devexit electra_cf_remove(struct of_device *ofdev)
+static int __devexit electra_cf_remove(struct platform_device *ofdev)
{
struct device *device = &ofdev->dev;
struct electra_cf_socket *cf;
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index f2f90a7..f0ecad9 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -1149,7 +1149,7 @@ static struct pccard_operations m8xx_services = {
.set_mem_map = m8xx_set_mem_map,
};
-static int __init m8xx_probe(struct of_device *ofdev,
+static int __init m8xx_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct pcmcia_win *w;
@@ -1249,7 +1249,7 @@ static int __init m8xx_probe(struct of_device *ofdev,
return 0;
}
-static int m8xx_remove(struct of_device *ofdev)
+static int m8xx_remove(struct platform_device *ofdev)
{
u32 m, i;
struct pcmcia_win *w;
diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c
new file mode 100644
index 0000000..dbbdd00
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_balloon3.c
@@ -0,0 +1,158 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_balloon3.c
+ *
+ * Balloon3 PCMCIA specific routines.
+ *
+ * Author: Nick Bane
+ * Created: June, 2006
+ * Copyright: Toby Churchill Ltd
+ * Derived from pxa2xx_mainstone.c, by Nico Pitre
+ *
+ * Various modification by Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/balloon3.h>
+
+#include "soc_common.h"
+
+/*
+ * These are a list of interrupt sources that provokes a polled
+ * check of status
+ */
+static struct pcmcia_irqs irqs[] = {
+ { 0, BALLOON3_S0_CD_IRQ, "PCMCIA0 CD" },
+ { 0, BALLOON3_BP_NSTSCHG_IRQ, "PCMCIA0 STSCHG" },
+};
+
+static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ uint16_t ver;
+ int ret;
+ static void __iomem *fpga_ver;
+
+ ver = __raw_readw(BALLOON3_FPGA_VER);
+ if (ver > 0x0201)
+ pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. "
+ "PCMCIA/CF support might be broken in this version!",
+ ver);
+
+ skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ;
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void balloon3_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static unsigned long balloon3_pcmcia_status[2] = {
+ BALLOON3_CF_nSTSCHG_BVD1,
+ BALLOON3_CF_nSTSCHG_BVD1
+};
+
+static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ uint16_t status;
+ int flip;
+
+ /* This actually reads the STATUS register */
+ status = __raw_readw(BALLOON3_CF_STATUS_REG);
+ flip = (status ^ balloon3_pcmcia_status[skt->nr])
+ & BALLOON3_CF_nSTSCHG_BVD1;
+ /*
+ * Workaround for STSCHG which can't be deasserted:
+ * We therefore disable/enable corresponding IRQs
+ * as needed to avoid IRQ locks.
+ */
+ if (flip) {
+ balloon3_pcmcia_status[skt->nr] = status;
+ if (status & BALLOON3_CF_nSTSCHG_BVD1)
+ enable_irq(BALLOON3_BP_NSTSCHG_IRQ);
+ else
+ disable_irq(BALLOON3_BP_NSTSCHG_IRQ);
+ }
+
+ state->detect = !gpio_get_value(BALLOON3_GPIO_S0_CD);
+ state->ready = !!(status & BALLOON3_CF_nIRQ);
+ state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1);
+ state->bvd2 = 0; /* not available */
+ state->vs_3v = 1; /* Always true its a CF card */
+ state->vs_Xv = 0; /* not available */
+ state->wrprot = 0; /* not available */
+}
+
+static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ __raw_writew((state->flags & SS_RESET) ? BALLOON3_CF_RESET : 0,
+ BALLOON3_CF_CONTROL_REG);
+ return 0;
+}
+
+static void balloon3_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void balloon3_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level balloon3_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = balloon3_pcmcia_hw_init,
+ .hw_shutdown = balloon3_pcmcia_hw_shutdown,
+ .socket_state = balloon3_pcmcia_socket_state,
+ .configure_socket = balloon3_pcmcia_configure_socket,
+ .socket_init = balloon3_pcmcia_socket_init,
+ .socket_suspend = balloon3_pcmcia_socket_suspend,
+ .first = 0,
+ .nr = 1,
+};
+
+static struct platform_device *balloon3_pcmcia_device;
+
+static int __init balloon3_pcmcia_init(void)
+{
+ int ret;
+
+ balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!balloon3_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(balloon3_pcmcia_device,
+ &balloon3_pcmcia_ops, sizeof(balloon3_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(balloon3_pcmcia_device);
+
+ if (ret)
+ platform_device_put(balloon3_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit balloon3_pcmcia_exit(void)
+{
+ platform_device_unregister(balloon3_pcmcia_device);
+}
+
+module_init(balloon3_pcmcia_init);
+module_exit(balloon3_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nick Bane <nick@cecomputing.co.uk>");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
+MODULE_DESCRIPTION("Balloon3 board CF/PCMCIA driver");
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 79baa63..044f430 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -219,6 +219,13 @@ config SONYPI_COMPAT
---help---
Build the sonypi driver compatibility code into the sony-laptop driver.
+config IDEAPAD_ACPI
+ tristate "Lenovo IdeaPad ACPI Laptop Extras"
+ depends on ACPI
+ depends on RFKILL
+ help
+ This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
+
config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 4744c77..85fb2b84 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
+obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c
new file mode 100644
index 0000000..7984963
--- /dev/null
+++ b/drivers/platform/x86/ideapad_acpi.c
@@ -0,0 +1,306 @@
+/*
+ * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras
+ *
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/rfkill.h>
+
+#define IDEAPAD_DEV_CAMERA 0
+#define IDEAPAD_DEV_WLAN 1
+#define IDEAPAD_DEV_BLUETOOTH 2
+#define IDEAPAD_DEV_3G 3
+#define IDEAPAD_DEV_KILLSW 4
+
+struct ideapad_private {
+ struct rfkill *rfk[5];
+};
+
+static struct {
+ char *name;
+ int type;
+} ideapad_rfk_data[] = {
+ /* camera has no rfkill */
+ { "ideapad_wlan", RFKILL_TYPE_WLAN },
+ { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH },
+ { "ideapad_3g", RFKILL_TYPE_WWAN },
+ { "ideapad_killsw", RFKILL_TYPE_WLAN }
+};
+
+static int ideapad_dev_exists(int device)
+{
+ acpi_status status;
+ union acpi_object in_param;
+ struct acpi_object_list input = { 1, &in_param };
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ in_param.type = ACPI_TYPE_INTEGER;
+ in_param.integer.value = device + 1;
+
+ status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status);
+ return -ENODEV;
+ }
+ if (out_obj.type != ACPI_TYPE_INTEGER) {
+ printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n");
+ return -ENODEV;
+ }
+ return out_obj.integer.value;
+}
+
+static int ideapad_dev_get_state(int device)
+{
+ acpi_status status;
+ union acpi_object in_param;
+ struct acpi_object_list input = { 1, &in_param };
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ in_param.type = ACPI_TYPE_INTEGER;
+ in_param.integer.value = device + 1;
+
+ status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status);
+ return -ENODEV;
+ }
+ if (out_obj.type != ACPI_TYPE_INTEGER) {
+ printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n");
+ return -ENODEV;
+ }
+ return out_obj.integer.value;
+}
+
+static int ideapad_dev_set_state(int device, int state)
+{
+ acpi_status status;
+ union acpi_object in_params[2];
+ struct acpi_object_list input = { 2, in_params };
+
+ in_params[0].type = ACPI_TYPE_INTEGER;
+ in_params[0].integer.value = device + 1;
+ in_params[1].type = ACPI_TYPE_INTEGER;
+ in_params[1].integer.value = state;
+
+ status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
+ return -ENODEV;
+ }
+ return 0;
+}
+static ssize_t show_ideapad_cam(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
+ if (state < 0)
+ return state;
+
+ return sprintf(buf, "%d\n", state);
+}
+
+static ssize_t store_ideapad_cam(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, state;
+
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", &state) != 1)
+ return -EINVAL;
+ ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
+
+static int ideapad_rfk_set(void *data, bool blocked)
+{
+ int device = (unsigned long)data;
+
+ if (device == IDEAPAD_DEV_KILLSW)
+ return -EINVAL;
+ return ideapad_dev_set_state(device, !blocked);
+}
+
+static struct rfkill_ops ideapad_rfk_ops = {
+ .set_block = ideapad_rfk_set,
+};
+
+static void ideapad_sync_rfk_state(struct acpi_device *adevice)
+{
+ struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+ int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW);
+ int i;
+
+ rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked);
+ for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
+ if (priv->rfk[i])
+ rfkill_set_hw_state(priv->rfk[i], hw_blocked);
+ if (hw_blocked)
+ return;
+
+ for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
+ if (priv->rfk[i])
+ rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i));
+}
+
+static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
+{
+ struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+ int ret;
+
+ priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev,
+ ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops,
+ (void *)(long)dev);
+ if (!priv->rfk[dev])
+ return -ENOMEM;
+
+ ret = rfkill_register(priv->rfk[dev]);
+ if (ret) {
+ rfkill_destroy(priv->rfk[dev]);
+ return ret;
+ }
+ return 0;
+}
+
+static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
+{
+ struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+
+ if (!priv->rfk[dev])
+ return;
+
+ rfkill_unregister(priv->rfk[dev]);
+ rfkill_destroy(priv->rfk[dev]);
+}
+
+static const struct acpi_device_id ideapad_device_ids[] = {
+ { "VPC2004", 0},
+ { "", 0},
+};
+MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
+
+static int ideapad_acpi_add(struct acpi_device *adevice)
+{
+ int i;
+ int devs_present[5];
+ struct ideapad_private *priv;
+
+ for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
+ devs_present[i] = ideapad_dev_exists(i);
+ if (devs_present[i] < 0)
+ return devs_present[i];
+ }
+
+ /* The hardware switch is always present */
+ devs_present[IDEAPAD_DEV_KILLSW] = 1;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (devs_present[IDEAPAD_DEV_CAMERA]) {
+ int ret = device_create_file(&adevice->dev, &dev_attr_camera_power);
+ if (ret) {
+ kfree(priv);
+ return ret;
+ }
+ }
+
+ dev_set_drvdata(&adevice->dev, priv);
+ for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
+ if (!devs_present[i])
+ continue;
+
+ ideapad_register_rfkill(adevice, i);
+ }
+ ideapad_sync_rfk_state(adevice);
+ return 0;
+}
+
+static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
+{
+ struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+ int i;
+
+ device_remove_file(&adevice->dev, &dev_attr_camera_power);
+
+ for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
+ ideapad_unregister_rfkill(adevice, i);
+
+ dev_set_drvdata(&adevice->dev, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
+{
+ ideapad_sync_rfk_state(adevice);
+}
+
+static struct acpi_driver ideapad_acpi_driver = {
+ .name = "ideapad_acpi",
+ .class = "IdeaPad",
+ .ids = ideapad_device_ids,
+ .ops.add = ideapad_acpi_add,
+ .ops.remove = ideapad_acpi_remove,
+ .ops.notify = ideapad_acpi_notify,
+ .owner = THIS_MODULE,
+};
+
+
+static int __init ideapad_acpi_module_init(void)
+{
+ acpi_bus_register_driver(&ideapad_acpi_driver);
+
+ return 0;
+}
+
+
+static void __exit ideapad_acpi_module_exit(void)
+{
+ acpi_bus_unregister_driver(&ideapad_acpi_driver);
+
+}
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("IdeaPad ACPI Extras");
+MODULE_LICENSE("GPL");
+
+module_init(ideapad_acpi_module_init);
+module_exit(ideapad_acpi_module_exit);
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1e5506b..0734356 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -136,6 +136,12 @@ config BATTERY_Z2
help
Say Y to include support for the battery on the Zipit Z2.
+config BATTERY_S3C_ADC
+ tristate "Battery driver for Samsung ADC based monitoring"
+ depends on S3C_ADC
+ help
+ Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
+
config CHARGER_PCF50633
tristate "NXP PCF50633 MBC"
depends on MFD_PCF50633
@@ -153,4 +159,11 @@ config BATTERY_JZ4740
This driver can be build as a module. If so, the module will be
called jz4740-battery.
+config BATTERY_INTEL_MID
+ tristate "Battery driver for Intel MID platforms"
+ depends on INTEL_SCU_IPC && SPI
+ help
+ Say Y here to enable the battery driver on Intel MID
+ platforms.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index cf95009..10143aa 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -33,5 +33,7 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
+obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
+obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c
new file mode 100644
index 0000000..c61ffec
--- /dev/null
+++ b/drivers/power/intel_mid_battery.c
@@ -0,0 +1,799 @@
+/*
+ * intel_mid_battery.c - Intel MID PMIC Battery 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 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.
+ *
+ * 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define DRIVER_NAME "pmic_battery"
+
+/*********************************************************************
+ * Generic defines
+ *********************************************************************/
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
+
+#define PMIC_BATT_DRV_INFO_UPDATED 1
+#define PMIC_BATT_PRESENT 1
+#define PMIC_BATT_NOT_PRESENT 0
+#define PMIC_USB_PRESENT PMIC_BATT_PRESENT
+#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT
+
+/* pmic battery register related */
+#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2
+#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1)
+#define PMIC_BATT_CHR_STEMP_MASK (1 << 2)
+#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3)
+#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4)
+#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5)
+#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6)
+#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7)
+#define PMIC_BATT_CHR_EXCPT_MASK 0xC6
+#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31)
+#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF
+
+/* pmic ipc related */
+#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4
+#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6
+
+/* types of battery charging */
+enum batt_charge_type {
+ BATT_USBOTG_500MA_CHARGE,
+ BATT_USBOTG_TRICKLE_CHARGE,
+};
+
+/* valid battery events */
+enum batt_event {
+ BATT_EVENT_BATOVP_EXCPT,
+ BATT_EVENT_USBOVP_EXCPT,
+ BATT_EVENT_TEMP_EXCPT,
+ BATT_EVENT_DCLMT_EXCPT,
+ BATT_EVENT_EXCPT
+};
+
+
+/*********************************************************************
+ * Battery properties
+ *********************************************************************/
+
+/*
+ * pmic battery info
+ */
+struct pmic_power_module_info {
+ bool is_dev_info_updated;
+ struct device *dev;
+ /* pmic battery data */
+ unsigned long update_time; /* jiffies when data read */
+ unsigned int usb_is_present;
+ unsigned int batt_is_present;
+ unsigned int batt_health;
+ unsigned int usb_health;
+ unsigned int batt_status;
+ unsigned int batt_charge_now; /* in mAS */
+ unsigned int batt_prev_charge_full; /* in mAS */
+ unsigned int batt_charge_rate; /* in units per second */
+
+ struct power_supply usb;
+ struct power_supply batt;
+ int irq; /* GPE_ID or IRQ# */
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_battery;
+ struct work_struct handler;
+};
+
+static unsigned int delay_time = 2000; /* in ms */
+
+/*
+ * pmic ac properties
+ */
+static enum power_supply_property pmic_usb_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+/*
+ * pmic battery properties
+ */
+static enum power_supply_property pmic_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+};
+
+
+/*
+ * Glue functions for talking to the IPC
+ */
+
+struct battery_property {
+ u32 capacity; /* Charger capacity */
+ u8 crnt; /* Quick charge current value*/
+ u8 volt; /* Fine adjustment of constant charge voltage */
+ u8 prot; /* CHRGPROT register value */
+ u8 prot2; /* CHRGPROT1 register value */
+ u8 timer; /* Charging timer */
+};
+
+#define IPCMSG_BATTERY 0xEF
+
+/* Battery coulomb counter accumulator commands */
+#define IPC_CMD_CC_WR 0 /* Update coulomb counter value */
+#define IPC_CMD_CC_RD 1 /* Read coulomb counter value */
+#define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */
+
+/**
+ * pmic_scu_ipc_battery_cc_read - read battery cc
+ * @value: battery coulomb counter read
+ *
+ * Reads the battery couloumb counter value, returns 0 on success, or
+ * an error code
+ *
+ * This function may sleep. Locking for SCU accesses is handled for
+ * the caller.
+ */
+static int pmic_scu_ipc_battery_cc_read(u32 *value)
+{
+ return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
+ NULL, 0, value, 1);
+}
+
+/**
+ * pmic_scu_ipc_battery_property_get - fetch properties
+ * @prop: battery properties
+ *
+ * Retrieve the battery properties from the power management
+ *
+ * This function may sleep. Locking for SCU accesses is handled for
+ * the caller.
+ */
+static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
+{
+ u32 data[3];
+ u8 *p = (u8 *)&data[1];
+ int err = intel_scu_ipc_command(IPC_CMD_BATTERY_PROPERTY,
+ IPCMSG_BATTERY, NULL, 0, data, 3);
+
+ prop->capacity = data[0];
+ prop->crnt = *p++;
+ prop->volt = *p++;
+ prop->prot = *p++;
+ prop->prot2 = *p++;
+ prop->timer = *p++;
+
+ return err;
+}
+
+/**
+ * pmic_scu_ipc_set_charger - set charger
+ * @charger: charger to select
+ *
+ * Switch the charging mode for the SCU
+ */
+
+static int pmic_scu_ipc_set_charger(int charger)
+{
+ return intel_scu_ipc_simple_command(charger, IPCMSG_BATTERY);
+}
+
+/**
+ * pmic_battery_log_event - log battery events
+ * @event: battery event to be logged
+ * Context: can sleep
+ *
+ * There are multiple battery events which may be of interest to users;
+ * this battery function logs the different battery events onto the
+ * kernel log messages.
+ */
+static void pmic_battery_log_event(enum batt_event event)
+{
+ printk(KERN_WARNING "pmic-battery: ");
+ switch (event) {
+ case BATT_EVENT_BATOVP_EXCPT:
+ printk(KERN_CONT "battery overvoltage condition\n");
+ break;
+ case BATT_EVENT_USBOVP_EXCPT:
+ printk(KERN_CONT "usb charger overvoltage condition\n");
+ break;
+ case BATT_EVENT_TEMP_EXCPT:
+ printk(KERN_CONT "high battery temperature condition\n");
+ break;
+ case BATT_EVENT_DCLMT_EXCPT:
+ printk(KERN_CONT "over battery charge current condition\n");
+ break;
+ default:
+ printk(KERN_CONT "charger/battery exception %d\n", event);
+ break;
+ }
+}
+
+/**
+ * pmic_battery_read_status - read battery status information
+ * @pbi: device info structure to update the read information
+ * Context: can sleep
+ *
+ * PMIC power source information need to be updated based on the data read
+ * from the PMIC battery registers.
+ *
+ */
+static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
+{
+ unsigned int update_time_intrvl;
+ unsigned int chrg_val;
+ u32 ccval;
+ u8 r8;
+ struct battery_property batt_prop;
+ int batt_present = 0;
+ int usb_present = 0;
+ int batt_exception = 0;
+
+ /* make sure the last batt_status read happened delay_time before */
+ if (pbi->update_time && time_before(jiffies, pbi->update_time +
+ msecs_to_jiffies(delay_time)))
+ return;
+
+ update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
+ pbi->update_time = jiffies;
+
+ /* read coulomb counter registers and schrgint register */
+ if (pmic_scu_ipc_battery_cc_read(&ccval)) {
+ dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
+ __func__);
+ return;
+ }
+
+ if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
+ dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * set pmic_power_module_info members based on pmic register values
+ * read.
+ */
+
+ /* set batt_is_present */
+ if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
+ pbi->batt_is_present = PMIC_BATT_PRESENT;
+ batt_present = 1;
+ } else {
+ pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
+ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ /* set batt_health */
+ if (batt_present) {
+ if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
+ batt_exception = 1;
+ } else if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
+ batt_exception = 1;
+ } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
+ batt_exception = 1;
+ } else {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ }
+
+ /* set usb_is_present */
+ if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
+ pbi->usb_is_present = PMIC_USB_PRESENT;
+ usb_present = 1;
+ } else {
+ pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ }
+
+ if (usb_present) {
+ if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
+ pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
+ } else {
+ pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ }
+
+ chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
+
+ /* set batt_prev_charge_full to battery capacity the first time */
+ if (!pbi->is_dev_info_updated) {
+ if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
+ dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
+ __func__);
+ return;
+ }
+ pbi->batt_prev_charge_full = batt_prop.capacity;
+ }
+
+ /* set batt_status */
+ if (batt_present && !batt_exception) {
+ if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
+ pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
+ pbi->batt_prev_charge_full = chrg_val;
+ } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
+ pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ }
+
+ /* set batt_charge_rate */
+ if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
+ if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (pbi->batt_charge_now - chrg_val) {
+ pbi->batt_charge_rate = ((pbi->batt_charge_now -
+ chrg_val) * 1000 * 60) /
+ update_time_intrvl;
+ }
+ } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (chrg_val - pbi->batt_charge_now) {
+ pbi->batt_charge_rate = ((chrg_val -
+ pbi->batt_charge_now) * 1000 * 60) /
+ update_time_intrvl;
+ }
+ } else
+ pbi->batt_charge_rate = 0;
+ } else {
+ pbi->batt_charge_rate = -1;
+ }
+
+ /* batt_charge_now */
+ if (batt_present && !batt_exception)
+ pbi->batt_charge_now = chrg_val;
+ else
+ pbi->batt_charge_now = -1;
+
+ pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
+}
+
+/**
+ * pmic_usb_get_property - usb power source get property
+ * @psy: usb power supply context
+ * @psp: usb power source property
+ * @val: usb power source property value
+ * Context: can sleep
+ *
+ * PMIC usb power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int pmic_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pmic_power_module_info *pbi = container_of(psy,
+ struct pmic_power_module_info, usb);
+
+ /* update pmic_power_module_info members */
+ pmic_battery_read_status(pbi);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = pbi->usb_is_present;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = pbi->usb_health;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline unsigned long mAStouAh(unsigned long v)
+{
+ /* seconds to hours, mA to µA */
+ return (v * 1000) / 3600;
+}
+
+/**
+ * pmic_battery_get_property - battery power source get property
+ * @psy: battery power supply context
+ * @psp: battery power source property
+ * @val: battery power source property value
+ * Context: can sleep
+ *
+ * PMIC battery power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int pmic_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pmic_power_module_info *pbi = container_of(psy,
+ struct pmic_power_module_info, batt);
+
+ /* update pmic_power_module_info members */
+ pmic_battery_read_status(pbi);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = pbi->batt_status;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = pbi->batt_health;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = pbi->batt_is_present;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = mAStouAh(pbi->batt_charge_now);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = mAStouAh(pbi->batt_prev_charge_full);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * pmic_battery_monitor - monitor battery status
+ * @work: work structure
+ * Context: can sleep
+ *
+ * PMIC battery status needs to be monitored for any change
+ * and information needs to be frequently updated.
+ */
+static void pmic_battery_monitor(struct work_struct *work)
+{
+ struct pmic_power_module_info *pbi = container_of(work,
+ struct pmic_power_module_info, monitor_battery.work);
+
+ /* update pmic_power_module_info members */
+ pmic_battery_read_status(pbi);
+ queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
+}
+
+/**
+ * pmic_battery_set_charger - set battery charger
+ * @pbi: device info structure
+ * @chrg: charge mode to set battery charger in
+ * Context: can sleep
+ *
+ * PMIC battery charger needs to be enabled based on the usb charge
+ * capabilities connected to the platform.
+ */
+static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
+ enum batt_charge_type chrg)
+{
+ int retval;
+
+ /* set usblmt bits and chrgcntl register bits appropriately */
+ switch (chrg) {
+ case BATT_USBOTG_500MA_CHARGE:
+ retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
+ break;
+ case BATT_USBOTG_TRICKLE_CHARGE:
+ retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
+ break;
+ default:
+ dev_warn(pbi->dev, "%s(): out of range usb charger "
+ "charge detected\n", __func__);
+ return -EINVAL;
+ }
+
+ if (retval) {
+ dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+ __func__);
+ return retval;;
+ }
+
+ return 0;
+}
+
+/**
+ * pmic_battery_interrupt_handler - pmic battery interrupt handler
+ * Context: interrupt context
+ *
+ * PMIC battery interrupt handler which will be called with either
+ * battery full condition occurs or usb otg & battery connect
+ * condition occurs.
+ */
+static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
+{
+ struct pmic_power_module_info *pbi = dev;
+
+ schedule_work(&pbi->handler);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * pmic_battery_handle_intrpt - pmic battery service interrupt
+ * @work: work structure
+ * Context: can sleep
+ *
+ * PMIC battery needs to either update the battery status as full
+ * if it detects battery full condition caused the interrupt or needs
+ * to enable battery charger if it detects usb and battery detect
+ * caused the source of interrupt.
+ */
+static void pmic_battery_handle_intrpt(struct work_struct *work)
+{
+ struct pmic_power_module_info *pbi = container_of(work,
+ struct pmic_power_module_info, handler);
+ enum batt_charge_type chrg;
+ u8 r8;
+
+ if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
+ dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+ __func__);
+ return;
+ }
+ /* find the cause of the interrupt */
+ if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
+ pbi->batt_is_present = PMIC_BATT_PRESENT;
+ } else {
+ pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
+ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ return;
+ }
+
+ if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pmic_battery_log_event(BATT_EVENT_EXCPT);
+ return;
+ } else {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+
+ if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
+ u32 ccval;
+ pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
+
+ if (pmic_scu_ipc_battery_cc_read(&ccval)) {
+ dev_warn(pbi->dev, "%s(): ipc config cmd "
+ "failed\n", __func__);
+ return;
+ }
+ pbi->batt_prev_charge_full = ccval &
+ PMIC_BATT_ADC_ACCCHRGVAL_MASK;
+ return;
+ }
+
+ if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
+ pbi->usb_is_present = PMIC_USB_PRESENT;
+ } else {
+ pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ return;
+ }
+
+ /* setup battery charging */
+
+#if 0
+ /* check usb otg power capability and set charger accordingly */
+ retval = langwell_udc_maxpower(&power);
+ if (retval) {
+ dev_warn(pbi->dev,
+ "%s(): usb otg power query failed with error code %d\n",
+ __func__, retval);
+ return;
+ }
+
+ if (power >= 500)
+ chrg = BATT_USBOTG_500MA_CHARGE;
+ else
+#endif
+ chrg = BATT_USBOTG_TRICKLE_CHARGE;
+
+ /* enable battery charging */
+ if (pmic_battery_set_charger(pbi, chrg)) {
+ dev_warn(pbi->dev,
+ "%s(): failed to set up battery charging\n", __func__);
+ return;
+ }
+
+ dev_dbg(pbi->dev,
+ "pmic-battery: %s() - setting up battery charger successful\n",
+ __func__);
+}
+
+/**
+ * pmic_battery_probe - pmic battery initialize
+ * @irq: pmic battery device irq
+ * @dev: pmic battery device structure
+ * Context: can sleep
+ *
+ * PMIC battery initializes its internal data structue and other
+ * infrastructure components for it to work as expected.
+ */
+static __devinit int probe(int irq, struct device *dev)
+{
+ int retval = 0;
+ struct pmic_power_module_info *pbi;
+
+ dev_dbg(dev, "pmic-battery: found pmic battery device\n");
+
+ pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
+ if (!pbi) {
+ dev_err(dev, "%s(): memory allocation failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ pbi->dev = dev;
+ pbi->irq = irq;
+ dev_set_drvdata(dev, pbi);
+
+ /* initialize all required framework before enabling interrupts */
+ INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
+ INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
+ pbi->monitor_wqueue =
+ create_singlethread_workqueue(dev_name(dev));
+ if (!pbi->monitor_wqueue) {
+ dev_err(dev, "%s(): wqueue init failed\n", __func__);
+ retval = -ESRCH;
+ goto wqueue_failed;
+ }
+
+ /* register interrupt */
+ retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
+ 0, DRIVER_NAME, pbi);
+ if (retval) {
+ dev_err(dev, "%s(): cannot get IRQ\n", __func__);
+ goto requestirq_failed;
+ }
+
+ /* register pmic-batt with power supply subsystem */
+ pbi->batt.name = "pmic-batt";
+ pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY;
+ pbi->batt.properties = pmic_battery_props;
+ pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props);
+ pbi->batt.get_property = pmic_battery_get_property;
+ retval = power_supply_register(dev, &pbi->batt);
+ if (retval) {
+ dev_err(dev,
+ "%s(): failed to register pmic battery device with power supply subsystem\n",
+ __func__);
+ goto power_reg_failed;
+ }
+
+ dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
+ "registration with power supply subsystem successful\n",
+ __func__);
+
+ queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
+
+ /* register pmic-usb with power supply subsystem */
+ pbi->usb.name = "pmic-usb";
+ pbi->usb.type = POWER_SUPPLY_TYPE_USB;
+ pbi->usb.properties = pmic_usb_props;
+ pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props);
+ pbi->usb.get_property = pmic_usb_get_property;
+ retval = power_supply_register(dev, &pbi->usb);
+ if (retval) {
+ dev_err(dev,
+ "%s(): failed to register pmic usb device with power supply subsystem\n",
+ __func__);
+ goto power_reg_failed_1;
+ }
+
+ if (debug)
+ printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
+ "registration with power supply subsystem successful\n",
+ __func__);
+
+ return retval;
+
+power_reg_failed_1:
+ power_supply_unregister(&pbi->batt);
+power_reg_failed:
+ cancel_rearming_delayed_workqueue(pbi->monitor_wqueue,
+ &pbi->monitor_battery);
+requestirq_failed:
+ destroy_workqueue(pbi->monitor_wqueue);
+wqueue_failed:
+ kfree(pbi);
+
+ return retval;
+}
+
+static int __devinit platform_pmic_battery_probe(struct platform_device *pdev)
+{
+ return probe(pdev->id, &pdev->dev);
+}
+
+/**
+ * pmic_battery_remove - pmic battery finalize
+ * @dev: pmic battery device structure
+ * Context: can sleep
+ *
+ * PMIC battery finalizes its internal data structue and other
+ * infrastructure components that it initialized in
+ * pmic_battery_probe.
+ */
+
+static int __devexit platform_pmic_battery_remove(struct platform_device *pdev)
+{
+ struct pmic_power_module_info *pbi = dev_get_drvdata(&pdev->dev);
+
+ free_irq(pbi->irq, pbi);
+ cancel_rearming_delayed_workqueue(pbi->monitor_wqueue,
+ &pbi->monitor_battery);
+ destroy_workqueue(pbi->monitor_wqueue);
+
+ power_supply_unregister(&pbi->usb);
+ power_supply_unregister(&pbi->batt);
+
+ flush_scheduled_work();
+ kfree(pbi);
+ return 0;
+}
+
+static struct platform_driver platform_pmic_battery_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = platform_pmic_battery_probe,
+ .remove = __devexit_p(platform_pmic_battery_remove),
+};
+
+static int __init platform_pmic_battery_module_init(void)
+{
+ return platform_driver_register(&platform_pmic_battery_driver);
+}
+
+static void __exit platform_pmic_battery_module_exit(void)
+{
+ platform_driver_unregister(&platform_pmic_battery_driver);
+}
+
+module_init(platform_pmic_battery_module_init);
+module_exit(platform_pmic_battery_module_exit);
+
+MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
+MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
index baefcf1..aafc1c5 100644
--- a/drivers/power/olpc_battery.c
+++ b/drivers/power/olpc_battery.c
@@ -1,7 +1,7 @@
/*
* Battery driver for One Laptop Per Child board.
*
- * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
+ * Copyright © 2006-2010 David Woodhouse <dwmw2@infradead.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
@@ -384,7 +384,6 @@ static struct bin_attribute olpc_bat_eeprom = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
- .owner = THIS_MODULE,
},
.size = 0,
.read = olpc_bat_eeprom_read,
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c
new file mode 100644
index 0000000..fe16b48
--- /dev/null
+++ b/drivers/power/s3c_adc_battery.c
@@ -0,0 +1,431 @@
+/*
+ * iPAQ h1930/h1940/rx1950 battery controler driver
+ * Copyright (c) Vasily Khoruzhick
+ * Based on h1940_battery.c by Arnaud Patard
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/s3c_adc_battery.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <plat/adc.h>
+
+#define BAT_POLL_INTERVAL 10000 /* ms */
+#define JITTER_DELAY 500 /* ms */
+
+struct s3c_adc_bat {
+ struct power_supply psy;
+ struct s3c_adc_client *client;
+ struct s3c_adc_bat_pdata *pdata;
+ int volt_value;
+ int cur_value;
+ unsigned int timestamp;
+ int level;
+ int status;
+ int cable_plugged:1;
+};
+
+static struct delayed_work bat_work;
+
+static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
+{
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+}
+
+static enum power_supply_property s3c_adc_backup_bat_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
+
+ if (!bat) {
+ dev_err(psy->dev, "%s: no battery infos ?!\n", __func__);
+ return -EINVAL;
+ }
+
+ if (bat->volt_value < 0 ||
+ jiffies_to_msecs(jiffies - bat->timestamp) >
+ BAT_POLL_INTERVAL) {
+ bat->volt_value = s3c_adc_read(bat->client,
+ bat->pdata->backup_volt_channel);
+ bat->volt_value *= bat->pdata->backup_volt_mult;
+ bat->timestamp = jiffies;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bat->volt_value;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ val->intval = bat->pdata->backup_volt_min;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = bat->pdata->backup_volt_max;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct s3c_adc_bat backup_bat = {
+ .psy = {
+ .name = "backup-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = s3c_adc_backup_bat_props,
+ .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
+ .get_property = s3c_adc_backup_bat_get_property,
+ .use_for_apm = 1,
+ },
+};
+
+static enum power_supply_property s3c_adc_main_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int calc_full_volt(int volt_val, int cur_val, int impedance)
+{
+ return volt_val + cur_val * impedance / 1000;
+}
+
+static int s3c_adc_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
+
+ int new_level;
+ int full_volt;
+ const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
+ unsigned int lut_size = bat->pdata->lut_noac_cnt;
+
+ if (!bat) {
+ dev_err(psy->dev, "no battery infos ?!\n");
+ return -EINVAL;
+ }
+
+ if (bat->volt_value < 0 || bat->cur_value < 0 ||
+ jiffies_to_msecs(jiffies - bat->timestamp) >
+ BAT_POLL_INTERVAL) {
+ bat->volt_value = s3c_adc_read(bat->client,
+ bat->pdata->volt_channel) * bat->pdata->volt_mult;
+ bat->cur_value = s3c_adc_read(bat->client,
+ bat->pdata->current_channel) * bat->pdata->current_mult;
+ bat->timestamp = jiffies;
+ }
+
+ if (bat->cable_plugged &&
+ ((bat->pdata->gpio_charge_finished < 0) ||
+ !gpio_get_value(bat->pdata->gpio_charge_finished))) {
+ lut = bat->pdata->lut_acin;
+ lut_size = bat->pdata->lut_acin_cnt;
+ }
+
+ new_level = 100000;
+ full_volt = calc_full_volt((bat->volt_value / 1000),
+ (bat->cur_value / 1000), bat->pdata->internal_impedance);
+
+ if (full_volt < calc_full_volt(lut->volt, lut->cur,
+ bat->pdata->internal_impedance)) {
+ lut_size--;
+ while (lut_size--) {
+ int lut_volt1;
+ int lut_volt2;
+
+ lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
+ bat->pdata->internal_impedance);
+ lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
+ bat->pdata->internal_impedance);
+ if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
+ new_level = (lut[1].level +
+ (lut[0].level - lut[1].level) *
+ (full_volt - lut_volt2) /
+ (lut_volt1 - lut_volt2)) * 1000;
+ break;
+ }
+ new_level = lut[1].level * 1000;
+ lut++;
+ }
+ }
+
+ bat->level = new_level;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (bat->pdata->gpio_charge_finished < 0)
+ val->intval = bat->level == 100000 ?
+ POWER_SUPPLY_STATUS_FULL : bat->status;
+ else
+ val->intval = bat->status;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = 100000;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+ val->intval = 0;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = bat->level;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bat->volt_value;
+ return 0;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = bat->cur_value;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct s3c_adc_bat main_bat = {
+ .psy = {
+ .name = "main-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = s3c_adc_main_bat_props,
+ .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props),
+ .get_property = s3c_adc_bat_get_property,
+ .external_power_changed = s3c_adc_bat_ext_power_changed,
+ .use_for_apm = 1,
+ },
+};
+
+static void s3c_adc_bat_work(struct work_struct *work)
+{
+ struct s3c_adc_bat *bat = &main_bat;
+ int is_charged;
+ int is_plugged;
+ static int was_plugged;
+
+ is_plugged = power_supply_am_i_supplied(&bat->psy);
+ bat->cable_plugged = is_plugged;
+ if (is_plugged != was_plugged) {
+ was_plugged = is_plugged;
+ if (is_plugged) {
+ if (bat->pdata->enable_charger)
+ bat->pdata->enable_charger();
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ if (bat->pdata->disable_charger)
+ bat->pdata->disable_charger();
+ bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ } else {
+ if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
+ is_charged = gpio_get_value(
+ main_bat.pdata->gpio_charge_finished);
+ if (is_charged) {
+ if (bat->pdata->disable_charger)
+ bat->pdata->disable_charger();
+ bat->status = POWER_SUPPLY_STATUS_FULL;
+ } else {
+ if (bat->pdata->enable_charger)
+ bat->pdata->enable_charger();
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ }
+ }
+
+ power_supply_changed(&bat->psy);
+}
+
+static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
+{
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+ return IRQ_HANDLED;
+}
+
+static int __init s3c_adc_bat_probe(struct platform_device *pdev)
+{
+ struct s3c_adc_client *client;
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+ int ret;
+
+ client = s3c_adc_register(pdev, NULL, NULL, 0);
+ if (IS_ERR(client)) {
+ dev_err(&pdev->dev, "cannot register adc\n");
+ return PTR_ERR(client);
+ }
+
+ platform_set_drvdata(pdev, client);
+
+ main_bat.client = client;
+ main_bat.pdata = pdata;
+ main_bat.volt_value = -1;
+ main_bat.cur_value = -1;
+ main_bat.cable_plugged = 0;
+ main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ ret = power_supply_register(&pdev->dev, &main_bat.psy);
+ if (ret)
+ goto err_reg_main;
+ if (pdata->backup_volt_mult) {
+ backup_bat.client = client;
+ backup_bat.pdata = pdev->dev.platform_data;
+ backup_bat.volt_value = -1;
+ ret = power_supply_register(&pdev->dev, &backup_bat.psy);
+ if (ret)
+ goto err_reg_backup;
+ }
+
+ INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
+
+ if (pdata->gpio_charge_finished >= 0) {
+ ret = gpio_request(pdata->gpio_charge_finished, "charged");
+ if (ret)
+ goto err_gpio;
+
+ ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
+ s3c_adc_bat_charged,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "battery charged", NULL);
+ if (ret)
+ goto err_irq;
+ }
+
+ if (pdata->init) {
+ ret = pdata->init();
+ if (ret)
+ goto err_platform;
+ }
+
+ dev_info(&pdev->dev, "successfully loaded\n");
+ device_init_wakeup(&pdev->dev, 1);
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+
+ return 0;
+
+err_platform:
+ if (pdata->gpio_charge_finished >= 0)
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+err_irq:
+ if (pdata->gpio_charge_finished >= 0)
+ gpio_free(pdata->gpio_charge_finished);
+err_gpio:
+ if (pdata->backup_volt_mult)
+ power_supply_unregister(&backup_bat.psy);
+err_reg_backup:
+ power_supply_unregister(&main_bat.psy);
+err_reg_main:
+ return ret;
+}
+
+static int s3c_adc_bat_remove(struct platform_device *pdev)
+{
+ struct s3c_adc_client *client = platform_get_drvdata(pdev);
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ power_supply_unregister(&main_bat.psy);
+ if (pdata->backup_volt_mult)
+ power_supply_unregister(&backup_bat.psy);
+
+ s3c_adc_release(client);
+
+ if (pdata->gpio_charge_finished >= 0) {
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+ gpio_free(pdata->gpio_charge_finished);
+ }
+
+ cancel_delayed_work(&bat_work);
+
+ if (pdata->exit)
+ pdata->exit();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_adc_bat_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_charge_finished >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(
+ gpio_to_irq(pdata->gpio_charge_finished));
+ else {
+ disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+ main_bat.pdata->disable_charger();
+ }
+ }
+
+ return 0;
+}
+
+static int s3c_adc_bat_resume(struct platform_device *pdev)
+{
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_charge_finished >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(
+ gpio_to_irq(pdata->gpio_charge_finished));
+ else
+ enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+ }
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+
+ return 0;
+}
+#else
+#define s3c_adc_battery_suspend NULL
+#define s3c_adc_battery_resume NULL
+#endif
+
+static struct platform_driver s3c_adc_bat_driver = {
+ .driver = {
+ .name = "s3c-adc-battery",
+ },
+ .probe = s3c_adc_bat_probe,
+ .remove = s3c_adc_bat_remove,
+ .suspend = s3c_adc_bat_suspend,
+ .resume = s3c_adc_bat_resume,
+};
+
+static int __init s3c_adc_bat_init(void)
+{
+ return platform_driver_register(&s3c_adc_bat_driver);
+}
+module_init(s3c_adc_bat_init);
+
+static void __exit s3c_adc_bat_exit(void)
+{
+ platform_driver_unregister(&s3c_adc_bat_driver);
+}
+module_exit(s3c_adc_bat_exit);
+
+MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
+MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c
index 4e8afce..5071d85 100644
--- a/drivers/power/wm97xx_battery.c
+++ b/drivers/power/wm97xx_battery.c
@@ -29,7 +29,6 @@ static DEFINE_MUTEX(bat_lock);
static struct work_struct bat_work;
static struct mutex work_lock;
static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
-static struct wm97xx_batt_info *gpdata;
static enum power_supply_property *prop;
static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
@@ -172,12 +171,6 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
struct wm97xx_pdata *wmdata = dev->dev.platform_data;
struct wm97xx_batt_pdata *pdata;
- if (gpdata) {
- dev_err(&dev->dev, "Do not pass platform_data through "
- "wm97xx_bat_set_pdata!\n");
- return -EINVAL;
- }
-
if (!wmdata) {
dev_err(&dev->dev, "No platform data supplied\n");
return -EINVAL;
@@ -308,15 +301,6 @@ static void __exit wm97xx_bat_exit(void)
platform_driver_unregister(&wm97xx_bat_driver);
}
-/* The interface is deprecated, as well as linux/wm97xx_batt.h */
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data);
-
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data)
-{
- gpdata = data;
-}
-EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata);
-
module_init(wm97xx_bat_init);
module_exit(wm97xx_bat_exit);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 04f2e08..172951b 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -100,6 +100,14 @@ config REGULATOR_MAX8925
help
Say y here to support the voltage regulaltor of Maxim MAX8925 PMIC.
+config REGULATOR_MAX8998
+ tristate "Maxim 8998 voltage regulator"
+ depends on MFD_MAX8998
+ help
+ This driver controls a Maxim 8998 voltage output regulator
+ via I2C bus. The provided regulator is suitable for S3C6410
+ and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
+
config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
depends on TWL4030_CORE
@@ -201,5 +209,31 @@ config REGULATOR_88PM8607
help
This driver supports 88PM8607 voltage regulator chips.
+config REGULATOR_ISL6271A
+ tristate "Intersil ISL6271A Power regulator"
+ depends on I2C
+ help
+ This driver supports ISL6271A voltage regulator chip.
+
+config REGULATOR_AD5398
+ tristate "Analog Devices AD5398/AD5821 regulators"
+ depends on I2C
+ help
+ This driver supports AD5398 and AD5821 current regulator chips.
+ If building into module, its name is ad5398.ko.
+
+config REGULATOR_AB8500
+ bool "ST-Ericsson AB8500 Power Regulators"
+ depends on AB8500_CORE
+ help
+ This driver supports the regulators found on the ST-Ericsson mixed
+ signal AB8500 PMIC
+
+config REGULATOR_TPS6586X
+ tristate "TI TPS6586X Power regulators"
+ depends on MFD_TPS6586X
+ help
+ This driver supports TPS6586X voltage regulator chips.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4e7feec..8285fd8 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
@@ -16,12 +17,14 @@ obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
@@ -31,5 +34,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
+obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
+obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
new file mode 100644
index 0000000..dc3f1a4
--- /dev/null
+++ b/drivers/regulator/ab8500.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 peripheral regulators
+ *
+ * AB8500 supports the following regulators,
+ * LDOs - VAUDIO, VANAMIC2/2, VDIGMIC, VINTCORE12, VTVOUT,
+ * VAUX1/2/3, VANA
+ *
+ * for DB8500 cut 1.0 and previous versions of the silicon, all accesses
+ * to registers are through the DB8500 SPI. In cut 1.1 onwards, these
+ * accesses are through the DB8500 PRCMU I2C
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500.h>
+
+/**
+ * struct ab8500_regulator_info - ab8500 regulator information
+ * @desc: regulator description
+ * @ab8500: ab8500 parent
+ * @regulator_dev: regulator device
+ * @max_uV: maximum voltage (for variable voltage supplies)
+ * @min_uV: minimum voltage (for variable voltage supplies)
+ * @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @update_reg: register to control on/off
+ * @mask: mask to enable/disable regulator
+ * @enable: bits to enable the regulator in normal(high power) mode
+ * @voltage_reg: register to control regulator voltage
+ * @voltage_mask: mask to control regulator voltage
+ * @supported_voltages: supported voltage table
+ * @voltages_len: number of supported voltages for the regulator
+ */
+struct ab8500_regulator_info {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct ab8500 *ab8500;
+ struct regulator_dev *regulator;
+ int max_uV;
+ int min_uV;
+ int fixed_uV;
+ int update_reg;
+ int mask;
+ int enable;
+ int voltage_reg;
+ int voltage_mask;
+ int const *supported_voltages;
+ int voltages_len;
+};
+
+/* voltage tables for the vauxn/vintcore supplies */
+static const int ldo_vauxn_voltages[] = {
+ 1100000,
+ 1200000,
+ 1300000,
+ 1400000,
+ 1500000,
+ 1800000,
+ 1850000,
+ 1900000,
+ 2500000,
+ 2650000,
+ 2700000,
+ 2750000,
+ 2800000,
+ 2900000,
+ 3000000,
+ 3300000,
+};
+
+static const int ldo_vintcore_voltages[] = {
+ 1200000,
+ 1225000,
+ 1250000,
+ 1275000,
+ 1300000,
+ 1325000,
+ 1350000,
+};
+
+static int ab8500_regulator_enable(struct regulator_dev *rdev)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_set_bits(info->ab8500, info->update_reg,
+ info->mask, info->enable);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set enable bits for regulator\n");
+ return ret;
+}
+
+static int ab8500_regulator_disable(struct regulator_dev *rdev)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_set_bits(info->ab8500, info->update_reg,
+ info->mask, 0x0);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set disable bits for regulator\n");
+ return ret;
+}
+
+static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_read(info->ab8500, info->update_reg);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read 0x%x register\n", info->update_reg);
+ return ret;
+ }
+
+ if (ret & info->mask)
+ return true;
+ else
+ return false;
+}
+
+static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ int regulator_id;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ /* return the uV for the fixed regulators */
+ if (info->fixed_uV)
+ return info->fixed_uV;
+
+ if (selector > info->voltages_len)
+ return -EINVAL;
+
+ return info->supported_voltages[selector];
+}
+
+static int ab8500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ int regulator_id, ret, val;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_read(info->ab8500, info->voltage_reg);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read voltage reg for regulator\n");
+ return ret;
+ }
+
+ /* vintcore has a different layout */
+ val = ret & info->voltage_mask;
+ if (regulator_id == AB8500_LDO_INTCORE)
+ ret = info->supported_voltages[val >> 0x3];
+ else
+ ret = info->supported_voltages[val];
+
+ return ret;
+}
+
+static int ab8500_get_best_voltage_index(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* check the supported voltage */
+ for (i = 0; i < info->voltages_len; i++) {
+ if ((info->supported_voltages[i] >= min_uV) &&
+ (info->supported_voltages[i] <= max_uV))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ /* get the appropriate voltages within the range */
+ ret = ab8500_get_best_voltage_index(rdev, min_uV, max_uV);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't get best voltage for regulator\n");
+ return ret;
+ }
+
+ /* set the registers for the request */
+ ret = ab8500_set_bits(info->ab8500, info->voltage_reg,
+ info->voltage_mask, ret);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set voltage reg for regulator\n");
+
+ return ret;
+}
+
+static struct regulator_ops ab8500_regulator_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_regulator_get_voltage,
+ .set_voltage = ab8500_regulator_set_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ int regulator_id;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ return info->fixed_uV;
+}
+
+static struct regulator_ops ab8500_ldo_fixed_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+#define AB8500_LDO(_id, min, max, reg, reg_mask, reg_enable, \
+ volt_reg, volt_mask, voltages, \
+ len_volts) \
+{ \
+ .desc = { \
+ .name = "LDO-" #_id, \
+ .ops = &ab8500_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AB8500_LDO_##_id, \
+ .owner = THIS_MODULE, \
+ }, \
+ .min_uV = (min) * 1000, \
+ .max_uV = (max) * 1000, \
+ .update_reg = reg, \
+ .mask = reg_mask, \
+ .enable = reg_enable, \
+ .voltage_reg = volt_reg, \
+ .voltage_mask = volt_mask, \
+ .supported_voltages = voltages, \
+ .voltages_len = len_volts, \
+ .fixed_uV = 0, \
+}
+
+#define AB8500_FIXED_LDO(_id, fixed, reg, reg_mask, \
+ reg_enable) \
+{ \
+ .desc = { \
+ .name = "LDO-" #_id, \
+ .ops = &ab8500_ldo_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AB8500_LDO_##_id, \
+ .owner = THIS_MODULE, \
+ }, \
+ .fixed_uV = fixed * 1000, \
+ .update_reg = reg, \
+ .mask = reg_mask, \
+ .enable = reg_enable, \
+}
+
+static struct ab8500_regulator_info ab8500_regulator_info[] = {
+ /*
+ * Variable Voltage LDOs
+ * name, min uV, max uV, ctrl reg, reg mask, enable mask,
+ * volt ctrl reg, volt ctrl mask, volt table, num supported volts
+ */
+ AB8500_LDO(AUX1, 1100, 3300, 0x0409, 0x3, 0x1, 0x041f, 0xf,
+ ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
+ AB8500_LDO(AUX2, 1100, 3300, 0x0409, 0xc, 0x4, 0x0420, 0xf,
+ ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
+ AB8500_LDO(AUX3, 1100, 3300, 0x040a, 0x3, 0x1, 0x0421, 0xf,
+ ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
+ AB8500_LDO(INTCORE, 1100, 3300, 0x0380, 0x4, 0x4, 0x0380, 0x38,
+ ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)),
+
+ /*
+ * Fixed Voltage LDOs
+ * name, o/p uV, ctrl reg, enable, disable
+ */
+ AB8500_FIXED_LDO(TVOUT, 2000, 0x0380, 0x2, 0x2),
+ AB8500_FIXED_LDO(AUDIO, 2000, 0x0383, 0x2, 0x2),
+ AB8500_FIXED_LDO(ANAMIC1, 2050, 0x0383, 0x4, 0x4),
+ AB8500_FIXED_LDO(ANAMIC2, 2050, 0x0383, 0x8, 0x8),
+ AB8500_FIXED_LDO(DMIC, 1800, 0x0383, 0x10, 0x10),
+ AB8500_FIXED_LDO(ANA, 1200, 0x0383, 0xc, 0x4),
+};
+
+static inline struct ab8500_regulator_info *find_regulator_info(int id)
+{
+ struct ab8500_regulator_info *info;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ info = &ab8500_regulator_info[i];
+ if (info->desc.id == id)
+ return info;
+ }
+ return NULL;
+}
+
+static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev);
+ int i, err;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ struct ab8500_regulator_info *info = NULL;
+
+ /* assign per-regulator data */
+ info = &ab8500_regulator_info[i];
+ info->dev = &pdev->dev;
+ info->ab8500 = ab8500;
+
+ info->regulator = regulator_register(&info->desc, &pdev->dev,
+ pdata->regulator[i], info);
+ if (IS_ERR(info->regulator)) {
+ err = PTR_ERR(info->regulator);
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ /* when we fail, un-register all earlier regulators */
+ i--;
+ while (i > 0) {
+ info = &ab8500_regulator_info[i];
+ regulator_unregister(info->regulator);
+ i--;
+ }
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ struct ab8500_regulator_info *info = NULL;
+ info = &ab8500_regulator_info[i];
+ regulator_unregister(info->regulator);
+ }
+
+ return 0;
+}
+
+static struct platform_driver ab8500_regulator_driver = {
+ .probe = ab8500_regulator_probe,
+ .remove = __devexit_p(ab8500_regulator_remove),
+ .driver = {
+ .name = "ab8500-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_regulator_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ab8500_regulator_driver);
+ if (ret != 0)
+ pr_err("Failed to register ab8500 regulator: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(ab8500_regulator_init);
+
+static void __exit ab8500_regulator_exit(void)
+{
+ platform_driver_unregister(&ab8500_regulator_driver);
+}
+module_exit(ab8500_regulator_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
+MODULE_ALIAS("platform:ab8500-regulator");
diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c
new file mode 100644
index 0000000..d59d2f2
--- /dev/null
+++ b/drivers/regulator/ad5398.c
@@ -0,0 +1,288 @@
+/*
+ * Voltage and current regulation for AD5398 and AD5821
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#define AD5398_CURRENT_EN_MASK 0x8000
+
+struct ad5398_chip_info {
+ struct i2c_client *client;
+ int min_uA;
+ int max_uA;
+ unsigned int current_level;
+ unsigned int current_mask;
+ unsigned int current_offset;
+ struct regulator_dev rdev;
+};
+
+static int ad5398_calc_current(struct ad5398_chip_info *chip,
+ unsigned selector)
+{
+ unsigned range_uA = chip->max_uA - chip->min_uA;
+
+ return chip->min_uA + (selector * range_uA / chip->current_level);
+}
+
+static int ad5398_read_reg(struct i2c_client *client, unsigned short *data)
+{
+ unsigned short val;
+ int ret;
+
+ ret = i2c_master_recv(client, (char *)&val, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C read error\n");
+ return ret;
+ }
+ *data = be16_to_cpu(val);
+
+ return ret;
+}
+
+static int ad5398_write_reg(struct i2c_client *client, const unsigned short data)
+{
+ unsigned short val;
+ int ret;
+
+ val = cpu_to_be16(data);
+ ret = i2c_master_send(client, (char *)&val, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "I2C write error\n");
+
+ return ret;
+}
+
+static int ad5398_get_current_limit(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ ret = (data & chip->current_mask) >> chip->current_offset;
+
+ return ad5398_calc_current(chip, ret);
+}
+
+static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned range_uA = chip->max_uA - chip->min_uA;
+ unsigned selector;
+ unsigned short data;
+ int ret;
+
+ if (min_uA > chip->max_uA || min_uA < chip->min_uA)
+ return -EINVAL;
+ if (max_uA > chip->max_uA || max_uA < chip->min_uA)
+ return -EINVAL;
+
+ selector = ((min_uA - chip->min_uA) * chip->current_level +
+ range_uA - 1) / range_uA;
+ if (ad5398_calc_current(chip, selector) > max_uA)
+ return -EINVAL;
+
+ dev_dbg(&client->dev, "changing current %dmA\n",
+ ad5398_calc_current(chip, selector) / 1000);
+
+ /* read chip enable bit */
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ /* prepare register data */
+ selector = (selector << chip->current_offset) & chip->current_mask;
+ data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK);
+
+ /* write the new current value back as well as enable bit */
+ ret = ad5398_write_reg(client, data);
+
+ return ret;
+}
+
+static int ad5398_is_enabled(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ if (data & AD5398_CURRENT_EN_MASK)
+ return 1;
+ else
+ return 0;
+}
+
+static int ad5398_enable(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ if (data & AD5398_CURRENT_EN_MASK)
+ return 0;
+
+ data |= AD5398_CURRENT_EN_MASK;
+
+ ret = ad5398_write_reg(client, data);
+
+ return ret;
+}
+
+static int ad5398_disable(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ if (!(data & AD5398_CURRENT_EN_MASK))
+ return 0;
+
+ data &= ~AD5398_CURRENT_EN_MASK;
+
+ ret = ad5398_write_reg(client, data);
+
+ return ret;
+}
+
+static struct regulator_ops ad5398_ops = {
+ .get_current_limit = ad5398_get_current_limit,
+ .set_current_limit = ad5398_set_current_limit,
+ .enable = ad5398_enable,
+ .disable = ad5398_disable,
+ .is_enabled = ad5398_is_enabled,
+};
+
+static struct regulator_desc ad5398_reg = {
+ .name = "isink",
+ .id = 0,
+ .ops = &ad5398_ops,
+ .type = REGULATOR_CURRENT,
+ .owner = THIS_MODULE,
+};
+
+struct ad5398_current_data_format {
+ int current_bits;
+ int current_offset;
+ int min_uA;
+ int max_uA;
+};
+
+static const struct ad5398_current_data_format df_10_4_120 = {10, 4, 0, 120000};
+
+static const struct i2c_device_id ad5398_id[] = {
+ { "ad5398", (kernel_ulong_t)&df_10_4_120 },
+ { "ad5821", (kernel_ulong_t)&df_10_4_120 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad5398_id);
+
+static int __devinit ad5398_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init_data = client->dev.platform_data;
+ struct ad5398_chip_info *chip;
+ const struct ad5398_current_data_format *df =
+ (struct ad5398_current_data_format *)id->driver_data;
+ int ret;
+
+ if (!init_data)
+ return -EINVAL;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+
+ chip->min_uA = df->min_uA;
+ chip->max_uA = df->max_uA;
+ chip->current_level = 1 << df->current_bits;
+ chip->current_offset = df->current_offset;
+ chip->current_mask = (chip->current_level - 1) << chip->current_offset;
+
+ rdev = regulator_register(&ad5398_reg, &client->dev, init_data, chip);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&client->dev, "failed to register %s %s\n",
+ id->name, ad5398_reg.name);
+ goto err;
+ }
+
+ i2c_set_clientdata(client, chip);
+ dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name);
+ return 0;
+
+err:
+ kfree(chip);
+ return ret;
+}
+
+static int __devexit ad5398_remove(struct i2c_client *client)
+{
+ struct ad5398_chip_info *chip = i2c_get_clientdata(client);
+
+ regulator_unregister(&chip->rdev);
+ kfree(chip);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+static struct i2c_driver ad5398_driver = {
+ .probe = ad5398_probe,
+ .remove = __devexit_p(ad5398_remove),
+ .driver = {
+ .name = "ad5398",
+ },
+ .id_table = ad5398_id,
+};
+
+static int __init ad5398_init(void)
+{
+ return i2c_add_driver(&ad5398_driver);
+}
+subsys_initcall(ad5398_init);
+
+static void __exit ad5398_exit(void)
+{
+ i2c_del_driver(&ad5398_driver);
+}
+module_exit(ad5398_exit);
+
+MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver");
+MODULE_AUTHOR("Sonic Zhang");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:ad5398-regulator");
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
new file mode 100644
index 0000000..e49d2bd
--- /dev/null
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -0,0 +1,236 @@
+/*
+ * isl6271a-regulator.c
+ *
+ * Support for Intersil ISL6271A voltage regulator
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#define ISL6271A_VOLTAGE_MIN 850000
+#define ISL6271A_VOLTAGE_MAX 1600000
+#define ISL6271A_VOLTAGE_STEP 50000
+
+/* PMIC details */
+struct isl_pmic {
+ struct i2c_client *client;
+ struct regulator_dev *rdev[3];
+ struct mutex mtx;
+};
+
+static int isl6271a_get_voltage(struct regulator_dev *dev)
+{
+ struct isl_pmic *pmic = rdev_get_drvdata(dev);
+ int idx, data;
+
+ mutex_lock(&pmic->mtx);
+
+ idx = i2c_smbus_read_byte(pmic->client);
+ if (idx < 0) {
+ dev_err(&pmic->client->dev, "Error getting voltage\n");
+ data = idx;
+ goto out;
+ }
+
+ /* Convert the data from chip to microvolts */
+ data = ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * (idx & 0xf));
+
+out:
+ mutex_unlock(&pmic->mtx);
+ return data;
+}
+
+static int isl6271a_set_voltage(struct regulator_dev *dev, int minuV, int maxuV)
+{
+ struct isl_pmic *pmic = rdev_get_drvdata(dev);
+ int vsel, err, data;
+
+ if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX)
+ return -EINVAL;
+ if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX)
+ return -EINVAL;
+
+ /* Align to 50000 mV */
+ vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP);
+
+ /* If the result fell out of [minuV,maxuV] range, put it back */
+ if (vsel < minuV)
+ vsel += ISL6271A_VOLTAGE_STEP;
+
+ /* Convert the microvolts to data for the chip */
+ data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP;
+
+ mutex_lock(&pmic->mtx);
+
+ err = i2c_smbus_write_byte(pmic->client, data);
+ if (err < 0)
+ dev_err(&pmic->client->dev, "Error setting voltage\n");
+
+ mutex_unlock(&pmic->mtx);
+ return err;
+}
+
+static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector);
+}
+
+static struct regulator_ops isl_core_ops = {
+ .get_voltage = isl6271a_get_voltage,
+ .set_voltage = isl6271a_set_voltage,
+ .list_voltage = isl6271a_list_voltage,
+};
+
+static int isl6271a_get_fixed_voltage(struct regulator_dev *dev)
+{
+ int id = rdev_get_id(dev);
+ return (id == 1) ? 1100000 : 1300000;
+}
+
+static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ int id = rdev_get_id(dev);
+ return (id == 1) ? 1100000 : 1300000;
+}
+
+static struct regulator_ops isl_fixed_ops = {
+ .get_voltage = isl6271a_get_fixed_voltage,
+ .list_voltage = isl6271a_list_fixed_voltage,
+};
+
+static struct regulator_desc isl_rd[] = {
+ {
+ .name = "Core Buck",
+ .id = 0,
+ .n_voltages = 16,
+ .ops = &isl_core_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO1",
+ .id = 1,
+ .n_voltages = 1,
+ .ops = &isl_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO2",
+ .id = 2,
+ .n_voltages = 1,
+ .ops = &isl_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit isl6271a_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = i2c->dev.platform_data;
+ struct isl_pmic *pmic;
+ int err, i;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if (!init_data) {
+ dev_err(&i2c->dev, "no platform data supplied\n");
+ return -EIO;
+ }
+
+ pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ pmic->client = i2c;
+
+ mutex_init(&pmic->mtx);
+
+ for (i = 0; i < 3; i++) {
+ pmic->rdev[i] = regulator_register(&isl_rd[0], &i2c->dev,
+ init_data, pmic);
+ if (IS_ERR(pmic->rdev[i])) {
+ dev_err(&i2c->dev, "failed to register %s\n", id->name);
+ err = PTR_ERR(pmic->rdev);
+ goto error;
+ }
+ }
+
+ i2c_set_clientdata(i2c, pmic);
+
+ return 0;
+
+error:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+ return err;
+}
+
+static int __devexit isl6271a_remove(struct i2c_client *i2c)
+{
+ struct isl_pmic *pmic = i2c_get_clientdata(i2c);
+ int i;
+
+ i2c_set_clientdata(i2c, NULL);
+
+ for (i = 0; i < 3; i++)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl6271a_id[] = {
+ {.name = "isl6271a", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, isl6271a_id);
+
+static struct i2c_driver isl6271a_i2c_driver = {
+ .driver = {
+ .name = "isl6271a",
+ .owner = THIS_MODULE,
+ },
+ .probe = isl6271a_probe,
+ .remove = __devexit_p(isl6271a_remove),
+ .id_table = isl6271a_id,
+};
+
+static int __init isl6271a_init(void)
+{
+ return i2c_add_driver(&isl6271a_i2c_driver);
+}
+
+static void __exit isl6271a_cleanup(void)
+{
+ i2c_del_driver(&isl6271a_i2c_driver);
+}
+
+subsys_initcall(isl6271a_init);
+module_exit(isl6271a_cleanup);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c
index 8ae3732..3bb82b6 100644
--- a/drivers/regulator/lp3971.c
+++ b/drivers/regulator/lp3971.c
@@ -377,7 +377,7 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count,
if (count != 1)
return -EIO;
ret = i2c_smbus_read_byte_data(i2c, reg);
- if (ret < 0 || count != 1)
+ if (ret < 0)
return -EIO;
*dest = ret;
@@ -387,15 +387,9 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count,
static int lp3971_i2c_write(struct i2c_client *i2c, char reg, int count,
const u16 *src)
{
- int ret;
-
if (count != 1)
return -EIO;
- ret = i2c_smbus_write_byte_data(i2c, reg, *src);
- if (ret >= 0)
- return 0;
-
- return ret;
+ return i2c_smbus_write_byte_data(i2c, reg, *src);
}
static u8 lp3971_reg_read(struct lp3971 *lp3971, u8 reg)
diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c
index 2b54d9d..8867c27 100644
--- a/drivers/regulator/max1586.c
+++ b/drivers/regulator/max1586.c
@@ -223,7 +223,7 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, rdev);
+ i2c_set_clientdata(client, max1586);
dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n");
return 0;
@@ -238,13 +238,13 @@ out:
static int __devexit max1586_pmic_remove(struct i2c_client *client)
{
- struct regulator_dev **rdev = i2c_get_clientdata(client);
+ struct max1586_data *max1586 = i2c_get_clientdata(client);
int i;
for (i = 0; i <= MAX1586_V6; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
- kfree(rdev);
+ if (max1586->rdev[i])
+ regulator_unregister(max1586->rdev[i]);
+ kfree(max1586);
return 0;
}
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
index d97220e..c570e6e 100644
--- a/drivers/regulator/max8660.c
+++ b/drivers/regulator/max8660.c
@@ -450,7 +450,7 @@ static int __devinit max8660_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, rdev);
+ i2c_set_clientdata(client, max8660);
dev_info(&client->dev, "Maxim 8660/8661 regulator driver loaded\n");
return 0;
@@ -465,13 +465,13 @@ out:
static int __devexit max8660_remove(struct i2c_client *client)
{
- struct regulator_dev **rdev = i2c_get_clientdata(client);
+ struct max8660 *max8660 = i2c_get_clientdata(client);
int i;
for (i = 0; i < MAX8660_V_END; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
- kfree(rdev);
+ if (max8660->rdev[i])
+ regulator_unregister(max8660->rdev[i]);
+ kfree(max8660);
return 0;
}
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
new file mode 100644
index 0000000..ab67298
--- /dev/null
+++ b/drivers/regulator/max8998.c
@@ -0,0 +1,635 @@
+/*
+ * max8998.c - Voltage regulator driver for the Maxim 8998
+ *
+ * Copyright (C) 2009-2010 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_data {
+ struct device *dev;
+ struct max8998_dev *iodev;
+ int num_regulators;
+ struct regulator_dev **rdev;
+};
+
+struct voltage_map_desc {
+ int min;
+ int max;
+ int step;
+};
+
+/* Voltage maps */
+static const struct voltage_map_desc ldo23_voltage_map_desc = {
+ .min = 800, .step = 50, .max = 1300,
+};
+static const struct voltage_map_desc ldo456711_voltage_map_desc = {
+ .min = 1600, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc ldo8_voltage_map_desc = {
+ .min = 3000, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc ldo9_voltage_map_desc = {
+ .min = 2800, .step = 100, .max = 3100,
+};
+static const struct voltage_map_desc ldo10_voltage_map_desc = {
+ .min = 950, .step = 50, .max = 1300,
+};
+static const struct voltage_map_desc ldo1213_voltage_map_desc = {
+ .min = 800, .step = 100, .max = 3300,
+};
+static const struct voltage_map_desc ldo1415_voltage_map_desc = {
+ .min = 1200, .step = 100, .max = 3300,
+};
+static const struct voltage_map_desc ldo1617_voltage_map_desc = {
+ .min = 1600, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc buck12_voltage_map_desc = {
+ .min = 750, .step = 25, .max = 1525,
+};
+static const struct voltage_map_desc buck3_voltage_map_desc = {
+ .min = 1600, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc buck4_voltage_map_desc = {
+ .min = 800, .step = 100, .max = 2300,
+};
+
+static const struct voltage_map_desc *ldo_voltage_map[] = {
+ NULL,
+ NULL,
+ &ldo23_voltage_map_desc, /* LDO2 */
+ &ldo23_voltage_map_desc, /* LDO3 */
+ &ldo456711_voltage_map_desc, /* LDO4 */
+ &ldo456711_voltage_map_desc, /* LDO5 */
+ &ldo456711_voltage_map_desc, /* LDO6 */
+ &ldo456711_voltage_map_desc, /* LDO7 */
+ &ldo8_voltage_map_desc, /* LDO8 */
+ &ldo9_voltage_map_desc, /* LDO9 */
+ &ldo10_voltage_map_desc, /* LDO10 */
+ &ldo456711_voltage_map_desc, /* LDO11 */
+ &ldo1213_voltage_map_desc, /* LDO12 */
+ &ldo1213_voltage_map_desc, /* LDO13 */
+ &ldo1415_voltage_map_desc, /* LDO14 */
+ &ldo1415_voltage_map_desc, /* LDO15 */
+ &ldo1617_voltage_map_desc, /* LDO16 */
+ &ldo1617_voltage_map_desc, /* LDO17 */
+ &buck12_voltage_map_desc, /* BUCK1 */
+ &buck12_voltage_map_desc, /* BUCK2 */
+ &buck3_voltage_map_desc, /* BUCK3 */
+ &buck4_voltage_map_desc, /* BUCK4 */
+};
+
+static inline int max8998_get_ldo(struct regulator_dev *rdev)
+{
+ return rdev_get_id(rdev);
+}
+
+static int max8998_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ const struct voltage_map_desc *desc;
+ int ldo = max8998_get_ldo(rdev);
+ int val;
+
+ if (ldo >= ARRAY_SIZE(ldo_voltage_map))
+ return -EINVAL;
+
+ desc = ldo_voltage_map[ldo];
+ if (desc == NULL)
+ return -EINVAL;
+
+ val = desc->min + desc->step * selector;
+ if (val > desc->max)
+ return -EINVAL;
+
+ return val * 1000;
+}
+
+static int max8998_get_enable_register(struct regulator_dev *rdev,
+ int *reg, int *shift)
+{
+ int ldo = max8998_get_ldo(rdev);
+
+ switch (ldo) {
+ case MAX8998_LDO2 ... MAX8998_LDO5:
+ *reg = MAX8998_REG_ONOFF1;
+ *shift = 3 - (ldo - MAX8998_LDO2);
+ break;
+ case MAX8998_LDO6 ... MAX8998_LDO13:
+ *reg = MAX8998_REG_ONOFF2;
+ *shift = 7 - (ldo - MAX8998_LDO6);
+ break;
+ case MAX8998_LDO14 ... MAX8998_LDO17:
+ *reg = MAX8998_REG_ONOFF3;
+ *shift = 7 - (ldo - MAX8998_LDO14);
+ break;
+ case MAX8998_BUCK1 ... MAX8998_BUCK4:
+ *reg = MAX8998_REG_ONOFF1;
+ *shift = 7 - (ldo - MAX8998_BUCK1);
+ break;
+ case MAX8998_EN32KHZ_AP ... MAX8998_ENVICHG:
+ *reg = MAX8998_REG_ONOFF4;
+ *shift = 7 - (ldo - MAX8998_EN32KHZ_AP);
+ break;
+ case MAX8998_ESAFEOUT1 ... MAX8998_ESAFEOUT2:
+ *reg = MAX8998_REG_CHGR2;
+ *shift = 7 - (ldo - MAX8998_ESAFEOUT1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int ret, reg, shift = 8;
+ u8 val;
+
+ ret = max8998_get_enable_register(rdev, &reg, &shift);
+ if (ret)
+ return ret;
+
+ ret = max8998_read_reg(max8998->iodev, reg, &val);
+ if (ret)
+ return ret;
+
+ return val & (1 << shift);
+}
+
+static int max8998_ldo_enable(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 8, ret;
+
+ ret = max8998_get_enable_register(rdev, &reg, &shift);
+ if (ret)
+ return ret;
+
+ return max8998_update_reg(max8998->iodev, reg, 1<<shift, 1<<shift);
+}
+
+static int max8998_ldo_disable(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 8, ret;
+
+ ret = max8998_get_enable_register(rdev, &reg, &shift);
+ if (ret)
+ return ret;
+
+ return max8998_update_reg(max8998->iodev, reg, 0, 1<<shift);
+}
+
+static int max8998_get_voltage_register(struct regulator_dev *rdev,
+ int *_reg, int *_shift, int *_mask)
+{
+ int ldo = max8998_get_ldo(rdev);
+ int reg, shift = 0, mask = 0xff;
+
+ switch (ldo) {
+ case MAX8998_LDO2 ... MAX8998_LDO3:
+ reg = MAX8998_REG_LDO2_LDO3;
+ mask = 0xf;
+ if (ldo == MAX8998_LDO2)
+ shift = 4;
+ else
+ shift = 0;
+ break;
+ case MAX8998_LDO4 ... MAX8998_LDO7:
+ reg = MAX8998_REG_LDO4 + (ldo - MAX8998_LDO4);
+ break;
+ case MAX8998_LDO8 ... MAX8998_LDO9:
+ reg = MAX8998_REG_LDO8_LDO9;
+ mask = 0xf;
+ if (ldo == MAX8998_LDO8)
+ shift = 4;
+ else
+ shift = 0;
+ break;
+ case MAX8998_LDO10 ... MAX8998_LDO11:
+ reg = MAX8998_REG_LDO10_LDO11;
+ if (ldo == MAX8998_LDO10) {
+ shift = 5;
+ mask = 0x7;
+ } else {
+ shift = 0;
+ mask = 0x1f;
+ }
+ break;
+ case MAX8998_LDO12 ... MAX8998_LDO17:
+ reg = MAX8998_REG_LDO12 + (ldo - MAX8998_LDO12);
+ break;
+ case MAX8998_BUCK1:
+ reg = MAX8998_REG_BUCK1_DVSARM1;
+ break;
+ case MAX8998_BUCK2:
+ reg = MAX8998_REG_BUCK2_DVSINT1;
+ break;
+ case MAX8998_BUCK3:
+ reg = MAX8998_REG_BUCK3;
+ break;
+ case MAX8998_BUCK4:
+ reg = MAX8998_REG_BUCK4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *_reg = reg;
+ *_shift = shift;
+ *_mask = mask;
+
+ return 0;
+}
+
+static int max8998_get_voltage(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 0, mask, ret;
+ u8 val;
+
+ ret = max8998_get_voltage_register(rdev, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ ret = max8998_read_reg(max8998->iodev, reg, &val);
+ if (ret)
+ return ret;
+
+ val >>= shift;
+ val &= mask;
+
+ return max8998_list_voltage(rdev, val);
+}
+
+static int max8998_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int min_vol = min_uV / 1000, max_vol = max_uV / 1000;
+ int previous_vol = 0;
+ const struct voltage_map_desc *desc;
+ int ldo = max8998_get_ldo(rdev);
+ int reg, shift = 0, mask, ret;
+ int i = 0;
+ u8 val;
+ bool en_ramp = false;
+
+ if (ldo >= ARRAY_SIZE(ldo_voltage_map))
+ return -EINVAL;
+
+ desc = ldo_voltage_map[ldo];
+ if (desc == NULL)
+ return -EINVAL;
+
+ if (max_vol < desc->min || min_vol > desc->max)
+ return -EINVAL;
+
+ while (desc->min + desc->step*i < min_vol &&
+ desc->min + desc->step*i < desc->max)
+ i++;
+
+ if (desc->min + desc->step*i > max_vol)
+ return -EINVAL;
+
+ ret = max8998_get_voltage_register(rdev, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ /* wait for RAMP_UP_DELAY if rdev is BUCK1/2 and
+ * ENRAMP is ON */
+ if (ldo == MAX8998_BUCK1 || ldo == MAX8998_BUCK2) {
+ max8998_read_reg(max8998->iodev, MAX8998_REG_ONOFF4, &val);
+ if (val & (1 << 4)) {
+ en_ramp = true;
+ previous_vol = max8998_get_voltage(rdev);
+ }
+ }
+
+ ret = max8998_update_reg(max8998->iodev, reg, i<<shift, mask<<shift);
+
+ if (en_ramp == true) {
+ int difference = desc->min + desc->step*i - previous_vol/1000;
+ if (difference > 0)
+ udelay(difference / ((val & 0x0f) + 1));
+ }
+
+ return ret;
+}
+
+static struct regulator_ops max8998_ldo_ops = {
+ .list_voltage = max8998_list_voltage,
+ .is_enabled = max8998_ldo_is_enabled,
+ .enable = max8998_ldo_enable,
+ .disable = max8998_ldo_disable,
+ .get_voltage = max8998_get_voltage,
+ .set_voltage = max8998_set_voltage,
+ .set_suspend_enable = max8998_ldo_enable,
+ .set_suspend_disable = max8998_ldo_disable,
+};
+
+static struct regulator_ops max8998_buck_ops = {
+ .list_voltage = max8998_list_voltage,
+ .is_enabled = max8998_ldo_is_enabled,
+ .enable = max8998_ldo_enable,
+ .disable = max8998_ldo_disable,
+ .get_voltage = max8998_get_voltage,
+ .set_voltage = max8998_set_voltage,
+ .set_suspend_enable = max8998_ldo_enable,
+ .set_suspend_disable = max8998_ldo_disable,
+};
+
+static struct regulator_ops max8998_others_ops = {
+ .is_enabled = max8998_ldo_is_enabled,
+ .enable = max8998_ldo_enable,
+ .disable = max8998_ldo_disable,
+ .set_suspend_enable = max8998_ldo_enable,
+ .set_suspend_disable = max8998_ldo_disable,
+};
+
+static struct regulator_desc regulators[] = {
+ {
+ .name = "LDO2",
+ .id = MAX8998_LDO2,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO3",
+ .id = MAX8998_LDO3,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO4",
+ .id = MAX8998_LDO4,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO5",
+ .id = MAX8998_LDO5,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO6",
+ .id = MAX8998_LDO6,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO7",
+ .id = MAX8998_LDO7,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO8",
+ .id = MAX8998_LDO8,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO9",
+ .id = MAX8998_LDO9,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO10",
+ .id = MAX8998_LDO10,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO11",
+ .id = MAX8998_LDO11,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO12",
+ .id = MAX8998_LDO12,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO13",
+ .id = MAX8998_LDO13,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO14",
+ .id = MAX8998_LDO14,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO15",
+ .id = MAX8998_LDO15,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO16",
+ .id = MAX8998_LDO16,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO17",
+ .id = MAX8998_LDO17,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK1",
+ .id = MAX8998_BUCK1,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK2",
+ .id = MAX8998_BUCK2,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK3",
+ .id = MAX8998_BUCK3,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK4",
+ .id = MAX8998_BUCK4,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "EN32KHz AP",
+ .id = MAX8998_EN32KHZ_AP,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "EN32KHz CP",
+ .id = MAX8998_EN32KHZ_CP,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "ENVICHG",
+ .id = MAX8998_ENVICHG,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "ESAFEOUT1",
+ .id = MAX8998_ESAFEOUT1,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "ESAFEOUT2",
+ .id = MAX8998_ESAFEOUT2,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }
+};
+
+static __devinit int max8998_pmic_probe(struct platform_device *pdev)
+{
+ struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct regulator_dev **rdev;
+ struct max8998_data *max8998;
+ int i, ret, size;
+
+ if (!pdata) {
+ dev_err(pdev->dev.parent, "No platform init data supplied\n");
+ return -ENODEV;
+ }
+
+ max8998 = kzalloc(sizeof(struct max8998_data), GFP_KERNEL);
+ if (!max8998)
+ return -ENOMEM;
+
+ size = sizeof(struct regulator_dev *) * (pdata->num_regulators + 1);
+ max8998->rdev = kzalloc(size, GFP_KERNEL);
+ if (!max8998->rdev) {
+ kfree(max8998);
+ return -ENOMEM;
+ }
+
+ rdev = max8998->rdev;
+ max8998->iodev = iodev;
+ platform_set_drvdata(pdev, max8998);
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ const struct voltage_map_desc *desc;
+ int id = pdata->regulators[i].id;
+ int index = id - MAX8998_LDO2;
+
+ desc = ldo_voltage_map[id];
+ if (desc && regulators[index].ops != &max8998_others_ops) {
+ int count = (desc->max - desc->min) / desc->step + 1;
+ regulators[index].n_voltages = count;
+ }
+ rdev[i] = regulator_register(&regulators[index], max8998->dev,
+ pdata->regulators[i].initdata, max8998);
+ if (IS_ERR(rdev[i])) {
+ ret = PTR_ERR(rdev[i]);
+ dev_err(max8998->dev, "regulator init failed\n");
+ rdev[i] = NULL;
+ goto err;
+ }
+ }
+
+
+ return 0;
+err:
+ for (i = 0; i <= max8998->num_regulators; i++)
+ if (rdev[i])
+ regulator_unregister(rdev[i]);
+
+ kfree(max8998->rdev);
+ kfree(max8998);
+
+ return ret;
+}
+
+static int __devexit max8998_pmic_remove(struct platform_device *pdev)
+{
+ struct max8998_data *max8998 = platform_get_drvdata(pdev);
+ struct regulator_dev **rdev = max8998->rdev;
+ int i;
+
+ for (i = 0; i <= max8998->num_regulators; i++)
+ if (rdev[i])
+ regulator_unregister(rdev[i]);
+
+ kfree(max8998->rdev);
+ kfree(max8998);
+
+ return 0;
+}
+
+static struct platform_driver max8998_pmic_driver = {
+ .driver = {
+ .name = "max8998-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8998_pmic_probe,
+ .remove = __devexit_p(max8998_pmic_remove),
+};
+
+static int __init max8998_pmic_init(void)
+{
+ return platform_driver_register(&max8998_pmic_driver);
+}
+subsys_initcall(max8998_pmic_init);
+
+static void __exit max8998_pmic_cleanup(void)
+{
+ platform_driver_unregister(&max8998_pmic_driver);
+}
+module_exit(max8998_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 8998 voltage regulator driver");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index f50afc9..cd6d4fc 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -585,6 +585,8 @@ static const struct tps_info tps65023_regs[] = {
static const struct i2c_device_id tps_65023_id[] = {
{.name = "tps65023",
.driver_data = (unsigned long) tps65023_regs,},
+ {.name = "tps65021",
+ .driver_data = (unsigned long) tps65023_regs,},
{ },
};
diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c
index 8152d65..c239f42 100644
--- a/drivers/regulator/tps6507x-regulator.c
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -614,6 +614,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev)
}
tps6507x_dev->pmic = tps;
+ platform_set_drvdata(pdev, tps6507x_dev);
return 0;
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
new file mode 100644
index 0000000..8cff141
--- /dev/null
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -0,0 +1,396 @@
+/*
+ * Regulator driver for TI TPS6586x
+ *
+ * Copyright (C) 2010 Compulab Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on da903x
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Copyright (C) 2008 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps6586x.h>
+
+/* supply control and voltage setting */
+#define TPS6586X_SUPPLYENA 0x10
+#define TPS6586X_SUPPLYENB 0x11
+#define TPS6586X_SUPPLYENC 0x12
+#define TPS6586X_SUPPLYEND 0x13
+#define TPS6586X_SUPPLYENE 0x14
+#define TPS6586X_VCC1 0x20
+#define TPS6586X_VCC2 0x21
+#define TPS6586X_SM1V1 0x23
+#define TPS6586X_SM1V2 0x24
+#define TPS6586X_SM1SL 0x25
+#define TPS6586X_SM0V1 0x26
+#define TPS6586X_SM0V2 0x27
+#define TPS6586X_SM0SL 0x28
+#define TPS6586X_LDO2AV1 0x29
+#define TPS6586X_LDO2AV2 0x2A
+#define TPS6586X_LDO2BV1 0x2F
+#define TPS6586X_LDO2BV2 0x30
+#define TPS6586X_LDO4V1 0x32
+#define TPS6586X_LDO4V2 0x33
+
+/* converter settings */
+#define TPS6586X_SUPPLYV1 0x41
+#define TPS6586X_SUPPLYV2 0x42
+#define TPS6586X_SUPPLYV3 0x43
+#define TPS6586X_SUPPLYV4 0x44
+#define TPS6586X_SUPPLYV5 0x45
+#define TPS6586X_SUPPLYV6 0x46
+#define TPS6586X_SMODE1 0x47
+#define TPS6586X_SMODE2 0x48
+
+struct tps6586x_regulator {
+ struct regulator_desc desc;
+
+ int volt_reg;
+ int volt_shift;
+ int volt_nbits;
+ int enable_bit[2];
+ int enable_reg[2];
+
+ int *voltages;
+
+ /* for DVM regulators */
+ int go_reg;
+ int go_bit;
+};
+
+static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev)
+{
+ return rdev_get_dev(rdev)->parent->parent;
+}
+
+static int tps6586x_ldo_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct tps6586x_regulator *info = rdev_get_drvdata(rdev);
+
+ return info->voltages[selector] * 1000;
+}
+
+
+static int __tps6586x_ldo_set_voltage(struct device *parent,
+ struct tps6586x_regulator *ri,
+ int min_uV, int max_uV)
+{
+ int val, uV;
+ uint8_t mask;
+
+ for (val = 0; val < ri->desc.n_voltages; val++) {
+ uV = ri->voltages[val] * 1000;
+
+ /* LDO0 has minimal voltage 1.2 rather than 1.25 */
+ if (ri->desc.id == TPS6586X_ID_LDO_0 && val == 0)
+ uV -= 50 * 1000;
+
+ /* use the first in-range value */
+ if (min_uV <= uV && uV <= max_uV) {
+
+ val <<= ri->volt_shift;
+ mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+
+ return tps6586x_update(parent, ri->volt_reg, val, mask);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tps6586x_ldo_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+
+ return __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV);
+}
+
+static int tps6586x_ldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+ uint8_t val, mask;
+ int ret;
+
+ ret = tps6586x_read(parent, ri->volt_reg, &val);
+ if (ret)
+ return ret;
+
+ mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+ val = (val & mask) >> ri->volt_shift;
+
+ if (val > ri->desc.n_voltages)
+ BUG();
+
+ return ri->voltages[val] * 1000;
+}
+
+static int tps6586x_dvm_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+ int ret;
+
+ ret = __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV);
+ if (ret)
+ return ret;
+
+ return tps6586x_set_bits(parent, ri->go_reg, ri->go_bit);
+}
+
+static int tps6586x_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+
+ return tps6586x_set_bits(parent, ri->enable_reg[0],
+ 1 << ri->enable_bit[0]);
+}
+
+static int tps6586x_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+
+ return tps6586x_clr_bits(parent, ri->enable_reg[0],
+ 1 << ri->enable_bit[0]);
+}
+
+static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+ uint8_t reg_val;
+ int ret;
+
+ ret = tps6586x_read(parent, ri->enable_reg[0], &reg_val);
+ if (ret)
+ return ret;
+
+ return !!(reg_val & (1 << ri->enable_bit[0]));
+}
+
+static struct regulator_ops tps6586x_regulator_ldo_ops = {
+ .list_voltage = tps6586x_ldo_list_voltage,
+ .get_voltage = tps6586x_ldo_get_voltage,
+ .set_voltage = tps6586x_ldo_set_voltage,
+
+ .is_enabled = tps6586x_regulator_is_enabled,
+ .enable = tps6586x_regulator_enable,
+ .disable = tps6586x_regulator_disable,
+};
+
+static struct regulator_ops tps6586x_regulator_dvm_ops = {
+ .list_voltage = tps6586x_ldo_list_voltage,
+ .get_voltage = tps6586x_ldo_get_voltage,
+ .set_voltage = tps6586x_dvm_set_voltage,
+
+ .is_enabled = tps6586x_regulator_is_enabled,
+ .enable = tps6586x_regulator_enable,
+ .disable = tps6586x_regulator_disable,
+};
+
+static int tps6586x_ldo_voltages[] = {
+ 1250, 1500, 1800, 2500, 2700, 2850, 3100, 3300,
+};
+
+static int tps6586x_ldo4_voltages[] = {
+ 1700, 1725, 1750, 1775, 1800, 1825, 1850, 1875,
+ 1900, 1925, 1950, 1975, 2000, 2025, 2050, 2075,
+ 2100, 2125, 2150, 2175, 2200, 2225, 2250, 2275,
+ 2300, 2325, 2350, 2375, 2400, 2425, 2450, 2475,
+};
+
+static int tps6586x_sm2_voltages[] = {
+ 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350,
+ 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750,
+ 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150,
+ 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550,
+};
+
+static int tps6586x_dvm_voltages[] = {
+ 725, 750, 775, 800, 825, 850, 875, 900,
+ 925, 950, 975, 1000, 1025, 1050, 1075, 1100,
+ 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300,
+ 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500,
+};
+
+#define TPS6586X_REGULATOR(_id, vdata, _ops, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit) \
+{ \
+ .desc = { \
+ .name = "REG-" #_id, \
+ .ops = &tps6586x_regulator_##_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = TPS6586X_ID_##_id, \
+ .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \
+ .owner = THIS_MODULE, \
+ }, \
+ .volt_reg = TPS6586X_##vreg, \
+ .volt_shift = (shift), \
+ .volt_nbits = (nbits), \
+ .enable_reg[0] = TPS6586X_SUPPLY##ereg0, \
+ .enable_bit[0] = (ebit0), \
+ .enable_reg[1] = TPS6586X_SUPPLY##ereg1, \
+ .enable_bit[1] = (ebit1), \
+ .voltages = tps6586x_##vdata##_voltages, \
+}
+
+#define TPS6586X_LDO(_id, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1) \
+ TPS6586X_REGULATOR(_id, vdata, ldo_ops, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, 0, 0)
+
+#define TPS6586X_DVM(_id, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit) \
+ TPS6586X_REGULATOR(_id, vdata, dvm_ops, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit)
+
+static struct tps6586x_regulator tps6586x_regulator[] = {
+ TPS6586X_LDO(LDO_0, ldo, SUPPLYV1, 5, 3, ENC, 0, END, 0),
+ TPS6586X_LDO(LDO_3, ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2),
+ TPS6586X_LDO(LDO_5, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6),
+ TPS6586X_LDO(LDO_6, ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4),
+ TPS6586X_LDO(LDO_7, ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5),
+ TPS6586X_LDO(LDO_8, ldo, SUPPLYV1, 5, 3, ENC, 6, END, 6),
+ TPS6586X_LDO(LDO_9, ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7),
+ TPS6586X_LDO(LDO_RTC, ldo, SUPPLYV4, 3, 3, ENE, 7, ENE, 7),
+ TPS6586X_LDO(LDO_1, dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1),
+ TPS6586X_LDO(SM_2, sm2, SUPPLYV2, 0, 5, ENC, 1, END, 1),
+
+ TPS6586X_DVM(LDO_2, dvm, LDO2BV1, 0, 5, ENA, 3, ENB, 3, VCC2, 6),
+ TPS6586X_DVM(LDO_4, ldo4, LDO4V1, 0, 5, ENC, 3, END, 3, VCC1, 6),
+ TPS6586X_DVM(SM_0, dvm, SM0V1, 0, 5, ENA, 1, ENB, 1, VCC1, 2),
+ TPS6586X_DVM(SM_1, dvm, SM1V1, 0, 5, ENA, 0, ENB, 0, VCC1, 0),
+};
+
+/*
+ * TPS6586X has 2 enable bits that are OR'ed to determine the actual
+ * regulator state. Clearing one of this bits allows switching
+ * regulator on and of with single register write.
+ */
+static inline int tps6586x_regulator_preinit(struct device *parent,
+ struct tps6586x_regulator *ri)
+{
+ uint8_t val1, val2;
+ int ret;
+
+ ret = tps6586x_read(parent, ri->enable_reg[0], &val1);
+ if (ret)
+ return ret;
+
+ ret = tps6586x_read(parent, ri->enable_reg[1], &val2);
+ if (ret)
+ return ret;
+
+ if (!(val2 & ri->enable_bit[1]))
+ return 0;
+
+ /*
+ * The regulator is on, but it's enabled with the bit we don't
+ * want to use, so we switch the enable bits
+ */
+ if (!(val1 & ri->enable_bit[0])) {
+ ret = tps6586x_set_bits(parent, ri->enable_reg[0],
+ 1 << ri->enable_bit[0]);
+ if (ret)
+ return ret;
+ }
+
+ return tps6586x_clr_bits(parent, ri->enable_reg[1],
+ 1 << ri->enable_bit[1]);
+}
+
+static inline struct tps6586x_regulator *find_regulator_info(int id)
+{
+ struct tps6586x_regulator *ri;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tps6586x_regulator); i++) {
+ ri = &tps6586x_regulator[i];
+ if (ri->desc.id == id)
+ return ri;
+ }
+ return NULL;
+}
+
+static int __devinit tps6586x_regulator_probe(struct platform_device *pdev)
+{
+ struct tps6586x_regulator *ri = NULL;
+ struct regulator_dev *rdev;
+ int id = pdev->id;
+ int err;
+
+ dev_dbg(&pdev->dev, "Probing reulator %d\n", id);
+
+ ri = find_regulator_info(id);
+ if (ri == NULL) {
+ dev_err(&pdev->dev, "invalid regulator ID specified\n");
+ return -EINVAL;
+ }
+
+ err = tps6586x_regulator_preinit(pdev->dev.parent, ri);
+ if (err)
+ return err;
+
+ rdev = regulator_register(&ri->desc, &pdev->dev,
+ pdev->dev.platform_data, ri);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ ri->desc.name);
+ return PTR_ERR(rdev);
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int __devexit tps6586x_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+ return 0;
+}
+
+static struct platform_driver tps6586x_regulator_driver = {
+ .driver = {
+ .name = "tps6586x-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps6586x_regulator_probe,
+ .remove = __devexit_p(tps6586x_regulator_remove),
+};
+
+static int __init tps6586x_regulator_init(void)
+{
+ return platform_driver_register(&tps6586x_regulator_driver);
+}
+subsys_initcall(tps6586x_regulator_init);
+
+static void __exit tps6586x_regulator_exit(void)
+{
+ platform_driver_unregister(&tps6586x_regulator_driver);
+}
+module_exit(tps6586x_regulator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Regulator Driver for TI TPS6586X PMIC");
+MODULE_ALIAS("platform:tps6586x-regulator");
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
index 5a1dc8a..03713bc 100644
--- a/drivers/regulator/wm8994-regulator.c
+++ b/drivers/regulator/wm8994-regulator.c
@@ -219,8 +219,6 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
ldo->wm8994 = wm8994;
- ldo->is_enabled = true;
-
if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) {
ldo->enable = pdata->ldo[id].enable;
@@ -237,7 +235,8 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
ret);
goto err_gpio;
}
- }
+ } else
+ ldo->is_enabled = true;
ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev,
pdata->ldo[id].init_data, ldo);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4301a6c..48ca713 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -166,6 +166,16 @@ config RTC_DRV_DS1672
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
+config RTC_DRV_DS3232
+ tristate "Dallas/Maxim DS3232"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS3232 real-time clock chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3232.
+
config RTC_DRV_MAX6900
tristate "Maxim MAX6900"
help
@@ -203,6 +213,15 @@ config RTC_DRV_ISL1208
This driver can also be built as a module. If so, the module
will be called rtc-isl1208.
+config RTC_DRV_ISL12022
+ tristate "Intersil ISL12022"
+ help
+ If you say yes here you get support for the
+ Intersil ISL12022 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12022.
+
config RTC_DRV_X1205
tristate "Xicor/Intersil X1205"
help
@@ -537,6 +556,16 @@ config RTC_DRV_MSM6242
This driver can also be built as a module. If so, the module
will be called rtc-msm6242.
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_MX25
+ depends on RTC_CLASS
+ help
+ Support for Freescale IMX DryIce RTC
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-imxdi".
+
config RTC_MXC
tristate "Freescale MXC Real Time Clock"
depends on ARCH_MXC
@@ -645,9 +674,16 @@ config RTC_DRV_OMAP
DA8xx/OMAP-L13x chips. This driver can also be built as a
module called rtc-omap.
+config HAVE_S3C_RTC
+ bool
+ help
+ This will include RTC support for Samsung SoCs. If
+ you want to include RTC support for any machine, kindly
+ select this in the respective mach-XXXX/Kconfig file.
+
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index fedf9bb..0f207b3 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -41,12 +41,15 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
+obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 11b8ea2..5856167 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -970,7 +970,6 @@ static inline int cmos_poweroff(struct device *dev)
#include <linux/acpi.h>
-#ifdef CONFIG_PM
static u32 rtc_handler(void *context)
{
acpi_clear_event(ACPI_EVENT_RTC);
@@ -999,11 +998,6 @@ static void rtc_wake_off(struct device *dev)
{
acpi_disable_event(ACPI_EVENT_RTC, 0);
}
-#else
-#define rtc_wake_setup() do{}while(0)
-#define rtc_wake_on NULL
-#define rtc_wake_off NULL
-#endif
/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
* its device node and pass extra config data. This helps its driver use
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 0000000..9daed8d
--- /dev/null
+++ b/drivers/rtc/rtc-ds3232.c
@@ -0,0 +1,326 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2010 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 the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS 0x00
+#define DS3232_REG_MINUTES 0x01
+#define DS3232_REG_HOURS 0x02
+#define DS3232_REG_AMPM 0x02
+#define DS3232_REG_DAY 0x03
+#define DS3232_REG_DATE 0x04
+#define DS3232_REG_MONTH 0x05
+#define DS3232_REG_CENTURY 0x05
+#define DS3232_REG_YEAR 0x06
+#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
+#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
+#define DS3232_REG_CR 0x0E /* Control register */
+# define DS3232_REG_CR_nEOSC 0x80
+# define DS3232_REG_CR_INTCN 0x04
+# define DS3232_REG_CR_A2IE 0x02
+# define DS3232_REG_CR_A1IE 0x01
+
+#define DS3232_REG_SR 0x0F /* control/status register */
+# define DS3232_REG_SR_OSF 0x80
+# define DS3232_REG_SR_BSY 0x04
+# define DS3232_REG_SR_A2F 0x02
+# define DS3232_REG_SR_A1F 0x01
+
+struct ds3232 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS3232_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ control |= DS3232_REG_CR_INTCN;
+
+ return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 buf[7];
+ unsigned int year, month, day, hour, minute, second;
+ unsigned int week, twelve_hr, am_pm;
+ unsigned int century, add_century = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < 7)
+ return -EIO;
+
+ second = buf[0];
+ minute = buf[1];
+ hour = buf[2];
+ week = buf[3];
+ day = buf[4];
+ month = buf[5];
+ year = buf[6];
+
+ /* Extract additional information for AM/PM and century */
+
+ twelve_hr = hour & 0x40;
+ am_pm = hour & 0x20;
+ century = month & 0x80;
+
+ /* Write to rtc_time structure */
+
+ time->tm_sec = bcd2bin(second);
+ time->tm_min = bcd2bin(minute);
+ if (twelve_hr) {
+ /* Convert to 24 hr */
+ if (am_pm)
+ time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+ else
+ time->tm_hour = bcd2bin(hour & 0x1F);
+ } else {
+ time->tm_hour = bcd2bin(hour);
+ }
+
+ time->tm_wday = bcd2bin(week);
+ time->tm_mday = bcd2bin(day);
+ time->tm_mon = bcd2bin(month & 0x7F);
+ if (century)
+ add_century = 100;
+
+ time->tm_year = bcd2bin(year) + add_century;
+
+ return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[7];
+
+ /* Extract time from rtc_time and load into ds3232*/
+
+ buf[0] = bin2bcd(time->tm_sec);
+ buf[1] = bin2bcd(time->tm_min);
+ buf[2] = bin2bcd(time->tm_hour);
+ buf[3] = bin2bcd(time->tm_wday); /* Day of the week */
+ buf[4] = bin2bcd(time->tm_mday); /* Date */
+ buf[5] = bin2bcd(time->tm_mon);
+ if (time->tm_year >= 100) {
+ buf[5] |= 0x80;
+ buf[6] = bin2bcd(time->tm_year - 100);
+ } else {
+ buf[6] = bin2bcd(time->tm_year);
+ }
+
+ return i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_SECONDS, 7, buf);
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds3232->work);
+ return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+ struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+ struct i2c_client *client = ds3232->client;
+ int stat, control;
+
+ mutex_lock(&ds3232->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS3232_REG_SR_A1F) {
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto out;
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+ .read_time = ds3232_read_time,
+ .set_time = ds3232_set_time,
+};
+
+static int __devinit ds3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds3232 *ds3232;
+ int ret;
+
+ ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL);
+ if (!ds3232)
+ return -ENOMEM;
+
+ ds3232->client = client;
+ i2c_set_clientdata(client, ds3232);
+
+ INIT_WORK(&ds3232->work, ds3232_work);
+ mutex_init(&ds3232->mutex);
+
+ ret = ds3232_check_rtc_status(client);
+ if (ret)
+ goto out_free;
+
+ ds3232->rtc = rtc_device_register(client->name, &client->dev,
+ &ds3232_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds3232->rtc)) {
+ ret = PTR_ERR(ds3232->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto out_irq;
+ }
+
+ if (client->irq >= 0) {
+ ret = request_irq(client->irq, ds3232_irq, 0,
+ "ds3232", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto out_free;
+ }
+ }
+
+ return 0;
+
+out_irq:
+ if (client->irq >= 0)
+ free_irq(client->irq, client);
+
+out_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return ret;
+}
+
+static int __devexit ds3232_remove(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds3232->mutex);
+ ds3232->exiting = 1;
+ mutex_unlock(&ds3232->mutex);
+
+ free_irq(client->irq, client);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds3232->rtc);
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return 0;
+}
+
+static const struct i2c_device_id ds3232_id[] = {
+ { "ds3232", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+ .driver = {
+ .name = "rtc-ds3232",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds3232_probe,
+ .remove = __devexit_p(ds3232_remove),
+ .id_table = ds3232_id,
+};
+
+static int __init ds3232_init(void)
+{
+ return i2c_add_driver(&ds3232_driver);
+}
+
+static void __exit ds3232_exit(void)
+{
+ i2c_del_driver(&ds3232_driver);
+}
+
+module_init(ds3232_init);
+module_exit(ds3232_exit);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
index e4de8f3..4cf2e70 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -52,8 +52,8 @@ struct fm3130 {
struct i2c_msg msg[4];
struct i2c_client *client;
struct rtc_device *rtc;
+ int alarm_valid;
int data_valid;
- int alarm;
};
static const struct i2c_device_id fm3130_id[] = {
{ "fm3130", 0 },
@@ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode)
dev_dbg(dev, "invalid mode %d\n", mode);
break;
}
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
i2c_smbus_write_byte_data(fm3130->client,
FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
}
@@ -208,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct fm3130 *fm3130 = dev_get_drvdata(dev);
int tmp;
struct rtc_time *tm = &alrm->time;
+
+ if (!fm3130->alarm_valid) {
+ /*
+ * We have invalid alarm in RTC, probably due to battery faults
+ * or other problems. Return EIO for now, it will allow us to
+ * set alarm value later instead of error during probing which
+ * disables device
+ */
+ return -EIO;
+ }
+
/* read the RTC alarm registers all at once */
tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
&fm3130->msg[2], 2);
@@ -222,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
fm3130->regs[FM3130_ALARM_DATE],
fm3130->regs[FM3130_ALARM_MONTHS]);
-
tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
+
if (tm->tm_mon > 0)
tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read alarm", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
+ /* check if alarm enabled */
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) &&
+ (~fm3130->regs[FM3130_RTC_CONTROL] &
+ FM3130_RTC_CONTROL_BIT_CAL)) {
+ alrm->enabled = 1;
+ }
+
return 0;
}
@@ -251,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- if (tm->tm_sec != -1)
- fm3130->regs[FM3130_ALARM_SECONDS] =
- bin2bcd(tm->tm_sec) | 0x80;
+ fm3130->regs[FM3130_ALARM_SECONDS] =
+ (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;
- if (tm->tm_min != -1)
- fm3130->regs[FM3130_ALARM_MINUTES] =
- bin2bcd(tm->tm_min) | 0x80;
+ fm3130->regs[FM3130_ALARM_MINUTES] =
+ (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;
- if (tm->tm_hour != -1)
- fm3130->regs[FM3130_ALARM_HOURS] =
- bin2bcd(tm->tm_hour) | 0x80;
+ fm3130->regs[FM3130_ALARM_HOURS] =
+ (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;
- if (tm->tm_mday != -1)
- fm3130->regs[FM3130_ALARM_DATE] =
- bin2bcd(tm->tm_mday) | 0x80;
+ fm3130->regs[FM3130_ALARM_DATE] =
+ (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;
- if (tm->tm_mon != -1)
- fm3130->regs[FM3130_ALARM_MONTHS] =
- bin2bcd(tm->tm_mon + 1) | 0x80;
+ fm3130->regs[FM3130_ALARM_MONTHS] =
+ (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;
dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
fm3130->regs[FM3130_ALARM_SECONDS],
@@ -285,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
fm3130->regs[FM3130_RTC_CONTROL] =
i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
+ /* enable or disable alarm */
if (alrm->enabled) {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
(fm3130->regs[FM3130_RTC_CONTROL] &
@@ -298,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
} else {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
- ~(FM3130_RTC_CONTROL_BIT_AEN));
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
}
+
+ /* We assume here that data is valid once written */
+ if (!fm3130->alarm_valid)
+ fm3130->alarm_valid = 1;
+
return 0;
}
+static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n",
+ enabled, fm3130->regs[FM3130_RTC_CONTROL]);
+
+ switch (enabled) {
+ case 0: /* alarm off */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ break;
+ case 1: /* alarm on */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct rtc_class_ops fm3130_rtc_ops = {
.read_time = fm3130_get_time,
.set_time = fm3130_set_time,
.read_alarm = fm3130_read_alarm,
.set_alarm = fm3130_set_alarm,
+ .alarm_irq_enable = fm3130_alarm_irq_enable,
};
static struct i2c_driver fm3130_driver;
@@ -356,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->msg[3].len = FM3130_ALARM_REGS;
fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
+ fm3130->alarm_valid = 0;
fm3130->data_valid = 0;
tmp = i2c_transfer(adapter, fm3130->msg, 4);
@@ -370,12 +420,6 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] =
i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
-
/* Disabling calibration mode */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
@@ -400,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] &
~(FM3130_CAL_CONTROL_BIT_nOSCEN));
- /* oscillator fault? clear flag, and warn */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB)
+ /* low battery? clear flag, and warn */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_LB));
dev_warn(&client->dev, "Low battery!\n");
+ }
- /* oscillator fault? clear flag, and warn */
+ /* check if Power On Reset bit is set */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~FM3130_RTC_CONTROL_BIT_POR);
- dev_warn(&client->dev, "SET TIME!\n");
+ dev_dbg(&client->dev, "POR bit is set\n");
}
/* ACS is controlled by alarm */
i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
- /* TODO */
- /* TODO need to sanity check alarm */
- tmp = fm3130->regs[FM3130_RTC_SECONDS];
- tmp = bcd2bin(tmp & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ /* alarm registers sanity check */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
if (tmp == 0 || tmp > 31)
- goto exit_bad;
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
if (tmp == 0 || tmp > 12)
- goto exit_bad;
+ goto bad_alarm;
- tmp = fm3130->regs[FM3130_RTC_HOURS];
+ fm3130->alarm_valid = 1;
+
+bad_alarm:
+
+ /* clock registers sanity chek */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7);
+ if (tmp == 0 || tmp > 7)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_clock;
fm3130->data_valid = 1;
-exit_bad:
- if (!fm3130->data_valid)
+bad_clock:
+
+ if (!fm3130->data_valid || !fm3130->alarm_valid)
dev_dbg(&client->dev,
"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
"%02x %02x %02x %02x %02x %02x %02x\n",
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 0000000..2dd3c01
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+ u32 dsr;
+ spinlock_t irq_lock;
+ wait_queue_head_t write_wait;
+ struct mutex write_mutex;
+ struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+ int cnt;
+
+ dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+ /* clear the write error flag */
+ __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(&imxdi->pdev->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+ int ret;
+ int rc = 0;
+
+ /* serialize register writes */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* enable the write-complete interrupt */
+ di_int_enable(imxdi, DIER_WCIE);
+
+ imxdi->dsr = 0;
+
+ /* do the register write */
+ __raw_writel(val, imxdi->ioaddr + reg);
+
+ /* wait for the write to finish */
+ ret = wait_event_interruptible_timeout(imxdi->write_wait,
+ imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+ if (ret < 0) {
+ rc = ret;
+ goto out;
+ } else if (ret == 0) {
+ dev_warn(&imxdi->pdev->dev,
+ "Write-wait timeout "
+ "val = 0x%08x reg = 0x%08x\n", val, reg);
+ }
+
+ /* check for write error */
+ if (imxdi->dsr & DSR_WEF) {
+ clear_write_error(imxdi);
+ rc = -EIO;
+ }
+
+out:
+ mutex_unlock(&imxdi->write_mutex);
+
+ return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ int rc;
+
+ /* zero the fractional part first */
+ rc = di_write_wait(imxdi, 0, DTCLR);
+ if (rc == 0)
+ rc = di_write_wait(imxdi, secs, DTCMR);
+
+ return rc;
+}
+
+static int dryice_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+ if (enabled)
+ di_int_enable(imxdi, DIER_CAIE);
+ else
+ di_int_disable(imxdi, DIER_CAIE);
+
+ return 0;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ u32 dcamr;
+
+ dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+ rtc_time_to_tm(dcamr, &alarm->time);
+
+ /* alarm is enabled if the interrupt is enabled */
+ alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+
+ /* don't allow the DSR read to mess up DSR_WCF */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* alarm is pending if the alarm flag is set */
+ alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+
+ mutex_unlock(&imxdi->write_mutex);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+ unsigned long alarm_time;
+ int rc;
+
+ rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+ if (rc)
+ return rc;
+
+ /* don't allow setting alarm in the past */
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ if (alarm_time < now)
+ return -EINVAL;
+
+ /* write the new alarm time */
+ rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+ if (rc)
+ return rc;
+
+ if (alarm->enabled)
+ di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */
+ else
+ di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+ return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+ .read_time = dryice_rtc_read_time,
+ .set_mmss = dryice_rtc_set_mmss,
+ .alarm_irq_enable = dryice_rtc_alarm_irq_enable,
+ .read_alarm = dryice_rtc_read_alarm,
+ .set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+ struct imxdi_dev *imxdi = dev_id;
+ u32 dsr, dier;
+ irqreturn_t rc = IRQ_NONE;
+
+ dier = __raw_readl(imxdi->ioaddr + DIER);
+
+ /* handle write complete and write error cases */
+ if ((dier & DIER_WCIE)) {
+ /*If the write wait queue is empty then there is no pending
+ operations. It means the interrupt is for DryIce -Security.
+ IRQ must be returned as none.*/
+ if (list_empty_careful(&imxdi->write_wait.task_list))
+ return rc;
+
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if ((dsr & (DSR_WCF | DSR_WEF))) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_WCIE);
+
+ /* save the dsr value for the wait queue */
+ imxdi->dsr |= dsr;
+
+ wake_up_interruptible(&imxdi->write_wait);
+ rc = IRQ_HANDLED;
+ }
+ }
+
+ /* handle the alarm case */
+ if ((dier & DIER_CAIE)) {
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_CAF) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_CAIE);
+
+ /* finish alarm in user context */
+ schedule_work(&imxdi->work);
+ rc = IRQ_HANDLED;
+ }
+ }
+ return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+ struct imxdi_dev *imxdi = container_of(work,
+ struct imxdi_dev, work);
+
+ /* dismiss the interrupt (ignore error) */
+ di_write_wait(imxdi, DSR_CAF, DSR);
+
+ /* pass the alarm event to the rtc framework. */
+ rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct imxdi_dev *imxdi;
+ int rc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
+ if (!imxdi)
+ return -ENOMEM;
+
+ imxdi->pdev = pdev;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name))
+ return -EBUSY;
+
+ imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (imxdi->ioaddr == NULL)
+ return -ENOMEM;
+
+ imxdi->irq = platform_get_irq(pdev, 0);
+ if (imxdi->irq < 0)
+ return imxdi->irq;
+
+ init_waitqueue_head(&imxdi->write_wait);
+
+ INIT_WORK(&imxdi->work, dryice_work);
+
+ mutex_init(&imxdi->write_mutex);
+
+ imxdi->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(imxdi->clk))
+ return PTR_ERR(imxdi->clk);
+ clk_enable(imxdi->clk);
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
+ IRQF_SHARED, pdev->name, imxdi);
+ if (rc) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ goto err;
+ }
+
+ /* put dryice into valid state */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
+ rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* initialize alarm */
+ rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+ if (rc)
+ goto err;
+ rc = di_write_wait(imxdi, 0, DCALR);
+ if (rc)
+ goto err;
+
+ /* clear alarm flag */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
+ rc = di_write_wait(imxdi, DSR_CAF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* the timer won't count if it has never been written to */
+ if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
+ rc = di_write_wait(imxdi, 0, DTCMR);
+ if (rc)
+ goto err;
+ }
+
+ /* start keeping time */
+ if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
+ rc = di_write_wait(imxdi,
+ __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
+ DCR);
+ if (rc)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, imxdi);
+ imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &dryice_rtc_ops, THIS_MODULE);
+ if (IS_ERR(imxdi->rtc)) {
+ rc = PTR_ERR(imxdi->rtc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return rc;
+}
+
+static int __devexit dryice_rtc_remove(struct platform_device *pdev)
+{
+ struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+
+ flush_work(&imxdi->work);
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rtc_device_unregister(imxdi->rtc);
+
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+ .driver = {
+ .name = "imxdi_rtc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+ return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+ platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
new file mode 100644
index 0000000..ddbc797
--- /dev/null
+++ b/drivers/rtc/rtc-isl12022.c
@@ -0,0 +1,327 @@
+/*
+ * An I2C driver for the Intersil ISL 12022
+ *
+ * Author: Roman Fietze <roman.fietze@telemotive.de>
+ *
+ * Based on the Philips PCF8563 RTC
+ * by Alessandro Zummo <a.zummo@towertech.it>.
+ *
+ * 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/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define DRV_VERSION "0.1"
+
+/* ISL register offsets */
+#define ISL12022_REG_SC 0x00
+#define ISL12022_REG_MN 0x01
+#define ISL12022_REG_HR 0x02
+#define ISL12022_REG_DT 0x03
+#define ISL12022_REG_MO 0x04
+#define ISL12022_REG_YR 0x05
+#define ISL12022_REG_DW 0x06
+
+#define ISL12022_REG_SR 0x07
+#define ISL12022_REG_INT 0x08
+
+/* ISL register bits */
+#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
+
+#define ISL12022_SR_LBAT85 (1 << 2)
+#define ISL12022_SR_LBAT75 (1 << 1)
+
+#define ISL12022_INT_WRTC (1 << 6)
+
+
+static struct i2c_driver isl12022_driver;
+
+struct isl12022 {
+ struct rtc_device *rtc;
+
+ bool write_enabled; /* true if write enable is set */
+};
+
+
+static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
+ uint8_t *data, size_t n)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = data
+ }, /* setup read ptr */
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = n,
+ .buf = data
+ }
+ };
+
+ int ret;
+
+ data[0] = reg;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: read error, ret=%d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int isl12022_write_reg(struct i2c_client *client,
+ uint8_t reg, uint8_t val)
+{
+ uint8_t data[2] = { reg, val };
+ int err;
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * In the routines that deal directly with the isl12022 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ uint8_t buf[ISL12022_REG_INT + 1];
+ int ret;
+
+ ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
+ dev_warn(&client->dev,
+ "voltage dropped below %u%%, "
+ "date and time is not reliable.\n",
+ buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, mon=%02x, year=%02x, wday=%02x, "
+ "sr=%02x, int=%02x",
+ __func__,
+ buf[ISL12022_REG_SC],
+ buf[ISL12022_REG_MN],
+ buf[ISL12022_REG_HR],
+ buf[ISL12022_REG_DT],
+ buf[ISL12022_REG_MO],
+ buf[ISL12022_REG_YR],
+ buf[ISL12022_REG_DW],
+ buf[ISL12022_REG_SR],
+ buf[ISL12022_REG_INT]);
+
+ tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
+ tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
+ tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* The clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup. */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date and time is invalid.\n");
+
+ return 0;
+}
+
+static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+ size_t i;
+ int ret;
+ uint8_t buf[ISL12022_REG_DW + 1];
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (!isl12022->write_enabled) {
+
+ ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
+ if (ret)
+ return ret;
+
+ /* Check if WRTC (write rtc enable) is set factory default is
+ * 0 (not set) */
+ if (!(buf[0] & ISL12022_INT_WRTC)) {
+ dev_info(&client->dev,
+ "init write enable and 24 hour format\n");
+
+ /* Set the write enable bit. */
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_INT,
+ buf[0] | ISL12022_INT_WRTC);
+ if (ret)
+ return ret;
+
+ /* Write to any RTC register to start RTC, we use the
+ * HR register, setting the MIL bit to use the 24 hour
+ * format. */
+ ret = isl12022_read_regs(client, ISL12022_REG_HR,
+ buf, 1);
+ if (ret)
+ return ret;
+
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_HR,
+ buf[0] | ISL12022_HR_MIL);
+ if (ret)
+ return ret;
+ }
+
+ isl12022->write_enabled = 1;
+ }
+
+ /* hours, minutes and seconds */
+ buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
+
+ buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
+
+ /* write register's data */
+ for (i = 0; i < ARRAY_SIZE(buf); i++) {
+ ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
+ buf[ISL12022_REG_SC + i]);
+ if (ret)
+ return -EIO;
+ };
+
+ return 0;
+}
+
+static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops isl12022_rtc_ops = {
+ .read_time = isl12022_rtc_read_time,
+ .set_time = isl12022_rtc_set_time,
+};
+
+static int isl12022_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl12022 *isl12022;
+
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL);
+ if (!isl12022)
+ return -ENOMEM;
+
+ dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, isl12022);
+
+ isl12022->rtc = rtc_device_register(isl12022_driver.driver.name,
+ &client->dev,
+ &isl12022_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(isl12022->rtc)) {
+ ret = PTR_ERR(isl12022->rtc);
+ goto exit_kfree;
+ }
+
+ return 0;
+
+exit_kfree:
+ kfree(isl12022);
+
+ return ret;
+}
+
+static int isl12022_remove(struct i2c_client *client)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+
+ rtc_device_unregister(isl12022->rtc);
+ kfree(isl12022);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl12022_id[] = {
+ { "isl12022", 0 },
+ { "rtc8564", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl12022_id);
+
+static struct i2c_driver isl12022_driver = {
+ .driver = {
+ .name = "rtc-isl12022",
+ },
+ .probe = isl12022_probe,
+ .remove = isl12022_remove,
+ .id_table = isl12022_id,
+};
+
+static int __init isl12022_init(void)
+{
+ return i2c_add_driver(&isl12022_driver);
+}
+
+static void __exit isl12022_exit(void)
+{
+ i2c_del_driver(&isl12022_driver);
+}
+
+module_init(isl12022_init);
+module_exit(isl12022_exit);
+
+MODULE_AUTHOR("roman.fietze@telemotive.de");
+MODULE_DESCRIPTION("ISL 12022 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 6dc4e62..66377f3 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -121,7 +121,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* assume 20YY not 19YY, and ignore the Century Bit */
tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
- return 0;
+ return rtc_valid_tm(tm);
}
/* Sets the given date and time to the real time clock. */
@@ -364,7 +364,7 @@ static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t)
t->time.tm_isdst = -1;
t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
- return 0;
+ return rtc_valid_tm(t);
}
static struct rtc_class_ops m41t80_rtc_ops = {
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index be8359f..a99a0b5 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -105,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -196,7 +196,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
/*
@@ -506,7 +506,6 @@ out:
free_irq(m48t59->irq, &pdev->dev);
if (m48t59->ioaddr)
iounmap(m48t59->ioaddr);
- if (m48t59)
kfree(m48t59);
return ret;
}
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index 7c045cf..f981287 100644
--- a/drivers/rtc/rtc-m48t86.c
+++ b/drivers/rtc/rtc-m48t86.c
@@ -77,7 +77,7 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
tm->tm_hour += 12;
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
index a4f6665..486142c 100644
--- a/drivers/rtc/rtc-max6900.c
+++ b/drivers/rtc/rtc-max6900.c
@@ -159,7 +159,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]);
- return 0;
+ return rtc_valid_tm(tm);
}
static int max6900_i2c_clear_write_protect(struct i2c_client *client)
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index db5d8c4..dfcdf09 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -268,7 +268,7 @@ static const struct rtc_class_ops mpc5121_rtc_ops = {
.update_irq_enable = mpc5121_rtc_update_irq_enable,
};
-static int __devinit mpc5121_rtc_probe(struct of_device *op,
+static int __devinit mpc5121_rtc_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mpc5121_rtc_data *rtc;
@@ -338,7 +338,7 @@ out_free:
return err;
}
-static int __devexit mpc5121_rtc_remove(struct of_device *op)
+static int __devexit mpc5121_rtc_remove(struct platform_device *op)
{
struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 25ec921..0b06c1e 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -83,12 +83,6 @@ struct rtc_plat_data {
void __iomem *ioaddr;
int irq;
struct clk *clk;
- unsigned int irqen;
- int alrm_sec;
- int alrm_min;
- int alrm_hour;
- int alrm_mday;
- struct timespec mxc_rtc_delta;
struct rtc_time g_rtc_alarm;
};
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index a351bd5..62de66a 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -85,25 +85,24 @@ static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
{
- unsigned int i;
+ unsigned int timeout = 0x1000;
__raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
mdelay(10);
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
- for (i = 0; i < 1000; i++) {
- if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- return 0;
- }
+ while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
+ && timeout--)
+ mdelay(1);
- if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0)
- return ERR_PTR(-ENODEV);
+ if (!timeout)
+ return ERR_PTR(-EPERM);
- return ERR_PTR(-EPERM);
+ return 0;
}
-static void nuc900_rtc_bcd2bin(unsigned int timereg,
+static int nuc900_rtc_bcd2bin(unsigned int timereg,
unsigned int calreg, struct rtc_time *tm)
{
tm->tm_mday = bcd2bin(calreg >> 0);
@@ -114,15 +113,21 @@ static void nuc900_rtc_bcd2bin(unsigned int timereg,
tm->tm_min = bcd2bin(timereg >> 8);
tm->tm_hour = bcd2bin(timereg >> 16);
- rtc_valid_tm(tm);
+ return rtc_valid_tm(tm);
}
-static void nuc900_rtc_bin2bcd(struct rtc_time *settm,
+static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
struct nuc900_bcd_time *gettm)
{
gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
- gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+
+ if (settm->tm_year < 100) {
+ dev_warn(dev, "The year will be between 1970-1999, right?\n");
+ gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
+ } else {
+ gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+ }
gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
@@ -165,9 +170,7 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
- nuc900_rtc_bcd2bin(timeval, clrval, tm);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, clrval, tm);
}
static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -177,7 +180,7 @@ static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(tm, &gettm);
+ nuc900_rtc_bin2bcd(dev, tm, &gettm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -200,9 +203,7 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
- nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
}
static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -212,7 +213,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(&alrm->time, &tm);
+ nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -268,29 +269,30 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
goto fail2;
}
- nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
- if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
- IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
- dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
- err = -EBUSY;
- goto fail3;
- }
+ platform_set_drvdata(pdev, nuc900_rtc);
nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
&nuc900_rtc_ops, THIS_MODULE);
if (IS_ERR(nuc900_rtc->rtcdev)) {
dev_err(&pdev->dev, "rtc device register faild\n");
err = PTR_ERR(nuc900_rtc->rtcdev);
- goto fail4;
+ goto fail3;
}
- platform_set_drvdata(pdev, nuc900_rtc);
__raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
nuc900_rtc->rtc_reg + REG_RTC_TSSR);
+ nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
+ if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
+ IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
+ dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
+ err = -EBUSY;
+ goto fail4;
+ }
+
return 0;
-fail4: free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+fail4: rtc_device_unregister(nuc900_rtc->rtcdev);
fail3: iounmap(nuc900_rtc->rtc_reg);
fail2: release_mem_region(res->start, resource_size(res));
fail1: kfree(nuc900_rtc);
@@ -302,8 +304,8 @@ static int __devexit nuc900_rtc_remove(struct platform_device *pdev)
struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
struct resource *res;
- rtc_device_unregister(nuc900_rtc->rtcdev);
free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+ rtc_device_unregister(nuc900_rtc->rtcdev);
iounmap(nuc900_rtc->rtc_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 1af42b4..b42c0c6 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -172,14 +172,6 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return 0;
}
-struct pcf8563_limit
-{
- unsigned char reg;
- unsigned char mask;
- unsigned char min;
- unsigned char max;
-};
-
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 71bbefc..6c418fe 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -23,7 +23,6 @@
#include <linux/io.h>
#include <linux/bcd.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/slab.h>
/*
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index e9c6fa0..29e867a 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -87,7 +87,6 @@ struct pxa_rtc {
int irq_Alrm;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
- struct rtc_time rtc_alarm;
};
static u32 ryxr_calc(struct rtc_time *tm)
@@ -236,32 +235,34 @@ static int pxa_periodic_irq_set_state(struct device *dev, int enabled)
return 0;
}
-static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
- int ret = 0;
spin_lock_irq(&pxa_rtc->lock);
- switch (cmd) {
- case RTC_AIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_AIE_ON:
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_UIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_HZE);
- break;
- case RTC_UIE_ON:
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+ return 0;
+}
+
+static int pxa_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_HZE);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_HZE);
spin_unlock_irq(&pxa_rtc->lock);
- return ret;
+ return 0;
}
static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -340,11 +341,12 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)
static const struct rtc_class_ops pxa_rtc_ops = {
.open = pxa_rtc_open,
.release = pxa_rtc_release,
- .ioctl = pxa_rtc_ioctl,
.read_time = pxa_rtc_read_time,
.set_time = pxa_rtc_set_time,
.read_alarm = pxa_rtc_read_alarm,
.set_alarm = pxa_rtc_set_alarm,
+ .alarm_irq_enable = pxa_alarm_irq_enable,
+ .update_irq_enable = pxa_update_irq_enable,
.proc = pxa_rtc_proc,
.irq_set_state = pxa_periodic_irq_set_state,
.irq_set_freq = pxa_periodic_irq_set_freq,
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index a95f733..36eb661 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -63,6 +63,8 @@ enum {
struct rp5c01_priv {
u32 __iomem *regs;
struct rtc_device *rtc;
+ spinlock_t lock; /* against concurrent RTC/NVRAM access */
+ struct bin_attribute nvram_attr;
};
static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
@@ -92,6 +94,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
@@ -111,6 +114,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year += 100;
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return rtc_valid_tm(tm);
}
@@ -119,6 +123,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
@@ -139,6 +144,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return 0;
}
@@ -147,6 +153,72 @@ static const struct rtc_class_ops rp5c01_rtc_ops = {
.set_time = rp5c01_set_time,
};
+
+/*
+ * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits.
+ * We provide access to them like AmigaOS does: the high nibble of each 8-bit
+ * byte is stored in BLOCK10, the low nibble in BLOCK11.
+ */
+
+static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ data = rp5c01_read(priv, pos) << 4;
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ data |= rp5c01_read(priv, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ *buf++ = data;
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data = *buf++;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ rp5c01_write(priv, data >> 4, pos);
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ rp5c01_write(priv, data & 0xf, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
static int __init rp5c01_rtc_probe(struct platform_device *dev)
{
struct resource *res;
@@ -168,6 +240,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
goto out_free_priv;
}
+ sysfs_bin_attr_init(&priv->nvram_attr);
+ priv->nvram_attr.attr.name = "nvram";
+ priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ priv->nvram_attr.read = rp5c01_nvram_read;
+ priv->nvram_attr.write = rp5c01_nvram_write;
+ priv->nvram_attr.size = RP5C01_MODE;
+
+ spin_lock_init(&priv->lock);
+
rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -177,8 +258,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
priv->rtc = rtc;
platform_set_drvdata(dev, priv);
+
+ error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ if (error)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ rtc_device_unregister(rtc);
out_unmap:
iounmap(priv->regs);
out_free_priv:
@@ -190,6 +278,7 @@ static int __exit rp5c01_rtc_remove(struct platform_device *dev)
{
struct rp5c01_priv *priv = platform_get_drvdata(dev);
+ sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
rtc_device_unregister(priv->rtc);
iounmap(priv->regs);
kfree(priv);
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 70b68d3..a0d3ec8 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -1,5 +1,8 @@
/* drivers/rtc/rtc-s3c.c
*
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
* Copyright (c) 2004,2006 Simtec Electronics
* Ben Dooks, <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
@@ -39,6 +42,7 @@ enum s3c_cpu_type {
static struct resource *s3c_rtc_mem;
+static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
@@ -53,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -61,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -94,7 +106,7 @@ static int s3c_rtc_setpie(struct device *dev, int enabled)
if (enabled)
tmp |= S3C64XX_RTCCON_TICEN;
- writeb(tmp, s3c_rtc_base + S3C2410_RTCCON);
+ writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
} else {
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
tmp &= ~S3C2410_TICNT_ENABLE;
@@ -128,7 +140,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
tmp |= (rtc_dev->max_user_freq / freq)-1;
- writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
+ writel(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
@@ -431,6 +443,10 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+ rtc_clk = NULL;
+
iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);
@@ -442,6 +458,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
+ unsigned int tmp, i;
int ret;
pr_debug("%s: probe=%p\n", __func__, pdev);
@@ -488,6 +505,16 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
goto err_nomap;
}
+ rtc_clk = clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(rtc_clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock source\n");
+ ret = PTR_ERR(rtc_clk);
+ rtc_clk = NULL;
+ goto err_clk;
+ }
+
+ clk_enable(rtc_clk);
+
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
@@ -510,6 +537,15 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
+ /* Check RTC Time */
+
+ for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) {
+ tmp = readb(s3c_rtc_base + i);
+
+ if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9)
+ writeb(0, s3c_rtc_base + i);
+ }
+
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
rtc->max_user_freq = 32768;
else
@@ -523,6 +559,10 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
err_nortc:
s3c_rtc_enable(pdev, 0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+
+ err_clk:
iounmap(s3c_rtc_base);
err_nomap:
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 33975e9..8373ca0 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -21,6 +21,7 @@
#include <linux/hdreg.h>
#include <linux/async.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
@@ -1324,14 +1325,14 @@ static void __dasd_device_check_expire(struct dasd_device *device)
if (device->discipline->term_IO(cqr) != 0) {
/* Hmpf, try again in 5 sec */
dev_err(&device->cdev->dev,
- "cqr %p timed out (%is) but cannot be "
+ "cqr %p timed out (%lus) but cannot be "
"ended, retrying in 5 s\n",
cqr, (cqr->expires/HZ));
cqr->expires += 5*HZ;
dasd_device_set_timer(device, 5*HZ);
} else {
dev_err(&device->cdev->dev,
- "cqr %p timed out (%is), %i retries "
+ "cqr %p timed out (%lus), %i retries "
"remaining\n", cqr, (cqr->expires/HZ),
cqr->retries);
}
@@ -2196,7 +2197,7 @@ static void dasd_setup_queue(struct dasd_block *block)
*/
blk_queue_max_segment_size(block->request_queue, PAGE_SIZE);
blk_queue_segment_boundary(block->request_queue, PAGE_SIZE - 1);
- blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN);
}
/*
@@ -2235,6 +2236,7 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
if (!block)
return -ENODEV;
+ lock_kernel();
base = block->base;
atomic_inc(&block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
@@ -2269,12 +2271,14 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
goto out;
}
+ unlock_kernel();
return 0;
out:
module_put(base->discipline->owner);
unlock:
atomic_dec(&block->open_count);
+ unlock_kernel();
return rc;
}
@@ -2282,8 +2286,10 @@ static int dasd_release(struct gendisk *disk, fmode_t mode)
{
struct dasd_block *block = disk->private_data;
+ lock_kernel();
atomic_dec(&block->open_count);
module_put(block->base->discipline->owner);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index bed7b46..8d41f3e 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -1083,6 +1083,49 @@ dasd_eer_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
+/*
+ * expiration time for default requests
+ */
+static ssize_t
+dasd_expires_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct dasd_device *device;
+ int len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+ len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_expires);
+ dasd_put_device(device);
+ return len;
+}
+
+static ssize_t
+dasd_expires_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dasd_device *device;
+ unsigned long val;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+
+ if ((strict_strtoul(buf, 10, &val) != 0) ||
+ (val > DASD_EXPIRES_MAX) || val == 0) {
+ dasd_put_device(device);
+ return -EINVAL;
+ }
+
+ if (val)
+ device->default_expires = val;
+
+ dasd_put_device(device);
+ return count;
+}
+
+static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store);
+
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
@@ -1094,6 +1137,7 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_eer_enabled.attr,
&dev_attr_erplog.attr,
&dev_attr_failfast.attr,
+ &dev_attr_expires.attr,
NULL,
};
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 687f323..2b3bc3e 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -43,7 +43,7 @@ MODULE_LICENSE("GPL");
sizeof(struct dasd_diag_req)) / \
sizeof(struct dasd_diag_bio)) / 2)
#define DIAG_MAX_RETRIES 32
-#define DIAG_TIMEOUT 50 * HZ
+#define DIAG_TIMEOUT 50
static struct dasd_discipline dasd_diag_discipline;
@@ -360,6 +360,8 @@ dasd_diag_check_device(struct dasd_device *device)
goto out;
}
+ device->default_expires = DIAG_TIMEOUT;
+
/* Figure out position of label block */
switch (private->rdc_data.vdev_class) {
case DEV_CLASS_FBA:
@@ -563,7 +565,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
- cqr->expires = DIAG_TIMEOUT;
+ cqr->expires = memdev->default_expires * HZ;
cqr->status = DASD_CQR_FILLED;
return cqr;
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index ab84da5..66360c2 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -82,6 +82,14 @@ static struct ccw_driver dasd_eckd_driver; /* see below */
#define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2
+/* emergency request for reserve/release */
+static struct {
+ struct dasd_ccw_req cqr;
+ struct ccw1 ccw;
+ char data[32];
+} *dasd_reserve_req;
+static DEFINE_MUTEX(dasd_reserve_mutex);
+
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
@@ -1107,8 +1115,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
struct dasd_eckd_private *private;
struct dasd_block *block;
struct dasd_uid temp_uid;
- int is_known, rc;
+ int is_known, rc, i;
int readonly;
+ unsigned long value;
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev,
@@ -1143,6 +1152,18 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err1;
+ /* set default timeout */
+ device->default_expires = DASD_EXPIRES;
+ if (private->gneq) {
+ value = 1;
+ for (i = 0; i < private->gneq->timeout.value; i++)
+ value = 10 * value;
+ value = value * private->gneq->timeout.number;
+ /* do not accept useless values */
+ if (value != 0 && value <= DASD_EXPIRES_MAX)
+ device->default_expires = value;
+ }
+
/* Generate device unique id */
rc = dasd_eckd_generate_uid(device);
if (rc)
@@ -1973,7 +1994,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@@ -2150,7 +2171,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@@ -2398,7 +2419,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@@ -2645,15 +2666,23 @@ dasd_eckd_release(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RELEASE;
@@ -2671,7 +2700,10 @@ dasd_eckd_release(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -2687,15 +2719,23 @@ dasd_eckd_reserve(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RESERVE;
@@ -2713,7 +2753,10 @@ dasd_eckd_reserve(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -2728,15 +2771,23 @@ dasd_eckd_steal_lock(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_SLCK;
@@ -2754,7 +2805,10 @@ dasd_eckd_steal_lock(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -3488,10 +3542,15 @@ dasd_eckd_init(void)
int ret;
ASCEBC(dasd_eckd_discipline.ebcname, 4);
+ dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
+ GFP_KERNEL | GFP_DMA);
+ if (!dasd_reserve_req)
+ return -ENOMEM;
ret = ccw_driver_register(&dasd_eckd_driver);
if (!ret)
wait_for_device_probe();
-
+ else
+ kfree(dasd_reserve_req);
return ret;
}
@@ -3499,6 +3558,7 @@ static void __exit
dasd_eckd_cleanup(void)
{
ccw_driver_unregister(&dasd_eckd_driver);
+ kfree(dasd_reserve_req);
}
module_init(dasd_eckd_init);
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index dd6385a..0eb4965 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -320,7 +320,12 @@ struct dasd_gneq {
__u8 identifier:2;
__u8 reserved:6;
} __attribute__ ((packed)) flags;
- __u8 reserved[7];
+ __u8 reserved[5];
+ struct {
+ __u8 value:2;
+ __u8 number:6;
+ } __attribute__ ((packed)) timeout;
+ __u8 reserved3;
__u16 subsystemID;
__u8 reserved2[22];
} __attribute__ ((packed));
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 37282b9..bec5486e 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -163,6 +163,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
return rc;
}
+ device->default_expires = DASD_EXPIRES;
+
readonly = dasd_device_is_ro(device);
if (readonly)
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
@@ -370,7 +372,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */
cqr->retries = 32;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 49b431d..500678d 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -186,7 +186,7 @@ struct dasd_ccw_req {
/* ... and how */
unsigned long starttime; /* jiffies time of request start */
- int expires; /* expiration period in jiffies */
+ unsigned long expires; /* expiration period in jiffies */
char lpm; /* logical path mask */
void *data; /* pointer to data area */
@@ -224,6 +224,9 @@ struct dasd_ccw_req {
#define DASD_CQR_CLEARED 0x84 /* request was cleared */
#define DASD_CQR_SUCCESS 0x85 /* request was successful */
+/* default expiration time*/
+#define DASD_EXPIRES 300
+#define DASD_EXPIRES_MAX 40000000
/* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
@@ -404,6 +407,9 @@ struct dasd_device {
/* hook for alias management */
struct list_head alias_list;
+
+ /* default expiration time in s */
+ unsigned long default_expires;
};
struct dasd_block {
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 9b43ae9..2bd72aa 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
@@ -775,6 +776,7 @@ dcssblk_open(struct block_device *bdev, fmode_t mode)
struct dcssblk_dev_info *dev_info;
int rc;
+ lock_kernel();
dev_info = bdev->bd_disk->private_data;
if (NULL == dev_info) {
rc = -ENODEV;
@@ -784,6 +786,7 @@ dcssblk_open(struct block_device *bdev, fmode_t mode)
bdev->bd_block_size = 4096;
rc = 0;
out:
+ unlock_kernel();
return rc;
}
@@ -794,6 +797,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
struct segment_info *entry;
int rc;
+ lock_kernel();
if (!dev_info) {
rc = -ENODEV;
goto out;
@@ -811,6 +815,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
up_write(&dcssblk_devices_sem);
rc = 0;
out:
+ unlock_kernel();
return rc;
}
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 097da8c..b7de025 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/buffer_head.h>
#include <linux/kernel.h>
@@ -361,6 +362,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
struct tape_device * device;
int rc;
+ lock_kernel();
device = tape_get_device(disk->private_data);
if (device->required_tapemarks) {
@@ -384,12 +386,14 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
* is called.
*/
tape_state_set(device, TS_BLKUSE);
+ unlock_kernel();
return 0;
release:
tape_release(device);
put_device:
tape_put_device(device);
+ unlock_kernel();
return rc;
}
@@ -403,10 +407,12 @@ static int
tapeblock_release(struct gendisk *disk, fmode_t mode)
{
struct tape_device *device = disk->private_data;
-
+
+ lock_kernel();
tape_state_set(device, TS_IN_USE);
tape_release(device);
tape_put_device(device);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c
index 7f206ed..d15f8b4 100644
--- a/drivers/s390/cio/ccwreq.c
+++ b/drivers/s390/cio/ccwreq.c
@@ -38,9 +38,13 @@ static u16 ccwreq_next_path(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
+ if (!req->singlepath) {
+ req->mask = 0;
+ goto out;
+ }
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask >>= 1, req->lpm);
-
+out:
return req->mask;
}
@@ -113,8 +117,12 @@ void ccw_request_start(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
- /* Try all paths twice to counter link flapping. */
- req->mask = 0x8080;
+ if (req->singlepath) {
+ /* Try all paths twice to counter link flapping. */
+ req->mask = 0x8080;
+ } else
+ req->mask = req->lpm;
+
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask, req->lpm);
req->drc = 0;
@@ -182,6 +190,8 @@ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
/* Ask the driver what to do */
if (cdev->drv && cdev->drv->uc_handler) {
todo = cdev->drv->uc_handler(cdev, lcirb);
+ CIO_TRACE_EVENT(2, "uc_response");
+ CIO_HEX_EVENT(2, &todo, sizeof(todo));
switch (todo) {
case UC_TODO_RETRY:
return IO_STATUS_ERROR;
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 407d0e9..4cbb1a6 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -29,6 +29,7 @@
#include "chsc.h"
static void *sei_page;
+static DEFINE_SPINLOCK(siosl_lock);
static DEFINE_SPINLOCK(sda_lock);
/**
@@ -48,6 +49,7 @@ int chsc_error_from_response(int response)
case 0x0007:
case 0x0008:
case 0x000a:
+ case 0x0104:
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
@@ -974,3 +976,49 @@ int chsc_sstpi(void *page, void *result, size_t size)
return (rr->response.code == 0x0001) ? 0 : -EIO;
}
+static struct {
+ struct chsc_header request;
+ u32 word1;
+ struct subchannel_id sid;
+ u32 word3;
+ struct chsc_header response;
+ u32 word[11];
+} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE)));
+
+int chsc_siosl(struct subchannel_id schid)
+{
+ unsigned long flags;
+ int ccode;
+ int rc;
+
+ spin_lock_irqsave(&siosl_lock, flags);
+ memset(&siosl_area, 0, sizeof(siosl_area));
+ siosl_area.request.length = 0x0010;
+ siosl_area.request.code = 0x0046;
+ siosl_area.word1 = 0x80000000;
+ siosl_area.sid = schid;
+
+ ccode = chsc(&siosl_area);
+ if (ccode > 0) {
+ if (ccode == 3)
+ rc = -ENODEV;
+ else
+ rc = -EBUSY;
+ CIO_MSG_EVENT(2, "chsc: chsc failed for 0.%x.%04x (ccode=%d)\n",
+ schid.ssid, schid.sch_no, ccode);
+ goto out;
+ }
+ rc = chsc_error_from_response(siosl_area.response.code);
+ if (rc)
+ CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n",
+ schid.ssid, schid.sch_no,
+ siosl_area.response.code);
+ else
+ CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n",
+ schid.ssid, schid.sch_no);
+out:
+ spin_unlock_irqrestore(&siosl_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(chsc_siosl);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 37aa611..5453013 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -80,4 +80,6 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp);
int chsc_error_from_response(int response);
+int chsc_siosl(struct subchannel_id schid);
+
#endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 6d229f3..51bd368 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -36,6 +36,7 @@
#include "ioasm.h"
#include "io_sch.h"
#include "blacklist.h"
+#include "chsc.h"
static struct timer_list recovery_timer;
static DEFINE_SPINLOCK(recovery_lock);
@@ -486,9 +487,11 @@ static int online_store_handle_offline(struct ccw_device *cdev)
spin_lock_irq(cdev->ccwlock);
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
spin_unlock_irq(cdev->ccwlock);
- } else if (cdev->online && cdev->drv && cdev->drv->set_offline)
+ return 0;
+ }
+ if (cdev->drv && cdev->drv->set_offline)
return ccw_device_set_offline(cdev);
- return 0;
+ return -EINVAL;
}
static int online_store_recog_and_online(struct ccw_device *cdev)
@@ -505,8 +508,8 @@ static int online_store_recog_and_online(struct ccw_device *cdev)
return -EAGAIN;
}
if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
- return 0;
+ return ccw_device_set_online(cdev);
+ return -EINVAL;
}
static int online_store_handle_online(struct ccw_device *cdev, int force)
@@ -598,6 +601,25 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf)
}
}
+static ssize_t
+initiate_logging(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ int rc;
+
+ rc = chsc_siosl(sch->schid);
+ if (rc < 0) {
+ pr_warning("Logging for subchannel 0.%x.%04x failed with "
+ "errno=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, rc);
+ return rc;
+ }
+ pr_notice("Logging for subchannel 0.%x.%04x was triggered\n",
+ sch->schid.ssid, sch->schid.sch_no);
+ return count;
+}
+
static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
@@ -605,10 +627,12 @@ static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
+static DEVICE_ATTR(logging, 0200, NULL, initiate_logging);
static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr,
&dev_attr_pimpampom.attr,
+ &dev_attr_logging.attr,
NULL,
};
@@ -2036,6 +2060,21 @@ void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
}
}
+/**
+ * ccw_device_siosl() - initiate logging
+ * @cdev: ccw device
+ *
+ * This function is used to invoke model-dependent logging within the channel
+ * subsystem.
+ */
+int ccw_device_siosl(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ return chsc_siosl(sch->schid);
+}
+EXPORT_SYMBOL_GPL(ccw_device_siosl);
+
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 6facb54..82a5ad0 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -208,6 +208,7 @@ static void spid_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
+ req->singlepath = 1;
req->callback = spid_callback;
spid_do(cdev);
}
@@ -420,6 +421,7 @@ static void verify_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
+ req->singlepath = 1;
if (cdev->private->flags.pgroup) {
CIO_TRACE_EVENT(4, "snid");
CIO_HEX_EVENT(4, devid, sizeof(*devid));
@@ -507,6 +509,7 @@ void ccw_device_disband_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = sch->schib.pmcw.pam & sch->opm;
+ req->singlepath = 1;
req->callback = disband_callback;
fn = SPID_FUNC_DISBAND;
if (cdev->private->flags.mpath)
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index b9ce712..469ef93 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -92,11 +92,12 @@ enum io_status {
* @filter: optional callback to adjust request status based on IRB data
* @callback: final callback
* @data: user-defined pointer passed to all callbacks
+ * @singlepath: if set, use only one path from @lpm per start I/O
+ * @cancel: non-zero if request was cancelled
+ * @done: non-zero if request was finished
* @mask: current path mask
* @retries: current number of retries
* @drc: delayed return code
- * @cancel: non-zero if request was cancelled
- * @done: non-zero if request was finished
*/
struct ccw_request {
struct ccw1 *cp;
@@ -108,12 +109,13 @@ struct ccw_request {
enum io_status);
void (*callback)(struct ccw_device *, void *, int);
void *data;
+ unsigned int singlepath:1;
/* These fields are used internally. */
+ unsigned int cancel:1;
+ unsigned int done:1;
u16 mask;
u16 retries;
int drc;
- int cancel:1;
- int done:1;
} __attribute__((packed));
/*
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index a75ed30..8e4153d 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -386,7 +386,7 @@ claw_tx(struct sk_buff *skb, struct net_device *dev)
struct chbk *p_ch;
CLAW_DBF_TEXT(4, trace, "claw_tx");
- p_ch=&privptr->channel[WRITE];
+ p_ch = &privptr->channel[WRITE_CHANNEL];
spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
rc=claw_hw_tx( skb, dev, 1 );
spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
@@ -407,7 +407,7 @@ static struct sk_buff *
claw_pack_skb(struct claw_privbk *privptr)
{
struct sk_buff *new_skb,*held_skb;
- struct chbk *p_ch = &privptr->channel[WRITE];
+ struct chbk *p_ch = &privptr->channel[WRITE_CHANNEL];
struct claw_env *p_env = privptr->p_env;
int pkt_cnt,pk_ind,so_far;
@@ -515,15 +515,15 @@ claw_open(struct net_device *dev)
privptr->p_env->write_size=CLAW_FRAME_SIZE;
}
claw_set_busy(dev);
- tasklet_init(&privptr->channel[READ].tasklet, claw_irq_tasklet,
- (unsigned long) &privptr->channel[READ]);
+ tasklet_init(&privptr->channel[READ_CHANNEL].tasklet, claw_irq_tasklet,
+ (unsigned long) &privptr->channel[READ_CHANNEL]);
for ( i = 0; i < 2; i++) {
CLAW_DBF_TEXT_(2, trace, "opn_ch%d", i);
init_waitqueue_head(&privptr->channel[i].wait);
/* skb_queue_head_init(&p_ch->io_queue); */
- if (i == WRITE)
+ if (i == WRITE_CHANNEL)
skb_queue_head_init(
- &privptr->channel[WRITE].collect_queue);
+ &privptr->channel[WRITE_CHANNEL].collect_queue);
privptr->channel[i].flag_a = 0;
privptr->channel[i].IO_active = 0;
privptr->channel[i].flag &= ~CLAW_TIMER;
@@ -551,12 +551,12 @@ claw_open(struct net_device *dev)
if((privptr->channel[i].flag & CLAW_TIMER) == 0x00)
del_timer(&timer);
}
- if ((((privptr->channel[READ].last_dstat |
- privptr->channel[WRITE].last_dstat) &
+ if ((((privptr->channel[READ_CHANNEL].last_dstat |
+ privptr->channel[WRITE_CHANNEL].last_dstat) &
~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) ||
- (((privptr->channel[READ].flag |
- privptr->channel[WRITE].flag) & CLAW_TIMER) != 0x00)) {
- dev_info(&privptr->channel[READ].cdev->dev,
+ (((privptr->channel[READ_CHANNEL].flag |
+ privptr->channel[WRITE_CHANNEL].flag) & CLAW_TIMER) != 0x00)) {
+ dev_info(&privptr->channel[READ_CHANNEL].cdev->dev,
"%s: remote side is not ready\n", dev->name);
CLAW_DBF_TEXT(2, trace, "notrdy");
@@ -608,8 +608,8 @@ claw_open(struct net_device *dev)
}
}
privptr->buffs_alloc = 0;
- privptr->channel[READ].flag= 0x00;
- privptr->channel[WRITE].flag = 0x00;
+ privptr->channel[READ_CHANNEL].flag = 0x00;
+ privptr->channel[WRITE_CHANNEL].flag = 0x00;
privptr->p_buff_ccw=NULL;
privptr->p_buff_read=NULL;
privptr->p_buff_write=NULL;
@@ -652,10 +652,10 @@ claw_irq_handler(struct ccw_device *cdev,
}
/* Try to extract channel from driver data. */
- if (privptr->channel[READ].cdev == cdev)
- p_ch = &privptr->channel[READ];
- else if (privptr->channel[WRITE].cdev == cdev)
- p_ch = &privptr->channel[WRITE];
+ if (privptr->channel[READ_CHANNEL].cdev == cdev)
+ p_ch = &privptr->channel[READ_CHANNEL];
+ else if (privptr->channel[WRITE_CHANNEL].cdev == cdev)
+ p_ch = &privptr->channel[WRITE_CHANNEL];
else {
dev_warn(&cdev->dev, "The device is not a CLAW device\n");
CLAW_DBF_TEXT(2, trace, "badchan");
@@ -813,7 +813,7 @@ claw_irq_handler(struct ccw_device *cdev,
claw_clearbit_busy(TB_TX, dev);
claw_clear_busy(dev);
}
- p_ch_r = (struct chbk *)&privptr->channel[READ];
+ p_ch_r = (struct chbk *)&privptr->channel[READ_CHANNEL];
if (test_and_set_bit(CLAW_BH_ACTIVE,
(void *)&p_ch_r->flag_a) == 0)
tasklet_schedule(&p_ch_r->tasklet);
@@ -878,13 +878,13 @@ claw_release(struct net_device *dev)
for ( i = 1; i >=0 ; i--) {
spin_lock_irqsave(
get_ccwdev_lock(privptr->channel[i].cdev), saveflags);
- /* del_timer(&privptr->channel[READ].timer); */
+ /* del_timer(&privptr->channel[READ_CHANNEL].timer); */
privptr->channel[i].claw_state = CLAW_STOP;
privptr->channel[i].IO_active = 0;
parm = (unsigned long) &privptr->channel[i];
- if (i == WRITE)
+ if (i == WRITE_CHANNEL)
claw_purge_skb_queue(
- &privptr->channel[WRITE].collect_queue);
+ &privptr->channel[WRITE_CHANNEL].collect_queue);
rc = ccw_device_halt (privptr->channel[i].cdev, parm);
if (privptr->system_validate_comp==0x00) /* never opened? */
init_waitqueue_head(&privptr->channel[i].wait);
@@ -971,16 +971,16 @@ claw_release(struct net_device *dev)
privptr->mtc_skipping = 1;
privptr->mtc_offset=0;
- if (((privptr->channel[READ].last_dstat |
- privptr->channel[WRITE].last_dstat) &
+ if (((privptr->channel[READ_CHANNEL].last_dstat |
+ privptr->channel[WRITE_CHANNEL].last_dstat) &
~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) {
- dev_warn(&privptr->channel[READ].cdev->dev,
+ dev_warn(&privptr->channel[READ_CHANNEL].cdev->dev,
"Deactivating %s completed with incorrect"
" subchannel status "
"(read %02x, write %02x)\n",
dev->name,
- privptr->channel[READ].last_dstat,
- privptr->channel[WRITE].last_dstat);
+ privptr->channel[READ_CHANNEL].last_dstat,
+ privptr->channel[WRITE_CHANNEL].last_dstat);
CLAW_DBF_TEXT(2, trace, "badclose");
}
CLAW_DBF_TEXT(4, trace, "rlsexit");
@@ -1324,7 +1324,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
CLAW_DBF_TEXT(4, trace, "hw_tx");
privptr = (struct claw_privbk *)(dev->ml_priv);
- p_ch=(struct chbk *)&privptr->channel[WRITE];
+ p_ch = (struct chbk *)&privptr->channel[WRITE_CHANNEL];
p_env =privptr->p_env;
claw_free_wrt_buf(dev); /* Clean up free chain if posible */
/* scan the write queue to free any completed write packets */
@@ -1357,7 +1357,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
claw_strt_out_IO(dev );
claw_free_wrt_buf( dev );
if (privptr->write_free_count==0) {
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
goto Done;
@@ -1369,7 +1369,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
}
/* tx lock */
if (claw_test_and_setbit_busy(TB_TX,dev)) { /* set to busy */
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
claw_strt_out_IO(dev );
@@ -1385,7 +1385,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
privptr->p_write_free_chain == NULL ) {
claw_setbit_busy(TB_NOBUFFER,dev);
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
CLAW_DBF_TEXT(2, trace, "clawbusy");
@@ -1397,7 +1397,7 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
while (len_of_data > 0) {
p_this_ccw=privptr->p_write_free_chain; /* get a block */
if (p_this_ccw == NULL) { /* lost the race */
- ch = &privptr->channel[WRITE];
+ ch = &privptr->channel[WRITE_CHANNEL];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
goto Done2;
@@ -2067,7 +2067,7 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
*catch up to each other */
privptr = dev->ml_priv;
p_env=privptr->p_env;
- tdev = &privptr->channel[READ].cdev->dev;
+ tdev = &privptr->channel[READ_CHANNEL].cdev->dev;
memcpy( &temp_host_name, p_env->host_name, 8);
memcpy( &temp_ws_name, p_env->adapter_name , 8);
dev_info(tdev, "%s: CLAW device %.8s: "
@@ -2245,7 +2245,7 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
dev->name, temp_ws_name,
p_ctlbk->linkid);
privptr->active_link_ID = p_ctlbk->linkid;
- p_ch = &privptr->channel[WRITE];
+ p_ch = &privptr->channel[WRITE_CHANNEL];
wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */
break;
case CONNECTION_RESPONSE:
@@ -2296,7 +2296,7 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
"%s: Confirmed Now packing\n", dev->name);
p_env->packing = DO_PACKED;
}
- p_ch = &privptr->channel[WRITE];
+ p_ch = &privptr->channel[WRITE_CHANNEL];
wake_up(&p_ch->wait);
} else {
dev_warn(tdev, "Activating %s failed because of"
@@ -2556,7 +2556,7 @@ unpack_read(struct net_device *dev )
p_packd=NULL;
privptr = dev->ml_priv;
- p_dev = &privptr->channel[READ].cdev->dev;
+ p_dev = &privptr->channel[READ_CHANNEL].cdev->dev;
p_env = privptr->p_env;
p_this_ccw=privptr->p_read_active_first;
while (p_this_ccw!=NULL && p_this_ccw->header.flag!=CLAW_PENDING) {
@@ -2728,7 +2728,7 @@ claw_strt_read (struct net_device *dev, int lock )
struct ccwbk*p_ccwbk;
struct chbk *p_ch;
struct clawh *p_clawh;
- p_ch=&privptr->channel[READ];
+ p_ch = &privptr->channel[READ_CHANNEL];
CLAW_DBF_TEXT(4, trace, "StRdNter");
p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
@@ -2782,7 +2782,7 @@ claw_strt_out_IO( struct net_device *dev )
return;
}
privptr = (struct claw_privbk *)dev->ml_priv;
- p_ch=&privptr->channel[WRITE];
+ p_ch = &privptr->channel[WRITE_CHANNEL];
CLAW_DBF_TEXT(4, trace, "strt_io");
p_first_ccw=privptr->p_write_active_first;
@@ -2875,7 +2875,7 @@ claw_free_netdevice(struct net_device * dev, int free_dev)
if (dev->flags & IFF_RUNNING)
claw_release(dev);
if (privptr) {
- privptr->channel[READ].ndev = NULL; /* say it's free */
+ privptr->channel[READ_CHANNEL].ndev = NULL; /* say it's free */
}
dev->ml_priv = NULL;
#ifdef MODULE
@@ -2960,18 +2960,18 @@ claw_new_device(struct ccwgroup_device *cgdev)
struct ccw_dev_id dev_id;
dev_info(&cgdev->dev, "add for %s\n",
- dev_name(&cgdev->cdev[READ]->dev));
+ dev_name(&cgdev->cdev[READ_CHANNEL]->dev));
CLAW_DBF_TEXT(2, setup, "new_dev");
privptr = dev_get_drvdata(&cgdev->dev);
- dev_set_drvdata(&cgdev->cdev[READ]->dev, privptr);
- dev_set_drvdata(&cgdev->cdev[WRITE]->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[READ_CHANNEL]->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[WRITE_CHANNEL]->dev, privptr);
if (!privptr)
return -ENODEV;
p_env = privptr->p_env;
- ccw_device_get_id(cgdev->cdev[READ], &dev_id);
- p_env->devno[READ] = dev_id.devno;
- ccw_device_get_id(cgdev->cdev[WRITE], &dev_id);
- p_env->devno[WRITE] = dev_id.devno;
+ ccw_device_get_id(cgdev->cdev[READ_CHANNEL], &dev_id);
+ p_env->devno[READ_CHANNEL] = dev_id.devno;
+ ccw_device_get_id(cgdev->cdev[WRITE_CHANNEL], &dev_id);
+ p_env->devno[WRITE_CHANNEL] = dev_id.devno;
ret = add_channel(cgdev->cdev[0],0,privptr);
if (ret == 0)
ret = add_channel(cgdev->cdev[1],1,privptr);
@@ -2980,14 +2980,14 @@ claw_new_device(struct ccwgroup_device *cgdev)
" failed with error code %d\n", ret);
goto out;
}
- ret = ccw_device_set_online(cgdev->cdev[READ]);
+ ret = ccw_device_set_online(cgdev->cdev[READ_CHANNEL]);
if (ret != 0) {
dev_warn(&cgdev->dev,
"Setting the read subchannel online"
" failed with error code %d\n", ret);
goto out;
}
- ret = ccw_device_set_online(cgdev->cdev[WRITE]);
+ ret = ccw_device_set_online(cgdev->cdev[WRITE_CHANNEL]);
if (ret != 0) {
dev_warn(&cgdev->dev,
"Setting the write subchannel online "
@@ -3002,8 +3002,8 @@ claw_new_device(struct ccwgroup_device *cgdev)
}
dev->ml_priv = privptr;
dev_set_drvdata(&cgdev->dev, privptr);
- dev_set_drvdata(&cgdev->cdev[READ]->dev, privptr);
- dev_set_drvdata(&cgdev->cdev[WRITE]->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[READ_CHANNEL]->dev, privptr);
+ dev_set_drvdata(&cgdev->cdev[WRITE_CHANNEL]->dev, privptr);
/* sysfs magic */
SET_NETDEV_DEV(dev, &cgdev->dev);
if (register_netdev(dev) != 0) {
@@ -3021,16 +3021,16 @@ claw_new_device(struct ccwgroup_device *cgdev)
goto out;
}
}
- privptr->channel[READ].ndev = dev;
- privptr->channel[WRITE].ndev = dev;
+ privptr->channel[READ_CHANNEL].ndev = dev;
+ privptr->channel[WRITE_CHANNEL].ndev = dev;
privptr->p_env->ndev = dev;
dev_info(&cgdev->dev, "%s:readsize=%d writesize=%d "
"readbuffer=%d writebuffer=%d read=0x%04x write=0x%04x\n",
dev->name, p_env->read_size,
p_env->write_size, p_env->read_buffers,
- p_env->write_buffers, p_env->devno[READ],
- p_env->devno[WRITE]);
+ p_env->write_buffers, p_env->devno[READ_CHANNEL],
+ p_env->devno[WRITE_CHANNEL]);
dev_info(&cgdev->dev, "%s:host_name:%.8s, adapter_name "
":%.8s api_type: %.8s\n",
dev->name, p_env->host_name,
@@ -3072,10 +3072,10 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)
priv = dev_get_drvdata(&cgdev->dev);
if (!priv)
return -ENODEV;
- ndev = priv->channel[READ].ndev;
+ ndev = priv->channel[READ_CHANNEL].ndev;
if (ndev) {
/* Close the device */
- dev_info(&cgdev->dev, "%s: shutting down \n",
+ dev_info(&cgdev->dev, "%s: shutting down\n",
ndev->name);
if (ndev->flags & IFF_RUNNING)
ret = claw_release(ndev);
@@ -3083,8 +3083,8 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)
unregister_netdev(ndev);
ndev->ml_priv = NULL; /* cgdev data, not ndev's to free */
claw_free_netdevice(ndev, 1);
- priv->channel[READ].ndev = NULL;
- priv->channel[WRITE].ndev = NULL;
+ priv->channel[READ_CHANNEL].ndev = NULL;
+ priv->channel[WRITE_CHANNEL].ndev = NULL;
priv->p_env->ndev = NULL;
}
ccw_device_set_offline(cgdev->cdev[1]);
@@ -3115,8 +3115,8 @@ claw_remove_device(struct ccwgroup_device *cgdev)
priv->channel[1].irb=NULL;
kfree(priv);
dev_set_drvdata(&cgdev->dev, NULL);
- dev_set_drvdata(&cgdev->cdev[READ]->dev, NULL);
- dev_set_drvdata(&cgdev->cdev[WRITE]->dev, NULL);
+ dev_set_drvdata(&cgdev->cdev[READ_CHANNEL]->dev, NULL);
+ dev_set_drvdata(&cgdev->cdev[WRITE_CHANNEL]->dev, NULL);
put_device(&cgdev->dev);
return;
diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h
index 46d59a13..1bc5904 100644
--- a/drivers/s390/net/claw.h
+++ b/drivers/s390/net/claw.h
@@ -74,8 +74,8 @@
#define MAX_ENVELOPE_SIZE 65536
#define CLAW_DEFAULT_MTU_SIZE 4096
#define DEF_PACK_BUFSIZE 32768
-#define READ 0
-#define WRITE 1
+#define READ_CHANNEL 0
+#define WRITE_CHANNEL 1
#define TB_TX 0 /* sk buffer handling in process */
#define TB_STOP 1 /* network device stop in process */
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 70eb7f1..8c921fc 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -454,7 +454,7 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg)
if ((fsmstate == CTC_STATE_SETUPWAIT) &&
(ch->protocol == CTCM_PROTO_OS390)) {
/* OS/390 resp. z/OS */
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
*((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC,
CTC_EVENT_TIMER, ch);
@@ -472,14 +472,14 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg)
* if in compatibility mode, since VM TCP delays the initial
* frame until it has some data to send.
*/
- if ((CHANNEL_DIRECTION(ch->flags) == WRITE) ||
+ if ((CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE) ||
(ch->protocol != CTCM_PROTO_S390))
fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
*((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
ch->ccw[1].count = 2; /* Transfer only length */
- fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
+ fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
rc = ccw_device_start(ch->cdev, &ch->ccw[0],
(unsigned long)ch, 0xff, 0);
@@ -495,7 +495,7 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg)
* reply from VM TCP which brings up the RX channel to it's
* final state.
*/
- if ((CHANNEL_DIRECTION(ch->flags) == READ) &&
+ if ((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) &&
(ch->protocol == CTCM_PROTO_S390)) {
struct net_device *dev = ch->netdev;
struct ctcm_priv *priv = dev->ml_priv;
@@ -600,15 +600,15 @@ static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
int rc;
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s",
- CTCM_FUNTAIL, ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ CTCM_FUNTAIL, ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX");
if (ch->trans_skb != NULL) {
clear_normalized_cda(&ch->ccw[1]);
dev_kfree_skb(ch->trans_skb);
ch->trans_skb = NULL;
}
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
ch->ccw[1].cmd_code = CCW_CMD_READ;
ch->ccw[1].flags = CCW_FLAG_SLI;
ch->ccw[1].count = 0;
@@ -622,7 +622,8 @@ static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
"%s(%s): %s trans_skb alloc delayed "
"until first transfer",
CTCM_FUNTAIL, ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
+ "RX" : "TX");
}
ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
@@ -720,7 +721,7 @@ static void ctcm_chx_cleanup(fsm_instance *fi, int state,
ch->th_seg = 0x00;
ch->th_seq_num = 0x00;
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
skb_queue_purge(&ch->io_queue);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
} else {
@@ -799,7 +800,8 @@ static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, CTC_STATE_STARTRETRY);
fsm_deltimer(&ch->timer);
fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
- if (!IS_MPC(ch) && (CHANNEL_DIRECTION(ch->flags) == READ)) {
+ if (!IS_MPC(ch) &&
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)) {
int rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
if (rc != 0)
ctcm_ccw_check_rc(ch, rc,
@@ -811,10 +813,10 @@ static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
"%s(%s) : %s error during %s channel setup state=%s\n",
CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event],
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX",
fsm_getstate_str(fi));
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
fsm_newstate(fi, CTC_STATE_RXERR);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
} else {
@@ -945,7 +947,7 @@ static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg)
fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
fsm_newstate(fi, CTC_STATE_DTERM);
- ch2 = priv->channel[WRITE];
+ ch2 = priv->channel[CTCM_WRITE];
fsm_newstate(ch2->fsm, CTC_STATE_DTERM);
ccw_device_halt(ch->cdev, (unsigned long)ch);
@@ -1074,13 +1076,13 @@ static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg)
fsm_deltimer(&ch->timer);
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s: %s: %s unrecoverable channel error",
- CTCM_FUNTAIL, ch->id, rd == READ ? "RX" : "TX");
+ CTCM_FUNTAIL, ch->id, rd == CTCM_READ ? "RX" : "TX");
if (IS_MPC(ch)) {
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
}
- if (rd == READ) {
+ if (rd == CTCM_READ) {
fsm_newstate(fi, CTC_STATE_RXERR);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
} else {
@@ -1503,7 +1505,7 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
switch (fsm_getstate(fi)) {
case CTC_STATE_STARTRETRY:
case CTC_STATE_SETUPWAIT:
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
ctcmpc_chx_rxidle(fi, event, arg);
} else {
fsm_newstate(fi, CTC_STATE_TXIDLE);
@@ -1514,7 +1516,7 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
break;
};
- fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
+ fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
done:
@@ -1753,8 +1755,8 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
struct net_device *dev = ach->netdev;
struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- struct channel *wch = priv->channel[WRITE];
- struct channel *rch = priv->channel[READ];
+ struct channel *wch = priv->channel[CTCM_WRITE];
+ struct channel *rch = priv->channel[CTCM_READ];
struct sk_buff *skb;
struct th_sweep *header;
int rc = 0;
@@ -2070,7 +2072,7 @@ static void dev_action_start(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
if (IS_MPC(priv))
priv->mpcg->channels_terminating = 0;
- for (direction = READ; direction <= WRITE; direction++) {
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
struct channel *ch = priv->channel[direction];
fsm_event(ch->fsm, CTC_EVENT_START, ch);
}
@@ -2092,7 +2094,7 @@ static void dev_action_stop(fsm_instance *fi, int event, void *arg)
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
struct channel *ch = priv->channel[direction];
fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
ch->th_seq_num = 0x00;
@@ -2183,11 +2185,11 @@ static void dev_action_chup(fsm_instance *fi, int event, void *arg)
if (IS_MPC(priv)) {
if (event == DEV_EVENT_RXUP)
- mpc_channel_action(priv->channel[READ],
- READ, MPC_CHANNEL_ADD);
+ mpc_channel_action(priv->channel[CTCM_READ],
+ CTCM_READ, MPC_CHANNEL_ADD);
else
- mpc_channel_action(priv->channel[WRITE],
- WRITE, MPC_CHANNEL_ADD);
+ mpc_channel_action(priv->channel[CTCM_WRITE],
+ CTCM_WRITE, MPC_CHANNEL_ADD);
}
}
@@ -2239,11 +2241,11 @@ static void dev_action_chdown(fsm_instance *fi, int event, void *arg)
}
if (IS_MPC(priv)) {
if (event == DEV_EVENT_RXDOWN)
- mpc_channel_action(priv->channel[READ],
- READ, MPC_CHANNEL_REMOVE);
+ mpc_channel_action(priv->channel[CTCM_READ],
+ CTCM_READ, MPC_CHANNEL_REMOVE);
else
- mpc_channel_action(priv->channel[WRITE],
- WRITE, MPC_CHANNEL_REMOVE);
+ mpc_channel_action(priv->channel[CTCM_WRITE],
+ CTCM_WRITE, MPC_CHANNEL_REMOVE);
}
}
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 4ecafbf..6edf20b 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -267,7 +267,7 @@ static struct channel *channel_get(enum ctcm_channel_types type,
else {
ch->flags |= CHANNEL_FLAGS_INUSE;
ch->flags &= ~CHANNEL_FLAGS_RWMASK;
- ch->flags |= (direction == WRITE)
+ ch->flags |= (direction == CTCM_WRITE)
? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ;
fsm_newstate(ch->fsm, CTC_STATE_STOPPED);
}
@@ -388,7 +388,8 @@ int ctcm_ch_alloc_buffer(struct channel *ch)
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s(%s): %s trans_skb allocation error",
CTCM_FUNTAIL, ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
+ "RX" : "TX");
return -ENOMEM;
}
@@ -399,7 +400,8 @@ int ctcm_ch_alloc_buffer(struct channel *ch)
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s(%s): %s set norm_cda failed",
CTCM_FUNTAIL, ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
+ "RX" : "TX");
return -ENOMEM;
}
@@ -603,14 +605,14 @@ static void ctcmpc_send_sweep_req(struct channel *rch)
priv = dev->ml_priv;
grp = priv->mpcg;
- ch = priv->channel[WRITE];
+ ch = priv->channel[CTCM_WRITE];
/* sweep processing is not complete until response and request */
/* has completed for all read channels in group */
if (grp->in_sweep == 0) {
grp->in_sweep = 1;
- grp->sweep_rsp_pend_num = grp->active_channels[READ];
- grp->sweep_req_pend_num = grp->active_channels[READ];
+ grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
+ grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
}
sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
@@ -911,7 +913,7 @@ static int ctcm_tx(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
dev->trans_start = jiffies;
- if (ctcm_transmit_skb(priv->channel[WRITE], skb) != 0)
+ if (ctcm_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0)
return NETDEV_TX_BUSY;
return NETDEV_TX_OK;
}
@@ -994,7 +996,7 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
}
dev->trans_start = jiffies;
- if (ctcmpc_transmit_skb(priv->channel[WRITE], skb) != 0) {
+ if (ctcmpc_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): device error - dropped",
CTCM_FUNTAIL, dev->name);
@@ -1035,7 +1037,7 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu)
return -EINVAL;
priv = dev->ml_priv;
- max_bufsize = priv->channel[READ]->max_bufsize;
+ max_bufsize = priv->channel[CTCM_READ]->max_bufsize;
if (IS_MPC(priv)) {
if (new_mtu > max_bufsize - TH_HEADER_LENGTH)
@@ -1226,10 +1228,10 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
priv = dev_get_drvdata(&cgdev->dev);
/* Try to extract channel from driver data. */
- if (priv->channel[READ]->cdev == cdev)
- ch = priv->channel[READ];
- else if (priv->channel[WRITE]->cdev == cdev)
- ch = priv->channel[WRITE];
+ if (priv->channel[CTCM_READ]->cdev == cdev)
+ ch = priv->channel[CTCM_READ];
+ else if (priv->channel[CTCM_WRITE]->cdev == cdev)
+ ch = priv->channel[CTCM_WRITE];
else {
dev_err(&cdev->dev,
"%s: Internal error: Can't determine channel for "
@@ -1587,13 +1589,13 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
goto out_ccw2;
}
- for (direction = READ; direction <= WRITE; direction++) {
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
priv->channel[direction] =
- channel_get(type, direction == READ ? read_id : write_id,
- direction);
+ channel_get(type, direction == CTCM_READ ?
+ read_id : write_id, direction);
if (priv->channel[direction] == NULL) {
- if (direction == WRITE)
- channel_free(priv->channel[READ]);
+ if (direction == CTCM_WRITE)
+ channel_free(priv->channel[CTCM_READ]);
goto out_dev;
}
priv->channel[direction]->netdev = dev;
@@ -1617,13 +1619,13 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
dev_info(&dev->dev,
"setup OK : r/w = %s/%s, protocol : %d\n",
- priv->channel[READ]->id,
- priv->channel[WRITE]->id, priv->protocol);
+ priv->channel[CTCM_READ]->id,
+ priv->channel[CTCM_WRITE]->id, priv->protocol);
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
"setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name,
- priv->channel[READ]->id,
- priv->channel[WRITE]->id, priv->protocol);
+ priv->channel[CTCM_READ]->id,
+ priv->channel[CTCM_WRITE]->id, priv->protocol);
return 0;
out_unregister:
@@ -1635,10 +1637,10 @@ out_ccw2:
out_ccw1:
ccw_device_set_offline(cgdev->cdev[0]);
out_remove_channel2:
- readc = channel_get(type, read_id, READ);
+ readc = channel_get(type, read_id, CTCM_READ);
channel_remove(readc);
out_remove_channel1:
- writec = channel_get(type, write_id, WRITE);
+ writec = channel_get(type, write_id, CTCM_WRITE);
channel_remove(writec);
out_err_result:
return result;
@@ -1660,19 +1662,19 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
if (!priv)
return -ENODEV;
- if (priv->channel[READ]) {
- dev = priv->channel[READ]->netdev;
+ if (priv->channel[CTCM_READ]) {
+ dev = priv->channel[CTCM_READ]->netdev;
CTCM_DBF_DEV(SETUP, dev, "");
/* Close the device */
ctcm_close(dev);
dev->flags &= ~IFF_RUNNING;
ctcm_remove_attributes(&cgdev->dev);
- channel_free(priv->channel[READ]);
+ channel_free(priv->channel[CTCM_READ]);
} else
dev = NULL;
- if (priv->channel[WRITE])
- channel_free(priv->channel[WRITE]);
+ if (priv->channel[CTCM_WRITE])
+ channel_free(priv->channel[CTCM_WRITE]);
if (dev) {
unregister_netdev(dev);
@@ -1685,11 +1687,11 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
- if (priv->channel[READ])
- channel_remove(priv->channel[READ]);
- if (priv->channel[WRITE])
- channel_remove(priv->channel[WRITE]);
- priv->channel[READ] = priv->channel[WRITE] = NULL;
+ if (priv->channel[CTCM_READ])
+ channel_remove(priv->channel[CTCM_READ]);
+ if (priv->channel[CTCM_WRITE])
+ channel_remove(priv->channel[CTCM_WRITE]);
+ priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL;
return 0;
@@ -1720,11 +1722,11 @@ static int ctcm_pm_suspend(struct ccwgroup_device *gdev)
if (gdev->state == CCWGROUP_OFFLINE)
return 0;
- netif_device_detach(priv->channel[READ]->netdev);
- ctcm_close(priv->channel[READ]->netdev);
+ netif_device_detach(priv->channel[CTCM_READ]->netdev);
+ ctcm_close(priv->channel[CTCM_READ]->netdev);
if (!wait_event_timeout(priv->fsm->wait_q,
fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) {
- netif_device_attach(priv->channel[READ]->netdev);
+ netif_device_attach(priv->channel[CTCM_READ]->netdev);
return -EBUSY;
}
ccw_device_set_offline(gdev->cdev[1]);
@@ -1745,9 +1747,9 @@ static int ctcm_pm_resume(struct ccwgroup_device *gdev)
rc = ccw_device_set_online(gdev->cdev[0]);
if (rc)
goto err_out;
- ctcm_open(priv->channel[READ]->netdev);
+ ctcm_open(priv->channel[CTCM_READ]->netdev);
err_out:
- netif_device_attach(priv->channel[READ]->netdev);
+ netif_device_attach(priv->channel[CTCM_READ]->netdev);
return rc;
}
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
index d34fa14..24d5215 100644
--- a/drivers/s390/net/ctcm_main.h
+++ b/drivers/s390/net/ctcm_main.h
@@ -111,8 +111,8 @@ enum ctcm_channel_types {
#define CTCM_INITIAL_BLOCKLEN 2
-#define READ 0
-#define WRITE 1
+#define CTCM_READ 0
+#define CTCM_WRITE 1
#define CTCM_ID_SIZE 20+3
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
index 87c24d2..2861e78 100644
--- a/drivers/s390/net/ctcm_mpc.c
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -419,8 +419,8 @@ void ctc_mpc_establish_connectivity(int port_num,
return;
priv = dev->ml_priv;
grp = priv->mpcg;
- rch = priv->channel[READ];
- wch = priv->channel[WRITE];
+ rch = priv->channel[CTCM_READ];
+ wch = priv->channel[CTCM_WRITE];
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
"%s(%s): state=%s",
@@ -578,7 +578,7 @@ void ctc_mpc_flow_control(int port_num, int flowc)
"%s: %s: flowc = %d",
CTCM_FUNTAIL, dev->name, flowc);
- rch = priv->channel[READ];
+ rch = priv->channel[CTCM_READ];
mpcg_state = fsm_getstate(grp->fsm);
switch (flowc) {
@@ -622,7 +622,7 @@ static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
struct net_device *dev = rch->netdev;
struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- struct channel *ch = priv->channel[WRITE];
+ struct channel *ch = priv->channel[CTCM_WRITE];
CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, ch, ch->id);
CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
@@ -656,7 +656,7 @@ static void ctcmpc_send_sweep_resp(struct channel *rch)
int rc = 0;
struct th_sweep *header;
struct sk_buff *sweep_skb;
- struct channel *ch = priv->channel[WRITE];
+ struct channel *ch = priv->channel[CTCM_WRITE];
CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, rch, rch->id);
@@ -712,7 +712,7 @@ static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
struct net_device *dev = rch->netdev;
struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- struct channel *ch = priv->channel[WRITE];
+ struct channel *ch = priv->channel[CTCM_WRITE];
if (do_debug)
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
@@ -721,8 +721,8 @@ static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
if (grp->in_sweep == 0) {
grp->in_sweep = 1;
ctcm_test_and_set_busy(dev);
- grp->sweep_req_pend_num = grp->active_channels[READ];
- grp->sweep_rsp_pend_num = grp->active_channels[READ];
+ grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
+ grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
}
CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
@@ -906,14 +906,14 @@ void mpc_group_ready(unsigned long adev)
fsm_newstate(grp->fsm, MPCG_STATE_READY);
/* Put up a read on the channel */
- ch = priv->channel[READ];
+ ch = priv->channel[CTCM_READ];
ch->pdu_seq = 0;
CTCM_PR_DBGDATA("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
__func__, ch->pdu_seq);
ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
/* Put the write channel in idle state */
- ch = priv->channel[WRITE];
+ ch = priv->channel[CTCM_WRITE];
if (ch->collect_len > 0) {
spin_lock(&ch->collect_lock);
ctcm_purge_skb_queue(&ch->collect_queue);
@@ -960,7 +960,8 @@ void mpc_channel_action(struct channel *ch, int direction, int action)
"%s: %i / Grp:%s total_channels=%i, active_channels: "
"read=%i, write=%i\n", __func__, action,
fsm_getstate_str(grp->fsm), grp->num_channel_paths,
- grp->active_channels[READ], grp->active_channels[WRITE]);
+ grp->active_channels[CTCM_READ],
+ grp->active_channels[CTCM_WRITE]);
if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) {
grp->num_channel_paths++;
@@ -994,10 +995,11 @@ void mpc_channel_action(struct channel *ch, int direction, int action)
grp->xid_skb->data,
grp->xid_skb->len);
- ch->xid->xid2_dlc_type = ((CHANNEL_DIRECTION(ch->flags) == READ)
+ ch->xid->xid2_dlc_type =
+ ((CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
? XID2_READ_SIDE : XID2_WRITE_SIDE);
- if (CHANNEL_DIRECTION(ch->flags) == WRITE)
+ if (CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE)
ch->xid->xid2_buf_len = 0x00;
ch->xid_skb->data = ch->xid_skb_data;
@@ -1006,8 +1008,8 @@ void mpc_channel_action(struct channel *ch, int direction, int action)
fsm_newstate(ch->fsm, CH_XID0_PENDING);
- if ((grp->active_channels[READ] > 0) &&
- (grp->active_channels[WRITE] > 0) &&
+ if ((grp->active_channels[CTCM_READ] > 0) &&
+ (grp->active_channels[CTCM_WRITE] > 0) &&
(fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
@@ -1027,10 +1029,10 @@ void mpc_channel_action(struct channel *ch, int direction, int action)
if (grp->channels_terminating)
goto done;
- if (((grp->active_channels[READ] == 0) &&
- (grp->active_channels[WRITE] > 0))
- || ((grp->active_channels[WRITE] == 0) &&
- (grp->active_channels[READ] > 0)))
+ if (((grp->active_channels[CTCM_READ] == 0) &&
+ (grp->active_channels[CTCM_WRITE] > 0))
+ || ((grp->active_channels[CTCM_WRITE] == 0) &&
+ (grp->active_channels[CTCM_READ] > 0)))
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
done:
@@ -1038,7 +1040,8 @@ done:
"exit %s: %i / Grp:%s total_channels=%i, active_channels: "
"read=%i, write=%i\n", __func__, action,
fsm_getstate_str(grp->fsm), grp->num_channel_paths,
- grp->active_channels[READ], grp->active_channels[WRITE]);
+ grp->active_channels[CTCM_READ],
+ grp->active_channels[CTCM_WRITE]);
CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
}
@@ -1392,8 +1395,8 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)
(grp->port_persist == 0))
fsm_deltimer(&priv->restart_timer);
- wch = priv->channel[WRITE];
- rch = priv->channel[READ];
+ wch = priv->channel[CTCM_WRITE];
+ rch = priv->channel[CTCM_READ];
switch (grp->saved_state) {
case MPCG_STATE_RESET:
@@ -1480,8 +1483,8 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg)
priv = dev->ml_priv;
grp = priv->mpcg;
- wch = priv->channel[WRITE];
- rch = priv->channel[READ];
+ wch = priv->channel[CTCM_WRITE];
+ rch = priv->channel[CTCM_READ];
switch (fsm_getstate(grp->fsm)) {
case MPCG_STATE_XID2INITW:
@@ -1586,7 +1589,7 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo)
CTCM_D3_DUMP((char *)xid, XID2_LENGTH);
/*the received direction should be the opposite of ours */
- if (((CHANNEL_DIRECTION(ch->flags) == READ) ? XID2_WRITE_SIDE :
+ if (((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? XID2_WRITE_SIDE :
XID2_READ_SIDE) != xid->xid2_dlc_type) {
rc = 2;
/* XID REJECTED: r/w channel pairing mismatch */
@@ -1912,7 +1915,7 @@ static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg)
if (grp == NULL)
return;
- for (direction = READ; direction <= WRITE; direction++) {
+ for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
struct channel *ch = priv->channel[direction];
struct xid2 *thisxid = ch->xid;
ch->xid_skb->data = ch->xid_skb_data;
@@ -2152,14 +2155,15 @@ static int mpc_send_qllc_discontact(struct net_device *dev)
return -ENOMEM;
}
- *((__u32 *)skb_push(skb, 4)) = priv->channel[READ]->pdu_seq;
- priv->channel[READ]->pdu_seq++;
+ *((__u32 *)skb_push(skb, 4)) =
+ priv->channel[CTCM_READ]->pdu_seq;
+ priv->channel[CTCM_READ]->pdu_seq++;
CTCM_PR_DBGDATA("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
- __func__, priv->channel[READ]->pdu_seq);
+ __func__, priv->channel[CTCM_READ]->pdu_seq);
/* receipt of CC03 resets anticipated sequence number on
receiving side */
- priv->channel[READ]->pdu_seq = 0x00;
+ priv->channel[CTCM_READ]->pdu_seq = 0x00;
skb_reset_mac_header(skb);
skb->dev = dev;
skb->protocol = htons(ETH_P_SNAP);
diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c
index 2b24550..8305319 100644
--- a/drivers/s390/net/ctcm_sysfs.c
+++ b/drivers/s390/net/ctcm_sysfs.c
@@ -38,8 +38,8 @@ static ssize_t ctcm_buffer_write(struct device *dev,
int bs1;
struct ctcm_priv *priv = dev_get_drvdata(dev);
- if (!(priv && priv->channel[READ] &&
- (ndev = priv->channel[READ]->netdev))) {
+ ndev = priv->channel[CTCM_READ]->netdev;
+ if (!(priv && priv->channel[CTCM_READ] && ndev)) {
CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, "bfnondev");
return -ENODEV;
}
@@ -55,12 +55,12 @@ static ssize_t ctcm_buffer_write(struct device *dev,
(bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2)))
goto einval;
- priv->channel[READ]->max_bufsize = bs1;
- priv->channel[WRITE]->max_bufsize = bs1;
+ priv->channel[CTCM_READ]->max_bufsize = bs1;
+ priv->channel[CTCM_WRITE]->max_bufsize = bs1;
if (!(ndev->flags & IFF_RUNNING))
ndev->mtu = bs1 - LL_HEADER_LENGTH - 2;
- priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
- priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
+ priv->channel[CTCM_READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
+ priv->channel[CTCM_WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
CTCM_DBF_DEV(SETUP, ndev, buf);
return count;
@@ -85,9 +85,9 @@ static void ctcm_print_statistics(struct ctcm_priv *priv)
p += sprintf(p, " Device FSM state: %s\n",
fsm_getstate_str(priv->fsm));
p += sprintf(p, " RX channel FSM state: %s\n",
- fsm_getstate_str(priv->channel[READ]->fsm));
+ fsm_getstate_str(priv->channel[CTCM_READ]->fsm));
p += sprintf(p, " TX channel FSM state: %s\n",
- fsm_getstate_str(priv->channel[WRITE]->fsm));
+ fsm_getstate_str(priv->channel[CTCM_WRITE]->fsm));
p += sprintf(p, " Max. TX buffer used: %ld\n",
priv->channel[WRITE]->prof.maxmulti);
p += sprintf(p, " Max. chained SKBs: %ld\n",
@@ -102,7 +102,7 @@ static void ctcm_print_statistics(struct ctcm_priv *priv)
priv->channel[WRITE]->prof.tx_time);
printk(KERN_INFO "Statistics for %s:\n%s",
- priv->channel[WRITE]->netdev->name, sbuf);
+ priv->channel[CTCM_WRITE]->netdev->name, sbuf);
kfree(sbuf);
return;
}
@@ -125,7 +125,7 @@ static ssize_t stats_write(struct device *dev, struct device_attribute *attr,
return -ENODEV;
/* Reset statistics */
memset(&priv->channel[WRITE]->prof, 0,
- sizeof(priv->channel[WRITE]->prof));
+ sizeof(priv->channel[CTCM_WRITE]->prof));
return count;
}
diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c
index 1376887..4d2ea40 100644
--- a/drivers/s390/net/smsgiucv_app.c
+++ b/drivers/s390/net/smsgiucv_app.c
@@ -180,6 +180,13 @@ static int __init smsgiucv_app_init(void)
goto fail_put_driver;
}
+ /* convert sender to uppercase characters */
+ if (sender) {
+ int len = strlen(sender);
+ while (len--)
+ sender[len] = toupper(sender[len]);
+ }
+
/* register with the smsgiucv device driver */
rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
if (rc) {
diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c
index 103fdf6..160e751 100644
--- a/drivers/sbus/char/bbc_envctrl.c
+++ b/drivers/sbus/char/bbc_envctrl.c
@@ -443,7 +443,7 @@ static int kenvctrld(void *__unused)
return 0;
}
-static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op,
+static void attach_one_temp(struct bbc_i2c_bus *bp, struct platform_device *op,
int temp_idx)
{
struct bbc_cpu_temperature *tp;
@@ -488,7 +488,7 @@ static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op,
tp->fan_todo[FAN_CPU] = FAN_SAME;
}
-static void attach_one_fan(struct bbc_i2c_bus *bp, struct of_device *op,
+static void attach_one_fan(struct bbc_i2c_bus *bp, struct platform_device *op,
int fan_idx)
{
struct bbc_fan_control *fp;
@@ -559,7 +559,7 @@ static void destroy_all_fans(struct bbc_i2c_bus *bp)
int bbc_envctrl_init(struct bbc_i2c_bus *bp)
{
- struct of_device *op;
+ struct platform_device *op;
int temp_index = 0;
int fan_index = 0;
int devidx = 0;
diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c
index 3e89c31..614a5e1 100644
--- a/drivers/sbus/char/bbc_i2c.c
+++ b/drivers/sbus/char/bbc_i2c.c
@@ -51,7 +51,7 @@
* The second controller also connects to the smartcard reader, if present.
*/
-static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, int val)
+static void set_device_claimage(struct bbc_i2c_bus *bp, struct platform_device *op, int val)
{
int i;
@@ -66,9 +66,9 @@ static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, in
#define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1)
#define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0)
-struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)
+struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)
{
- struct of_device *op = NULL;
+ struct platform_device *op = NULL;
int curidx = 0, i;
for (i = 0; i < NUM_CHILDREN; i++) {
@@ -86,7 +86,7 @@ out:
return NULL;
}
-struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *op)
+struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *op)
{
struct bbc_i2c_client *client;
const u32 *reg;
@@ -114,7 +114,7 @@ struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *
void bbc_i2c_detach(struct bbc_i2c_client *client)
{
struct bbc_i2c_bus *bp = client->bp;
- struct of_device *op = client->op;
+ struct platform_device *op = client->op;
release_device(bp, op);
kfree(client);
@@ -297,7 +297,7 @@ static void __init reset_one_i2c(struct bbc_i2c_bus *bp)
writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);
}
-static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int index)
+static struct bbc_i2c_bus * __init attach_one_i2c(struct platform_device *op, int index)
{
struct bbc_i2c_bus *bp;
struct device_node *dp;
@@ -330,7 +330,7 @@ static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int inde
for (dp = op->dev.of_node->child;
dp && entry < 8;
dp = dp->sibling, entry++) {
- struct of_device *child_op;
+ struct platform_device *child_op;
child_op = of_find_device_by_node(dp);
bp->devs[entry].device = child_op;
@@ -361,7 +361,7 @@ fail:
extern int bbc_envctrl_init(struct bbc_i2c_bus *bp);
extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp);
-static int __devinit bbc_i2c_probe(struct of_device *op,
+static int __devinit bbc_i2c_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct bbc_i2c_bus *bp;
@@ -386,7 +386,7 @@ static int __devinit bbc_i2c_probe(struct of_device *op,
return err;
}
-static int __devexit bbc_i2c_remove(struct of_device *op)
+static int __devexit bbc_i2c_remove(struct platform_device *op)
{
struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev);
diff --git a/drivers/sbus/char/bbc_i2c.h b/drivers/sbus/char/bbc_i2c.h
index 83c4811..4b45310 100644
--- a/drivers/sbus/char/bbc_i2c.h
+++ b/drivers/sbus/char/bbc_i2c.h
@@ -7,7 +7,7 @@
struct bbc_i2c_client {
struct bbc_i2c_bus *bp;
- struct of_device *op;
+ struct platform_device *op;
int bus;
int address;
};
@@ -64,16 +64,16 @@ struct bbc_i2c_bus {
struct list_head temps;
struct list_head fans;
- struct of_device *op;
+ struct platform_device *op;
struct {
- struct of_device *device;
+ struct platform_device *device;
int client_claimed;
} devs[NUM_CHILDREN];
};
/* Probing and attachment. */
-extern struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int);
-extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *);
+extern struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int);
+extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *);
extern void bbc_i2c_detach(struct bbc_i2c_client *);
/* Register read/write. NOTE: Blocking! */
diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c
index 47db975..1690e53 100644
--- a/drivers/sbus/char/display7seg.c
+++ b/drivers/sbus/char/display7seg.c
@@ -170,7 +170,7 @@ static struct miscdevice d7s_miscdev = {
.fops = &d7s_fops
};
-static int __devinit d7s_probe(struct of_device *op,
+static int __devinit d7s_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *opts;
@@ -236,7 +236,7 @@ out_free:
goto out;
}
-static int __devexit d7s_remove(struct of_device *op)
+static int __devexit d7s_remove(struct platform_device *op)
{
struct d7s *p = dev_get_drvdata(&op->dev);
u8 regs = readb(p->regs);
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index 3c27f45..078e5f4 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -1027,7 +1027,7 @@ static int kenvctrld(void *__unused)
return 0;
}
-static int __devinit envctrl_probe(struct of_device *op,
+static int __devinit envctrl_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp;
@@ -1104,7 +1104,7 @@ out_iounmap:
return err;
}
-static int __devexit envctrl_remove(struct of_device *op)
+static int __devexit envctrl_remove(struct platform_device *op)
{
int index;
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 8bb31c5..2b4b4b6 100644
--- a/drivers/sbus/char/flash.c
+++ b/drivers/sbus/char/flash.c
@@ -160,7 +160,7 @@ static const struct file_operations flash_fops = {
static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
-static int __devinit flash_probe(struct of_device *op,
+static int __devinit flash_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -192,7 +192,7 @@ static int __devinit flash_probe(struct of_device *op,
return misc_register(&flash_dev);
}
-static int __devexit flash_remove(struct of_device *op)
+static int __devexit flash_remove(struct platform_device *op)
{
misc_deregister(&flash_dev);
diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c
index 41eb672..1b345be 100644
--- a/drivers/sbus/char/uctrl.c
+++ b/drivers/sbus/char/uctrl.c
@@ -348,7 +348,7 @@ static void uctrl_get_external_status(struct uctrl_driver *driver)
}
-static int __devinit uctrl_probe(struct of_device *op,
+static int __devinit uctrl_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct uctrl_driver *p;
@@ -404,7 +404,7 @@ out_free:
goto out;
}
-static int __devexit uctrl_remove(struct of_device *op)
+static int __devexit uctrl_remove(struct platform_device *op)
{
struct uctrl_driver *p = dev_get_drvdata(&op->dev);
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index 80dc3ac..89fc1c8 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -309,9 +309,6 @@ NCR_700_detect(struct scsi_host_template *tpnt,
hostdata->msgin = memory + MSGIN_OFFSET;
hostdata->msgout = memory + MSGOUT_OFFSET;
hostdata->status = memory + STATUS_OFFSET;
- /* all of these offsets are L1_CACHE_BYTES separated. It is fatal
- * if this isn't sufficient separation to avoid dma flushing issues */
- BUG_ON(!dma_is_consistent(hostdata->dev, pScript) && L1_CACHE_BYTES < dma_get_cache_alignment());
hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET);
hostdata->dev = dev;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 158284f..bbf91ae 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -370,6 +370,14 @@ config ISCSI_TCP
http://open-iscsi.org
+config ISCSI_BOOT_SYSFS
+ tristate "iSCSI Boot Sysfs Interface"
+ default n
+ help
+ This option enables support for exposing iSCSI boot information
+ via sysfs to userspace. If you wish to export this information,
+ say Y. Otherwise, say N.
+
source "drivers/scsi/cxgb3i/Kconfig"
source "drivers/scsi/bnx2i/Kconfig"
source "drivers/scsi/be2iscsi/Kconfig"
@@ -1853,7 +1861,7 @@ config ZFCP_DIF
config SCSI_PMCRAID
tristate "PMC SIERRA Linux MaxRAID adapter support"
- depends on PCI && SCSI
+ depends on PCI && SCSI && NET
---help---
This driver supports the PMC SIERRA MaxRAID adapters.
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 2a3fca2..2703c6e 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_FCOE) += fcoe/
obj-$(CONFIG_FCOE_FNIC) += fnic/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
+obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o
obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o
obj-$(CONFIG_SCSI_ZORRO7XX) += 53c700.o zorro7xx.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index f92da9f..5d2f148 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -1857,7 +1857,9 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase
#endif
/* KLL May need eop and parity in 53c400 */
if (hostdata->flags & FLAG_NCR53C400)
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE | MR_MONITOR_BSY);
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE |
+ MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR |
+ MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
else
NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
#endif /* def REAL_DMA */
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
index 04057ab..84d77fd 100644
--- a/drivers/scsi/aacraid/rx.c
+++ b/drivers/scsi/aacraid/rx.c
@@ -352,9 +352,8 @@ static int aac_rx_check_health(struct aac_dev *dev)
pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS),
post, paddr);
if (likely((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X')))) {
- ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10);
- ret <<= 4;
- ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10);
+ ret = (hex_to_bin(buffer[2]) << 4) +
+ hex_to_bin(buffer[3]);
}
pci_free_consistent(dev->pdev, 512, buffer, baddr);
return ret;
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index 2a8cf13..4f785f2 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -52,22 +52,6 @@
#define SCSI_BUF_PA(address) isa_virt_to_bus(address)
#define SCSI_SG_PA(sgent) (isa_page_to_bus(sg_page((sgent))) + (sgent)->offset)
-static void BAD_SG_DMA(Scsi_Cmnd * SCpnt,
- struct scatterlist *sgp,
- int nseg,
- int badseg)
-{
- printk(KERN_CRIT "sgpnt[%d:%d] page %p/0x%llx length %u\n",
- badseg, nseg, sg_virt(sgp),
- (unsigned long long)SCSI_SG_PA(sgp),
- sgp->length);
-
- /*
- * Not safe to continue.
- */
- panic("Buffer at physical address > 16Mb used for aha1542");
-}
-
#include<linux/stat.h>
#ifdef DEBUG
@@ -691,8 +675,6 @@ static int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
}
scsi_for_each_sg(SCpnt, sg, sg_count, i) {
any2scsi(cptr[i].dataptr, SCSI_SG_PA(sg));
- if (SCSI_SG_PA(sg) + sg->length - 1 > ISA_DMA_THRESHOLD)
- BAD_SG_DMA(SCpnt, scsi_sglist(SCpnt), sg_count, i);
any2scsi(cptr[i].datalen, sg->length);
};
any2scsi(ccb[mbo].datalen, sg_count * sizeof(struct chain));
@@ -1133,16 +1115,9 @@ static int __init aha1542_detect(struct scsi_host_template * tpnt)
release_region(bases[indx], 4);
continue;
}
- /* For now we do this - until kmalloc is more intelligent
- we are resigned to stupid hacks like this */
- if (SCSI_BUF_PA(shpnt) >= ISA_DMA_THRESHOLD) {
- printk(KERN_ERR "Invalid address for shpnt with 1542.\n");
- goto unregister;
- }
if (!aha1542_test_port(bases[indx], shpnt))
goto unregister;
-
base_io = bases[indx];
/* Set the Bus on/off-times as not to ruin floppy performance */
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index 24ac231..3b7e83d 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -688,9 +688,9 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
{
int i;
struct asd_sas_phy **sas_phys =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_phys), GFP_KERNEL);
struct asd_sas_port **sas_ports =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_ports), GFP_KERNEL);
if (!sas_phys || !sas_ports) {
kfree(sas_phys);
diff --git a/drivers/scsi/be2iscsi/Kconfig b/drivers/scsi/be2iscsi/Kconfig
index 84c275f..ceaca32 100644
--- a/drivers/scsi/be2iscsi/Kconfig
+++ b/drivers/scsi/be2iscsi/Kconfig
@@ -2,6 +2,7 @@ config BE2ISCSI
tristate "ServerEngines' 10Gbps iSCSI - BladeEngine 2"
depends on PCI && SCSI && NET
select SCSI_ISCSI_ATTRS
+ select ISCSI_BOOT_SYSFS
help
This driver implements the iSCSI functionality for ServerEngines'
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index 40641d0..5218de4 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -162,6 +162,13 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES 2
#define OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES 3
#define OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG 7
+#define OPCODE_COMMON_ISCSI_NTWK_SET_VLAN 14
+#define OPCODE_COMMON_ISCSI_NTWK_CONFIGURE_STATELESS_IP_ADDR 17
+#define OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR 21
+#define OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY 22
+#define OPCODE_COMMON_ISCSI_NTWK_MODIFY_DEFAULT_GATEWAY 23
+#define OPCODE_COMMON_ISCSI_NTWK_GET_ALL_IF_ID 24
+#define OPCODE_COMMON_ISCSI_NTWK_GET_IF_INFO 25
#define OPCODE_COMMON_ISCSI_SET_FRAGNUM_BITS_FOR_SGL_CRA 61
#define OPCODE_COMMON_ISCSI_DEFQ_CREATE 64
#define OPCODE_COMMON_ISCSI_DEFQ_DESTROY 65
@@ -237,11 +244,109 @@ struct be_cmd_resp_eq_create {
u16 rsvd0; /* sword */
} __packed;
+struct mgmt_chap_format {
+ u32 flags;
+ u8 intr_chap_name[256];
+ u8 intr_secret[16];
+ u8 target_chap_name[256];
+ u8 target_secret[16];
+ u16 intr_chap_name_length;
+ u16 intr_secret_length;
+ u16 target_chap_name_length;
+ u16 target_secret_length;
+} __packed;
+
+struct mgmt_auth_method_format {
+ u8 auth_method_type;
+ u8 padding[3];
+ struct mgmt_chap_format chap;
+} __packed;
+
+struct mgmt_conn_login_options {
+ u8 flags;
+ u8 header_digest;
+ u8 data_digest;
+ u8 rsvd0;
+ u32 max_recv_datasegment_len_ini;
+ u32 max_recv_datasegment_len_tgt;
+ u32 tcp_mss;
+ u32 tcp_window_size;
+ struct mgmt_auth_method_format auth_data;
+} __packed;
+
+struct ip_address_format {
+ u16 size_of_structure;
+ u8 reserved;
+ u8 ip_type;
+ u8 ip_address[16];
+ u32 rsvd0;
+} __packed;
+
+struct mgmt_conn_info {
+ u32 connection_handle;
+ u32 connection_status;
+ u16 src_port;
+ u16 dest_port;
+ u16 dest_port_redirected;
+ u16 cid;
+ u32 estimated_throughput;
+ struct ip_address_format src_ipaddr;
+ struct ip_address_format dest_ipaddr;
+ struct ip_address_format dest_ipaddr_redirected;
+ struct mgmt_conn_login_options negotiated_login_options;
+} __packed;
+
+struct mgmt_session_login_options {
+ u8 flags;
+ u8 error_recovery_level;
+ u16 rsvd0;
+ u32 first_burst_length;
+ u32 max_burst_length;
+ u16 max_connections;
+ u16 max_outstanding_r2t;
+ u16 default_time2wait;
+ u16 default_time2retain;
+} __packed;
+
+struct mgmt_session_info {
+ u32 session_handle;
+ u32 status;
+ u8 isid[6];
+ u16 tsih;
+ u32 session_flags;
+ u16 conn_count;
+ u16 pad;
+ u8 target_name[224];
+ u8 initiator_iscsiname[224];
+ struct mgmt_session_login_options negotiated_login_options;
+ struct mgmt_conn_info conn_list[1];
+} __packed;
+
+struct be_cmd_req_get_session {
+ struct be_cmd_req_hdr hdr;
+ u32 session_handle;
+} __packed;
+
+struct be_cmd_resp_get_session {
+ struct be_cmd_resp_hdr hdr;
+ struct mgmt_session_info session_info;
+} __packed;
+
struct mac_addr {
u16 size_of_struct;
u8 addr[ETH_ALEN];
} __packed;
+struct be_cmd_req_get_boot_target {
+ struct be_cmd_req_hdr hdr;
+} __packed;
+
+struct be_cmd_resp_get_boot_target {
+ struct be_cmd_resp_hdr hdr;
+ u32 boot_session_count;
+ int boot_session_handle;
+};
+
struct be_cmd_req_mac_query {
struct be_cmd_req_hdr hdr;
u8 type;
@@ -426,6 +531,11 @@ int be_poll_mcc(struct be_ctrl_info *ctrl);
int mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
struct beiscsi_hba *phba);
unsigned int be_cmd_get_mac_addr(struct beiscsi_hba *phba);
+unsigned int beiscsi_get_boot_target(struct beiscsi_hba *phba);
+unsigned int beiscsi_get_session_info(struct beiscsi_hba *phba,
+ u32 boot_session_handle,
+ struct be_dma_mem *nonemb_cmd);
+
void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag);
/*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
@@ -601,14 +711,6 @@ struct be_eq_delay_params_in {
struct eq_delay delay[8];
} __packed;
-struct ip_address_format {
- u16 size_of_structure;
- u8 reserved;
- u8 ip_type;
- u8 ip_address[16];
- u32 rsvd0;
-} __packed;
-
struct tcp_connect_and_offload_in {
struct be_cmd_req_hdr hdr;
struct ip_address_format ip_address;
@@ -688,18 +790,29 @@ struct be_fw_cfg {
u32 function_caps;
} __packed;
-#define CMD_ISCSI_COMMAND_INVALIDATE 1
-#define ISCSI_OPCODE_SCSI_DATA_OUT 5
+struct be_all_if_id {
+ struct be_cmd_req_hdr hdr;
+ u32 if_count;
+ u32 if_hndl_list[1];
+} __packed;
+
+#define ISCSI_OPCODE_SCSI_DATA_OUT 5
+#define OPCODE_COMMON_MODIFY_EQ_DELAY 41
+#define OPCODE_COMMON_ISCSI_CLEANUP 59
+#define OPCODE_COMMON_TCP_UPLOAD 56
#define OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD 70
-#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41
-#define OPCODE_COMMON_MODIFY_EQ_DELAY 41
-#define OPCODE_COMMON_ISCSI_CLEANUP 59
-#define OPCODE_COMMON_TCP_UPLOAD 56
#define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1
-/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
-#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001
-#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002
+#define OPCODE_ISCSI_INI_CFG_GET_HBA_NAME 6
+#define OPCODE_ISCSI_INI_CFG_SET_HBA_NAME 7
+#define OPCODE_ISCSI_INI_SESSION_GET_A_SESSION 14
+#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41
#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
+#define OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET 52
+
+/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
+#define CMD_ISCSI_COMMAND_INVALIDATE 1
+#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001
+#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002
#define INI_WR_CMD 1 /* Initiator write command */
#define INI_TMF_CMD 2 /* Initiator TMF command */
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 6d63e7b..7d4d227 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -300,40 +300,16 @@ 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;
+ int status;
SE_DEBUG(DBG_LVL_8, "In beiscsi_get_host_param, param= %d\n", param);
switch (param) {
case ISCSI_HOST_PARAM_HWADDRESS:
- tag = be_cmd_get_mac_addr(phba);
- if (!tag) {
- SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed\n");
- return -EAGAIN;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
- 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 -EAGAIN;
- } 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);
+ status = beiscsi_get_macaddr(buf, phba);
+ if (status < 0) {
+ SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
+ return status;
}
break;
default:
@@ -342,6 +318,48 @@ int beiscsi_get_host_param(struct Scsi_Host *shost,
return len;
}
+int beiscsi_get_macaddr(char *buf, struct beiscsi_hba *phba)
+{
+ struct be_cmd_resp_get_mac_addr *resp;
+ struct be_mcc_wrb *wrb;
+ unsigned int tag, wrb_num;
+ unsigned short status, extd_status;
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ int rc;
+
+ if (phba->read_mac_address)
+ return 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 -EBUSY;
+ } else
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+
+ wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+ extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+ status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ if (status || extd_status) {
+ SE_DEBUG(DBG_LVL_1, "Failed to get be_cmd_get_mac_addr"
+ " status = %d extd_status = %d\n",
+ status, extd_status);
+ free_mcc_tag(&phba->ctrl, tag);
+ return -EAGAIN;
+ }
+ 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);
+ rc = sysfs_format_mac(buf, phba->mac_address,
+ ETH_ALEN);
+ phba->read_mac_address = 1;
+ return rc;
+}
+
+
/**
* beiscsi_conn_get_stats - get the iscsi stats
* @cls_conn: pointer to iscsi cls conn
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index 870cdb2..8950a70 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -54,6 +54,8 @@ int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
int beiscsi_get_host_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf);
+int beiscsi_get_macaddr(char *buf, struct beiscsi_hba *phba);
+
int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen);
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 7436c5a..8220bde 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -26,6 +26,7 @@
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
+#include <linux/iscsi_boot_sysfs.h>
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
@@ -211,6 +212,218 @@ unlock:
return rc;
}
+static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
+{
+ struct beiscsi_hba *phba = data;
+ char *str = buf;
+ int rc;
+
+ switch (type) {
+ case ISCSI_BOOT_TGT_NAME:
+ rc = sprintf(buf, "%.*s\n",
+ (int)strlen(phba->boot_sess.target_name),
+ (char *)&phba->boot_sess.target_name);
+ break;
+ case ISCSI_BOOT_TGT_IP_ADDR:
+ if (phba->boot_sess.conn_list[0].dest_ipaddr.ip_type == 0x1)
+ rc = sprintf(buf, "%pI4\n",
+ (char *)&phba->boot_sess.conn_list[0].
+ dest_ipaddr.ip_address);
+ else
+ rc = sprintf(str, "%pI6\n",
+ (char *)&phba->boot_sess.conn_list[0].
+ dest_ipaddr.ip_address);
+ break;
+ case ISCSI_BOOT_TGT_PORT:
+ rc = sprintf(str, "%d\n", phba->boot_sess.conn_list[0].
+ dest_port);
+ break;
+
+ case ISCSI_BOOT_TGT_CHAP_NAME:
+ rc = sprintf(str, "%.*s\n",
+ phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ target_chap_name_length,
+ (char *)&phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ target_chap_name);
+ break;
+ case ISCSI_BOOT_TGT_CHAP_SECRET:
+ rc = sprintf(str, "%.*s\n",
+ phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ target_secret_length,
+ (char *)&phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ target_secret);
+
+ break;
+ case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+ rc = sprintf(str, "%.*s\n",
+ phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ intr_chap_name_length,
+ (char *)&phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ intr_chap_name);
+
+ break;
+ case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+ rc = sprintf(str, "%.*s\n",
+ phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ intr_secret_length,
+ (char *)&phba->boot_sess.conn_list[0].
+ negotiated_login_options.auth_data.chap.
+ intr_secret);
+ break;
+ case ISCSI_BOOT_TGT_FLAGS:
+ rc = sprintf(str, "2\n");
+ break;
+ case ISCSI_BOOT_TGT_NIC_ASSOC:
+ rc = sprintf(str, "0\n");
+ break;
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+ return rc;
+}
+
+static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
+{
+ struct beiscsi_hba *phba = data;
+ char *str = buf;
+ int rc;
+
+ switch (type) {
+ case ISCSI_BOOT_INI_INITIATOR_NAME:
+ rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname);
+ break;
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+ return rc;
+}
+
+static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
+{
+ struct beiscsi_hba *phba = data;
+ char *str = buf;
+ int rc;
+
+ switch (type) {
+ case ISCSI_BOOT_ETH_FLAGS:
+ rc = sprintf(str, "2\n");
+ break;
+ case ISCSI_BOOT_ETH_INDEX:
+ rc = sprintf(str, "0\n");
+ break;
+ case ISCSI_BOOT_ETH_MAC:
+ rc = beiscsi_get_macaddr(buf, phba);
+ if (rc < 0) {
+ SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
+ return rc;
+ }
+ break;
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+ return rc;
+}
+
+
+static mode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
+{
+ int rc;
+
+ switch (type) {
+ case ISCSI_BOOT_TGT_NAME:
+ case ISCSI_BOOT_TGT_IP_ADDR:
+ case ISCSI_BOOT_TGT_PORT:
+ case ISCSI_BOOT_TGT_CHAP_NAME:
+ case ISCSI_BOOT_TGT_CHAP_SECRET:
+ case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+ case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+ case ISCSI_BOOT_TGT_NIC_ASSOC:
+ case ISCSI_BOOT_TGT_FLAGS:
+ rc = S_IRUGO;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static mode_t beiscsi_ini_get_attr_visibility(void *data, int type)
+{
+ int rc;
+
+ switch (type) {
+ case ISCSI_BOOT_INI_INITIATOR_NAME:
+ rc = S_IRUGO;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+
+static mode_t beiscsi_eth_get_attr_visibility(void *data, int type)
+{
+ int rc;
+
+ switch (type) {
+ case ISCSI_BOOT_ETH_FLAGS:
+ case ISCSI_BOOT_ETH_MAC:
+ case ISCSI_BOOT_ETH_INDEX:
+ rc = S_IRUGO;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static int beiscsi_setup_boot_info(struct beiscsi_hba *phba)
+{
+ struct iscsi_boot_kobj *boot_kobj;
+
+ phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
+ if (!phba->boot_kset)
+ return -ENOMEM;
+
+ /* get boot info using mgmt cmd */
+ boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba,
+ beiscsi_show_boot_tgt_info,
+ beiscsi_tgt_get_attr_visibility);
+ if (!boot_kobj)
+ goto free_kset;
+
+ boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba,
+ beiscsi_show_boot_ini_info,
+ beiscsi_ini_get_attr_visibility);
+ if (!boot_kobj)
+ goto free_kset;
+
+ boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba,
+ beiscsi_show_boot_eth_info,
+ beiscsi_eth_get_attr_visibility);
+ if (!boot_kobj)
+ goto free_kset;
+ return 0;
+
+free_kset:
+ iscsi_boot_destroy_kset(phba->boot_kset);
+ return -ENOMEM;
+}
+
/*------------------- PCI Driver operations and data ----------------- */
static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
@@ -268,6 +481,15 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
if (iscsi_host_add(shost, &phba->pcidev->dev))
goto free_devices;
+
+ if (beiscsi_setup_boot_info(phba))
+ /*
+ * log error but continue, because we may not be using
+ * iscsi boot.
+ */
+ shost_printk(KERN_ERR, phba->shost, "Could not set up "
+ "iSCSI boot info.");
+
return phba;
free_devices:
@@ -3279,6 +3501,89 @@ static void hwi_disable_intr(struct beiscsi_hba *phba)
"In hwi_disable_intr, Already Disabled\n");
}
+static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
+{
+ struct be_cmd_resp_get_boot_target *boot_resp;
+ struct be_cmd_resp_get_session *session_resp;
+ struct be_mcc_wrb *wrb;
+ struct be_dma_mem nonemb_cmd;
+ unsigned int tag, wrb_num;
+ unsigned short status, extd_status;
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+
+ tag = beiscsi_get_boot_target(phba);
+ if (!tag) {
+ SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed\n");
+ return -EAGAIN;
+ } else
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+
+ wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+ extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+ status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ if (status || extd_status) {
+ 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 -EBUSY;
+ }
+ wrb = queue_get_wrb(mccq, wrb_num);
+ free_mcc_tag(&phba->ctrl, tag);
+ boot_resp = embedded_payload(wrb);
+
+ if (boot_resp->boot_session_handle < 0) {
+ printk(KERN_ERR "No Boot Session for this pci_func,"
+ "session Hndl = %d\n", boot_resp->boot_session_handle);
+ return -ENXIO;
+ }
+
+ nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
+ sizeof(*session_resp),
+ &nonemb_cmd.dma);
+ if (nonemb_cmd.va == NULL) {
+ SE_DEBUG(DBG_LVL_1,
+ "Failed to allocate memory for"
+ "beiscsi_get_session_info\n");
+ return -ENOMEM;
+ }
+
+ memset(nonemb_cmd.va, 0, sizeof(*session_resp));
+ tag = beiscsi_get_session_info(phba,
+ boot_resp->boot_session_handle, &nonemb_cmd);
+ if (!tag) {
+ SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info"
+ " Failed\n");
+ goto boot_freemem;
+ } else
+ wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag]);
+
+ wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+ extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+ status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ if (status || extd_status) {
+ SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info Failed"
+ " status = %d extd_status = %d\n",
+ status, extd_status);
+ free_mcc_tag(&phba->ctrl, tag);
+ goto boot_freemem;
+ }
+ wrb = queue_get_wrb(mccq, wrb_num);
+ free_mcc_tag(&phba->ctrl, tag);
+ session_resp = nonemb_cmd.va ;
+ memcpy(&phba->boot_sess, &session_resp->session_info,
+ sizeof(struct mgmt_session_info));
+ pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ return 0;
+boot_freemem:
+ pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ return -ENOMEM;
+}
+
static int beiscsi_init_port(struct beiscsi_hba *phba)
{
int ret;
@@ -3841,6 +4146,7 @@ static void beiscsi_remove(struct pci_dev *pcidev)
iscsi_host_remove(phba->shost);
pci_dev_put(phba->pcidev);
iscsi_host_free(phba->shost);
+ iscsi_boot_destroy_kset(phba->boot_kset);
}
static void beiscsi_msix_enable(struct beiscsi_hba *phba)
@@ -3996,6 +4302,11 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
goto free_blkenbld;
}
hwi_enable_intr(phba);
+ ret = beiscsi_get_boot_info(phba);
+ if (ret < 0) {
+ shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
+ "No Boot Devices !!!!!\n");
+ }
SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED\n\n\n");
return 0;
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index c643bb3..90eb74f 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -35,7 +35,7 @@
#include "be.h"
#define DRV_NAME "be2iscsi"
-#define BUILD_STR "2.0.527.0"
+#define BUILD_STR "2.0.549.0"
#define BE_NAME "ServerEngines BladeEngine2" \
"Linux iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
@@ -63,7 +63,7 @@
#define BEISCSI_SGLIST_ELEMENTS 30
#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */
-#define BEISCSI_MAX_SECTORS 256 /* scsi_host->max_sectors */
+#define BEISCSI_MAX_SECTORS 2048 /* 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 */
@@ -312,6 +312,7 @@ struct beiscsi_hba {
struct list_head hba_queue;
unsigned short *cid_array;
struct iscsi_endpoint **ep_array;
+ struct iscsi_boot_kset *boot_kset;
struct Scsi_Host *shost;
struct {
/**
@@ -342,6 +343,8 @@ struct beiscsi_hba {
struct work_struct work_cqs; /* The work being queued */
struct be_ctrl_info ctrl;
unsigned int generation;
+ unsigned int read_mac_address;
+ struct mgmt_session_info boot_sess;
struct invalidate_command_table inv_tbl[128];
};
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 3f3fab9..26350e4 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -20,6 +20,77 @@
#include "be_mgmt.h"
#include "be_iscsi.h"
+#include <scsi/scsi_transport_iscsi.h>
+
+unsigned int beiscsi_get_boot_target(struct beiscsi_hba *phba)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_get_mac_addr *req;
+ unsigned int tag = 0;
+
+ SE_DEBUG(DBG_LVL_8, "In bescsi_get_boot_target\n");
+ spin_lock(&ctrl->mbox_lock);
+ 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_INI,
+ OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET,
+ sizeof(*req));
+
+ be_mcc_notify(phba);
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+}
+
+unsigned int beiscsi_get_session_info(struct beiscsi_hba *phba,
+ u32 boot_session_handle,
+ struct be_dma_mem *nonemb_cmd)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ unsigned int tag = 0;
+ struct be_cmd_req_get_session *req;
+ struct be_cmd_resp_get_session *resp;
+ struct be_sge *sge;
+
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_get_session_info\n");
+ spin_lock(&ctrl->mbox_lock);
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+ }
+
+ nonemb_cmd->size = sizeof(*resp);
+ req = nonemb_cmd->va;
+ memset(req, 0, sizeof(*req));
+ wrb = wrb_from_mccq(phba);
+ sge = nonembedded_sgl(wrb);
+ wrb->tag0 |= tag;
+
+
+ wrb->tag0 |= tag;
+ be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_SESSION_GET_A_SESSION,
+ sizeof(*resp));
+ req->session_handle = boot_session_handle;
+ sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
+ sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(nonemb_cmd->size);
+
+ be_mcc_notify(phba);
+ spin_unlock(&ctrl->mbox_lock);
+ return tag;
+}
int mgmt_get_fw_config(struct be_ctrl_info *ctrl,
struct beiscsi_hba *phba)
diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c
index f0933d8..76867b5 100644
--- a/drivers/scsi/bfa/bfa_fcport.c
+++ b/drivers/scsi/bfa/bfa_fcport.c
@@ -1310,7 +1310,7 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
break;
case BFI_FCPORT_I2H_DISABLE_RSP:
- if (fcport->msgtag == i2hmsg.penable_rsp->msgtag)
+ if (fcport->msgtag == i2hmsg.pdisable_rsp->msgtag)
bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP);
break;
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index 915a29d..ca04cc9 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -788,6 +788,7 @@ bfad_drv_init(struct bfad_s *bfad)
memset(&driver_info, 0, sizeof(driver_info));
strncpy(driver_info.version, BFAD_DRIVER_VERSION,
sizeof(driver_info.version) - 1);
+ __kernel_param_lock();
if (host_name)
strncpy(driver_info.host_machine_name, host_name,
sizeof(driver_info.host_machine_name) - 1);
@@ -797,6 +798,7 @@ bfad_drv_init(struct bfad_s *bfad)
if (os_patch)
strncpy(driver_info.host_os_patch, os_patch,
sizeof(driver_info.host_os_patch) - 1);
+ __kernel_param_unlock();
strncpy(driver_info.os_device_name, bfad->pci_name,
sizeof(driver_info.os_device_name - 1));
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index 678120b..6ef87f6 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -291,7 +291,7 @@ bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd)
struct bfa_tskim_s *tskim;
struct bfad_itnim_s *itnim;
struct bfa_itnim_s *bfa_itnim;
- DECLARE_WAIT_QUEUE_HEAD(wq);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
int rc = SUCCESS;
unsigned long flags;
enum bfi_tskim_status task_status;
@@ -353,7 +353,7 @@ bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd)
struct bfad_itnim_s *itnim;
unsigned long flags;
u32 i, rc, err_cnt = 0;
- DECLARE_WAIT_QUEUE_HEAD(wq);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
enum bfi_tskim_status task_status;
spin_lock_irqsave(&bfad->bfad_lock, flags);
diff --git a/drivers/scsi/bfa/include/protocol/fcp.h b/drivers/scsi/bfa/include/protocol/fcp.h
index 9ade68a..74ea63c 100644
--- a/drivers/scsi/bfa/include/protocol/fcp.h
+++ b/drivers/scsi/bfa/include/protocol/fcp.h
@@ -18,6 +18,7 @@
#ifndef __FCPPROTO_H__
#define __FCPPROTO_H__
+#include <linux/bitops.h>
#include <protocol/scsi.h>
#pragma pack(1)
@@ -102,9 +103,6 @@ enum {
/*
* Task management flags field - only one bit shall be set
*/
-#ifndef BIT
-#define BIT(_x) (1 << (_x))
-#endif
enum fcp_tm_cmnd{
FCP_TM_ABORT_TASK_SET = BIT(1),
FCP_TM_CLEAR_TASK_SET = BIT(2),
diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig
index 1e9f714..45a6154 100644
--- a/drivers/scsi/bnx2i/Kconfig
+++ b/drivers/scsi/bnx2i/Kconfig
@@ -1,10 +1,11 @@
config SCSI_BNX2_ISCSI
tristate "Broadcom NetXtreme II iSCSI support"
+ depends on NET
+ depends on PCI
select SCSI_ISCSI_ATTRS
select NETDEVICES
select NETDEV_1000
select CNIC
- depends on PCI
---help---
This driver supports iSCSI offload for the Broadcom NetXtreme II
devices.
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 4799d43..d653218 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -84,10 +84,16 @@ static const char * vendor_labels[CH_TYPES-4] = {
};
// module_param_string_array(vendor_labels, NULL, 0444);
-#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s: " fmt, ch->name , ## arg)
-#define vprintk(fmt, arg...) if (verbose) \
- printk(KERN_INFO "%s: " fmt, ch->name , ## arg)
+#define DPRINTK(fmt, arg...) \
+do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg); \
+} while (0)
+#define VPRINTK(level, fmt, arg...) \
+do { \
+ if (verbose) \
+ printk(level "%s: " fmt, ch->name, ##arg); \
+} while (0)
/* ------------------------------------------------------------------- */
@@ -186,7 +192,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
retry:
errno = 0;
if (debug) {
- dprintk("command: ");
+ DPRINTK("command: ");
__scsi_print_command(cmd);
}
@@ -194,7 +200,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
buflength, &sshdr, timeout * HZ,
MAX_RETRIES, NULL);
- dprintk("result: 0x%x\n",result);
+ DPRINTK("result: 0x%x\n",result);
if (driver_byte(result) & DRIVER_SENSE) {
if (debug)
scsi_print_sense_hdr(ch->name, &sshdr);
@@ -250,7 +256,7 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
cmd[9] = 255;
if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
if (((buffer[16] << 8) | buffer[17]) != elem) {
- dprintk("asked for element 0x%02x, got 0x%02x\n",
+ DPRINTK("asked for element 0x%02x, got 0x%02x\n",
elem,(buffer[16] << 8) | buffer[17]);
kfree(buffer);
return -EIO;
@@ -259,10 +265,10 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
} else {
if (ch->voltags) {
ch->voltags = 0;
- vprintk("device has no volume tag support\n");
+ VPRINTK(KERN_INFO, "device has no volume tag support\n");
goto retry;
}
- dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem);
+ DPRINTK("READ ELEMENT STATUS for element 0x%x failed\n",elem);
}
kfree(buffer);
return result;
@@ -274,12 +280,12 @@ ch_init_elem(scsi_changer *ch)
int err;
u_char cmd[6];
- vprintk("INITIALIZE ELEMENT STATUS, may take some time ...\n");
+ VPRINTK(KERN_INFO, "INITIALIZE ELEMENT STATUS, may take some time ...\n");
memset(cmd,0,sizeof(cmd));
cmd[0] = INITIALIZE_ELEMENT_STATUS;
cmd[1] = ch->device->lun << 5;
err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
- vprintk("... finished\n");
+ VPRINTK(KERN_INFO, "... finished\n");
return err;
}
@@ -322,20 +328,20 @@ ch_readconfig(scsi_changer *ch)
(buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19];
ch->counts[CHET_DT] =
(buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21];
- vprintk("type #1 (mt): 0x%x+%d [medium transport]\n",
+ VPRINTK(KERN_INFO, "type #1 (mt): 0x%x+%d [medium transport]\n",
ch->firsts[CHET_MT],
ch->counts[CHET_MT]);
- vprintk("type #2 (st): 0x%x+%d [storage]\n",
+ VPRINTK(KERN_INFO, "type #2 (st): 0x%x+%d [storage]\n",
ch->firsts[CHET_ST],
ch->counts[CHET_ST]);
- vprintk("type #3 (ie): 0x%x+%d [import/export]\n",
+ VPRINTK(KERN_INFO, "type #3 (ie): 0x%x+%d [import/export]\n",
ch->firsts[CHET_IE],
ch->counts[CHET_IE]);
- vprintk("type #4 (dt): 0x%x+%d [data transfer]\n",
+ VPRINTK(KERN_INFO, "type #4 (dt): 0x%x+%d [data transfer]\n",
ch->firsts[CHET_DT],
ch->counts[CHET_DT]);
} else {
- vprintk("reading element address assigment page failed!\n");
+ VPRINTK(KERN_INFO, "reading element address assigment page failed!\n");
}
/* vendor specific element types */
@@ -346,13 +352,13 @@ ch_readconfig(scsi_changer *ch)
continue;
ch->firsts[CHET_V1+i] = vendor_firsts[i];
ch->counts[CHET_V1+i] = vendor_counts[i];
- vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
+ VPRINTK(KERN_INFO, "type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
i+5,i+1,vendor_firsts[i],vendor_counts[i],
vendor_labels[i]);
}
/* look up the devices of the data transfer elements */
- ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device),
+ ch->dt = kcalloc(ch->counts[CHET_DT], sizeof(*ch->dt),
GFP_KERNEL);
if (!ch->dt) {
@@ -366,21 +372,19 @@ ch_readconfig(scsi_changer *ch)
if (elem < CH_DT_MAX && -1 != dt_id[elem]) {
id = dt_id[elem];
lun = dt_lun[elem];
- vprintk("dt 0x%x: [insmod option] ",
+ VPRINTK(KERN_INFO, "dt 0x%x: [insmod option] ",
elem+ch->firsts[CHET_DT]);
} else if (0 != ch_read_element_status
(ch,elem+ch->firsts[CHET_DT],data)) {
- vprintk("dt 0x%x: READ ELEMENT STATUS failed\n",
+ VPRINTK(KERN_INFO, "dt 0x%x: READ ELEMENT STATUS failed\n",
elem+ch->firsts[CHET_DT]);
} else {
- vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]);
+ VPRINTK(KERN_INFO, "dt 0x%x: ",elem+ch->firsts[CHET_DT]);
if (data[6] & 0x80) {
- if (verbose)
- printk("not this SCSI bus\n");
+ VPRINTK(KERN_CONT, "not this SCSI bus\n");
ch->dt[elem] = NULL;
} else if (0 == (data[6] & 0x30)) {
- if (verbose)
- printk("ID/LUN unknown\n");
+ VPRINTK(KERN_CONT, "ID/LUN unknown\n");
ch->dt[elem] = NULL;
} else {
id = ch->device->id;
@@ -390,22 +394,19 @@ ch_readconfig(scsi_changer *ch)
}
}
if (-1 != id) {
- if (verbose)
- printk("ID %i, LUN %i, ",id,lun);
+ VPRINTK(KERN_CONT, "ID %i, LUN %i, ",id,lun);
ch->dt[elem] =
scsi_device_lookup(ch->device->host,
ch->device->channel,
id,lun);
if (!ch->dt[elem]) {
/* should not happen */
- if (verbose)
- printk("Huh? device not found!\n");
+ VPRINTK(KERN_CONT, "Huh? device not found!\n");
} else {
- if (verbose)
- printk("name: %8.8s %16.16s %4.4s\n",
- ch->dt[elem]->vendor,
- ch->dt[elem]->model,
- ch->dt[elem]->rev);
+ VPRINTK(KERN_CONT, "name: %8.8s %16.16s %4.4s\n",
+ ch->dt[elem]->vendor,
+ ch->dt[elem]->model,
+ ch->dt[elem]->rev);
}
}
}
@@ -422,7 +423,7 @@ ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate)
{
u_char cmd[10];
- dprintk("position: 0x%x\n",elem);
+ DPRINTK("position: 0x%x\n",elem);
if (0 == trans)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
@@ -441,7 +442,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
{
u_char cmd[12];
- dprintk("move: 0x%x => 0x%x\n",src,dest);
+ DPRINTK("move: 0x%x => 0x%x\n",src,dest);
if (0 == trans)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
@@ -463,7 +464,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src,
{
u_char cmd[12];
- dprintk("exchange: 0x%x => 0x%x => 0x%x\n",
+ DPRINTK("exchange: 0x%x => 0x%x => 0x%x\n",
src,dest1,dest2);
if (0 == trans)
trans = ch->firsts[CHET_MT];
@@ -511,7 +512,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
if (!buffer)
return -ENOMEM;
- dprintk("%s %s voltag: 0x%x => \"%s\"\n",
+ DPRINTK("%s %s voltag: 0x%x => \"%s\"\n",
clear ? "clear" : "set",
alternate ? "alternate" : "primary",
elem, tag);
@@ -550,7 +551,7 @@ static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest)
}
put_user(data[2], dest+i);
if (data[2] & CESTATUS_EXCEPT)
- vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n",
+ VPRINTK(KERN_INFO, "element 0x%x: asc=0x%x, ascq=0x%x\n",
ch->firsts[type]+i,
(int)data[4],(int)data[5]);
retval = ch_read_element_status
@@ -660,7 +661,7 @@ static long ch_ioctl(struct file *file,
return -EFAULT;
if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {
- dprintk("CHIOPOSITION: invalid parameter\n");
+ DPRINTK("CHIOPOSITION: invalid parameter\n");
return -EBADSLT;
}
mutex_lock(&ch->lock);
@@ -680,7 +681,7 @@ static long ch_ioctl(struct file *file,
if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||
0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) {
- dprintk("CHIOMOVE: invalid parameter\n");
+ DPRINTK("CHIOMOVE: invalid parameter\n");
return -EBADSLT;
}
@@ -703,7 +704,7 @@ static long ch_ioctl(struct file *file,
if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) ||
0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||
0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) {
- dprintk("CHIOEXCHANGE: invalid parameter\n");
+ DPRINTK("CHIOEXCHANGE: invalid parameter\n");
return -EBADSLT;
}
@@ -796,7 +797,7 @@ static long ch_ioctl(struct file *file,
}
} else if (ch->voltags) {
ch->voltags = 0;
- vprintk("device has no volume tag support\n");
+ VPRINTK(KERN_INFO, "device has no volume tag support\n");
goto voltag_retry;
}
kfree(buffer);
@@ -824,7 +825,7 @@ static long ch_ioctl(struct file *file,
return -EFAULT;
if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {
- dprintk("CHIOSVOLTAG: invalid parameter\n");
+ DPRINTK("CHIOSVOLTAG: invalid parameter\n");
return -EBADSLT;
}
elem = ch->firsts[csv.csv_type] + csv.csv_unit;
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index bd977be..54f50b0 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -1597,7 +1597,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
u32 tag_mask = 1;
u8 tag_number = 0;
while (tag_mask & dcb->tag_mask
- && tag_number <= dcb->max_command) {
+ && tag_number < dcb->max_command) {
tag_mask = tag_mask << 1;
tag_number++;
}
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 19338e0..cbb20b1 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
+#include <linux/bitops.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
#include "fnic_io.h"
@@ -49,7 +50,6 @@
/*
* Tag bits used for special requests.
*/
-#define BIT(nr) (1UL << (nr))
#define FNIC_TAG_ABORT BIT(30) /* tag bit indicating abort */
#define FNIC_TAG_DEV_RST BIT(29) /* indicates device reset */
#define FNIC_TAG_MASK (BIT(24) - 1) /* mask for lookup */
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 75585a5..427a56d 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -285,9 +285,12 @@ static int __init do_DTC3181E_setup(char *str)
int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
{
static int current_override = 0;
- int count, i;
+ int count;
unsigned int *ports;
+#ifndef SCSI_G_NCR5380_MEM
+ int i;
unsigned long region_size = 16;
+#endif
static unsigned int __initdata ncr_53c400a_ports[] = {
0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
};
@@ -296,7 +299,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
};
int flags = 0;
struct Scsi_Host *instance;
-#ifdef CONFIG_SCSI_G_NCR5380_MEM
+#ifdef SCSI_G_NCR5380_MEM
unsigned long base;
void __iomem *iomem;
#endif
@@ -315,17 +318,15 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
overrides[0].board = BOARD_NCR53C400A;
else if (dtc_3181e != NCR_NOT_SET)
overrides[0].board = BOARD_DTC3181E;
-
+#ifndef SCSI_G_NCR5380_MEM
if (!current_override && isapnp_present()) {
struct pnp_dev *dev = NULL;
count = 0;
while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
if (count >= NO_OVERRIDES)
break;
- if (pnp_device_attach(dev) < 0) {
- printk(KERN_ERR "dtc436e probe: attach failed\n");
+ if (pnp_device_attach(dev) < 0)
continue;
- }
if (pnp_activate_dev(dev) < 0) {
printk(KERN_ERR "dtc436e probe: activate failed\n");
pnp_device_detach(dev);
@@ -349,7 +350,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
count++;
}
}
-
+#endif
tpnt->proc_name = "g_NCR5380";
for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
@@ -374,7 +375,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
break;
}
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
if (ports) {
/* wakeup sequence for the NCR53C400A and DTC3181E */
@@ -436,7 +437,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
#endif
instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
if (instance == NULL) {
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
release_region(overrides[current_override].NCR5380_map_name, region_size);
#else
iounmap(iomem);
@@ -446,10 +447,10 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
}
instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
instance->n_io_port = region_size;
#else
- ((struct NCR5380_hostdata *)instance->hostdata).iomem = iomem;
+ ((struct NCR5380_hostdata *)instance->hostdata)->iomem = iomem;
#endif
NCR5380_init(instance, flags);
@@ -517,10 +518,10 @@ int generic_NCR5380_release_resources(struct Scsi_Host *instance)
free_irq(instance->irq, instance);
NCR5380_exit(instance);
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
release_region(instance->NCR5380_instance_name, instance->n_io_port);
#else
- iounmap(((struct NCR5380_hostdata *)instance->hostdata).iomem);
+ iounmap(((struct NCR5380_hostdata *)instance->hostdata)->iomem);
release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size);
#endif
@@ -590,14 +591,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst,
}
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY);
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
int i;
for (i = 0; i < 128; i++)
dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
#endif
start += 128;
@@ -610,14 +611,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst,
// FIXME - no timeout
}
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
int i;
for (i = 0; i < 128; i++)
dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
#endif
start += 128;
@@ -676,13 +677,13 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src,
}
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
; // FIXME - timeout
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
for (i = 0; i < 128; i++)
NCR5380_write(C400_HOST_BUFFER, src[start + i]);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
#endif
start += 128;
@@ -692,13 +693,13 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src,
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
; // FIXME - no timeout
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
for (i = 0; i < 128; i++)
NCR5380_write(C400_HOST_BUFFER, src[start + i]);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
#endif
start += 128;
@@ -938,7 +939,7 @@ module_param(ncr_53c400a, int, 0);
module_param(dtc_3181e, int, 0);
MODULE_LICENSE("GPL");
-
+#ifndef SCSI_G_NCR5380_MEM
static struct isapnp_device_id id_table[] __devinitdata = {
{
ISAPNP_ANY_ID, ISAPNP_ANY_ID,
@@ -948,7 +949,7 @@ static struct isapnp_device_id id_table[] __devinitdata = {
};
MODULE_DEVICE_TABLE(isapnp, id_table);
-
+#endif
__setup("ncr5380=", do_NCR5380_setup);
__setup("ncr53c400=", do_NCR53C400_setup);
diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
index df0b3f6..921764c 100644
--- a/drivers/scsi/g_NCR5380.h
+++ b/drivers/scsi/g_NCR5380.h
@@ -63,7 +63,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *);
#define __STRVAL(x) #x
#define STRVAL(x) __STRVAL(x)
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
#define NCR5380_map_config port
#define NCR5380_map_type int
@@ -91,7 +91,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *);
NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
#else
-/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+/* therefore SCSI_G_NCR5380_MEM */
#define NCR5380_map_config memory
#define NCR5380_map_type unsigned long
@@ -114,7 +114,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *);
register void __iomem *iomem
#define NCR5380_setup(instance) \
- iomem = (((struct NCR5380_hostdata *)(instance)->hostdata).iomem)
+ iomem = (((struct NCR5380_hostdata *)(instance)->hostdata)->iomem)
#endif
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index f672d62..b860d65 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -4914,7 +4914,7 @@ static int __init gdth_eisa_probe_one(u16 eisa_slot)
error = scsi_add_host(shp, NULL);
if (error)
- goto out_free_coal_stat;
+ goto out_free_ccb_phys;
list_add_tail(&ha->list, &gdth_instances);
gdth_timer_init();
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bd96cec..9f75a6d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -433,6 +433,9 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
{
switch (tgt->action) {
case IBMVFC_TGT_ACTION_DEL_RPORT:
+ if (action == IBMVFC_TGT_ACTION_DELETED_RPORT)
+ tgt->action = action;
+ case IBMVFC_TGT_ACTION_DELETED_RPORT:
break;
default:
if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
@@ -2036,95 +2039,108 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
}
/**
- * ibmvfc_abort_task_set - Abort outstanding commands to the device
- * @sdev: scsi device to abort commands
- *
- * This sends an Abort Task Set to the VIOS for the specified device. This does
- * NOT send any cancel to the VIOS. That must be done separately.
+ * ibmvfc_match_rport - Match function for specified remote port
+ * @evt: ibmvfc event struct
+ * @device: device to match (rport)
*
* Returns:
- * 0 on success / other on failure
+ * 1 if event matches rport / 0 if event does not match rport
**/
-static int ibmvfc_abort_task_set(struct scsi_device *sdev)
+static int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport)
{
- struct ibmvfc_host *vhost = shost_priv(sdev->host);
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- struct ibmvfc_cmd *tmf;
- struct ibmvfc_event *evt, *found_evt;
- union ibmvfc_iu rsp_iu;
- struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
- int rsp_rc = -EBUSY;
- unsigned long flags;
- int rsp_code = 0;
+ struct fc_rport *cmd_rport;
- spin_lock_irqsave(vhost->host->host_lock, flags);
- found_evt = NULL;
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (evt->cmnd && evt->cmnd->device == sdev) {
- found_evt = evt;
- break;
- }
- }
-
- if (!found_evt) {
- if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
- sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- return 0;
- }
-
- if (vhost->state == IBMVFC_ACTIVE) {
- evt = ibmvfc_get_event(vhost);
- ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
-
- tmf = &evt->iu.cmd;
- memset(tmf, 0, sizeof(*tmf));
- tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- tmf->resp.len = sizeof(tmf->rsp);
- tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
- tmf->payload_len = sizeof(tmf->iu);
- tmf->resp_len = sizeof(tmf->rsp);
- tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->tgt_scsi_id = rport->port_id;
- int_to_scsilun(sdev->lun, &tmf->iu.lun);
- tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
- tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
- evt->sync_iu = &rsp_iu;
-
- init_completion(&evt->comp);
- rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
+ if (evt->cmnd) {
+ cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
+ if (cmd_rport == rport)
+ return 1;
}
+ return 0;
+}
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
+/**
+ * ibmvfc_match_target - Match function for specified target
+ * @evt: ibmvfc event struct
+ * @device: device to match (starget)
+ *
+ * Returns:
+ * 1 if event matches starget / 0 if event does not match starget
+ **/
+static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
+{
+ if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
+ return 1;
+ return 0;
+}
- if (rsp_rc != 0) {
- sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
- return -EIO;
- }
+/**
+ * ibmvfc_match_lun - Match function for specified LUN
+ * @evt: ibmvfc event struct
+ * @device: device to match (sdev)
+ *
+ * Returns:
+ * 1 if event matches sdev / 0 if event does not match sdev
+ **/
+static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
+{
+ if (evt->cmnd && evt->cmnd->device == device)
+ return 1;
+ return 0;
+}
- sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
- wait_for_completion(&evt->comp);
+/**
+ * ibmvfc_wait_for_ops - Wait for ops to complete
+ * @vhost: ibmvfc host struct
+ * @device: device to match (starget or sdev)
+ * @match: match function
+ *
+ * Returns:
+ * SUCCESS / FAILED
+ **/
+static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
+ int (*match) (struct ibmvfc_event *, void *))
+{
+ struct ibmvfc_event *evt;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ int wait;
+ unsigned long flags;
+ signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
- if (rsp_iu.cmd.status)
- rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+ ENTER;
+ do {
+ wait = 0;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (match(evt, device)) {
+ evt->eh_comp = &comp;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
- if (rsp_code) {
- if (fc_rsp->flags & FCP_RSP_LEN_VALID)
- rsp_code = fc_rsp->data.info.rsp_code;
+ if (wait) {
+ timeout = wait_for_completion_timeout(&comp, timeout);
- sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
- "flags: %x fcp_rsp: %x, scsi_status: %x\n",
- ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
- rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
- fc_rsp->scsi_status);
- rsp_rc = -EIO;
- } else
- sdev_printk(KERN_INFO, sdev, "Abort successful\n");
+ if (!timeout) {
+ wait = 0;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (match(evt, device)) {
+ evt->eh_comp = NULL;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (wait)
+ dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
+ LEAVE;
+ return wait ? FAILED : SUCCESS;
+ }
+ }
+ } while (wait);
- spin_lock_irqsave(vhost->host->host_lock, flags);
- ibmvfc_free_event(evt);
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- return rsp_rc;
+ LEAVE;
+ return SUCCESS;
}
/**
@@ -2212,88 +2228,130 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
}
/**
- * ibmvfc_match_target - Match function for specified target
+ * ibmvfc_match_key - Match function for specified cancel key
* @evt: ibmvfc event struct
- * @device: device to match (starget)
+ * @key: cancel key to match
*
* Returns:
- * 1 if event matches starget / 0 if event does not match starget
+ * 1 if event matches key / 0 if event does not match key
**/
-static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
+static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
{
- if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
- return 1;
- return 0;
-}
+ unsigned long cancel_key = (unsigned long)key;
-/**
- * ibmvfc_match_lun - Match function for specified LUN
- * @evt: ibmvfc event struct
- * @device: device to match (sdev)
- *
- * Returns:
- * 1 if event matches sdev / 0 if event does not match sdev
- **/
-static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
-{
- if (evt->cmnd && evt->cmnd->device == device)
+ if (evt->crq.format == IBMVFC_CMD_FORMAT &&
+ evt->iu.cmd.cancel_key == cancel_key)
return 1;
return 0;
}
/**
- * ibmvfc_wait_for_ops - Wait for ops to complete
- * @vhost: ibmvfc host struct
- * @device: device to match (starget or sdev)
- * @match: match function
+ * ibmvfc_abort_task_set - Abort outstanding commands to the device
+ * @sdev: scsi device to abort commands
+ *
+ * This sends an Abort Task Set to the VIOS for the specified device. This does
+ * NOT send any cancel to the VIOS. That must be done separately.
*
* Returns:
- * SUCCESS / FAILED
+ * 0 on success / other on failure
**/
-static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
- int (*match) (struct ibmvfc_event *, void *))
+static int ibmvfc_abort_task_set(struct scsi_device *sdev)
{
- struct ibmvfc_event *evt;
- DECLARE_COMPLETION_ONSTACK(comp);
- int wait;
- unsigned long flags;
- signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct ibmvfc_cmd *tmf;
+ struct ibmvfc_event *evt, *found_evt;
+ union ibmvfc_iu rsp_iu;
+ struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
+ int rc, rsp_rc = -EBUSY;
+ unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT;
+ int rsp_code = 0;
- ENTER;
- do {
- wait = 0;
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (match(evt, device)) {
- evt->eh_comp = &comp;
- wait++;
- }
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ found_evt = NULL;
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (evt->cmnd && evt->cmnd->device == sdev) {
+ found_evt = evt;
+ break;
}
+ }
+
+ if (!found_evt) {
+ if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+ sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return 0;
+ }
- if (wait) {
- timeout = wait_for_completion_timeout(&comp, timeout);
+ if (vhost->state == IBMVFC_ACTIVE) {
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
- if (!timeout) {
- wait = 0;
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (match(evt, device)) {
- evt->eh_comp = NULL;
- wait++;
- }
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- if (wait)
- dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
- LEAVE;
- return wait ? FAILED : SUCCESS;
- }
+ tmf = &evt->iu.cmd;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
+ tmf->resp.len = sizeof(tmf->rsp);
+ tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
+ tmf->payload_len = sizeof(tmf->iu);
+ tmf->resp_len = sizeof(tmf->rsp);
+ tmf->cancel_key = (unsigned long)sdev->hostdata;
+ tmf->tgt_scsi_id = rport->port_id;
+ int_to_scsilun(sdev->lun, &tmf->iu.lun);
+ tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
+ tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
+ evt->sync_iu = &rsp_iu;
+
+ init_completion(&evt->comp);
+ rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
+ }
+
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rsp_rc != 0) {
+ sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
+ return -EIO;
+ }
+
+ sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
+ timeout = wait_for_completion_timeout(&evt->comp, timeout);
+
+ if (!timeout) {
+ rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ if (!rc) {
+ rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
+ if (rc == SUCCESS)
+ rc = 0;
}
- } while (wait);
- LEAVE;
- return SUCCESS;
+ if (rc) {
+ sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
+ ibmvfc_reset_host(vhost);
+ rsp_rc = 0;
+ goto out;
+ }
+ }
+
+ if (rsp_iu.cmd.status)
+ rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+ if (rsp_code) {
+ if (fc_rsp->flags & FCP_RSP_LEN_VALID)
+ rsp_code = fc_rsp->data.info.rsp_code;
+
+ sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
+ "flags: %x fcp_rsp: %x, scsi_status: %x\n",
+ ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
+ rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+ fc_rsp->scsi_status);
+ rsp_rc = -EIO;
+ } else
+ sdev_printk(KERN_INFO, sdev, "Abort successful\n");
+
+out:
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return rsp_rc;
}
/**
@@ -2351,18 +2409,6 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
}
/**
- * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
- * @sdev: scsi device struct
- * @data: return code
- *
- **/
-static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
-{
- unsigned long *rc = data;
- *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
-}
-
-/**
* ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
@@ -2375,18 +2421,6 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
}
/**
- * ibmvfc_dev_abort_all - Device iterated abort task set function
- * @sdev: scsi device struct
- * @data: return code
- *
- **/
-static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
-{
- unsigned long *rc = data;
- *rc |= ibmvfc_abort_task_set(sdev);
-}
-
-/**
* ibmvfc_eh_target_reset_handler - Reset the target
* @cmd: scsi command struct
*
@@ -2440,19 +2474,22 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
**/
static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
{
- struct scsi_target *starget = to_scsi_target(&rport->dev);
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct Scsi_Host *shost = rport_to_shost(rport);
struct ibmvfc_host *vhost = shost_priv(shost);
- unsigned long cancel_rc = 0;
- unsigned long abort_rc = 0;
- int rc = FAILED;
+ struct fc_rport *dev_rport;
+ struct scsi_device *sdev;
+ unsigned long rc;
ENTER;
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts);
- starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
+ shost_for_each_device(sdev, shost) {
+ dev_rport = starget_to_rport(scsi_target(sdev));
+ if (dev_rport != rport)
+ continue;
+ ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ ibmvfc_abort_task_set(sdev);
+ }
- if (!cancel_rc && !abort_rc)
- rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
+ rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
if (rc == FAILED)
ibmvfc_issue_fc_host_lip(shost);
@@ -4193,11 +4230,15 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt_dbg(tgt, "Deleting rport\n");
list_del(&tgt->queue);
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
fc_remote_port_delete(rport);
del_timer_sync(&tgt->timer);
kref_put(&tgt->kref, ibmvfc_release_tgt);
return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return;
}
if (rport) {
@@ -4297,6 +4338,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
rport = tgt->rport;
tgt->rport = NULL;
list_del(&tgt->queue);
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (rport)
fc_remote_port_delete(rport);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index d7e8dcd..608af39 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,8 +29,8 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.8"
-#define IBMVFC_DRIVER_DATE "(June 17, 2010)"
+#define IBMVFC_DRIVER_VERSION "1.0.9"
+#define IBMVFC_DRIVER_DATE "(August 5, 2010)"
#define IBMVFC_DEFAULT_TIMEOUT 60
#define IBMVFC_ADISC_CANCEL_TIMEOUT 45
@@ -38,6 +38,7 @@
#define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \
(IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT)
#define IBMVFC_INIT_TIMEOUT 120
+#define IBMVFC_ABORT_TIMEOUT 8
#define IBMVFC_ABORT_WAIT_TIMEOUT 40
#define IBMVFC_MAX_REQUESTS_DEFAULT 100
@@ -597,6 +598,7 @@ enum ibmvfc_target_action {
IBMVFC_TGT_ACTION_INIT,
IBMVFC_TGT_ACTION_INIT_WAIT,
IBMVFC_TGT_ACTION_DEL_RPORT,
+ IBMVFC_TGT_ACTION_DELETED_RPORT,
};
struct ibmvfc_target {
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index a771416..1087977 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -2817,7 +2817,6 @@ static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem)
}
cmnd->result = cblk->tastat | (cblk->hastat << 16);
- WARN_ON(cmnd == NULL);
i91u_unmap_scb(host->pci_dev, cmnd);
cmnd->scsi_done(cmnd); /* Notify system DONE */
initio_release_scb(host, cblk); /* Release SCB for current channel */
diff --git a/drivers/firmware/iscsi_boot_sysfs.c b/drivers/scsi/iscsi_boot_sysfs.c
index df6bff7..df6bff7 100644
--- a/drivers/firmware/iscsi_boot_sysfs.c
+++ b/drivers/scsi/iscsi_boot_sysfs.c
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index eac4d09..c797f6b 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1765,14 +1765,14 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
struct fcoe_dev_stats *stats;
lport = shost_priv(sc_cmd->device->host);
- spin_unlock_irq(lport->host->host_lock);
rval = fc_remote_port_chkready(rport);
if (rval) {
sc_cmd->result = rval;
done(sc_cmd);
- goto out;
+ return 0;
}
+ spin_unlock_irq(lport->host->host_lock);
if (!*(struct fc_remote_port **)rport->dd_data) {
/*
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 3482d5a..a50aa03 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -775,6 +775,7 @@ struct lpfc_hba {
uint8_t temp_sensor_support;
/* Fields used for heart beat. */
unsigned long last_completion_time;
+ unsigned long skipped_hb;
struct timer_list hb_tmofunc;
uint8_t hb_outstanding;
enum hba_temp_state over_temp_state;
@@ -817,6 +818,8 @@ struct lpfc_hba {
uint32_t iocb_cnt;
uint32_t iocb_max;
atomic_t sdev_cnt;
+ uint8_t fips_spec_rev;
+ uint8_t fips_level;
};
static inline struct Scsi_Host *
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 162704c..23ce457 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/aer.h>
#include <linux/gfp.h>
+#include <linux/kernel.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -1239,6 +1240,44 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
}
/**
+ * lpfc_fips_level_show - Return the current FIPS level for the HBA
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_fips_level_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", phba->fips_level);
+}
+
+/**
+ * lpfc_fips_rev_show - Return the FIPS Spec revision for the HBA
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_fips_rev_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", phba->fips_spec_rev);
+}
+
+/**
* lpfc_param_show - Return a cfg attribute value in decimal
*
* Description:
@@ -1676,6 +1715,8 @@ static DEVICE_ATTR(max_xri, S_IRUGO, lpfc_max_xri_show, NULL);
static DEVICE_ATTR(used_xri, S_IRUGO, lpfc_used_xri_show, NULL);
static DEVICE_ATTR(npiv_info, S_IRUGO, lpfc_npiv_info_show, NULL);
static DEVICE_ATTR(lpfc_temp_sensor, S_IRUGO, lpfc_temp_sensor_show, NULL);
+static DEVICE_ATTR(lpfc_fips_level, S_IRUGO, lpfc_fips_level_show, NULL);
+static DEVICE_ATTR(lpfc_fips_rev, S_IRUGO, lpfc_fips_rev_show, NULL);
static char *lpfc_soft_wwn_key = "C99G71SL8032A";
@@ -1795,12 +1836,11 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr,
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*buf >= 'a') && (*buf <= 'f'))
- j = ((j << 4) | ((*buf++ -'a') + 10));
- else if ((*buf >= 'A') && (*buf <= 'F'))
- j = ((j << 4) | ((*buf++ -'A') + 10));
- else if ((*buf >= '0') && (*buf <= '9'))
- j = ((j << 4) | (*buf++ -'0'));
+ int value;
+
+ value = hex_to_bin(*buf++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
@@ -1888,12 +1928,11 @@ lpfc_soft_wwnn_store(struct device *dev, struct device_attribute *attr,
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*buf >= 'a') && (*buf <= 'f'))
- j = ((j << 4) | ((*buf++ -'a') + 10));
- else if ((*buf >= 'A') && (*buf <= 'F'))
- j = ((j << 4) | ((*buf++ -'A') + 10));
- else if ((*buf >= '0') && (*buf <= '9'))
- j = ((j << 4) | (*buf++ -'0'));
+ int value;
+
+ value = hex_to_bin(*buf++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
@@ -3279,7 +3318,7 @@ LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
# - Default will result in registering capabilities for all profiles.
#
*/
-unsigned int lpfc_prot_mask = SHOST_DIX_TYPE0_PROTECTION;
+unsigned int lpfc_prot_mask = SHOST_DIF_TYPE1_PROTECTION;
module_param(lpfc_prot_mask, uint, 0);
MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
@@ -3384,6 +3423,8 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_iocb_hw,
&dev_attr_txq_hw,
&dev_attr_txcmplq_hw,
+ &dev_attr_lpfc_fips_level,
+ &dev_attr_lpfc_fips_rev,
NULL,
};
@@ -3410,6 +3451,8 @@ struct device_attribute *lpfc_vport_attrs[] = {
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
&dev_attr_lpfc_static_vport,
+ &dev_attr_lpfc_fips_level,
+ &dev_attr_lpfc_fips_rev,
NULL,
};
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index d521569..49d0cf9 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -2724,15 +2724,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmboxq->context2 = ext;
pmboxq->in_ext_byte_len =
- mbox_req->inExtWLen *
- sizeof(uint32_t);
- pmboxq->out_ext_byte_len =
- mbox_req->outExtWLen *
- sizeof(uint32_t);
- pmboxq->mbox_offset_word =
- mbox_req->mbOffset;
- pmboxq->context2 = ext;
- pmboxq->in_ext_byte_len =
mbox_req->inExtWLen * sizeof(uint32_t);
pmboxq->out_ext_byte_len =
mbox_req->outExtWLen * sizeof(uint32_t);
diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h
index a11f1ae..75e2e56 100644
--- a/drivers/scsi/lpfc/lpfc_compat.h
+++ b/drivers/scsi/lpfc/lpfc_compat.h
@@ -82,8 +82,7 @@ lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes)
static inline void
lpfc_memcpy_to_slim( void __iomem *dest, void *src, unsigned int bytes)
{
- /* actually returns 1 byte past dest */
- memcpy_toio( dest, src, bytes);
+ __iowrite32_copy(dest, src, bytes);
}
static inline void
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index afbed6b..8d09191 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -600,6 +600,14 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
spin_unlock_irq(shost->host_lock);
}
+ } else if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
+ /*
+ * Driver needs to re-reg VPI in order for f/w
+ * to update the MAC address.
+ */
+ lpfc_register_new_vport(phba, vport, ndlp);
+ return 0;
}
if (phba->sli_rev < LPFC_SLI_REV4) {
@@ -801,9 +809,12 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
(irsp->un.ulpWord[4] != IOERR_SLI_ABORTED)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP | LOG_ELS,
"2611 FLOGI failed on registered "
- "FCF record fcf_index:%d, trying "
- "to perform round robin failover\n",
- phba->fcf.current_rec.fcf_indx);
+ "FCF record fcf_index(%d), status: "
+ "x%x/x%x, tmo:x%x, trying to perform "
+ "round robin failover\n",
+ phba->fcf.current_rec.fcf_indx,
+ irsp->ulpStatus, irsp->un.ulpWord[4],
+ irsp->ulpTimeout);
fcf_index = lpfc_sli4_fcf_rr_next_index_get(phba);
if (fcf_index == LPFC_FCOE_FCF_NEXT_NONE) {
/*
@@ -841,6 +852,12 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
}
+ /* FLOGI failure */
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "2858 FLOGI failure Status:x%x/x%x TMO:x%x\n",
+ irsp->ulpStatus, irsp->un.ulpWord[4],
+ irsp->ulpTimeout);
+
/* Check for retry */
if (lpfc_els_retry(phba, cmdiocb, rspiocb))
goto out;
@@ -1291,6 +1308,8 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
struct serv_parm *sp;
uint8_t name[sizeof(struct lpfc_name)];
uint32_t rc, keepDID = 0;
+ int put_node;
+ int put_rport;
/* Fabric nodes can have the same WWPN so we don't bother searching
* by WWPN. Just return the ndlp that was given to us.
@@ -1379,6 +1398,28 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
/* Two ndlps cannot have the same did */
ndlp->nlp_DID = keepDID;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ /* Since we are swapping the ndlp passed in with the new one
+ * and the did has already been swapped, copy over the
+ * state and names.
+ */
+ memcpy(&new_ndlp->nlp_portname, &ndlp->nlp_portname,
+ sizeof(struct lpfc_name));
+ memcpy(&new_ndlp->nlp_nodename, &ndlp->nlp_nodename,
+ sizeof(struct lpfc_name));
+ new_ndlp->nlp_state = ndlp->nlp_state;
+ /* Fix up the rport accordingly */
+ rport = ndlp->rport;
+ if (rport) {
+ rdata = rport->dd_data;
+ put_node = rdata->pnode != NULL;
+ put_rport = ndlp->rport != NULL;
+ rdata->pnode = NULL;
+ ndlp->rport = NULL;
+ if (put_node)
+ lpfc_nlp_put(ndlp);
+ if (put_rport)
+ put_device(&rport->dev);
+ }
}
return new_ndlp;
}
@@ -2880,6 +2921,17 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
retry = 0;
if (retry) {
+ if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_FDISC)) {
+ /* Stop retrying PLOGI and FDISC if in FCF discovery */
+ if (phba->fcf.fcf_flag & FCF_DISCOVERY) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2849 Stop retry ELS command "
+ "x%x to remote NPORT x%x, "
+ "Data: x%x x%x\n", cmd, did,
+ cmdiocb->retry, delay);
+ return 0;
+ }
+ }
/* Retry ELS command <elsCmd> to remote NPORT <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
@@ -6076,8 +6128,12 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
if (mb->mbxStatus) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "0915 Register VPI failed: 0x%x\n",
- mb->mbxStatus);
+ "0915 Register VPI failed : Status: x%x"
+ " upd bit: x%x \n", mb->mbxStatus,
+ mb->un.varRegVpi.upd);
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ mb->un.varRegVpi.upd)
+ goto mbox_err_exit ;
switch (mb->mbxStatus) {
case 0x11: /* unsupported feature */
@@ -6142,7 +6198,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
} else
lpfc_do_scr_ns_plogi(phba, vport);
}
-
+mbox_err_exit:
/* Now, we decrement the ndlp reference count held for this
* callback function
*/
@@ -6387,6 +6443,14 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
else
vport->fc_flag |= FC_LOGO_RCVD_DID_CHNG;
spin_unlock_irq(shost->host_lock);
+ } else if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
+ /*
+ * Driver needs to re-reg VPI in order for f/w
+ * to update the MAC address.
+ */
+ lpfc_register_new_vport(phba, vport, ndlp);
+ return ;
}
if (vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI)
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 0639c99..1f62ea8 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -588,7 +588,7 @@ lpfc_work_done(struct lpfc_hba *phba)
(status &
HA_RXMASK));
}
- if (pring->txq_cnt)
+ if ((phba->sli_rev == LPFC_SLI_REV4) && pring->txq_cnt)
lpfc_drain_txq(phba);
/*
* Turn on Ring interrupts
@@ -1852,8 +1852,7 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
__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 |
- FCF_DISCOVERY);
+ phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
spin_unlock_irq(&phba->hbalock);
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2836 The new FCF record (x%x) "
@@ -2651,7 +2650,6 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
spin_unlock_irq(&phba->hbalock);
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY,
"2778 Start FCF table scan at linkup\n");
-
rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba,
LPFC_FCOE_FCF_GET_FIRST);
if (rc) {
@@ -2660,6 +2658,9 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
spin_unlock_irq(&phba->hbalock);
goto out;
}
+ /* Reset FCF roundrobin bmask for new discovery */
+ memset(phba->fcf.fcf_rr_bmask, 0,
+ sizeof(*phba->fcf.fcf_rr_bmask));
}
return;
@@ -5097,6 +5098,7 @@ static void
lpfc_unregister_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_vport *vport = mboxq->vport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if (mboxq->u.mb.mbxStatus) {
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
@@ -5104,6 +5106,9 @@ lpfc_unregister_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
"HBA state x%x\n",
mboxq->u.mb.mbxStatus, vport->port_state);
}
+ spin_lock_irq(shost->host_lock);
+ phba->pport->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
mempool_free(mboxq, phba->mbox_mem_pool);
return;
}
@@ -5285,6 +5290,10 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag |= FCF_INIT_DISC;
spin_unlock_irq(&phba->hbalock);
+
+ /* Reset FCF roundrobin bmask for new discovery */
+ memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask));
+
rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba, LPFC_FCOE_FCF_GET_FIRST);
if (rc) {
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index f5dbf2b..1676f61 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -2291,7 +2291,8 @@ typedef struct {
typedef struct {
#ifdef __BIG_ENDIAN_BITFIELD
uint32_t rsvd1;
- uint32_t rsvd2:8;
+ uint32_t rsvd2:7;
+ uint32_t upd:1;
uint32_t sid:24;
uint32_t wwn[2];
uint32_t rsvd5;
@@ -2300,7 +2301,8 @@ typedef struct {
#else /* __LITTLE_ENDIAN */
uint32_t rsvd1;
uint32_t sid:24;
- uint32_t rsvd2:8;
+ uint32_t upd:1;
+ uint32_t rsvd2:7;
uint32_t wwn[2];
uint32_t rsvd5;
uint16_t vpi;
@@ -2806,11 +2808,15 @@ typedef struct {
uint32_t rsvd6; /* Reserved */
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd7 : 16; /* Reserved */
+ uint32_t fips_rev : 3; /* FIPS Spec Revision */
+ uint32_t fips_level : 4; /* FIPS Level */
+ uint32_t sec_err : 9; /* security crypto error */
uint32_t max_vpi : 16; /* Max number of virt N-Ports */
#else /* __LITTLE_ENDIAN */
uint32_t max_vpi : 16; /* Max number of virt N-Ports */
- uint32_t rsvd7 : 16; /* Reserved */
+ uint32_t sec_err : 9; /* security crypto error */
+ uint32_t fips_level : 4; /* FIPS Level */
+ uint32_t fips_rev : 3; /* FIPS Spec Revision */
#endif
} CONFIG_PORT_VAR;
@@ -3441,63 +3447,63 @@ struct sli3_bg_fields {
static inline uint32_t
lpfc_bgs_get_bidir_bg_prof(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_BIDIR_BG_PROF_MASK) >>
+ return (bgstat & BGS_BIDIR_BG_PROF_MASK) >>
BGS_BIDIR_BG_PROF_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_bidir_err_cond(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_BIDIR_ERR_COND_FLAGS_MASK) >>
+ return (bgstat & BGS_BIDIR_ERR_COND_FLAGS_MASK) >>
BGS_BIDIR_ERR_COND_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_bg_prof(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_BG_PROFILE_MASK) >>
+ return (bgstat & BGS_BG_PROFILE_MASK) >>
BGS_BG_PROFILE_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_invalid_prof(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_INVALID_PROF_MASK) >>
+ return (bgstat & BGS_INVALID_PROF_MASK) >>
BGS_INVALID_PROF_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_uninit_dif_block(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_UNINIT_DIF_BLOCK_MASK) >>
+ return (bgstat & BGS_UNINIT_DIF_BLOCK_MASK) >>
BGS_UNINIT_DIF_BLOCK_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_hi_water_mark_present(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_HI_WATER_MARK_PRESENT_MASK) >>
+ return (bgstat & BGS_HI_WATER_MARK_PRESENT_MASK) >>
BGS_HI_WATER_MARK_PRESENT_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_reftag_err(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_REFTAG_ERR_MASK) >>
+ return (bgstat & BGS_REFTAG_ERR_MASK) >>
BGS_REFTAG_ERR_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_apptag_err(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_APPTAG_ERR_MASK) >>
+ return (bgstat & BGS_APPTAG_ERR_MASK) >>
BGS_APPTAG_ERR_SHIFT;
}
static inline uint32_t
lpfc_bgs_get_guard_err(uint32_t bgstat)
{
- return (le32_to_cpu(bgstat) & BGS_GUARD_ERR_MASK) >>
+ return (bgstat & BGS_GUARD_ERR_MASK) >>
BGS_GUARD_ERR_SHIFT;
}
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 2786ee3..da9ba06 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1032,27 +1032,46 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
/* If there is no heart beat outstanding, issue a heartbeat command */
if (phba->cfg_enable_hba_heartbeat) {
if (!phba->hb_outstanding) {
- pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL);
- if (!pmboxq) {
- mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
- return;
- }
+ if ((!(psli->sli_flag & LPFC_SLI_MBOX_ACTIVE)) &&
+ (list_empty(&psli->mboxq))) {
+ pmboxq = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!pmboxq) {
+ mod_timer(&phba->hb_tmofunc,
+ jiffies +
+ HZ * LPFC_HB_MBOX_INTERVAL);
+ return;
+ }
- lpfc_heart_beat(phba, pmboxq);
- pmboxq->mbox_cmpl = lpfc_hb_mbox_cmpl;
- pmboxq->vport = phba->pport;
- retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
+ lpfc_heart_beat(phba, pmboxq);
+ pmboxq->mbox_cmpl = lpfc_hb_mbox_cmpl;
+ pmboxq->vport = phba->pport;
+ retval = lpfc_sli_issue_mbox(phba, pmboxq,
+ MBX_NOWAIT);
+
+ if (retval != MBX_BUSY &&
+ retval != MBX_SUCCESS) {
+ mempool_free(pmboxq,
+ phba->mbox_mem_pool);
+ mod_timer(&phba->hb_tmofunc,
+ jiffies +
+ HZ * LPFC_HB_MBOX_INTERVAL);
+ return;
+ }
+ phba->skipped_hb = 0;
+ phba->hb_outstanding = 1;
+ } else if (time_before_eq(phba->last_completion_time,
+ phba->skipped_hb)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2857 Last completion time not "
+ " updated in %d ms\n",
+ jiffies_to_msecs(jiffies
+ - phba->last_completion_time));
+ } else
+ phba->skipped_hb = jiffies;
- if (retval != MBX_BUSY && retval != MBX_SUCCESS) {
- mempool_free(pmboxq, phba->mbox_mem_pool);
- mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
- return;
- }
mod_timer(&phba->hb_tmofunc,
jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
- phba->hb_outstanding = 1;
return;
} else {
/*
@@ -3281,10 +3300,10 @@ lpfc_sli4_perform_vport_cvl(struct lpfc_vport *vport)
if (!ndlp)
return 0;
}
- if (phba->pport->port_state <= LPFC_FLOGI)
+ if (phba->pport->port_state < LPFC_FLOGI)
return NULL;
/* If virtual link is not yet instantiated ignore CVL */
- if (vport->port_state <= LPFC_FDISC)
+ if ((vport != phba->pport) && (vport->port_state < LPFC_FDISC))
return NULL;
shost = lpfc_shost_from_vport(vport);
if (!shost)
@@ -3357,21 +3376,7 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
"evt_tag:x%x, fcf_index:x%x\n",
acqe_fcoe->event_tag,
acqe_fcoe->index);
- /* If the FCF discovery is in progress, do nothing. */
- spin_lock_irq(&phba->hbalock);
- if (phba->hba_flag & FCF_DISC_INPROGRESS) {
- spin_unlock_irq(&phba->hbalock);
- break;
- }
- /* If fast FCF failover rescan event is pending, do nothing */
- if (phba->fcf.fcf_flag & FCF_REDISC_EVT) {
- spin_unlock_irq(&phba->hbalock);
- break;
- }
- spin_unlock_irq(&phba->hbalock);
-
- if ((phba->fcf.fcf_flag & FCF_DISCOVERY) &&
- !(phba->fcf.fcf_flag & FCF_REDISC_FOV)) {
+ if (phba->fcf.fcf_flag & FCF_DISCOVERY) {
/*
* During period of FCF discovery, read the FCF
* table record indexed by the event to update
@@ -3385,13 +3390,26 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
acqe_fcoe->index);
rc = lpfc_sli4_read_fcf_rec(phba, acqe_fcoe->index);
}
- /* If the FCF has been in discovered state, do nothing. */
+
+ /* If the FCF discovery is in progress, do nothing. */
spin_lock_irq(&phba->hbalock);
+ if (phba->hba_flag & FCF_DISC_INPROGRESS) {
+ spin_unlock_irq(&phba->hbalock);
+ break;
+ }
+ /* If fast FCF failover rescan event is pending, do nothing */
+ if (phba->fcf.fcf_flag & FCF_REDISC_EVT) {
+ spin_unlock_irq(&phba->hbalock);
+ break;
+ }
+
+ /* If the FCF has been in discovered state, do nothing. */
if (phba->fcf.fcf_flag & FCF_SCAN_DONE) {
spin_unlock_irq(&phba->hbalock);
break;
}
spin_unlock_irq(&phba->hbalock);
+
/* Otherwise, scan the entire FCF table and re-discover SAN */
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY,
"2770 Start FCF table scan due to new FCF "
@@ -3417,13 +3435,9 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
"2549 FCF disconnected from network index 0x%x"
" tag 0x%x\n", acqe_fcoe->index,
acqe_fcoe->event_tag);
- /* If the event is not for currently used fcf do nothing */
- if (phba->fcf.current_rec.fcf_indx != acqe_fcoe->index)
- break;
- /* We request port to rediscover the entire FCF table for
- * a fast recovery from case that the current FCF record
- * is no longer valid if we are not in the middle of FCF
- * failover process already.
+ /*
+ * If we are in the middle of FCF failover process, clear
+ * the corresponding FCF bit in the roundrobin bitmap.
*/
spin_lock_irq(&phba->hbalock);
if (phba->fcf.fcf_flag & FCF_DISCOVERY) {
@@ -3432,9 +3446,23 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
lpfc_sli4_fcf_rr_index_clear(phba, acqe_fcoe->index);
break;
}
+ spin_unlock_irq(&phba->hbalock);
+
+ /* If the event is not for currently used fcf do nothing */
+ if (phba->fcf.current_rec.fcf_indx != acqe_fcoe->index)
+ break;
+
+ /*
+ * Otherwise, request the port to rediscover the entire FCF
+ * table for a fast recovery from case that the current FCF
+ * is no longer valid as we are not in the middle of FCF
+ * failover process already.
+ */
+ spin_lock_irq(&phba->hbalock);
/* Mark the fast failover process in progress */
phba->fcf.fcf_flag |= FCF_DEAD_DISC;
spin_unlock_irq(&phba->hbalock);
+
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY,
"2771 Start FCF fast failover process due to "
"FCF DEAD event: evt_tag:x%x, fcf_index:x%x "
@@ -3454,12 +3482,16 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
* as a link down to FCF registration.
*/
lpfc_sli4_fcf_dead_failthrough(phba);
- } else
- /* Handling fast FCF failover to a DEAD FCF event
- * is considered equalivant to receiving CVL to all
- * vports.
+ } else {
+ /* Reset FCF roundrobin bmask for new discovery */
+ memset(phba->fcf.fcf_rr_bmask, 0,
+ sizeof(*phba->fcf.fcf_rr_bmask));
+ /*
+ * Handling fast FCF failover to a DEAD FCF event is
+ * considered equalivant to receiving CVL to all vports.
*/
lpfc_sli4_perform_all_vport_cvl(phba);
+ }
break;
case LPFC_FCOE_EVENT_TYPE_CVL:
lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
@@ -3534,7 +3566,13 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
* the current registered FCF entry.
*/
lpfc_retry_pport_discovery(phba);
- }
+ } else
+ /*
+ * Reset FCF roundrobin bmask for new
+ * discovery.
+ */
+ memset(phba->fcf.fcf_rr_bmask, 0,
+ sizeof(*phba->fcf.fcf_rr_bmask));
}
break;
default:
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 9c2c7c7..0dfa310 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -815,9 +815,15 @@ void
lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
{
MAILBOX_t *mb = &pmb->u.mb;
+ struct lpfc_hba *phba = vport->phba;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
-
+ /*
+ * Set the re-reg VPI bit for f/w to update the MAC address.
+ */
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI))
+ mb->un.varRegVpi.upd = 1;
mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base;
mb->un.varRegVpi.sid = vport->fc_myDID;
mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index c818a72..2e51aa6 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1325,7 +1325,7 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
pde5->reftag = reftag;
- /* Endian convertion if necessary for PDE5 */
+ /* Endianness conversion if necessary for PDE5 */
pde5->word0 = cpu_to_le32(pde5->word0);
pde5->reftag = cpu_to_le32(pde5->reftag);
@@ -1347,7 +1347,7 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde6_ai, pde6, 1);
bf_set(pde6_apptagval, pde6, apptagval);
- /* Endian convertion if necessary for PDE6 */
+ /* Endianness conversion if necessary for PDE6 */
pde6->word0 = cpu_to_le32(pde6->word0);
pde6->word1 = cpu_to_le32(pde6->word1);
pde6->word2 = cpu_to_le32(pde6->word2);
@@ -1459,7 +1459,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
pde5->reftag = reftag;
- /* Endian convertion if necessary for PDE5 */
+ /* Endianness conversion if necessary for PDE5 */
pde5->word0 = cpu_to_le32(pde5->word0);
pde5->reftag = cpu_to_le32(pde5->reftag);
@@ -1479,7 +1479,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde6_ai, pde6, 1);
bf_set(pde6_apptagval, pde6, apptagval);
- /* Endian convertion if necessary for PDE6 */
+ /* Endianness conversion if necessary for PDE6 */
pde6->word0 = cpu_to_le32(pde6->word0);
pde6->word1 = cpu_to_le32(pde6->word1);
pde6->word2 = cpu_to_le32(pde6->word2);
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index e758eae..fb8905f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1046,7 +1046,7 @@ lpfc_sli_next_iotag(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
} else
spin_unlock_irq(&phba->hbalock);
- lpfc_printf_log(phba, KERN_ERR,LOG_SLI,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0318 Failed to allocate IOTAG.last IOTAG is %d\n",
psli->last_iotag);
@@ -3914,7 +3914,8 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
LPFC_SLI3_HBQ_ENABLED |
LPFC_SLI3_CRP_ENABLED |
- LPFC_SLI3_BG_ENABLED);
+ LPFC_SLI3_BG_ENABLED |
+ LPFC_SLI3_DSS_ENABLED);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0442 Adapter failed to init, mbxCmd x%x "
@@ -3949,8 +3950,23 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
} else
phba->max_vpi = 0;
- if (pmb->u.mb.un.varCfgPort.gdss)
+ phba->fips_level = 0;
+ phba->fips_spec_rev = 0;
+ if (pmb->u.mb.un.varCfgPort.gdss) {
phba->sli3_options |= LPFC_SLI3_DSS_ENABLED;
+ phba->fips_level = pmb->u.mb.un.varCfgPort.fips_level;
+ phba->fips_spec_rev = pmb->u.mb.un.varCfgPort.fips_rev;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2850 Security Crypto Active. FIPS x%d "
+ "(Spec Rev: x%d)",
+ phba->fips_level, phba->fips_spec_rev);
+ }
+ if (pmb->u.mb.un.varCfgPort.sec_err) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2856 Config Port Security Crypto "
+ "Error: x%x ",
+ pmb->u.mb.un.varCfgPort.sec_err);
+ }
if (pmb->u.mb.un.varCfgPort.gerbm)
phba->sli3_options |= LPFC_SLI3_HBQ_ENABLED;
if (pmb->u.mb.un.varCfgPort.gcrp)
@@ -9040,6 +9056,7 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
switch (bf_get(lpfc_cqe_code, &cqevt)) {
case CQE_CODE_COMPL_WQE:
/* Process the WQ/RQ complete event */
+ phba->last_completion_time = jiffies;
workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
(struct lpfc_wcqe_complete *)&cqevt);
break;
@@ -9050,11 +9067,13 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
break;
case CQE_CODE_XRI_ABORTED:
/* Process the WQ XRI abort event */
+ phba->last_completion_time = jiffies;
workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
(struct sli4_wcqe_xri_aborted *)&cqevt);
break;
case CQE_CODE_RECEIVE:
/* Process the RQ event */
+ phba->last_completion_time = jiffies;
workposted = lpfc_sli4_sp_handle_rcqe(phba,
(struct lpfc_rcqe *)&cqevt);
break;
@@ -9276,7 +9295,6 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
{
struct lpfc_wcqe_release wcqe;
bool workposted = false;
- unsigned long iflag;
/* Copy the work queue CQE and convert endian order if needed */
lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
@@ -9285,9 +9303,7 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
case CQE_CODE_COMPL_WQE:
/* Process the WQ complete event */
- spin_lock_irqsave(&phba->hbalock, iflag);
phba->last_completion_time = jiffies;
- spin_unlock_irqrestore(&phba->hbalock, iflag);
lpfc_sli4_fp_handle_fcp_wcqe(phba,
(struct lpfc_wcqe_complete *)&wcqe);
break;
@@ -9298,6 +9314,7 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
break;
case CQE_CODE_XRI_ABORTED:
/* Process the WQ XRI abort event */
+ phba->last_completion_time = jiffies;
workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
(struct sli4_wcqe_xri_aborted *)&wcqe);
break;
@@ -12278,12 +12295,9 @@ lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
spin_lock_irq(&phba->hbalock);
phba->hba_flag |= FCF_DISC_INPROGRESS;
spin_unlock_irq(&phba->hbalock);
- /* Reset FCF round robin index bmask for new scan */
- if (fcf_index == LPFC_FCOE_FCF_GET_FIRST) {
- memset(phba->fcf.fcf_rr_bmask, 0,
- sizeof(*phba->fcf.fcf_rr_bmask));
+ /* Reset eligible FCF count for new scan */
+ if (fcf_index == LPFC_FCOE_FCF_GET_FIRST)
phba->fcf.eligible_fcf_cnt = 0;
- }
error = 0;
}
fail_fcf_scan:
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index d28830a..61afb34 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.15"
+#define LPFC_DRIVER_VERSION "8.3.16"
#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/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
index ee4b691..fda4de3 100644
--- a/drivers/scsi/osd/osd_initiator.c
+++ b/drivers/scsi/osd/osd_initiator.c
@@ -716,7 +716,7 @@ static int _osd_req_list_objects(struct osd_request *or,
return PTR_ERR(bio);
}
- bio->bi_rw &= ~(1 << BIO_RW);
+ bio->bi_rw &= ~REQ_WRITE;
or->in.bio = bio;
or->in.total_bytes = bio->bi_size;
return 0;
@@ -814,7 +814,7 @@ void osd_req_write(struct osd_request *or,
{
_osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
WARN_ON(or->out.bio || or->out.total_bytes);
- WARN_ON(0 == bio_rw_flagged(bio, BIO_RW));
+ WARN_ON(0 == (bio->bi_rw & REQ_WRITE));
or->out.bio = bio;
or->out.total_bytes = len;
}
@@ -829,7 +829,7 @@ int osd_req_write_kern(struct osd_request *or,
if (IS_ERR(bio))
return PTR_ERR(bio);
- bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */
+ bio->bi_rw |= REQ_WRITE; /* FIXME: bio_set_dir() */
osd_req_write(or, obj, offset, bio, len);
return 0;
}
@@ -865,7 +865,7 @@ void osd_req_read(struct osd_request *or,
{
_osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
WARN_ON(or->in.bio || or->in.total_bytes);
- WARN_ON(1 == bio_rw_flagged(bio, BIO_RW));
+ WARN_ON(1 == (bio->bi_rw & REQ_WRITE));
or->in.bio = bio;
or->in.total_bytes = len;
}
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
index d64b717..278b352 100644
--- a/drivers/scsi/osst.c
+++ b/drivers/scsi/osst.c
@@ -5868,7 +5868,8 @@ static int osst_probe(struct device *dev)
}
/* find a free minor number */
- for (i=0; os_scsi_tapes[i] && i<osst_max_dev; i++);
+ for (i = 0; i < osst_max_dev && os_scsi_tapes[i]; i++)
+ ;
if(i >= osst_max_dev) panic ("Scsi_devices corrupt (osst)");
dev_num = i;
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 58d1134..9793aa6 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -4199,8 +4199,10 @@ static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
circularQ = &pm8001_ha->inbnd_q_tbl[0];
memset(&nvmd_req, 0, sizeof(nvmd_req));
rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
+ if (rc) {
+ kfree(fw_control_context);
return rc;
+ }
ccb = &pm8001_ha->ccb_info[tag];
ccb->ccb_tag = tag;
ccb->fw_control_context = fw_control_context;
@@ -4276,8 +4278,10 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
ioctl_payload->length);
memset(&nvmd_req, 0, sizeof(nvmd_req));
rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
+ if (rc) {
+ kfree(fw_control_context);
return rc;
+ }
ccb = &pm8001_ha->ccb_info[tag];
ccb->fw_control_context = fw_control_context;
ccb->ccb_tag = tag;
@@ -4387,6 +4391,7 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
fw_control->len, 0) != 0) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Mem alloc failure\n"));
+ kfree(fw_control_context);
return -ENOMEM;
}
}
@@ -4401,8 +4406,10 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
fw_control_context->virtAddr = buffer;
fw_control_context->len = fw_control->len;
rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
+ if (rc) {
+ kfree(fw_control_context);
return rc;
+ }
ccb = &pm8001_ha->ccb_info[tag];
ccb->fw_control_context = fw_control_context;
ccb->ccb_tag = tag;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ff2172d..8c80b49 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -115,8 +115,8 @@ int ql2xmaxqueues = 1;
module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR);
MODULE_PARM_DESC(ql2xmaxqueues,
"Enables MQ settings "
- "Default is 1 for single queue. Set it to number \
- of queues in MQ mode.");
+ "Default is 1 for single queue. Set it to number "
+ "of queues in MQ mode.");
int ql2xmultique_tag;
module_param(ql2xmultique_tag, int, S_IRUGO|S_IRUSR);
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index a79da8d..9dc0a66 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -36,6 +36,24 @@
#include "ql4_dbg.h"
#include "ql4_nx.h"
+#if defined(CONFIG_PCIEAER)
+#include <linux/aer.h>
+#else
+/* AER releated */
+static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev)
+{
+ return -EINVAL;
+}
+static inline int pci_disable_pcie_error_reporting(struct pci_dev *dev)
+{
+ return -EINVAL;
+}
+static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
+{
+ return -EINVAL;
+}
+#endif
+
#ifndef PCI_DEVICE_ID_QLOGIC_ISP4010
#define PCI_DEVICE_ID_QLOGIC_ISP4010 0x4010
#endif
@@ -137,6 +155,9 @@
#define ISCSI_ALIAS_SIZE 32 /* ISCSI Alias name size */
#define ISCSI_NAME_SIZE 0xE0 /* ISCSI Name size */
+#define QL4_SESS_RECOVERY_TMO 30 /* iSCSI session */
+ /* recovery timeout */
+
#define LSDW(x) ((u32)((u64)(x)))
#define MSDW(x) ((u32)((((u64)(x)) >> 16) >> 16))
@@ -249,7 +270,6 @@ struct ddb_entry {
uint32_t default_time2wait; /* Default Min time between
* relogins (+aens) */
- atomic_t port_down_timer; /* Device connection timer */
atomic_t retry_relogin_timer; /* Min Time between relogins
* (4000 only) */
atomic_t relogin_timer; /* Max Time to wait for relogin to complete */
@@ -378,7 +398,9 @@ struct scsi_qla_host {
#define AF_MSI_ENABLED 16 /* 0x00010000 */
#define AF_MSIX_ENABLED 17 /* 0x00020000 */
#define AF_MBOX_COMMAND_NOPOLL 18 /* 0x00040000 */
-
+#define AF_FW_RECOVERY 19 /* 0x00080000 */
+#define AF_EEH_BUSY 20 /* 0x00100000 */
+#define AF_PCI_CHANNEL_IO_PERM_FAILURE 21 /* 0x00200000 */
unsigned long dpc_flags;
@@ -474,7 +496,6 @@ struct scsi_qla_host {
uint32_t timer_active;
/* Recovery Timers */
- uint32_t port_down_retry_count;
uint32_t discovery_wait;
atomic_t check_relogin_timeouts;
uint32_t retry_reset_ha_cnt;
@@ -615,6 +636,15 @@ static inline int is_qla8022(struct scsi_qla_host *ha)
return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022;
}
+/* Note: Currently AER/EEH is now supported only for 8022 cards
+ * This function needs to be updated when AER/EEH is enabled
+ * for other cards.
+ */
+static inline int is_aer_supported(struct scsi_qla_host *ha)
+{
+ return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022;
+}
+
static inline int adapter_up(struct scsi_qla_host *ha)
{
return (test_bit(AF_ONLINE, &ha->flags) != 0) &&
diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h
index c94c9dd..0336c6d 100644
--- a/drivers/scsi/qla4xxx/ql4_fw.h
+++ b/drivers/scsi/qla4xxx/ql4_fw.h
@@ -673,17 +673,17 @@ struct flash_sys_info {
}; /* 200 */
struct mbx_sys_info {
- uint8_t board_id_str[16]; /* Keep board ID string first */
- /* in this structure for GUI. */
- uint16_t board_id; /* board ID code */
- uint16_t phys_port_cnt; /* number of physical network ports */
- uint16_t port_num; /* network port for this PCI function */
+ uint8_t board_id_str[16]; /* 0-f Keep board ID string first */
+ /* in this structure for GUI. */
+ uint16_t board_id; /* 10-11 board ID code */
+ uint16_t phys_port_cnt; /* 12-13 number of physical network ports */
+ uint16_t port_num; /* 14-15 network port for this PCI function */
/* (port 0 is first port) */
- uint8_t mac_addr[6]; /* MAC address for this PCI function */
- uint32_t iscsi_pci_func_cnt; /* number of iSCSI PCI functions */
- uint32_t pci_func; /* this PCI function */
- unsigned char serial_number[16]; /* serial number string */
- uint8_t reserved[16];
+ uint8_t mac_addr[6]; /* 16-1b MAC address for this PCI function */
+ uint32_t iscsi_pci_func_cnt; /* 1c-1f number of iSCSI PCI functions */
+ uint32_t pci_func; /* 20-23 this PCI function */
+ unsigned char serial_number[16]; /* 24-33 serial number string */
+ uint8_t reserved[12]; /* 34-3f */
};
struct crash_record {
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h
index c9cd5d6..f065204 100644
--- a/drivers/scsi/qla4xxx/ql4_glbl.h
+++ b/drivers/scsi/qla4xxx/ql4_glbl.h
@@ -93,6 +93,7 @@ void qla4xxx_free_irqs(struct scsi_qla_host *ha);
void qla4xxx_process_response_queue(struct scsi_qla_host *ha);
void qla4xxx_wake_dpc(struct scsi_qla_host *ha);
void qla4xxx_get_conn_event_log(struct scsi_qla_host *ha);
+void qla4xxx_mailbox_premature_completion(struct scsi_qla_host *ha);
void qla4_8xxx_pci_config(struct scsi_qla_host *);
int qla4_8xxx_iospace_config(struct scsi_qla_host *ha);
@@ -131,6 +132,7 @@ void qla4_8xxx_idc_unlock(struct scsi_qla_host *ha);
int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha);
void qla4_8xxx_need_qsnt_handler(struct scsi_qla_host *ha);
void qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha);
+inline void qla4_8xxx_set_drv_active(struct scsi_qla_host *ha);
extern int ql4xextended_error_logging;
extern int ql4xdiscoverywait;
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c
index 3007357..4c9be77 100644
--- a/drivers/scsi/qla4xxx/ql4_init.c
+++ b/drivers/scsi/qla4xxx/ql4_init.c
@@ -308,7 +308,6 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha)
DEBUG2(printk("scsi%ld: %s: unable to get firmware "
"state\n", ha->host_no, __func__));
break;
-
}
if (ha->firmware_state & FW_STATE_ERROR) {
@@ -445,6 +444,16 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
{
int status = QLA_ERROR;
+ if (is_aer_supported(ha) &&
+ test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))
+ return status;
+
+ /* For 82xx, stop firmware before initializing because if BIOS
+ * has previously initialized firmware, then driver's initialize
+ * firmware will fail. */
+ if (is_qla8022(ha))
+ qla4_8xxx_stop_firmware(ha);
+
ql4_printk(KERN_INFO, ha, "Initializing firmware..\n");
if (qla4xxx_initialize_fw_cb(ha) == QLA_ERROR) {
DEBUG2(printk("scsi%ld: %s: Failed to initialize firmware "
@@ -669,7 +678,6 @@ static struct ddb_entry * qla4xxx_alloc_ddb(struct scsi_qla_host *ha,
}
ddb_entry->fw_ddb_index = fw_ddb_index;
- atomic_set(&ddb_entry->port_down_timer, ha->port_down_retry_count);
atomic_set(&ddb_entry->retry_relogin_timer, INVALID_ENTRY);
atomic_set(&ddb_entry->relogin_timer, 0);
atomic_set(&ddb_entry->relogin_retry_count, 0);
@@ -1556,8 +1564,6 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
/* Device is back online. */
if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) {
atomic_set(&ddb_entry->state, DDB_STATE_ONLINE);
- atomic_set(&ddb_entry->port_down_timer,
- ha->port_down_retry_count);
atomic_set(&ddb_entry->relogin_retry_count, 0);
atomic_set(&ddb_entry->relogin_timer, 0);
clear_bit(DF_RELOGIN, &ddb_entry->flags);
diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c
index f89973d..4ef9ba1 100644
--- a/drivers/scsi/qla4xxx/ql4_iocb.c
+++ b/drivers/scsi/qla4xxx/ql4_iocb.c
@@ -19,7 +19,7 @@ qla4xxx_space_in_req_ring(struct scsi_qla_host *ha, uint16_t req_cnt)
/* Calculate number of free request entries. */
if ((req_cnt + 2) >= ha->req_q_count) {
- cnt = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out);
+ cnt = (uint16_t) ha->isp_ops->rd_shdw_req_q_out(ha);
if (ha->request_in < cnt)
ha->req_q_count = cnt - ha->request_in;
else
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
index aa65697..2a1ab63 100644
--- a/drivers/scsi/qla4xxx/ql4_isr.c
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -816,6 +816,9 @@ irqreturn_t qla4_8xxx_intr_handler(int irq, void *dev_id)
unsigned long flags = 0;
uint8_t reqs_count = 0;
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return IRQ_HANDLED;
+
ha->isr_count++;
status = qla4_8xxx_rd_32(ha, ISR_INT_VECTOR);
if (!(status & ha->nx_legacy_intr.int_vec_bit))
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index 940ee56..9002170 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -39,6 +39,22 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
"pointer\n", ha->host_no, __func__));
return status;
}
+
+ if (is_qla8022(ha) &&
+ test_bit(AF_FW_RECOVERY, &ha->flags)) {
+ DEBUG2(ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: prematurely "
+ "completing mbx cmd as firmware recovery detected\n",
+ ha->host_no, __func__));
+ return status;
+ }
+
+ if ((is_aer_supported(ha)) &&
+ (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))) {
+ DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Perm failure on EEH, "
+ "timeout MBX Exiting.\n", ha->host_no, __func__));
+ return status;
+ }
+
/* Mailbox code active */
wait_count = MBOX_TOV * 100;
@@ -150,6 +166,7 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
while (test_bit(AF_MBOX_COMMAND_DONE, &ha->flags) == 0) {
if (time_after_eq(jiffies, wait_count))
break;
+
/*
* Service the interrupt.
* The ISR will save the mailbox status registers
@@ -196,6 +213,14 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
/* Check for mailbox timeout. */
if (!test_bit(AF_MBOX_COMMAND_DONE, &ha->flags)) {
+ if (is_qla8022(ha) &&
+ test_bit(AF_FW_RECOVERY, &ha->flags)) {
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "scsi%ld: %s: prematurely completing mbx cmd as "
+ "firmware recovery detected\n",
+ ha->host_no, __func__));
+ goto mbox_exit;
+ }
DEBUG2(printk("scsi%ld: Mailbox Cmd 0x%08X timed out ...,"
" Scheduling Adapter Reset\n", ha->host_no,
mbx_cmd[0]));
@@ -246,6 +271,28 @@ mbox_exit:
return status;
}
+void qla4xxx_mailbox_premature_completion(struct scsi_qla_host *ha)
+{
+ set_bit(AF_FW_RECOVERY, &ha->flags);
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: set FW RECOVERY!\n",
+ ha->host_no, __func__);
+
+ if (test_bit(AF_MBOX_COMMAND, &ha->flags)) {
+ if (test_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags)) {
+ complete(&ha->mbx_intr_comp);
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Due to fw "
+ "recovery, doing premature completion of "
+ "mbx cmd\n", ha->host_no, __func__);
+
+ } else {
+ set_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Due to fw "
+ "recovery, doing premature completion of "
+ "polling mbx cmd\n", ha->host_no, __func__);
+ }
+ }
+}
+
static uint8_t
qla4xxx_set_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd,
uint32_t *mbox_sts, dma_addr_t init_fw_cb_dma)
@@ -361,7 +408,6 @@ qla4xxx_update_local_ifcb(struct scsi_qla_host *ha,
min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/
/* Save Command Line Paramater info */
- ha->port_down_retry_count = le16_to_cpu(init_fw_cb->conn_ka_timeout);
ha->discovery_wait = ql4xdiscoverywait;
if (ha->acb_version == ACB_SUPPORTED) {
diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c
index 3e119ae..e031a73 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.c
+++ b/drivers/scsi/qla4xxx/ql4_nx.c
@@ -1418,7 +1418,7 @@ static int qla4_8xxx_rcvpeg_ready(struct scsi_qla_host *ha)
return QLA_SUCCESS;
}
-static inline void
+inline void
qla4_8xxx_set_drv_active(struct scsi_qla_host *ha)
{
uint32_t drv_active;
@@ -1441,11 +1441,15 @@ qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha)
static inline int
qla4_8xxx_need_reset(struct scsi_qla_host *ha)
{
- uint32_t drv_state;
+ uint32_t drv_state, drv_active;
int rval;
+ drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
rval = drv_state & (1 << (ha->func_num * 4));
+ if ((test_bit(AF_EEH_BUSY, &ha->flags)) && drv_active)
+ rval = 1;
+
return rval;
}
@@ -1949,7 +1953,8 @@ qla4_8xxx_get_fdt_info(struct scsi_qla_host *ha)
uint16_t cnt, chksum;
uint16_t *wptr;
struct qla_fdt_layout *fdt;
- uint16_t mid, fid;
+ uint16_t mid = 0;
+ uint16_t fid = 0;
struct ql82xx_hw_data *hw = &ha->hw;
hw->flash_conf_off = FARX_ACCESS_FLASH_CONF;
@@ -2105,6 +2110,9 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
qla4_8xxx_clear_rst_ready(ha);
qla4_8xxx_idc_unlock(ha);
+ if (rval == QLA_SUCCESS)
+ clear_bit(AF_FW_RECOVERY, &ha->flags);
+
return rval;
}
@@ -2145,7 +2153,8 @@ int qla4_8xxx_get_sys_info(struct scsi_qla_host *ha)
goto exit_validate_mac82;
}
- if (mbox_sts[4] < sizeof(*sys_info)) {
+ /* Make sure we receive the minimum required data to cache internally */
+ if (mbox_sts[4] < offsetof(struct mbx_sys_info, reserved)) {
DEBUG2(printk("scsi%ld: %s: GET_SYS_INFO data receive"
" error (%x)\n", ha->host_no, __func__, mbox_sts[4]));
goto exit_validate_mac82;
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 5529b2a..370d40f 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -163,10 +163,10 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session)
if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
- DEBUG2(printk("scsi%ld: %s: ddb [%d] port down retry count "
+ DEBUG2(printk("scsi%ld: %s: ddb [%d] session recovery timeout "
"of (%d) secs exhausted, marking device DEAD.\n",
ha->host_no, __func__, ddb_entry->fw_ddb_index,
- ha->port_down_retry_count));
+ QL4_SESS_RECOVERY_TMO));
qla4xxx_wake_dpc(ha);
}
@@ -298,7 +298,8 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
{
int err;
- ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count;
+ ddb_entry->sess->recovery_tmo = QL4_SESS_RECOVERY_TMO;
+
err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index);
if (err) {
DEBUG2(printk(KERN_ERR "Could not add session.\n"));
@@ -474,6 +475,14 @@ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
struct srb *srb;
int rval;
+ if (test_bit(AF_EEH_BUSY, &ha->flags)) {
+ if (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))
+ cmd->result = DID_NO_CONNECT << 16;
+ else
+ cmd->result = DID_REQUEUE << 16;
+ goto qc_fail_command;
+ }
+
if (!sess) {
cmd->result = DID_IMM_RETRY << 16;
goto qc_fail_command;
@@ -654,6 +663,13 @@ static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
uint32_t fw_heartbeat_counter, halt_status;
fw_heartbeat_counter = qla4_8xxx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
+ /* If PEG_ALIVE_COUNTER is 0xffffffff, AER/EEH is in progress, ignore */
+ if (fw_heartbeat_counter == 0xffffffff) {
+ DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Device in frozen "
+ "state, QLA82XX_PEG_ALIVE_COUNTER is 0xffffffff\n",
+ ha->host_no, __func__));
+ return;
+ }
if (ha->fw_heartbeat_counter == fw_heartbeat_counter) {
ha->seconds_since_last_heartbeat++;
@@ -662,6 +678,7 @@ static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
ha->seconds_since_last_heartbeat = 0;
halt_status = qla4_8xxx_rd_32(ha,
QLA82XX_PEG_HALT_STATUS1);
+
/* Since we cannot change dev_state in interrupt
* context, set appropriate DPC flag then wakeup
* DPC */
@@ -673,6 +690,7 @@ static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
set_bit(DPC_RESET_HA, &ha->dpc_flags);
}
qla4xxx_wake_dpc(ha);
+ qla4xxx_mailbox_premature_completion(ha);
}
}
ha->fw_heartbeat_counter = fw_heartbeat_counter;
@@ -698,6 +716,7 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
ha->host_no, __func__);
set_bit(DPC_RESET_HA, &ha->dpc_flags);
qla4xxx_wake_dpc(ha);
+ qla4xxx_mailbox_premature_completion(ha);
} else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
!test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags)) {
printk("scsi%ld: %s: HW State: NEED QUIES!\n",
@@ -719,6 +738,19 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
{
struct ddb_entry *ddb_entry, *dtemp;
int start_dpc = 0;
+ uint16_t w;
+
+ /* If we are in the middle of AER/EEH processing
+ * skip any processing and reschedule the timer
+ */
+ if (test_bit(AF_EEH_BUSY, &ha->flags)) {
+ mod_timer(&ha->timer, jiffies + HZ);
+ return;
+ }
+
+ /* Hardware read to trigger an EEH error during mailbox waits. */
+ if (!pci_channel_offline(ha->pdev))
+ pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w);
if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) {
DEBUG2(ql4_printk(KERN_INFO, ha, "%s exited. HBA GOING AWAY\n",
@@ -1207,7 +1239,13 @@ static void qla4xxx_do_dpc(struct work_struct *work)
/* Initialization not yet finished. Don't do anything yet. */
if (!test_bit(AF_INIT_DONE, &ha->flags))
- return;
+ goto do_dpc_exit;
+
+ if (test_bit(AF_EEH_BUSY, &ha->flags)) {
+ DEBUG2(printk(KERN_INFO "scsi%ld: %s: flags = %lx\n",
+ ha->host_no, __func__, ha->flags));
+ goto do_dpc_exit;
+ }
/* HBA is in the process of being permanently disabled.
* Don't process anything */
@@ -1346,6 +1384,8 @@ dpc_post_reset_ha:
}
}
}
+
+do_dpc_exit:
clear_bit(AF_DPC_SCHEDULED, &ha->flags);
}
@@ -1612,6 +1652,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
ha->host = host;
ha->host_no = host->host_no;
+ pci_enable_pcie_error_reporting(pdev);
+
/* Setup Runtime configurable options */
if (is_qla8022(ha)) {
ha->isp_ops = &qla4_8xxx_isp_ops;
@@ -1630,6 +1672,10 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
ha->isp_ops = &qla4xxx_isp_ops;
}
+ /* Set EEH reset type to fundamental if required by hba */
+ if (is_qla8022(ha))
+ pdev->needs_freset = 1;
+
/* Configure PCI I/O space. */
ret = ha->isp_ops->iospace_config(ha);
if (ret)
@@ -1726,6 +1772,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
}
}
+ pci_save_state(ha->pdev);
ha->isp_ops->enable_intrs(ha);
/* Start timer thread. */
@@ -1752,6 +1799,7 @@ probe_failed:
qla4xxx_free_adapter(ha);
probe_failed_ioconfig:
+ pci_disable_pcie_error_reporting(pdev);
scsi_host_put(ha->host);
probe_disable_device:
@@ -1781,6 +1829,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
scsi_host_put(ha->host);
+ pci_disable_pcie_error_reporting(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
@@ -1877,6 +1926,17 @@ static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha,
int done = 0;
struct srb *rp;
uint32_t max_wait_time = EH_WAIT_CMD_TOV;
+ int ret = SUCCESS;
+
+ /* Dont wait on command if PCI error is being handled
+ * by PCI AER driver
+ */
+ if (unlikely(pci_channel_offline(ha->pdev)) ||
+ (test_bit(AF_EEH_BUSY, &ha->flags))) {
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: Return from %s\n",
+ ha->host_no, __func__);
+ return ret;
+ }
do {
/* Checking to see if its returned to OS */
@@ -2172,6 +2232,252 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
return return_status;
}
+/* PCI AER driver recovers from all correctable errors w/o
+ * driver intervention. For uncorrectable errors PCI AER
+ * driver calls the following device driver's callbacks
+ *
+ * - Fatal Errors - link_reset
+ * - Non-Fatal Errors - driver's pci_error_detected() which
+ * returns CAN_RECOVER, NEED_RESET or DISCONNECT.
+ *
+ * PCI AER driver calls
+ * CAN_RECOVER - driver's pci_mmio_enabled(), mmio_enabled
+ * returns RECOVERED or NEED_RESET if fw_hung
+ * NEED_RESET - driver's slot_reset()
+ * DISCONNECT - device is dead & cannot recover
+ * RECOVERED - driver's pci_resume()
+ */
+static pci_ers_result_t
+qla4xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct scsi_qla_host *ha = pci_get_drvdata(pdev);
+
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: error detected:state %x\n",
+ ha->host_no, __func__, state);
+
+ if (!is_aer_supported(ha))
+ return PCI_ERS_RESULT_NONE;
+
+ switch (state) {
+ case pci_channel_io_normal:
+ clear_bit(AF_EEH_BUSY, &ha->flags);
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ set_bit(AF_EEH_BUSY, &ha->flags);
+ qla4xxx_mailbox_premature_completion(ha);
+ qla4xxx_free_irqs(ha);
+ pci_disable_device(pdev);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ set_bit(AF_EEH_BUSY, &ha->flags);
+ set_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags);
+ qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * qla4xxx_pci_mmio_enabled() gets called if
+ * qla4xxx_pci_error_detected() returns PCI_ERS_RESULT_CAN_RECOVER
+ * and read/write to the device still works.
+ **/
+static pci_ers_result_t
+qla4xxx_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ struct scsi_qla_host *ha = pci_get_drvdata(pdev);
+
+ if (!is_aer_supported(ha))
+ return PCI_ERS_RESULT_NONE;
+
+ if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: firmware hang -- "
+ "mmio_enabled\n", ha->host_no, __func__);
+ return PCI_ERS_RESULT_NEED_RESET;
+ } else
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+uint32_t qla4_8xxx_error_recovery(struct scsi_qla_host *ha)
+{
+ uint32_t rval = QLA_ERROR;
+ int fn;
+ struct pci_dev *other_pdev = NULL;
+
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: In %s\n", ha->host_no, __func__);
+
+ set_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
+
+ if (test_bit(AF_ONLINE, &ha->flags)) {
+ clear_bit(AF_ONLINE, &ha->flags);
+ qla4xxx_mark_all_devices_missing(ha);
+ qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+ qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
+ }
+
+ fn = PCI_FUNC(ha->pdev->devfn);
+ while (fn > 0) {
+ fn--;
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Finding PCI device at "
+ "func %x\n", ha->host_no, __func__, fn);
+ /* Get the pci device given the domain, bus,
+ * slot/function number */
+ other_pdev =
+ pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
+ ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
+ fn));
+
+ if (!other_pdev)
+ continue;
+
+ if (atomic_read(&other_pdev->enable_cnt)) {
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Found PCI "
+ "func in enabled state%x\n", ha->host_no,
+ __func__, fn);
+ pci_dev_put(other_pdev);
+ break;
+ }
+ pci_dev_put(other_pdev);
+ }
+
+ /* The first function on the card, the reset owner will
+ * start & initialize the firmware. The other functions
+ * on the card will reset the firmware context
+ */
+ if (!fn) {
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: devfn being reset "
+ "0x%x is the owner\n", ha->host_no, __func__,
+ ha->pdev->devfn);
+
+ qla4_8xxx_idc_lock(ha);
+ qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_COLD);
+
+ qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
+ QLA82XX_IDC_VERSION);
+
+ qla4_8xxx_idc_unlock(ha);
+ clear_bit(AF_FW_RECOVERY, &ha->flags);
+ rval = qla4xxx_initialize_adapter(ha, PRESERVE_DDB_LIST);
+ qla4_8xxx_idc_lock(ha);
+
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: HW State: "
+ "FAILED\n", ha->host_no, __func__);
+ qla4_8xxx_clear_drv_active(ha);
+ qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_FAILED);
+ } else {
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: HW State: "
+ "READY\n", ha->host_no, __func__);
+ qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_READY);
+ /* Clear driver state register */
+ qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
+ qla4_8xxx_set_drv_active(ha);
+ ha->isp_ops->enable_intrs(ha);
+ }
+ qla4_8xxx_idc_unlock(ha);
+ } else {
+ ql4_printk(KERN_INFO, ha, "scsi%ld: %s: devfn 0x%x is not "
+ "the reset owner\n", ha->host_no, __func__,
+ ha->pdev->devfn);
+ if ((qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
+ QLA82XX_DEV_READY)) {
+ clear_bit(AF_FW_RECOVERY, &ha->flags);
+ rval = qla4xxx_initialize_adapter(ha,
+ PRESERVE_DDB_LIST);
+ if (rval == QLA_SUCCESS)
+ ha->isp_ops->enable_intrs(ha);
+ qla4_8xxx_idc_lock(ha);
+ qla4_8xxx_set_drv_active(ha);
+ qla4_8xxx_idc_unlock(ha);
+ }
+ }
+ clear_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
+ return rval;
+}
+
+static pci_ers_result_t
+qla4xxx_pci_slot_reset(struct pci_dev *pdev)
+{
+ pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
+ struct scsi_qla_host *ha = pci_get_drvdata(pdev);
+ int rc;
+
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: slot_reset\n",
+ ha->host_no, __func__);
+
+ if (!is_aer_supported(ha))
+ return PCI_ERS_RESULT_NONE;
+
+ /* Restore the saved state of PCIe device -
+ * BAR registers, PCI Config space, PCIX, MSI,
+ * IOV states
+ */
+ 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);
+
+ /* Initialize device or resume if in suspended state */
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: Cant re-enable "
+ "device after reset\n", ha->host_no, __func__);
+ goto exit_slot_reset;
+ }
+
+ ret = qla4xxx_request_irqs(ha);
+ if (ret) {
+ ql4_printk(KERN_WARNING, ha, "Failed to reserve interrupt %d"
+ " already in use.\n", pdev->irq);
+ goto exit_slot_reset;
+ }
+
+ if (is_qla8022(ha)) {
+ if (qla4_8xxx_error_recovery(ha) == QLA_SUCCESS) {
+ ret = PCI_ERS_RESULT_RECOVERED;
+ goto exit_slot_reset;
+ } else
+ goto exit_slot_reset;
+ }
+
+exit_slot_reset:
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: Return=%x\n"
+ "device after reset\n", ha->host_no, __func__, ret);
+ return ret;
+}
+
+static void
+qla4xxx_pci_resume(struct pci_dev *pdev)
+{
+ struct scsi_qla_host *ha = pci_get_drvdata(pdev);
+ int ret;
+
+ ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: pci_resume\n",
+ ha->host_no, __func__);
+
+ ret = qla4xxx_wait_for_hba_online(ha);
+ if (ret != QLA_SUCCESS) {
+ ql4_printk(KERN_ERR, ha, "scsi%ld: %s: the device failed to "
+ "resume I/O from slot/link_reset\n", ha->host_no,
+ __func__);
+ }
+
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+ clear_bit(AF_EEH_BUSY, &ha->flags);
+}
+
+static struct pci_error_handlers qla4xxx_err_handler = {
+ .error_detected = qla4xxx_pci_error_detected,
+ .mmio_enabled = qla4xxx_pci_mmio_enabled,
+ .slot_reset = qla4xxx_pci_slot_reset,
+ .resume = qla4xxx_pci_resume,
+};
+
static struct pci_device_id qla4xxx_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_QLOGIC,
@@ -2206,6 +2512,7 @@ static struct pci_driver qla4xxx_pci_driver = {
.id_table = qla4xxx_pci_tbl,
.probe = qla4xxx_probe_adapter,
.remove = qla4xxx_remove_adapter,
+ .err_handler = &qla4xxx_err_handler,
};
static int __init qla4xxx_module_init(void)
diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h
index c905dbd..a77b973 100644
--- a/drivers/scsi/qla4xxx/ql4_version.h
+++ b/drivers/scsi/qla4xxx/ql4_version.h
@@ -5,4 +5,4 @@
* See LICENSE.qla4xxx for copyright and licensing details.
*/
-#define QLA4XXX_DRIVER_VERSION "5.02.00-k2"
+#define QLA4XXX_DRIVER_VERSION "5.02.00-k3"
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 53d7ed0..f8c561c 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -704,7 +704,7 @@ static void __devexit qpti_chain_del(struct qlogicpti *qpti)
static int __devinit qpti_map_regs(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
qpti->qregs = of_ioremap(&op->resource[0], 0,
resource_size(&op->resource[0]),
@@ -727,7 +727,7 @@ static int __devinit qpti_map_regs(struct qlogicpti *qpti)
static int __devinit qpti_register_irq(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
qpti->qhost->irq = qpti->irq = op->archdata.irqs[0];
@@ -752,7 +752,7 @@ fail:
static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
struct device_node *dp;
dp = op->dev.of_node;
@@ -773,7 +773,7 @@ static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti)
static void qpti_get_bursts(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
u8 bursts, bmask;
bursts = of_getintprop_default(op->dev.of_node, "burst-sizes", 0xff);
@@ -806,7 +806,7 @@ static void qpti_get_clock(struct qlogicpti *qpti)
*/
static int __devinit qpti_map_queues(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
qpti->res_cpu = dma_alloc_coherent(&op->dev,
@@ -1290,7 +1290,7 @@ static struct scsi_host_template qpti_template = {
.use_clustering = ENABLE_CLUSTERING,
};
-static int __devinit qpti_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit qpti_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct scsi_host_template *tpnt = match->data;
struct device_node *dp = op->dev.of_node;
@@ -1401,7 +1401,7 @@ fail_unlink:
return -ENODEV;
}
-static int __devexit qpti_sbus_remove(struct of_device *op)
+static int __devexit qpti_sbus_remove(struct platform_device *op)
{
struct qlogicpti *qpti = dev_get_drvdata(&op->dev);
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
index e3c74d1..4377e87 100644
--- a/drivers/scsi/qlogicpti.h
+++ b/drivers/scsi/qlogicpti.h
@@ -342,7 +342,7 @@ struct qlogicpti {
u_int req_in_ptr; /* index of next request slot */
u_int res_out_ptr; /* index of next result slot */
long send_marker; /* must we send a marker? */
- struct of_device *op;
+ struct platform_device *op;
unsigned long __pad;
int cmd_count[MAX_TARGETS];
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 2bf9846..1de30eb 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -320,7 +320,7 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
"changed. The Linux SCSI layer does not "
"automatically adjust these parameters.\n");
- if (blk_barrier_rq(scmd->request))
+ if (scmd->request->cmd_flags & REQ_HARDBARRIER)
/*
* barrier requests should always retry on UA
* otherwise block will get a spurious error
@@ -473,14 +473,17 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
*/
return SUCCESS;
case RESERVATION_CONFLICT:
- /*
- * let issuer deal with this, it could be just fine
- */
- return SUCCESS;
+ if (scmd->cmnd[0] == TEST_UNIT_READY)
+ /* it is a success, we probed the device and
+ * found it */
+ return SUCCESS;
+ /* otherwise, we failed to send the command */
+ return FAILED;
case QUEUE_FULL:
scsi_handle_queue_full(scmd->device);
/* fall through */
case BUSY:
+ return NEEDS_RETRY;
default:
return FAILED;
}
@@ -1331,16 +1334,16 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd)
case DID_OK:
break;
case DID_BUS_BUSY:
- return blk_failfast_transport(scmd->request);
+ return (scmd->request->cmd_flags & REQ_FAILFAST_TRANSPORT);
case DID_PARITY:
- return blk_failfast_dev(scmd->request);
+ return (scmd->request->cmd_flags & REQ_FAILFAST_DEV);
case DID_ERROR:
if (msg_byte(scmd->result) == COMMAND_COMPLETE &&
status_byte(scmd->result) == RESERVATION_CONFLICT)
return 0;
/* fall through */
case DID_SOFT_ERROR:
- return blk_failfast_driver(scmd->request);
+ return (scmd->request->cmd_flags & REQ_FAILFAST_DRIVER);
}
switch (status_byte(scmd->result)) {
@@ -1349,7 +1352,9 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd)
* assume caller has checked sense and determinted
* the check condition was retryable.
*/
- return blk_failfast_dev(scmd->request);
+ if (scmd->request->cmd_flags & REQ_FAILFAST_DEV ||
+ scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)
+ return 1;
}
return 0;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 1646fe7..9ade720 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -85,7 +85,7 @@ static void scsi_unprep_request(struct request *req)
{
struct scsi_cmnd *cmd = req->special;
- req->cmd_flags &= ~REQ_DONTPREP;
+ blk_unprep_request(req);
req->special = NULL;
scsi_put_command(cmd);
@@ -722,7 +722,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
- if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
+ if (req->cmd_type == REQ_TYPE_BLOCK_PC) { /* SG_IO ioctl from block level */
req->errors = result;
if (result) {
if (sense_valid && req->sense) {
@@ -757,7 +757,8 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
}
}
- BUG_ON(blk_bidi_rq(req)); /* bidi not support for !blk_pc_request yet */
+ /* no bidi support for !REQ_TYPE_BLOCK_PC yet */
+ BUG_ON(blk_bidi_rq(req));
/*
* Next deal with any sectors which we were able to correctly
@@ -1010,11 +1011,8 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
err_exit:
scsi_release_buffers(cmd);
- if (error == BLKPREP_KILL)
- scsi_put_command(cmd);
- else /* BLKPREP_DEFER */
- scsi_unprep_request(cmd->request);
-
+ scsi_put_command(cmd);
+ cmd->request->special = NULL;
return error;
}
EXPORT_SYMBOL(scsi_init_io);
@@ -1372,12 +1370,6 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
blk_start_request(req);
- if (unlikely(cmd == NULL)) {
- printk(KERN_CRIT "impossible request in %s.\n",
- __func__);
- BUG();
- }
-
sdev = cmd->device;
starget = scsi_target(sdev);
shost = sdev->host;
diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c
index 66241dd..c399be9 100644
--- a/drivers/scsi/scsi_tgt_lib.c
+++ b/drivers/scsi/scsi_tgt_lib.c
@@ -185,6 +185,7 @@ static void scsi_tgt_cmd_destroy(struct work_struct *work)
dprintk("cmd %p %d %u\n", cmd, cmd->sc_data_direction,
rq_data_dir(cmd->request));
scsi_unmap_user_pages(tcmd);
+ tcmd->rq->bio = NULL;
scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd);
}
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index edb6b36..d7e470a 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -29,6 +29,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/kernel.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
@@ -1730,12 +1731,11 @@ fc_parse_wwn(const char *ns, u64 *nm)
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*ns >= 'a') && (*ns <= 'f'))
- j = ((j << 4) | ((*ns++ -'a') + 10));
- else if ((*ns >= 'A') && (*ns <= 'F'))
- j = ((j << 4) | ((*ns++ -'A') + 10));
- else if ((*ns >= '0') && (*ns <= '9'))
- j = ((j << 4) | (*ns++ -'0'));
+ int value;
+
+ value = hex_to_bin(*ns++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index cc8a1d1..2714bec 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -46,6 +46,7 @@
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
+#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include <linux/string_helpers.h>
#include <linux/async.h>
@@ -118,8 +119,8 @@ static DEFINE_IDA(sd_index_ida);
* object after last put) */
static DEFINE_MUTEX(sd_ref_mutex);
-struct kmem_cache *sd_cdb_cache;
-mempool_t *sd_cdb_pool;
+static struct kmem_cache *sd_cdb_cache;
+static mempool_t *sd_cdb_pool;
static const char *sd_cache_types[] = {
"write through", "none", "write back",
@@ -146,7 +147,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) {
- const int len = strlen(sd_cache_types[i]);
+ len = strlen(sd_cache_types[i]);
if (strncmp(sd_cache_types[i], buf, len) == 0 &&
buf[len] == '\n') {
ct = i;
@@ -411,54 +412,85 @@ static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif)
}
/**
- * sd_prepare_discard - unmap blocks on thinly provisioned device
+ * scsi_setup_discard_cmnd - unmap blocks on thinly provisioned device
+ * @sdp: scsi device to operate one
* @rq: Request to prepare
*
* Will issue either UNMAP or WRITE SAME(16) depending on preference
* indicated by target device.
**/
-static int sd_prepare_discard(struct request *rq)
+static int scsi_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
{
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
struct bio *bio = rq->bio;
sector_t sector = bio->bi_sector;
- unsigned int num = bio_sectors(bio);
+ unsigned int nr_sectors = bio_sectors(bio);
+ unsigned int len;
+ int ret;
+ struct page *page;
if (sdkp->device->sector_size == 4096) {
sector >>= 3;
- num >>= 3;
+ nr_sectors >>= 3;
}
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->timeout = SD_TIMEOUT;
memset(rq->cmd, 0, rq->cmd_len);
+ page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+ if (!page)
+ return BLKPREP_DEFER;
+
if (sdkp->unmap) {
- char *buf = kmap_atomic(bio_page(bio), KM_USER0);
+ char *buf = page_address(page);
+ rq->cmd_len = 10;
rq->cmd[0] = UNMAP;
rq->cmd[8] = 24;
- rq->cmd_len = 10;
-
- /* Ensure that data length matches payload */
- rq->__data_len = bio->bi_size = bio->bi_io_vec->bv_len = 24;
put_unaligned_be16(6 + 16, &buf[0]);
put_unaligned_be16(16, &buf[2]);
put_unaligned_be64(sector, &buf[8]);
- put_unaligned_be32(num, &buf[16]);
+ put_unaligned_be32(nr_sectors, &buf[16]);
- kunmap_atomic(buf, KM_USER0);
+ len = 24;
} else {
+ rq->cmd_len = 16;
rq->cmd[0] = WRITE_SAME_16;
rq->cmd[1] = 0x8; /* UNMAP */
put_unaligned_be64(sector, &rq->cmd[2]);
- put_unaligned_be32(num, &rq->cmd[10]);
- rq->cmd_len = 16;
+ put_unaligned_be32(nr_sectors, &rq->cmd[10]);
+
+ len = sdkp->device->sector_size;
+ }
+
+ blk_add_request_payload(rq, page, len);
+ ret = scsi_setup_blk_pc_cmnd(sdp, rq);
+ rq->buffer = page_address(page);
+ if (ret != BLKPREP_OK) {
+ __free_page(page);
+ rq->buffer = NULL;
}
+ return ret;
+}
- return BLKPREP_OK;
+static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
+{
+ rq->timeout = SD_TIMEOUT;
+ rq->retries = SD_MAX_RETRIES;
+ rq->cmd[0] = SYNCHRONIZE_CACHE;
+ rq->cmd_len = 10;
+
+ return scsi_setup_blk_pc_cmnd(sdp, rq);
+}
+
+static void sd_unprep_fn(struct request_queue *q, struct request *rq)
+{
+ if (rq->cmd_flags & REQ_DISCARD) {
+ free_page((unsigned long)rq->buffer);
+ rq->buffer = NULL;
+ }
}
/**
@@ -485,10 +517,13 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
* Discard request come in as REQ_TYPE_FS but we turn them into
* block PC requests to make life easier.
*/
- if (blk_discard_rq(rq))
- ret = sd_prepare_discard(rq);
-
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
+ if (rq->cmd_flags & REQ_DISCARD) {
+ ret = scsi_setup_discard_cmnd(sdp, rq);
+ goto out;
+ } else if (rq->cmd_flags & REQ_FLUSH) {
+ ret = scsi_setup_flush_cmnd(sdp, rq);
+ goto out;
+ } else if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
ret = scsi_setup_blk_pc_cmnd(sdp, rq);
goto out;
} else if (rq->cmd_type != REQ_TYPE_FS) {
@@ -636,7 +671,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD;
SCpnt->cmnd[7] = 0x18;
SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32;
- SCpnt->cmnd[10] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+ SCpnt->cmnd[10] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
/* LBA */
SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
@@ -661,7 +696,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
SCpnt->cmnd[31] = (unsigned char) this_count & 0xff;
} else if (block > 0xffffffff) {
SCpnt->cmnd[0] += READ_16 - READ_6;
- SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+ SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0;
SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0;
@@ -682,7 +717,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
this_count = 0xffff;
SCpnt->cmnd[0] += READ_10 - READ_6;
- SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+ SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
@@ -691,7 +726,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
} else {
- if (unlikely(blk_fua_rq(rq))) {
+ if (unlikely(rq->cmd_flags & REQ_FUA)) {
/*
* This happens only if this drive failed
* 10byte rw command with ILLEGAL_REQUEST
@@ -745,6 +780,8 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
* or from within the kernel (e.g. as a result of a mount(1) ).
* In the latter case @inode and @filp carry an abridged amount
* of information as noted above.
+ *
+ * Locking: called with bdev->bd_mutex held.
**/
static int sd_open(struct block_device *bdev, fmode_t mode)
{
@@ -799,7 +836,7 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
if (!scsi_device_online(sdev))
goto error_out;
- if (!sdkp->openers++ && sdev->removable) {
+ if ((atomic_inc_return(&sdkp->openers) == 1) && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
}
@@ -823,6 +860,8 @@ error_autopm:
*
* Note: may block (uninterruptible) if error recovery is underway
* on this disk.
+ *
+ * Locking: called with bdev->bd_mutex held.
**/
static int sd_release(struct gendisk *disk, fmode_t mode)
{
@@ -831,7 +870,7 @@ static int sd_release(struct gendisk *disk, fmode_t mode)
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
- if (!--sdkp->openers && sdev->removable) {
+ if (atomic_dec_return(&sdkp->openers) && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
}
@@ -904,7 +943,7 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
error = scsi_nonblockable_ioctl(sdp, cmd, p,
(mode & FMODE_NDELAY) != 0);
if (!scsi_block_when_processing_errors(sdp) || !error)
- return error;
+ goto out;
/*
* Send SCSI addressing ioctls directly to mid level, send other
@@ -914,13 +953,17 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
switch (cmd) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
- return scsi_ioctl(sdp, cmd, p);
+ error = scsi_ioctl(sdp, cmd, p);
+ break;
default:
error = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p);
if (error != -ENOTTY)
- return error;
+ break;
+ error = scsi_ioctl(sdp, cmd, p);
+ break;
}
- return scsi_ioctl(sdp, cmd, p);
+out:
+ return error;
}
static void set_media_not_present(struct scsi_disk *sdkp)
@@ -1045,15 +1088,6 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
return 0;
}
-static void sd_prepare_flush(struct request_queue *q, struct request *rq)
-{
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->timeout = SD_TIMEOUT;
- rq->retries = SD_MAX_RETRIES;
- rq->cmd[0] = SYNCHRONIZE_CACHE;
- rq->cmd_len = 10;
-}
-
static void sd_rescan(struct device *dev)
{
struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
@@ -1103,7 +1137,7 @@ static const struct block_device_operations sd_fops = {
.owner = THIS_MODULE,
.open = sd_open,
.release = sd_release,
- .locked_ioctl = sd_ioctl,
+ .ioctl = sd_ioctl,
.getgeo = sd_getgeo,
#ifdef CONFIG_COMPAT
.compat_ioctl = sd_compat_ioctl,
@@ -1120,7 +1154,7 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
u64 bad_lba;
int info_valid;
- if (!blk_fs_request(scmd->request))
+ if (scmd->request->cmd_type != REQ_TYPE_FS)
return 0;
info_valid = scsi_get_sense_info_fld(scmd->sense_buffer,
@@ -1171,6 +1205,12 @@ static int sd_done(struct scsi_cmnd *SCpnt)
int sense_valid = 0;
int sense_deferred = 0;
+ if (SCpnt->request->cmd_flags & REQ_DISCARD) {
+ if (!result)
+ scsi_set_resid(SCpnt, 0);
+ return good_bytes;
+ }
+
if (result) {
sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr);
if (sense_valid)
@@ -1383,7 +1423,7 @@ sd_spinup_disk(struct scsi_disk *sdkp)
/*
* Determine whether disk supports Data Integrity Field.
*/
-void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
+static void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
{
struct scsi_device *sdp = sdkp->device;
u8 type;
@@ -1929,7 +1969,7 @@ defaults:
* The ATO bit indicates whether the DIF application tag is available
* for use by the operating system.
*/
-void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
+static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
{
int res, offset;
struct scsi_device *sdp = sdkp->device;
@@ -2121,7 +2161,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
else
ordered = QUEUE_ORDERED_DRAIN;
- blk_queue_ordered(sdkp->disk->queue, ordered, sd_prepare_flush);
+ blk_queue_ordered(sdkp->disk->queue, ordered);
set_capacity(disk, sdkp->capacity);
kfree(buffer);
@@ -2234,6 +2274,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_revalidate_disk(gd);
blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
+ blk_queue_unprep_rq(sdp->request_queue, sd_unprep_fn);
gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_EXT_DEVT;
@@ -2274,7 +2315,7 @@ static int sd_probe(struct device *dev)
struct scsi_device *sdp = to_scsi_device(dev);
struct scsi_disk *sdkp;
struct gendisk *gd;
- u32 index;
+ int index;
int error;
error = -ENODEV;
@@ -2313,7 +2354,7 @@ static int sd_probe(struct device *dev)
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
- sdkp->openers = 0;
+ atomic_set(&sdkp->openers, 0);
sdkp->previous_state = 1;
if (!sdp->request_queue->rq_timeout) {
@@ -2372,6 +2413,7 @@ static int sd_remove(struct device *dev)
async_synchronize_full();
blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn);
+ blk_queue_unprep_rq(sdkp->device->request_queue, NULL);
device_del(&sdkp->dev);
del_gendisk(sdkp->disk);
sd_shutdown(dev);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 43d3caf..f81a930 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -47,7 +47,7 @@ struct scsi_disk {
struct scsi_device *device;
struct device dev;
struct gendisk *disk;
- unsigned int openers; /* protected by BKL for now, yuck */
+ atomic_t openers;
sector_t capacity; /* size in 512-byte sectors */
u32 index;
unsigned short hw_sector_size;
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 2968c6b..78d6163 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1686,14 +1686,9 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
int len, size = sizeof(struct sg_iovec) * iov_count;
struct iovec *iov;
- iov = kmalloc(size, GFP_ATOMIC);
- if (!iov)
- return -ENOMEM;
-
- if (copy_from_user(iov, hp->dxferp, size)) {
- kfree(iov);
- return -EFAULT;
- }
+ iov = memdup_user(hp->dxferp, size);
+ if (IS_ERR(iov))
+ return PTR_ERR(iov);
len = iov_length(iov, iov_count);
if (hp->dxfer_len < len) {
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 0a90abc..ba9c3e0 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -44,6 +44,7 @@
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
@@ -466,22 +467,27 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq)
static int sr_block_open(struct block_device *bdev, fmode_t mode)
{
- struct scsi_cd *cd = scsi_cd_get(bdev->bd_disk);
+ struct scsi_cd *cd;
int ret = -ENXIO;
+ lock_kernel();
+ cd = scsi_cd_get(bdev->bd_disk);
if (cd) {
ret = cdrom_open(&cd->cdi, bdev, mode);
if (ret)
scsi_cd_put(cd);
}
+ unlock_kernel();
return ret;
}
static int sr_block_release(struct gendisk *disk, fmode_t mode)
{
struct scsi_cd *cd = scsi_cd(disk);
+ lock_kernel();
cdrom_release(&cd->cdi, mode);
scsi_cd_put(cd);
+ unlock_kernel();
return 0;
}
@@ -493,6 +499,8 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
void __user *argp = (void __user *)arg;
int ret;
+ lock_kernel();
+
/*
* Send SCSI addressing ioctls directly to mid level, send other
* ioctls to cdrom/block level.
@@ -500,12 +508,13 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
switch (cmd) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
- return scsi_ioctl(sdev, cmd, argp);
+ ret = scsi_ioctl(sdev, cmd, argp);
+ goto out;
}
ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, arg);
if (ret != -ENOSYS)
- return ret;
+ goto out;
/*
* ENODEV means that we didn't recognise the ioctl, or that we
@@ -516,8 +525,12 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
ret = scsi_nonblockable_ioctl(sdev, cmd, argp,
(mode & FMODE_NDELAY) != 0);
if (ret != -ENODEV)
- return ret;
- return scsi_ioctl(sdev, cmd, argp);
+ goto out;
+ ret = scsi_ioctl(sdev, cmd, argp);
+
+out:
+ unlock_kernel();
+ return ret;
}
static int sr_block_media_changed(struct gendisk *disk)
@@ -531,7 +544,7 @@ static const struct block_device_operations sr_bdops =
.owner = THIS_MODULE,
.open = sr_block_open,
.release = sr_block_release,
- .locked_ioctl = sr_block_ioctl,
+ .ioctl = sr_block_ioctl,
.media_changed = sr_block_media_changed,
/*
* No compat_ioctl for now because sr_block_ioctl never
diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c
index b5838d5..713620e 100644
--- a/drivers/scsi/sun3_NCR5380.c
+++ b/drivers/scsi/sun3_NCR5380.c
@@ -2022,7 +2022,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance)
if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
!= cmd))
{
- if(blk_fs_request(cmd->request)) {
+ if (cmd->request->cmd_type == REQ_TYPE_FS) {
sun3scsi_dma_setup(d, count,
rq_data_dir(cmd->request));
sun3_dma_setup_done = cmd;
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
index e606cf0..613f588 100644
--- a/drivers/scsi/sun3_scsi.c
+++ b/drivers/scsi/sun3_scsi.c
@@ -524,7 +524,7 @@ static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted,
struct scsi_cmnd *cmd,
int write_flag)
{
- if(blk_fs_request(cmd->request))
+ if (cmd->request->cmd_type == REQ_TYPE_FS)
return wanted;
else
return 0;
diff --git a/drivers/scsi/sun3_scsi_vme.c b/drivers/scsi/sun3_scsi_vme.c
index aaa4fd0..7c526b8 100644
--- a/drivers/scsi/sun3_scsi_vme.c
+++ b/drivers/scsi/sun3_scsi_vme.c
@@ -458,7 +458,7 @@ static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted,
struct scsi_cmnd *cmd,
int write_flag)
{
- if(blk_fs_request(cmd->request))
+ if (cmd->request->cmd_type == REQ_TYPE_FS)
return wanted;
else
return 0;
diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c
index 89ba6fe..193b37b 100644
--- a/drivers/scsi/sun_esp.c
+++ b/drivers/scsi/sun_esp.c
@@ -44,7 +44,7 @@ enum dvma_rev {
};
static int __devinit esp_sbus_setup_dma(struct esp *esp,
- struct of_device *dma_of)
+ struct platform_device *dma_of)
{
esp->dma = dma_of;
@@ -81,7 +81,7 @@ static int __devinit esp_sbus_setup_dma(struct esp *esp,
static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct resource *res;
/* On HME, two reg sets exist, first is DVMA,
@@ -101,7 +101,7 @@ static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
static int __devinit esp_sbus_map_command_block(struct esp *esp)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
esp->command_block = dma_alloc_coherent(&op->dev, 16,
&esp->command_block_dma,
@@ -114,15 +114,15 @@ static int __devinit esp_sbus_map_command_block(struct esp *esp)
static int __devinit esp_sbus_register_irq(struct esp *esp)
{
struct Scsi_Host *host = esp->host;
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
host->irq = op->archdata.irqs[0];
return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
}
-static void __devinit esp_get_scsi_id(struct esp *esp, struct of_device *espdma)
+static void __devinit esp_get_scsi_id(struct esp *esp, struct platform_device *espdma)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *dp;
dp = op->dev.of_node;
@@ -144,7 +144,7 @@ done:
static void __devinit esp_get_differential(struct esp *esp)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *dp;
dp = op->dev.of_node;
@@ -156,7 +156,7 @@ static void __devinit esp_get_differential(struct esp *esp)
static void __devinit esp_get_clock_params(struct esp *esp)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *bus_dp, *dp;
int fmhz;
@@ -170,10 +170,10 @@ static void __devinit esp_get_clock_params(struct esp *esp)
esp->cfreq = fmhz;
}
-static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of)
+static void __devinit esp_get_bursts(struct esp *esp, struct platform_device *dma_of)
{
struct device_node *dma_dp = dma_of->dev.of_node;
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *dp;
u8 bursts, val;
@@ -195,7 +195,7 @@ static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of)
esp->bursts = bursts;
}
-static void __devinit esp_sbus_get_props(struct esp *esp, struct of_device *espdma)
+static void __devinit esp_sbus_get_props(struct esp *esp, struct platform_device *espdma)
{
esp_get_scsi_id(esp, espdma);
esp_get_differential(esp);
@@ -216,7 +216,7 @@ static u8 sbus_esp_read8(struct esp *esp, unsigned long reg)
static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
size_t sz, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
return dma_map_single(&op->dev, buf, sz, dir);
}
@@ -224,7 +224,7 @@ static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
int num_sg, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
return dma_map_sg(&op->dev, sg, num_sg, dir);
}
@@ -232,7 +232,7 @@ static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
size_t sz, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
dma_unmap_single(&op->dev, addr, sz, dir);
}
@@ -240,7 +240,7 @@ static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
int num_sg, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
dma_unmap_sg(&op->dev, sg, num_sg, dir);
}
@@ -256,7 +256,7 @@ static void sbus_esp_reset_dma(struct esp *esp)
{
int can_do_burst16, can_do_burst32, can_do_burst64;
int can_do_sbus64, lim;
- struct of_device *op;
+ struct platform_device *op;
u32 val;
can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
@@ -487,8 +487,8 @@ static const struct esp_driver_ops sbus_esp_ops = {
.dma_error = sbus_esp_dma_error,
};
-static int __devinit esp_sbus_probe_one(struct of_device *op,
- struct of_device *espdma,
+static int __devinit esp_sbus_probe_one(struct platform_device *op,
+ struct platform_device *espdma,
int hme)
{
struct scsi_host_template *tpnt = &scsi_esp_template;
@@ -562,11 +562,11 @@ fail:
return err;
}
-static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit esp_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dma_node = NULL;
struct device_node *dp = op->dev.of_node;
- struct of_device *dma_of = NULL;
+ struct platform_device *dma_of = NULL;
int hme = 0;
if (dp->parent &&
@@ -585,10 +585,10 @@ static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device
return esp_sbus_probe_one(op, dma_of, hme);
}
-static int __devexit esp_sbus_remove(struct of_device *op)
+static int __devexit esp_sbus_remove(struct platform_device *op)
{
struct esp *esp = dev_get_drvdata(&op->dev);
- struct of_device *dma_of = esp->dma;
+ struct platform_device *dma_of = esp->dma;
unsigned int irq = esp->host->irq;
bool is_hme;
u32 val;
diff --git a/drivers/serial/21285.c b/drivers/serial/21285.c
index 8681f13..d89aa38 100644
--- a/drivers/serial/21285.c
+++ b/drivers/serial/21285.c
@@ -216,7 +216,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
unsigned long flags;
- unsigned int baud, quot, h_lcr;
+ unsigned int baud, quot, h_lcr, b;
/*
* We don't support modem control lines.
@@ -234,12 +234,8 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
*/
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
-
- if (port->state && port->state->port.tty) {
- struct tty_struct *tty = port->state->port.tty;
- unsigned int b = port->uartclk / (16 * quot);
- tty_encode_baud_rate(tty, b, b);
- }
+ b = port->uartclk / (16 * quot);
+ tty_termios_encode_baud_rate(termios, b, b);
switch (termios->c_cflag & CSIZE) {
case CS5:
diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c
index 3046386..7356a56 100644
--- a/drivers/serial/68328serial.c
+++ b/drivers/serial/68328serial.c
@@ -78,10 +78,6 @@ struct m68k_serial *m68k_consinfo = 0;
#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */
-#ifdef CONFIG_CONSOLE
-extern wait_queue_head_t keypress_wait;
-#endif
-
struct tty_driver *serial_driver;
/* number of characters left in xmit buffer before we ask for more */
@@ -102,19 +98,13 @@ static void change_speed(struct m68k_serial *info);
* Setup for console. Argument comes from the boot command line.
*/
-#if defined(CONFIG_M68EZ328ADS) || defined(CONFIG_ALMA_ANS) || defined(CONFIG_DRAGONIXVZ)
-#define CONSOLE_BAUD_RATE 115200
-#define DEFAULT_CBAUD B115200
-#else
- /* (es) */
- /* note: this is messy, but it works, again, perhaps defined somewhere else?*/
- #ifdef CONFIG_M68VZ328
- #define CONSOLE_BAUD_RATE 19200
- #define DEFAULT_CBAUD B19200
- #endif
- /* (/es) */
+/* note: this is messy, but it works, again, perhaps defined somewhere else?*/
+#ifdef CONFIG_M68VZ328
+#define CONSOLE_BAUD_RATE 19200
+#define DEFAULT_CBAUD B19200
#endif
+
#ifndef CONSOLE_BAUD_RATE
#define CONSOLE_BAUD_RATE 9600
#define DEFAULT_CBAUD B9600
@@ -300,10 +290,6 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx)
return;
#endif /* CONFIG_MAGIC_SYSRQ */
}
- /* It is a 'keyboard interrupt' ;-) */
-#ifdef CONFIG_CONSOLE
- wake_up(&keypress_wait);
-#endif
}
if(!tty)
@@ -1243,7 +1229,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
retval = -ERESTARTSYS;
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c
index 768612f..0dff3bb 100644
--- a/drivers/serial/68360serial.c
+++ b/drivers/serial/68360serial.c
@@ -1705,7 +1705,6 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout)
printk("jiff=%lu...", jiffies);
#endif
- lock_kernel();
/* We go through the loop at least once because we can't tell
* exactly when the last character exits the shifter. There can
* be at least two characters waiting to be sent after the buffers
@@ -1734,7 +1733,6 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout)
bdp--;
} while (bdp->status & BD_SC_READY);
current->state = TASK_RUNNING;
- unlock_kernel();
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
#endif
@@ -1862,7 +1860,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, state->count);
#endif
+ tty_unlock();
schedule();
+ tty_lock();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 09ef570..24110f6 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -241,7 +241,7 @@ static const struct serial8250_config uart_config[] = {
.fifo_size = 128,
.tx_loadsz = 128,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
- .flags = UART_CAP_FIFO,
+ .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
},
[PORT_16654] = {
.name = "ST16654",
@@ -300,6 +300,13 @@ static const struct serial8250_config uart_config[] = {
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+ [PORT_U6_16550A] = {
+ .name = "U6_16550A",
+ .fifo_size = 64,
+ .tx_loadsz = 64,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
};
#if defined(CONFIG_MIPS_ALCHEMY)
@@ -1070,6 +1077,15 @@ static void autoconfig_16550a(struct uart_8250_port *up)
DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
}
serial_outp(up, UART_IER, iersave);
+
+ /*
+ * We distinguish between 16550A and U6 16550A by counting
+ * how many bytes are in the FIFO.
+ */
+ if (up->port.type == PORT_16550A && size_fifo(up) == 64) {
+ up->port.type = PORT_U6_16550A;
+ up->capabilities |= UART_CAP_AFE;
+ }
}
/*
@@ -2224,9 +2240,9 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
return quot;
}
-static void
-serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+void
+serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char cval, fcr = 0;
@@ -2402,16 +2418,22 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
}
+EXPORT_SYMBOL(serial8250_do_set_termios);
static void
-serial8250_set_ldisc(struct uart_port *port)
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
- int line = port->line;
-
- if (line >= port->state->port.tty->driver->num)
- return;
+ if (port->set_termios)
+ port->set_termios(port, termios, old);
+ else
+ serial8250_do_set_termios(port, termios, old);
+}
- if (port->state->port.tty->ldisc->ops->num == N_PPS) {
+static void
+serial8250_set_ldisc(struct uart_port *port, int new)
+{
+ if (new == N_PPS) {
port->flags |= UPF_HARDPPS_CD;
serial8250_enable_ms(port);
} else
@@ -2987,6 +3009,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
port.type = p->type;
port.serial_in = p->serial_in;
port.serial_out = p->serial_out;
+ port.set_termios = p->set_termios;
port.dev = &dev->dev;
port.irqflags |= irqflag;
ret = serial8250_register_port(&port);
@@ -3150,6 +3173,9 @@ int serial8250_register_port(struct uart_port *port)
uart->port.serial_in = port->serial_in;
if (port->serial_out)
uart->port.serial_out = port->serial_out;
+ /* Possibly override set_termios call */
+ if (port->set_termios)
+ uart->port.set_termios = port->set_termios;
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
diff --git a/drivers/serial/8250_early.c b/drivers/serial/8250_early.c
index f279745..b745792 100644
--- a/drivers/serial/8250_early.c
+++ b/drivers/serial/8250_early.c
@@ -19,9 +19,11 @@
* The user can specify the device directly, e.g.,
* earlycon=uart8250,io,0x3f8,9600n8
* earlycon=uart8250,mmio,0xff5e0000,115200n8
+ * earlycon=uart8250,mmio32,0xff5e0000,115200n8
* or
* console=uart8250,io,0x3f8,9600n8
* console=uart8250,mmio,0xff5e0000,115200n8
+ * console=uart8250,mmio32,0xff5e0000,115200n8
*/
#include <linux/tty.h>
@@ -48,18 +50,31 @@ static struct early_serial8250_device early_device;
static unsigned int __init serial_in(struct uart_port *port, int offset)
{
- if (port->iotype == UPIO_MEM)
+ switch (port->iotype) {
+ case UPIO_MEM:
return readb(port->membase + offset);
- else
+ case UPIO_MEM32:
+ return readl(port->membase + (offset << 2));
+ case UPIO_PORT:
return inb(port->iobase + offset);
+ default:
+ return 0;
+ }
}
static void __init serial_out(struct uart_port *port, int offset, int value)
{
- if (port->iotype == UPIO_MEM)
+ switch (port->iotype) {
+ case UPIO_MEM:
writeb(value, port->membase + offset);
- else
+ break;
+ case UPIO_MEM32:
+ writel(value, port->membase + (offset << 2));
+ break;
+ case UPIO_PORT:
outb(value, port->iobase + offset);
+ break;
+ }
}
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
@@ -137,15 +152,21 @@ static int __init parse_options(struct early_serial8250_device *device,
char *options)
{
struct uart_port *port = &device->port;
- int mmio, length;
+ int mmio, mmio32, length;
if (!options)
return -ENODEV;
port->uartclk = BASE_BAUD * 16;
- if (!strncmp(options, "mmio,", 5)) {
- port->iotype = UPIO_MEM;
- port->mapbase = simple_strtoul(options + 5, &options, 0);
+
+ mmio = !strncmp(options, "mmio,", 5);
+ mmio32 = !strncmp(options, "mmio32,", 7);
+ if (mmio || mmio32) {
+ port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+ port->mapbase = simple_strtoul(options + (mmio ? 5 : 7),
+ &options, 0);
+ if (mmio32)
+ port->regshift = 2;
#ifdef CONFIG_FIX_EARLYCON_MEM
set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
port->mapbase & PAGE_MASK);
@@ -157,11 +178,10 @@ static int __init parse_options(struct early_serial8250_device *device,
if (!port->membase) {
printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
__func__,
- (unsigned long long)port->mapbase);
+ (unsigned long long) port->mapbase);
return -ENOMEM;
}
#endif
- mmio = 1;
} else if (!strncmp(options, "io,", 3)) {
port->iotype = UPIO_PORT;
port->iobase = simple_strtoul(options + 3, &options, 0);
@@ -181,11 +201,18 @@ static int __init parse_options(struct early_serial8250_device *device,
device->baud);
}
- printk(KERN_INFO "Early serial console at %s 0x%llx (options '%s')\n",
- mmio ? "MMIO" : "I/O port",
- mmio ? (unsigned long long) port->mapbase
- : (unsigned long long) port->iobase,
- device->options);
+ if (mmio || mmio32)
+ printk(KERN_INFO
+ "Early serial console at MMIO%s 0x%llu (options '%s')\n",
+ mmio32 ? "32" : "",
+ (unsigned long long)port->mapbase,
+ device->options);
+ else
+ printk(KERN_INFO
+ "Early serial console at I/O port 0x%lu (options '%s')\n",
+ port->iobase,
+ device->options);
+
return 0;
}
diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c
index 746a446..53be4d3 100644
--- a/drivers/serial/8250_pci.c
+++ b/drivers/serial/8250_pci.c
@@ -994,6 +994,7 @@ static int skip_tx_en_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_TITAN_800E 0xA014
#define PCI_DEVICE_ID_TITAN_200EI 0xA016
#define PCI_DEVICE_ID_TITAN_200EISI 0xA017
+#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
@@ -1542,6 +1543,8 @@ enum pci_board_num_t {
pbn_b2_4_921600,
pbn_b2_8_921600,
+ pbn_b2_8_1152000,
+
pbn_b2_bt_1_115200,
pbn_b2_bt_2_115200,
pbn_b2_bt_4_115200,
@@ -1960,6 +1963,13 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.uart_offset = 8,
},
+ [pbn_b2_8_1152000] = {
+ .flags = FL_BASE2,
+ .num_ports = 8,
+ .base_baud = 1152000,
+ .uart_offset = 8,
+ },
+
[pbn_b2_bt_1_115200] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 1,
@@ -2875,6 +2885,9 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
+ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958,
+ PCI_ANY_ID , PCI_ANY_ID, 0, 0,
+ pbn_b2_8_1152000 },
/*
* Oxford Semiconductor Inc. Tornado PCI express device range.
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index e437ce8..12900f7 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -536,12 +536,13 @@ config SERIAL_S3C6400
config SERIAL_S5PV210
tristate "Samsung S5PV210 Serial port support"
- depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442)
- select SERIAL_SAMSUNG_UARTS_4 if CPU_S5PV210
+ depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310)
+ select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310)
default y
help
Serial port support for Samsung's S5P Family of SoC's
+
config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
@@ -549,6 +550,22 @@ config SERIAL_MAX3100
help
MAX3100 chip support
+config SERIAL_MAX3107
+ tristate "MAX3107 support"
+ depends on SPI
+ select SERIAL_CORE
+ help
+ MAX3107 chip support
+
+config SERIAL_MAX3107_AAVA
+ tristate "MAX3107 AAVA platform support"
+ depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB
+ select SERIAL_CORE
+ help
+ Support for the MAX3107 chip configuration found on the AAVA
+ platform. Includes the extra initialisation and GPIO support
+ neded for this device.
+
config SERIAL_DZ
bool "DECstation DZ serial driver"
depends on MACH_DECSTATION && 32BIT
@@ -690,6 +707,33 @@ config SERIAL_SA1100_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_MRST_MAX3110
+ tristate "SPI UART driver for Max3110"
+ depends on SPI_DW_PCI
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ help
+ This is the UART protocol driver for the MAX3110 device on
+ the Intel Moorestown platform. On other systems use the max3100
+ driver.
+
+config MRST_MAX3110_IRQ
+ boolean "Enable GPIO IRQ for Max3110 over Moorestown"
+ default n
+ depends on SERIAL_MRST_MAX3110 && GPIO_LANGWELL
+ help
+ This has to be enabled after Moorestown GPIO driver is loaded
+
+config SERIAL_MFD_HSU
+ tristate "Medfield High Speed UART support"
+ depends on PCI
+ select SERIAL_CORE
+
+config SERIAL_MFD_HSU_CONSOLE
+ boolean "Medfile HSU serial console support"
+ depends on SERIAL_MFD_HSU=y
+ select SERIAL_CORE_CONSOLE
+
config SERIAL_BFIN
tristate "Blackfin serial port support"
depends on BLACKFIN
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 208a855..1ca4fd5 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -46,6 +46,8 @@ obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o
obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
+obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
@@ -84,3 +86,5 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
+obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c
index 0f11896..f8d8a00 100644
--- a/drivers/serial/altera_uart.c
+++ b/drivers/serial/altera_uart.c
@@ -394,7 +394,7 @@ int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp)
static void altera_uart_console_putc(struct uart_port *port, const char c)
{
while (!(readl(port->membase + ALTERA_UART_STATUS_REG) &
- ALTERA_UART_STATUS_TRDY_MSK))
+ ALTERA_UART_STATUS_TRDY_MSK))
cpu_relax();
writel(c, port->membase + ALTERA_UART_TXDATA_REG);
diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c
index 0099b86..cc01c65 100644
--- a/drivers/serial/apbuart.c
+++ b/drivers/serial/apbuart.c
@@ -551,7 +551,7 @@ static struct uart_driver grlib_apbuart_driver = {
/* OF Platform Driver */
/* ======================================================================== */
-static int __devinit apbuart_probe(struct of_device *op,
+static int __devinit apbuart_probe(struct platform_device *op,
const struct of_device_id *match)
{
int i = -1;
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index a182def..3892666 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -217,7 +217,8 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
if (rs485conf->flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
- UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+ if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
+ UART_PUT_TTGR(port, rs485conf->delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -292,7 +293,9 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ UART_PUT_TTGR(port,
+ atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -1211,7 +1214,9 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ UART_PUT_TTGR(port,
+ atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c
index 511cbf6..a9eff2b 100644
--- a/drivers/serial/bfin_5xx.c
+++ b/drivers/serial/bfin_5xx.c
@@ -957,15 +957,12 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
* Enable the IrDA function if tty->ldisc.num is N_IRDA.
* In other cases, disable IrDA function.
*/
-static void bfin_serial_set_ldisc(struct uart_port *port)
+static void bfin_serial_set_ldisc(struct uart_port *port, int ld)
{
int line = port->line;
unsigned short val;
- if (line >= port->state->port.tty->driver->num)
- return;
-
- switch (port->state->port.tty->termios->c_line) {
+ switch (ld) {
case N_IRDA:
val = UART_GET_GCTL(&bfin_serial_ports[line]);
val |= (IREN | RPOLC);
diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c
index 6016179..f2b8adc 100644
--- a/drivers/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/serial/cpm_uart/cpm_uart_core.c
@@ -1340,7 +1340,7 @@ static struct uart_driver cpm_reg = {
static int probe_index;
-static int __devinit cpm_uart_probe(struct of_device *ofdev,
+static int __devinit cpm_uart_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int index = probe_index++;
@@ -1364,7 +1364,7 @@ static int __devinit cpm_uart_probe(struct of_device *ofdev,
return uart_add_one_port(&cpm_reg, &pinfo->port);
}
-static int __devexit cpm_uart_remove(struct of_device *ofdev)
+static int __devexit cpm_uart_remove(struct platform_device *ofdev)
{
struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev);
return uart_remove_one_port(&cpm_reg, &pinfo->port);
diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c
index 3062644..c856905 100644
--- a/drivers/serial/crisv10.c
+++ b/drivers/serial/crisv10.c
@@ -3935,7 +3935,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
* Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
* R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
*/
- lock_kernel();
orig_jiffies = jiffies;
while (info->xmit.head != info->xmit.tail || /* More in send queue */
(*info->ostatusadr & 0x007f) || /* more in FIFO */
@@ -3952,7 +3951,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
curr_time_usec - info->last_tx_active_usec;
}
set_current_state(TASK_RUNNING);
- unlock_kernel();
}
/*
@@ -3992,7 +3990,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
- wait_event_interruptible(info->close_wait,
+ wait_event_interruptible_tty(info->close_wait,
!(info->flags & ASYNC_CLOSING));
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
@@ -4068,7 +4066,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
printk("block_til_ready blocking: ttyS%d, count = %d\n",
info->line, info->count);
#endif
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
@@ -4150,7 +4150,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
- wait_event_interruptible(info->close_wait,
+ wait_event_interruptible_tty(info->close_wait,
!(info->flags & ASYNC_CLOSING));
#ifdef SERIAL_DO_RESTART
return ((info->flags & ASYNC_HUP_NOTIFY) ?
@@ -4533,8 +4533,8 @@ static int __init rs_init(void)
INIT_WORK(&info->work, do_softint);
if (info->enabled) {
- printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
- serial_driver->name, info->line, (unsigned int)info->ioport);
+ printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
+ serial_driver->name, info->line, info->ioport);
}
}
#ifdef CONFIG_ETRAX_FAST_TIMER
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index eacb588..66ecc7a 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -909,13 +909,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
rational_best_approximation(16 * div * baud, sport->port.uartclk,
1 << 16, 1 << 16, &num, &denom);
- if (port->state && port->state->port.tty) {
- tdiv64 = sport->port.uartclk;
- tdiv64 *= num;
- do_div(tdiv64, denom * 16 * div);
- tty_encode_baud_rate(sport->port.state->port.tty,
+ tdiv64 = sport->port.uartclk;
+ tdiv64 *= num;
+ do_div(tdiv64, denom * 16 * div);
+ tty_termios_encode_baud_rate(termios,
(speed_t)tdiv64, (speed_t)tdiv64);
- }
num -= 1;
denom -= 1;
diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c
index f164ba4..93de907 100644
--- a/drivers/serial/ioc3_serial.c
+++ b/drivers/serial/ioc3_serial.c
@@ -954,12 +954,13 @@ ioc3_change_speed(struct uart_port *the_port,
struct ktermios *new_termios, struct ktermios *old_termios)
{
struct ioc3_port *port = get_ioc3_port(the_port);
- unsigned int cflag;
+ unsigned int cflag, iflag;
int baud;
int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
struct uart_state *state = the_port->state;
cflag = new_termios->c_cflag;
+ iflag = new_termios->c_iflag;
switch (cflag & CSIZE) {
case CS5:
@@ -1000,12 +1001,12 @@ ioc3_change_speed(struct uart_port *the_port,
state->port.tty->low_latency = 1;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
| N_FRAMING_ERROR);
- if (I_IGNBRK(state->port.tty)) {
+ if (iflag & IGNBRK) {
the_port->ignore_status_mask &= ~N_BREAK;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
}
if (!(cflag & CREAD)) {
diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c
index 8ad28fc..fcfe826 100644
--- a/drivers/serial/ioc4_serial.c
+++ b/drivers/serial/ioc4_serial.c
@@ -1685,11 +1685,12 @@ ioc4_change_speed(struct uart_port *the_port,
{
struct ioc4_port *port = get_ioc4_port(the_port, 0);
int baud, bits;
- unsigned cflag;
+ unsigned cflag, iflag;
int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
struct uart_state *state = the_port->state;
cflag = new_termios->c_cflag;
+ iflag = new_termios->c_iflag;
switch (cflag & CSIZE) {
case CS5:
@@ -1741,12 +1742,12 @@ ioc4_change_speed(struct uart_port *the_port,
state->port.tty->low_latency = 1;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
| N_FRAMING_ERROR);
- if (I_IGNBRK(state->port.tty)) {
+ if (iflag & IGNBRK) {
the_port->ignore_status_mask &= ~N_BREAK;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
}
if (!(cflag & CREAD)) {
diff --git a/drivers/serial/max3100.c b/drivers/serial/max3100.c
index 3351c3b..beb1afa2 100644
--- a/drivers/serial/max3100.c
+++ b/drivers/serial/max3100.c
@@ -430,17 +430,14 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios,
int baud = 0;
unsigned cflag;
u32 param_new, param_mask, parity = 0;
- struct tty_struct *tty = s->port.state->port.tty;
dev_dbg(&s->spi->dev, "%s\n", __func__);
- if (!tty)
- return;
cflag = termios->c_cflag;
param_new = 0;
param_mask = 0;
- baud = tty_get_baud_rate(tty);
+ baud = tty_termios_baud_rate(termios);
param_new = s->conf & MAX3100_BAUD;
switch (baud) {
case 300:
@@ -485,7 +482,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios,
default:
baud = s->baud;
}
- tty_encode_baud_rate(tty, baud, baud);
+ tty_termios_encode_baud_rate(termios, baud, baud);
s->baud = baud;
param_mask |= MAX3100_BAUD;
diff --git a/drivers/serial/max3107-aava.c b/drivers/serial/max3107-aava.c
new file mode 100644
index 0000000..a1fe304
--- /dev/null
+++ b/drivers/serial/max3107-aava.c
@@ -0,0 +1,344 @@
+/*
+ * max3107.c - spi uart protocol driver for Maxim 3107
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/sfi.h>
+#include <asm/mrst.h>
+#include "max3107.h"
+
+/* GPIO direction to input function */
+static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[1]; /* Buffer for SPI transfer */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return -EINVAL;
+ }
+
+ /* Read current GPIO configuration register */
+ buf[0] = MAX3107_GPIOCFG_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n");
+ return -EIO;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+
+ /* Set GPIO to input */
+ buf[0] &= ~(0x0001 << offset);
+
+ /* Write new GPIO configuration register value */
+ buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* GPIO direction to output function */
+static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[2]; /* Buffer for SPI transfers */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return -EINVAL;
+ }
+
+ /* Read current GPIO configuration and data registers */
+ buf[0] = MAX3107_GPIOCFG_REG;
+ buf[1] = MAX3107_GPIODATA_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
+ dev_err(&s->spi->dev, "SPI transfer gpio failed\n");
+ return -EIO;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+ buf[1] &= MAX3107_SPI_RX_DATA_MASK;
+
+ /* Set GPIO to output */
+ buf[0] |= (0x0001 << offset);
+ /* Set value */
+ if (value)
+ buf[1] |= (0x0001 << offset);
+ else
+ buf[1] &= ~(0x0001 << offset);
+
+ /* Write new GPIO configuration and data register values */
+ buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
+ buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer for GPIO conf data w failed\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* GPIO value query function */
+static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[1]; /* Buffer for SPI transfer */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return -EINVAL;
+ }
+
+ /* Read current GPIO data register */
+ buf[0] = MAX3107_GPIODATA_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n");
+ return -EIO;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+
+ /* Return value */
+ return buf[0] & (0x0001 << offset);
+}
+
+/* GPIO value set function */
+static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[2]; /* Buffer for SPI transfers */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return;
+ }
+
+ /* Read current GPIO configuration registers*/
+ buf[0] = MAX3107_GPIODATA_REG;
+ buf[1] = MAX3107_GPIOCFG_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer for GPIO data and config read failed\n");
+ return;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+ buf[1] &= MAX3107_SPI_RX_DATA_MASK;
+
+ if (!(buf[1] & (0x0001 << offset))) {
+ /* Configured as input, can't set value */
+ dev_warn(&s->spi->dev,
+ "Trying to set value for input GPIO\n");
+ return;
+ }
+
+ /* Set value */
+ if (value)
+ buf[0] |= (0x0001 << offset);
+ else
+ buf[0] &= ~(0x0001 << offset);
+
+ /* Write new GPIO data register value */
+ buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n");
+}
+
+/* GPIO chip data */
+static struct gpio_chip max3107_gpio_chip = {
+ .owner = THIS_MODULE,
+ .direction_input = max3107_gpio_direction_in,
+ .direction_output = max3107_gpio_direction_out,
+ .get = max3107_gpio_get,
+ .set = max3107_gpio_set,
+ .can_sleep = 1,
+ .base = MAX3107_GPIO_BASE,
+ .ngpio = MAX3107_GPIO_COUNT,
+};
+
+/**
+ * max3107_aava_reset - reset on AAVA systems
+ * @spi: The SPI device we are probing
+ *
+ * Reset the device ready for probing.
+ */
+
+static int max3107_aava_reset(struct spi_device *spi)
+{
+ /* Reset the chip */
+ if (gpio_request(MAX3107_RESET_GPIO, "max3107")) {
+ pr_err("Requesting RESET GPIO failed\n");
+ return -EIO;
+ }
+ if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) {
+ pr_err("Setting RESET GPIO to 0 failed\n");
+ gpio_free(MAX3107_RESET_GPIO);
+ return -EIO;
+ }
+ msleep(MAX3107_RESET_DELAY);
+ if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) {
+ pr_err("Setting RESET GPIO to 1 failed\n");
+ gpio_free(MAX3107_RESET_GPIO);
+ return -EIO;
+ }
+ gpio_free(MAX3107_RESET_GPIO);
+ msleep(MAX3107_WAKEUP_DELAY);
+ return 0;
+}
+
+static int max3107_aava_configure(struct max3107_port *s)
+{
+ int retval;
+
+ /* Initialize GPIO chip data */
+ s->chip = max3107_gpio_chip;
+ s->chip.label = s->spi->modalias;
+ s->chip.dev = &s->spi->dev;
+
+ /* Add GPIO chip */
+ retval = gpiochip_add(&s->chip);
+ if (retval) {
+ dev_err(&s->spi->dev, "Adding GPIO chip failed\n");
+ return retval;
+ }
+
+ /* Temporary fix for EV2 boot problems, set modem reset to 0 */
+ max3107_gpio_direction_out(&s->chip, 3, 0);
+ return 0;
+}
+
+#if 0
+/* This will get enabled once we have the board stuff merged for this
+ specific case */
+
+static const struct baud_table brg13_ext[] = {
+ { 300, MAX3107_BRG13_B300 },
+ { 600, MAX3107_BRG13_B600 },
+ { 1200, MAX3107_BRG13_B1200 },
+ { 2400, MAX3107_BRG13_B2400 },
+ { 4800, MAX3107_BRG13_B4800 },
+ { 9600, MAX3107_BRG13_B9600 },
+ { 19200, MAX3107_BRG13_B19200 },
+ { 57600, MAX3107_BRG13_B57600 },
+ { 115200, MAX3107_BRG13_B115200 },
+ { 230400, MAX3107_BRG13_B230400 },
+ { 460800, MAX3107_BRG13_B460800 },
+ { 921600, MAX3107_BRG13_B921600 },
+ { 0, 0 }
+};
+
+static void max3107_aava_init(struct max3107_port *s)
+{
+ /*override for AAVA SC specific*/
+ if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) {
+ if (get_koski_build_id() <= KOSKI_EV2)
+ if (s->ext_clk) {
+ s->brg_cfg = MAX3107_BRG13_B9600;
+ s->baud_tbl = (struct baud_table *)brg13_ext;
+ }
+ }
+}
+#endif
+
+static int __devexit max3107_aava_remove(struct spi_device *spi)
+{
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ /* Remove GPIO chip */
+ if (gpiochip_remove(&s->chip))
+ dev_warn(&spi->dev, "Removing GPIO chip failed\n");
+
+ /* Then do the default remove */
+ return max3107_remove(spi);
+}
+
+/* Platform data */
+static struct max3107_plat aava_plat_data = {
+ .loopback = 0,
+ .ext_clk = 1,
+/* .init = max3107_aava_init, */
+ .configure = max3107_aava_configure,
+ .hw_suspend = max3107_hw_susp,
+ .polled_mode = 0,
+ .poll_time = 0,
+};
+
+
+static int __devinit max3107_probe_aava(struct spi_device *spi)
+{
+ int err = max3107_aava_reset(spi);
+ if (err < 0)
+ return err;
+ return max3107_probe(spi, &aava_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+ .driver = {
+ .name = "aava-max3107",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = max3107_probe_aava,
+ .remove = __devexit_p(max3107_aava_remove),
+ .suspend = max3107_suspend,
+ .resume = max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+ return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+ spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("aava-max3107-spi");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/serial/max3107.c b/drivers/serial/max3107.c
new file mode 100644
index 0000000..67283c1
--- /dev/null
+++ b/drivers/serial/max3107.c
@@ -0,0 +1,1197 @@
+/*
+ * max3107.c - spi uart protocol driver for Maxim 3107
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include "max3107.h"
+
+static const struct baud_table brg26_ext[] = {
+ { 300, MAX3107_BRG26_B300 },
+ { 600, MAX3107_BRG26_B600 },
+ { 1200, MAX3107_BRG26_B1200 },
+ { 2400, MAX3107_BRG26_B2400 },
+ { 4800, MAX3107_BRG26_B4800 },
+ { 9600, MAX3107_BRG26_B9600 },
+ { 19200, MAX3107_BRG26_B19200 },
+ { 57600, MAX3107_BRG26_B57600 },
+ { 115200, MAX3107_BRG26_B115200 },
+ { 230400, MAX3107_BRG26_B230400 },
+ { 460800, MAX3107_BRG26_B460800 },
+ { 921600, MAX3107_BRG26_B921600 },
+ { 0, 0 }
+};
+
+static const struct baud_table brg13_int[] = {
+ { 300, MAX3107_BRG13_IB300 },
+ { 600, MAX3107_BRG13_IB600 },
+ { 1200, MAX3107_BRG13_IB1200 },
+ { 2400, MAX3107_BRG13_IB2400 },
+ { 4800, MAX3107_BRG13_IB4800 },
+ { 9600, MAX3107_BRG13_IB9600 },
+ { 19200, MAX3107_BRG13_IB19200 },
+ { 57600, MAX3107_BRG13_IB57600 },
+ { 115200, MAX3107_BRG13_IB115200 },
+ { 230400, MAX3107_BRG13_IB230400 },
+ { 460800, MAX3107_BRG13_IB460800 },
+ { 921600, MAX3107_BRG13_IB921600 },
+ { 0, 0 }
+};
+
+static u32 get_new_brg(int baud, struct max3107_port *s)
+{
+ int i;
+ const struct baud_table *baud_tbl = s->baud_tbl;
+
+ for (i = 0; i < 13; i++) {
+ if (baud == baud_tbl[i].baud)
+ return baud_tbl[i].new_brg;
+ }
+
+ return 0;
+}
+
+/* Perform SPI transfer for write/read of device register(s) */
+int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len)
+{
+ struct spi_message spi_msg;
+ struct spi_transfer spi_xfer;
+
+ /* Initialize SPI ,message */
+ spi_message_init(&spi_msg);
+
+ /* Initialize SPI transfer */
+ memset(&spi_xfer, 0, sizeof spi_xfer);
+ spi_xfer.len = len;
+ spi_xfer.tx_buf = tx;
+ spi_xfer.rx_buf = rx;
+ spi_xfer.speed_hz = MAX3107_SPI_SPEED;
+
+ /* Add SPI transfer to SPI message */
+ spi_message_add_tail(&spi_xfer, &spi_msg);
+
+#ifdef DBG_TRACE_SPI_DATA
+ {
+ int i;
+ pr_info("tx len %d:\n", spi_xfer.len);
+ for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+ pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]);
+ pr_info("\n");
+ }
+#endif
+
+ /* Perform synchronous SPI transfer */
+ if (spi_sync(s->spi, &spi_msg)) {
+ dev_err(&s->spi->dev, "spi_sync failure\n");
+ return -EIO;
+ }
+
+#ifdef DBG_TRACE_SPI_DATA
+ if (spi_xfer.rx_buf) {
+ int i;
+ pr_info("rx len %d:\n", spi_xfer.len);
+ for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+ pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]);
+ pr_info("\n");
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_rw);
+
+/* Puts received data to circular buffer */
+static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data,
+ int len)
+{
+ struct uart_port *port = &s->port;
+ struct tty_struct *tty;
+
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return;
+
+ /* Insert received data */
+ tty_insert_flip_string(tty, data, len);
+ /* Update RX counter */
+ port->icount.rx += len;
+}
+
+/* Handle data receiving */
+static void max3107_handlerx(struct max3107_port *s, u16 rxlvl)
+{
+ int i;
+ int j;
+ int len; /* SPI transfer buffer length */
+ u16 *buf;
+ u8 *valid_str;
+
+ if (!s->rx_enabled)
+ /* RX is disabled */
+ return;
+
+ if (rxlvl == 0) {
+ /* RX fifo is empty */
+ return;
+ } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) {
+ dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl);
+ /* Ensure sanity of RX level */
+ rxlvl = MAX3107_RX_FIFO_SIZE;
+ }
+ if ((s->rxbuf == 0) || (s->rxstr == 0)) {
+ dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n");
+ return;
+ }
+ buf = s->rxbuf;
+ valid_str = s->rxstr;
+ while (rxlvl) {
+ pr_debug("rxlvl %d\n", rxlvl);
+ /* Clear buffer */
+ memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2));
+ len = 0;
+ if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) {
+ /* First disable RX FIFO interrupt */
+ pr_debug("Disabling RX INT\n");
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT;
+ buf[0] |= s->irqen_reg;
+ len++;
+ }
+ /* Just increase the length by amount of words in FIFO since
+ * buffer was zeroed and SPI transfer of 0x0000 means reading
+ * from RX FIFO
+ */
+ len += rxlvl;
+ /* Append RX level query */
+ buf[len] = MAX3107_RXFIFOLVL_REG;
+ len++;
+
+ /* Perform the SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) {
+ dev_err(&s->spi->dev, "SPI transfer for RX h failed\n");
+ return;
+ }
+
+ /* Skip RX FIFO interrupt disabling word if it was added */
+ j = ((len - 1) - rxlvl);
+ /* Read received words */
+ for (i = 0; i < rxlvl; i++, j++)
+ valid_str[i] = (u8)buf[j];
+ put_data_to_circ_buf(s, valid_str, rxlvl);
+ /* Get new RX level */
+ rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK);
+ }
+
+ if (s->rx_enabled) {
+ /* RX still enabled, re-enable RX FIFO interrupt */
+ pr_debug("Enabling RX INT\n");
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+ buf[0] |= s->irqen_reg;
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n");
+ }
+
+ /* Push the received data to receivers */
+ if (s->port.state->port.tty)
+ tty_flip_buffer_push(s->port.state->port.tty);
+}
+
+
+/* Handle data sending */
+static void max3107_handletx(struct max3107_port *s)
+{
+ struct circ_buf *xmit = &s->port.state->xmit;
+ int i;
+ unsigned long flags;
+ int len; /* SPI transfer buffer length */
+ u16 *buf;
+
+ if (!s->tx_fifo_empty)
+ /* Don't send more data before previous data is sent */
+ return;
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+ /* No data to send or TX is stopped */
+ return;
+
+ if (!s->txbuf) {
+ dev_warn(&s->spi->dev, "Txbuf isn't ready\n");
+ return;
+ }
+ buf = s->txbuf;
+ /* Get length of data pending in circular buffer */
+ len = uart_circ_chars_pending(xmit);
+ if (len) {
+ /* Limit to size of TX FIFO */
+ if (len > MAX3107_TX_FIFO_SIZE)
+ len = MAX3107_TX_FIFO_SIZE;
+
+ pr_debug("txlen %d\n", len);
+
+ /* Update TX counter */
+ s->port.icount.tx += len;
+
+ /* TX FIFO will no longer be empty */
+ s->tx_fifo_empty = 0;
+
+ i = 0;
+ if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) {
+ /* First disable TX empty interrupt */
+ pr_debug("Disabling TE INT\n");
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT;
+ buf[i] |= s->irqen_reg;
+ i++;
+ len++;
+ }
+ /* Add data to send */
+ spin_lock_irqsave(&s->port.lock, flags);
+ for ( ; i < len ; i++) {
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG);
+ buf[i] |= ((u16)xmit->buf[xmit->tail] &
+ MAX3107_SPI_TX_DATA_MASK);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ }
+ spin_unlock_irqrestore(&s->port.lock, flags);
+ if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) {
+ /* Enable TX empty interrupt */
+ pr_debug("Enabling TE INT\n");
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT;
+ buf[i] |= s->irqen_reg;
+ i++;
+ len++;
+ }
+ if (!s->tx_enabled) {
+ /* Enable TX */
+ pr_debug("Enable TX\n");
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ spin_lock_irqsave(&s->data_lock, flags);
+ s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT;
+ buf[i] |= s->mode1_reg;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ s->tx_enabled = 1;
+ i++;
+ len++;
+ }
+
+ /* Perform the SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, len*2)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer TX handling failed\n");
+ return;
+ }
+ }
+
+ /* Indicate wake up if circular buffer is getting low on data */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+
+}
+
+/* Handle interrupts
+ * Also reads and returns current RX FIFO level
+ */
+static u16 handle_interrupt(struct max3107_port *s)
+{
+ u16 buf[4]; /* Buffer for SPI transfers */
+ u8 irq_status;
+ u16 rx_level;
+ unsigned long flags;
+
+ /* Read IRQ status register */
+ buf[0] = MAX3107_IRQSTS_REG;
+ /* Read status IRQ status register */
+ buf[1] = MAX3107_STS_IRQSTS_REG;
+ /* Read LSR IRQ status register */
+ buf[2] = MAX3107_LSR_IRQSTS_REG;
+ /* Query RX level */
+ buf[3] = MAX3107_RXFIFOLVL_REG;
+
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer for INTR handling failed\n");
+ return 0;
+ }
+
+ irq_status = (u8)buf[0];
+ pr_debug("IRQSTS %x\n", irq_status);
+ rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK);
+
+ if (irq_status & MAX3107_IRQ_LSR_BIT) {
+ /* LSR interrupt */
+ if (buf[2] & MAX3107_LSR_RXTO_BIT)
+ /* RX timeout interrupt,
+ * handled by normal RX handling
+ */
+ pr_debug("RX TO INT\n");
+ }
+
+ if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) {
+ /* Tx empty interrupt,
+ * disable TX and set tx_fifo_empty flag
+ */
+ pr_debug("TE INT, disabling TX\n");
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ spin_lock_irqsave(&s->data_lock, flags);
+ s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+ buf[0] |= s->mode1_reg;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "SPI transfer TX dis failed\n");
+ s->tx_enabled = 0;
+ s->tx_fifo_empty = 1;
+ }
+
+ if (irq_status & MAX3107_IRQ_RXFIFO_BIT)
+ /* RX FIFO interrupt,
+ * handled by normal RX handling
+ */
+ pr_debug("RFIFO INT\n");
+
+ /* Return RX level */
+ return rx_level;
+}
+
+/* Trigger work thread*/
+static void max3107_dowork(struct max3107_port *s)
+{
+ if (!work_pending(&s->work) && !freezing(current) && !s->suspended)
+ queue_work(s->workqueue, &s->work);
+ else
+ dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n");
+}
+
+/* Work thread */
+static void max3107_work(struct work_struct *w)
+{
+ struct max3107_port *s = container_of(w, struct max3107_port, work);
+ u16 rxlvl = 0;
+ int len; /* SPI transfer buffer length */
+ u16 buf[5]; /* Buffer for SPI transfers */
+ unsigned long flags;
+
+ /* Start by reading current RX FIFO level */
+ buf[0] = MAX3107_RXFIFOLVL_REG;
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer RX lev failed\n");
+ rxlvl = 0;
+ } else {
+ rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK);
+ }
+
+ do {
+ pr_debug("rxlvl %d\n", rxlvl);
+
+ /* Handle RX */
+ max3107_handlerx(s, rxlvl);
+ rxlvl = 0;
+
+ if (s->handle_irq) {
+ /* Handle pending interrupts
+ * We also get new RX FIFO level since new data may
+ * have been received while pushing received data to
+ * receivers
+ */
+ s->handle_irq = 0;
+ rxlvl = handle_interrupt(s);
+ }
+
+ /* Handle TX */
+ max3107_handletx(s);
+
+ /* Handle configuration changes */
+ len = 0;
+ spin_lock_irqsave(&s->data_lock, flags);
+ if (s->mode1_commit) {
+ pr_debug("mode1_commit\n");
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ buf[len++] |= s->mode1_reg;
+ s->mode1_commit = 0;
+ }
+ if (s->lcr_commit) {
+ pr_debug("lcr_commit\n");
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG);
+ buf[len++] |= s->lcr_reg;
+ s->lcr_commit = 0;
+ }
+ if (s->brg_commit) {
+ pr_debug("brg_commit\n");
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG);
+ buf[len++] |= ((s->brg_cfg >> 16) &
+ MAX3107_SPI_TX_DATA_MASK);
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG);
+ buf[len++] |= ((s->brg_cfg >> 8) &
+ MAX3107_SPI_TX_DATA_MASK);
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG);
+ buf[len++] |= ((s->brg_cfg) & 0xff);
+ s->brg_commit = 0;
+ }
+ spin_unlock_irqrestore(&s->data_lock, flags);
+
+ if (len > 0) {
+ if (max3107_rw(s, (u8 *)buf, NULL, len * 2))
+ dev_err(&s->spi->dev,
+ "SPI transfer config failed\n");
+ }
+
+ /* Reloop if interrupt handling indicated data in RX FIFO */
+ } while (rxlvl);
+
+}
+
+/* Set sleep mode */
+static void max3107_set_sleep(struct max3107_port *s, int mode)
+{
+ u16 buf[1]; /* Buffer for SPI transfer */
+ unsigned long flags;
+ pr_debug("enter, mode %d\n", mode);
+
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ spin_lock_irqsave(&s->data_lock, flags);
+ switch (mode) {
+ case MAX3107_DISABLE_FORCED_SLEEP:
+ s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX3107_ENABLE_FORCED_SLEEP:
+ s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX3107_DISABLE_AUTOSLEEP:
+ s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT;
+ break;
+ case MAX3107_ENABLE_AUTOSLEEP:
+ s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT;
+ break;
+ default:
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ dev_warn(&s->spi->dev, "invalid sleep mode\n");
+ return;
+ }
+ buf[0] |= s->mode1_reg;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n");
+
+ if (mode == MAX3107_DISABLE_AUTOSLEEP ||
+ mode == MAX3107_DISABLE_FORCED_SLEEP)
+ msleep(MAX3107_WAKEUP_DELAY);
+}
+
+/* Perform full register initialization */
+static void max3107_register_init(struct max3107_port *s)
+{
+ u16 buf[11]; /* Buffer for SPI transfers */
+
+ /* 1. Configure baud rate, 9600 as default */
+ s->baud = 9600;
+ /* the below is default*/
+ if (s->ext_clk) {
+ s->brg_cfg = MAX3107_BRG26_B9600;
+ s->baud_tbl = (struct baud_table *)brg26_ext;
+ } else {
+ s->brg_cfg = MAX3107_BRG13_IB9600;
+ s->baud_tbl = (struct baud_table *)brg13_int;
+ }
+
+ if (s->pdata->init)
+ s->pdata->init(s);
+
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG)
+ | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK);
+ buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG)
+ | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK);
+ buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG)
+ | ((s->brg_cfg) & 0xff);
+
+ /* 2. Configure LCR register, 8N1 mode by default */
+ s->lcr_reg = MAX3107_LCR_WORD_LEN_8;
+ buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG)
+ | s->lcr_reg;
+
+ /* 3. Configure MODE 1 register */
+ s->mode1_reg = 0;
+ /* Enable IRQ pin */
+ s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT;
+ /* Disable TX */
+ s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+ s->tx_enabled = 0;
+ /* RX is enabled */
+ s->rx_enabled = 1;
+ buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG)
+ | s->mode1_reg;
+
+ /* 4. Configure MODE 2 register */
+ buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+ if (s->loopback) {
+ /* Enable loopback */
+ buf[5] |= MAX3107_MODE2_LOOPBACK_BIT;
+ }
+ /* Reset FIFOs */
+ buf[5] |= MAX3107_MODE2_FIFORST_BIT;
+ s->tx_fifo_empty = 1;
+
+ /* 5. Configure FIFO trigger level register */
+ buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG);
+ /* RX FIFO trigger for 16 words, TX FIFO trigger not used */
+ buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0));
+
+ /* 6. Configure flow control levels */
+ buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG);
+ /* Flow control halt level 96, resume level 48 */
+ buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96));
+
+ /* 7. Configure flow control */
+ buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG);
+ /* Enable auto CTS and auto RTS flow control */
+ buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT);
+
+ /* 8. Configure RX timeout register */
+ buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG);
+ /* Timeout after 48 character intervals */
+ buf[9] |= 0x0030;
+
+ /* 9. Configure LSR interrupt enable register */
+ buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG);
+ /* Enable RX timeout interrupt */
+ buf[10] |= MAX3107_LSR_RXTO_BIT;
+
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 22))
+ dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+ /* 10. Clear IRQ status register by reading it */
+ buf[0] = MAX3107_IRQSTS_REG;
+
+ /* 11. Configure interrupt enable register */
+ /* Enable LSR interrupt */
+ s->irqen_reg = MAX3107_IRQ_LSR_BIT;
+ /* Enable RX FIFO interrupt */
+ s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+ buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG)
+ | s->irqen_reg;
+
+ /* 12. Clear FIFO reset that was set in step 6 */
+ buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+ if (s->loopback) {
+ /* Keep loopback enabled */
+ buf[2] |= MAX3107_MODE2_LOOPBACK_BIT;
+ }
+
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6))
+ dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+}
+
+/* IRQ handler */
+static irqreturn_t max3107_irq(int irqno, void *dev_id)
+{
+ struct max3107_port *s = dev_id;
+
+ if (irqno != s->spi->irq) {
+ /* Unexpected IRQ */
+ return IRQ_NONE;
+ }
+
+ /* Indicate irq */
+ s->handle_irq = 1;
+
+ /* Trigger work thread */
+ max3107_dowork(s);
+
+ return IRQ_HANDLED;
+}
+
+/* HW suspension function
+ *
+ * Currently autosleep is used to decrease current consumption, alternative
+ * approach would be to set the chip to reset mode if UART is not being
+ * used but that would mess the GPIOs
+ *
+ */
+void max3107_hw_susp(struct max3107_port *s, int suspend)
+{
+ pr_debug("enter, suspend %d\n", suspend);
+
+ if (suspend) {
+ /* Suspend requested,
+ * enable autosleep to decrease current consumption
+ */
+ s->suspended = 1;
+ max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP);
+ } else {
+ /* Resume requested,
+ * disable autosleep
+ */
+ s->suspended = 0;
+ max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP);
+ }
+}
+EXPORT_SYMBOL_GPL(max3107_hw_susp);
+
+/* Modem status IRQ enabling */
+static void max3107_enable_ms(struct uart_port *port)
+{
+ /* Modem status not supported */
+}
+
+/* Data send function */
+static void max3107_start_tx(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ /* Trigger work thread for sending data */
+ max3107_dowork(s);
+}
+
+/* Function for checking that there is no pending transfers */
+static unsigned int max3107_tx_empty(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ pr_debug("returning %d\n",
+ (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit)));
+ return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit);
+}
+
+/* Function for stopping RX */
+static void max3107_stop_rx(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ unsigned long flags;
+
+ /* Set RX disabled in MODE 1 register */
+ spin_lock_irqsave(&s->data_lock, flags);
+ s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT;
+ s->mode1_commit = 1;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ /* Set RX disabled */
+ s->rx_enabled = 0;
+ /* Trigger work thread for doing the actual configuration change */
+ max3107_dowork(s);
+}
+
+/* Function for returning control pin states */
+static unsigned int max3107_get_mctrl(struct uart_port *port)
+{
+ /* DCD and DSR are not wired and CTS/RTS is handled automatically
+ * so just indicate DSR and CAR asserted
+ */
+ return TIOCM_DSR | TIOCM_CAR;
+}
+
+/* Function for setting control pin states */
+static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* DCD and DSR are not wired and CTS/RTS is hadnled automatically
+ * so do nothing
+ */
+}
+
+/* Function for configuring UART parameters */
+static void max3107_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ struct tty_struct *tty;
+ int baud;
+ u16 new_lcr = 0;
+ u32 new_brg = 0;
+ unsigned long flags;
+
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return;
+
+ /* Get new LCR register values */
+ /* Word size */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ new_lcr |= MAX3107_LCR_WORD_LEN_7;
+ else
+ new_lcr |= MAX3107_LCR_WORD_LEN_8;
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ new_lcr |= MAX3107_LCR_PARITY_BIT;
+ if (!(termios->c_cflag & PARODD))
+ new_lcr |= MAX3107_LCR_EVENPARITY_BIT;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB) {
+ /* 2 stop bits */
+ new_lcr |= MAX3107_LCR_STOPLEN_BIT;
+ }
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+
+ /* Set status ignore mask */
+ s->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ s->port.ignore_status_mask |= MAX3107_ALL_ERRORS;
+
+ /* Set low latency to immediately handle pushed data */
+ s->port.state->port.tty->low_latency = 1;
+
+ /* Get new baud rate generator configuration */
+ baud = tty_get_baud_rate(tty);
+
+ spin_lock_irqsave(&s->data_lock, flags);
+ new_brg = get_new_brg(baud, s);
+ /* if can't find the corrent config, use previous */
+ if (!new_brg) {
+ baud = s->baud;
+ new_brg = s->brg_cfg;
+ }
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ s->baud = baud;
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_lock_irqsave(&s->data_lock, flags);
+ if (s->lcr_reg != new_lcr) {
+ s->lcr_reg = new_lcr;
+ s->lcr_commit = 1;
+ }
+ if (s->brg_cfg != new_brg) {
+ s->brg_cfg = new_brg;
+ s->brg_commit = 1;
+ }
+ spin_unlock_irqrestore(&s->data_lock, flags);
+
+ /* Trigger work thread for doing the actual configuration change */
+ max3107_dowork(s);
+}
+
+/* Port shutdown function */
+static void max3107_shutdown(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ if (s->suspended && s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 0);
+
+ /* Free the interrupt */
+ free_irq(s->spi->irq, s);
+
+ if (s->workqueue) {
+ /* Flush and destroy work queue */
+ flush_workqueue(s->workqueue);
+ destroy_workqueue(s->workqueue);
+ s->workqueue = NULL;
+ }
+
+ /* Suspend HW */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 1);
+}
+
+/* Port startup function */
+static int max3107_startup(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ /* Initialize work queue */
+ s->workqueue = create_freezeable_workqueue("max3107");
+ if (!s->workqueue) {
+ dev_err(&s->spi->dev, "Workqueue creation failed\n");
+ return -EBUSY;
+ }
+ INIT_WORK(&s->work, max3107_work);
+
+ /* Setup IRQ */
+ if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING,
+ "max3107", s)) {
+ dev_err(&s->spi->dev, "IRQ reguest failed\n");
+ destroy_workqueue(s->workqueue);
+ s->workqueue = NULL;
+ return -EBUSY;
+ }
+
+ /* Resume HW */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 0);
+
+ /* Init registers */
+ max3107_register_init(s);
+
+ return 0;
+}
+
+/* Port type function */
+static const char *max3107_type(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ return s->spi->modalias;
+}
+
+/* Port release function */
+static void max3107_release_port(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+/* Port request function */
+static int max3107_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+/* Port config function */
+static void max3107_config_port(struct uart_port *port, int flags)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ s->port.type = PORT_MAX3107;
+}
+
+/* Port verify function */
+static int max3107_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107)
+ return 0;
+
+ return -EINVAL;
+}
+
+/* Port stop TX function */
+static void max3107_stop_tx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+/* Port break control function */
+static void max3107_break_ctl(struct uart_port *port, int break_state)
+{
+ /* We don't support break control, do nothing */
+}
+
+
+/* Port functions */
+static struct uart_ops max3107_ops = {
+ .tx_empty = max3107_tx_empty,
+ .set_mctrl = max3107_set_mctrl,
+ .get_mctrl = max3107_get_mctrl,
+ .stop_tx = max3107_stop_tx,
+ .start_tx = max3107_start_tx,
+ .stop_rx = max3107_stop_rx,
+ .enable_ms = max3107_enable_ms,
+ .break_ctl = max3107_break_ctl,
+ .startup = max3107_startup,
+ .shutdown = max3107_shutdown,
+ .set_termios = max3107_set_termios,
+ .type = max3107_type,
+ .release_port = max3107_release_port,
+ .request_port = max3107_request_port,
+ .config_port = max3107_config_port,
+ .verify_port = max3107_verify_port,
+};
+
+/* UART driver data */
+static struct uart_driver max3107_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyMAX",
+ .dev_name = "ttyMAX",
+ .nr = 1,
+};
+
+static int driver_registered = 0;
+
+
+
+/* 'Generic' platform data */
+static struct max3107_plat generic_plat_data = {
+ .loopback = 0,
+ .ext_clk = 1,
+ .hw_suspend = max3107_hw_susp,
+ .polled_mode = 0,
+ .poll_time = 0,
+};
+
+
+/*******************************************************************/
+
+/**
+ * max3107_probe - SPI bus probe entry point
+ * @spi: the spi device
+ *
+ * SPI wants us to probe this device and if appropriate claim it.
+ * Perform any platform specific requirements and then initialise
+ * the device.
+ */
+
+int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata)
+{
+ struct max3107_port *s;
+ u16 buf[2]; /* Buffer for SPI transfers */
+ int retval;
+
+ pr_info("enter max3107 probe\n");
+
+ /* Allocate port structure */
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s) {
+ pr_err("Allocating port structure failed\n");
+ return -ENOMEM;
+ }
+
+ s->pdata = pdata;
+
+ /* SPI Rx buffer
+ * +2 for RX FIFO interrupt
+ * disabling and RX level query
+ */
+ s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL);
+ if (!s->rxbuf) {
+ pr_err("Allocating RX buffer failed\n");
+ return -ENOMEM;
+ }
+ s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL);
+ if (!s->rxstr) {
+ pr_err("Allocating RX buffer failed\n");
+ return -ENOMEM;
+ }
+ /* SPI Tx buffer
+ * SPI transfer buffer
+ * +3 for TX FIFO empty
+ * interrupt disabling and
+ * enabling and TX enabling
+ */
+ s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL);
+ if (!s->txbuf) {
+ pr_err("Allocating TX buffer failed\n");
+ return -ENOMEM;
+ }
+ /* Initialize shared data lock */
+ spin_lock_init(&s->data_lock);
+
+ /* SPI intializations */
+ dev_set_drvdata(&spi->dev, s);
+ spi->mode = SPI_MODE_0;
+ spi->dev.platform_data = pdata;
+ spi->bits_per_word = 16;
+ s->ext_clk = pdata->ext_clk;
+ s->loopback = pdata->loopback;
+ spi_setup(spi);
+ s->spi = spi;
+
+ /* Check REV ID to ensure we are talking to what we expect */
+ buf[0] = MAX3107_REVID_REG;
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n");
+ return -EIO;
+ }
+ if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 &&
+ (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) {
+ dev_err(&s->spi->dev, "REVID %x does not match\n",
+ (buf[0] & MAX3107_SPI_RX_DATA_MASK));
+ return -ENODEV;
+ }
+
+ /* Disable all interrupts */
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000);
+ buf[0] |= 0x0000;
+
+ /* Configure clock source */
+ buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG);
+ if (s->ext_clk) {
+ /* External clock */
+ buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT;
+ }
+
+ /* PLL bypass ON */
+ buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT;
+
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+ dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+ return -EIO;
+ }
+
+ /* Register UART driver */
+ if (!driver_registered) {
+ retval = uart_register_driver(&max3107_uart_driver);
+ if (retval) {
+ dev_err(&s->spi->dev, "Registering UART driver failed\n");
+ return retval;
+ }
+ driver_registered = 1;
+ }
+
+ /* Initialize UART port data */
+ s->port.fifosize = 128;
+ s->port.ops = &max3107_ops;
+ s->port.line = 0;
+ s->port.dev = &spi->dev;
+ s->port.uartclk = 9600;
+ s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+ s->port.irq = s->spi->irq;
+ s->port.type = PORT_MAX3107;
+
+ /* Add UART port */
+ retval = uart_add_one_port(&max3107_uart_driver, &s->port);
+ if (retval < 0) {
+ dev_err(&s->spi->dev, "Adding UART port failed\n");
+ return retval;
+ }
+
+ if (pdata->configure) {
+ retval = pdata->configure(s);
+ if (retval < 0)
+ return retval;
+ }
+
+ /* Go to suspend mode */
+ if (pdata->hw_suspend)
+ pdata->hw_suspend(s, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_probe);
+
+/* Driver remove function */
+int max3107_remove(struct spi_device *spi)
+{
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ pr_info("enter max3107 remove\n");
+
+ /* Remove port */
+ if (uart_remove_one_port(&max3107_uart_driver, &s->port))
+ dev_warn(&s->spi->dev, "Removing UART port failed\n");
+
+
+ /* Free TxRx buffer */
+ kfree(s->rxbuf);
+ kfree(s->rxstr);
+ kfree(s->txbuf);
+
+ /* Free port structure */
+ kfree(s);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_remove);
+
+/* Driver suspend function */
+int max3107_suspend(struct spi_device *spi, pm_message_t state)
+{
+#ifdef CONFIG_PM
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ pr_debug("enter suspend\n");
+
+ /* Suspend UART port */
+ uart_suspend_port(&max3107_uart_driver, &s->port);
+
+ /* Go to suspend mode */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 1);
+#endif /* CONFIG_PM */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_suspend);
+
+/* Driver resume function */
+int max3107_resume(struct spi_device *spi)
+{
+#ifdef CONFIG_PM
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ pr_debug("enter resume\n");
+
+ /* Resume from suspend */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 0);
+
+ /* Resume UART port */
+ uart_resume_port(&max3107_uart_driver, &s->port);
+#endif /* CONFIG_PM */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_resume);
+
+static int max3107_probe_generic(struct spi_device *spi)
+{
+ return max3107_probe(spi, &generic_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+ .driver = {
+ .name = "max3107",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = max3107_probe_generic,
+ .remove = __devexit_p(max3107_remove),
+ .suspend = max3107_suspend,
+ .resume = max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+ pr_info("enter max3107 init\n");
+ return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+ pr_info("enter max3107 exit\n");
+ /* Unregister UART driver */
+ if (driver_registered)
+ uart_unregister_driver(&max3107_uart_driver);
+ spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("max3107-spi");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/serial/max3107.h b/drivers/serial/max3107.h
new file mode 100644
index 0000000..7ab63239
--- /dev/null
+++ b/drivers/serial/max3107.h
@@ -0,0 +1,441 @@
+/*
+ * max3107.h - spi uart protocol driver header for Maxim 3107
+ *
+ * Copyright (C) Aavamobile 2009
+ * Based on serial_max3100.h by Christian Pellegrin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _MAX3107_H
+#define _MAX3107_H
+
+/* Serial error status definitions */
+#define MAX3107_PARITY_ERROR 1
+#define MAX3107_FRAME_ERROR 2
+#define MAX3107_OVERRUN_ERROR 4
+#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \
+ MAX3107_FRAME_ERROR | \
+ MAX3107_OVERRUN_ERROR)
+
+/* GPIO definitions */
+#define MAX3107_GPIO_BASE 88
+#define MAX3107_GPIO_COUNT 4
+
+
+/* GPIO connected to chip's reset pin */
+#define MAX3107_RESET_GPIO 87
+
+
+/* Chip reset delay */
+#define MAX3107_RESET_DELAY 10
+
+/* Chip wakeup delay */
+#define MAX3107_WAKEUP_DELAY 50
+
+
+/* Sleep mode definitions */
+#define MAX3107_DISABLE_FORCED_SLEEP 0
+#define MAX3107_ENABLE_FORCED_SLEEP 1
+#define MAX3107_DISABLE_AUTOSLEEP 2
+#define MAX3107_ENABLE_AUTOSLEEP 3
+
+
+/* Definitions for register access with SPI transfers
+ *
+ * SPI transfer format:
+ *
+ * Master to slave bits xzzzzzzzyyyyyyyy
+ * Slave to master bits aaaaaaaabbbbbbbb
+ *
+ * where:
+ * x = 0 for reads, 1 for writes
+ * z = register address
+ * y = new register value if write, 0 if read
+ * a = unspecified
+ * b = register value if read, unspecified if write
+ */
+
+/* SPI speed */
+#define MAX3107_SPI_SPEED (3125000 * 2)
+
+/* Write bit */
+#define MAX3107_WRITE_BIT (1 << 15)
+
+/* SPI TX data mask */
+#define MAX3107_SPI_RX_DATA_MASK (0x00ff)
+
+/* SPI RX data mask */
+#define MAX3107_SPI_TX_DATA_MASK (0x00ff)
+
+/* Register access masks */
+#define MAX3107_RHR_REG (0x0000) /* RX FIFO */
+#define MAX3107_THR_REG (0x0000) /* TX FIFO */
+#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */
+#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */
+#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */
+#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */
+#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */
+#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */
+#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */
+#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */
+#define MAX3107_MODE1_REG (0x0900) /* MODE1 */
+#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */
+#define MAX3107_LCR_REG (0x0b00) /* LCR */
+#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */
+#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */
+#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */
+#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */
+#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */
+#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */
+#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */
+#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */
+#define MAX3107_XON1_REG (0x1400) /* XON1 character */
+#define MAX3107_XON2_REG (0x1500) /* XON2 character */
+#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */
+#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */
+#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */
+#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */
+#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */
+#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */
+#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */
+#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */
+#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */
+#define MAX3107_REVID_REG (0x1f00) /* Revision identification */
+
+/* IRQ register bits */
+#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
+#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */
+#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */
+#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */
+#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */
+#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */
+#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */
+#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */
+#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */
+#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */
+#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */
+#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */
+#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */
+#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */
+#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */
+#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */
+#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */
+#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */
+#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */
+#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* Status register bits */
+#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */
+#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */
+#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */
+#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */
+#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */
+#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */
+#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */
+#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* MODE1 register bits */
+#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */
+#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */
+#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */
+#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */
+#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */
+#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */
+#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */
+#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */
+#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */
+#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */
+#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */
+#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */
+#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */
+#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */
+#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
+#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1
+ *
+ * Word length bits table:
+ * 00 -> 5 bit words
+ * 01 -> 6 bit words
+ * 10 -> 7 bit words
+ * 11 -> 8 bit words
+ */
+#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit
+ *
+ * STOP length bit table:
+ * 0 -> 1 stop bit
+ * 1 -> 1-1.5 stop bits if
+ * word length is 5,
+ * 2 stop bits otherwise
+ */
+#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
+#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
+#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
+#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
+#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */
+#define MAX3107_LCR_WORD_LEN_5 (0x0000)
+#define MAX3107_LCR_WORD_LEN_6 (0x0001)
+#define MAX3107_LCR_WORD_LEN_7 (0x0002)
+#define MAX3107_LCR_WORD_LEN_8 (0x0003)
+
+
+/* IRDA register bits */
+#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
+#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
+#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */
+#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */
+#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */
+#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */
+#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* Flow control trigger level register masks */
+#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
+#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */
+#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f)
+#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f)
+#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4)
+
+/* Flow control register bits */
+#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs
+ * are used in conjunction with
+ * XOFF2 for definition of
+ * special character */
+#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */
+#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */
+#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1
+ *
+ * SWFLOW bits 1 & 0 table:
+ * 00 -> no transmitter flow
+ * control
+ * 01 -> receiver compares
+ * XON2 and XOFF2
+ * and controls
+ * transmitter
+ * 10 -> receiver compares
+ * XON1 and XOFF1
+ * and controls
+ * transmitter
+ * 11 -> receiver compares
+ * XON1, XON2, XOFF1 and
+ * XOFF2 and controls
+ * transmitter
+ */
+#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */
+#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no received flow
+ * control
+ * 01 -> transmitter generates
+ * XON2 and XOFF2
+ * 10 -> transmitter generates
+ * XON1 and XOFF1
+ * 11 -> transmitter generates
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+
+/* GPIO configuration register bits */
+#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */
+#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */
+#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */
+#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */
+#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */
+#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */
+#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */
+#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */
+
+/* GPIO DATA register bits */
+#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */
+#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */
+#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */
+#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */
+#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */
+#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */
+#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */
+#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */
+
+/* PLL configuration register masks */
+#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */
+#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register masks and bits */
+#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of
+ * Baud rate generator divisor
+ */
+#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */
+#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */
+#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* Clock source register bits */
+#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */
+#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */
+#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */
+#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */
+#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
+#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */
+#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
+
+
+/* HW definitions */
+#define MAX3107_RX_FIFO_SIZE 128
+#define MAX3107_TX_FIFO_SIZE 128
+#define MAX3107_REVID1 0x00a0
+#define MAX3107_REVID2 0x00a1
+
+
+/* Baud rate generator configuration values for external clock 13MHz */
+#define MAX3107_BRG13_B300 (0x0A9400 | 0x05)
+#define MAX3107_BRG13_B600 (0x054A00 | 0x03)
+#define MAX3107_BRG13_B1200 (0x02A500 | 0x01)
+#define MAX3107_BRG13_B2400 (0x015200 | 0x09)
+#define MAX3107_BRG13_B4800 (0x00A900 | 0x04)
+#define MAX3107_BRG13_B9600 (0x005400 | 0x0A)
+#define MAX3107_BRG13_B19200 (0x002A00 | 0x05)
+#define MAX3107_BRG13_B38400 (0x001500 | 0x03)
+#define MAX3107_BRG13_B57600 (0x000E00 | 0x02)
+#define MAX3107_BRG13_B115200 (0x000700 | 0x01)
+#define MAX3107_BRG13_B230400 (0x000300 | 0x08)
+#define MAX3107_BRG13_B460800 (0x000100 | 0x0c)
+#define MAX3107_BRG13_B921600 (0x000100 | 0x1c)
+
+/* Baud rate generator configuration values for external clock 26MHz */
+#define MAX3107_BRG26_B300 (0x152800 | 0x0A)
+#define MAX3107_BRG26_B600 (0x0A9400 | 0x05)
+#define MAX3107_BRG26_B1200 (0x054A00 | 0x03)
+#define MAX3107_BRG26_B2400 (0x02A500 | 0x01)
+#define MAX3107_BRG26_B4800 (0x015200 | 0x09)
+#define MAX3107_BRG26_B9600 (0x00A900 | 0x04)
+#define MAX3107_BRG26_B19200 (0x005400 | 0x0A)
+#define MAX3107_BRG26_B38400 (0x002A00 | 0x05)
+#define MAX3107_BRG26_B57600 (0x001C00 | 0x03)
+#define MAX3107_BRG26_B115200 (0x000E00 | 0x02)
+#define MAX3107_BRG26_B230400 (0x000700 | 0x01)
+#define MAX3107_BRG26_B460800 (0x000300 | 0x08)
+#define MAX3107_BRG26_B921600 (0x000100 | 0x0C)
+
+/* Baud rate generator configuration values for internal clock */
+#define MAX3107_BRG13_IB300 (0x008000 | 0x00)
+#define MAX3107_BRG13_IB600 (0x004000 | 0x00)
+#define MAX3107_BRG13_IB1200 (0x002000 | 0x00)
+#define MAX3107_BRG13_IB2400 (0x001000 | 0x00)
+#define MAX3107_BRG13_IB4800 (0x000800 | 0x00)
+#define MAX3107_BRG13_IB9600 (0x000400 | 0x00)
+#define MAX3107_BRG13_IB19200 (0x000200 | 0x00)
+#define MAX3107_BRG13_IB38400 (0x000100 | 0x00)
+#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B)
+#define MAX3107_BRG13_IB115200 (0x000000 | 0x05)
+#define MAX3107_BRG13_IB230400 (0x000000 | 0x03)
+#define MAX3107_BRG13_IB460800 (0x000000 | 0x00)
+#define MAX3107_BRG13_IB921600 (0x000000 | 0x00)
+
+
+struct baud_table {
+ int baud;
+ u32 new_brg;
+};
+
+struct max3107_port {
+ /* UART port structure */
+ struct uart_port port;
+
+ /* SPI device structure */
+ struct spi_device *spi;
+
+#if defined(CONFIG_GPIOLIB)
+ /* GPIO chip stucture */
+ struct gpio_chip chip;
+#endif
+
+ /* Workqueue that does all the magic */
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ /* Lock for shared data */
+ spinlock_t data_lock;
+
+ /* Device configuration */
+ int ext_clk; /* 1 if external clock used */
+ int loopback; /* Current loopback mode state */
+ int baud; /* Current baud rate */
+
+ /* State flags */
+ int suspended; /* Indicates suspend mode */
+ int tx_fifo_empty; /* Flag for TX FIFO state */
+ int rx_enabled; /* Flag for receiver state */
+ int tx_enabled; /* Flag for transmitter state */
+
+ u16 irqen_reg; /* Current IRQ enable register value */
+ /* Shared data */
+ u16 mode1_reg; /* Current mode1 register value*/
+ int mode1_commit; /* Flag for setting new mode1 register value */
+ u16 lcr_reg; /* Current LCR register value */
+ int lcr_commit; /* Flag for setting new LCR register value */
+ u32 brg_cfg; /* Current Baud rate generator config */
+ int brg_commit; /* Flag for setting new baud rate generator
+ * config
+ */
+ struct baud_table *baud_tbl;
+ int handle_irq; /* Indicates that IRQ should be handled */
+
+ /* Rx buffer and str*/
+ u16 *rxbuf;
+ u8 *rxstr;
+ /* Tx buffer*/
+ u16 *txbuf;
+
+ struct max3107_plat *pdata; /* Platform data */
+};
+
+/* Platform data structure */
+struct max3107_plat {
+ /* Loopback mode enable */
+ int loopback;
+ /* External clock enable */
+ int ext_clk;
+ /* Called during the register initialisation */
+ void (*init)(struct max3107_port *s);
+ /* Called when the port is found and configured */
+ int (*configure)(struct max3107_port *s);
+ /* HW suspend function */
+ void (*hw_suspend) (struct max3107_port *s, int suspend);
+ /* Polling mode enable */
+ int polled_mode;
+ /* Polling period if polling mode enabled */
+ int poll_time;
+};
+
+extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len);
+extern void max3107_hw_susp(struct max3107_port *s, int suspend);
+extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata);
+extern int max3107_remove(struct spi_device *spi);
+extern int max3107_suspend(struct spi_device *spi, pm_message_t state);
+extern int max3107_resume(struct spi_device *spi);
+
+#endif /* _LINUX_SERIAL_MAX3107_H */
diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c
index b5aaef9..3394b7c 100644
--- a/drivers/serial/mcf.c
+++ b/drivers/serial/mcf.c
@@ -70,16 +70,14 @@ static unsigned int mcf_tx_empty(struct uart_port *port)
static unsigned int mcf_get_mctrl(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
unsigned int sigs;
- spin_lock_irqsave(&port->lock, flags);
sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ?
0 : TIOCM_CTS;
sigs |= (pp->sigs & TIOCM_RTS);
sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0);
sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0);
- spin_unlock_irqrestore(&port->lock, flags);
+
return sigs;
}
@@ -88,16 +86,13 @@ static unsigned int mcf_get_mctrl(struct uart_port *port)
static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->sigs = sigs;
mcf_setppdtr(port->line, (sigs & TIOCM_DTR));
if (sigs & TIOCM_RTS)
writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
else
writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
@@ -105,12 +100,9 @@ static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
static void mcf_start_tx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->imr |= MCFUART_UIR_TXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
@@ -118,12 +110,9 @@ static void mcf_start_tx(struct uart_port *port)
static void mcf_stop_tx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->imr &= ~MCFUART_UIR_TXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
@@ -131,12 +120,9 @@ static void mcf_stop_tx(struct uart_port *port)
static void mcf_stop_rx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->imr &= ~MCFUART_UIR_RXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
@@ -366,13 +352,22 @@ static irqreturn_t mcf_interrupt(int irq, void *data)
struct uart_port *port = data;
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned int isr;
+ irqreturn_t ret = IRQ_NONE;
isr = readb(port->membase + MCFUART_UISR) & pp->imr;
- if (isr & MCFUART_UIR_RXREADY)
+
+ spin_lock(&port->lock);
+ if (isr & MCFUART_UIR_RXREADY) {
mcf_rx_chars(pp);
- if (isr & MCFUART_UIR_TXREADY)
+ ret = IRQ_HANDLED;
+ }
+ if (isr & MCFUART_UIR_TXREADY) {
mcf_tx_chars(pp);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
+ }
+ spin_unlock(&port->lock);
+
+ return ret;
}
/****************************************************************************/
diff --git a/drivers/serial/mfd.c b/drivers/serial/mfd.c
new file mode 100644
index 0000000..bc9af50
--- /dev/null
+++ b/drivers/serial/mfd.c
@@ -0,0 +1,1498 @@
+/*
+ * mfd.c: driver for High Speed UART device of Intel Medfield platform
+ *
+ * Refer pxa.c, 8250.c and some other drivers in drivers/serial/
+ *
+ * (C) Copyright 2010 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+/* Notes:
+ * 1. DMA channel allocation: 0/1 channel are assigned to port 0,
+ * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans
+ * are used for RX, odd chans for TX
+ *
+ * 2. In A0 stepping, UART will not support TX half empty flag
+ *
+ * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always
+ * asserted, only when the HW is reset the DDCD and DDSR will
+ * be triggered
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_mfd.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+
+#define MFD_HSU_A0_STEPPING 1
+
+#define HSU_DMA_BUF_SIZE 2048
+
+#define chan_readl(chan, offset) readl(chan->reg + offset)
+#define chan_writel(chan, offset, val) writel(val, chan->reg + offset)
+
+#define mfd_readl(obj, offset) readl(obj->reg + offset)
+#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset)
+
+#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10)
+
+struct hsu_dma_buffer {
+ u8 *buf;
+ dma_addr_t dma_addr;
+ u32 dma_size;
+ u32 ofs;
+};
+
+struct hsu_dma_chan {
+ u32 id;
+ enum dma_data_direction dirt;
+ struct uart_hsu_port *uport;
+ void __iomem *reg;
+ struct timer_list rx_timer; /* only needed by RX channel */
+};
+
+struct uart_hsu_port {
+ struct uart_port port;
+ unsigned char ier;
+ unsigned char lcr;
+ unsigned char mcr;
+ unsigned int lsr_break_flag;
+ char name[12];
+ int index;
+ struct device *dev;
+
+ struct hsu_dma_chan *txc;
+ struct hsu_dma_chan *rxc;
+ struct hsu_dma_buffer txbuf;
+ struct hsu_dma_buffer rxbuf;
+ int use_dma; /* flag for DMA/PIO */
+ int running;
+ int dma_tx_on;
+};
+
+/* Top level data structure of HSU */
+struct hsu_port {
+ void __iomem *reg;
+ unsigned long paddr;
+ unsigned long iolen;
+ u32 irq;
+
+ struct uart_hsu_port port[3];
+ struct hsu_dma_chan chans[10];
+
+ struct dentry *debugfs;
+};
+
+static inline unsigned int serial_in(struct uart_hsu_port *up, int offset)
+{
+ unsigned int val;
+
+ if (offset > UART_MSR) {
+ offset <<= 2;
+ val = readl(up->port.membase + offset);
+ } else
+ val = (unsigned int)readb(up->port.membase + offset);
+
+ return val;
+}
+
+static inline void serial_out(struct uart_hsu_port *up, int offset, int value)
+{
+ if (offset > UART_MSR) {
+ offset <<= 2;
+ writel(value, up->port.membase + offset);
+ } else {
+ unsigned char val = value & 0xff;
+ writeb(val, up->port.membase + offset);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define HSU_REGS_BUFSIZE 1024
+
+static int hsu_show_regs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t port_show_regs(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct uart_hsu_port *up = file->private_data;
+ char *buf;
+ u32 len = 0;
+ ssize_t ret;
+
+ buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MFD HSU port[%d] regs:\n", up->index);
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "=================================\n");
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "IER: \t\t0x%08x\n", serial_in(up, UART_IER));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "PS: \t\t0x%08x\n", serial_in(up, UART_PS));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV));
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t dma_show_regs(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hsu_dma_chan *chan = file->private_data;
+ char *buf;
+ u32 len = 0;
+ ssize_t ret;
+
+ buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MFD HSU DMA channel [%d] regs:\n", chan->id);
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "=================================\n");
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR));
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations port_regs_ops = {
+ .owner = THIS_MODULE,
+ .open = hsu_show_regs_open,
+ .read = port_show_regs,
+};
+
+static const struct file_operations dma_regs_ops = {
+ .owner = THIS_MODULE,
+ .open = hsu_show_regs_open,
+ .read = dma_show_regs,
+};
+
+static int hsu_debugfs_init(struct hsu_port *hsu)
+{
+ int i;
+ char name[32];
+
+ hsu->debugfs = debugfs_create_dir("hsu", NULL);
+ if (!hsu->debugfs)
+ return -ENOMEM;
+
+ for (i = 0; i < 3; i++) {
+ snprintf(name, sizeof(name), "port_%d_regs", i);
+ debugfs_create_file(name, S_IFREG | S_IRUGO,
+ hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops);
+ }
+
+ for (i = 0; i < 6; i++) {
+ snprintf(name, sizeof(name), "dma_chan_%d_regs", i);
+ debugfs_create_file(name, S_IFREG | S_IRUGO,
+ hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops);
+ }
+
+ return 0;
+}
+
+static void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+ if (hsu->debugfs)
+ debugfs_remove_recursive(hsu->debugfs);
+}
+
+#else
+static inline int hsu_debugfs_init(struct hsu_port *hsu)
+{
+ return 0;
+}
+
+static inline void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void serial_hsu_enable_ms(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+
+ up->ier |= UART_IER_MSI;
+ serial_out(up, UART_IER, up->ier);
+}
+
+void hsu_dma_tx(struct uart_hsu_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ struct hsu_dma_buffer *dbuf = &up->txbuf;
+ int count;
+
+ /* test_and_set_bit may be better, but anyway it's in lock protected mode */
+ if (up->dma_tx_on)
+ return;
+
+ /* Update the circ buf info */
+ xmit->tail += dbuf->ofs;
+ xmit->tail &= UART_XMIT_SIZE - 1;
+
+ up->port.icount.tx += dbuf->ofs;
+ dbuf->ofs = 0;
+
+ /* Disable the channel */
+ chan_writel(up->txc, HSU_CH_CR, 0x0);
+
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) {
+ dma_sync_single_for_device(up->port.dev,
+ dbuf->dma_addr,
+ dbuf->dma_size,
+ DMA_TO_DEVICE);
+
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ dbuf->ofs = count;
+
+ /* Reprogram the channel */
+ chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail);
+ chan_writel(up->txc, HSU_CH_D0TSR, count);
+
+ /* Reenable the channel */
+ chan_writel(up->txc, HSU_CH_DCR, 0x1
+ | (0x1 << 8)
+ | (0x1 << 16)
+ | (0x1 << 24));
+ up->dma_tx_on = 1;
+ chan_writel(up->txc, HSU_CH_CR, 0x1);
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+}
+
+/* The buffer is already cache coherent */
+void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf)
+{
+ dbuf->ofs = 0;
+
+ chan_writel(rxc, HSU_CH_BSR, 32);
+ chan_writel(rxc, HSU_CH_MOTSR, 4);
+
+ chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr);
+ chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size);
+ chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8)
+ | (0x1 << 16)
+ | (0x1 << 24) /* timeout bit, see HSU Errata 1 */
+ );
+ chan_writel(rxc, HSU_CH_CR, 0x3);
+
+ mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ);
+}
+
+/* Protected by spin_lock_irqsave(port->lock) */
+static void serial_hsu_start_tx(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+
+ if (up->use_dma) {
+ hsu_dma_tx(up);
+ } else if (!(up->ier & UART_IER_THRI)) {
+ up->ier |= UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+static void serial_hsu_stop_tx(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ struct hsu_dma_chan *txc = up->txc;
+
+ if (up->use_dma)
+ chan_writel(txc, HSU_CH_CR, 0x0);
+ else if (up->ier & UART_IER_THRI) {
+ up->ier &= ~UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+/* This is always called in spinlock protected mode, so
+ * modify timeout timer is safe here */
+void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
+{
+ struct hsu_dma_buffer *dbuf = &up->rxbuf;
+ struct hsu_dma_chan *chan = up->rxc;
+ struct uart_port *port = &up->port;
+ struct tty_struct *tty = port->state->port.tty;
+ int count;
+
+ if (!tty)
+ return;
+
+ /*
+ * First need to know how many is already transferred,
+ * then check if its a timeout DMA irq, and return
+ * the trail bytes out, push them up and reenable the
+ * channel
+ */
+
+ /* Timeout IRQ, need wait some time, see Errata 2 */
+ if (int_sts & 0xf00)
+ udelay(2);
+
+ /* Stop the channel */
+ chan_writel(chan, HSU_CH_CR, 0x0);
+
+ count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+ if (!count) {
+ /* Restart the channel before we leave */
+ chan_writel(chan, HSU_CH_CR, 0x3);
+ return;
+ }
+ del_timer(&chan->rx_timer);
+
+ dma_sync_single_for_cpu(port->dev, dbuf->dma_addr,
+ dbuf->dma_size, DMA_FROM_DEVICE);
+
+ /*
+ * Head will only wrap around when we recycle
+ * the DMA buffer, and when that happens, we
+ * explicitly set tail to 0. So head will
+ * always be greater than tail.
+ */
+ tty_insert_flip_string(tty, dbuf->buf, count);
+ port->icount.rx += count;
+
+ dma_sync_single_for_device(up->port.dev, dbuf->dma_addr,
+ dbuf->dma_size, DMA_FROM_DEVICE);
+
+ /* Reprogram the channel */
+ chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr);
+ chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size);
+ chan_writel(chan, HSU_CH_DCR, 0x1
+ | (0x1 << 8)
+ | (0x1 << 16)
+ | (0x1 << 24) /* timeout bit, see HSU Errata 1 */
+ );
+ tty_flip_buffer_push(tty);
+
+ chan_writel(chan, HSU_CH_CR, 0x3);
+ chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ;
+ add_timer(&chan->rx_timer);
+
+}
+
+static void serial_hsu_stop_rx(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ struct hsu_dma_chan *chan = up->rxc;
+
+ if (up->use_dma)
+ chan_writel(chan, HSU_CH_CR, 0x2);
+ else {
+ up->ier &= ~UART_IER_RLSI;
+ up->port.read_status_mask &= ~UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+static inline void receive_chars(struct uart_hsu_port *up, int *status)
+{
+ struct tty_struct *tty = up->port.state->port.tty;
+ unsigned int ch, flag;
+ unsigned int max_count = 256;
+
+ if (!tty)
+ return;
+
+ do {
+ ch = serial_in(up, UART_RX);
+ flag = TTY_NORMAL;
+ up->port.icount.rx++;
+
+ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE))) {
+
+ dev_warn(up->dev, "We really rush into ERR/BI case"
+ "status = 0x%02x", *status);
+ /* For statistics only */
+ if (*status & UART_LSR_BI) {
+ *status &= ~(UART_LSR_FE | UART_LSR_PE);
+ up->port.icount.brk++;
+ /*
+ * We do the SysRQ and SAK checking
+ * here because otherwise the break
+ * may get masked by ignore_status_mask
+ * or read_status_mask.
+ */
+ if (uart_handle_break(&up->port))
+ goto ignore_char;
+ } else if (*status & UART_LSR_PE)
+ up->port.icount.parity++;
+ else if (*status & UART_LSR_FE)
+ up->port.icount.frame++;
+ if (*status & UART_LSR_OE)
+ up->port.icount.overrun++;
+
+ /* Mask off conditions which should be ignored. */
+ *status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+ if (up->port.cons &&
+ up->port.cons->index == up->port.line) {
+ /* Recover the break flag from console xmit */
+ *status |= up->lsr_break_flag;
+ up->lsr_break_flag = 0;
+ }
+#endif
+ if (*status & UART_LSR_BI) {
+ flag = TTY_BREAK;
+ } else if (*status & UART_LSR_PE)
+ flag = TTY_PARITY;
+ else if (*status & UART_LSR_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(&up->port, ch))
+ goto ignore_char;
+
+ uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+ ignore_char:
+ *status = serial_in(up, UART_LSR);
+ } while ((*status & UART_LSR_DR) && max_count--);
+ tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_hsu_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ int count;
+
+ if (up->port.x_char) {
+ serial_out(up, UART_TX, up->port.x_char);
+ up->port.icount.tx++;
+ up->port.x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+ serial_hsu_stop_tx(&up->port);
+ return;
+ }
+
+#ifndef MFD_HSU_A0_STEPPING
+ count = up->port.fifosize / 2;
+#else
+ /*
+ * A0 only supports fully empty IRQ, and the first char written
+ * into it won't clear the EMPT bit, so we may need be cautious
+ * by useing a shorter buffer
+ */
+ count = up->port.fifosize - 4;
+#endif
+ do {
+ serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+
+ up->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (uart_circ_empty(xmit))
+ serial_hsu_stop_tx(&up->port);
+}
+
+static inline void check_modem_status(struct uart_hsu_port *up)
+{
+ int status;
+
+ status = serial_in(up, UART_MSR);
+
+ if ((status & UART_MSR_ANY_DELTA) == 0)
+ return;
+
+ if (status & UART_MSR_TERI)
+ up->port.icount.rng++;
+ if (status & UART_MSR_DDSR)
+ up->port.icount.dsr++;
+ /* We may only get DDCD when HW init and reset */
+ if (status & UART_MSR_DDCD)
+ uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+ /* Will start/stop_tx accordingly */
+ if (status & UART_MSR_DCTS)
+ uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static irqreturn_t port_irq(int irq, void *dev_id)
+{
+ struct uart_hsu_port *up = dev_id;
+ unsigned int iir, lsr;
+ unsigned long flags;
+
+ if (unlikely(!up->running))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (up->use_dma) {
+ lsr = serial_in(up, UART_LSR);
+ if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)))
+ dev_warn(up->dev,
+ "Got lsr irq while using DMA, lsr = 0x%2x\n",
+ lsr);
+ check_modem_status(up);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ iir = serial_in(up, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return IRQ_NONE;
+ }
+
+ lsr = serial_in(up, UART_LSR);
+ if (lsr & UART_LSR_DR)
+ receive_chars(up, &lsr);
+ check_modem_status(up);
+
+ /* lsr will be renewed during the receive_chars */
+ if (lsr & UART_LSR_THRE)
+ transmit_chars(up);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return IRQ_HANDLED;
+}
+
+static inline void dma_chan_irq(struct hsu_dma_chan *chan)
+{
+ struct uart_hsu_port *up = chan->uport;
+ unsigned long flags;
+ u32 int_sts;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ if (!up->use_dma || !up->running)
+ goto exit;
+
+ /*
+ * No matter what situation, need read clear the IRQ status
+ * There is a bug, see Errata 5, HSD 2900918
+ */
+ int_sts = chan_readl(chan, HSU_CH_SR);
+
+ /* Rx channel */
+ if (chan->dirt == DMA_FROM_DEVICE)
+ hsu_dma_rx(up, int_sts);
+
+ /* Tx channel */
+ if (chan->dirt == DMA_TO_DEVICE) {
+ chan_writel(chan, HSU_CH_CR, 0x0);
+ up->dma_tx_on = 0;
+ hsu_dma_tx(up);
+ }
+
+exit:
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return;
+}
+
+static irqreturn_t dma_irq(int irq, void *dev_id)
+{
+ struct hsu_port *hsu = dev_id;
+ u32 int_sts, i;
+
+ int_sts = mfd_readl(hsu, HSU_GBL_DMAISR);
+
+ /* Currently we only have 6 channels may be used */
+ for (i = 0; i < 6; i++) {
+ if (int_sts & 0x1)
+ dma_chan_irq(&hsu->chans[i]);
+ int_sts >>= 1;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int serial_hsu_tx_empty(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+ unsigned int ret;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return ret;
+}
+
+static unsigned int serial_hsu_get_mctrl(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned char status;
+ unsigned int ret;
+
+ status = serial_in(up, UART_MSR);
+
+ ret = 0;
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned char mcr = 0;
+
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ mcr |= up->mcr;
+
+ serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_hsu_break_ctl(struct uart_port *port, int break_state)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (break_state == -1)
+ up->lcr |= UART_LCR_SBC;
+ else
+ up->lcr &= ~UART_LCR_SBC;
+ serial_out(up, UART_LCR, up->lcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * What special to do:
+ * 1. chose the 64B fifo mode
+ * 2. make sure not to select half empty mode for A0 stepping
+ * 3. start dma or pio depends on configuration
+ * 4. we only allocate dma memory when needed
+ */
+static int serial_hsu_startup(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+
+ /*
+ * Clear the FIFO buffers and disable them.
+ * (they will be reenabled in set_termios())
+ */
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR, 0);
+
+ /* Clear the interrupt registers. */
+ (void) serial_in(up, UART_LSR);
+ (void) serial_in(up, UART_RX);
+ (void) serial_in(up, UART_IIR);
+ (void) serial_in(up, UART_MSR);
+
+ /* Now, initialize the UART, default is 8n1 */
+ serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ up->port.mctrl |= TIOCM_OUT2;
+ serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+
+ /*
+ * Finally, enable interrupts. Note: Modem status interrupts
+ * are set via set_termios(), which will be occurring imminently
+ * anyway, so we don't enable them here.
+ */
+ if (!up->use_dma)
+ up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE;
+ else
+ up->ier = 0;
+ serial_out(up, UART_IER, up->ier);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /* DMA init */
+ if (up->use_dma) {
+ struct hsu_dma_buffer *dbuf;
+ struct circ_buf *xmit = &port->state->xmit;
+
+ up->dma_tx_on = 0;
+
+ /* First allocate the RX buffer */
+ dbuf = &up->rxbuf;
+ dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL);
+ if (!dbuf->buf) {
+ up->use_dma = 0;
+ goto exit;
+ }
+ dbuf->dma_addr = dma_map_single(port->dev,
+ dbuf->buf,
+ HSU_DMA_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ dbuf->dma_size = HSU_DMA_BUF_SIZE;
+
+ /* Start the RX channel right now */
+ hsu_dma_start_rx_chan(up->rxc, dbuf);
+
+ /* Next init the TX DMA */
+ dbuf = &up->txbuf;
+ dbuf->buf = xmit->buf;
+ dbuf->dma_addr = dma_map_single(port->dev,
+ dbuf->buf,
+ UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ dbuf->dma_size = UART_XMIT_SIZE;
+
+ /* This should not be changed all around */
+ chan_writel(up->txc, HSU_CH_BSR, 32);
+ chan_writel(up->txc, HSU_CH_MOTSR, 4);
+ dbuf->ofs = 0;
+ }
+
+exit:
+ /* And clear the interrupt registers again for luck. */
+ (void) serial_in(up, UART_LSR);
+ (void) serial_in(up, UART_RX);
+ (void) serial_in(up, UART_IIR);
+ (void) serial_in(up, UART_MSR);
+
+ up->running = 1;
+ return 0;
+}
+
+static void serial_hsu_shutdown(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+
+ del_timer_sync(&up->rxc->rx_timer);
+
+ /* Disable interrupts from this port */
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+ up->running = 0;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl &= ~TIOCM_OUT2;
+ serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /* Disable break condition and FIFOs */
+ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR, 0);
+}
+
+static void
+serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ struct tty_struct *tty = port->state->port.tty;
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+ unsigned int baud, quot;
+ u32 mul = 0x3600;
+ u32 ps = 0x10;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ break;
+ }
+
+ /* CMSPAR isn't supported by this driver */
+ if (tty)
+ tty->termios->c_cflag &= ~CMSPAR;
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /*
+ * For those basic low baud rate we can get the direct
+ * scalar from 2746800, like 115200 = 2746800/24, for those
+ * higher baud rate, we have to handle them case by case,
+ * but DIV reg is never touched as its default value 0x3d09
+ */
+ baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ quot = uart_get_divisor(port, baud);
+
+ switch (baud) {
+ case 3500000:
+ mul = 0x3345;
+ ps = 0xC;
+ quot = 1;
+ break;
+ case 2500000:
+ mul = 0x2710;
+ ps = 0x10;
+ quot = 1;
+ break;
+ case 18432000:
+ mul = 0x2400;
+ ps = 0x10;
+ quot = 1;
+ break;
+ case 1500000:
+ mul = 0x1D4C;
+ ps = 0xc;
+ quot = 1;
+ break;
+ default:
+ ;
+ }
+
+ if ((up->port.uartclk / quot) < (2400 * 16))
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B;
+ else if ((up->port.uartclk / quot) < (230400 * 16))
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B;
+
+ fcr |= UART_FCR_HSU_64B_FIFO;
+#ifdef MFD_HSU_A0_STEPPING
+ /* A0 doesn't support half empty IRQ */
+ fcr |= UART_FCR_FULL_EMPT_TXI;
+#endif
+
+ /*
+ * Ok, we're now changing the port state. Do it with
+ * interrupts disabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /* Update the per-port timeout */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ up->port.read_status_mask |= UART_LSR_BI;
+
+ /* Characters to ignore */
+ up->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ up->port.ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /* Ignore all characters if CREAD is not set */
+ if ((termios->c_cflag & CREAD) == 0)
+ up->port.ignore_status_mask |= UART_LSR_DR;
+
+ /*
+ * CTS flow control flag and modem status interrupts, disable
+ * MSI by default
+ */
+ up->ier &= ~UART_IER_MSI;
+ if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+ up->ier |= UART_IER_MSI;
+
+ serial_out(up, UART_IER, up->ier);
+
+ if (termios->c_cflag & CRTSCTS)
+ up->mcr |= UART_MCR_AFE | UART_MCR_RTS;
+ else
+ up->mcr &= ~UART_MCR_AFE;
+
+ serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_out(up, UART_LCR, cval); /* reset DLAB */
+ serial_out(up, UART_MUL, mul); /* set MUL */
+ serial_out(up, UART_PS, ps); /* set PS */
+ up->lcr = cval; /* Save LCR */
+ serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+ serial_out(up, UART_FCR, fcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_hsu_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+}
+
+static void serial_hsu_release_port(struct uart_port *port)
+{
+}
+
+static int serial_hsu_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static void serial_hsu_config_port(struct uart_port *port, int flags)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ up->port.type = PORT_MFD;
+}
+
+static int
+serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* We don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+static const char *
+serial_hsu_type(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ return up->name;
+}
+
+/* Mainly for uart console use */
+static struct uart_hsu_port *serial_hsu_ports[3];
+static struct uart_driver serial_hsu_reg;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/* Wait for transmitter & holding register to empty */
+static inline void wait_for_xmitr(struct uart_hsu_port *up)
+{
+ unsigned int status, tmout = 1000;
+
+ /* Wait up to 1ms for the character to be sent. */
+ do {
+ status = serial_in(up, UART_LSR);
+
+ if (status & UART_LSR_BI)
+ up->lsr_break_flag = UART_LSR_BI;
+
+ if (--tmout == 0)
+ break;
+ udelay(1);
+ } while (!(status & BOTH_EMPTY));
+
+ /* Wait up to 1s for flow control if necessary */
+ if (up->port.flags & UPF_CONS_FLOW) {
+ tmout = 1000000;
+ while (--tmout &&
+ ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+ udelay(1);
+ }
+}
+
+static void serial_hsu_console_putchar(struct uart_port *port, int ch)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+
+ wait_for_xmitr(up);
+ serial_out(up, UART_TX, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ * The console_lock must be held when we get here.
+ */
+static void
+serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_hsu_port *up = serial_hsu_ports[co->index];
+ unsigned long flags;
+ unsigned int ier;
+ int locked = 1;
+
+ local_irq_save(flags);
+ if (up->port.sysrq)
+ locked = 0;
+ else if (oops_in_progress) {
+ locked = spin_trylock(&up->port.lock);
+ } else
+ spin_lock(&up->port.lock);
+
+ /* First save the IER then disable the interrupts */
+ ier = serial_in(up, UART_IER);
+ serial_out(up, UART_IER, 0);
+
+ uart_console_write(&up->port, s, count, serial_hsu_console_putchar);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the IER
+ */
+ wait_for_xmitr(up);
+ serial_out(up, UART_IER, ier);
+
+ if (locked)
+ spin_unlock(&up->port.lock);
+ local_irq_restore(flags);
+}
+
+static struct console serial_hsu_console;
+
+static int __init
+serial_hsu_console_setup(struct console *co, char *options)
+{
+ struct uart_hsu_port *up;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+
+ if (co->index == -1 || co->index >= serial_hsu_reg.nr)
+ co->index = 0;
+ up = serial_hsu_ports[co->index];
+ if (!up)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ ret = uart_set_options(&up->port, co, baud, parity, bits, flow);
+
+ return ret;
+}
+
+static struct console serial_hsu_console = {
+ .name = "ttyMFD",
+ .write = serial_hsu_console_write,
+ .device = uart_console_device,
+ .setup = serial_hsu_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = 2,
+ .data = &serial_hsu_reg,
+};
+#endif
+
+struct uart_ops serial_hsu_pops = {
+ .tx_empty = serial_hsu_tx_empty,
+ .set_mctrl = serial_hsu_set_mctrl,
+ .get_mctrl = serial_hsu_get_mctrl,
+ .stop_tx = serial_hsu_stop_tx,
+ .start_tx = serial_hsu_start_tx,
+ .stop_rx = serial_hsu_stop_rx,
+ .enable_ms = serial_hsu_enable_ms,
+ .break_ctl = serial_hsu_break_ctl,
+ .startup = serial_hsu_startup,
+ .shutdown = serial_hsu_shutdown,
+ .set_termios = serial_hsu_set_termios,
+ .pm = serial_hsu_pm,
+ .type = serial_hsu_type,
+ .release_port = serial_hsu_release_port,
+ .request_port = serial_hsu_request_port,
+ .config_port = serial_hsu_config_port,
+ .verify_port = serial_hsu_verify_port,
+};
+
+static struct uart_driver serial_hsu_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "MFD serial",
+ .dev_name = "ttyMFD",
+ .major = TTY_MAJOR,
+ .minor = 128,
+ .nr = 3,
+};
+
+#ifdef CONFIG_PM
+static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ void *priv = pci_get_drvdata(pdev);
+ struct uart_hsu_port *up;
+
+ /* Make sure this is not the internal dma controller */
+ if (priv && (pdev->device != 0x081E)) {
+ up = priv;
+ uart_suspend_port(&serial_hsu_reg, &up->port);
+ }
+
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int serial_hsu_resume(struct pci_dev *pdev)
+{
+ void *priv = pci_get_drvdata(pdev);
+ struct uart_hsu_port *up;
+ int ret;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "HSU: can't re-enable device, try to continue\n");
+
+ if (priv && (pdev->device != 0x081E)) {
+ up = priv;
+ uart_resume_port(&serial_hsu_reg, &up->port);
+ }
+ return 0;
+}
+#else
+#define serial_hsu_suspend NULL
+#define serial_hsu_resume NULL
+#endif
+
+/* temp global pointer before we settle down on using one or four PCI dev */
+static struct hsu_port *phsu;
+
+static int serial_hsu_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct uart_hsu_port *uport;
+ int index, ret;
+
+ printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n",
+ pdev->vendor, pdev->device);
+
+ switch (pdev->device) {
+ case 0x081B:
+ index = 0;
+ break;
+ case 0x081C:
+ index = 1;
+ break;
+ case 0x081D:
+ index = 2;
+ break;
+ case 0x081E:
+ /* internal DMA controller */
+ index = 3;
+ break;
+ default:
+ dev_err(&pdev->dev, "HSU: out of index!");
+ return -ENODEV;
+ }
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ if (index == 3) {
+ /* DMA controller */
+ ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu);
+ if (ret) {
+ dev_err(&pdev->dev, "can not get IRQ\n");
+ goto err_disable;
+ }
+ pci_set_drvdata(pdev, phsu);
+ } else {
+ /* UART port 0~2 */
+ uport = &phsu->port[index];
+ uport->port.irq = pdev->irq;
+ uport->port.dev = &pdev->dev;
+ uport->dev = &pdev->dev;
+
+ ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport);
+ if (ret) {
+ dev_err(&pdev->dev, "can not get IRQ\n");
+ goto err_disable;
+ }
+ uart_add_one_port(&serial_hsu_reg, &uport->port);
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+ if (index == 2) {
+ register_console(&serial_hsu_console);
+ uport->port.cons = &serial_hsu_console;
+ }
+#endif
+ pci_set_drvdata(pdev, uport);
+ }
+
+ return 0;
+
+err_disable:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void hsu_dma_rx_timeout(unsigned long data)
+{
+ struct hsu_dma_chan *chan = (void *)data;
+ struct uart_hsu_port *up = chan->uport;
+ struct hsu_dma_buffer *dbuf = &up->rxbuf;
+ int count = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+
+ if (!count) {
+ mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ);
+ goto exit;
+ }
+
+ hsu_dma_rx(up, 0);
+exit:
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void hsu_global_init(void)
+{
+ struct hsu_port *hsu;
+ struct uart_hsu_port *uport;
+ struct hsu_dma_chan *dchan;
+ int i, ret;
+
+ hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL);
+ if (!hsu)
+ return;
+
+ /* Get basic io resource and map it */
+ hsu->paddr = 0xffa28000;
+ hsu->iolen = 0x1000;
+
+ if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global")))
+ pr_warning("HSU: error in request mem region\n");
+
+ hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen);
+ if (!hsu->reg) {
+ pr_err("HSU: error in ioremap\n");
+ ret = -ENOMEM;
+ goto err_free_region;
+ }
+
+ /* Initialise the 3 UART ports */
+ uport = hsu->port;
+ for (i = 0; i < 3; i++) {
+ uport->port.type = PORT_MFD;
+ uport->port.iotype = UPIO_MEM;
+ uport->port.mapbase = (resource_size_t)hsu->paddr
+ + HSU_PORT_REG_OFFSET
+ + i * HSU_PORT_REG_LENGTH;
+ uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET
+ + i * HSU_PORT_REG_LENGTH;
+
+ sprintf(uport->name, "hsu_port%d", i);
+ uport->port.fifosize = 64;
+ uport->port.ops = &serial_hsu_pops;
+ uport->port.line = i;
+ uport->port.flags = UPF_IOREMAP;
+ /* set the scalable maxim support rate to 2746800 bps */
+ uport->port.uartclk = 115200 * 24 * 16;
+
+ uport->running = 0;
+ uport->txc = &hsu->chans[i * 2];
+ uport->rxc = &hsu->chans[i * 2 + 1];
+
+ serial_hsu_ports[i] = uport;
+ uport->index = i;
+ uport++;
+ }
+
+ /* Initialise 6 dma channels */
+ dchan = hsu->chans;
+ for (i = 0; i < 6; i++) {
+ dchan->id = i;
+ dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dchan->uport = &hsu->port[i/2];
+ dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET +
+ i * HSU_DMA_CHANS_REG_LENGTH;
+
+ /* Work around for RX */
+ if (dchan->dirt == DMA_FROM_DEVICE) {
+ init_timer(&dchan->rx_timer);
+ dchan->rx_timer.function = hsu_dma_rx_timeout;
+ dchan->rx_timer.data = (unsigned long)dchan;
+ }
+ dchan++;
+ }
+
+ phsu = hsu;
+
+ hsu_debugfs_init(hsu);
+ return;
+
+err_free_region:
+ release_mem_region(hsu->paddr, hsu->iolen);
+ kfree(hsu);
+ return;
+}
+
+static void serial_hsu_remove(struct pci_dev *pdev)
+{
+ struct hsu_port *hsu;
+ int i;
+
+ hsu = pci_get_drvdata(pdev);
+ if (!hsu)
+ return;
+
+ for (i = 0; i < 3; i++)
+ uart_remove_one_port(&serial_hsu_reg, &hsu->port[i].port);
+
+ pci_set_drvdata(pdev, NULL);
+ free_irq(hsu->irq, hsu);
+ pci_disable_device(pdev);
+}
+
+/* First 3 are UART ports, and the 4th is the DMA */
+static const struct pci_device_id pci_ids[] __devinitdata = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) },
+ {},
+};
+
+static struct pci_driver hsu_pci_driver = {
+ .name = "HSU serial",
+ .id_table = pci_ids,
+ .probe = serial_hsu_probe,
+ .remove = __devexit_p(serial_hsu_remove),
+ .suspend = serial_hsu_suspend,
+ .resume = serial_hsu_resume,
+};
+
+static int __init hsu_pci_init(void)
+{
+ int ret;
+
+ hsu_global_init();
+
+ ret = uart_register_driver(&serial_hsu_reg);
+ if (ret)
+ return ret;
+
+ return pci_register_driver(&hsu_pci_driver);
+}
+
+static void __exit hsu_pci_exit(void)
+{
+ pci_unregister_driver(&hsu_pci_driver);
+ uart_unregister_driver(&serial_hsu_reg);
+
+ hsu_debugfs_remove(phsu);
+
+ kfree(phsu);
+}
+
+module_init(hsu_pci_init);
+module_exit(hsu_pci_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:medfield-hsu");
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 1a88b36..8dedb26 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -1298,7 +1298,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = {
};
static int __devinit
-mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
+mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *match)
{
int idx = -1;
unsigned int uartclk;
@@ -1369,7 +1369,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
}
static int
-mpc52xx_uart_of_remove(struct of_device *op)
+mpc52xx_uart_of_remove(struct platform_device *op)
{
struct uart_port *port = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
@@ -1382,7 +1382,7 @@ mpc52xx_uart_of_remove(struct of_device *op)
#ifdef CONFIG_PM
static int
-mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state)
+mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
{
struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
@@ -1393,7 +1393,7 @@ mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state)
}
static int
-mpc52xx_uart_of_resume(struct of_device *op)
+mpc52xx_uart_of_resume(struct platform_device *op)
{
struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
diff --git a/drivers/serial/mrst_max3110.c b/drivers/serial/mrst_max3110.c
new file mode 100644
index 0000000..f6ad1ec
--- /dev/null
+++ b/drivers/serial/mrst_max3110.c
@@ -0,0 +1,844 @@
+/*
+ * max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown
+ *
+ * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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.
+ */
+
+/*
+ * Note:
+ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
+ * 1 word. If SPI master controller doesn't support sclk frequency change,
+ * then the char need be sent out one by one with some delay
+ *
+ * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE
+ * interrupt for a low speed UART device
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/dw_spi.h>
+
+#include "mrst_max3110.h"
+
+#define PR_FMT "mrst_max3110: "
+
+#define UART_TX_NEEDED 1
+#define CON_TX_NEEDED 2
+#define BIT_IRQ_PENDING 3
+
+struct uart_max3110 {
+ struct uart_port port;
+ struct spi_device *spi;
+ char *name;
+
+ wait_queue_head_t wq;
+ struct task_struct *main_thread;
+ struct task_struct *read_thread;
+ struct mutex thread_mutex;;
+
+ u32 baud;
+ u16 cur_conf;
+ u8 clock;
+ u8 parity, word_7bits;
+
+ unsigned long uart_flags;
+
+ /* console related */
+ struct circ_buf con_xmit;
+
+ /* irq related */
+ u16 irq;
+};
+
+/* global data structure, may need be removed */
+struct uart_max3110 *pmax;
+static inline void receive_char(struct uart_max3110 *max, u8 ch);
+static void receive_chars(struct uart_max3110 *max,
+ unsigned char *str, int len);
+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf);
+static void max3110_console_receive(struct uart_max3110 *max);
+
+int max3110_write_then_read(struct uart_max3110 *max,
+ const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast)
+{
+ struct spi_device *spi = max->spi;
+ struct spi_message message;
+ struct spi_transfer x;
+ int ret;
+
+ if (!txbuf || !rxbuf)
+ return -EINVAL;
+
+ spi_message_init(&message);
+ memset(&x, 0, sizeof x);
+ x.len = len;
+ x.tx_buf = txbuf;
+ x.rx_buf = rxbuf;
+ spi_message_add_tail(&x, &message);
+
+ if (always_fast)
+ x.speed_hz = 3125000;
+ else if (max->baud)
+ x.speed_hz = max->baud;
+
+ /* Do the i/o */
+ ret = spi_sync(spi, &message);
+ return ret;
+}
+
+/* Write a u16 to the device, and return one u16 read back */
+int max3110_out(struct uart_max3110 *max, const u16 out)
+{
+ u16 tmp;
+ int ret;
+
+ ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1);
+ if (ret)
+ return ret;
+
+ /* If some valid data is read back */
+ if (tmp & MAX3110_READ_DATA_AVAILABLE)
+ receive_char(max, (tmp & 0xff));
+
+ return ret;
+}
+
+#define MAX_READ_LEN 20
+/*
+ * This is usually used to read data from SPIC RX FIFO, which doesn't
+ * need any delay like flushing character out. It returns how many
+ * valide bytes are read back
+ */
+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf)
+{
+ u16 out[MAX_READ_LEN], in[MAX_READ_LEN];
+ u8 *pbuf, valid_str[MAX_READ_LEN];
+ int i, j, bytelen;
+
+ if (len > MAX_READ_LEN) {
+ pr_err(PR_FMT "read len %d is too large\n", len);
+ return 0;
+ }
+
+ bytelen = len * 2;
+ memset(out, 0, bytelen);
+ memset(in, 0, bytelen);
+
+ if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1))
+ return 0;
+
+ /* If caller don't provide a buffer, then handle received char */
+ pbuf = buf ? buf : valid_str;
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (in[i] & MAX3110_READ_DATA_AVAILABLE)
+ pbuf[j++] = (u8)(in[i] & 0xff);
+ }
+
+ if (j && (pbuf == valid_str))
+ receive_chars(max, valid_str, j);
+
+ return j;
+}
+
+static void serial_m3110_con_putchar(struct uart_port *port, int ch)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ struct circ_buf *xmit = &max->con_xmit;
+
+ if (uart_circ_chars_free(xmit)) {
+ xmit->buf[xmit->head] = (char)ch;
+ xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
+ }
+
+
+ if (!test_and_set_bit(CON_TX_NEEDED, &max->uart_flags))
+ wake_up_process(max->main_thread);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ * The console_lock must be held when we get here.
+ */
+static void serial_m3110_con_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ if (!pmax)
+ return;
+
+ uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
+}
+
+static int __init
+serial_m3110_con_setup(struct console *co, char *options)
+{
+ struct uart_max3110 *max = pmax;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ pr_info(PR_FMT "setting up console\n");
+
+ if (!max) {
+ pr_err(PR_FMT "pmax is NULL, return");
+ return -ENODEV;
+ }
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&max->port, co, baud, parity, bits, flow);
+}
+
+static struct tty_driver *serial_m3110_con_device(struct console *co,
+ int *index)
+{
+ struct uart_driver *p = co->data;
+ *index = co->index;
+ return p->tty_driver;
+}
+
+static struct uart_driver serial_m3110_reg;
+static struct console serial_m3110_console = {
+ .name = "ttyS",
+ .write = serial_m3110_con_write,
+ .device = serial_m3110_con_device,
+ .setup = serial_m3110_con_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &serial_m3110_reg,
+};
+
+#define MRST_CONSOLE (&serial_m3110_console)
+
+static unsigned int serial_m3110_tx_empty(struct uart_port *port)
+{
+ return 1;
+}
+
+static void serial_m3110_stop_tx(struct uart_port *port)
+{
+ return;
+}
+
+/* stop_rx will be called in spin_lock env */
+static void serial_m3110_stop_rx(struct uart_port *port)
+{
+ return;
+}
+
+#define WORDS_PER_XFER 128
+static inline void send_circ_buf(struct uart_max3110 *max,
+ struct circ_buf *xmit)
+{
+ int len, left = 0;
+ u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER];
+ u8 valid_str[WORDS_PER_XFER];
+ int i, j;
+
+ while (!uart_circ_empty(xmit)) {
+ left = uart_circ_chars_pending(xmit);
+ while (left) {
+ len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left;
+
+ memset(obuf, 0, len * 2);
+ memset(ibuf, 0, len * 2);
+ for (i = 0; i < len; i++) {
+ obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
+ xmit->tail = (xmit->tail + 1) &
+ (UART_XMIT_SIZE - 1);
+ }
+ max3110_write_then_read(max, (u8 *)obuf,
+ (u8 *)ibuf, len * 2, 0);
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE)
+ valid_str[j++] = (u8)(ibuf[i] & 0xff);
+ }
+
+ if (j)
+ receive_chars(max, valid_str, j);
+
+ max->port.icount.tx += len;
+ left -= len;
+ }
+ }
+}
+
+static void transmit_char(struct uart_max3110 *max)
+{
+ struct uart_port *port = &max->port;
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ return;
+
+ send_circ_buf(max, xmit);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ serial_m3110_stop_tx(port);
+}
+
+/* This will be called by uart_write() and tty_write, can't
+ * go to sleep */
+static void serial_m3110_start_tx(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+
+ if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
+ wake_up_process(max->main_thread);
+}
+
+static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len)
+{
+ struct uart_port *port = &max->port;
+ struct tty_struct *tty;
+ int usable;
+
+ /* If uart is not opened, just return */
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return; /* receive some char before the tty is opened */
+
+ while (len) {
+ usable = tty_buffer_request_room(tty, len);
+ if (usable) {
+ tty_insert_flip_string(tty, str, usable);
+ str += usable;
+ port->icount.rx += usable;
+ tty_flip_buffer_push(tty);
+ }
+ len -= usable;
+ }
+}
+
+static inline void receive_char(struct uart_max3110 *max, u8 ch)
+{
+ receive_chars(max, &ch, 1);
+}
+
+static void max3110_console_receive(struct uart_max3110 *max)
+{
+ int loop = 1, num, total = 0;
+ u8 recv_buf[512], *pbuf;
+
+ pbuf = recv_buf;
+ do {
+ num = max3110_read_multi(max, 8, pbuf);
+
+ if (num) {
+ loop = 10;
+ pbuf += num;
+ total += num;
+
+ if (total >= 500) {
+ receive_chars(max, recv_buf, total);
+ pbuf = recv_buf;
+ total = 0;
+ }
+ }
+ } while (--loop);
+
+ if (total)
+ receive_chars(max, recv_buf, total);
+}
+
+static int max3110_main_thread(void *_max)
+{
+ struct uart_max3110 *max = _max;
+ wait_queue_head_t *wq = &max->wq;
+ int ret = 0;
+ struct circ_buf *xmit = &max->con_xmit;
+
+ init_waitqueue_head(wq);
+ pr_info(PR_FMT "start main thread\n");
+
+ do {
+ wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop());
+
+ mutex_lock(&max->thread_mutex);
+
+ if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
+ max3110_console_receive(max);
+
+ /* first handle console output */
+ if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
+ send_circ_buf(max, xmit);
+
+ /* handle uart output */
+ if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
+ transmit_char(max);
+
+ mutex_unlock(&max->thread_mutex);
+
+ } while (!kthread_should_stop());
+
+ return ret;
+}
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
+{
+ struct uart_max3110 *max = dev_id;
+
+ /* max3110's irq is a falling edge, not level triggered,
+ * so no need to disable the irq */
+ if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
+ wake_up_process(max->main_thread);
+
+ return IRQ_HANDLED;
+}
+#else
+/* if don't use RX IRQ, then need a thread to polling read */
+static int max3110_read_thread(void *_max)
+{
+ struct uart_max3110 *max = _max;
+
+ pr_info(PR_FMT "start read thread\n");
+ do {
+ mutex_lock(&max->thread_mutex);
+ max3110_console_receive(max);
+ mutex_unlock(&max->thread_mutex);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 20);
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+#endif
+
+static int serial_m3110_startup(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ u16 config = 0;
+ int ret = 0;
+
+ if (port->line != 0)
+ pr_err(PR_FMT "uart port startup failed\n");
+
+ /* firstly disable all IRQ and config it to 115200, 8n1 */
+ config = WC_TAG | WC_FIFO_ENABLE
+ | WC_1_STOPBITS
+ | WC_8BIT_WORD
+ | WC_BAUD_DR2;
+ ret = max3110_out(max, config);
+
+ /* as we use thread to handle tx/rx, need set low latency */
+ port->state->port.tty->low_latency = 1;
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+ ret = request_irq(max->irq, serial_m3110_irq,
+ IRQ_TYPE_EDGE_FALLING, "max3110", max);
+ if (ret)
+ return ret;
+
+ /* enable RX IRQ only */
+ config |= WC_RXA_IRQ_ENABLE;
+ max3110_out(max, config);
+#else
+ /* if IRQ is disabled, start a read thread for input data */
+ max->read_thread =
+ kthread_run(max3110_read_thread, max, "max3110_read");
+#endif
+
+ max->cur_conf = config;
+ return 0;
+}
+
+static void serial_m3110_shutdown(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ u16 config;
+
+ if (max->read_thread) {
+ kthread_stop(max->read_thread);
+ max->read_thread = NULL;
+ }
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+ free_irq(max->irq, max);
+#endif
+
+ /* Disable interrupts from this port */
+ config = WC_TAG | WC_SW_SHDI;
+ max3110_out(max, config);
+}
+
+static void serial_m3110_release_port(struct uart_port *port)
+{
+}
+
+static int serial_m3110_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static void serial_m3110_config_port(struct uart_port *port, int flags)
+{
+ /* give it fake type */
+ port->type = PORT_PXA;
+}
+
+static int
+serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* we don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+
+static const char *serial_m3110_type(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ return max->name;
+}
+
+static void
+serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ unsigned char cval;
+ unsigned int baud, parity = 0;
+ int clk_div = -1;
+ u16 new_conf = max->cur_conf;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ new_conf |= WC_7BIT_WORD;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ new_conf |= WC_8BIT_WORD;
+ break;
+ }
+
+ baud = uart_get_baud_rate(port, termios, old, 0, 230400);
+
+ /* first calc the div for 1.8MHZ clock case */
+ switch (baud) {
+ case 300:
+ clk_div = WC_BAUD_DR384;
+ break;
+ case 600:
+ clk_div = WC_BAUD_DR192;
+ break;
+ case 1200:
+ clk_div = WC_BAUD_DR96;
+ break;
+ case 2400:
+ clk_div = WC_BAUD_DR48;
+ break;
+ case 4800:
+ clk_div = WC_BAUD_DR24;
+ break;
+ case 9600:
+ clk_div = WC_BAUD_DR12;
+ break;
+ case 19200:
+ clk_div = WC_BAUD_DR6;
+ break;
+ case 38400:
+ clk_div = WC_BAUD_DR3;
+ break;
+ case 57600:
+ clk_div = WC_BAUD_DR2;
+ break;
+ case 115200:
+ clk_div = WC_BAUD_DR1;
+ break;
+ case 230400:
+ if (max->clock & MAX3110_HIGH_CLK)
+ break;
+ default:
+ /* pick the previous baud rate */
+ baud = max->baud;
+ clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ }
+
+ if (max->clock & MAX3110_HIGH_CLK) {
+ clk_div += 1;
+ /* high clk version max3110 doesn't support B300 */
+ if (baud == 300)
+ baud = 600;
+ if (baud == 230400)
+ clk_div = WC_BAUD_DR1;
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ }
+
+ new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
+ if (termios->c_cflag & CSTOPB)
+ new_conf |= WC_2_STOPBITS;
+ else
+ new_conf &= ~WC_2_STOPBITS;
+
+ if (termios->c_cflag & PARENB) {
+ new_conf |= WC_PARITY_ENABLE;
+ parity |= UART_LCR_PARITY;
+ } else
+ new_conf &= ~WC_PARITY_ENABLE;
+
+ if (!(termios->c_cflag & PARODD))
+ parity |= UART_LCR_EPAR;
+ max->parity = parity;
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ new_conf |= WC_TAG;
+ if (new_conf != max->cur_conf) {
+ max3110_out(max, new_conf);
+ max->cur_conf = new_conf;
+ max->baud = baud;
+ }
+}
+
+/* don't handle hw handshaking */
+static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static void serial_m3110_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+}
+
+static void serial_m3110_enable_ms(struct uart_port *port)
+{
+}
+
+struct uart_ops serial_m3110_ops = {
+ .tx_empty = serial_m3110_tx_empty,
+ .set_mctrl = serial_m3110_set_mctrl,
+ .get_mctrl = serial_m3110_get_mctrl,
+ .stop_tx = serial_m3110_stop_tx,
+ .start_tx = serial_m3110_start_tx,
+ .stop_rx = serial_m3110_stop_rx,
+ .enable_ms = serial_m3110_enable_ms,
+ .break_ctl = serial_m3110_break_ctl,
+ .startup = serial_m3110_startup,
+ .shutdown = serial_m3110_shutdown,
+ .set_termios = serial_m3110_set_termios, /* must have */
+ .pm = serial_m3110_pm,
+ .type = serial_m3110_type,
+ .release_port = serial_m3110_release_port,
+ .request_port = serial_m3110_request_port,
+ .config_port = serial_m3110_config_port,
+ .verify_port = serial_m3110_verify_port,
+};
+
+static struct uart_driver serial_m3110_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "MRST serial",
+ .dev_name = "ttyS",
+ .major = TTY_MAJOR,
+ .minor = 64,
+ .nr = 1,
+ .cons = MRST_CONSOLE,
+};
+
+static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
+{
+ return 0;
+}
+
+static int serial_m3110_resume(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct dw_spi_chip spi0_uart = {
+ .poll_mode = 1,
+ .enable_dma = 0,
+ .type = SPI_FRF_SPI,
+};
+
+static int serial_m3110_probe(struct spi_device *spi)
+{
+ struct uart_max3110 *max;
+ int ret;
+ unsigned char *buffer;
+ u16 res;
+ max = kzalloc(sizeof(*max), GFP_KERNEL);
+ if (!max)
+ return -ENOMEM;
+
+ /* set spi info */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 16;
+ max->clock = MAX3110_HIGH_CLK;
+ spi->controller_data = &spi0_uart;
+
+ spi_setup(spi);
+
+ max->port.type = PORT_PXA; /* need apply for a max3110 type */
+ max->port.fifosize = 2; /* only have 16b buffer */
+ max->port.ops = &serial_m3110_ops;
+ max->port.line = 0;
+ max->port.dev = &spi->dev;
+ max->port.uartclk = 115200;
+
+ max->spi = spi;
+ max->name = spi->modalias; /* use spi name as the name */
+ max->irq = (u16)spi->irq;
+
+ mutex_init(&max->thread_mutex);
+
+ max->word_7bits = 0;
+ max->parity = 0;
+ max->baud = 0;
+
+ max->cur_conf = 0;
+ max->uart_flags = 0;
+
+ /* Check if reading configuration register returns something sane */
+
+ res = RC_TAG;
+ ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
+ if (ret < 0 || res == 0 || res == 0xffff) {
+ printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)",
+ res);
+ ret = -ENODEV;
+ goto err_get_page;
+ }
+ buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto err_get_page;
+ }
+ max->con_xmit.buf = (unsigned char *)buffer;
+ max->con_xmit.head = max->con_xmit.tail = 0;
+
+ max->main_thread = kthread_run(max3110_main_thread,
+ max, "max3110_main");
+ if (IS_ERR(max->main_thread)) {
+ ret = PTR_ERR(max->main_thread);
+ goto err_kthread;
+ }
+
+ pmax = max;
+ /* give membase a psudo value to pass serial_core's check */
+ max->port.membase = (void *)0xff110000;
+ uart_add_one_port(&serial_m3110_reg, &max->port);
+
+ return 0;
+
+err_kthread:
+ free_page((unsigned long)buffer);
+err_get_page:
+ pmax = NULL;
+ kfree(max);
+ return ret;
+}
+
+static int max3110_remove(struct spi_device *dev)
+{
+ struct uart_max3110 *max = pmax;
+
+ if (!pmax)
+ return 0;
+
+ pmax = NULL;
+ uart_remove_one_port(&serial_m3110_reg, &max->port);
+
+ free_page((unsigned long)max->con_xmit.buf);
+
+ if (max->main_thread)
+ kthread_stop(max->main_thread);
+
+ kfree(max);
+ return 0;
+}
+
+static struct spi_driver uart_max3110_driver = {
+ .driver = {
+ .name = "spi_max3111",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = serial_m3110_probe,
+ .remove = __devexit_p(max3110_remove),
+ .suspend = serial_m3110_suspend,
+ .resume = serial_m3110_resume,
+};
+
+
+int __init serial_m3110_init(void)
+{
+ int ret = 0;
+
+ ret = uart_register_driver(&serial_m3110_reg);
+ if (ret)
+ return ret;
+
+ ret = spi_register_driver(&uart_max3110_driver);
+ if (ret)
+ uart_unregister_driver(&serial_m3110_reg);
+
+ return ret;
+}
+
+void __exit serial_m3110_exit(void)
+{
+ spi_unregister_driver(&uart_max3110_driver);
+ uart_unregister_driver(&serial_m3110_reg);
+}
+
+module_init(serial_m3110_init);
+module_exit(serial_m3110_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("max3110-uart");
diff --git a/drivers/serial/mrst_max3110.h b/drivers/serial/mrst_max3110.h
new file mode 100644
index 0000000..363478a
--- /dev/null
+++ b/drivers/serial/mrst_max3110.h
@@ -0,0 +1,59 @@
+#ifndef _MRST_MAX3110_H
+#define _MRST_MAX3110_H
+
+#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */
+#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */
+
+/* status bits for all 4 MAX3110 operate modes */
+#define MAX3110_READ_DATA_AVAILABLE (1 << 15)
+#define MAX3110_WRITE_BUF_EMPTY (1 << 14)
+
+#define WC_TAG (3 << 14)
+#define RC_TAG (1 << 14)
+#define WD_TAG (2 << 14)
+#define RD_TAG (0 << 14)
+
+/* bits def for write configuration */
+#define WC_FIFO_ENABLE_MASK (1 << 13)
+#define WC_FIFO_ENABLE (0 << 13)
+
+#define WC_SW_SHDI (1 << 12)
+
+#define WC_IRQ_MASK (0xF << 8)
+#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */
+#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */
+#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
+#define WC_REC_ACT_IRQ_ENABLE (1 << 8)
+
+#define WC_IRDA_ENABLE (1 << 7)
+
+#define WC_STOPBITS_MASK (1 << 6)
+#define WC_2_STOPBITS (1 << 6)
+#define WC_1_STOPBITS (0 << 6)
+
+#define WC_PARITY_ENABLE_MASK (1 << 5)
+#define WC_PARITY_ENABLE (1 << 5)
+
+#define WC_WORDLEN_MASK (1 << 4)
+#define WC_7BIT_WORD (1 << 4)
+#define WC_8BIT_WORD (0 << 4)
+
+#define WC_BAUD_DIV_MASK (0xF)
+#define WC_BAUD_DR1 (0x0)
+#define WC_BAUD_DR2 (0x1)
+#define WC_BAUD_DR4 (0x2)
+#define WC_BAUD_DR8 (0x3)
+#define WC_BAUD_DR16 (0x4)
+#define WC_BAUD_DR32 (0x5)
+#define WC_BAUD_DR64 (0x6)
+#define WC_BAUD_DR128 (0x7)
+#define WC_BAUD_DR3 (0x8)
+#define WC_BAUD_DR6 (0x9)
+#define WC_BAUD_DR12 (0xA)
+#define WC_BAUD_DR24 (0xB)
+#define WC_BAUD_DR48 (0xC)
+#define WC_BAUD_DR96 (0xD)
+#define WC_BAUD_DR192 (0xE)
+#define WC_BAUD_DR384 (0xF)
+
+#endif
diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c
index e65b0d9..de17367 100644
--- a/drivers/serial/nwpserial.c
+++ b/drivers/serial/nwpserial.c
@@ -344,7 +344,7 @@ int nwpserial_register_port(struct uart_port *port)
mutex_lock(&nwpserial_mutex);
- dn = to_of_device(port->dev)->dev.of_node;
+ dn = port->dev->of_node;
if (dn == NULL)
goto out;
diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c
index a48d908..659a695 100644
--- a/drivers/serial/of_serial.c
+++ b/drivers/serial/of_serial.c
@@ -27,7 +27,7 @@ struct of_serial_info {
/*
* Fill a struct uart_port for a given device node
*/
-static int __devinit of_platform_serial_setup(struct of_device *ofdev,
+static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
int type, struct uart_port *port)
{
struct resource resource;
@@ -80,7 +80,7 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev,
/*
* Try to register a serial port
*/
-static int __devinit of_platform_serial_probe(struct of_device *ofdev,
+static int __devinit of_platform_serial_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct of_serial_info *info;
@@ -134,7 +134,7 @@ out:
/*
* Release a line
*/
-static int of_platform_serial_remove(struct of_device *ofdev)
+static int of_platform_serial_remove(struct platform_device *ofdev)
{
struct of_serial_info *info = dev_get_drvdata(&ofdev->dev);
switch (info->type) {
diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c
index 4a789e5..6ebccd7 100644
--- a/drivers/serial/s5pv210.c
+++ b/drivers/serial/s5pv210.c
@@ -28,8 +28,12 @@
static int s5pv210_serial_setsource(struct uart_port *port,
struct s3c24xx_uart_clksrc *clk)
{
+ struct s3c2410_uartcfg *cfg = port->dev->platform_data;
unsigned long ucon = rd_regl(port, S3C2410_UCON);
+ if ((cfg->clocks_size) == 1)
+ return 0;
+
if (strcmp(clk->name, "pclk") == 0)
ucon &= ~S5PV210_UCON_CLKMASK;
else if (strcmp(clk->name, "uclk1") == 0)
@@ -47,10 +51,14 @@ static int s5pv210_serial_setsource(struct uart_port *port,
static int s5pv210_serial_getsource(struct uart_port *port,
struct s3c24xx_uart_clksrc *clk)
{
+ struct s3c2410_uartcfg *cfg = port->dev->platform_data;
u32 ucon = rd_regl(port, S3C2410_UCON);
clk->divisor = 1;
+ if ((cfg->clocks_size) == 1)
+ return 0;
+
switch (ucon & S5PV210_UCON_CLKMASK) {
case S5PV210_UCON_PCLK:
clk->name = "pclk";
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index a9d6c56..b1156ba 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -705,8 +705,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
if (ourport->info->has_divslot) {
unsigned int div = ourport->baudclk_rate / baud;
- udivslot = udivslot_table[div & 15];
- dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ if (cfg->has_fracval) {
+ udivslot = (div & 15);
+ dbg("fracval = %04x\n", udivslot);
+ } else {
+ udivslot = udivslot_table[div & 15];
+ dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ }
}
switch (termios->c_cflag & CSIZE) {
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 7f28307..cd85112 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -58,9 +58,9 @@ static struct lock_class_key port_lock_key;
#define uart_console(port) (0)
#endif
-static void uart_change_speed(struct uart_state *state,
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
struct ktermios *old_termios);
-static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
+static void __uart_wait_until_sent(struct uart_port *port, int timeout);
static void uart_change_pm(struct uart_state *state, int pm_state);
/*
@@ -137,7 +137,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
* Startup the port. This will be called once per open. All calls
* will be serialised by the per-port mutex.
*/
-static int uart_startup(struct uart_state *state, int init_hw)
+static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
@@ -152,7 +152,7 @@ static int uart_startup(struct uart_state *state, int init_hw)
* once we have successfully opened the port. Also set
* up the tty->alt_speed kludge
*/
- set_bit(TTY_IO_ERROR, &port->tty->flags);
+ set_bit(TTY_IO_ERROR, &tty->flags);
if (uport->type == PORT_UNKNOWN)
return 0;
@@ -177,26 +177,26 @@ static int uart_startup(struct uart_state *state, int init_hw)
/*
* Initialise the hardware port settings.
*/
- uart_change_speed(state, NULL);
+ uart_change_speed(tty, state, NULL);
/*
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
- if (port->tty->termios->c_cflag & CBAUD)
+ if (tty->termios->c_cflag & CBAUD)
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
}
if (port->flags & ASYNC_CTS_FLOW) {
spin_lock_irq(&uport->lock);
if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
- port->tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
spin_unlock_irq(&uport->lock);
}
set_bit(ASYNCB_INITIALIZED, &port->flags);
- clear_bit(TTY_IO_ERROR, &port->tty->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
}
if (retval && capable(CAP_SYS_ADMIN))
@@ -210,11 +210,10 @@ static int uart_startup(struct uart_state *state, int init_hw)
* DTR is dropped if the hangup on close termio flag is on. Calls to
* uart_shutdown are serialised by the per-port semaphore.
*/
-static void uart_shutdown(struct uart_state *state)
+static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
- struct tty_struct *tty = port->tty;
/*
* Set the TTY IO error marker
@@ -430,11 +429,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud)
EXPORT_SYMBOL(uart_get_divisor);
/* FIXME: Consistent locking policy */
-static void
-uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
+ struct ktermios *old_termios)
{
struct tty_port *port = &state->port;
- struct tty_struct *tty = port->tty;
struct uart_port *uport = state->uart_port;
struct ktermios *termios;
@@ -463,8 +461,8 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
uport->ops->set_termios(uport, termios, old_termios);
}
-static inline int
-__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
+static inline int __uart_put_char(struct uart_port *port,
+ struct circ_buf *circ, unsigned char c)
{
unsigned long flags;
int ret = 0;
@@ -494,8 +492,8 @@ static void uart_flush_chars(struct tty_struct *tty)
uart_start(tty);
}
-static int
-uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
+static int uart_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port;
@@ -675,7 +673,7 @@ static int uart_get_info(struct uart_state *state,
return 0;
}
-static int uart_set_info(struct uart_state *state,
+static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
struct serial_struct __user *newinfo)
{
struct serial_struct new_serial;
@@ -770,7 +768,7 @@ static int uart_set_info(struct uart_state *state,
* We need to shutdown the serial port at the old
* port/type/irq combination.
*/
- uart_shutdown(state);
+ uart_shutdown(tty, state);
}
if (change_port) {
@@ -869,25 +867,27 @@ static int uart_set_info(struct uart_state *state,
"is deprecated.\n", current->comm,
tty_name(port->tty, buf));
}
- uart_change_speed(state, NULL);
+ uart_change_speed(tty, state, NULL);
}
} else
- retval = uart_startup(state, 1);
+ retval = uart_startup(tty, state, 1);
exit:
mutex_unlock(&port->mutex);
return retval;
}
-
-/*
- * uart_get_lsr_info - get line status register info.
- * Note: uart_ioctl protects us against hangups.
+/**
+ * uart_get_lsr_info - get line status register info
+ * @tty: tty associated with the UART
+ * @state: UART being queried
+ * @value: returned modem value
+ *
+ * Note: uart_ioctl protects us against hangups.
*/
-static int uart_get_lsr_info(struct uart_state *state,
- unsigned int __user *value)
+static int uart_get_lsr_info(struct tty_struct *tty,
+ struct uart_state *state, unsigned int __user *value)
{
struct uart_port *uport = state->uart_port;
- struct tty_port *port = &state->port;
unsigned int result;
result = uport->ops->tx_empty(uport);
@@ -900,7 +900,7 @@ static int uart_get_lsr_info(struct uart_state *state,
*/
if (uport->x_char ||
((uart_circ_chars_pending(&state->xmit) > 0) &&
- !port->tty->stopped && !port->tty->hw_stopped))
+ !tty->stopped && !tty->hw_stopped))
result &= ~TIOCSER_TEMT;
return put_user(result, value);
@@ -961,7 +961,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state)
return 0;
}
-static int uart_do_autoconfig(struct uart_state *state)
+static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
@@ -980,7 +980,7 @@ static int uart_do_autoconfig(struct uart_state *state)
ret = -EBUSY;
if (tty_port_users(port) == 1) {
- uart_shutdown(state);
+ uart_shutdown(tty, state);
/*
* If we already have a port type configured,
@@ -999,7 +999,7 @@ static int uart_do_autoconfig(struct uart_state *state)
*/
uport->ops->config_port(uport, flags);
- ret = uart_startup(state, 1);
+ ret = uart_startup(tty, state, 1);
}
mutex_unlock(&port->mutex);
return ret;
@@ -1122,11 +1122,11 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
break;
case TIOCSSERIAL:
- ret = uart_set_info(state, uarg);
+ ret = uart_set_info(tty, state, uarg);
break;
case TIOCSERCONFIG:
- ret = uart_do_autoconfig(state);
+ ret = uart_do_autoconfig(tty, state);
break;
case TIOCSERGWILD: /* obsolete */
@@ -1172,7 +1172,7 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
*/
switch (cmd) {
case TIOCSERGETLSR: /* Get line status register */
- ret = uart_get_lsr_info(state, uarg);
+ ret = uart_get_lsr_info(tty, state, uarg);
break;
default: {
@@ -1194,7 +1194,7 @@ static void uart_set_ldisc(struct tty_struct *tty)
struct uart_port *uport = state->uart_port;
if (uport->ops->set_ldisc)
- uport->ops->set_ldisc(uport);
+ uport->ops->set_ldisc(uport, tty->termios->c_line);
}
static void uart_set_termios(struct tty_struct *tty,
@@ -1219,7 +1219,7 @@ static void uart_set_termios(struct tty_struct *tty,
return;
}
- uart_change_speed(state, old_termios);
+ uart_change_speed(tty, state, old_termios);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
@@ -1272,8 +1272,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
struct uart_state *state = tty->driver_data;
struct tty_port *port;
struct uart_port *uport;
+ unsigned long flags;
- BUG_ON(!kernel_locked());
+ BUG_ON(!tty_locked());
if (!state)
return;
@@ -1284,9 +1285,12 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
pr_debug("uart_close(%d) called\n", uport->line);
mutex_lock(&port->mutex);
+ spin_lock_irqsave(&port->lock, flags);
- if (tty_hung_up_p(filp))
+ if (tty_hung_up_p(filp)) {
+ spin_unlock_irqrestore(&port->lock, flags);
goto done;
+ }
if ((tty->count == 1) && (port->count != 1)) {
/*
@@ -1305,8 +1309,10 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
tty->name, port->count);
port->count = 0;
}
- if (port->count)
+ if (port->count) {
+ spin_unlock_irqrestore(&port->lock, flags);
goto done;
+ }
/*
* Now we wait for the transmit buffer to clear; and we notify
@@ -1314,9 +1320,18 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
* setting tty->closing.
*/
tty->closing = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
- if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait));
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+ /*
+ * hack: open-coded tty_wait_until_sent to avoid
+ * recursive tty_lock
+ */
+ long timeout = msecs_to_jiffies(port->closing_wait);
+ if (wait_event_interruptible_timeout(tty->write_wait,
+ !tty_chars_in_buffer(tty), timeout) >= 0)
+ __uart_wait_until_sent(uport, timeout);
+ }
/*
* At this point, we stop accepting input. To do this, we
@@ -1332,45 +1347,47 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
- uart_wait_until_sent(tty, uport->timeout);
+ __uart_wait_until_sent(uport, uport->timeout);
}
- uart_shutdown(state);
+ uart_shutdown(tty, state);
uart_flush_buffer(tty);
tty_ldisc_flush(tty);
- tty->closing = 0;
tty_port_tty_set(port, NULL);
+ spin_lock_irqsave(&port->lock, flags);
+ tty->closing = 0;
if (port->blocked_open) {
+ spin_unlock_irqrestore(&port->lock, flags);
if (port->close_delay)
msleep_interruptible(port->close_delay);
+ spin_lock_irqsave(&port->lock, flags);
} else if (!uart_console(uport)) {
+ spin_unlock_irqrestore(&port->lock, flags);
uart_change_pm(state, 3);
+ spin_lock_irqsave(&port->lock, flags);
}
/*
* Wake up anyone trying to open this port.
*/
clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+ spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&port->open_wait);
done:
mutex_unlock(&port->mutex);
}
-static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+static void __uart_wait_until_sent(struct uart_port *port, int timeout)
{
- struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
unsigned long char_time, expire;
if (port->type == PORT_UNKNOWN || port->fifosize == 0)
return;
- lock_kernel();
-
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
@@ -1416,7 +1433,16 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
break;
}
set_current_state(TASK_RUNNING); /* might not be needed */
- unlock_kernel();
+}
+
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct uart_state *state = tty->driver_data;
+ struct uart_port *port = state->uart_port;
+
+ tty_lock();
+ __uart_wait_until_sent(port, timeout);
+ tty_unlock();
}
/*
@@ -1429,16 +1455,19 @@ static void uart_hangup(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
+ unsigned long flags;
- BUG_ON(!kernel_locked());
+ BUG_ON(!tty_locked());
pr_debug("uart_hangup(%d)\n", state->uart_port->line);
mutex_lock(&port->mutex);
if (port->flags & ASYNC_NORMAL_ACTIVE) {
uart_flush_buffer(tty);
- uart_shutdown(state);
+ uart_shutdown(tty, state);
+ spin_lock_irqsave(&port->lock, flags);
port->count = 0;
clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+ spin_unlock_irqrestore(&port->lock, flags);
tty_port_tty_set(port, NULL);
wake_up_interruptible(&port->open_wait);
wake_up_interruptible(&port->delta_msr_wait);
@@ -1446,15 +1475,19 @@ static void uart_hangup(struct tty_struct *tty)
mutex_unlock(&port->mutex);
}
-/*
- * Copy across the serial console cflag setting into the termios settings
- * for the initial open of the port. This allows continuity between the
- * kernel settings, and the settings init adopts when it opens the port
- * for the first time.
+/**
+ * uart_update_termios - update the terminal hw settings
+ * @tty: tty associated with UART
+ * @state: UART to update
+ *
+ * Copy across the serial console cflag setting into the termios settings
+ * for the initial open of the port. This allows continuity between the
+ * kernel settings, and the settings init adopts when it opens the port
+ * for the first time.
*/
-static void uart_update_termios(struct uart_state *state)
+static void uart_update_termios(struct tty_struct *tty,
+ struct uart_state *state)
{
- struct tty_struct *tty = state->port.tty;
struct uart_port *port = state->uart_port;
if (uart_console(port) && port->cons->cflag) {
@@ -1471,7 +1504,7 @@ static void uart_update_termios(struct uart_state *state)
/*
* Make termios settings take effect.
*/
- uart_change_speed(state, NULL);
+ uart_change_speed(tty, state, NULL);
/*
* And finally enable the RTS and DTR signals.
@@ -1481,90 +1514,37 @@ static void uart_update_termios(struct uart_state *state)
}
}
-/*
- * Block the open until the port is ready. We must be called with
- * the per-port semaphore held.
- */
-static int
-uart_block_til_ready(struct file *filp, struct uart_state *state)
+static int uart_carrier_raised(struct tty_port *port)
{
- DECLARE_WAITQUEUE(wait, current);
+ struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport = state->uart_port;
- struct tty_port *port = &state->port;
- unsigned int mctrl;
-
- port->blocked_open++;
- port->count--;
-
- add_wait_queue(&port->open_wait, &wait);
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- /*
- * If we have been hung up, tell userspace/restart open.
- */
- if (tty_hung_up_p(filp) || port->tty == NULL)
- break;
-
- /*
- * If the port has been closed, tell userspace/restart open.
- */
- if (!(port->flags & ASYNC_INITIALIZED))
- break;
+ int mctrl;
+ spin_lock_irq(&uport->lock);
+ uport->ops->enable_ms(uport);
+ mctrl = uport->ops->get_mctrl(uport);
+ spin_unlock_irq(&uport->lock);
+ if (mctrl & TIOCM_CAR)
+ return 1;
+ return 0;
+}
- /*
- * If non-blocking mode is set, or CLOCAL mode is set,
- * we don't want to wait for the modem status lines to
- * indicate that the port is ready.
- *
- * Also, if the port is not enabled/configured, we want
- * to allow the open to succeed here. Note that we will
- * have set TTY_IO_ERROR for a non-existant port.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (port->tty->termios->c_cflag & CLOCAL) ||
- (port->tty->flags & (1 << TTY_IO_ERROR)))
- break;
+static void uart_dtr_rts(struct tty_port *port, int onoff)
+{
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport = state->uart_port;
- /*
- * Set DTR to allow modem to know we're waiting. Do
- * not set RTS here - we want to make sure we catch
- * the data from the modem.
- */
- if (port->tty->termios->c_cflag & CBAUD)
- uart_set_mctrl(uport, TIOCM_DTR);
+ if (onoff) {
+ uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
/*
- * and wait for the carrier to indicate that the
- * modem is ready for us.
+ * If this is the first open to succeed,
+ * adjust things to suit.
*/
- spin_lock_irq(&uport->lock);
- uport->ops->enable_ms(uport);
- mctrl = uport->ops->get_mctrl(uport);
- spin_unlock_irq(&uport->lock);
- if (mctrl & TIOCM_CAR)
- break;
-
- mutex_unlock(&port->mutex);
- schedule();
- mutex_lock(&port->mutex);
-
- if (signal_pending(current))
- break;
+ if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags))
+ uart_update_termios(port->tty, state);
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->open_wait, &wait);
-
- port->count++;
- port->blocked_open--;
-
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- if (!port->tty || tty_hung_up_p(filp))
- return -EAGAIN;
-
- return 0;
+ else
+ uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
}
static struct uart_state *uart_get(struct uart_driver *drv, int line)
@@ -1611,7 +1591,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
struct tty_port *port;
int retval, line = tty->index;
- BUG_ON(!kernel_locked());
+ BUG_ON(!tty_locked());
pr_debug("uart_open(%d) called\n", line);
/*
@@ -1668,23 +1648,14 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
/*
* Start up the serial port.
*/
- retval = uart_startup(state, 0);
+ retval = uart_startup(tty, state, 0);
/*
* If we succeeded, wait until the port is ready.
*/
- if (retval == 0)
- retval = uart_block_til_ready(filp, state);
mutex_unlock(&port->mutex);
-
- /*
- * If this is the first open to succeed, adjust things to suit.
- */
- if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
- set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
-
- uart_update_termios(state);
- }
+ if (retval == 0)
+ retval = tty_port_block_til_ready(port, tty, filp);
fail:
return retval;
@@ -2010,9 +1981,13 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
struct tty_port *port = &state->port;
struct device *tty_dev;
struct uart_match match = {uport, drv};
+ struct tty_struct *tty;
mutex_lock(&port->mutex);
+ /* Must be inside the mutex lock until we convert to tty_port */
+ tty = port->tty;
+
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (device_may_wakeup(tty_dev)) {
enable_irq_wake(uport->irq);
@@ -2105,9 +2080,12 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
ops->set_mctrl(uport, 0);
spin_unlock_irq(&uport->lock);
if (console_suspend_enabled || !uart_console(uport)) {
+ /* Protected by port mutex for now */
+ struct tty_struct *tty = port->tty;
ret = ops->startup(uport);
if (ret == 0) {
- uart_change_speed(state, NULL);
+ if (tty)
+ uart_change_speed(tty, state, NULL);
spin_lock_irq(&uport->lock);
ops->set_mctrl(uport, uport->mctrl);
ops->start_tx(uport);
@@ -2119,7 +2097,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
* Clear the "initialized" flag so we won't try
* to call the low level drivers shutdown method.
*/
- uart_shutdown(state);
+ uart_shutdown(tty, state);
}
}
@@ -2312,6 +2290,11 @@ static const struct tty_operations uart_ops = {
#endif
};
+static const struct tty_port_operations uart_port_ops = {
+ .carrier_raised = uart_carrier_raised,
+ .dtr_rts = uart_dtr_rts,
+};
+
/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
@@ -2368,6 +2351,7 @@ int uart_register_driver(struct uart_driver *drv)
struct tty_port *port = &state->port;
tty_port_init(port);
+ port->ops = &uart_port_ops;
port->close_delay = 500; /* .5 seconds */
port->closing_wait = 30000; /* 30 seconds */
tasklet_init(&state->tlet, uart_tasklet_action,
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 5f90fcd..c291b3a 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -346,6 +346,27 @@ static int scif_rxfill(struct uart_port *port)
return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
}
}
+#elif defined(CONFIG_ARCH_SH7372)
+static int scif_txfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) >> 8;
+ else
+ return sci_in(port, SCTFDR);
+}
+
+static int scif_txroom(struct uart_port *port)
+{
+ return port->fifosize - scif_txfill(port);
+}
+
+static int scif_rxfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
+ else
+ return sci_in(port, SCRFDR);
+}
#else
static int scif_txfill(struct uart_port *port)
{
@@ -683,7 +704,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
u16 ssr = sci_in(port, SCxSR);
/* Disable future Rx interrupts */
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
disable_irq_nosync(irq);
scr |= 0x4000;
} else {
@@ -928,7 +949,7 @@ static void sci_dma_tx_complete(void *arg)
if (!uart_circ_empty(xmit)) {
schedule_work(&s->work_tx);
- } else if (port->type == PORT_SCIFA) {
+ } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE);
}
@@ -1184,7 +1205,7 @@ static void sci_start_tx(struct uart_port *port)
unsigned short ctrl;
#ifdef CONFIG_SERIAL_SH_SCI_DMA
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 new, scr = sci_in(port, SCSCR);
if (s->chan_tx)
new = scr | 0x8000;
@@ -1197,7 +1218,7 @@ static void sci_start_tx(struct uart_port *port)
s->cookie_tx < 0)
schedule_work(&s->work_tx);
#endif
- if (!s->chan_tx || port->type == PORT_SCIFA) {
+ if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE);
@@ -1210,7 +1231,7 @@ static void sci_stop_tx(struct uart_port *port)
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x8000;
ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
@@ -1222,7 +1243,7 @@ static void sci_start_rx(struct uart_port *port)
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl |= sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
sci_out(port, SCSCR, ctrl);
}
@@ -1233,7 +1254,7 @@ static void sci_stop_rx(struct uart_port *port)
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl);
@@ -1271,7 +1292,7 @@ static void rx_timer_fn(unsigned long arg)
struct uart_port *port = &s->port;
u16 scr = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
scr &= ~0x4000;
enable_irq(s->irqs[1]);
}
@@ -1524,6 +1545,8 @@ static const char *sci_type(struct uart_port *port)
return "scif";
case PORT_SCIFA:
return "scifa";
+ case PORT_SCIFB:
+ return "scifb";
}
return NULL;
@@ -1612,6 +1635,9 @@ static int __devinit sci_init_single(struct platform_device *dev,
port->line = index;
switch (p->type) {
+ case PORT_SCIFB:
+ port->fifosize = 256;
+ break;
case PORT_SCIFA:
port->fifosize = 64;
break;
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index f70c49f..9b52f77 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -322,7 +322,7 @@
#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\
static inline unsigned int sci_##name##_in(struct uart_port *port) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_IN(scif_size, scif_offset) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_IN(sci_size, sci_offset); \
@@ -330,7 +330,7 @@
} \
static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_OUT(scif_size, scif_offset, value) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_OUT(sci_size, sci_offset, value); \
@@ -384,8 +384,12 @@
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
+#define SCIF_FNS(name, scif_offset, scif_size) \
+ CPU_SCIF_FNS(name, scif_offset, scif_size)
+#elif defined(CONFIG_ARCH_SH7372)
+#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \
+ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size)
#define SCIF_FNS(name, scif_offset, scif_size) \
CPU_SCIF_FNS(name, scif_offset, scif_size)
#else
@@ -422,8 +426,7 @@
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
SCIF_FNS(SCSMR, 0x00, 16)
SCIF_FNS(SCBRR, 0x04, 8)
@@ -436,6 +439,20 @@ SCIF_FNS(SCFDR, 0x1c, 16)
SCIF_FNS(SCxTDR, 0x20, 8)
SCIF_FNS(SCxRDR, 0x24, 8)
SCIF_FNS(SCLSR, 0x00, 0)
+#elif defined(CONFIG_ARCH_SH7372)
+SCIF_FNS(SCSMR, 0x00, 16)
+SCIF_FNS(SCBRR, 0x04, 8)
+SCIF_FNS(SCSCR, 0x08, 16)
+SCIF_FNS(SCTDSR, 0x0c, 16)
+SCIF_FNS(SCFER, 0x10, 16)
+SCIF_FNS(SCxSR, 0x14, 16)
+SCIF_FNS(SCFCR, 0x18, 16)
+SCIF_FNS(SCFDR, 0x1c, 16)
+SCIF_FNS(SCTFDR, 0x38, 16)
+SCIF_FNS(SCRFDR, 0x3c, 16)
+SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8)
+SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8)
+SCIF_FNS(SCLSR, 0x00, 0)
#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
defined(CONFIG_CPU_SUBTYPE_SH7724)
SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16)
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c
index a779e22..c901486 100644
--- a/drivers/serial/sunhv.c
+++ b/drivers/serial/sunhv.c
@@ -519,7 +519,7 @@ static struct console sunhv_console = {
.data = &sunhv_reg,
};
-static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit hv_probe(struct platform_device *op, const struct of_device_id *match)
{
struct uart_port *port;
unsigned long minor;
@@ -598,7 +598,7 @@ out_free_port:
return err;
}
-static int __devexit hv_remove(struct of_device *dev)
+static int __devexit hv_remove(struct platform_device *dev)
{
struct uart_port *port = dev_get_drvdata(&dev->dev);
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c
index 9845fb1..5b246b1 100644
--- a/drivers/serial/sunsab.c
+++ b/drivers/serial/sunsab.c
@@ -883,7 +883,7 @@ static int sunsab_console_setup(struct console *con, char *options)
printk("Console: ttyS%d (SAB82532)\n",
(sunsab_reg.minor - 64) + con->index);
- sunserial_console_termios(con, to_of_device(up->port.dev)->dev.of_node);
+ sunserial_console_termios(con, up->port.dev->of_node);
switch (con->cflag & CBAUD) {
case B150: baud = 150; break;
@@ -954,7 +954,7 @@ static inline struct console *SUNSAB_CONSOLE(void)
#endif
static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
- struct of_device *op,
+ struct platform_device *op,
unsigned long offset,
int line)
{
@@ -1006,7 +1006,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
return 0;
}
-static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit sab_probe(struct platform_device *op, const struct of_device_id *match)
{
static int inst;
struct uart_sunsab_port *up;
@@ -1062,7 +1062,7 @@ out:
return err;
}
-static int __devexit sab_remove(struct of_device *op)
+static int __devexit sab_remove(struct platform_device *op)
{
struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
index 3cdf748..551ebfe 100644
--- a/drivers/serial/sunsu.c
+++ b/drivers/serial/sunsu.c
@@ -1200,7 +1200,7 @@ static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up)
return -ENODEV;
printk("%s: %s port at %llx, irq %u\n",
- to_of_device(up->port.dev)->dev.of_node->full_name,
+ up->port.dev->of_node->full_name,
(up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse",
(unsigned long long) up->port.mapbase,
up->port.irq);
@@ -1352,7 +1352,7 @@ static int __init sunsu_console_setup(struct console *co, char *options)
spin_lock_init(&port->lock);
/* Get firmware console settings. */
- sunserial_console_termios(co, to_of_device(port->dev)->dev.of_node);
+ sunserial_console_termios(co, port->dev->of_node);
memset(&termios, 0, sizeof(struct ktermios));
termios.c_cflag = co->cflag;
@@ -1406,7 +1406,7 @@ static enum su_type __devinit su_get_type(struct device_node *dp)
return SU_PORT_PORT;
}
-static int __devinit su_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit su_probe(struct platform_device *op, const struct of_device_id *match)
{
static int inst;
struct device_node *dp = op->dev.of_node;
@@ -1497,7 +1497,7 @@ out_unmap:
return err;
}
-static int __devexit su_remove(struct of_device *op)
+static int __devexit su_remove(struct platform_device *op)
{
struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
bool kbdms = false;
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index d1e6bcb..c1967ac 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1230,7 +1230,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
(sunzilog_reg.minor - 64) + con->index, con->index);
/* Get firmware console settings. */
- sunserial_console_termios(con, to_of_device(up->port.dev)->dev.of_node);
+ sunserial_console_termios(con, up->port.dev->of_node);
/* Firmware console speed is limited to 150-->38400 baud so
* this hackish cflag thing is OK.
@@ -1399,7 +1399,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
static int zilog_irq = -1;
-static int __devinit zs_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit zs_probe(struct platform_device *op, const struct of_device_id *match)
{
static int kbm_inst, uart_inst;
int inst;
@@ -1516,7 +1516,7 @@ static void __devexit zs_remove_one(struct uart_sunzilog_port *up)
uart_remove_one_port(&sunzilog_reg, &up->port);
}
-static int __devexit zs_remove(struct of_device *op)
+static int __devexit zs_remove(struct platform_device *op)
{
struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev);
struct zilog_layout __iomem *regs;
diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c
index 67ca642..1f36b7e 100644
--- a/drivers/serial/timbuart.c
+++ b/drivers/serial/timbuart.c
@@ -423,7 +423,7 @@ static struct uart_driver timbuart_driver = {
.nr = 1
};
-static int timbuart_probe(struct platform_device *dev)
+static int __devinit timbuart_probe(struct platform_device *dev)
{
int err, irq;
struct timbuart_port *uart;
@@ -489,7 +489,7 @@ err_mem:
return err;
}
-static int timbuart_remove(struct platform_device *dev)
+static int __devexit timbuart_remove(struct platform_device *dev)
{
struct timbuart_port *uart = platform_get_drvdata(dev);
@@ -507,7 +507,7 @@ static struct platform_driver timbuart_platform_driver = {
.owner = THIS_MODULE,
},
.probe = timbuart_probe,
- .remove = timbuart_remove,
+ .remove = __devexit_p(timbuart_remove),
};
/*--------------------------------------------------------------------------*/
diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c
index caf085d..9b03d7b 100644
--- a/drivers/serial/uartlite.c
+++ b/drivers/serial/uartlite.c
@@ -584,7 +584,7 @@ static struct platform_driver ulite_platform_driver = {
*/
#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE))
static int __devinit
-ulite_of_probe(struct of_device *op, const struct of_device_id *match)
+ulite_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct resource res;
const unsigned int *id;
@@ -605,7 +605,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match)
return ulite_assign(&op->dev, id ? *id : -1, res.start, irq);
}
-static int __devexit ulite_of_remove(struct of_device *op)
+static int __devexit ulite_of_remove(struct platform_device *op)
{
return ulite_release(&op->dev);
}
diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c
index 907b06f..3f4848e 100644
--- a/drivers/serial/ucc_uart.c
+++ b/drivers/serial/ucc_uart.c
@@ -1194,7 +1194,7 @@ static void uart_firmware_cont(const struct firmware *fw, void *context)
release_firmware(fw);
}
-static int ucc_uart_probe(struct of_device *ofdev,
+static int ucc_uart_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -1462,7 +1462,7 @@ static int ucc_uart_probe(struct of_device *ofdev,
return 0;
}
-static int ucc_uart_remove(struct of_device *ofdev)
+static int ucc_uart_remove(struct platform_device *ofdev)
{
struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 78bb512..08fc653 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -1,9 +1,10 @@
#
# Makefile for the SuperH specific drivers.
#
+obj-y := clk.o intc.o
+
obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_MAPLE) += maple/
+
obj-$(CONFIG_GENERIC_GPIO) += pfc.o
-obj-$(CONFIG_SUPERH) += clk.o
obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o
-obj-y += intc.o
diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk-cpg.c
index f5c80ba..8c024b9 100644
--- a/drivers/sh/clk-cpg.c
+++ b/drivers/sh/clk-cpg.c
@@ -68,6 +68,39 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk)
return clk->freq_table[idx].frequency;
}
+static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_div_mult_table *table = &sh_clk_div6_table;
+ u32 value;
+ int ret, i;
+
+ if (!clk->parent_table || !clk->parent_num)
+ return -EINVAL;
+
+ /* Search the parent */
+ for (i = 0; i < clk->parent_num; i++)
+ if (clk->parent_table[i] == parent)
+ break;
+
+ if (i == clk->parent_num)
+ return -ENODEV;
+
+ ret = clk_reparent(clk, parent);
+ if (ret < 0)
+ return ret;
+
+ value = __raw_readl(clk->enable_reg) &
+ ~(((1 << clk->src_width) - 1) << clk->src_shift);
+
+ __raw_writel(value | (i << clk->src_shift), clk->enable_reg);
+
+ /* Rebuild the frequency table */
+ clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
+ table, &clk->arch_flags);
+
+ return 0;
+}
+
static int sh_clk_div6_set_rate(struct clk *clk,
unsigned long rate, int algo_id)
{
@@ -117,7 +150,17 @@ static struct clk_ops sh_clk_div6_clk_ops = {
.disable = sh_clk_div6_disable,
};
-int __init sh_clk_div6_register(struct clk *clks, int nr)
+static struct clk_ops sh_clk_div6_reparent_clk_ops = {
+ .recalc = sh_clk_div6_recalc,
+ .round_rate = sh_clk_div_round_rate,
+ .set_rate = sh_clk_div6_set_rate,
+ .enable = sh_clk_div6_enable,
+ .disable = sh_clk_div6_disable,
+ .set_parent = sh_clk_div6_set_parent,
+};
+
+static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
+ struct clk_ops *ops)
{
struct clk *clkp;
void *freq_table;
@@ -136,7 +179,7 @@ int __init sh_clk_div6_register(struct clk *clks, int nr)
for (k = 0; !ret && (k < nr); k++) {
clkp = clks + k;
- clkp->ops = &sh_clk_div6_clk_ops;
+ clkp->ops = ops;
clkp->id = -1;
clkp->freq_table = freq_table + (k * freq_table_size);
clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
@@ -147,6 +190,17 @@ int __init sh_clk_div6_register(struct clk *clks, int nr)
return ret;
}
+int __init sh_clk_div6_register(struct clk *clks, int nr)
+{
+ return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops);
+}
+
+int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
+{
+ return sh_clk_div6_register_ops(clks, nr,
+ &sh_clk_div6_reparent_clk_ops);
+}
+
static unsigned long sh_clk_div4_recalc(struct clk *clk)
{
struct clk_div4_table *d4t = clk->priv;
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index f0a1418..acd35d1 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -1723,7 +1723,7 @@ static void pl022_cleanup(struct spi_device *spi)
}
-static int __init
+static int __devinit
pl022_probe(struct amba_device *adev, struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -1838,7 +1838,7 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
return status;
}
-static int __exit
+static int __devexit
pl022_remove(struct amba_device *adev)
{
struct pl022 *pl022 = amba_get_drvdata(adev);
@@ -1970,7 +1970,7 @@ static struct amba_driver pl022_driver = {
},
.id_table = pl022_ids,
.probe = pl022_probe,
- .remove = __exit_p(pl022_remove),
+ .remove = __devexit_p(pl022_remove),
.suspend = pl022_suspend,
.resume = pl022_resume,
};
diff --git a/drivers/spi/mpc512x_psc_spi.c b/drivers/spi/mpc512x_psc_spi.c
index 10baac3..77d9e7e 100644
--- a/drivers/spi/mpc512x_psc_spi.c
+++ b/drivers/spi/mpc512x_psc_spi.c
@@ -406,9 +406,9 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
}
/* bus_num is used only for the case dev->platform_data == NULL */
-static int __init mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
- u32 size, unsigned int irq,
- s16 bus_num)
+static int __devinit mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
+ u32 size, unsigned int irq,
+ s16 bus_num)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
struct mpc512x_psc_spi *mps;
@@ -492,7 +492,7 @@ free_master:
return ret;
}
-static int __exit mpc512x_psc_spi_do_remove(struct device *dev)
+static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
@@ -507,8 +507,8 @@ static int __exit mpc512x_psc_spi_do_remove(struct device *dev)
return 0;
}
-static int __init mpc512x_psc_spi_of_probe(struct of_device *op,
- const struct of_device_id *match)
+static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op,
+ const struct of_device_id *match)
{
const u32 *regaddr_p;
u64 regaddr64, size64;
@@ -539,7 +539,7 @@ static int __init mpc512x_psc_spi_of_probe(struct of_device *op,
irq_of_parse_and_map(op->dev.of_node, 0), id);
}
-static int __exit mpc512x_psc_spi_of_remove(struct of_device *op)
+static int __devexit mpc512x_psc_spi_of_remove(struct platform_device *op)
{
return mpc512x_psc_spi_do_remove(&op->dev);
}
@@ -553,7 +553,7 @@ MODULE_DEVICE_TABLE(of, mpc512x_psc_spi_of_match);
static struct of_platform_driver mpc512x_psc_spi_of_driver = {
.probe = mpc512x_psc_spi_of_probe,
- .remove = __exit_p(mpc512x_psc_spi_of_remove),
+ .remove = __devexit_p(mpc512x_psc_spi_of_remove),
.driver = {
.name = "mpc512x-psc-spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c
index 66d1701..983fbbf 100644
--- a/drivers/spi/mpc52xx_psc_spi.c
+++ b/drivers/spi/mpc52xx_psc_spi.c
@@ -465,7 +465,7 @@ static int __exit mpc52xx_psc_spi_do_remove(struct device *dev)
return 0;
}
-static int __init mpc52xx_psc_spi_of_probe(struct of_device *op,
+static int __init mpc52xx_psc_spi_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
const u32 *regaddr_p;
@@ -495,7 +495,7 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op,
irq_of_parse_and_map(op->dev.of_node, 0), id);
}
-static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op)
+static int __exit mpc52xx_psc_spi_of_remove(struct platform_device *op)
{
return mpc52xx_psc_spi_do_remove(&op->dev);
}
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
index 56136ff..ec9f0b1 100644
--- a/drivers/spi/mpc52xx_spi.c
+++ b/drivers/spi/mpc52xx_spi.c
@@ -390,7 +390,7 @@ static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
/*
* OF Platform Bus Binding
*/
-static int __devinit mpc52xx_spi_probe(struct of_device *op,
+static int __devinit mpc52xx_spi_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct spi_master *master;
@@ -530,7 +530,7 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
return rc;
}
-static int __devexit mpc52xx_spi_remove(struct of_device *op)
+static int __devexit mpc52xx_spi_remove(struct platform_device *op)
{
struct spi_master *master = dev_get_drvdata(&op->dev);
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
diff --git a/drivers/spi/omap_spi_100k.c b/drivers/spi/omap_spi_100k.c
index 24668b3..9bd1c92 100644
--- a/drivers/spi/omap_spi_100k.c
+++ b/drivers/spi/omap_spi_100k.c
@@ -141,7 +141,12 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
{
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- /* write 16-bit word */
+ /* write 16-bit word, shifting 8-bit data if necessary */
+ if (len <= 8) {
+ data <<= 8;
+ len = 16;
+ }
+
spi100k_enable_clock(master);
writew( data , spi100k->base + SPI_TX_MSB);
@@ -162,6 +167,10 @@ static int spi100k_read_data(struct spi_master *master, int len)
int dataH,dataL;
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+ /* Always do at least 16 bits */
+ if (len <= 8)
+ len = 16;
+
spi100k_enable_clock(master);
writew(SPI_CTRL_SEN(0) |
SPI_CTRL_WORD_SIZE(len) |
@@ -214,10 +223,6 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
c = count;
word_len = cs->word_len;
- /* RX_ONLY mode needs dummy data in TX reg */
- if (xfer->tx_buf == NULL)
- spi100k_write_data(spi->master,word_len, 0);
-
if (word_len <= 8) {
u8 *rx;
const u8 *tx;
@@ -227,9 +232,9 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
do {
c-=1;
if (xfer->tx_buf != NULL)
- spi100k_write_data(spi->master,word_len, *tx);
+ spi100k_write_data(spi->master, word_len, *tx++);
if (xfer->rx_buf != NULL)
- *rx = spi100k_read_data(spi->master,word_len);
+ *rx++ = spi100k_read_data(spi->master, word_len);
} while(c);
} else if (word_len <= 16) {
u16 *rx;
@@ -380,10 +385,6 @@ static void omap1_spi100k_work(struct work_struct *work)
if (t->len) {
unsigned count;
- /* RX_ONLY mode needs dummy data in TX reg */
- if (t->tx_buf == NULL)
- spi100k_write_data(spi->master, 8, 0);
-
count = omap1_spi100k_txrx_pio(spi, t);
m->actual_length += count;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1bb1b88..a9e5c79 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -528,6 +528,10 @@ int spi_register_master(struct spi_master *master)
dynamic = 1;
}
+ spin_lock_init(&master->bus_lock_spinlock);
+ mutex_init(&master->bus_lock_mutex);
+ master->bus_lock_flag = 0;
+
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
@@ -670,6 +674,35 @@ int spi_setup(struct spi_device *spi)
}
EXPORT_SYMBOL_GPL(spi_setup);
+static int __spi_async(struct spi_device *spi, struct spi_message *message)
+{
+ struct spi_master *master = spi->master;
+
+ /* Half-duplex links include original MicroWire, and ones with
+ * only one data pin like SPI_3WIRE (switches direction) or where
+ * either MOSI or MISO is missing. They can also be caused by
+ * software limitations.
+ */
+ if ((master->flags & SPI_MASTER_HALF_DUPLEX)
+ || (spi->mode & SPI_3WIRE)) {
+ struct spi_transfer *xfer;
+ unsigned flags = master->flags;
+
+ list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ if (xfer->rx_buf && xfer->tx_buf)
+ return -EINVAL;
+ if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
+ return -EINVAL;
+ if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
+ return -EINVAL;
+ }
+ }
+
+ message->spi = spi;
+ message->status = -EINPROGRESS;
+ return master->transfer(spi, message);
+}
+
/**
* spi_async - asynchronous SPI transfer
* @spi: device with which data will be exchanged
@@ -702,33 +735,68 @@ EXPORT_SYMBOL_GPL(spi_setup);
int spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
+ int ret;
+ unsigned long flags;
- /* Half-duplex links include original MicroWire, and ones with
- * only one data pin like SPI_3WIRE (switches direction) or where
- * either MOSI or MISO is missing. They can also be caused by
- * software limitations.
- */
- if ((master->flags & SPI_MASTER_HALF_DUPLEX)
- || (spi->mode & SPI_3WIRE)) {
- struct spi_transfer *xfer;
- unsigned flags = master->flags;
+ spin_lock_irqsave(&master->bus_lock_spinlock, flags);
- list_for_each_entry(xfer, &message->transfers, transfer_list) {
- if (xfer->rx_buf && xfer->tx_buf)
- return -EINVAL;
- if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
- return -EINVAL;
- if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
- return -EINVAL;
- }
- }
+ if (master->bus_lock_flag)
+ ret = -EBUSY;
+ else
+ ret = __spi_async(spi, message);
- message->spi = spi;
- message->status = -EINPROGRESS;
- return master->transfer(spi, message);
+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(spi_async);
+/**
+ * spi_async_locked - version of spi_async with exclusive bus usage
+ * @spi: device with which data will be exchanged
+ * @message: describes the data transfers, including completion callback
+ * Context: any (irqs may be blocked, etc)
+ *
+ * This call may be used in_irq and other contexts which can't sleep,
+ * as well as from task contexts which can sleep.
+ *
+ * The completion callback is invoked in a context which can't sleep.
+ * Before that invocation, the value of message->status is undefined.
+ * When the callback is issued, message->status holds either zero (to
+ * indicate complete success) or a negative error code. After that
+ * callback returns, the driver which issued the transfer request may
+ * deallocate the associated memory; it's no longer in use by any SPI
+ * core or controller driver code.
+ *
+ * Note that although all messages to a spi_device are handled in
+ * FIFO order, messages may go to different devices in other orders.
+ * Some device might be higher priority, or have various "hard" access
+ * time requirements, for example.
+ *
+ * On detection of any fault during the transfer, processing of
+ * the entire message is aborted, and the device is deselected.
+ * Until returning from the associated message completion callback,
+ * no other spi_message queued to that device will be processed.
+ * (This rule applies equally to all the synchronous transfer calls,
+ * which are wrappers around this core asynchronous primitive.)
+ */
+int spi_async_locked(struct spi_device *spi, struct spi_message *message)
+{
+ struct spi_master *master = spi->master;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&master->bus_lock_spinlock, flags);
+
+ ret = __spi_async(spi, message);
+
+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
+
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(spi_async_locked);
+
/*-------------------------------------------------------------------------*/
@@ -742,6 +810,32 @@ static void spi_complete(void *arg)
complete(arg);
}
+static int __spi_sync(struct spi_device *spi, struct spi_message *message,
+ int bus_locked)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ int status;
+ struct spi_master *master = spi->master;
+
+ message->complete = spi_complete;
+ message->context = &done;
+
+ if (!bus_locked)
+ mutex_lock(&master->bus_lock_mutex);
+
+ status = spi_async_locked(spi, message);
+
+ if (!bus_locked)
+ mutex_unlock(&master->bus_lock_mutex);
+
+ if (status == 0) {
+ wait_for_completion(&done);
+ status = message->status;
+ }
+ message->context = NULL;
+ return status;
+}
+
/**
* spi_sync - blocking/synchronous SPI data transfers
* @spi: device with which data will be exchanged
@@ -765,21 +859,86 @@ static void spi_complete(void *arg)
*/
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
- DECLARE_COMPLETION_ONSTACK(done);
- int status;
-
- message->complete = spi_complete;
- message->context = &done;
- status = spi_async(spi, message);
- if (status == 0) {
- wait_for_completion(&done);
- status = message->status;
- }
- message->context = NULL;
- return status;
+ return __spi_sync(spi, message, 0);
}
EXPORT_SYMBOL_GPL(spi_sync);
+/**
+ * spi_sync_locked - version of spi_sync with exclusive bus usage
+ * @spi: device with which data will be exchanged
+ * @message: describes the data transfers
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep. The sleep
+ * is non-interruptible, and has no timeout. Low-overhead controller
+ * drivers may DMA directly into and out of the message buffers.
+ *
+ * This call should be used by drivers that require exclusive access to the
+ * SPI bus. It has to be preceeded by a spi_bus_lock call. The SPI bus must
+ * be released by a spi_bus_unlock call when the exclusive access is over.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int spi_sync_locked(struct spi_device *spi, struct spi_message *message)
+{
+ return __spi_sync(spi, message, 1);
+}
+EXPORT_SYMBOL_GPL(spi_sync_locked);
+
+/**
+ * spi_bus_lock - obtain a lock for exclusive SPI bus usage
+ * @master: SPI bus master that should be locked for exclusive bus access
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep. The sleep
+ * is non-interruptible, and has no timeout.
+ *
+ * This call should be used by drivers that require exclusive access to the
+ * SPI bus. The SPI bus must be released by a spi_bus_unlock call when the
+ * exclusive access is over. Data transfer must be done by spi_sync_locked
+ * and spi_async_locked calls when the SPI bus lock is held.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int spi_bus_lock(struct spi_master *master)
+{
+ unsigned long flags;
+
+ mutex_lock(&master->bus_lock_mutex);
+
+ spin_lock_irqsave(&master->bus_lock_spinlock, flags);
+ master->bus_lock_flag = 1;
+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
+
+ /* mutex remains locked until spi_bus_unlock is called */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_bus_lock);
+
+/**
+ * spi_bus_unlock - release the lock for exclusive SPI bus usage
+ * @master: SPI bus master that was locked for exclusive bus access
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep. The sleep
+ * is non-interruptible, and has no timeout.
+ *
+ * This call releases an SPI bus lock previously obtained by an spi_bus_lock
+ * call.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int spi_bus_unlock(struct spi_master *master)
+{
+ master->bus_lock_flag = 0;
+
+ mutex_unlock(&master->bus_lock_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_bus_unlock);
+
/* portable code must never pass more than 32 bytes */
#define SPI_BUFSIZ max(32,SMP_CACHE_BYTES)
diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c
index 5265330a..8b55724 100644
--- a/drivers/spi/spi_bitbang.c
+++ b/drivers/spi/spi_bitbang.c
@@ -259,7 +259,6 @@ static void bitbang_work(struct work_struct *work)
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
- int do_setup = -1;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
@@ -275,6 +274,7 @@ static void bitbang_work(struct work_struct *work)
unsigned tmp;
unsigned cs_change;
int status;
+ int do_setup = -1;
m = container_of(bitbang->queue.next, struct spi_message,
queue);
@@ -307,6 +307,8 @@ static void bitbang_work(struct work_struct *work)
status = setup_transfer(spi, t);
if (status < 0)
break;
+ if (do_setup == -1)
+ do_setup = 0;
}
/* set up default clock polarity, and activate chip;
@@ -367,11 +369,6 @@ static void bitbang_work(struct work_struct *work)
m->status = status;
m->complete(m->context);
- /* restore speed and wordsize if it was overridden */
- if (do_setup == 1)
- setup_transfer(spi, NULL);
- do_setup = 0;
-
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
* be for this chip too.
diff --git a/drivers/spi/spi_bitbang_txrx.h b/drivers/spi/spi_bitbang_txrx.h
index fc033bb..c16bf85 100644
--- a/drivers/spi/spi_bitbang_txrx.h
+++ b/drivers/spi/spi_bitbang_txrx.h
@@ -44,7 +44,7 @@
static inline u32
bitbang_txrx_be_cpha0(struct spi_device *spi,
- unsigned nsecs, unsigned cpol,
+ unsigned nsecs, unsigned cpol, unsigned flags,
u32 word, u8 bits)
{
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
@@ -53,7 +53,8 @@ bitbang_txrx_be_cpha0(struct spi_device *spi,
for (word <<= (32 - bits); likely(bits); bits--) {
/* setup MSB (to slave) on trailing edge */
- setmosi(spi, word & (1 << 31));
+ if ((flags & SPI_MASTER_NO_TX) == 0)
+ setmosi(spi, word & (1 << 31));
spidelay(nsecs); /* T(setup) */
setsck(spi, !cpol);
@@ -61,7 +62,8 @@ bitbang_txrx_be_cpha0(struct spi_device *spi,
/* sample MSB (from slave) on leading edge */
word <<= 1;
- word |= getmiso(spi);
+ if ((flags & SPI_MASTER_NO_RX) == 0)
+ word |= getmiso(spi);
setsck(spi, cpol);
}
return word;
@@ -69,7 +71,7 @@ bitbang_txrx_be_cpha0(struct spi_device *spi,
static inline u32
bitbang_txrx_be_cpha1(struct spi_device *spi,
- unsigned nsecs, unsigned cpol,
+ unsigned nsecs, unsigned cpol, unsigned flags,
u32 word, u8 bits)
{
/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
@@ -79,7 +81,8 @@ bitbang_txrx_be_cpha1(struct spi_device *spi,
/* setup MSB (to slave) on leading edge */
setsck(spi, !cpol);
- setmosi(spi, word & (1 << 31));
+ if ((flags & SPI_MASTER_NO_TX) == 0)
+ setmosi(spi, word & (1 << 31));
spidelay(nsecs); /* T(setup) */
setsck(spi, cpol);
@@ -87,7 +90,8 @@ bitbang_txrx_be_cpha1(struct spi_device *spi,
/* sample MSB (from slave) on trailing edge */
word <<= 1;
- word |= getmiso(spi);
+ if ((flags & SPI_MASTER_NO_RX) == 0)
+ word |= getmiso(spi);
}
return word;
}
diff --git a/drivers/spi/spi_butterfly.c b/drivers/spi/spi_butterfly.c
index 8b52812..0d4ceba 100644
--- a/drivers/spi/spi_butterfly.c
+++ b/drivers/spi/spi_butterfly.c
@@ -156,7 +156,7 @@ butterfly_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
/*----------------------------------------------------------------------*/
diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi_gpio.c
index 7edbd58..e24a634 100644
--- a/drivers/spi/spi_gpio.c
+++ b/drivers/spi/spi_gpio.c
@@ -146,25 +146,63 @@ static inline int getmiso(const struct spi_device *spi)
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits);
}
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits);
}
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits);
+}
+
+/*
+ * These functions do not call setmosi or getmiso if respective flag
+ * (SPI_MASTER_NO_RX or SPI_MASTER_NO_TX) is set, so they are safe to
+ * call when such pin is not present or defined in the controller.
+ * A separate set of callbacks is defined to get highest possible
+ * speed in the generic case (when both MISO and MOSI lines are
+ * available), as optimiser will remove the checks when argument is
+ * constant.
+ */
+
+static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ unsigned flags = spi->master->flags;
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
+}
+
+static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ unsigned flags = spi->master->flags;
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits);
+}
+
+static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ unsigned flags = spi->master->flags;
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits);
+}
+
+static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ unsigned flags = spi->master->flags;
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits);
}
/*----------------------------------------------------------------------*/
@@ -232,19 +270,30 @@ static int __init spi_gpio_alloc(unsigned pin, const char *label, bool is_in)
}
static int __init
-spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label)
+spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label,
+ u16 *res_flags)
{
int value;
/* NOTE: SPI_*_GPIO symbols may reference "pdata" */
- value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false);
- if (value)
- goto done;
+ if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) {
+ value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false);
+ if (value)
+ goto done;
+ } else {
+ /* HW configuration without MOSI pin */
+ *res_flags |= SPI_MASTER_NO_TX;
+ }
- value = spi_gpio_alloc(SPI_MISO_GPIO, label, true);
- if (value)
- goto free_mosi;
+ if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) {
+ value = spi_gpio_alloc(SPI_MISO_GPIO, label, true);
+ if (value)
+ goto free_mosi;
+ } else {
+ /* HW configuration without MISO pin */
+ *res_flags |= SPI_MASTER_NO_RX;
+ }
value = spi_gpio_alloc(SPI_SCK_GPIO, label, false);
if (value)
@@ -253,9 +302,11 @@ spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label)
goto done;
free_miso:
- gpio_free(SPI_MISO_GPIO);
+ if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
+ gpio_free(SPI_MISO_GPIO);
free_mosi:
- gpio_free(SPI_MOSI_GPIO);
+ if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
+ gpio_free(SPI_MOSI_GPIO);
done:
return value;
}
@@ -266,6 +317,7 @@ static int __init spi_gpio_probe(struct platform_device *pdev)
struct spi_master *master;
struct spi_gpio *spi_gpio;
struct spi_gpio_platform_data *pdata;
+ u16 master_flags = 0;
pdata = pdev->dev.platform_data;
#ifdef GENERIC_BITBANG
@@ -273,7 +325,7 @@ static int __init spi_gpio_probe(struct platform_device *pdev)
return -ENODEV;
#endif
- status = spi_gpio_request(pdata, dev_name(&pdev->dev));
+ status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);
if (status < 0)
return status;
@@ -289,6 +341,7 @@ static int __init spi_gpio_probe(struct platform_device *pdev)
if (pdata)
spi_gpio->pdata = *pdata;
+ master->flags = master_flags;
master->bus_num = pdev->id;
master->num_chipselect = SPI_N_CHIPSEL;
master->setup = spi_gpio_setup;
@@ -296,10 +349,18 @@ static int __init spi_gpio_probe(struct platform_device *pdev)
spi_gpio->bitbang.master = spi_master_get(master);
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
- spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
- spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
- spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
- spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
+
+ if ((master_flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_RX)) == 0) {
+ spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
+ } else {
+ spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
+ }
spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;
spi_gpio->bitbang.flags = SPI_CS_HIGH;
@@ -307,8 +368,10 @@ static int __init spi_gpio_probe(struct platform_device *pdev)
if (status < 0) {
spi_master_put(spi_gpio->bitbang.master);
gpio_free:
- gpio_free(SPI_MISO_GPIO);
- gpio_free(SPI_MOSI_GPIO);
+ if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
+ gpio_free(SPI_MISO_GPIO);
+ if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
+ gpio_free(SPI_MOSI_GPIO);
gpio_free(SPI_SCK_GPIO);
spi_master_put(master);
}
@@ -331,8 +394,10 @@ static int __exit spi_gpio_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
- gpio_free(SPI_MISO_GPIO);
- gpio_free(SPI_MOSI_GPIO);
+ if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
+ gpio_free(SPI_MISO_GPIO);
+ if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
+ gpio_free(SPI_MOSI_GPIO);
gpio_free(SPI_SCK_GPIO);
return status;
diff --git a/drivers/spi/spi_lm70llp.c b/drivers/spi/spi_lm70llp.c
index 86fb7b5..7746a41 100644
--- a/drivers/spi/spi_lm70llp.c
+++ b/drivers/spi/spi_lm70llp.c
@@ -191,7 +191,7 @@ static void lm70_chipselect(struct spi_device *spi, int value)
*/
static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
static void spi_lm70llp_attach(struct parport *p)
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index aad9ae1..d31b57f 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -1236,7 +1236,7 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
return 0;
}
-static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
+static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
@@ -1308,7 +1308,7 @@ err_clk:
return ret;
}
-static int __devexit of_mpc8xxx_spi_remove(struct of_device *ofdev)
+static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
{
int ret;
diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
index 0f5fa7e..80e172d 100644
--- a/drivers/spi/spi_ppc4xx.c
+++ b/drivers/spi/spi_ppc4xx.c
@@ -388,9 +388,9 @@ static void free_gpios(struct ppc4xx_spi *hw)
}
/*
- * of_device layer stuff...
+ * platform_device layer stuff...
*/
-static int __init spi_ppc4xx_of_probe(struct of_device *op,
+static int __init spi_ppc4xx_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct ppc4xx_spi *hw;
@@ -565,7 +565,7 @@ free_master:
return ret;
}
-static int __exit spi_ppc4xx_of_remove(struct of_device *op)
+static int __exit spi_ppc4xx_of_remove(struct platform_device *op)
{
struct spi_master *master = dev_get_drvdata(&op->dev);
struct ppc4xx_spi *hw = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
index 8979a75..be99135 100644
--- a/drivers/spi/spi_s3c24xx_gpio.c
+++ b/drivers/spi/spi_s3c24xx_gpio.c
@@ -64,25 +64,25 @@ static inline u32 getmiso(struct spi_device *dev)
static u32 s3c2410_spigpio_txrx_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
static u32 s3c2410_spigpio_txrx_mode1(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits);
}
static u32 s3c2410_spigpio_txrx_mode2(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits);
}
static u32 s3c2410_spigpio_txrx_mode3(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits);
}
diff --git a/drivers/spi/spi_sh_sci.c b/drivers/spi/spi_sh_sci.c
index a511be7..5c64391 100644
--- a/drivers/spi/spi_sh_sci.c
+++ b/drivers/spi/spi_sh_sci.c
@@ -83,25 +83,25 @@ static inline u32 getmiso(struct spi_device *dev)
static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
static u32 sh_sci_spi_txrx_mode1(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits);
}
static u32 sh_sci_spi_txrx_mode2(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits);
}
static u32 sh_sci_spi_txrx_mode3(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits);
}
static void sh_sci_spi_chipselect(struct spi_device *dev, int value)
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
index f53d3f6..b66c2db 100644
--- a/drivers/spi/xilinx_spi_of.c
+++ b/drivers/spi/xilinx_spi_of.c
@@ -38,7 +38,7 @@
#include "xilinx_spi.h"
-static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
+static int __devinit xilinx_spi_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct spi_master *master;
@@ -84,7 +84,7 @@ static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
return 0;
}
-static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+static int __devexit xilinx_spi_remove(struct platform_device *ofdev)
{
xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev));
dev_set_drvdata(&ofdev->dev, 0);
@@ -93,7 +93,7 @@ static int __devexit xilinx_spi_remove(struct of_device *ofdev)
return 0;
}
-static int __exit xilinx_spi_of_remove(struct of_device *op)
+static int __exit xilinx_spi_of_remove(struct platform_device *op)
{
return xilinx_spi_remove(op);
}
diff --git a/drivers/staging/easycap/easycap.h b/drivers/staging/easycap/easycap.h
index ad836d2..f3c827e 100644
--- a/drivers/staging/easycap/easycap.h
+++ b/drivers/staging/easycap/easycap.h
@@ -463,15 +463,12 @@ struct data_buffer audio_buffer[];
void easycap_complete(struct urb *);
int easycap_open(struct inode *, struct file *);
int easycap_release(struct inode *, struct file *);
-int easycap_ioctl(struct inode *, struct file *, \
- unsigned int, unsigned long);
+long easycap_ioctl(struct file *, unsigned int, unsigned long);
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
int easycap_open_noinode(struct file *);
int easycap_release_noinode(struct file *);
-long easycap_ioctl_noinode(struct file *, \
- unsigned int, unsigned long);
int videodev_release(struct video_device *);
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
@@ -515,8 +512,7 @@ void easysnd_complete(struct urb *);
ssize_t easysnd_read(struct file *, char __user *, size_t, loff_t *);
int easysnd_open(struct inode *, struct file *);
int easysnd_release(struct inode *, struct file *);
-int easysnd_ioctl(struct inode *, struct file *, \
- unsigned int, unsigned long);
+long easysnd_ioctl(struct file *, unsigned int, unsigned long);
unsigned int easysnd_poll(struct file *, poll_table *);
void easysnd_delete(struct kref *);
int submit_audio_urbs(struct easycap *);
diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c
index 276b63d..9a42ae0 100644
--- a/drivers/staging/easycap/easycap_ioctl.c
+++ b/drivers/staging/easycap/easycap_ioctl.c
@@ -25,6 +25,7 @@
*/
/*****************************************************************************/
+#include <linux/smp_lock.h>
#include "easycap.h"
#include "easycap_debug.h"
#include "easycap_standard.h"
@@ -773,19 +774,10 @@ while (0xFFFFFFFF != easycap_control[i1].id) {
SAY("WARNING: failed to adjust mute: control not found\n");
return -ENOENT;
}
-/****************************************************************************/
-/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
-#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
-long
-easycap_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg)\
- {
- return easycap_ioctl((struct inode *)NULL, file, cmd, arg);
-}
-#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
/*--------------------------------------------------------------------------*/
-int easycap_ioctl(struct inode *inode, struct file *file, \
- unsigned int cmd, unsigned long arg)
+static int easycap_ioctl_bkl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
static struct easycap *peasycap;
static struct usb_device *p;
@@ -1956,19 +1948,22 @@ default: {
}
return 0;
}
-/****************************************************************************/
-/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
-#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
-long
-easysnd_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg)
+
+long easycap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return easysnd_ioctl((struct inode *)NULL, file, cmd, arg);
+ struct inode *inode = file->f_dentry->d_inode;
+ long ret;
+
+ lock_kernel();
+ ret = easycap_ioctl_bkl(inode, file, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
-#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
/*--------------------------------------------------------------------------*/
-int easysnd_ioctl(struct inode *inode, struct file *file, \
- unsigned int cmd, unsigned long arg)
+static int easysnd_ioctl_bkl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct easycap *peasycap;
struct usb_device *p;
@@ -2158,6 +2153,19 @@ default: {
}
return 0;
}
+
+long easysnd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ long ret;
+
+ lock_kernel();
+ ret = easysnd_ioctl_bkl(inode, file, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
+
/*****************************************************************************/
int explain_ioctl(__u32 wot)
{
diff --git a/drivers/staging/easycap/easycap_main.c b/drivers/staging/easycap/easycap_main.c
index 09c194c..5a4bbd9 100644
--- a/drivers/staging/easycap/easycap_main.c
+++ b/drivers/staging/easycap/easycap_main.c
@@ -60,13 +60,13 @@ struct usb_driver easycap_usb_driver = {
*/
/*---------------------------------------------------------------------------*/
const struct file_operations easycap_fops = {
-.owner = THIS_MODULE,
-.open = easycap_open,
-.release = easycap_release,
-.ioctl = easycap_ioctl,
-.poll = easycap_poll,
-.mmap = easycap_mmap,
-.llseek = no_llseek,
+ .owner = THIS_MODULE,
+ .open = easycap_open,
+ .release = easycap_release,
+ .unlocked_ioctl = easycap_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
+ .llseek = no_llseek,
};
struct vm_operations_struct easycap_vm_ops = {
.open = easycap_vma_open,
@@ -83,12 +83,12 @@ struct usb_class_driver easycap_class = {
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#if defined(EASYCAP_NEEDS_V4L2_FOPS)
const struct v4l2_file_operations v4l2_fops = {
-.owner = THIS_MODULE,
-.open = easycap_open_noinode,
-.release = easycap_release_noinode,
-.ioctl = easycap_ioctl_noinode,
-.poll = easycap_poll,
-.mmap = easycap_mmap,
+ .owner = THIS_MODULE,
+ .open = easycap_open_noinode,
+ .release = easycap_release_noinode,
+ .unlocked_ioctl = easycap_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
};
#endif /*EASYCAP_NEEDS_V4L2_FOPS*/
int video_device_many /*=0*/;
@@ -102,12 +102,12 @@ struct video_device *pvideo_array[VIDEO_DEVICE_MANY], *pvideo_device;
*/
/*--------------------------------------------------------------------------*/
const struct file_operations easysnd_fops = {
-.owner = THIS_MODULE,
-.open = easysnd_open,
-.release = easysnd_release,
-.ioctl = easysnd_ioctl,
-.read = easysnd_read,
-.llseek = no_llseek,
+ .owner = THIS_MODULE,
+ .open = easysnd_open,
+ .release = easysnd_release,
+ .unlocked_ioctl = easysnd_ioctl,
+ .read = easysnd_read,
+ .llseek = no_llseek,
};
struct usb_class_driver easysnd_class = {
.name = "usb/easysnd%d",
diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c
index f7ea2a3..ff1d247 100644
--- a/drivers/staging/hv/blkvsc_drv.c
+++ b/drivers/staging/hv/blkvsc_drv.c
@@ -25,6 +25,7 @@
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/hdreg.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -805,7 +806,8 @@ static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req)
blkvsc_req->cmnd[0] = READ_16;
}
- blkvsc_req->cmnd[1] |= blk_fua_rq(blkvsc_req->req) ? 0x8 : 0;
+ blkvsc_req->cmnd[1] |=
+ (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0;
*(unsigned long long *)&blkvsc_req->cmnd[2] =
cpu_to_be64(blkvsc_req->sector_start);
@@ -821,7 +823,8 @@ static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req)
blkvsc_req->cmnd[0] = READ_10;
}
- blkvsc_req->cmnd[1] |= blk_fua_rq(blkvsc_req->req) ? 0x8 : 0;
+ blkvsc_req->cmnd[1] |=
+ (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0;
*(unsigned int *)&blkvsc_req->cmnd[2] =
cpu_to_be32(blkvsc_req->sector_start);
@@ -1268,7 +1271,7 @@ static void blkvsc_request(struct request_queue *queue)
DPRINT_DBG(BLKVSC_DRV, "- req %p\n", req);
blkdev = req->rq_disk->private_data;
- if (blkdev->shutting_down || !blk_fs_request(req) ||
+ if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS ||
blkdev->media_not_present) {
__blk_end_request_cur(req, 0);
continue;
@@ -1306,6 +1309,7 @@ static int blkvsc_open(struct block_device *bdev, fmode_t mode)
DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users,
blkdev->gd->disk_name);
+ lock_kernel();
spin_lock(&blkdev->lock);
if (!blkdev->users && blkdev->device_type == DVD_TYPE) {
@@ -1317,6 +1321,7 @@ static int blkvsc_open(struct block_device *bdev, fmode_t mode)
blkdev->users++;
spin_unlock(&blkdev->lock);
+ unlock_kernel();
return 0;
}
@@ -1327,6 +1332,7 @@ static int blkvsc_release(struct gendisk *disk, fmode_t mode)
DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users,
blkdev->gd->disk_name);
+ lock_kernel();
spin_lock(&blkdev->lock);
if (blkdev->users == 1) {
spin_unlock(&blkdev->lock);
@@ -1337,6 +1343,7 @@ static int blkvsc_release(struct gendisk *disk, fmode_t mode)
blkdev->users--;
spin_unlock(&blkdev->lock);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig
index 968c2ade..100c4d4 100644
--- a/drivers/staging/lirc/Kconfig
+++ b/drivers/staging/lirc/Kconfig
@@ -3,6 +3,7 @@
#
menuconfig LIRC_STAGING
bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+ depends on LIRC
help
Say Y here, and all supported Linux Infrared Remote Control IR and
RF receiver and transmitter drivers will be displayed. When paired
@@ -13,21 +14,13 @@ if LIRC_STAGING
config LIRC_BT829
tristate "BT829 based hardware"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && PCI
help
Driver for the IR interface on BT829-based hardware
-config LIRC_ENE0100
- tristate "ENE KB3924/ENE0100 CIR Port Reciever"
- depends on LIRC_STAGING
- help
- This is a driver for CIR port handled by ENE KB3924 embedded
- controller found on some notebooks.
- It appears on PNP list as ENE0100.
-
config LIRC_I2C
tristate "I2C Based IR Receivers"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && I2C
help
Driver for I2C-based IR receivers, such as those commonly
found onboard Hauppauge PVR-150/250/350 video capture cards
@@ -40,7 +33,7 @@ config LIRC_IGORPLUGUSB
config LIRC_IMON
tristate "Legacy SoundGraph iMON Receiver and Display"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && USB
help
Driver for the original SoundGraph iMON IR Receiver and Display
@@ -48,7 +41,7 @@ config LIRC_IMON
config LIRC_IT87
tristate "ITE IT87XX CIR Port Receiver"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && PNP
help
Driver for the ITE IT87xx IR Receiver
@@ -60,13 +53,13 @@ config LIRC_ITE8709
config LIRC_PARALLEL
tristate "Homebrew Parallel Port Receiver"
- depends on LIRC_STAGING && !SMP
+ depends on LIRC_STAGING && PARPORT && !SMP
help
Driver for Homebrew Parallel Port Receivers
config LIRC_SASEM
tristate "Sasem USB IR Remote"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && USB
help
Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
@@ -89,12 +82,6 @@ config LIRC_SIR
help
Driver for the SIR IrDA port
-config LIRC_STREAMZAP
- tristate "Streamzap PC Receiver"
- depends on LIRC_STAGING
- help
- Driver for the Streamzap PC Receiver
-
config LIRC_TTUSBIR
tristate "Technotrend USB IR Receiver"
depends on LIRC_STAGING && USB
@@ -103,7 +90,7 @@ config LIRC_TTUSBIR
config LIRC_ZILOG
tristate "Zilog/Hauppauge IR Transmitter"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && I2C
help
Driver for the Zilog/Hauppauge IR Transmitter, found on
PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile
index a019182..4da1f33 100644
--- a/drivers/staging/lirc/Makefile
+++ b/drivers/staging/lirc/Makefile
@@ -4,7 +4,6 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
-obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o
obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
@@ -14,6 +13,5 @@ obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
-obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o
obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
diff --git a/drivers/staging/lirc/lirc_ene0100.c b/drivers/staging/lirc/lirc_ene0100.c
deleted file mode 100644
index a152c52..0000000
--- a/drivers/staging/lirc/lirc_ene0100.c
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
- *
- * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@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/kernel.h>
-#include <linux/module.h>
-#include <linux/pnp.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include "lirc_ene0100.h"
-
-static int sample_period = 75;
-static int enable_idle = 1;
-static int enable_learning;
-
-static void ene_set_idle(struct ene_device *dev, int idle);
-static void ene_set_inputs(struct ene_device *dev, int enable);
-
-/* read a hardware register */
-static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
-{
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
- return inb(dev->hw_io + ENE_IO);
-}
-
-/* write a hardware register */
-static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
-{
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
- outb(value, dev->hw_io + ENE_IO);
-}
-
-/* change specific bits in hardware register */
-static void ene_hw_write_reg_mask(struct ene_device *dev,
- u16 reg, u8 value, u8 mask)
-{
- u8 regvalue;
-
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
-
- regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
- regvalue |= (value & mask);
- outb(regvalue, dev->hw_io + ENE_IO);
-}
-
-/* read irq status and ack it */
-static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
-{
- u8 irq_status;
- u8 fw_flags1, fw_flags2;
-
- fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
-
- if (buffer_pointer)
- *buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
-
- if (dev->hw_revision < ENE_HW_C) {
- irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
-
- if (!(irq_status & ENEB_IRQ_STATUS_IR))
- return 0;
- ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
- irq_status & ~ENEB_IRQ_STATUS_IR);
-
- /* rev B support only recieving */
- return ENE_IRQ_RX;
- }
-
- irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
-
- if (!(irq_status & ENEC_IRQ_STATUS))
- return 0;
-
- /* original driver does that twice - a workaround ? */
- ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
- ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
-
- /* clear unknown flag in F8F9 */
- if (fw_flags2 & ENE_FW2_IRQ_CLR)
- ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
-
- /* check if this is a TX interrupt */
- fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
-
- if (fw_flags1 & ENE_FW1_TXIRQ) {
- ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
- return ENE_IRQ_TX;
- } else
- return ENE_IRQ_RX;
-}
-
-static int ene_hw_detect(struct ene_device *dev)
-{
- u8 chip_major, chip_minor;
- u8 hw_revision, old_ver;
- u8 tmp;
- u8 fw_capabilities;
-
- tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
- ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
-
- chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
- chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
-
- ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
- hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
- old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
-
- if (hw_revision == 0xFF) {
-
- ene_printk(KERN_WARNING, "device seems to be disabled\n");
- ene_printk(KERN_WARNING,
- "send a mail to lirc-list@lists.sourceforge.net\n");
- ene_printk(KERN_WARNING, "please attach output of acpidump\n");
-
- return -ENODEV;
- }
-
- if (chip_major == 0x33) {
- ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
- return -ENODEV;
- }
-
- if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
- dev->hw_revision = ENE_HW_C;
- ene_printk(KERN_WARNING,
- "KB3926C detected, driver support is not complete!\n");
-
- } else if (old_ver == 0x24 && hw_revision == 0xC0) {
- dev->hw_revision = ENE_HW_B;
- ene_printk(KERN_NOTICE, "KB3926B detected\n");
- } else {
- dev->hw_revision = ENE_HW_D;
- ene_printk(KERN_WARNING,
- "unknown ENE chip detected, assuming KB3926D\n");
- ene_printk(KERN_WARNING, "driver support incomplete");
-
- }
-
- ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
- chip_major, chip_minor, old_ver, hw_revision);
-
-
- /* detect features hardware supports */
-
- if (dev->hw_revision < ENE_HW_C)
- return 0;
-
- fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
-
- dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
- dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
-
- dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
- fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
-
- ene_printk(KERN_NOTICE, "hardware features:\n");
- ene_printk(KERN_NOTICE,
- "learning and tx %s, gpio40_learn %s, fan_in %s\n",
- dev->hw_learning_and_tx_capable ? "on" : "off",
- dev->hw_gpio40_learning ? "on" : "off",
- dev->hw_fan_as_normal_input ? "on" : "off");
-
- if (!dev->hw_learning_and_tx_capable && enable_learning)
- enable_learning = 0;
-
- if (dev->hw_learning_and_tx_capable) {
- ene_printk(KERN_WARNING,
- "Device supports transmitting, but the driver doesn't\n");
- ene_printk(KERN_WARNING,
- "due to lack of hardware to test against.\n");
- ene_printk(KERN_WARNING,
- "Send a mail to: lirc-list@lists.sourceforge.net\n");
- }
- return 0;
-}
-
-/* hardware initialization */
-static int ene_hw_init(void *data)
-{
- u8 reg_value;
- struct ene_device *dev = (struct ene_device *)data;
- dev->in_use = 1;
-
- if (dev->hw_revision < ENE_HW_C) {
- ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
- ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
- } else {
- reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
- reg_value |= ENEC_IRQ_UNK_EN;
- reg_value &= ~ENEC_IRQ_STATUS;
- reg_value |= (dev->irq & ENEC_IRQ_MASK);
- ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
- ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
- }
-
- ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
- ene_set_inputs(dev, enable_learning);
-
- /* set sampling period */
- ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
-
- /* ack any pending irqs - just in case */
- ene_hw_irq_status(dev, NULL);
-
- /* enter idle mode */
- ene_set_idle(dev, 1);
-
- /* enable firmware bits */
- ene_hw_write_reg_mask(dev, ENE_FW1,
- ENE_FW1_ENABLE | ENE_FW1_IRQ,
- ENE_FW1_ENABLE | ENE_FW1_IRQ);
- /* clear stats */
- dev->sample = 0;
- return 0;
-}
-
-/* this enables gpio40 signal, used if connected to wide band input*/
-static void ene_enable_gpio40(struct ene_device *dev, int enable)
-{
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
- 0 : ENE_CIR_CONF2_GPIO40DIS,
- ENE_CIR_CONF2_GPIO40DIS);
-}
-
-/* this enables the classic sampler */
-static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
-{
- ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
-}
-
-/* this enables recieve via fan input */
-static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
-{
- if (!enable)
- ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
- else {
- ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
- ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
- }
- dev->fan_input_inuse = enable;
-}
-
-/* determine which input to use*/
-static void ene_set_inputs(struct ene_device *dev, int learning_enable)
-{
- ene_enable_normal_recieve(dev, 1);
-
- /* old hardware doesn't support learning mode for sure */
- if (dev->hw_revision <= ENE_HW_B)
- return;
-
- /* reciever not learning capable, still set gpio40 correctly */
- if (!dev->hw_learning_and_tx_capable) {
- ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
- return;
- }
-
- /* enable learning mode */
- if (learning_enable) {
- ene_enable_gpio40(dev, dev->hw_gpio40_learning);
-
- /* fan input is not used for learning */
- if (dev->hw_fan_as_normal_input)
- ene_enable_fan_recieve(dev, 0);
-
- /* disable learning mode */
- } else {
- if (dev->hw_fan_as_normal_input) {
- ene_enable_fan_recieve(dev, 1);
- ene_enable_normal_recieve(dev, 0);
- } else
- ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
- }
-
- /* set few additional settings for this mode */
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
- ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
-
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
- ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
-}
-
-/* deinitialization */
-static void ene_hw_deinit(void *data)
-{
- struct ene_device *dev = (struct ene_device *)data;
-
- /* disable samplers */
- ene_enable_normal_recieve(dev, 0);
-
- if (dev->hw_fan_as_normal_input)
- ene_enable_fan_recieve(dev, 0);
-
- /* disable hardware IRQ and firmware flag */
- ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
-
- ene_set_idle(dev, 1);
- dev->in_use = 0;
-}
-
-/* sends current sample to userspace */
-static void send_sample(struct ene_device *dev)
-{
- int value = abs(dev->sample) & PULSE_MASK;
-
- if (dev->sample > 0)
- value |= PULSE_BIT;
-
- if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
- lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
- wake_up(&dev->lirc_driver->rbuf->wait_poll);
- }
- dev->sample = 0;
-}
-
-/* this updates current sample */
-static void update_sample(struct ene_device *dev, int sample)
-{
- if (!dev->sample)
- dev->sample = sample;
- else if (same_sign(dev->sample, sample))
- dev->sample += sample;
- else {
- send_sample(dev);
- dev->sample = sample;
- }
-}
-
-/* enable or disable idle mode */
-static void ene_set_idle(struct ene_device *dev, int idle)
-{
- struct timeval now;
- int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
-
- ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
- disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
- ENE_CIR_SAMPLE_OVERFLOW);
- dev->idle = idle;
-
- /* remember when we have entered the idle mode */
- if (idle) {
- do_gettimeofday(&dev->gap_start);
- return;
- }
-
- /* send the gap between keypresses now */
- do_gettimeofday(&now);
-
- if (now.tv_sec - dev->gap_start.tv_sec > 16)
- dev->sample = space(PULSE_MASK);
- else
- dev->sample = dev->sample +
- space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
- + space(now.tv_usec - dev->gap_start.tv_usec);
-
- if (abs(dev->sample) > PULSE_MASK)
- dev->sample = space(PULSE_MASK);
- send_sample(dev);
-}
-
-/* interrupt handler */
-static irqreturn_t ene_hw_irq(int irq, void *data)
-{
- u16 hw_value;
- int i, hw_sample;
- int space;
- int buffer_pointer;
- int irq_status;
-
- struct ene_device *dev = (struct ene_device *)data;
- irq_status = ene_hw_irq_status(dev, &buffer_pointer);
-
- if (!irq_status)
- return IRQ_NONE;
-
- /* TODO: only RX for now */
- if (irq_status == ENE_IRQ_TX)
- return IRQ_HANDLED;
-
- for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
-
- hw_value = ene_hw_read_reg(dev,
- ENE_SAMPLE_BUFFER + buffer_pointer + i);
-
- if (dev->fan_input_inuse) {
- /* read high part of the sample */
- hw_value |= ene_hw_read_reg(dev,
- ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
-
- /* test for _space_ bit */
- space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
-
- /* clear space bit, and other unused bits */
- hw_value &= ENE_FAN_VALUE_MASK;
- hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
-
- } else {
- space = hw_value & ENE_SAMPLE_SPC_MASK;
- hw_value &= ENE_SAMPLE_VALUE_MASK;
- hw_sample = hw_value * sample_period;
- }
-
- /* no more data */
- if (!(hw_value))
- break;
-
- if (space)
- hw_sample *= -1;
-
- /* overflow sample recieved, handle it */
-
- if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
-
- if (dev->idle)
- continue;
-
- if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
- update_sample(dev, hw_sample);
- else
- ene_set_idle(dev, 1);
-
- continue;
- }
-
- /* normal first sample recieved */
- if (!dev->fan_input_inuse && dev->idle) {
- ene_set_idle(dev, 0);
-
- /* discard first recieved value, its random
- since its the time signal was off before
- first pulse if idle mode is enabled, HW
- does that for us */
-
- if (!enable_idle)
- continue;
- }
- update_sample(dev, hw_sample);
- send_sample(dev);
- }
- return IRQ_HANDLED;
-}
-
-static int ene_probe(struct pnp_dev *pnp_dev,
- const struct pnp_device_id *dev_id)
-{
- struct ene_device *dev;
- struct lirc_driver *lirc_driver;
- int error = -ENOMEM;
-
- dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
-
- if (!dev)
- goto err1;
-
- dev->pnp_dev = pnp_dev;
- pnp_set_drvdata(pnp_dev, dev);
-
-
- /* prepare lirc interface */
- error = -ENOMEM;
- lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
-
- if (!lirc_driver)
- goto err2;
-
- dev->lirc_driver = lirc_driver;
-
- strcpy(lirc_driver->name, ENE_DRIVER_NAME);
- lirc_driver->minor = -1;
- lirc_driver->code_length = sizeof(int) * 8;
- lirc_driver->features = LIRC_CAN_REC_MODE2;
- lirc_driver->data = dev;
- lirc_driver->set_use_inc = ene_hw_init;
- lirc_driver->set_use_dec = ene_hw_deinit;
- lirc_driver->dev = &pnp_dev->dev;
- lirc_driver->owner = THIS_MODULE;
-
- lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-
- if (!lirc_driver->rbuf)
- goto err3;
-
- if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
- goto err4;
-
- error = -ENODEV;
- if (lirc_register_driver(lirc_driver))
- goto err5;
-
- /* validate resources */
- if (!pnp_port_valid(pnp_dev, 0) ||
- pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
- goto err6;
-
- if (!pnp_irq_valid(pnp_dev, 0))
- goto err6;
-
- dev->hw_io = pnp_port_start(pnp_dev, 0);
- dev->irq = pnp_irq(pnp_dev, 0);
-
- /* claim the resources */
- error = -EBUSY;
- if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
- goto err6;
-
- if (request_irq(dev->irq, ene_hw_irq,
- IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
- goto err7;
-
- /* detect hardware version and features */
- error = ene_hw_detect(dev);
- if (error)
- goto err8;
-
- ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
- return 0;
-
-err8:
- free_irq(dev->irq, dev);
-err7:
- release_region(dev->hw_io, ENE_MAX_IO);
-err6:
- lirc_unregister_driver(lirc_driver->minor);
-err5:
- lirc_buffer_free(lirc_driver->rbuf);
-err4:
- kfree(lirc_driver->rbuf);
-err3:
- kfree(lirc_driver);
-err2:
- kfree(dev);
-err1:
- return error;
-}
-
-static void ene_remove(struct pnp_dev *pnp_dev)
-{
- struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- ene_hw_deinit(dev);
- free_irq(dev->irq, dev);
- release_region(dev->hw_io, ENE_MAX_IO);
- lirc_unregister_driver(dev->lirc_driver->minor);
- lirc_buffer_free(dev->lirc_driver->rbuf);
- kfree(dev->lirc_driver);
- kfree(dev);
-}
-
-#ifdef CONFIG_PM
-
-/* TODO: make 'wake on IR' configurable and add .shutdown */
-/* currently impossible due to lack of kernel support */
-
-static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
-{
- struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
- return 0;
-}
-
-static int ene_resume(struct pnp_dev *pnp_dev)
-{
- struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- if (dev->in_use)
- ene_hw_init(dev);
-
- ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
- return 0;
-}
-
-#endif
-
-static const struct pnp_device_id ene_ids[] = {
- {.id = "ENE0100",},
- {},
-};
-
-static struct pnp_driver ene_driver = {
- .name = ENE_DRIVER_NAME,
- .id_table = ene_ids,
- .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
-
- .probe = ene_probe,
- .remove = __devexit_p(ene_remove),
-
-#ifdef CONFIG_PM
- .suspend = ene_suspend,
- .resume = ene_resume,
-#endif
-};
-
-static int __init ene_init(void)
-{
- if (sample_period < 5) {
- ene_printk(KERN_ERR, "sample period must be at\n");
- ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
- return -EINVAL;
- }
- return pnp_register_driver(&ene_driver);
-}
-
-static void ene_exit(void)
-{
- pnp_unregister_driver(&ene_driver);
-}
-
-module_param(sample_period, int, S_IRUGO);
-MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
-
-module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(enable_idle,
- "Enables turning off signal sampling after long inactivity time; "
- "if disabled might help detecting input signal (default: enabled)");
-
-module_param(enable_learning, bool, S_IRUGO);
-MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
-
-MODULE_DEVICE_TABLE(pnp, ene_ids);
-MODULE_DESCRIPTION
- ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
-MODULE_AUTHOR("Maxim Levitsky");
-MODULE_LICENSE("GPL");
-
-module_init(ene_init);
-module_exit(ene_exit);
diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c
index 09f3696..ec11c0e 100644
--- a/drivers/staging/lirc/lirc_it87.c
+++ b/drivers/staging/lirc/lirc_it87.c
@@ -109,6 +109,7 @@ static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
static DEFINE_SPINLOCK(hardware_lock);
static DEFINE_SPINLOCK(dev_lock);
+static bool device_open;
static int rx_buf[RBUF_LEN];
unsigned int rx_tail, rx_head;
@@ -147,10 +148,11 @@ static void drop_port(void);
static int lirc_open(struct inode *inode, struct file *file)
{
spin_lock(&dev_lock);
- if (module_refcount(THIS_MODULE)) {
+ if (device_open) {
spin_unlock(&dev_lock);
return -EBUSY;
}
+ device_open = true;
spin_unlock(&dev_lock);
return 0;
}
@@ -158,6 +160,9 @@ static int lirc_open(struct inode *inode, struct file *file)
static int lirc_close(struct inode *inode, struct file *file)
{
+ spin_lock(&dev_lock);
+ device_open = false;
+ spin_unlock(&dev_lock);
return 0;
}
@@ -363,7 +368,6 @@ static struct lirc_driver driver = {
};
-#ifdef MODULE
static int init_chrdev(void)
{
driver.minor = lirc_register_driver(&driver);
@@ -380,7 +384,6 @@ static void drop_chrdev(void)
{
lirc_unregister_driver(driver.minor);
}
-#endif
/* SECTION: Hardware */
diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c
index a1ebd07..6da4a8c 100644
--- a/drivers/staging/lirc/lirc_parallel.c
+++ b/drivers/staging/lirc/lirc_parallel.c
@@ -240,7 +240,7 @@ static void irq_handler(void *blah)
unsigned int level, newlevel;
unsigned int timeout;
- if (!module_refcount(THIS_MODULE))
+ if (!is_open)
return;
if (!is_claimed)
@@ -515,7 +515,7 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
static int lirc_open(struct inode *node, struct file *filep)
{
- if (module_refcount(THIS_MODULE) || !lirc_claim())
+ if (is_open || !lirc_claim())
return -EBUSY;
parport_enable_irq(pport);
diff --git a/drivers/staging/lirc/lirc_streamzap.c b/drivers/staging/lirc/lirc_streamzap.c
deleted file mode 100644
index be09c10..0000000
--- a/drivers/staging/lirc/lirc_streamzap.c
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
- * Streamzap Remote Control driver
- *
- * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
- *
- * This driver was based on the work of Greg Wickham and Adrian
- * Dewhurst. It was substantially rewritten to support correct signal
- * gaps and now maintains a delay buffer, which is used to present
- * consistent timing behaviour to user space applications. Without the
- * delay buffer an ugly hack would be required in lircd, which can
- * cause sluggish signal decoding in certain situations.
- *
- * This driver is based on the USB skeleton driver packaged with the
- * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/completion.h>
-#include <linux/uaccess.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-#define DRIVER_VERSION "1.28"
-#define DRIVER_NAME "lirc_streamzap"
-#define DRIVER_DESC "Streamzap Remote Control driver"
-
-static int debug;
-
-#define USB_STREAMZAP_VENDOR_ID 0x0e9c
-#define USB_STREAMZAP_PRODUCT_ID 0x0000
-
-/* Use our own dbg macro */
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
- fmt "\n", ## args); \
- } while (0)
-
-/* table of devices that work with this driver */
-static struct usb_device_id streamzap_table[] = {
- /* Streamzap Remote Control */
- { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
- /* Terminating entry */
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, streamzap_table);
-
-#define STREAMZAP_PULSE_MASK 0xf0
-#define STREAMZAP_SPACE_MASK 0x0f
-#define STREAMZAP_TIMEOUT 0xff
-#define STREAMZAP_RESOLUTION 256
-
-/* number of samples buffered */
-#define STREAMZAP_BUF_LEN 128
-
-enum StreamzapDecoderState {
- PulseSpace,
- FullPulse,
- FullSpace,
- IgnorePulse
-};
-
-/* Structure to hold all of our device specific stuff
- *
- * some remarks regarding locking:
- * theoretically this struct can be accessed from three threads:
- *
- * - from lirc_dev through set_use_inc/set_use_dec
- *
- * - from the USB layer throuh probe/disconnect/irq
- *
- * Careful placement of lirc_register_driver/lirc_unregister_driver
- * calls will prevent conflicts. lirc_dev makes sure that
- * set_use_inc/set_use_dec are not being executed and will not be
- * called after lirc_unregister_driver returns.
- *
- * - by the timer callback
- *
- * The timer is only running when the device is connected and the
- * LIRC device is open. Making sure the timer is deleted by
- * set_use_dec will make conflicts impossible.
- */
-struct usb_streamzap {
-
- /* usb */
- /* save off the usb device pointer */
- struct usb_device *udev;
- /* the interface for this device */
- struct usb_interface *interface;
-
- /* buffer & dma */
- unsigned char *buf_in;
- dma_addr_t dma_in;
- unsigned int buf_in_len;
-
- struct usb_endpoint_descriptor *endpoint;
-
- /* IRQ */
- struct urb *urb_in;
-
- /* lirc */
- struct lirc_driver *driver;
- struct lirc_buffer *delay_buf;
-
- /* timer used to support delay buffering */
- struct timer_list delay_timer;
- int timer_running;
- spinlock_t timer_lock;
-
- /* tracks whether we are currently receiving some signal */
- int idle;
- /* sum of signal lengths received since signal start */
- unsigned long sum;
- /* start time of signal; necessary for gap tracking */
- struct timeval signal_last;
- struct timeval signal_start;
- enum StreamzapDecoderState decoder_state;
- struct timer_list flush_timer;
- int flush;
- int in_use;
- int timeout_enabled;
-};
-
-
-/* local function prototypes */
-static int streamzap_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void streamzap_disconnect(struct usb_interface *interface);
-static void usb_streamzap_irq(struct urb *urb);
-static int streamzap_use_inc(void *data);
-static void streamzap_use_dec(void *data);
-static long streamzap_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg);
-static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
-static int streamzap_resume(struct usb_interface *intf);
-
-/* usb specific object needed to register this driver with the usb subsystem */
-
-static struct usb_driver streamzap_driver = {
- .name = DRIVER_NAME,
- .probe = streamzap_probe,
- .disconnect = streamzap_disconnect,
- .suspend = streamzap_suspend,
- .resume = streamzap_resume,
- .id_table = streamzap_table,
-};
-
-static void stop_timer(struct usb_streamzap *sz)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sz->timer_lock, flags);
- if (sz->timer_running) {
- sz->timer_running = 0;
- spin_unlock_irqrestore(&sz->timer_lock, flags);
- del_timer_sync(&sz->delay_timer);
- } else {
- spin_unlock_irqrestore(&sz->timer_lock, flags);
- }
-}
-
-static void flush_timeout(unsigned long arg)
-{
- struct usb_streamzap *sz = (struct usb_streamzap *) arg;
-
- /* finally start accepting data */
- sz->flush = 0;
-}
-static void delay_timeout(unsigned long arg)
-{
- unsigned long flags;
- /* deliver data every 10 ms */
- static unsigned long timer_inc =
- (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
- struct usb_streamzap *sz = (struct usb_streamzap *) arg;
- int data;
-
- spin_lock_irqsave(&sz->timer_lock, flags);
-
- if (!lirc_buffer_empty(sz->delay_buf) &&
- !lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
- lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
- }
- if (!lirc_buffer_empty(sz->delay_buf)) {
- while (lirc_buffer_available(sz->delay_buf) <
- STREAMZAP_BUF_LEN / 2 &&
- !lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_read(sz->delay_buf,
- (unsigned char *) &data);
- lirc_buffer_write(sz->driver->rbuf,
- (unsigned char *) &data);
- }
- if (sz->timer_running) {
- sz->delay_timer.expires = jiffies + timer_inc;
- add_timer(&sz->delay_timer);
- }
- } else {
- sz->timer_running = 0;
- }
-
- if (!lirc_buffer_empty(sz->driver->rbuf))
- wake_up(&sz->driver->rbuf->wait_poll);
-
- spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void flush_delay_buffer(struct usb_streamzap *sz)
-{
- int data;
- int empty = 1;
-
- while (!lirc_buffer_empty(sz->delay_buf)) {
- empty = 0;
- lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
- if (!lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_write(sz->driver->rbuf,
- (unsigned char *) &data);
- } else {
- dprintk("buffer overflow", sz->driver->minor);
- }
- }
- if (!empty)
- wake_up(&sz->driver->rbuf->wait_poll);
-}
-
-static void push(struct usb_streamzap *sz, unsigned char *data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sz->timer_lock, flags);
- if (lirc_buffer_full(sz->delay_buf)) {
- int read_data;
-
- lirc_buffer_read(sz->delay_buf,
- (unsigned char *) &read_data);
- if (!lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_write(sz->driver->rbuf,
- (unsigned char *) &read_data);
- } else {
- dprintk("buffer overflow", sz->driver->minor);
- }
- }
-
- lirc_buffer_write(sz->delay_buf, data);
-
- if (!sz->timer_running) {
- sz->delay_timer.expires = jiffies + HZ/10;
- add_timer(&sz->delay_timer);
- sz->timer_running = 1;
- }
-
- spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void push_full_pulse(struct usb_streamzap *sz,
- unsigned char value)
-{
- int pulse;
-
- if (sz->idle) {
- long deltv;
- int tmp;
-
- sz->signal_last = sz->signal_start;
- do_gettimeofday(&sz->signal_start);
-
- deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
- if (deltv > 15) {
- /* really long time */
- tmp = LIRC_SPACE(LIRC_VALUE_MASK);
- } else {
- tmp = (int) (deltv*1000000+
- sz->signal_start.tv_usec -
- sz->signal_last.tv_usec);
- tmp -= sz->sum;
- tmp = LIRC_SPACE(tmp);
- }
- dprintk("ls %u", sz->driver->minor, tmp);
- push(sz, (char *)&tmp);
-
- sz->idle = 0;
- sz->sum = 0;
- }
-
- pulse = ((int) value) * STREAMZAP_RESOLUTION;
- pulse += STREAMZAP_RESOLUTION / 2;
- sz->sum += pulse;
- pulse = LIRC_PULSE(pulse);
-
- dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
- push(sz, (char *)&pulse);
-}
-
-static void push_half_pulse(struct usb_streamzap *sz,
- unsigned char value)
-{
- push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
-}
-
-static void push_full_space(struct usb_streamzap *sz,
- unsigned char value)
-{
- int space;
-
- space = ((int) value)*STREAMZAP_RESOLUTION;
- space += STREAMZAP_RESOLUTION/2;
- sz->sum += space;
- space = LIRC_SPACE(space);
- dprintk("s %u", sz->driver->minor, space);
- push(sz, (char *)&space);
-}
-
-static void push_half_space(struct usb_streamzap *sz,
- unsigned char value)
-{
- push_full_space(sz, value & STREAMZAP_SPACE_MASK);
-}
-
-/**
- * usb_streamzap_irq - IRQ handler
- *
- * This procedure is invoked on reception of data from
- * the usb remote.
- */
-static void usb_streamzap_irq(struct urb *urb)
-{
- struct usb_streamzap *sz;
- int len;
- unsigned int i = 0;
-
- if (!urb)
- return;
-
- sz = urb->context;
- len = urb->actual_length;
-
- switch (urb->status) {
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /*
- * this urb is terminated, clean up.
- * sz might already be invalid at this point
- */
- dprintk("urb status: %d", -1, urb->status);
- return;
- default:
- break;
- }
-
- dprintk("received %d", sz->driver->minor, urb->actual_length);
- if (!sz->flush) {
- for (i = 0; i < urb->actual_length; i++) {
- dprintk("%d: %x", sz->driver->minor,
- i, (unsigned char) sz->buf_in[i]);
- switch (sz->decoder_state) {
- case PulseSpace:
- if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
- STREAMZAP_PULSE_MASK) {
- sz->decoder_state = FullPulse;
- continue;
- } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
- == STREAMZAP_SPACE_MASK) {
- push_half_pulse(sz, sz->buf_in[i]);
- sz->decoder_state = FullSpace;
- continue;
- } else {
- push_half_pulse(sz, sz->buf_in[i]);
- push_half_space(sz, sz->buf_in[i]);
- }
- break;
- case FullPulse:
- push_full_pulse(sz, sz->buf_in[i]);
- sz->decoder_state = IgnorePulse;
- break;
- case FullSpace:
- if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
- sz->idle = 1;
- stop_timer(sz);
- if (sz->timeout_enabled) {
- int timeout =
- LIRC_TIMEOUT
- (STREAMZAP_TIMEOUT *
- STREAMZAP_RESOLUTION);
- push(sz, (char *)&timeout);
- }
- flush_delay_buffer(sz);
- } else
- push_full_space(sz, sz->buf_in[i]);
- sz->decoder_state = PulseSpace;
- break;
- case IgnorePulse:
- if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
- STREAMZAP_SPACE_MASK) {
- sz->decoder_state = FullSpace;
- continue;
- }
- push_half_space(sz, sz->buf_in[i]);
- sz->decoder_state = PulseSpace;
- break;
- }
- }
- }
-
- usb_submit_urb(urb, GFP_ATOMIC);
-
- return;
-}
-
-static const struct file_operations streamzap_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = streamzap_ioctl,
- .read = lirc_dev_fop_read,
- .write = lirc_dev_fop_write,
- .poll = lirc_dev_fop_poll,
- .open = lirc_dev_fop_open,
- .release = lirc_dev_fop_close,
-};
-
-
-/**
- * streamzap_probe
- *
- * Called by usb-core to associated with a candidate device
- * On any failure the return value is the ERROR
- * On success return 0
- */
-static int streamzap_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- struct usb_host_interface *iface_host;
- struct usb_streamzap *sz;
- struct lirc_driver *driver;
- struct lirc_buffer *lirc_buf;
- struct lirc_buffer *delay_buf;
- char buf[63], name[128] = "";
- int retval = -ENOMEM;
- int minor = 0;
-
- /* Allocate space for device driver specific data */
- sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
- if (sz == NULL)
- return -ENOMEM;
-
- sz->udev = udev;
- sz->interface = interface;
-
- /* Check to ensure endpoint information matches requirements */
- iface_host = interface->cur_altsetting;
-
- if (iface_host->desc.bNumEndpoints != 1) {
- err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
- iface_host->desc.bNumEndpoints);
- retval = -ENODEV;
- goto free_sz;
- }
-
- sz->endpoint = &(iface_host->endpoint[0].desc);
- if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- != USB_DIR_IN) {
- err("%s: endpoint doesn't match input device 02%02x",
- __func__, sz->endpoint->bEndpointAddress);
- retval = -ENODEV;
- goto free_sz;
- }
-
- if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- != USB_ENDPOINT_XFER_INT) {
- err("%s: endpoint attributes don't match xfer 02%02x",
- __func__, sz->endpoint->bmAttributes);
- retval = -ENODEV;
- goto free_sz;
- }
-
- if (sz->endpoint->wMaxPacketSize == 0) {
- err("%s: endpoint message size==0? ", __func__);
- retval = -ENODEV;
- goto free_sz;
- }
-
- /* Allocate the USB buffer and IRQ URB */
-
- sz->buf_in_len = sz->endpoint->wMaxPacketSize;
- sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len,
- GFP_ATOMIC, &sz->dma_in);
- if (sz->buf_in == NULL)
- goto free_sz;
-
- sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
- if (sz->urb_in == NULL)
- goto free_sz;
-
- /* Connect this device to the LIRC sub-system */
- driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
- if (!driver)
- goto free_sz;
-
- lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!lirc_buf)
- goto free_driver;
- if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
- goto kfree_lirc_buf;
-
- delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!delay_buf)
- goto free_lirc_buf;
- if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
- goto kfree_delay_buf;
-
- sz->driver = driver;
- strcpy(sz->driver->name, DRIVER_NAME);
- sz->driver->minor = -1;
- sz->driver->sample_rate = 0;
- sz->driver->code_length = sizeof(int) * 8;
- sz->driver->features = LIRC_CAN_REC_MODE2 |
- LIRC_CAN_GET_REC_RESOLUTION |
- LIRC_CAN_SET_REC_TIMEOUT;
- sz->driver->data = sz;
- sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
- sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
- sz->driver->rbuf = lirc_buf;
- sz->delay_buf = delay_buf;
- sz->driver->set_use_inc = &streamzap_use_inc;
- sz->driver->set_use_dec = &streamzap_use_dec;
- sz->driver->fops = &streamzap_fops;
- sz->driver->dev = &interface->dev;
- sz->driver->owner = THIS_MODULE;
-
- sz->idle = 1;
- sz->decoder_state = PulseSpace;
- init_timer(&sz->delay_timer);
- sz->delay_timer.function = delay_timeout;
- sz->delay_timer.data = (unsigned long) sz;
- sz->timer_running = 0;
- spin_lock_init(&sz->timer_lock);
-
- init_timer(&sz->flush_timer);
- sz->flush_timer.function = flush_timeout;
- sz->flush_timer.data = (unsigned long) sz;
- /* Complete final initialisations */
-
- usb_fill_int_urb(sz->urb_in, udev,
- usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
- sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
- sz->endpoint->bInterval);
- sz->urb_in->transfer_dma = sz->dma_in;
- sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- if (udev->descriptor.iManufacturer
- && usb_string(udev, udev->descriptor.iManufacturer,
- buf, sizeof(buf)) > 0)
- strlcpy(name, buf, sizeof(name));
-
- if (udev->descriptor.iProduct
- && usb_string(udev, udev->descriptor.iProduct,
- buf, sizeof(buf)) > 0)
- snprintf(name + strlen(name), sizeof(name) - strlen(name),
- " %s", buf);
-
- minor = lirc_register_driver(driver);
-
- if (minor < 0)
- goto free_delay_buf;
-
- sz->driver->minor = minor;
-
- usb_set_intfdata(interface, sz);
-
- printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
- sz->driver->minor, name,
- udev->bus->busnum, sz->udev->devnum);
-
- return 0;
-
-free_delay_buf:
- lirc_buffer_free(sz->delay_buf);
-kfree_delay_buf:
- kfree(delay_buf);
-free_lirc_buf:
- lirc_buffer_free(sz->driver->rbuf);
-kfree_lirc_buf:
- kfree(lirc_buf);
-free_driver:
- kfree(driver);
-free_sz:
- if (retval == -ENOMEM)
- err("Out of memory");
-
- if (sz) {
- usb_free_urb(sz->urb_in);
- usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
- kfree(sz);
- }
-
- return retval;
-}
-
-static int streamzap_use_inc(void *data)
-{
- struct usb_streamzap *sz = data;
-
- if (!sz) {
- dprintk("%s called with no context", -1, __func__);
- return -EINVAL;
- }
- dprintk("set use inc", sz->driver->minor);
-
- lirc_buffer_clear(sz->driver->rbuf);
- lirc_buffer_clear(sz->delay_buf);
-
- sz->flush_timer.expires = jiffies + HZ;
- sz->flush = 1;
- add_timer(&sz->flush_timer);
-
- sz->urb_in->dev = sz->udev;
- if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
- dprintk("open result = -EIO error submitting urb",
- sz->driver->minor);
- return -EIO;
- }
- sz->in_use++;
-
- return 0;
-}
-
-static void streamzap_use_dec(void *data)
-{
- struct usb_streamzap *sz = data;
-
- if (!sz) {
- dprintk("%s called with no context", -1, __func__);
- return;
- }
- dprintk("set use dec", sz->driver->minor);
-
- if (sz->flush) {
- sz->flush = 0;
- del_timer_sync(&sz->flush_timer);
- }
-
- usb_kill_urb(sz->urb_in);
-
- stop_timer(sz);
-
- sz->in_use--;
-}
-
-static long streamzap_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg)
-{
- int result = 0;
- int val;
- struct usb_streamzap *sz = lirc_get_pdata(filep);
-
- switch (cmd) {
- case LIRC_GET_REC_RESOLUTION:
- result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
- break;
- case LIRC_SET_REC_TIMEOUT:
- result = get_user(val, (int *)arg);
- if (result == 0) {
- if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION)
- sz->timeout_enabled = 1;
- else if (val == 0)
- sz->timeout_enabled = 0;
- else
- result = -EINVAL;
- }
- break;
- default:
- return lirc_dev_fop_ioctl(filep, cmd, arg);
- }
- return result;
-}
-
-/**
- * streamzap_disconnect
- *
- * Called by the usb core when the device is removed from the system.
- *
- * This routine guarantees that the driver will not submit any more urbs
- * by clearing dev->udev. It is also supposed to terminate any currently
- * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
- * does not provide any way to do this.
- */
-static void streamzap_disconnect(struct usb_interface *interface)
-{
- struct usb_streamzap *sz;
- int errnum;
- int minor;
-
- sz = usb_get_intfdata(interface);
-
- /* unregister from the LIRC sub-system */
-
- errnum = lirc_unregister_driver(sz->driver->minor);
- if (errnum != 0)
- dprintk("error in lirc_unregister: (returned %d)",
- sz->driver->minor, errnum);
-
- lirc_buffer_free(sz->delay_buf);
- lirc_buffer_free(sz->driver->rbuf);
-
- /* unregister from the USB sub-system */
-
- usb_free_urb(sz->urb_in);
-
- usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
-
- minor = sz->driver->minor;
- kfree(sz->driver->rbuf);
- kfree(sz->driver);
- kfree(sz->delay_buf);
- kfree(sz);
-
- printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
-}
-
-static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct usb_streamzap *sz = usb_get_intfdata(intf);
-
- printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
- if (sz->in_use) {
- if (sz->flush) {
- sz->flush = 0;
- del_timer_sync(&sz->flush_timer);
- }
-
- stop_timer(sz);
-
- usb_kill_urb(sz->urb_in);
- }
- return 0;
-}
-
-static int streamzap_resume(struct usb_interface *intf)
-{
- struct usb_streamzap *sz = usb_get_intfdata(intf);
-
- lirc_buffer_clear(sz->driver->rbuf);
- lirc_buffer_clear(sz->delay_buf);
-
- if (sz->in_use) {
- sz->flush_timer.expires = jiffies + HZ;
- sz->flush = 1;
- add_timer(&sz->flush_timer);
-
- sz->urb_in->dev = sz->udev;
- if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
- dprintk("open result = -EIO error submitting urb",
- sz->driver->minor);
- return -EIO;
- }
- }
- return 0;
-}
-
-/**
- * usb_streamzap_init
- */
-static int __init usb_streamzap_init(void)
-{
- int result;
-
- /* register this driver with the USB subsystem */
- result = usb_register(&streamzap_driver);
-
- if (result) {
- err("usb_register failed. Error number %d",
- result);
- return result;
- }
-
- printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
- return 0;
-}
-
-/**
- * usb_streamzap_exit
- */
-static void __exit usb_streamzap_exit(void)
-{
- usb_deregister(&streamzap_driver);
-}
-
-
-module_init(usb_streamzap_init);
-module_exit(usb_streamzap_exit);
-
-MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c
index 49ab9fa..ed7457b 100644
--- a/drivers/staging/rtl8187se/r8180_core.c
+++ b/drivers/staging/rtl8187se/r8180_core.c
@@ -61,7 +61,7 @@ static struct pci_device_id rtl8180_pci_id_tbl[] __devinitdata = {
};
-static char *ifname = "wlan%d";
+static char ifname[IFNAMSIZ] = "wlan%d";
static int hwseqnum = 0;
static int hwwep = 0;
static int channels = 0x3fff;
@@ -72,7 +72,7 @@ MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
MODULE_DESCRIPTION("Linux driver for Realtek RTL8180 / RTL8185 WiFi cards");
-module_param(ifname, charp, S_IRUGO|S_IWUSR);
+module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
module_param(hwseqnum, int, S_IRUGO|S_IWUSR);
module_param(hwwep, int, S_IRUGO|S_IWUSR);
module_param(channels, int, S_IRUGO|S_IWUSR);
@@ -3609,7 +3609,7 @@ static int __devinit rtl8180_pci_probe(struct pci_dev *pdev,
if (dev_alloc_name(dev, ifname) < 0) {
DMESG("Oops: devname already taken! Trying wlan%%d...\n");
- ifname = "wlan%d";
+ strcpy(ifname, "wlan%d");
dev_alloc_name(dev, ifname);
}
diff --git a/drivers/staging/rtl8192e/r8192E_core.c b/drivers/staging/rtl8192e/r8192E_core.c
index 4cd071a..17a806f 100644
--- a/drivers/staging/rtl8192e/r8192E_core.c
+++ b/drivers/staging/rtl8192e/r8192E_core.c
@@ -112,7 +112,7 @@ static const struct pci_device_id rtl8192_pci_id_tbl[] __devinitdata = {
{}
};
-static char* ifname = "wlan%d";
+static char ifname[IFNAMSIZ] = "wlan%d";
static int hwwep = 1; //default use hw. set 0 to use software security
static int channels = 0x3fff;
@@ -123,7 +123,7 @@ MODULE_DEVICE_TABLE(pci, rtl8192_pci_id_tbl);
MODULE_DESCRIPTION("Linux driver for Realtek RTL819x WiFi cards");
-module_param(ifname, charp, S_IRUGO|S_IWUSR );
+module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
//module_param(hwseqnum,int, S_IRUGO|S_IWUSR);
module_param(hwwep,int, S_IRUGO|S_IWUSR);
module_param(channels,int, S_IRUGO|S_IWUSR);
@@ -6446,7 +6446,7 @@ static int __devinit rtl8192_pci_probe(struct pci_dev *pdev,
if (dev_alloc_name(dev, ifname) < 0){
RT_TRACE(COMP_INIT, "Oops: devname already taken! Trying wlan%%d...\n");
- ifname = "wlan%d";
+ strcpy(ifname, "wlan%d");
dev_alloc_name(dev, ifname);
}
diff --git a/drivers/staging/rtl8192su/r8192U_core.c b/drivers/staging/rtl8192su/r8192U_core.c
index 6970c97..df5b52b 100644
--- a/drivers/staging/rtl8192su/r8192U_core.c
+++ b/drivers/staging/rtl8192su/r8192U_core.c
@@ -144,13 +144,13 @@ MODULE_VERSION("V 1.1");
MODULE_DEVICE_TABLE(usb, rtl8192_usb_id_tbl);
MODULE_DESCRIPTION("Linux driver for Realtek RTL8192 USB WiFi cards");
-static char* ifname = "wlan%d";
+static char ifname[IFNAMSIZ] = "wlan%d";
static int hwwep = 1; //default use hw. set 0 to use software security
static int channels = 0x3fff;
-module_param(ifname, charp, S_IRUGO|S_IWUSR );
+module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
//module_param(hwseqnum,int, S_IRUGO|S_IWUSR);
module_param(hwwep,int, S_IRUGO|S_IWUSR);
module_param(channels,int, S_IRUGO|S_IWUSR);
@@ -7406,7 +7406,7 @@ static int __devinit rtl8192_usb_probe(struct usb_interface *intf,
if (dev_alloc_name(dev, ifname) < 0){
RT_TRACE(COMP_INIT, "Oops: devname already taken! Trying wlan%%d...\n");
- ifname = "wlan%d";
+ strcpy(ifname, "wlan%d");
dev_alloc_name(dev, ifname);
}
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index be5d8db..0574d84 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -215,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
vhci = hcd_to_vhci(hcd);
spin_lock_irqsave(&vhci->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
usbip_dbg_vhci_rh("hw accessible flag in on?\n");
goto done;
}
@@ -269,7 +269,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u32 prev_port_status[VHCI_NPORTS];
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
/*
@@ -1041,7 +1041,7 @@ static int vhci_bus_resume(struct usb_hcd *hcd)
dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
spin_lock_irq(&vhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
} else {
/* vhci->rh_state = DUMMY_RH_RUNNING;
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 80b4008..239f050 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -41,7 +41,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/
obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB) += misc/
-obj-y += early/
+obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 101ffc9..593fc5e 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -564,7 +564,7 @@ static void cxacru_timeout_kill(unsigned long data)
}
static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
- int* actual_length)
+ int *actual_length)
{
struct timer_list timer;
@@ -952,7 +952,7 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb));
offb += 4;
addr += l;
- if(l)
+ if (l)
memcpy(buf + offb, data + offd, l);
if (l < stride)
memset(buf + offb + l, 0, stride - l);
@@ -967,7 +967,7 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
}
offb = 0;
}
- } while(offd < size);
+ } while (offd < size);
dbg("sent fw %#x", fw);
ret = 0;
@@ -1043,8 +1043,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
if (instance->modem_type->boot_rom_patch) {
val = cpu_to_le32(BR_ADDR);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4);
- }
- else {
+ } else {
ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0);
}
if (ret) {
@@ -1068,7 +1067,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
}
static int cxacru_find_firmware(struct cxacru_data *instance,
- char* phase, const struct firmware **fw_p)
+ char *phase, const struct firmware **fw_p)
{
struct usbatm_data *usbatm = instance->usbatm;
struct device *dev = &usbatm->usb_intf->dev;
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index 80f9617..4716e70 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -753,11 +753,13 @@ static struct usb_driver speedtch_usb_driver = {
.id_table = speedtch_usb_ids
};
-static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) {
+static void speedtch_release_interfaces(struct usb_device *usb_dev,
+ int num_interfaces)
+{
struct usb_interface *cur_intf;
int i;
- for(i = 0; i < num_interfaces; i++)
+ for (i = 0; i < num_interfaces; i++)
if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
usb_set_intfdata(cur_intf, NULL);
usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
@@ -792,7 +794,7 @@ static int speedtch_bind(struct usbatm_data *usbatm,
/* claim all interfaces */
- for (i=0; i < num_interfaces; i++) {
+ for (i = 0; i < num_interfaces; i++) {
cur_intf = usb_ifnum_to_if(usb_dev, i);
if ((i != ifnum) && cur_intf) {
@@ -842,7 +844,7 @@ static int speedtch_bind(struct usbatm_data *usbatm,
use_isoc = 0; /* fall back to bulk if endpoint not found */
- for (i=0; i<desc->desc.bNumEndpoints; i++) {
+ for (i = 0; i < desc->desc.bNumEndpoints; i++) {
const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc;
if ((endpoint_desc->bEndpointAddress == target_address)) {
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index ebae944..ea071a5 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -67,6 +67,7 @@
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <linux/slab.h>
+#include <linux/kernel.h>
#include <asm/unaligned.h>
@@ -1576,6 +1577,7 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
char file_arr[] = "CMVxy.bin";
char *file;
+ kparam_block_sysfs_write(cmv_file);
/* set proper name corresponding modem version and line type */
if (cmv_file[sc->modem_index] == NULL) {
if (UEA_CHIP_VERSION(sc) == ADI930)
@@ -1594,6 +1596,7 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
strlcat(cmv_name, file, UEA_FW_NAME_MAX);
if (ver == 2)
strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX);
+ kparam_unblock_sysfs_write(cmv_file);
}
static int request_cmvs_old(struct uea_softc *sc,
@@ -2436,7 +2439,6 @@ UEA_ATTR(firmid, 0);
/* Retrieve the device End System Identifier (MAC) */
-#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10)
static int uea_getesi(struct uea_softc *sc, u_char * esi)
{
unsigned char mac_str[2 * ETH_ALEN + 1];
@@ -2447,7 +2449,8 @@ static int uea_getesi(struct uea_softc *sc, u_char * esi)
return 1;
for (i = 0; i < ETH_ALEN; i++)
- esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
+ esi[i] = hex_to_bin(mac_str[2 * i]) * 16 +
+ hex_to_bin(mac_str[2 * i + 1]);
return 0;
}
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 9b53e8d..05bf5a2 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -84,8 +84,8 @@
#ifdef VERBOSE_DEBUG
static int usbatm_print_packet(const unsigned char *data, int len);
-#define PACKETDEBUG(arg...) usbatm_print_packet (arg)
-#define vdbg(arg...) dbg (arg)
+#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
+#define vdbg(arg...) dbg(arg)
#else
#define PACKETDEBUG(arg...)
#define vdbg(arg...)
@@ -273,8 +273,7 @@ static void usbatm_complete(struct urb *urb)
if (unlikely(status) &&
(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
- status != -EILSEQ ))
- {
+ status != -EILSEQ)) {
if (status == -ESHUTDOWN)
return;
@@ -494,7 +493,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
ptr += data_len;
__skb_pull(skb, data_len);
- if(!left)
+ if (!left)
continue;
memset(ptr, 0, left);
@@ -506,7 +505,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
trailer[2] = ctrl->len >> 8;
trailer[3] = ctrl->len;
- ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
+ ctrl->crc = ~crc32_be(ctrl->crc, ptr, left - 4);
trailer[4] = ctrl->crc >> 24;
trailer[5] = ctrl->crc >> 16;
@@ -516,8 +515,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
target[3] |= 0x2; /* adjust PTI */
ctrl->len = 0; /* tag this skb finished */
- }
- else
+ } else
ctrl->crc = crc32_be(ctrl->crc, ptr, left);
}
@@ -1146,7 +1144,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->bulk_out);
/* tx buffer size must be a positive multiple of the stride */
- instance->tx_channel.buf_size = max (instance->tx_channel.stride,
+ instance->tx_channel.buf_size = max(instance->tx_channel.stride,
snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride));
/* rx buffer size must be a positive multiple of the endpoint maxpacket */
@@ -1159,7 +1157,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
goto fail_unbind;
}
- num_packets = max (1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
+ num_packets = max(1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
if (num_packets * maxpacket > UDSL_MAX_BUF_SIZE)
num_packets--;
@@ -1262,7 +1260,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
usb_free_urb(instance->urbs[i]);
}
- kfree (instance);
+ kfree(instance);
return error;
}
@@ -1390,9 +1388,8 @@ static int usbatm_print_packet(const unsigned char *data, int len)
for (i = 0; i < len;) {
buffer[0] = '\0';
sprintf(buffer, "%.3d :", i);
- for (j = 0; (j < 16) && (i < len); j++, i++) {
+ for (j = 0; (j < 16) && (i < len); j++, i++)
sprintf(buffer, "%s %2.2x", buffer, data[i]);
- }
dbg("%s", buffer);
}
return i;
diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h
index 0863f85..5fc4894 100644
--- a/drivers/usb/atm/usbatm.h
+++ b/drivers/usb/atm/usbatm.h
@@ -48,7 +48,7 @@
dev_warn(&(instance)->usb_intf->dev, \
"failed assertion '%s' at line %d", \
__stringify(x), __LINE__); \
- } while(0)
+ } while (0)
#endif
#define usb_err(instance, format, arg...) \
@@ -59,7 +59,7 @@
dev_warn(&(instance)->usb_intf->dev , format , ## arg)
#ifdef DEBUG
#define usb_dbg(instance, format, arg...) \
- dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
+ dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
#else
#define usb_dbg(instance, format, arg...) \
do {} while (0)
@@ -104,21 +104,21 @@ struct usbatm_data;
/*
* Assuming all methods exist and succeed, they are called in this order:
*
-* bind, heavy_init, atm_start, ..., atm_stop, unbind
+* bind, heavy_init, atm_start, ..., atm_stop, unbind
*/
struct usbatm_driver {
const char *driver_name;
/* init device ... can sleep, or cause probe() failure */
- int (*bind) (struct usbatm_data *, struct usb_interface *,
+ int (*bind) (struct usbatm_data *, struct usb_interface *,
const struct usb_device_id *id);
/* additional device initialization that is too slow to be done in probe() */
- int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
+ int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
/* cleanup device ... can sleep, but can't fail */
- void (*unbind) (struct usbatm_data *, struct usb_interface *);
+ void (*unbind) (struct usbatm_data *, struct usb_interface *);
/* init ATM device ... can sleep, or cause ATM initialization failure */
int (*atm_start) (struct usbatm_data *, struct atm_dev *);
@@ -126,9 +126,9 @@ struct usbatm_driver {
/* cleanup ATM device ... can sleep, but can't fail */
void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
- int bulk_in; /* bulk rx endpoint */
- int isoc_in; /* isochronous rx endpoint */
- int bulk_out; /* bulk tx endpoint */
+ int bulk_in; /* bulk rx endpoint */
+ int isoc_in; /* isochronous rx endpoint */
+ int bulk_out; /* bulk tx endpoint */
unsigned rx_padding;
unsigned tx_padding;
@@ -156,7 +156,7 @@ struct usbatm_channel {
struct usbatm_data {
/******************
* public fields *
- ******************/
+ ******************/
/* mini driver */
struct usbatm_driver *driver;
@@ -174,7 +174,7 @@ struct usbatm_data {
/********************************
* private fields - do not use *
- ********************************/
+ ********************************/
struct kref refcount;
struct mutex serialize;
diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c
index 17d167b..48ee0c5 100644
--- a/drivers/usb/atm/xusbatm.c
+++ b/drivers/usb/atm/xusbatm.c
@@ -49,13 +49,13 @@ static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX];
static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
static struct usb_driver xusbatm_usb_driver;
-static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int altsetting, u8 ep)
+static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep)
{
struct usb_host_interface *alt;
struct usb_interface *intf;
int i, j;
- for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
+ for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting)))
for (j = 0; j < alt->desc.bNumEndpoints; j++)
if (alt->endpoint[j].desc.bEndpointAddress == ep)
@@ -63,7 +63,7 @@ static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int
return NULL;
}
-static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *usb_dev,
+static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev,
struct usb_interface *intf, int altsetting, int claim)
{
int ifnum = intf->altsetting->desc.bInterfaceNumber;
@@ -80,7 +80,7 @@ static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *
return 0;
}
-static void xusbatm_release_intf (struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
+static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
{
if (claimed) {
usb_set_intfdata(intf, NULL);
@@ -147,7 +147,7 @@ static void xusbatm_unbind(struct usbatm_data *usbatm,
usb_dbg(usbatm, "%s entered\n", __func__);
- for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
+ for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *cur_intf = usb_dev->actconfig->interface[i];
if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) {
diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c
index a22b887..d3e1356 100644
--- a/drivers/usb/c67x00/c67x00-hcd.c
+++ b/drivers/usb/c67x00/c67x00-hcd.c
@@ -264,7 +264,7 @@ static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
if (unlikely(hcd->state == HC_STATE_HALT))
return;
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
return;
/* Handle Start of frame events */
@@ -282,7 +282,7 @@ static int c67x00_hcd_start(struct usb_hcd *hcd)
{
hcd->uses_new_polling = 1;
hcd->state = HC_STATE_RUNNING;
- hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
return 0;
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 89d260d..1833b3a 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -636,19 +636,13 @@ static void acm_tty_unregister(struct acm *acm)
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
-static void acm_port_down(struct acm *acm, int drain)
+static void acm_port_down(struct acm *acm)
{
int i, nr = acm->rx_buflimit;
mutex_lock(&open_mutex);
if (acm->dev) {
usb_autopm_get_interface(acm->control);
acm_set_control(acm, acm->ctrlout = 0);
- /* try letting the last writes drain naturally */
- if (drain) {
- wait_event_interruptible_timeout(acm->drain_wait,
- (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
- ACM_CLOSE_TIMEOUT * HZ);
- }
usb_kill_urb(acm->ctrlurb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
@@ -664,7 +658,7 @@ static void acm_tty_hangup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
tty_port_hangup(&acm->port);
- acm_port_down(acm, 0);
+ acm_port_down(acm);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
@@ -685,7 +679,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
mutex_unlock(&open_mutex);
return;
}
- acm_port_down(acm, 0);
+ acm_port_down(acm);
tty_port_close_end(&acm->port, tty);
tty_port_tty_set(&acm->port, NULL);
}
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 84f9e52..e325162 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -135,7 +135,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
* ->lock locks what interrupt accesses.
*/
struct usblp {
- struct usb_device *dev; /* USB device */
+ struct usb_device *dev; /* USB device */
struct mutex wmut;
struct mutex mut;
spinlock_t lock; /* locks rcomplete, wcomplete */
@@ -169,7 +169,8 @@ struct usblp {
};
#ifdef DEBUG
-static void usblp_dump(struct usblp *usblp) {
+static void usblp_dump(struct usblp *usblp)
+{
int p;
dbg("usblp=0x%p", usblp);
@@ -216,8 +217,8 @@ static const struct quirk_printer_struct quirk_printers[] = {
{ 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
{ 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
{ 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
- { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
- { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
+ { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
+ { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
{ 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
{ 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
{ 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
@@ -254,9 +255,8 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
/* High byte has the interface index.
Low byte has the alternate setting.
*/
- if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) {
- index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
- }
+ if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS))
+ index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
retval = usb_control_msg(usblp->dev,
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
@@ -372,7 +372,7 @@ static int usblp_check_status(struct usblp *usblp, int err)
return newerr;
}
-static int handle_bidir (struct usblp *usblp)
+static int handle_bidir(struct usblp *usblp)
{
if (usblp->bidir && usblp->used) {
if (usblp_submit_read(usblp) < 0)
@@ -395,14 +395,13 @@ static int usblp_open(struct inode *inode, struct file *file)
if (minor < 0)
return -ENODEV;
- mutex_lock (&usblp_mutex);
+ mutex_lock(&usblp_mutex);
retval = -ENODEV;
intf = usb_find_interface(&usblp_driver, minor);
- if (!intf) {
+ if (!intf)
goto out;
- }
- usblp = usb_get_intfdata (intf);
+ usblp = usb_get_intfdata(intf);
if (!usblp || !usblp->dev || !usblp->present)
goto out;
@@ -433,18 +432,18 @@ static int usblp_open(struct inode *inode, struct file *file)
retval = -EIO;
}
out:
- mutex_unlock (&usblp_mutex);
+ mutex_unlock(&usblp_mutex);
return retval;
}
-static void usblp_cleanup (struct usblp *usblp)
+static void usblp_cleanup(struct usblp *usblp)
{
printk(KERN_INFO "usblp%d: removed\n", usblp->minor);
kfree(usblp->readbuf);
- kfree (usblp->device_id_string);
- kfree (usblp->statusbuf);
- kfree (usblp);
+ kfree(usblp->device_id_string);
+ kfree(usblp->statusbuf);
+ kfree(usblp);
}
static void usblp_unlink_urbs(struct usblp *usblp)
@@ -458,14 +457,14 @@ static int usblp_release(struct inode *inode, struct file *file)
usblp->flags &= ~LP_ABORT;
- mutex_lock (&usblp_mutex);
+ mutex_lock(&usblp_mutex);
usblp->used = 0;
if (usblp->present) {
usblp_unlink_urbs(usblp);
usb_autopm_put_interface(usblp->intf);
- } else /* finish cleanup from disconnect */
- usblp_cleanup (usblp);
- mutex_unlock (&usblp_mutex);
+ } else /* finish cleanup from disconnect */
+ usblp_cleanup(usblp);
+ mutex_unlock(&usblp_mutex);
return 0;
}
@@ -495,190 +494,190 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
int twoints[2];
int retval = 0;
- mutex_lock (&usblp->mut);
+ mutex_lock(&usblp->mut);
if (!usblp->present) {
retval = -ENODEV;
goto done;
}
dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd),
- _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd) );
+ _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
switch (_IOC_NR(cmd)) {
- case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
- if (_IOC_DIR(cmd) != _IOC_READ) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+ if (_IOC_DIR(cmd) != _IOC_READ) {
+ retval = -EINVAL;
+ goto done;
+ }
- length = usblp_cache_device_id_string(usblp);
- if (length < 0) {
- retval = length;
- goto done;
- }
- if (length > _IOC_SIZE(cmd))
- length = _IOC_SIZE(cmd); /* truncate */
-
- if (copy_to_user((void __user *) arg,
- usblp->device_id_string,
- (unsigned long) length)) {
- retval = -EFAULT;
- goto done;
- }
+ length = usblp_cache_device_id_string(usblp);
+ if (length < 0) {
+ retval = length;
+ goto done;
+ }
+ if (length > _IOC_SIZE(cmd))
+ length = _IOC_SIZE(cmd); /* truncate */
+
+ if (copy_to_user((void __user *) arg,
+ usblp->device_id_string,
+ (unsigned long) length)) {
+ retval = -EFAULT;
+ goto done;
+ }
- break;
+ break;
- case IOCNR_GET_PROTOCOLS:
- if (_IOC_DIR(cmd) != _IOC_READ ||
- _IOC_SIZE(cmd) < sizeof(twoints)) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_PROTOCOLS:
+ if (_IOC_DIR(cmd) != _IOC_READ ||
+ _IOC_SIZE(cmd) < sizeof(twoints)) {
+ retval = -EINVAL;
+ goto done;
+ }
- twoints[0] = usblp->current_protocol;
- twoints[1] = 0;
- for (i = USBLP_FIRST_PROTOCOL;
- i <= USBLP_LAST_PROTOCOL; i++) {
- if (usblp->protocol[i].alt_setting >= 0)
- twoints[1] |= (1<<i);
- }
+ twoints[0] = usblp->current_protocol;
+ twoints[1] = 0;
+ for (i = USBLP_FIRST_PROTOCOL;
+ i <= USBLP_LAST_PROTOCOL; i++) {
+ if (usblp->protocol[i].alt_setting >= 0)
+ twoints[1] |= (1<<i);
+ }
- if (copy_to_user((void __user *)arg,
- (unsigned char *)twoints,
- sizeof(twoints))) {
- retval = -EFAULT;
- goto done;
- }
+ if (copy_to_user((void __user *)arg,
+ (unsigned char *)twoints,
+ sizeof(twoints))) {
+ retval = -EFAULT;
+ goto done;
+ }
- break;
+ break;
- case IOCNR_SET_PROTOCOL:
- if (_IOC_DIR(cmd) != _IOC_WRITE) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_SET_PROTOCOL:
+ if (_IOC_DIR(cmd) != _IOC_WRITE) {
+ retval = -EINVAL;
+ goto done;
+ }
#ifdef DEBUG
- if (arg == -10) {
- usblp_dump(usblp);
- break;
- }
+ if (arg == -10) {
+ usblp_dump(usblp);
+ break;
+ }
#endif
- usblp_unlink_urbs(usblp);
- retval = usblp_set_protocol(usblp, arg);
- if (retval < 0) {
- usblp_set_protocol(usblp,
- usblp->current_protocol);
- }
- break;
+ usblp_unlink_urbs(usblp);
+ retval = usblp_set_protocol(usblp, arg);
+ if (retval < 0) {
+ usblp_set_protocol(usblp,
+ usblp->current_protocol);
+ }
+ break;
- case IOCNR_HP_SET_CHANNEL:
- if (_IOC_DIR(cmd) != _IOC_WRITE ||
- le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
- usblp->quirks & USBLP_QUIRK_BIDIR) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_HP_SET_CHANNEL:
+ if (_IOC_DIR(cmd) != _IOC_WRITE ||
+ le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
+ usblp->quirks & USBLP_QUIRK_BIDIR) {
+ retval = -EINVAL;
+ goto done;
+ }
- err = usblp_hp_channel_change_request(usblp,
- arg, &newChannel);
- if (err < 0) {
- dev_err(&usblp->dev->dev,
- "usblp%d: error = %d setting "
- "HP channel\n",
- usblp->minor, err);
- retval = -EIO;
- goto done;
- }
+ err = usblp_hp_channel_change_request(usblp,
+ arg, &newChannel);
+ if (err < 0) {
+ dev_err(&usblp->dev->dev,
+ "usblp%d: error = %d setting "
+ "HP channel\n",
+ usblp->minor, err);
+ retval = -EIO;
+ goto done;
+ }
- dbg("usblp%d requested/got HP channel %ld/%d",
- usblp->minor, arg, newChannel);
- break;
+ dbg("usblp%d requested/got HP channel %ld/%d",
+ usblp->minor, arg, newChannel);
+ break;
- case IOCNR_GET_BUS_ADDRESS:
- if (_IOC_DIR(cmd) != _IOC_READ ||
- _IOC_SIZE(cmd) < sizeof(twoints)) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_BUS_ADDRESS:
+ if (_IOC_DIR(cmd) != _IOC_READ ||
+ _IOC_SIZE(cmd) < sizeof(twoints)) {
+ retval = -EINVAL;
+ goto done;
+ }
- twoints[0] = usblp->dev->bus->busnum;
- twoints[1] = usblp->dev->devnum;
- if (copy_to_user((void __user *)arg,
- (unsigned char *)twoints,
- sizeof(twoints))) {
- retval = -EFAULT;
- goto done;
- }
+ twoints[0] = usblp->dev->bus->busnum;
+ twoints[1] = usblp->dev->devnum;
+ if (copy_to_user((void __user *)arg,
+ (unsigned char *)twoints,
+ sizeof(twoints))) {
+ retval = -EFAULT;
+ goto done;
+ }
- dbg("usblp%d is bus=%d, device=%d",
- usblp->minor, twoints[0], twoints[1]);
- break;
+ dbg("usblp%d is bus=%d, device=%d",
+ usblp->minor, twoints[0], twoints[1]);
+ break;
- case IOCNR_GET_VID_PID:
- if (_IOC_DIR(cmd) != _IOC_READ ||
- _IOC_SIZE(cmd) < sizeof(twoints)) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_VID_PID:
+ if (_IOC_DIR(cmd) != _IOC_READ ||
+ _IOC_SIZE(cmd) < sizeof(twoints)) {
+ retval = -EINVAL;
+ goto done;
+ }
- twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
- twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
- if (copy_to_user((void __user *)arg,
- (unsigned char *)twoints,
- sizeof(twoints))) {
- retval = -EFAULT;
- goto done;
- }
+ twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
+ twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
+ if (copy_to_user((void __user *)arg,
+ (unsigned char *)twoints,
+ sizeof(twoints))) {
+ retval = -EFAULT;
+ goto done;
+ }
- dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
- usblp->minor, twoints[0], twoints[1]);
- break;
+ dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
+ usblp->minor, twoints[0], twoints[1]);
+ break;
- case IOCNR_SOFT_RESET:
- if (_IOC_DIR(cmd) != _IOC_NONE) {
- retval = -EINVAL;
- goto done;
- }
- retval = usblp_reset(usblp);
- break;
- default:
- retval = -ENOTTY;
+ case IOCNR_SOFT_RESET:
+ if (_IOC_DIR(cmd) != _IOC_NONE) {
+ retval = -EINVAL;
+ goto done;
+ }
+ retval = usblp_reset(usblp);
+ break;
+ default:
+ retval = -ENOTTY;
}
else /* old-style ioctl value */
switch (cmd) {
- case LPGETSTATUS:
- if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
- if (printk_ratelimit())
- printk(KERN_ERR "usblp%d:"
- "failed reading printer status (%d)\n",
- usblp->minor, retval);
- retval = -EIO;
- goto done;
- }
- status = *usblp->statusbuf;
- if (copy_to_user ((void __user *)arg, &status, sizeof(int)))
- retval = -EFAULT;
- break;
+ case LPGETSTATUS:
+ if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "usblp%d:"
+ "failed reading printer status (%d)\n",
+ usblp->minor, retval);
+ retval = -EIO;
+ goto done;
+ }
+ status = *usblp->statusbuf;
+ if (copy_to_user((void __user *)arg, &status, sizeof(int)))
+ retval = -EFAULT;
+ break;
- case LPABORT:
- if (arg)
- usblp->flags |= LP_ABORT;
- else
- usblp->flags &= ~LP_ABORT;
- break;
+ case LPABORT:
+ if (arg)
+ usblp->flags |= LP_ABORT;
+ else
+ usblp->flags &= ~LP_ABORT;
+ break;
- default:
- retval = -ENOTTY;
+ default:
+ retval = -ENOTTY;
}
done:
- mutex_unlock (&usblp->mut);
+ mutex_unlock(&usblp->mut);
return retval;
}
@@ -840,7 +839,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, lo
}
done:
- mutex_unlock (&usblp->mut);
+ mutex_unlock(&usblp->mut);
return count;
}
@@ -1023,7 +1022,7 @@ raise_urb:
* while you are sending print data, and you don't try to query the
* printer status every couple of milliseconds, you will probably be OK.
*/
-static unsigned int usblp_quirks (__u16 vendor, __u16 product)
+static unsigned int usblp_quirks(__u16 vendor, __u16 product)
{
int i;
@@ -1031,7 +1030,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product)
if (vendor == quirk_printers[i].vendorId &&
product == quirk_printers[i].productId)
return quirk_printers[i].quirks;
- }
+ }
return 0;
}
@@ -1061,7 +1060,7 @@ static struct usb_class_driver usblp_class = {
static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
if (usblp->device_id_string[0] == 0 &&
usblp->device_id_string[1] == 0)
@@ -1075,7 +1074,7 @@ static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL);
static int usblp_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- struct usb_device *dev = interface_to_usbdev (intf);
+ struct usb_device *dev = interface_to_usbdev(intf);
struct usblp *usblp;
int protocol;
int retval;
@@ -1089,7 +1088,7 @@ static int usblp_probe(struct usb_interface *intf,
}
usblp->dev = dev;
mutex_init(&usblp->wmut);
- mutex_init (&usblp->mut);
+ mutex_init(&usblp->mut);
spin_lock_init(&usblp->lock);
init_waitqueue_head(&usblp->rwait);
init_waitqueue_head(&usblp->wwait);
@@ -1153,7 +1152,7 @@ static int usblp_probe(struct usb_interface *intf,
usblp_check_status(usblp, 0);
#endif
- usb_set_intfdata (intf, usblp);
+ usb_set_intfdata(intf, usblp);
usblp->present = 1;
@@ -1177,7 +1176,7 @@ static int usblp_probe(struct usb_interface *intf,
return 0;
abort_intfdata:
- usb_set_intfdata (intf, NULL);
+ usb_set_intfdata(intf, NULL);
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
abort:
kfree(usblp->readbuf);
@@ -1340,35 +1339,35 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
static void usblp_disconnect(struct usb_interface *intf)
{
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
usb_deregister_dev(intf, &usblp_class);
if (!usblp || !usblp->dev) {
dev_err(&intf->dev, "bogus disconnect\n");
- BUG ();
+ BUG();
}
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
- mutex_lock (&usblp_mutex);
- mutex_lock (&usblp->mut);
+ mutex_lock(&usblp_mutex);
+ mutex_lock(&usblp->mut);
usblp->present = 0;
wake_up(&usblp->wwait);
wake_up(&usblp->rwait);
- usb_set_intfdata (intf, NULL);
+ usb_set_intfdata(intf, NULL);
usblp_unlink_urbs(usblp);
- mutex_unlock (&usblp->mut);
+ mutex_unlock(&usblp->mut);
if (!usblp->used)
- usblp_cleanup (usblp);
- mutex_unlock (&usblp_mutex);
+ usblp_cleanup(usblp);
+ mutex_unlock(&usblp_mutex);
}
static int usblp_suspend(struct usb_interface *intf, pm_message_t message)
{
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
usblp_unlink_urbs(usblp);
#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */
@@ -1382,10 +1381,10 @@ static int usblp_suspend(struct usb_interface *intf, pm_message_t message)
static int usblp_resume(struct usb_interface *intf)
{
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
int r;
- r = handle_bidir (usblp);
+ r = handle_bidir(usblp);
return r;
}
@@ -1401,7 +1400,7 @@ static const struct usb_device_id usblp_ids[] = {
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, usblp_ids);
+MODULE_DEVICE_TABLE(usb, usblp_ids);
static struct usb_driver usblp_driver = {
.name = "usblp",
@@ -1426,8 +1425,8 @@ static void __exit usblp_exit(void)
module_init(usblp_init);
module_exit(usblp_exit);
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
module_param(proto_bias, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(proto_bias, "Favourite protocol number");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index c2f62a3..f1aaff6 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1668,13 +1668,10 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
default:
if (intf->dev.driver)
driver = to_usb_driver(intf->dev.driver);
- if (driver == NULL || driver->ioctl == NULL) {
+ if (driver == NULL || driver->unlocked_ioctl == NULL) {
retval = -ENOTTY;
} else {
- /* keep API that guarantees BKL */
- lock_kernel();
- retval = driver->ioctl(intf, ctl->ioctl_code, buf);
- unlock_kernel();
+ retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf);
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index a6bd53a..d7a4401 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1742,9 +1742,8 @@ static int usb_runtime_suspend(struct device *dev)
}
/* Prevent the parent from suspending immediately after */
- else if (udev->parent) {
+ else if (udev->parent)
udev->parent->last_busy = jiffies;
- }
}
/* Runtime suspend for a USB interface doesn't mean anything. */
@@ -1786,21 +1785,19 @@ static int usb_runtime_idle(struct device *dev)
return 0;
}
-static struct dev_pm_ops usb_bus_pm_ops = {
+static const struct dev_pm_ops usb_bus_pm_ops = {
.runtime_suspend = usb_runtime_suspend,
.runtime_resume = usb_runtime_resume,
.runtime_idle = usb_runtime_idle,
};
-#else
-
-#define usb_bus_pm_ops (*(struct dev_pm_ops *) NULL)
-
#endif /* CONFIG_USB_SUSPEND */
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
+#ifdef CONFIG_USB_SUSPEND
.pm = &usb_bus_pm_ops,
+#endif
};
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 4f84a41..3788e73 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -96,16 +96,21 @@ static ssize_t show_ep_interval(struct device *dev,
switch (usb_endpoint_type(ep->desc)) {
case USB_ENDPOINT_XFER_CONTROL:
- if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
+ if (ep->udev->speed == USB_SPEED_HIGH)
+ /* uframes per NAK */
interval = ep->desc->bInterval;
break;
+
case USB_ENDPOINT_XFER_ISOC:
interval = 1 << (ep->desc->bInterval - 1);
break;
+
case USB_ENDPOINT_XFER_BULK:
- if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
+ if (ep->udev->speed == USB_SPEED_HIGH && !in)
+ /* uframes per NAK */
interval = ep->desc->bInterval;
break;
+
case USB_ENDPOINT_XFER_INT:
if (ep->udev->speed == USB_SPEED_HIGH)
interval = 1 << (ep->desc->bInterval - 1);
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 9a34ccb..69ecd3c 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -105,8 +105,10 @@ int usb_choose_configuration(struct usb_device *udev)
/* When the first config's first interface is one of Microsoft's
* pet nonstandard Ethernet-over-USB protocols, ignore it unless
* this kernel has enabled the necessary host side driver.
+ * But: Don't ignore it if it's the only config.
*/
- if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {
+ if (i == 0 && num_configs > 1 && desc &&
+ (is_rndis(desc) || is_activesync(desc))) {
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
continue;
#else
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 1cf2d1e..c3f9854 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -66,10 +66,7 @@ static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
* vice versa.
*/
companion = NULL;
- for (;;) {
- companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
- if (!companion)
- break;
+ for_each_pci_dev(companion) {
if (companion->bus != pdev->bus ||
PCI_SLOT(companion->devfn) != slot)
continue;
@@ -250,6 +247,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (retval != 0)
goto err4;
set_hs_companion(dev, hcd);
+
+ if (pci_dev_run_wake(dev))
+ pm_runtime_put_noidle(&dev->dev);
return retval;
err4:
@@ -292,6 +292,17 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
if (!hcd)
return;
+ if (pci_dev_run_wake(dev))
+ pm_runtime_get_noresume(&dev->dev);
+
+ /* Fake an interrupt request in order to give the driver a chance
+ * to test whether the controller hardware has been removed (e.g.,
+ * cardbus physical eject).
+ */
+ local_irq_disable();
+ usb_hcd_irq(0, hcd);
+ local_irq_enable();
+
usb_remove_hcd(hcd);
if (hcd->driver->flags & HCD_MEMORY) {
iounmap(hcd->regs);
@@ -317,12 +328,34 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
if (!hcd)
return;
- if (hcd->driver->shutdown)
+ if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
+ hcd->driver->shutdown)
hcd->driver->shutdown(hcd);
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_OPS
+
+#ifdef CONFIG_PPC_PMAC
+static void powermac_set_asic(struct pci_dev *pci_dev, int enable)
+{
+ /* Enanble or disable ASIC clocks for USB */
+ if (machine_is(powermac)) {
+ struct device_node *of_node;
+
+ of_node = pci_device_to_OF_node(pci_dev);
+ if (of_node)
+ pmac_call_feature(PMAC_FTR_USB_ENABLE,
+ of_node, 0, enable);
+ }
+}
+
+#else
+
+static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
+{}
+
+#endif /* CONFIG_PPC_PMAC */
static int check_root_hub_suspended(struct device *dev)
{
@@ -337,7 +370,7 @@ static int check_root_hub_suspended(struct device *dev)
return 0;
}
-static int hcd_pci_suspend(struct device *dev)
+static int suspend_common(struct device *dev, bool do_wakeup)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
@@ -352,13 +385,21 @@ static int hcd_pci_suspend(struct device *dev)
if (retval)
return retval;
- /* We might already be suspended (runtime PM -- not yet written) */
- if (pci_dev->current_state != PCI_D0)
- return retval;
-
if (hcd->driver->pci_suspend) {
- retval = hcd->driver->pci_suspend(hcd);
+ /* Optimization: Don't suspend if a root-hub wakeup is
+ * pending and it would cause the HCD to wake up anyway.
+ */
+ if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
+ return -EBUSY;
+ retval = hcd->driver->pci_suspend(hcd, do_wakeup);
suspend_report_result(hcd->driver->pci_suspend, retval);
+
+ /* Check again in case wakeup raced with pci_suspend */
+ if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+ if (hcd->driver->pci_resume)
+ hcd->driver->pci_resume(hcd, false);
+ retval = -EBUSY;
+ }
if (retval)
return retval;
}
@@ -374,6 +415,48 @@ static int hcd_pci_suspend(struct device *dev)
return retval;
}
+static int resume_common(struct device *dev, int event)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+ int retval;
+
+ if (hcd->state != HC_STATE_SUSPENDED) {
+ dev_dbg(dev, "can't resume, not suspended!\n");
+ return 0;
+ }
+
+ retval = pci_enable_device(pci_dev);
+ if (retval < 0) {
+ dev_err(dev, "can't re-enable after resume, %d!\n", retval);
+ return retval;
+ }
+
+ pci_set_master(pci_dev);
+
+ clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ if (hcd->driver->pci_resume) {
+ if (event != PM_EVENT_AUTO_RESUME)
+ wait_for_companions(pci_dev, hcd);
+
+ retval = hcd->driver->pci_resume(hcd,
+ event == PM_EVENT_RESTORE);
+ if (retval) {
+ dev_err(dev, "PCI post-resume error %d!\n", retval);
+ usb_hc_died(hcd);
+ }
+ }
+ return retval;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int hcd_pci_suspend(struct device *dev)
+{
+ return suspend_common(dev, device_may_wakeup(dev));
+}
+
static int hcd_pci_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -408,16 +491,7 @@ static int hcd_pci_suspend_noirq(struct device *dev)
return retval;
}
-#ifdef CONFIG_PPC_PMAC
- /* Disable ASIC clocks for USB */
- if (machine_is(powermac)) {
- struct device_node *of_node;
-
- of_node = pci_device_to_OF_node(pci_dev);
- if (of_node)
- pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
- }
-#endif
+ powermac_set_asic(pci_dev, 0);
return retval;
}
@@ -425,69 +499,63 @@ static int hcd_pci_resume_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
-#ifdef CONFIG_PPC_PMAC
- /* Reenable ASIC clocks for USB */
- if (machine_is(powermac)) {
- struct device_node *of_node;
-
- of_node = pci_device_to_OF_node(pci_dev);
- if (of_node)
- pmac_call_feature(PMAC_FTR_USB_ENABLE,
- of_node, 0, 1);
- }
-#endif
+ powermac_set_asic(pci_dev, 1);
/* Go back to D0 and disable remote wakeup */
pci_back_from_sleep(pci_dev);
return 0;
}
-static int resume_common(struct device *dev, bool hibernated)
+static int hcd_pci_resume(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
- int retval;
+ return resume_common(dev, PM_EVENT_RESUME);
+}
- if (hcd->state != HC_STATE_SUSPENDED) {
- dev_dbg(dev, "can't resume, not suspended!\n");
- return 0;
- }
+static int hcd_pci_restore(struct device *dev)
+{
+ return resume_common(dev, PM_EVENT_RESTORE);
+}
- retval = pci_enable_device(pci_dev);
- if (retval < 0) {
- dev_err(dev, "can't re-enable after resume, %d!\n", retval);
- return retval;
- }
+#else
- pci_set_master(pci_dev);
+#define hcd_pci_suspend NULL
+#define hcd_pci_suspend_noirq NULL
+#define hcd_pci_resume_noirq NULL
+#define hcd_pci_resume NULL
+#define hcd_pci_restore NULL
- clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+#endif /* CONFIG_PM_SLEEP */
- if (hcd->driver->pci_resume) {
- /* This call should be made only during system resume,
- * not during runtime resume.
- */
- wait_for_companions(pci_dev, hcd);
+#ifdef CONFIG_PM_RUNTIME
- retval = hcd->driver->pci_resume(hcd, hibernated);
- if (retval) {
- dev_err(dev, "PCI post-resume error %d!\n", retval);
- usb_hc_died(hcd);
- }
- }
+static int hcd_pci_runtime_suspend(struct device *dev)
+{
+ int retval;
+
+ retval = suspend_common(dev, true);
+ if (retval == 0)
+ powermac_set_asic(to_pci_dev(dev), 0);
+ dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
return retval;
}
-static int hcd_pci_resume(struct device *dev)
+static int hcd_pci_runtime_resume(struct device *dev)
{
- return resume_common(dev, false);
-}
+ int retval;
-static int hcd_pci_restore(struct device *dev)
-{
- return resume_common(dev, true);
+ powermac_set_asic(to_pci_dev(dev), 1);
+ retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
+ dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
+ return retval;
}
+#else
+
+#define hcd_pci_runtime_suspend NULL
+#define hcd_pci_runtime_resume NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.suspend = hcd_pci_suspend,
.suspend_noirq = hcd_pci_suspend_noirq,
@@ -501,7 +569,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.poweroff_noirq = hcd_pci_suspend_noirq,
.restore_noirq = hcd_pci_resume_noirq,
.restore = hcd_pci_restore,
+ .runtime_suspend = hcd_pci_runtime_suspend,
+ .runtime_resume = hcd_pci_runtime_resume,
};
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
-#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_OPS */
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 12742f1..5cca00a 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -667,7 +667,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
- if (unlikely(!hcd->rh_registered))
+ if (unlikely(!hcd->rh_pollable))
return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
@@ -679,7 +679,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
spin_lock_irqsave(&hcd_root_hub_lock, flags);
urb = hcd->status_urb;
if (urb) {
- hcd->poll_pending = 0;
+ clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
@@ -690,7 +690,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
spin_lock(&hcd_root_hub_lock);
} else {
length = 0;
- hcd->poll_pending = 1;
+ set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
}
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
}
@@ -699,7 +699,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
* exceed that limit if HZ is 100. The math is more clunky than
* maybe expected, this is to make sure that all timers for USB devices
* fire at the same time to give the CPU a break inbetween */
- if (hcd->uses_new_polling ? hcd->poll_rh :
+ if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :
(length == 0 && hcd->status_urb != NULL))
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
@@ -736,7 +736,7 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
/* If a status change has already occurred, report it ASAP */
- else if (hcd->poll_pending)
+ else if (HCD_POLL_PENDING(hcd))
mod_timer(&hcd->rh_timer, jiffies);
retval = 0;
done:
@@ -1150,8 +1150,7 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
* finish unlinking the initial failed usb_set_address()
* or device descriptor fetch.
*/
- if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) &&
- !is_root_hub(urb->dev)) {
+ if (!HCD_SAW_IRQ(hcd) && !is_root_hub(urb->dev)) {
dev_warn(hcd->self.controller, "Unlink after no-IRQ? "
"Controller is probably using the wrong IRQ.\n");
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
@@ -1219,6 +1218,11 @@ static int hcd_alloc_coherent(struct usb_bus *bus,
{
unsigned char *vaddr;
+ if (*vaddr_handle == NULL) {
+ WARN_ON_ONCE(1);
+ return -EFAULT;
+ }
+
vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr),
mem_flags, dma_handle);
if (!vaddr)
@@ -1941,6 +1945,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
dev_dbg(&rhdev->dev, "usb %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
+ clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
if (!hcd->driver->bus_resume)
return -ENOENT;
if (hcd->state == HC_STATE_RUNNING)
@@ -1994,8 +1999,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
unsigned long flags;
spin_lock_irqsave (&hcd_root_hub_lock, flags);
- if (hcd->rh_registered)
+ if (hcd->rh_registered) {
+ set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
queue_work(pm_wq, &hcd->wakeup_work);
+ }
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -2063,8 +2070,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
*/
local_irq_save(flags);
- if (unlikely(hcd->state == HC_STATE_HALT ||
- !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) {
rc = IRQ_NONE;
} else if (hcd->driver->irq(hcd) == IRQ_NONE) {
rc = IRQ_NONE;
@@ -2079,6 +2085,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
local_irq_restore(flags);
return rc;
}
+EXPORT_SYMBOL_GPL(usb_hcd_irq);
/*-------------------------------------------------------------------------*/
@@ -2098,7 +2105,7 @@ void usb_hc_died (struct usb_hcd *hcd)
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered) {
- hcd->poll_rh = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
/* make khubd clean up old urbs and devices */
usb_set_device_state (hcd->self.root_hub,
@@ -2217,6 +2224,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
retval = -ENOMEM;
goto err_allocate_root_hub;
}
+ hcd->self.root_hub = rhdev;
switch (hcd->driver->flags & HCD_MASK) {
case HCD_USB11:
@@ -2229,9 +2237,8 @@ int usb_add_hcd(struct usb_hcd *hcd,
rhdev->speed = USB_SPEED_SUPER;
break;
default:
- goto err_allocate_root_hub;
+ goto err_set_rh_speed;
}
- hcd->self.root_hub = rhdev;
/* wakeup flag init defaults to "everything works" for root hubs,
* but drivers can override it in reset() if needed, along with
@@ -2246,6 +2253,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_err(hcd->self.controller, "can't setup\n");
goto err_hcd_driver_setup;
}
+ hcd->rh_pollable = 1;
/* NOTE: root hub and controller capabilities may not be the same */
if (device_can_wakeup(hcd->self.controller)
@@ -2300,23 +2308,38 @@ int usb_add_hcd(struct usb_hcd *hcd,
retval);
goto error_create_attr_group;
}
- if (hcd->uses_new_polling && hcd->poll_rh)
+ if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
return retval;
error_create_attr_group:
+ if (HC_IS_RUNNING(hcd->state))
+ hcd->state = HC_STATE_QUIESCING;
+ spin_lock_irq(&hcd_root_hub_lock);
+ hcd->rh_registered = 0;
+ spin_unlock_irq(&hcd_root_hub_lock);
+
+#ifdef CONFIG_USB_SUSPEND
+ cancel_work_sync(&hcd->wakeup_work);
+#endif
mutex_lock(&usb_bus_list_lock);
- usb_disconnect(&hcd->self.root_hub);
+ usb_disconnect(&rhdev); /* Sets rhdev to NULL */
mutex_unlock(&usb_bus_list_lock);
err_register_root_hub:
+ hcd->rh_pollable = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
hcd->driver->stop(hcd);
+ hcd->state = HC_STATE_HALT;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
err_hcd_driver_start:
if (hcd->irq >= 0)
free_irq(irqnum, hcd);
err_request_irq:
err_hcd_driver_setup:
- hcd->self.root_hub = NULL;
- usb_put_dev(rhdev);
+err_set_rh_speed:
+ usb_put_dev(hcd->self.root_hub);
err_allocate_root_hub:
usb_deregister_bus(&hcd->self);
err_register_bus:
@@ -2335,8 +2358,13 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
*/
void usb_remove_hcd(struct usb_hcd *hcd)
{
+ struct usb_device *rhdev = hcd->self.root_hub;
+
dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
+ usb_get_dev(rhdev);
+ sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
+
if (HC_IS_RUNNING (hcd->state))
hcd->state = HC_STATE_QUIESCING;
@@ -2349,19 +2377,30 @@ void usb_remove_hcd(struct usb_hcd *hcd)
cancel_work_sync(&hcd->wakeup_work);
#endif
- sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
mutex_lock(&usb_bus_list_lock);
- usb_disconnect(&hcd->self.root_hub);
+ usb_disconnect(&rhdev); /* Sets rhdev to NULL */
mutex_unlock(&usb_bus_list_lock);
+ /* Prevent any more root-hub status calls from the timer.
+ * The HCD might still restart the timer (if a port status change
+ * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
+ * the hub_status_data() callback.
+ */
+ hcd->rh_pollable = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
+
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
- hcd->poll_rh = 0;
+ /* In case the HCD restarted the timer, stop it again. */
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer);
if (hcd->irq >= 0)
free_irq(hcd->irq, hcd);
+
+ usb_put_dev(hcd->self.root_hub);
usb_deregister_bus(&hcd->self);
hcd_buffer_destroy(hcd);
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 70cccc7..84c18971 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
@@ -1294,6 +1295,7 @@ descriptor_error:
return -ENODEV;
}
+/* No BKL needed */
static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
@@ -1801,7 +1803,6 @@ int usb_new_device(struct usb_device *udev)
pm_runtime_set_active(&udev->dev);
pm_runtime_enable(&udev->dev);
- usb_detect_quirks(udev);
err = usb_enumerate_device(udev); /* Read descriptors */
if (err < 0)
goto fail;
@@ -2880,7 +2881,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
}
retval = 0;
-
+ /* notify HCD that we have a device connected and addressed */
+ if (hcd->driver->update_device)
+ hcd->driver->update_device(hcd, udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
@@ -3111,6 +3114,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (status < 0)
goto loop;
+ usb_detect_quirks(udev);
+ if (udev->quirks & USB_QUIRK_DELAY_INIT)
+ msleep(1000);
+
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
* a "powered" LED, users should notice we didn't enable it
@@ -3463,7 +3470,7 @@ static struct usb_driver hub_driver = {
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
- .ioctl = hub_ioctl,
+ .unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 1a27618..095fa53 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -265,13 +265,9 @@ static int remount(struct super_block *sb, int *flags, char *data)
return -EINVAL;
}
- lock_kernel();
-
if (usbfs_mount && usbfs_mount->mnt_sb)
update_sb(usbfs_mount->mnt_sb);
- unlock_kernel();
-
return 0;
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index db99c08..25719da 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -38,6 +38,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Harmony 700-series */
+ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Philips PSC805 audio device */
{ USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 7c05555..419e6b3 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -137,6 +137,16 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
}
EXPORT_SYMBOL_GPL(usb_anchor_urb);
+/* Callers must hold anchor->lock */
+static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+ urb->anchor = NULL;
+ list_del(&urb->anchor_list);
+ usb_put_urb(urb);
+ if (list_empty(&anchor->urb_list))
+ wake_up(&anchor->wait);
+}
+
/**
* usb_unanchor_urb - unanchors an URB
* @urb: pointer to the urb to anchor
@@ -156,17 +166,14 @@ void usb_unanchor_urb(struct urb *urb)
return;
spin_lock_irqsave(&anchor->lock, flags);
- if (unlikely(anchor != urb->anchor)) {
- /* we've lost the race to another thread */
- spin_unlock_irqrestore(&anchor->lock, flags);
- return;
- }
- urb->anchor = NULL;
- list_del(&urb->anchor_list);
+ /*
+ * At this point, we could be competing with another thread which
+ * has the same intention. To protect the urb from being unanchored
+ * twice, only the winner of the race gets the job.
+ */
+ if (likely(anchor == urb->anchor))
+ __usb_unanchor_urb(urb, anchor);
spin_unlock_irqrestore(&anchor->lock, flags);
- usb_put_urb(urb);
- if (list_empty(&anchor->urb_list))
- wake_up(&anchor->wait);
}
EXPORT_SYMBOL_GPL(usb_unanchor_urb);
@@ -749,20 +756,11 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
- unsigned long flags;
- spin_lock_irqsave(&anchor->lock, flags);
- while (!list_empty(&anchor->urb_list)) {
- victim = list_entry(anchor->urb_list.prev, struct urb,
- anchor_list);
- usb_get_urb(victim);
- spin_unlock_irqrestore(&anchor->lock, flags);
- /* this will unanchor the URB */
+ while ((victim = usb_get_from_anchor(anchor)) != NULL) {
usb_unlink_urb(victim);
usb_put_urb(victim);
- spin_lock_irqsave(&anchor->lock, flags);
}
- spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
@@ -799,12 +797,11 @@ struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
victim = list_entry(anchor->urb_list.next, struct urb,
anchor_list);
usb_get_urb(victim);
- spin_unlock_irqrestore(&anchor->lock, flags);
- usb_unanchor_urb(victim);
+ __usb_unanchor_urb(victim, anchor);
} else {
- spin_unlock_irqrestore(&anchor->lock, flags);
victim = NULL;
}
+ spin_unlock_irqrestore(&anchor->lock, flags);
return victim;
}
@@ -826,12 +823,7 @@ void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
while (!list_empty(&anchor->urb_list)) {
victim = list_entry(anchor->urb_list.prev, struct urb,
anchor_list);
- usb_get_urb(victim);
- spin_unlock_irqrestore(&anchor->lock, flags);
- /* this may free the URB */
- usb_unanchor_urb(victim);
- usb_put_urb(victim);
- spin_lock_irqsave(&anchor->lock, flags);
+ __usb_unanchor_urb(victim, anchor);
}
spin_unlock_irqrestore(&anchor->lock, flags);
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 5ae14f6..fdd4130 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -317,10 +317,6 @@ static const struct dev_pm_ops usb_device_pm_ops = {
.restore = usb_dev_restore,
};
-#else
-
-#define usb_device_pm_ops (*(struct dev_pm_ops *) NULL)
-
#endif /* CONFIG_PM */
@@ -338,7 +334,9 @@ struct device_type usb_device_type = {
.release = usb_release_dev,
.uevent = usb_dev_uevent,
.devnode = usb_devnode,
+#ifdef CONFIG_PM
.pm = &usb_device_pm_ops,
+#endif
};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 591ae9f..cd27f9b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -714,6 +714,7 @@ config USB_GADGETFS
config USB_FUNCTIONFS
tristate "Function Filesystem (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
help
The Function Filesystem (FunctioFS) lets one create USB
composite functions in user space in the same way as GadgetFS
@@ -722,31 +723,31 @@ config USB_FUNCTIONFS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
+ If you say "y" or "m" here you will be able what kind of
+ configurations the gadget will provide.
+
Say "y" to link the driver statically, or "m" to build
a dynamically linked module called "g_ffs".
config USB_FUNCTIONFS_ETH
- bool "Include CDC ECM (Ethernet) function"
+ bool "Include configuration with CDC ECM (Ethernet)"
depends on USB_FUNCTIONFS && NET
help
- Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion)
- Filesystem. If you also say "y" to the RNDIS query below the
- gadget will have two configurations.
+ Include a configuration with CDC ECM funcion (Ethernet) and the
+ Funcion Filesystem.
config USB_FUNCTIONFS_RNDIS
- bool "Include RNDIS (Ethernet) function"
+ bool "Include configuration with RNDIS (Ethernet)"
depends on USB_FUNCTIONFS && NET
help
- Include an RNDIS (Ethernet) funcion in the Funcion Filesystem.
- If you also say "y" to the CDC ECM query above the gadget will
- have two configurations.
+ Include a configuration with RNDIS funcion (Ethernet) and the Filesystem.
config USB_FUNCTIONFS_GENERIC
bool "Include 'pure' configuration"
- depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+ depends on USB_FUNCTIONFS
help
- Include a configuration with FunctionFS and no Ethernet
- configuration.
+ Include a configuration with the Function Filesystem alone with
+ no Ethernet interface.
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
@@ -863,6 +864,7 @@ config USB_G_NOKIA
config USB_G_MULTI
tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
depends on BLOCK && NET
+ select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
help
The Multifunction Composite Gadget provides Ethernet (RNDIS
and/or CDC Ethernet), mass storage and ACM serial link
@@ -913,6 +915,34 @@ config USB_G_HID
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_hid".
+config USB_G_DBGP
+ tristate "EHCI Debug Device Gadget"
+ help
+ This gadget emulates an EHCI Debug device. This is useful when you want
+ to interact with an EHCI Debug Port.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_dbgp".
+
+if USB_G_DBGP
+choice
+ prompt "EHCI Debug Device mode"
+ default USB_G_DBGP_SERIAL
+
+config USB_G_DBGP_PRINTK
+ depends on USB_G_DBGP
+ bool "printk"
+ help
+ Directly printk() received data. No interaction.
+
+config USB_G_DBGP_SERIAL
+ depends on USB_G_DBGP
+ bool "serial"
+ help
+ Userland can interact using /dev/ttyGSxxx.
+endchoice
+endif
+
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
config USB_G_WEBCAM
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9bcde11..27283df 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -44,6 +44,7 @@ g_printer-objs := printer.o
g_cdc-objs := cdc2.o
g_multi-objs := multi.o
g_hid-objs := hid.o
+g_dbgp-objs := dbgp.o
g_nokia-objs := nokia.o
g_webcam-objs := webcam.o
@@ -52,7 +53,6 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
-obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
@@ -60,6 +60,7 @@ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
obj-$(CONFIG_USB_G_HID) += g_hid.o
+obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index a62af7b..b744ccd 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -89,7 +89,7 @@ static const struct usb_descriptor_header *otg_desc[] = {
/*-------------------------------------------------------------------------*/
-static int __init audio_do_config(struct usb_configuration *c)
+static int __ref audio_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
@@ -113,7 +113,7 @@ static struct usb_configuration audio_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init audio_bind(struct usb_composite_dev *cdev)
+static int __ref audio_bind(struct usb_composite_dev *cdev)
{
int gcnum;
int status;
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
index 928137d..1f5ba2f 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -129,7 +129,7 @@ static u8 hostaddr[ETH_ALEN];
/*
* We _always_ have both CDC ECM and CDC ACM functions.
*/
-static int __init cdc_do_config(struct usb_configuration *c)
+static int __ref cdc_do_config(struct usb_configuration *c)
{
int status;
@@ -159,7 +159,7 @@ static struct usb_configuration cdc_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init cdc_bind(struct usb_composite_dev *cdev)
+static int __ref cdc_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 391d169..e483f80 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -673,20 +673,83 @@ static int get_string(struct usb_composite_dev *cdev,
* string IDs. Drivers for functions, configurations, or gadgets will
* then store that ID in the appropriate descriptors and string table.
*
- * All string identifier should be allocated using this routine, to
- * ensure that for example different functions don't wrongly assign
- * different meanings to the same identifier.
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
*/
int usb_string_id(struct usb_composite_dev *cdev)
{
if (cdev->next_string_id < 254) {
- /* string id 0 is reserved */
+ /* string id 0 is reserved by USB spec for list of
+ * supported languages */
+ /* 255 reserved as well? -- mina86 */
cdev->next_string_id++;
return cdev->next_string_id;
}
return -ENODEV;
}
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+ int next = cdev->next_string_id;
+
+ for (; str->s; ++str) {
+ if (unlikely(next >= 254))
+ return -ENODEV;
+ str->id = ++next;
+ }
+
+ cdev->next_string_id = next;
+
+ return 0;
+}
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID. This ID and next @n-1 IDs are now
+ * valid IDs. At least providind that @n is non zore because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+ unsigned next = c->next_string_id;
+ if (unlikely(n > 254 || (unsigned)next + n > 254))
+ return -ENODEV;
+ c->next_string_id += n;
+ return next + 1;
+}
+
+
/*-------------------------------------------------------------------------*/
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
@@ -893,6 +956,8 @@ static void composite_disconnect(struct usb_gadget *gadget)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
+ if (composite->disconnect)
+ composite->disconnect(cdev);
spin_unlock_irqrestore(&cdev->lock, flags);
}
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c
new file mode 100644
index 0000000..0ed50a2
--- /dev/null
+++ b/drivers/usb/gadget/dbgp.c
@@ -0,0 +1,434 @@
+/*
+ * dbgp.c -- EHCI Debug Port device gadget
+ *
+ * Copyright (C) 2010 Stephane Duverger
+ *
+ * Released under the GPLv2.
+ *
+ */
+
+/* verbose messages */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* See comments in "zero.c" */
+#include "epautoconf.c"
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+#include "u_serial.c"
+#endif
+
+#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
+#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
+
+#define USB_DEBUG_MAX_PACKET_SIZE 8
+#define DBGP_REQ_EP0_LEN 128
+#define DBGP_REQ_LEN 512
+
+static struct dbgp {
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ struct usb_ep *i_ep;
+ struct usb_ep *o_ep;
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ struct gserial *serial;
+#endif
+} dbgp;
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
+ .bNumConfigurations = 1,
+};
+
+static struct usb_debug_descriptor dbg_desc = {
+ .bLength = sizeof dbg_desc,
+ .bDescriptorType = USB_DT_DEBUG,
+};
+
+static struct usb_endpoint_descriptor i_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .bEndpointAddress = USB_DIR_IN,
+};
+
+static struct usb_endpoint_descriptor o_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .bEndpointAddress = USB_DIR_OUT,
+};
+
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+static int dbgp_consume(char *buf, unsigned len)
+{
+ char c;
+
+ if (!len)
+ return 0;
+
+ c = buf[len-1];
+ if (c != 0)
+ buf[len-1] = 0;
+
+ printk(KERN_NOTICE "%s%c", buf, c);
+ return 0;
+}
+
+static void __disable_ep(struct usb_ep *ep)
+{
+ if (ep && ep->driver_data == dbgp.gadget) {
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ }
+}
+
+static void dbgp_disable_ep(void)
+{
+ __disable_ep(dbgp.i_ep);
+ __disable_ep(dbgp.o_ep);
+}
+
+static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int stp;
+ int err = 0;
+ int status = req->status;
+
+ if (ep == dbgp.i_ep) {
+ stp = 1;
+ goto fail;
+ }
+
+ if (status != 0) {
+ stp = 2;
+ goto release_req;
+ }
+
+ dbgp_consume(req->buf, req->actual);
+
+ req->length = DBGP_REQ_LEN;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ stp = 3;
+ goto release_req;
+ }
+
+ return;
+
+release_req:
+ kfree(req->buf);
+ usb_ep_free_request(dbgp.o_ep, req);
+ dbgp_disable_ep();
+fail:
+ dev_dbg(&dbgp.gadget->dev,
+ "complete: failure (%d:%d) ==> %d\n", stp, err, status);
+}
+
+static int dbgp_enable_ep_req(struct usb_ep *ep)
+{
+ int err, stp;
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ stp = 1;
+ goto fail_1;
+ }
+
+ req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
+ if (!req->buf) {
+ err = -ENOMEM;
+ stp = 2;
+ goto fail_2;
+ }
+
+ req->complete = dbgp_complete;
+ req->length = DBGP_REQ_LEN;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ kfree(req->buf);
+fail_2:
+ usb_ep_free_request(dbgp.o_ep, req);
+fail_1:
+ dev_dbg(&dbgp.gadget->dev,
+ "enable ep req: failure (%d:%d)\n", stp, err);
+ return err;
+}
+
+static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
+{
+ int err = usb_ep_enable(ep, desc);
+ ep->driver_data = dbgp.gadget;
+ return err;
+}
+
+static int dbgp_enable_ep(void)
+{
+ int err, stp;
+
+ err = __enable_ep(dbgp.i_ep, &i_desc);
+ if (err < 0) {
+ stp = 1;
+ goto fail_1;
+ }
+
+ err = __enable_ep(dbgp.o_ep, &o_desc);
+ if (err < 0) {
+ stp = 2;
+ goto fail_2;
+ }
+
+ err = dbgp_enable_ep_req(dbgp.o_ep);
+ if (err < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ __disable_ep(dbgp.o_ep);
+fail_2:
+ __disable_ep(dbgp.i_ep);
+fail_1:
+ dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
+ return err;
+}
+#endif
+
+static void dbgp_disconnect(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+ dbgp_disable_ep();
+#else
+ gserial_disconnect(dbgp.serial);
+#endif
+}
+
+static void dbgp_unbind(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ kfree(dbgp.serial);
+#endif
+ if (dbgp.req) {
+ kfree(dbgp.req->buf);
+ usb_ep_free_request(gadget->ep0, dbgp.req);
+ }
+
+ gadget->ep0->driver_data = NULL;
+}
+
+static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
+{
+ int stp;
+
+ usb_ep_autoconfig_reset(gadget);
+
+ dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
+ if (!dbgp.i_ep) {
+ stp = 1;
+ goto fail_1;
+ }
+
+ dbgp.i_ep->driver_data = gadget;
+ i_desc.wMaxPacketSize =
+ __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+ dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
+ if (!dbgp.o_ep) {
+ dbgp.i_ep->driver_data = NULL;
+ stp = 2;
+ goto fail_2;
+ }
+
+ dbgp.o_ep->driver_data = gadget;
+ o_desc.wMaxPacketSize =
+ __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+ dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f;
+ dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ dbgp.serial->in = dbgp.i_ep;
+ dbgp.serial->out = dbgp.o_ep;
+
+ dbgp.serial->in_desc = &i_desc;
+ dbgp.serial->out_desc = &o_desc;
+
+ if (gserial_setup(gadget, 1) < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ dbgp.o_ep->driver_data = NULL;
+#else
+ return 0;
+#endif
+fail_2:
+ dbgp.i_ep->driver_data = NULL;
+fail_1:
+ dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
+ return -ENODEV;
+}
+
+static int __init dbgp_bind(struct usb_gadget *gadget)
+{
+ int err, stp;
+
+ dbgp.gadget = gadget;
+
+ dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dbgp.req) {
+ err = -ENOMEM;
+ stp = 1;
+ goto fail;
+ }
+
+ dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
+ if (!dbgp.req->buf) {
+ err = -ENOMEM;
+ stp = 2;
+ goto fail;
+ }
+
+ dbgp.req->length = DBGP_REQ_EP0_LEN;
+ gadget->ep0->driver_data = gadget;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
+ if (!dbgp.serial) {
+ stp = 3;
+ err = -ENOMEM;
+ goto fail;
+ }
+#endif
+ err = dbgp_configure_endpoints(gadget);
+ if (err < 0) {
+ stp = 4;
+ goto fail;
+ }
+
+ dev_dbg(&dbgp.gadget->dev, "bind: success\n");
+ return 0;
+
+fail:
+ dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
+ dbgp_unbind(gadget);
+ return err;
+}
+
+static void dbgp_setup_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+static int dbgp_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_request *req = dbgp.req;
+ u8 request = ctrl->bRequest;
+ u16 value = le16_to_cpu(ctrl->wValue);
+ u16 length = le16_to_cpu(ctrl->wLength);
+ int err = 0;
+ void *data;
+ u16 len;
+
+ gadget->ep0->driver_data = gadget;
+
+ if (request == USB_REQ_GET_DESCRIPTOR) {
+ switch (value>>8) {
+ case USB_DT_DEVICE:
+ dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
+ len = sizeof device_desc;
+ data = &device_desc;
+ break;
+ case USB_DT_DEBUG:
+ dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
+ len = sizeof dbg_desc;
+ data = &dbg_desc;
+ break;
+ default:
+ goto fail;
+ }
+ } else if (request == USB_REQ_SET_FEATURE &&
+ value == USB_DEVICE_DEBUG_MODE) {
+ len = 0;
+ data = NULL;
+ dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+ err = dbgp_enable_ep();
+#else
+ err = gserial_connect(dbgp.serial, 0);
+#endif
+ if (err < 0)
+ goto fail;
+ } else
+ goto fail;
+
+ if (len >= 0) {
+ req->length = min(length, len);
+ req->zero = len < req->length;
+ if (data && req->length)
+ memcpy(req->buf, data, req->length);
+
+ req->complete = dbgp_setup_complete;
+ return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ }
+
+fail:
+ dev_dbg(&dbgp.gadget->dev,
+ "setup: failure req %x v %x\n", request, value);
+ return err;
+}
+
+static struct usb_gadget_driver dbgp_driver = {
+ .function = "dbgp",
+ .speed = USB_SPEED_HIGH,
+ .bind = dbgp_bind,
+ .unbind = dbgp_unbind,
+ .setup = dbgp_setup,
+ .disconnect = dbgp_disconnect,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dbgp"
+ },
+};
+
+static int __init dbgp_init(void)
+{
+ return usb_gadget_register_driver(&dbgp_driver);
+}
+
+static void __exit dbgp_exit(void)
+{
+ usb_gadget_unregister_driver(&dbgp_driver);
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ gserial_cleanup();
+#endif
+}
+
+MODULE_AUTHOR("Stephane Duverger");
+MODULE_LICENSE("GPL");
+module_init(dbgp_init);
+module_exit(dbgp_exit);
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 4f9e578..dc65462 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -1542,7 +1542,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
@@ -1588,7 +1588,7 @@ static int dummy_hub_control (
int retval = 0;
unsigned long flags;
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
dum = hcd_to_dummy (hcd);
@@ -1739,7 +1739,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd)
dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
spin_lock_irq (&dum->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
} else {
dum->rh_state = DUMMY_RH_RUNNING;
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 400f8037..114fa02 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -237,7 +237,7 @@ static u8 hostaddr[ETH_ALEN];
* the first one present. That's to make Microsoft's drivers happy,
* and to follow DOCSIS 1.0 (cable modem standard).
*/
-static int __init rndis_do_config(struct usb_configuration *c)
+static int __ref rndis_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
@@ -270,7 +270,7 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
/*
* We _always_ have an ECM, CDC Subset, or EEM configuration.
*/
-static int __init eth_do_config(struct usb_configuration *c)
+static int __ref eth_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
@@ -297,7 +297,7 @@ static struct usb_configuration eth_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init eth_bind(struct usb_composite_dev *cdev)
+static int __ref eth_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 2aaa0f7..e4f5950 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -714,9 +714,7 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
struct ffs_function *func = ffs->func;
ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
} else if (gadget->ops->ioctl) {
- lock_kernel();
ret = gadget->ops->ioctl(gadget, code, value);
- unlock_kernel();
} else {
ret = -ENOTTY;
}
@@ -1377,7 +1375,8 @@ static void ffs_data_reset(struct ffs_data *ffs)
static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
{
- unsigned i, count;
+ struct usb_gadget_strings **lang;
+ int first_id;
ENTER();
@@ -1385,7 +1384,9 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
|| test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
return -EBADFD;
- ffs_data_get(ffs);
+ first_id = usb_string_ids_n(cdev, ffs->strings_count);
+ if (unlikely(first_id < 0))
+ return first_id;
ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
if (unlikely(!ffs->ep0req))
@@ -1393,25 +1394,16 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
ffs->ep0req->complete = ffs_ep0_complete;
ffs->ep0req->context = ffs;
- /* Get strings identifiers */
- for (count = ffs->strings_count, i = 0; i < count; ++i) {
- struct usb_gadget_strings **lang;
-
- int id = usb_string_id(cdev);
- if (unlikely(id < 0)) {
- usb_ep_free_request(cdev->gadget->ep0, ffs->ep0req);
- ffs->ep0req = NULL;
- return id;
- }
-
- lang = ffs->stringtabs;
- do {
- (*lang)->strings[i].id = id;
- ++lang;
- } while (*lang);
+ lang = ffs->stringtabs;
+ for (lang = ffs->stringtabs; *lang; ++lang) {
+ struct usb_string *str = (*lang)->strings;
+ int id = first_id;
+ for (; str->s; ++id, ++str)
+ str->id = id;
}
ffs->gadget = cdev->gadget;
+ ffs_data_get(ffs);
return 0;
}
@@ -1480,9 +1472,9 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
}
-static int functionfs_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct ffs_data *ffs)
+static int functionfs_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
{
struct ffs_function *func;
int ret;
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 1e00ff9..53e1202 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -142,7 +142,7 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
size_t count, loff_t *ptr)
{
- struct f_hidg *hidg = (struct f_hidg *)file->private_data;
+ struct f_hidg *hidg = file->private_data;
char *tmp_buff = NULL;
unsigned long flags;
@@ -200,7 +200,7 @@ static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
size_t count, loff_t *offp)
{
- struct f_hidg *hidg = (struct f_hidg *)file->private_data;
+ struct f_hidg *hidg = file->private_data;
ssize_t status = -ENOMEM;
if (!access_ok(VERIFY_READ, buffer, count))
@@ -257,7 +257,7 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
{
- struct f_hidg *hidg = (struct f_hidg *)file->private_data;
+ struct f_hidg *hidg = file->private_data;
unsigned int ret = 0;
poll_wait(file, &hidg->read_queue, wait);
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index e91d1b1..4322587 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -324,7 +324,7 @@ static void loopback_disable(struct usb_function *f)
/*-------------------------------------------------------------------------*/
-static int __init loopback_bind_config(struct usb_configuration *c)
+static int __ref loopback_bind_config(struct usb_configuration *c)
{
struct f_loopback *loop;
int status;
@@ -346,7 +346,7 @@ static int __init loopback_bind_config(struct usb_configuration *c)
return status;
}
-static struct usb_configuration loopback_driver = {
+static struct usb_configuration loopback_driver = {
.label = "loopback",
.strings = loopback_strings,
.bind = loopback_bind_config,
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 4ce899c..32cce02 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -316,6 +316,27 @@ static const char fsg_string_interface[] = "Mass Storage";
/*-------------------------------------------------------------------------*/
struct fsg_dev;
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+ /* Callback function to call when thread exits. If no
+ * callback is set or it returns value lower then zero MSF
+ * will force eject all LUNs it operates on (including those
+ * marked as non-removable or with prevent_medium_removal flag
+ * set). */
+ int (*thread_exits)(struct fsg_common *common);
+
+ /* Called prior to ejection. Negative return means error,
+ * zero means to continue with ejection, positive means not to
+ * eject. */
+ int (*pre_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+ /* Called after ejection. Negative return means error, zero
+ * or positive is just a success. */
+ int (*post_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+};
/* Data shared by all the FSG instances. */
@@ -333,7 +354,6 @@ struct fsg_common {
struct usb_ep *ep0; /* Copy of gadget->ep0 */
struct usb_request *ep0req; /* Copy of cdev->req */
unsigned int ep0_req_tag;
- const char *ep0req_name;
struct fsg_buffhd *next_buffhd_to_fill;
struct fsg_buffhd *next_buffhd_to_drain;
@@ -369,8 +389,8 @@ struct fsg_common {
struct completion thread_notifier;
struct task_struct *thread_task;
- /* Callback function to call when thread exits. */
- int (*thread_exits)(struct fsg_common *common);
+ /* Callback functions. */
+ const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
@@ -394,12 +414,8 @@ struct fsg_config {
const char *lun_name_format;
const char *thread_name;
- /* Callback function to call when thread exits. If no
- * callback is set or it returns value lower then zero MSF
- * will force eject all LUNs it operates on (including those
- * marked as non-removable or with prevent_medium_removal flag
- * set). */
- int (*thread_exits)(struct fsg_common *common);
+ /* Callback functions. */
+ const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
@@ -435,6 +451,7 @@ static inline int __fsg_is_set(struct fsg_common *common,
if (common->fsg)
return 1;
ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+ WARN_ON(1);
return 0;
}
@@ -623,8 +640,6 @@ static int fsg_setup(struct usb_function *f,
/* Respond with data/status */
req->length = min((u16)1, w_length);
- fsg->common->ep0req_name =
- ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out";
return ep0_queue(fsg->common);
}
@@ -1395,43 +1410,55 @@ static int do_start_stop(struct fsg_common *common)
} else if (!curlun->removable) {
curlun->sense_data = SS_INVALID_COMMAND;
return -EINVAL;
- }
-
- loej = common->cmnd[4] & 0x02;
- start = common->cmnd[4] & 0x01;
-
- /* eject code from file_storage.c:do_start_stop() */
-
- if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
- (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+ } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+ (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
- if (!start) {
- /* Are we allowed to unload the media? */
- if (curlun->prevent_medium_removal) {
- LDBG(curlun, "unload attempt prevented\n");
- curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
- return -EINVAL;
- }
- if (loej) { /* Simulate an unload/eject */
- up_read(&common->filesem);
- down_write(&common->filesem);
- fsg_lun_close(curlun);
- up_write(&common->filesem);
- down_read(&common->filesem);
- }
- } else {
+ loej = common->cmnd[4] & 0x02;
+ start = common->cmnd[4] & 0x01;
- /* Our emulation doesn't support mounting; the medium is
- * available for use as soon as it is loaded. */
+ /* Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded. */
+ if (start) {
if (!fsg_lun_is_open(curlun)) {
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
return -EINVAL;
}
+ return 0;
}
- return 0;
+
+ /* Are we allowed to unload the media? */
+ if (curlun->prevent_medium_removal) {
+ LDBG(curlun, "unload attempt prevented\n");
+ curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+ return -EINVAL;
+ }
+
+ if (!loej)
+ return 0;
+
+ /* Simulate an unload/eject */
+ if (common->ops && common->ops->pre_eject) {
+ int r = common->ops->pre_eject(common, curlun,
+ curlun - common->luns);
+ if (unlikely(r < 0))
+ return r;
+ else if (r)
+ return 0;
+ }
+
+ up_read(&common->filesem);
+ down_write(&common->filesem);
+ fsg_lun_close(curlun);
+ up_write(&common->filesem);
+ down_read(&common->filesem);
+
+ return common->ops && common->ops->post_eject
+ ? min(0, common->ops->post_eject(common, curlun,
+ curlun - common->luns))
+ : 0;
}
@@ -2610,7 +2637,8 @@ static int fsg_main_thread(void *common_)
common->thread_task = NULL;
spin_unlock_irq(&common->lock);
- if (!common->thread_exits || common->thread_exits(common) < 0) {
+ if (!common->ops || !common->ops->thread_exits
+ || common->ops->thread_exits(common) < 0) {
struct fsg_lun *curlun = common->luns;
unsigned i = common->nluns;
@@ -2686,6 +2714,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
common->free_storage_on_release = 0;
}
+ common->ops = cfg->ops;
common->private_data = cfg->private_data;
common->gadget = gadget;
@@ -2807,7 +2836,6 @@ buffhds_first_it:
/* Tell the thread to start working */
- common->thread_exits = cfg->thread_exits;
common->thread_task =
kthread_create(fsg_main_thread, common,
OR(cfg->thread_name, "file-storage"));
@@ -2990,9 +3018,9 @@ static struct usb_gadget_strings *fsg_strings_array[] = {
NULL,
};
-static int fsg_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct fsg_common *common)
+static int fsg_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
{
struct fsg_dev *fsg;
int rc;
@@ -3024,6 +3052,13 @@ static int fsg_add(struct usb_composite_dev *cdev,
return rc;
}
+static inline int __deprecated __maybe_unused
+fsg_add(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
+{
+ return fsg_bind_config(cdev, c, common);
+}
/************************* Module parameters *************************/
@@ -3096,8 +3131,8 @@ fsg_config_from_params(struct fsg_config *cfg,
cfg->product_name = 0;
cfg->release = 0xffff;
- cfg->thread_exits = 0;
- cfg->private_data = 0;
+ cfg->ops = NULL;
+ cfg->private_data = NULL;
/* Finalise */
cfg->can_stall = params->stall;
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index 6d3cc44..685d768 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -404,7 +404,7 @@ static void sourcesink_disable(struct usb_function *f)
/*-------------------------------------------------------------------------*/
-static int __init sourcesink_bind_config(struct usb_configuration *c)
+static int __ref sourcesink_bind_config(struct usb_configuration *c)
{
struct f_sourcesink *ss;
int status;
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index b49d86e..a857b7a 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -56,7 +56,7 @@
* following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03),
* UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by
* the optional "protocol" module parameter. In addition, the default
- * Vendor ID, Product ID, and release number can be overridden.
+ * Vendor ID, Product ID, release number and serial number can be overridden.
*
* There is support for multiple logical units (LUNs), each of which has
* its own backing file. The number of LUNs can be set using the optional
@@ -93,6 +93,8 @@
* removable Default false, boolean for removable media
* luns=N Default N = number of filenames, number of
* LUNs to support
+ * nofua=b[,b...] Default false, booleans for ignore FUA flag
+ * in SCSI WRITE(10,12) commands
* stall Default determined according to the type of
* USB device controller (usually true),
* boolean to permit the driver to halt
@@ -106,17 +108,18 @@
* vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID
* product=0xPPPP Default 0xa4a5 (FSG), USB Product ID
* release=0xRRRR Override the USB release number (bcdDevice)
+ * serial=HHHH... Override serial number (string of hex chars)
* buflen=N Default N=16384, buffer size used (will be
* rounded down to a multiple of
* PAGE_CACHE_SIZE)
*
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
- * "removable", "luns", "stall", and "cdrom" options are available; default
- * values are used for everything else.
+ * "removable", "luns", "nofua", "stall", and "cdrom" options are available;
+ * default values are used for everything else.
*
* The pathnames of the backing files and the ro settings are available in
- * the attribute files "file" and "ro" in the lun<n> subdirectory of the
- * gadget's sysfs directory. If the "removable" option is set, writing to
+ * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of
+ * the gadget's sysfs directory. If the "removable" option is set, writing to
* these files will simulate ejecting/loading the medium (writing an empty
* line means eject) and adjusting a write-enable tab. Changes to the ro
* setting are not allowed when the medium is loaded or if CD-ROM emulation
@@ -270,6 +273,8 @@
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
+/* DRIVER_VERSION must be at least 6 characters long, as it is used
+ * to generate a fallback serial number. */
#define DRIVER_VERSION "20 November 2008"
static char fsg_string_manufacturer[64];
@@ -301,8 +306,10 @@ MODULE_LICENSE("Dual BSD/GPL");
static struct {
char *file[FSG_MAX_LUNS];
int ro[FSG_MAX_LUNS];
+ int nofua[FSG_MAX_LUNS];
unsigned int num_filenames;
unsigned int num_ros;
+ unsigned int num_nofuas;
unsigned int nluns;
int removable;
@@ -314,6 +321,7 @@ static struct {
unsigned short vendor;
unsigned short product;
unsigned short release;
+ char *serial;
unsigned int buflen;
int transport_type;
@@ -341,6 +349,10 @@ MODULE_PARM_DESC(file, "names of backing files or devices");
module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
MODULE_PARM_DESC(ro, "true to force read-only");
+module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
+ S_IRUGO);
+MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
+
module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
MODULE_PARM_DESC(luns, "number of LUNs");
@@ -353,6 +365,8 @@ MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
+module_param_named(serial, mod_data.serial, charp, S_IRUGO);
+MODULE_PARM_DESC(serial, "USB serial number");
/* In the non-TEST version, only the module parameters listed above
* are available. */
@@ -1272,7 +1286,8 @@ static int do_write(struct fsg_dev *fsg)
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
- if (fsg->cmnd[1] & 0x08) { // FUA
+ /* FUA */
+ if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
spin_lock(&curlun->filp->f_lock);
curlun->filp->f_flags |= O_DSYNC;
spin_unlock(&curlun->filp->f_lock);
@@ -3126,6 +3141,7 @@ static int fsg_main_thread(void *fsg_)
/* The write permissions and store_xxx pointers are set in fsg_bind() */
static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
+static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
@@ -3197,6 +3213,7 @@ static int __init check_parameters(struct fsg_dev *fsg)
{
int prot;
int gcnum;
+ int i;
/* Store the default values */
mod_data.transport_type = USB_PR_BULK;
@@ -3272,13 +3289,65 @@ static int __init check_parameters(struct fsg_dev *fsg)
ERROR(fsg, "invalid buflen\n");
return -ETOOSMALL;
}
+
#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+ /* Serial string handling.
+ * On a real device, the serial string would be loaded
+ * from permanent storage. */
+ if (mod_data.serial) {
+ const char *ch;
+ unsigned len = 0;
+
+ /* Sanity check :
+ * The CB[I] specification limits the serial string to
+ * 12 uppercase hexadecimal characters.
+ * BBB need at least 12 uppercase hexadecimal characters,
+ * with a maximum of 126. */
+ for (ch = mod_data.serial; *ch; ++ch) {
+ ++len;
+ if ((*ch < '0' || *ch > '9') &&
+ (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
+ WARNING(fsg,
+ "Invalid serial string character: %c; "
+ "Failing back to default\n",
+ *ch);
+ goto fill_serial;
+ }
+ }
+ if (len > 126 ||
+ (mod_data.transport_type == USB_PR_BULK && len < 12) ||
+ (mod_data.transport_type != USB_PR_BULK && len > 12)) {
+ WARNING(fsg,
+ "Invalid serial string length; "
+ "Failing back to default\n");
+ goto fill_serial;
+ }
+ fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
+ } else {
+ WARNING(fsg,
+ "Userspace failed to provide serial number; "
+ "Failing back to default\n");
+fill_serial:
+ /* Serial number not specified or invalid, make our own.
+ * We just encode it from the driver version string,
+ * 12 characters to comply with both CB[I] and BBB spec.
+ * Warning : Two devices running the same kernel will have
+ * the same fallback serial number. */
+ for (i = 0; i < 12; i += 2) {
+ unsigned char c = DRIVER_VERSION[i / 2];
+
+ if (!c)
+ break;
+ sprintf(&fsg_string_serial[i], "%02X", c);
+ }
+ }
+
return 0;
}
-static int __init fsg_bind(struct usb_gadget *gadget)
+static int __ref fsg_bind(struct usb_gadget *gadget)
{
struct fsg_dev *fsg = the_fsg;
int rc;
@@ -3305,6 +3374,10 @@ static int __init fsg_bind(struct usb_gadget *gadget)
}
}
+ /* Only for removable media? */
+ dev_attr_nofua.attr.mode = 0644;
+ dev_attr_nofua.store = fsg_store_nofua;
+
/* Find out how many LUNs there should be */
i = mod_data.nluns;
if (i == 0)
@@ -3330,6 +3403,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
curlun->ro = mod_data.cdrom || mod_data.ro[i];
curlun->initially_ro = curlun->ro;
curlun->removable = mod_data.removable;
+ curlun->nofua = mod_data.nofua[i];
curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
@@ -3344,6 +3418,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
if ((rc = device_create_file(&curlun->dev,
&dev_attr_ro)) != 0 ||
(rc = device_create_file(&curlun->dev,
+ &dev_attr_nofua)) != 0 ||
+ (rc = device_create_file(&curlun->dev,
&dev_attr_file)) != 0) {
device_unregister(&curlun->dev);
goto out;
@@ -3447,16 +3523,6 @@ static int __init fsg_bind(struct usb_gadget *gadget)
init_utsname()->sysname, init_utsname()->release,
gadget->name);
- /* On a real device, serial[] would be loaded from permanent
- * storage. We just encode it from the driver version string. */
- for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) {
- unsigned char c = DRIVER_VERSION[i / 2];
-
- if (!c)
- break;
- sprintf(&fsg_string_serial[i], "%02X", c);
- }
-
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
"file-storage-gadget");
if (IS_ERR(fsg->thread_task)) {
@@ -3478,8 +3544,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
if (IS_ERR(p))
p = NULL;
}
- LINFO(curlun, "ro=%d, file: %s\n",
- curlun->ro, (p ? p : "(error)"));
+ LINFO(curlun, "ro=%d, nofua=%d, file: %s\n",
+ curlun->ro, curlun->nofua, (p ? p : "(error)"));
}
}
kfree(pathbuf);
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index 9648b75..a5ea2c1 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -2398,7 +2398,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
EXPORT_SYMBOL(usb_gadget_unregister_driver);
/* udc structure's alloc and setup, include ep-param alloc */
-static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev)
+static struct qe_udc __devinit *qe_udc_config(struct platform_device *ofdev)
{
struct qe_udc *udc;
struct device_node *np = ofdev->dev.of_node;
@@ -2523,7 +2523,7 @@ static void qe_udc_release(struct device *dev)
}
/* Driver probe functions */
-static int __devinit qe_udc_probe(struct of_device *ofdev,
+static int __devinit qe_udc_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -2679,18 +2679,18 @@ err1:
}
#ifdef CONFIG_PM
-static int qe_udc_suspend(struct of_device *dev, pm_message_t state)
+static int qe_udc_suspend(struct platform_device *dev, pm_message_t state)
{
return -ENOTSUPP;
}
-static int qe_udc_resume(struct of_device *dev)
+static int qe_udc_resume(struct platform_device *dev)
{
return -ENOTSUPP;
}
#endif
-static int __devexit qe_udc_remove(struct of_device *ofdev)
+static int __devexit qe_udc_remove(struct platform_device *ofdev)
{
struct qe_ep *ep;
unsigned int size;
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index d1af253..a9474f8 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -32,12 +32,13 @@
# include "u_ether.c"
static u8 gfs_hostaddr[ETH_ALEN];
-#else
-# if !defined CONFIG_USB_FUNCTIONFS_GENERIC
-# define CONFIG_USB_FUNCTIONFS_GENERIC
+# ifdef CONFIG_USB_FUNCTIONFS_ETH
+static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
# endif
+#else
# define gether_cleanup() do { } while (0)
# define gether_setup(gadget, hostaddr) ((int)0)
+# define gfs_hostaddr NULL
#endif
#include "f_fs.c"
@@ -107,15 +108,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
enum {
GFS_STRING_MANUFACTURER_IDX,
GFS_STRING_PRODUCT_IDX,
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
- GFS_STRING_RNDIS_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_ETH
- GFS_STRING_ECM_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
- GFS_STRING_GENERIC_CONFIG_IDX,
-#endif
+ GFS_STRING_FIRST_CONFIG_IDX,
};
static char gfs_manufacturer[50];
@@ -126,13 +119,13 @@ static struct usb_string gfs_strings[] = {
[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
- [GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS",
+ { .s = "FunctionFS + RNDIS" },
#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
- [GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM",
+ { .s = "FunctionFS + ECM" },
#endif
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
- [GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS",
+ { .s = "FunctionFS" },
#endif
{ } /* end of list */
};
@@ -146,59 +139,33 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
};
+
+struct gfs_configuration {
+ struct usb_configuration c;
+ int (*eth)(struct usb_configuration *c, u8 *ethaddr);
+} gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-static int gfs_do_rndis_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_rndis_config_driver = {
- .label = "FunctionFS + RNDIS",
- .bind = gfs_do_rndis_config,
- .bConfigurationValue = 1,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-# define gfs_add_rndis_config(cdev) \
- usb_add_config(cdev, &gfs_rndis_config_driver)
-#else
-# define gfs_add_rndis_config(cdev) 0
+ {
+ .eth = rndis_bind_config,
+ },
#endif
-
#ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int gfs_do_ecm_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_ecm_config_driver = {
- .label = "FunctionFS + ECM",
- .bind = gfs_do_ecm_config,
- .bConfigurationValue = 1,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-# define gfs_add_ecm_config(cdev) \
- usb_add_config(cdev, &gfs_ecm_config_driver)
-#else
-# define gfs_add_ecm_config(cdev) 0
+ {
+ .eth = eth_bind_config,
+ },
#endif
-
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-static int gfs_do_generic_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_generic_config_driver = {
- .label = "FunctionFS",
- .bind = gfs_do_generic_config,
- .bConfigurationValue = 2,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-# define gfs_add_generic_config(cdev) \
- usb_add_config(cdev, &gfs_generic_config_driver)
-#else
-# define gfs_add_generic_config(cdev) 0
+ {
+ },
#endif
+};
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
static struct usb_composite_driver gfs_driver = {
.name = gfs_short_name,
@@ -267,7 +234,7 @@ static int functionfs_check_dev_callback(const char *dev_name)
static int gfs_bind(struct usb_composite_dev *cdev)
{
- int ret;
+ int ret, i;
ENTER();
@@ -284,57 +251,32 @@ static int gfs_bind(struct usb_composite_dev *cdev)
snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
cdev->gadget->name);
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
- gfs_dev_desc.iManufacturer = ret;
-
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
- gfs_dev_desc.iProduct = ret;
-
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret;
- gfs_rndis_config_driver.iConfiguration = ret;
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_ETH
- ret = usb_string_id(cdev);
+ ret = usb_string_ids_tab(cdev, gfs_strings);
if (unlikely(ret < 0))
goto error;
- gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret;
- gfs_ecm_config_driver.iConfiguration = ret;
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret;
- gfs_generic_config_driver.iConfiguration = ret;
-#endif
+ gfs_dev_desc.iManufacturer = gfs_strings[GFS_STRING_MANUFACTURER_IDX].id;
+ gfs_dev_desc.iProduct = gfs_strings[GFS_STRING_PRODUCT_IDX].id;
ret = functionfs_bind(gfs_ffs_data, cdev);
if (unlikely(ret < 0))
goto error;
- ret = gfs_add_rndis_config(cdev);
- if (unlikely(ret < 0))
- goto error_unbind;
+ for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+ struct gfs_configuration *c = gfs_configurations + i;
- ret = gfs_add_ecm_config(cdev);
- if (unlikely(ret < 0))
- goto error_unbind;
+ ret = GFS_STRING_FIRST_CONFIG_IDX + i;
+ c->c.label = gfs_strings[ret].s;
+ c->c.iConfiguration = gfs_strings[ret].id;
+ c->c.bind = gfs_do_config;
+ c->c.bConfigurationValue = 1 + i;
+ c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
- ret = gfs_add_generic_config(cdev);
- if (unlikely(ret < 0))
- goto error_unbind;
+ ret = usb_add_config(cdev, &c->c);
+ if (unlikely(ret < 0))
+ goto error_unbind;
+ }
return 0;
@@ -368,10 +310,10 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
}
-static int __gfs_do_config(struct usb_configuration *c,
- int (*eth)(struct usb_configuration *c, u8 *ethaddr),
- u8 *ethaddr)
+static int gfs_do_config(struct usb_configuration *c)
{
+ struct gfs_configuration *gc =
+ container_of(c, struct gfs_configuration, c);
int ret;
if (WARN_ON(!gfs_ffs_data))
@@ -382,13 +324,13 @@ static int __gfs_do_config(struct usb_configuration *c,
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- if (eth) {
- ret = eth(c, ethaddr);
+ if (gc->eth) {
+ ret = gc->eth(c, gfs_hostaddr);
if (unlikely(ret < 0))
return ret;
}
- ret = functionfs_add(c->cdev, c, gfs_ffs_data);
+ ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data);
if (unlikely(ret < 0))
return ret;
@@ -406,32 +348,12 @@ static int __gfs_do_config(struct usb_configuration *c,
return 0;
}
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-static int gfs_do_rndis_config(struct usb_configuration *c)
-{
- ENTER();
-
- return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr);
-}
-#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int gfs_do_ecm_config(struct usb_configuration *c)
-{
- ENTER();
-
- return __gfs_do_config(c,
- can_support_ecm(c->cdev->gadget)
- ? ecm_bind_config : geth_bind_config,
- gfs_hostaddr);
-}
-#endif
-
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-static int gfs_do_generic_config(struct usb_configuration *c)
+static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
- ENTER();
-
- return __gfs_do_config(c, NULL, NULL);
+ return can_support_ecm(c->cdev->gadget)
+ ? ecm_bind_config(c, ethaddr)
+ : geth_bind_config(c, ethaddr);
}
#endif
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index b7bf880..1b413a5c 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -1157,7 +1157,7 @@ fail:
/*
* Creates an output endpoint, and initializes output ports.
*/
-static int __init gmidi_bind(struct usb_gadget *gadget)
+static int __ref gmidi_bind(struct usb_gadget *gadget)
{
struct gmidi_device *dev;
struct usb_ep *in_ep, *out_ep;
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c
index 7757226..735495b 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/hid.c
@@ -127,7 +127,7 @@ static struct usb_gadget_strings *dev_strings[] = {
/****************************** Configurations ******************************/
-static int __init do_config(struct usb_configuration *c)
+static int __ref do_config(struct usb_configuration *c)
{
struct hidg_func_node *e;
int func = 0, status = 0;
@@ -156,7 +156,7 @@ static struct usb_configuration config_driver = {
/****************************** Gadget Bind ******************************/
-static int __init hid_bind(struct usb_composite_dev *cdev)
+static int __ref hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index de8a838..fc35406 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -1299,11 +1299,9 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
struct usb_gadget *gadget = dev->gadget;
long ret = -ENOTTY;
- if (gadget->ops->ioctl) {
- lock_kernel();
+ if (gadget->ops->ioctl)
ret = gadget->ops->ioctl (gadget, code, value);
- unlock_kernel();
- }
+
return ret;
}
@@ -1867,13 +1865,9 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
buf += 4;
length -= 4;
- kbuf = kmalloc (length, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
- if (copy_from_user (kbuf, buf, length)) {
- kfree (kbuf);
- return -EFAULT;
- }
+ kbuf = memdup_user(buf, length);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
spin_lock_irq (&dev->lock);
value = -EINVAL;
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index a391351..c2d2a20 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -842,9 +842,9 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
VDBG(dev, "req->mapped = 0\n");
}
- DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
- _ep->name,
- _req, _req->length, _req->buf, _req->dma);
+ DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08llx\n",
+ _ep->name,
+ _req, _req->length, _req->buf, (unsigned long long)_req->dma);
_req->status = -EINPROGRESS;
_req->actual = 0;
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 705cc1f..585f255 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -141,9 +141,14 @@ static int msg_thread_exits(struct fsg_common *common)
return 0;
}
-static int __init msg_do_config(struct usb_configuration *c)
+static int __ref msg_do_config(struct usb_configuration *c)
{
- struct fsg_common *common;
+ static const struct fsg_operations ops = {
+ .thread_exits = msg_thread_exits,
+ };
+ static struct fsg_common common;
+
+ struct fsg_common *retp;
struct fsg_config config;
int ret;
@@ -153,13 +158,14 @@ static int __init msg_do_config(struct usb_configuration *c)
}
fsg_config_from_params(&config, &mod_data);
- config.thread_exits = msg_thread_exits;
- common = fsg_common_init(0, c->cdev, &config);
- if (IS_ERR(common))
- return PTR_ERR(common);
+ config.ops = &ops;
+
+ retp = fsg_common_init(&common, c->cdev, &config);
+ if (IS_ERR(retp))
+ return PTR_ERR(retp);
- ret = fsg_add(c->cdev, c, common);
- fsg_common_put(common);
+ ret = fsg_bind_config(c->cdev, c, &common);
+ fsg_common_put(&common);
return ret;
}
@@ -176,7 +182,7 @@ static struct usb_configuration msg_config_driver = {
/****************************** Gadget Bind ******************************/
-static int __init msg_bind(struct usb_composite_dev *cdev)
+static int __ref msg_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status;
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index a930d7f..795d762 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -24,6 +24,7 @@
#include <linux/kernel.h>
#include <linux/utsname.h>
+#include <linux/module.h>
#if defined USB_ETH_RNDIS
@@ -35,14 +36,13 @@
#define DRIVER_DESC "Multifunction Composite Gadget"
-#define DRIVER_VERSION "2009/07/21"
-/*-------------------------------------------------------------------------*/
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
-#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
-#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
-/*-------------------------------------------------------------------------*/
+/***************************** All the files... *****************************/
/*
* kbuild is not very cooperative with respect to linking separately
@@ -57,6 +57,8 @@
#include "config.c"
#include "epautoconf.c"
+#include "f_mass_storage.c"
+
#include "u_serial.c"
#include "f_acm.c"
@@ -68,13 +70,24 @@
#endif
#include "u_ether.c"
-#undef DBG /* u_ether.c has broken idea about macros */
-#undef VDBG /* so clean up after it */
-#undef ERROR
-#undef INFO
-#include "f_mass_storage.c"
-/*-------------------------------------------------------------------------*/
+
+/***************************** Device Descriptor ****************************/
+
+#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
+#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
+
+
+enum {
+ __MULTI_NO_CONFIG,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ MULTI_RNDIS_CONFIG_NUM,
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_CDC_CONFIG_NUM,
+#endif
+};
+
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -82,80 +95,82 @@ static struct usb_device_descriptor device_desc = {
.bcdUSB = cpu_to_le16(0x0200),
- /* .bDeviceClass = USB_CLASS_COMM, */
- /* .bDeviceSubClass = 0, */
- /* .bDeviceProtocol = 0, */
- .bDeviceClass = 0xEF,
+ .bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
.bDeviceProtocol = 1,
- /* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
.idProduct = cpu_to_le16(MULTI_PRODUCT_NUM),
- /* .bcdDevice = f(hardware) */
- /* .iManufacturer = DYNAMIC */
- /* .iProduct = DYNAMIC */
- /* NO SERIAL NUMBER */
- .bNumConfigurations = 1,
};
-static struct usb_otg_descriptor otg_descriptor = {
- .bLength = sizeof otg_descriptor,
- .bDescriptorType = USB_DT_OTG,
-
- /* REVISIT SRP-only hardware is possible, although
- * it would not be called "OTG" ...
- */
- .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
-};
static const struct usb_descriptor_header *otg_desc[] = {
- (struct usb_descriptor_header *) &otg_descriptor,
+ (struct usb_descriptor_header *) &(struct usb_otg_descriptor){
+ .bLength = sizeof(struct usb_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ },
NULL,
};
-/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
+enum {
+ MULTI_STRING_MANUFACTURER_IDX,
+ MULTI_STRING_PRODUCT_IDX,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ MULTI_STRING_RNDIS_CONFIG_IDX,
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_STRING_CDC_CONFIG_IDX,
+#endif
+};
static char manufacturer[50];
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [MULTI_STRING_MANUFACTURER_IDX].s = manufacturer,
+ [MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
+#endif
{ } /* end of list */
};
-static struct usb_gadget_strings stringtab_dev = {
- .language = 0x0409, /* en-us */
- .strings = strings_dev,
-};
-
static struct usb_gadget_strings *dev_strings[] = {
- &stringtab_dev,
+ &(struct usb_gadget_strings){
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+ },
NULL,
};
-static u8 hostaddr[ETH_ALEN];
/****************************** Configurations ******************************/
-static struct fsg_module_parameters mod_data = {
- .stall = 1
-};
-FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+static struct fsg_common fsg_common;
+
+static u8 hostaddr[ETH_ALEN];
-static struct fsg_common *fsg_common;
+/********** RNDIS **********/
#ifdef USB_ETH_RNDIS
-static int __init rndis_do_config(struct usb_configuration *c)
+static __ref int rndis_do_config(struct usb_configuration *c)
{
int ret;
@@ -172,26 +187,42 @@ static int __init rndis_do_config(struct usb_configuration *c)
if (ret < 0)
return ret;
- ret = fsg_add(c->cdev, c, fsg_common);
+ ret = fsg_bind_config(c->cdev, c, &fsg_common);
if (ret < 0)
return ret;
return 0;
}
-static struct usb_configuration rndis_config_driver = {
- .label = "Multifunction Composite (RNDIS + MS + ACM)",
- .bind = rndis_do_config,
- .bConfigurationValue = 2,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
+static int rndis_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .bind = rndis_do_config,
+ .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
+ config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
+
+ return usb_add_config(cdev, &config);
+}
+
+#else
+
+static int rndis_config_register(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
#endif
+
+/********** CDC ECM **********/
+
#ifdef CONFIG_USB_G_MULTI_CDC
-static int __init cdc_do_config(struct usb_configuration *c)
+static __ref int cdc_do_config(struct usb_configuration *c)
{
int ret;
@@ -208,20 +239,33 @@ static int __init cdc_do_config(struct usb_configuration *c)
if (ret < 0)
return ret;
- ret = fsg_add(c->cdev, c, fsg_common);
+ ret = fsg_bind_config(c->cdev, c, &fsg_common);
if (ret < 0)
return ret;
return 0;
}
-static struct usb_configuration cdc_config_driver = {
- .label = "Multifunction Composite (CDC + MS + ACM)",
- .bind = cdc_do_config,
- .bConfigurationValue = 1,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
+static int cdc_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .bind = cdc_do_config,
+ .bConfigurationValue = MULTI_CDC_CONFIG_NUM,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
+ config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
+
+ return usb_add_config(cdev, &config);
+}
+
+#else
+
+static int cdc_config_register(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
#endif
@@ -230,7 +274,7 @@ static struct usb_configuration cdc_config_driver = {
/****************************** Gadget Bind ******************************/
-static int __init multi_bind(struct usb_composite_dev *cdev)
+static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status, gcnum;
@@ -252,67 +296,56 @@ static int __init multi_bind(struct usb_composite_dev *cdev)
goto fail0;
/* set up mass storage function */
- fsg_common = fsg_common_from_params(0, cdev, &mod_data);
- if (IS_ERR(fsg_common)) {
- status = PTR_ERR(fsg_common);
- goto fail1;
+ {
+ void *retp;
+ retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
+ if (IS_ERR(retp)) {
+ status = PTR_ERR(retp);
+ goto fail1;
+ }
}
-
+ /* set bcdDevice */
gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
+ if (gcnum >= 0) {
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- WARNING(cdev, "controller '%s' not recognized\n",
- gadget->name);
+ } else {
+ WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
}
-
- /* Allocate string descriptor numbers ... note that string
- * contents can be overridden by the composite_dev glue.
- */
-
- /* device descriptor strings: manufacturer, product */
+ /* allocate string descriptor numbers */
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail2;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
- status = usb_string_id(cdev);
- if (status < 0)
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (unlikely(status < 0))
goto fail2;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
-#ifdef USB_ETH_RNDIS
- /* register our first configuration */
- status = usb_add_config(cdev, &rndis_config_driver);
- if (status < 0)
+ device_desc.iManufacturer =
+ strings_dev[MULTI_STRING_MANUFACTURER_IDX].id;
+ device_desc.iProduct =
+ strings_dev[MULTI_STRING_PRODUCT_IDX].id;
+
+ /* register configurations */
+ status = rndis_config_register(cdev);
+ if (unlikely(status < 0))
goto fail2;
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
- /* register our second configuration */
- status = usb_add_config(cdev, &cdc_config_driver);
- if (status < 0)
+ status = cdc_config_register(cdev);
+ if (unlikely(status < 0))
goto fail2;
-#endif
- dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
- fsg_common_put(fsg_common);
+ /* we're done */
+ dev_info(&gadget->dev, DRIVER_DESC "\n");
+ fsg_common_put(&fsg_common);
return 0;
+
+ /* error recovery */
fail2:
- fsg_common_put(fsg_common);
+ fsg_common_put(&fsg_common);
fail1:
gserial_cleanup();
fail0:
@@ -339,18 +372,15 @@ static struct usb_composite_driver multi_driver = {
.unbind = __exit_p(multi_unbind),
};
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Michal Nazarewicz");
-MODULE_LICENSE("GPL");
-static int __init g_multi_init(void)
+static int __init multi_init(void)
{
return usb_composite_register(&multi_driver);
}
-module_init(g_multi_init);
+module_init(multi_init);
-static void __exit g_multi_cleanup(void)
+static void __exit multi_exit(void)
{
usb_composite_unregister(&multi_driver);
}
-module_exit(g_multi_cleanup);
+module_exit(multi_exit);
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index 4c3ac5c..cf241c3 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -25,7 +25,7 @@
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
@@ -70,6 +70,7 @@
#define DRIVER_DESC "Printer Gadget"
#define DRIVER_VERSION "2007 OCT 06"
+static DEFINE_MUTEX(printer_mutex);
static const char shortname [] = "printer";
static const char driver_desc [] = DRIVER_DESC;
@@ -476,7 +477,7 @@ printer_open(struct inode *inode, struct file *fd)
unsigned long flags;
int ret = -EBUSY;
- lock_kernel();
+ mutex_lock(&printer_mutex);
dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
spin_lock_irqsave(&dev->lock, flags);
@@ -492,7 +493,7 @@ printer_open(struct inode *inode, struct file *fd)
spin_unlock_irqrestore(&dev->lock, flags);
DBG(dev, "printer_open returned %x\n", ret);
- unlock_kernel();
+ mutex_unlock(&printer_mutex);
return ret;
}
@@ -1346,7 +1347,7 @@ printer_unbind(struct usb_gadget *gadget)
set_gadget_data(gadget, NULL);
}
-static int __init
+static int __ref
printer_bind(struct usb_gadget *gadget)
{
struct printer_dev *dev;
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 26193ec..521ebed 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -12,6 +12,8 @@
* published by the Free Software Foundation.
*/
+#define DEBUG
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
@@ -23,6 +25,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -33,6 +36,7 @@
#include <plat/regs-usb-hsotg.h>
#include <mach/regs-sys.h>
#include <plat/udc-hs.h>
+#include <plat/cpu.h>
#define DMA_ADDR_INVALID (~((dma_addr_t)0))
@@ -91,7 +95,9 @@ struct s3c_hsotg_req;
* For periodic IN endpoints, we have fifo_size and fifo_load to try
* and keep track of the amount of data in the periodic FIFO for each
* of these as we don't have a status register that tells us how much
- * is in each of them.
+ * is in each of them. (note, this may actually be useless information
+ * as in shared-fifo mode periodic in acts like a single-frame packet
+ * buffer than a fifo)
*/
struct s3c_hsotg_ep {
struct usb_ep ep;
@@ -128,6 +134,7 @@ struct s3c_hsotg_ep {
* @regs: The memory area mapped for accessing registers.
* @regs_res: The resource that was allocated when claiming register space.
* @irq: The IRQ number we are using
+ * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
* @debug_root: root directrory for debugfs.
* @debug_file: main status file for debugfs.
* @debug_fifo: FIFO status file for debugfs.
@@ -145,6 +152,9 @@ struct s3c_hsotg {
void __iomem *regs;
struct resource *regs_res;
int irq;
+ struct clk *clk;
+
+ unsigned int dedicated_fifos:1;
struct dentry *debug_root;
struct dentry *debug_file;
@@ -310,11 +320,11 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
hsotg->regs + S3C_GNPTXFSIZ);
*/
- /* set FIFO sizes to 2048/0x1C0 */
+ /* set FIFO sizes to 2048/1024 */
writel(2048, hsotg->regs + S3C_GRXFSIZ);
writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) |
- S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
+ S3C_GNPTXFSIZ_NPTxFDep(1024),
hsotg->regs + S3C_GNPTXFSIZ);
/* arange all the rest of the TX FIFOs, as some versions of this
@@ -464,7 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
if (to_write == 0)
return 0;
- if (periodic) {
+ if (periodic && !hsotg->dedicated_fifos) {
u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
int size_left;
int size_done;
@@ -474,6 +484,14 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
+ /* if shared fifo, we cannot write anything until the
+ * previous data has been completely sent.
+ */
+ if (hs_ep->fifo_load != 0) {
+ s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
+ return -ENOSPC;
+ }
+
dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
__func__, size_left,
hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
@@ -494,6 +512,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
return -ENOSPC;
}
+ } else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
+ can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index));
+
+ can_write &= 0xffff;
+ can_write *= 4;
} else {
if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) {
dev_dbg(hsotg->dev,
@@ -505,6 +528,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
}
can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts);
+ can_write *= 4; /* fifo size is in 32bit quantities. */
}
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
@@ -517,6 +541,17 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
if (can_write > 512)
can_write = 512;
+ /* limit the write to one max-packet size worth of data, but allow
+ * the transfer to return that it did not run out of fifo space
+ * doing it. */
+ if (to_write > hs_ep->ep.maxpacket) {
+ to_write = hs_ep->ep.maxpacket;
+
+ s3c_hsotg_en_gsint(hsotg,
+ periodic ? S3C_GINTSTS_PTxFEmp :
+ S3C_GINTSTS_NPTxFEmp);
+ }
+
/* see if we can write data */
if (to_write > can_write) {
@@ -579,12 +614,10 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1;
maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1;
} else {
+ maxsize = 64+64;
if (hs_ep->dir_in) {
- /* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */
- maxsize = 64+64+1;
maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1;
} else {
- maxsize = 0x3f;
maxpkt = 2;
}
}
@@ -1353,6 +1386,9 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
read_ptr = hs_req->req.actual;
max_req = hs_req->req.length - read_ptr;
+ dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
+ __func__, to_read, max_req, read_ptr, hs_req->req.length);
+
if (to_read > max_req) {
/* more data appeared than we where willing
* to deal with in this request.
@@ -1362,9 +1398,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
WARN_ON_ONCE(1);
}
- dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
- __func__, to_read, max_req, read_ptr, hs_req->req.length);
-
hs_ep->total_data += to_read;
hs_req->req.actual += to_read;
to_read = DIV_ROUND_UP(to_read, 4);
@@ -1433,9 +1466,11 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
int epnum, bool was_setup)
{
+ u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
struct s3c_hsotg_req *hs_req = hs_ep->req;
struct usb_request *req = &hs_req->req;
+ unsigned size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
int result = 0;
if (!hs_req) {
@@ -1444,9 +1479,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
}
if (using_dma(hsotg)) {
- u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
unsigned size_done;
- unsigned size_left;
/* Calculate the size of the transfer by checking how much
* is left in the endpoint size register and then working it
@@ -1456,14 +1489,18 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
* so may overshoot/undershoot the transfer.
*/
- size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
-
size_done = hs_ep->size_loaded - size_left;
size_done += hs_ep->last_load;
req->actual = size_done;
}
+ /* if there is more request to do, schedule new transfer */
+ if (req->actual < req->length && size_left == 0) {
+ s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
+ return;
+ }
+
if (req->actual < req->length && req->short_not_ok) {
dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n",
__func__, req->actual, req->length);
@@ -1758,7 +1795,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
if (dir_in) {
s3c_hsotg_complete_in(hsotg, hs_ep);
- if (idx == 0)
+ if (idx == 0 && !hs_ep->req)
s3c_hsotg_enqueue_setup(hsotg);
} else if (using_dma(hsotg)) {
/* We're using DMA, we need to fire an OutDone here
@@ -1818,6 +1855,15 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
__func__, idx);
clear |= S3C_DIEPMSK_INTknEPMisMsk;
}
+
+ /* FIFO has space or is empty (see GAHBCFG) */
+ if (hsotg->dedicated_fifos &&
+ ints & S3C_DIEPMSK_TxFIFOEmpty) {
+ dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
+ __func__, idx);
+ s3c_hsotg_trytx(hsotg, hs_ep);
+ clear |= S3C_DIEPMSK_TxFIFOEmpty;
+ }
}
writel(clear, hsotg->regs + epint_reg);
@@ -2071,17 +2117,12 @@ irq_retry:
kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true);
/* it seems after a reset we can end up with a situation
- * where the TXFIFO still has data in it... try flushing
- * it to remove anything that may still be in it.
+ * where the TXFIFO still has data in it... the docs
+ * suggest resetting all the fifos, so use the init_fifo
+ * code to relayout and flush the fifos.
*/
- if (1) {
- writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh,
- hsotg->regs + S3C_GRSTCTL);
-
- dev_info(hsotg->dev, "GNPTXSTS=%08x\n",
- readl(hsotg->regs + S3C_GNPTXSTS));
- }
+ s3c_hsotg_init_fifo(hsotg);
s3c_hsotg_enqueue_setup(hsotg);
@@ -2274,6 +2315,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
break;
}
+ /* if the hardware has dedicated fifos, we must give each IN EP
+ * a unique tx-fifo even if it is non-periodic.
+ */
+ if (dir_in && hsotg->dedicated_fifos)
+ epctrl |= S3C_DxEPCTL_TxFNum(index);
+
/* for non control endpoints, set PID to D0 */
if (index)
epctrl |= S3C_DxEPCTL_SetD0PID;
@@ -2563,7 +2610,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
S3C_DIEPMSK_INTknEPMisMsk |
- S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
+ S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk |
+ ((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0),
hsotg->regs + S3C_DIEPMSK);
/* don't need XferCompl, we get that from RXFIFO in slave mode. In
@@ -2732,7 +2780,7 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
*/
ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum));
- hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo);
+ hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4;
/* if we're using dma, we need to set the next-endpoint pointer
* to be something valid.
@@ -2753,13 +2801,33 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
*/
static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
{
- u32 osc;
+ struct clk *xusbxti;
+ u32 pwr, osc;
- writel(0, S3C_PHYPWR);
+ pwr = readl(S3C_PHYPWR);
+ pwr &= ~0x19;
+ writel(pwr, S3C_PHYPWR);
mdelay(1);
osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0;
+ xusbxti = clk_get(hsotg->dev, "xusbxti");
+ if (xusbxti && !IS_ERR(xusbxti)) {
+ switch (clk_get_rate(xusbxti)) {
+ case 12*MHZ:
+ osc |= S3C_PHYCLK_CLKSEL_12M;
+ break;
+ case 24*MHZ:
+ osc |= S3C_PHYCLK_CLKSEL_24M;
+ break;
+ default:
+ case 48*MHZ:
+ /* default reference clock */
+ break;
+ }
+ clk_put(xusbxti);
+ }
+
writel(osc | 0x10, S3C_PHYCLK);
/* issue a full set of resets to the otg and core */
@@ -2772,6 +2840,8 @@ static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
{
+ u32 cfg4;
+
/* unmask subset of endpoint interrupts */
writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
@@ -2807,6 +2877,14 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0,
hsotg->regs + S3C_GAHBCFG);
+
+ /* check hardware configuration */
+
+ cfg4 = readl(hsotg->regs + 0x50);
+ hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+
+ dev_info(hsotg->dev, "%s fifos\n",
+ hsotg->dedicated_fifos ? "dedicated" : "shared");
}
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
@@ -3181,13 +3259,20 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
hsotg->dev = dev;
hsotg->plat = plat;
+ hsotg->clk = clk_get(&pdev->dev, "otg");
+ if (IS_ERR(hsotg->clk)) {
+ dev_err(dev, "cannot get otg clock\n");
+ ret = -EINVAL;
+ goto err_mem;
+ }
+
platform_set_drvdata(pdev, hsotg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "cannot find register resource 0\n");
ret = -EINVAL;
- goto err_mem;
+ goto err_clk;
}
hsotg->regs_res = request_mem_region(res->start, resource_size(res),
@@ -3195,7 +3280,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
if (!hsotg->regs_res) {
dev_err(dev, "cannot reserve registers\n");
ret = -ENOENT;
- goto err_mem;
+ goto err_clk;
}
hsotg->regs = ioremap(res->start, resource_size(res));
@@ -3248,6 +3333,8 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
/* reset the system */
+ clk_enable(hsotg->clk);
+
s3c_hsotg_gate(pdev, true);
s3c_hsotg_otgreset(hsotg);
@@ -3271,7 +3358,8 @@ err_regs:
err_regs_res:
release_resource(hsotg->regs_res);
kfree(hsotg->regs_res);
-
+err_clk:
+ clk_put(hsotg->clk);
err_mem:
kfree(hsotg);
return ret;
@@ -3293,6 +3381,9 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
s3c_hsotg_gate(pdev, false);
+ clk_disable(hsotg->clk);
+ clk_put(hsotg->clk);
+
kfree(hsotg);
return 0;
}
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index f46a609..b22eedb 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -137,7 +137,7 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
/*-------------------------------------------------------------------------*/
-static int __init serial_bind_config(struct usb_configuration *c)
+static int __ref serial_bind_config(struct usb_configuration *c)
{
unsigned i;
int status = 0;
@@ -161,7 +161,7 @@ static struct usb_configuration serial_config_driver = {
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
-static int __init gs_bind(struct usb_composite_dev *cdev)
+static int __ref gs_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 04c462f..484acfb 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -57,10 +57,12 @@
#include <asm/unaligned.h>
-/* Thanks to NetChip Technologies for donating this product ID.
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
- * Instead: allocate your own, using normal USB-IF procedures. */
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
#define FSG_VENDOR_ID 0x0525 /* NetChip */
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
@@ -84,14 +86,27 @@
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
-/* Keep those macros in sync with thos in
- * include/linux/ubs/composite.h or else GCC will complain. If they
+/*
+ * Keep those macros in sync with those in
+ * include/linux/usb/composite.h or else GCC will complain. If they
* are identical (the same names of arguments, white spaces in the
* same places) GCC will allow redefinition otherwise (even if some
- * white space is removed or added) warning will be issued. No
- * checking if those symbols is defined is performed because warning
- * is desired when those macros were defined by someone else to mean
- * something else. */
+ * white space is removed or added) warning will be issued.
+ *
+ * Those macros are needed here because File Storage Gadget does not
+ * include the composite.h header. For composite gadgets those macros
+ * are redundant since composite.h is included any way.
+ *
+ * One could check whether those macros are already defined (which
+ * would indicate composite.h had been included) or not (which would
+ * indicate we were in FSG) but this is not done because a warning is
+ * desired if definitions here differ from the ones in composite.h.
+ *
+ * We want the definitions to match and be the same in File Storage
+ * Gadget as well as Mass Storage Function (and so composite gadgets
+ * using MSF). If someone changes them in composite.h it will produce
+ * a warning in this file when building MSF.
+ */
#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
@@ -269,6 +284,7 @@ struct fsg_lun {
unsigned int prevent_medium_removal:1;
unsigned int registered:1;
unsigned int info_valid:1;
+ unsigned int nofua:1;
u32 sense_data;
u32 sense_data_info;
@@ -313,9 +329,11 @@ struct fsg_buffhd {
enum fsg_buffer_state state;
struct fsg_buffhd *next;
- /* The NetChip 2280 is faster, and handles some protocol faults
+ /*
+ * The NetChip 2280 is faster, and handles some protocol faults
* better, if we don't submit any short bulk-out read requests.
- * So we will record the intended request length here. */
+ * So we will record the intended request length here.
+ */
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
@@ -395,8 +413,10 @@ fsg_intf_desc = {
.iInterface = FSG_STRING_INTERFACE,
};
-/* Three full-speed endpoint descriptors: bulk-in, bulk-out,
- * and interrupt-in. */
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
static struct usb_endpoint_descriptor
fsg_fs_bulk_in_desc = {
@@ -459,7 +479,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = {
*
* That means alternate endpoint descriptors (bigger packets)
* and a "device qualifier" ... plus more construction options
- * for the config descriptor.
+ * for the configuration descriptor.
*/
static struct usb_endpoint_descriptor
fsg_hs_bulk_in_desc = {
@@ -547,8 +567,10 @@ static struct usb_gadget_strings fsg_stringtab = {
/*-------------------------------------------------------------------------*/
-/* If the next two routines are called while the gadget is registered,
- * the caller must own fsg->filesem for writing. */
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
{
@@ -587,8 +609,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
goto out;
}
- /* If we can't read the file, it's no good.
- * If we can't write the file, use it read-only. */
+ /*
+ * If we can't read the file, it's no good.
+ * If we can't write the file, use it read-only.
+ */
if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
LINFO(curlun, "file not readable: %s\n", filename);
goto out;
@@ -646,8 +670,10 @@ static void fsg_lun_close(struct fsg_lun *curlun)
/*-------------------------------------------------------------------------*/
-/* Sync the file data, don't bother with the metadata.
- * This code was copied from fs/buffer.c:sys_fdatasync(). */
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
@@ -689,6 +715,14 @@ static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
: curlun->initially_ro);
}
+static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%u\n", curlun->nofua);
+}
+
static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -723,26 +757,47 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
ssize_t rc = count;
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
struct rw_semaphore *filesem = dev_get_drvdata(dev);
- int i;
+ unsigned long ro;
- if (sscanf(buf, "%d", &i) != 1)
+ if (strict_strtoul(buf, 2, &ro))
return -EINVAL;
- /* Allow the write-enable status to change only while the backing file
- * is closed. */
+ /*
+ * Allow the write-enable status to change only while the
+ * backing file is closed.
+ */
down_read(filesem);
if (fsg_lun_is_open(curlun)) {
LDBG(curlun, "read-only status change prevented\n");
rc = -EBUSY;
} else {
- curlun->ro = !!i;
- curlun->initially_ro = !!i;
+ curlun->ro = ro;
+ curlun->initially_ro = ro;
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
}
up_read(filesem);
return rc;
}
+static ssize_t fsg_store_nofua(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ unsigned long nofua;
+
+ if (strict_strtoul(buf, 2, &nofua))
+ return -EINVAL;
+
+ /* Sync data when switching from async mode to sync */
+ if (!nofua && curlun->nofua)
+ fsg_lun_fsync_sub(curlun);
+
+ curlun->nofua = nofua;
+
+ return count;
+}
+
static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 1da755a..6bb876d 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -704,17 +704,6 @@ static char *host_addr;
module_param(host_addr, charp, S_IRUGO);
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
-
-static u8 __init nibble(unsigned char c)
-{
- if (isdigit(c))
- return c - '0';
- c = toupper(c);
- if (isxdigit(c))
- return 10 + c - 'A';
- return 0;
-}
-
static int get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
@@ -725,8 +714,8 @@ static int get_ether_addr(const char *str, u8 *dev_addr)
if ((*str == '.') || (*str == ':'))
str++;
- num = nibble(*str++) << 4;
- num |= (nibble(*str++));
+ num = hex_to_bin(*str++) << 4;
+ num |= hex_to_bin(*str++);
dev_addr [i] = num;
}
if (is_valid_ether_addr(dev_addr))
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 3e8dcb5..01e5354 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -18,6 +18,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/delay.h>
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c
index 288d211..de1deb7 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/webcam.c
@@ -308,7 +308,7 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
* USB configuration
*/
-static int __init
+static int __ref
webcam_config_bind(struct usb_configuration *c)
{
return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
@@ -330,7 +330,7 @@ webcam_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static int __init
+static int __ref
webcam_bind(struct usb_composite_dev *cdev)
{
int ret;
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 807280d..cf353920 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -264,7 +264,7 @@ static void zero_resume(struct usb_composite_dev *cdev)
/*-------------------------------------------------------------------------*/
-static int __init zero_bind(struct usb_composite_dev *cdev)
+static int __ref zero_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f865be2..2d926ce 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -72,8 +72,9 @@ config USB_EHCI_ROOT_HUB_TT
from ARC, and has since changed hands a few times.
config USB_EHCI_TT_NEWSCHED
- bool "Improved Transaction Translator scheduling (EXPERIMENTAL)"
- depends on USB_EHCI_HCD && EXPERIMENTAL
+ bool "Improved Transaction Translator scheduling"
+ depends on USB_EHCI_HCD
+ default y
---help---
This changes the periodic scheduling code to fill more of the low
and full speed bandwidth available from the Transaction Translator
@@ -84,9 +85,11 @@ config USB_EHCI_TT_NEWSCHED
If you have multiple periodic low/fullspeed devices connected to a
highspeed USB hub which is connected to a highspeed USB Host
Controller, and some of those devices will not work correctly
- (possibly due to "ENOSPC" or "-28" errors), say Y.
+ (possibly due to "ENOSPC" or "-28" errors), say Y. Conversely, if
+ you have only one such device and it doesn't work, you could try
+ saying N.
- If unsure, say N.
+ If unsure, say Y.
config USB_EHCI_BIG_ENDIAN_MMIO
bool
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index faa6174..2baf8a8 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -228,7 +228,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
* the root hub is either suspended or stopped.
*/
spin_lock_irqsave(&ehci->lock, flags);
- ehci_prepare_ports_for_controller_suspend(ehci);
+ ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 874d200..76b7fd2 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
} else {
ehci_dbg (ehci,
- "%s hcc_params %04x thresh %d uframes %s%s%s\n",
+ "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
label,
params,
HCC_ISOC_THRES(params),
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
HCC_CANPARK(params) ? " park" : "",
- HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
+ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "",
+ HCC_LPM(params) ? " LPM" : "",
+ HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "",
+ HCC_HW_PREFETCH(params) ? " hw prefetch" : "",
+ HCC_32FRAME_PERIODIC_LIST(params) ?
+ " 32 peridic list" : "");
}
}
#else
@@ -191,8 +196,9 @@ static int __maybe_unused
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{
return scnprintf (buf, len,
- "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", status,
+ (status & STS_PPCE_MASK) ? " PPCE" : "",
(status & STS_ASS) ? " Async" : "",
(status & STS_PSS) ? " Periodic" : "",
(status & STS_RECL) ? " Recl" : "",
@@ -210,8 +216,9 @@ static int __maybe_unused
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{
return scnprintf (buf, len,
- "%s%sintrenable %02x%s%s%s%s%s%s",
+ "%s%sintrenable %02x%s%s%s%s%s%s%s",
label, label [0] ? " " : "", enable,
+ (enable & STS_PPCE_MASK) ? " PPCE" : "",
(enable & STS_IAA) ? " IAA" : "",
(enable & STS_FATAL) ? " FATAL" : "",
(enable & STS_FLR) ? " FLR" : "",
@@ -228,9 +235,15 @@ static int
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{
return scnprintf (buf, len,
- "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
+ "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
+ "period=%s%s %s",
label, label [0] ? " " : "", command,
- (command & CMD_PARK) ? "park" : "(park)",
+ (command & CMD_HIRD) ? " HIRD" : "",
+ (command & CMD_PPCEE) ? " PPCEE" : "",
+ (command & CMD_FSP) ? " FSP" : "",
+ (command & CMD_ASPE) ? " ASPE" : "",
+ (command & CMD_PSPE) ? " PSPE" : "",
+ (command & CMD_PARK) ? " park" : "(park)",
CMD_PARK_CNT (command),
(command >> 16) & 0x3f,
(command & CMD_LRESET) ? " LReset" : "",
@@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
}
return scnprintf (buf, len,
- "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s",
+ "%s%sport:%d status %06x %d %s%s%s%s%s%s "
+ "sig=%s%s%s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", port, status,
+ status>>25,/*device address */
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
+ " ACK" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
+ " NYET" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
+ " STALL" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
+ " ERR" : "",
(status & PORT_POWER) ? " POWER" : "",
(status & PORT_OWNER) ? " OWNER" : "",
sig,
+ (status & PORT_LPM) ? " LPM" : "",
(status & PORT_RESET) ? " RESET" : "",
(status & PORT_SUSPEND) ? " SUSPEND" : "",
(status & PORT_RESUME) ? " RESUME" : "",
@@ -330,6 +354,13 @@ static int debug_async_open(struct inode *, struct file *);
static int debug_periodic_open(struct inode *, struct file *);
static int debug_registers_open(struct inode *, struct file *);
static int debug_async_open(struct inode *, struct file *);
+static int debug_lpm_open(struct inode *, struct file *);
+static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos);
+static ssize_t debug_lpm_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos);
+static int debug_lpm_close(struct inode *inode, struct file *file);
+
static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
static int debug_close(struct inode *, struct file *);
@@ -351,6 +382,13 @@ static const struct file_operations debug_registers_fops = {
.read = debug_output,
.release = debug_close,
};
+static const struct file_operations debug_lpm_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_lpm_open,
+ .read = debug_lpm_read,
+ .write = debug_lpm_write,
+ .release = debug_lpm_close,
+};
static struct dentry *ehci_debug_root;
@@ -674,7 +712,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
spin_lock_irqsave (&ehci->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
size = scnprintf (next, size,
"bus %s, device %s\n"
"%s\n"
@@ -917,51 +955,127 @@ static int debug_registers_open(struct inode *inode, struct file *file)
return file->private_data ? 0 : -ENOMEM;
}
+static int debug_lpm_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int debug_lpm_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* TODO: show lpm stats */
+ return 0;
+}
+
+static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ char buf[50];
+ size_t len;
+ u32 temp;
+ unsigned long port;
+ u32 __iomem *portsc ;
+ u32 params;
+
+ hcd = bus_to_hcd(file->private_data);
+ ehci = hcd_to_ehci(hcd);
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ if (strncmp(buf, "enable", 5) == 0) {
+ if (strict_strtoul(buf + 7, 10, &port))
+ return -EINVAL;
+ params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ if (port > HCS_N_PORTS(params)) {
+ ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port);
+ return -ENODEV;
+ }
+ portsc = &ehci->regs->port_status[port-1];
+ temp = ehci_readl(ehci, portsc);
+ if (!(temp & PORT_DEV_ADDR)) {
+ ehci_dbg(ehci, "LPM: no device attached\n");
+ return -ENODEV;
+ }
+ temp |= PORT_LPM;
+ ehci_writel(ehci, temp, portsc);
+ printk(KERN_INFO "force enable LPM for port %lu\n", port);
+ } else if (strncmp(buf, "hird=", 5) == 0) {
+ unsigned long hird;
+ if (strict_strtoul(buf + 5, 16, &hird))
+ return -EINVAL;
+ printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird);
+ temp = ehci_readl(ehci, &ehci->regs->command);
+ temp &= ~CMD_HIRD;
+ temp |= hird << 24;
+ ehci_writel(ehci, temp, &ehci->regs->command);
+ } else if (strncmp(buf, "disable", 7) == 0) {
+ if (strict_strtoul(buf + 8, 10, &port))
+ return -EINVAL;
+ params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ if (port > HCS_N_PORTS(params)) {
+ ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port);
+ return -ENODEV;
+ }
+ portsc = &ehci->regs->port_status[port-1];
+ temp = ehci_readl(ehci, portsc);
+ if (!(temp & PORT_DEV_ADDR)) {
+ ehci_dbg(ehci, "ERR: no device attached\n");
+ return -ENODEV;
+ }
+ temp &= ~PORT_LPM;
+ ehci_writel(ehci, temp, portsc);
+ printk(KERN_INFO "disabled LPM for port %lu\n", port);
+ } else
+ return -EOPNOTSUPP;
+ return count;
+}
+
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root);
if (!ehci->debug_dir)
- goto dir_error;
-
- ehci->debug_async = debugfs_create_file("async", S_IRUGO,
- ehci->debug_dir, bus,
- &debug_async_fops);
- if (!ehci->debug_async)
- goto async_error;
-
- ehci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
- ehci->debug_dir, bus,
- &debug_periodic_fops);
- if (!ehci->debug_periodic)
- goto periodic_error;
-
- ehci->debug_registers = debugfs_create_file("registers", S_IRUGO,
- ehci->debug_dir, bus,
- &debug_registers_fops);
- if (!ehci->debug_registers)
- goto registers_error;
+ return;
+
+ if (!debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus,
+ &debug_async_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
+ &debug_periodic_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus,
+ &debug_registers_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("lpm", S_IRUGO|S_IWUGO, ehci->debug_dir, bus,
+ &debug_lpm_fops))
+ goto file_error;
+
return;
-registers_error:
- debugfs_remove(ehci->debug_periodic);
-periodic_error:
- debugfs_remove(ehci->debug_async);
-async_error:
- debugfs_remove(ehci->debug_dir);
-dir_error:
- ehci->debug_periodic = NULL;
- ehci->debug_async = NULL;
- ehci->debug_dir = NULL;
+file_error:
+ debugfs_remove_recursive(ehci->debug_dir);
}
static inline void remove_debug_files (struct ehci_hcd *ehci)
{
- debugfs_remove(ehci->debug_registers);
- debugfs_remove(ehci->debug_periodic);
- debugfs_remove(ehci->debug_async);
- debugfs_remove(ehci->debug_dir);
+ debugfs_remove_recursive(ehci->debug_dir);
}
#endif /* STUB_DEBUG_FILES */
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 5cd967d..a416421 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -313,7 +313,8 @@ static int ehci_fsl_drv_suspend(struct device *dev)
struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
void __iomem *non_ehci = hcd->regs;
- ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd));
+ ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
+ device_may_wakeup(dev));
if (!fsl_deep_sleep())
return 0;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a3ef2a9..34a928d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -36,6 +36,7 @@
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include <asm/io.h>
@@ -78,7 +79,13 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1
-#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
+/*
+ * Some drivers think it's safe to schedule isochronous transfers more than
+ * 256 ms into the future (partly as a result of an old bug in the scheduling
+ * code). In an attempt to avoid trouble, we will use a minimum scheduling
+ * length of 512 frames instead of 256.
+ */
+#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
@@ -100,6 +107,11 @@ static int ignore_oc = 0;
module_param (ignore_oc, bool, S_IRUGO);
MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
+/* for link power management(LPM) feature */
+static unsigned int hird;
+module_param(hird, int, S_IRUGO);
+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
+
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
/*-------------------------------------------------------------------------*/
@@ -304,6 +316,7 @@ static void end_unlink_async(struct ehci_hcd *ehci);
static void ehci_work(struct ehci_hcd *ehci);
#include "ehci-hub.c"
+#include "ehci-lpm.c"
#include "ehci-mem.c"
#include "ehci-q.c"
#include "ehci-sched.c"
@@ -577,6 +590,11 @@ static int ehci_init(struct usb_hcd *hcd)
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) {
+ ehci->has_ppcd = 1;
+ ehci_dbg(ehci, "enable per-port change event\n");
+ temp |= CMD_PPCEE;
+ }
if (HCC_CANPARK(hcc_params)) {
/* HW default park == 3, on hardware that supports it (like
* NVidia and ALI silicon), maximizes throughput on the async
@@ -603,10 +621,22 @@ static int ehci_init(struct usb_hcd *hcd)
default: BUG();
}
}
+ if (HCC_LPM(hcc_params)) {
+ /* support link power management EHCI 1.1 addendum */
+ ehci_dbg(ehci, "support lpm\n");
+ ehci->has_lpm = 1;
+ if (hird > 0xf) {
+ ehci_dbg(ehci, "hird %d invalid, use default 0",
+ hird);
+ hird = 0;
+ }
+ temp |= hird << 24;
+ }
ehci->command = temp;
/* Accept arbitrarily long scatter-gather lists */
- hcd->self.sg_tablesize = ~0;
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
return 0;
}
@@ -619,7 +649,6 @@ static int ehci_run (struct usb_hcd *hcd)
u32 hcc_params;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
/* EHCI spec section 4.1 */
if ((retval = ehci_reset(ehci)) != 0) {
@@ -764,6 +793,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* remote wakeup [4.3.1] */
if (status & STS_PCD) {
unsigned i = HCS_N_PORTS (ehci->hcs_params);
+ u32 ppcd = 0;
/* kick root hub later */
pcd_status = status;
@@ -772,9 +802,18 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
if (!(cmd & CMD_RUN))
usb_hcd_resume_root_hub(hcd);
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = status >> 16;
+
while (i--) {
- int pstatus = ehci_readl(ehci,
- &ehci->regs->port_status [i]);
+ int pstatus;
+
+ /* leverage per-port change bits feature */
+ if (ehci->has_ppcd && !(ppcd & (1 << i)))
+ continue;
+ pstatus = ehci_readl(ehci,
+ &ehci->regs->port_status[i]);
if (pstatus & PORT_OWNER)
continue;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index e7d3d8d..796ea0c 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
}
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
- bool suspending)
+ bool suspending, bool do_wakeup)
{
int port;
u32 temp;
@@ -117,8 +117,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
* when the controller is suspended or resumed. In all other
* cases they don't need to be changed.
*/
- if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup ||
- device_may_wakeup(ehci_to_hcd(ehci)->self.controller))
+ if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
return;
/* clear phy low-power mode before changing wakeup flags */
@@ -167,6 +166,10 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
}
}
+
+ /* Does the root hub have a port wakeup pending? */
+ if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD))
+ usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
}
static int ehci_bus_suspend (struct usb_hcd *hcd)
@@ -316,7 +319,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
spin_lock_irq (&ehci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
spin_unlock_irq(&ehci->lock);
return -ESHUTDOWN;
}
@@ -603,6 +606,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
u32 mask;
int ports, i, retval = 1;
unsigned long flags;
+ u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
@@ -632,7 +636,15 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
/* port N changes (bit N)? */
spin_lock_irqsave (&ehci->lock, flags);
+
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
+
for (i = 0; i < ports; i++) {
+ /* leverage per-port change bits feature */
+ if (ehci->has_ppcd && !(ppcd & (1 << i)))
+ continue;
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
/*
@@ -790,6 +802,11 @@ static int ehci_hub_control (
status_reg);
break;
case USB_PORT_FEAT_C_CONNECTION:
+ if (ehci->has_lpm) {
+ /* clear PORTSC bits on disconnect */
+ temp &= ~PORT_LPM;
+ temp &= ~PORT_DEV_ADDR;
+ }
ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC,
status_reg);
break;
diff --git a/drivers/usb/host/ehci-lpm.c b/drivers/usb/host/ehci-lpm.c
new file mode 100644
index 0000000..b4d4d63
--- /dev/null
+++ b/drivers/usb/host/ehci-lpm.c
@@ -0,0 +1,83 @@
+/* ehci-lpm.c EHCI HCD LPM support code
+ * Copyright (c) 2008 - 2010, Intel Corporation.
+ * Author: Jacob Pan <jacob.jun.pan@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* this file is part of ehci-hcd.c */
+static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num)
+{
+ u32 __iomem portsc;
+
+ ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num);
+ if (port_num > HCS_N_PORTS(ehci->hcs_params)) {
+ ehci_dbg(ehci, "invalid port number %d\n", port_num);
+ return -ENODEV;
+ }
+ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]);
+ portsc &= ~PORT_DEV_ADDR;
+ portsc |= dev_addr<<25;
+ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]);
+ return 0;
+}
+
+/*
+ * this function is used to check if the device support LPM
+ * if yes, mark the PORTSC register with PORT_LPM bit
+ */
+static int ehci_lpm_check(struct ehci_hcd *ehci, int port)
+{
+ u32 __iomem *portsc ;
+ u32 val32;
+ int retval;
+
+ portsc = &ehci->regs->port_status[port-1];
+ val32 = ehci_readl(ehci, portsc);
+ if (!(val32 & PORT_DEV_ADDR)) {
+ ehci_dbg(ehci, "LPM: no device attached\n");
+ return -ENODEV;
+ }
+ val32 |= PORT_LPM;
+ ehci_writel(ehci, val32, portsc);
+ msleep(5);
+ val32 |= PORT_SUSPEND;
+ ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port);
+ ehci_writel(ehci, val32, portsc);
+ /* wait for ACK */
+ msleep(10);
+ retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS,
+ PORTSC_SUSPEND_STS_ACK, 125);
+ dbg_port(ehci, "LPM", port, val32);
+ if (retval != -ETIMEDOUT) {
+ ehci_dbg(ehci, "LPM: device ACK for LPM\n");
+ val32 |= PORT_LPM;
+ /*
+ * now device should be in L1 sleep, let's wake up the device
+ * so that we can complete enumeration.
+ */
+ ehci_writel(ehci, val32, portsc);
+ msleep(10);
+ val32 |= PORT_RESUME;
+ ehci_writel(ehci, val32, portsc);
+ } else {
+ ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n",
+ retval);
+ val32 &= ~PORT_LPM;
+ retval = -ETIMEDOUT;
+ ehci_writel(ehci, val32, portsc);
+ }
+
+ return retval;
+}
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 5450e62..116ae28 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -38,6 +38,7 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/usb/ulpi.h>
#include <plat/usb.h>
/*
@@ -236,6 +237,35 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
/*-------------------------------------------------------------------------*/
+static void omap_ehci_soft_phy_reset(struct ehci_hcd_omap *omap, u8 port)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ unsigned reg = 0;
+
+ reg = ULPI_FUNC_CTRL_RESET
+ /* FUNCTION_CTRL_SET register */
+ | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
+ /* Write */
+ | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
+ /* PORTn */
+ | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
+ /* start ULPI access*/
+ | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
+
+ ehci_omap_writel(omap->ehci_base, EHCI_INSNREG05_ULPI, reg);
+
+ /* Wait for ULPI access completion */
+ while ((ehci_omap_readl(omap->ehci_base, EHCI_INSNREG05_ULPI)
+ & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(omap->dev, "phy reset operation timed out\n");
+ break;
+ }
+ }
+}
+
/* omap_start_ehc
* - Start the TI USBHOST controller
*/
@@ -425,6 +455,12 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
gpio_set_value(omap->reset_gpio_port[1], 1);
}
+ /* Soft reset the PHY using PHY reset command over ULPI */
+ if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
+ omap_ehci_soft_phy_reset(omap, 0);
+ if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
+ omap_ehci_soft_phy_reset(omap, 1);
+
return 0;
err_sys_status:
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index d43d176..58b72d7 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -114,6 +114,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
break;
case PCI_VENDOR_ID_INTEL:
ehci->need_io_watchdog = 0;
+ ehci->fs_i_thresh = 1;
if (pdev->device == 0x27cc) {
ehci->broken_periodic = 1;
ehci_info(ehci, "using broken periodic workaround\n");
@@ -277,7 +278,7 @@ done:
* Also they depend on separate root hub suspend/resume.
*/
-static int ehci_pci_suspend(struct usb_hcd *hcd)
+static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
unsigned long flags;
@@ -291,7 +292,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd)
* the root hub is either suspended or stopped.
*/
spin_lock_irqsave (&ehci->lock, flags);
- ehci_prepare_ports_for_controller_suspend(ehci);
+ ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
@@ -361,6 +362,22 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
}
#endif
+static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int rc = 0;
+
+ if (!udev->parent) /* udev is root hub itself, impossible */
+ rc = -1;
+ /* we only support lpm device connected to root hub yet */
+ if (ehci->has_lpm && !udev->parent->parent) {
+ rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
+ if (!rc)
+ rc = ehci_lpm_check(ehci, udev->portnum);
+ }
+ return rc;
+}
+
static const struct hc_driver ehci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "EHCI Host Controller",
@@ -407,6 +424,11 @@ static const struct hc_driver ehci_pci_hc_driver = {
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
+ /*
+ * call back when device connected and addressed
+ */
+ .update_device = ehci_update_device,
+
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index 5aec928..335ee69 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -106,7 +106,7 @@ ppc44x_enable_bmt(struct device_node *dn)
static int __devinit
-ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
+ehci_hcd_ppc_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
@@ -210,7 +210,7 @@ err_rmr:
}
-static int ehci_hcd_ppc_of_remove(struct of_device *op)
+static int ehci_hcd_ppc_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -253,7 +253,7 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op)
}
-static int ehci_hcd_ppc_of_shutdown(struct of_device *op)
+static int ehci_hcd_ppc_of_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 11a79c4..233c288 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1126,8 +1126,7 @@ submit_async (
#endif
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
rc = -ESHUTDOWN;
goto done;
}
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 805ec63..a92526d 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -880,8 +880,7 @@ static int intr_submit (
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
status = -ESHUTDOWN;
goto done_not_linked;
}
@@ -1075,15 +1074,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
if (stream->ep)
stream->ep->hcpriv = NULL;
- if (stream->rescheduled) {
- ehci_info (ehci, "ep%d%s-iso rescheduled "
- "%lu times in %lu seconds\n",
- stream->bEndpointAddress, is_in ? "in" : "out",
- stream->rescheduled,
- ((jiffies - stream->start)/HZ)
- );
- }
-
kfree(stream);
}
}
@@ -1396,30 +1386,25 @@ iso_stream_schedule (
struct ehci_iso_stream *stream
)
{
- u32 now, next, start, period;
+ u32 now, next, start, period, span;
int status;
unsigned mod = ehci->periodic_size << 3;
struct ehci_iso_sched *sched = urb->hcpriv;
- struct pci_dev *pdev;
- if (sched->span > (mod - SCHEDULE_SLOP)) {
- ehci_dbg (ehci, "iso request %p too long\n", urb);
- status = -EFBIG;
- goto fail;
+ period = urb->interval;
+ span = sched->span;
+ if (!stream->highspeed) {
+ period <<= 3;
+ span <<= 3;
}
- if ((stream->depth + sched->span) > mod) {
- ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n",
- urb, stream->depth, sched->span, mod);
+ if (span > mod - SCHEDULE_SLOP) {
+ ehci_dbg (ehci, "iso request %p too long\n", urb);
status = -EFBIG;
goto fail;
}
- period = urb->interval;
- if (!stream->highspeed)
- period <<= 3;
-
- now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
+ now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
/* Typical case: reuse current schedule, stream is still active.
* Hopefully there are no gaps from the host falling behind
@@ -1427,34 +1412,35 @@ iso_stream_schedule (
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
*/
if (likely (!list_empty (&stream->td_list))) {
- pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
- start = stream->next_uframe;
+ u32 excess;
/* For high speed devices, allow scheduling within the
- * isochronous scheduling threshold. For full speed devices,
- * don't. (Work around for Intel ICH9 bug.)
+ * isochronous scheduling threshold. For full speed devices
+ * and Intel PCI-based controllers, don't (work around for
+ * Intel ICH9 bug).
*/
- if (!stream->highspeed &&
- pdev->vendor == PCI_VENDOR_ID_INTEL)
+ if (!stream->highspeed && ehci->fs_i_thresh)
next = now + ehci->i_thresh;
else
next = now;
- /* Fell behind (by up to twice the slop amount)? */
- if (((start - next) & (mod - 1)) >=
- mod - 2 * SCHEDULE_SLOP)
- start += period * DIV_ROUND_UP(
- (next - start) & (mod - 1),
- period);
-
- /* Tried to schedule too far into the future? */
- if (unlikely(((start - now) & (mod - 1)) + sched->span
- >= mod - 2 * SCHEDULE_SLOP)) {
+ /* Fell behind (by up to twice the slop amount)?
+ * We decide based on the time of the last currently-scheduled
+ * slot, not the time of the next available slot.
+ */
+ excess = (stream->next_uframe - period - next) & (mod - 1);
+ if (excess >= mod - 2 * SCHEDULE_SLOP)
+ start = next + excess - mod + period *
+ DIV_ROUND_UP(mod - excess, period);
+ else
+ start = next + excess + period;
+ if (start - now >= mod) {
+ ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now - period, period,
+ mod);
status = -EFBIG;
goto fail;
}
- stream->next_uframe = start;
- goto ready;
}
/* need to schedule; when's the next (u)frame we could start?
@@ -1463,51 +1449,60 @@ iso_stream_schedule (
* can also help high bandwidth if the dma and irq loads don't
* jump until after the queue is primed.
*/
- start = SCHEDULE_SLOP + (now & ~0x07);
- start %= mod;
- stream->next_uframe = start;
-
- /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
-
- /* find a uframe slot with enough bandwidth */
- for (; start < (stream->next_uframe + period); start++) {
- int enough_space;
-
- /* check schedule: enough space? */
- if (stream->highspeed)
- enough_space = itd_slot_ok (ehci, mod, start,
- stream->usecs, period);
- else {
- if ((start % 8) >= 6)
- continue;
- enough_space = sitd_slot_ok (ehci, mod, stream,
- start, sched, period);
+ else {
+ start = SCHEDULE_SLOP + (now & ~0x07);
+
+ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
+
+ /* find a uframe slot with enough bandwidth */
+ next = start + period;
+ for (; start < next; start++) {
+
+ /* check schedule: enough space? */
+ if (stream->highspeed) {
+ if (itd_slot_ok(ehci, mod, start,
+ stream->usecs, period))
+ break;
+ } else {
+ if ((start % 8) >= 6)
+ continue;
+ if (sitd_slot_ok(ehci, mod, stream,
+ start, sched, period))
+ break;
+ }
}
- /* schedule it here if there's enough bandwidth */
- if (enough_space) {
- stream->next_uframe = start % mod;
- goto ready;
+ /* no room in the schedule */
+ if (start == next) {
+ ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n",
+ urb, now, now + mod);
+ status = -ENOSPC;
+ goto fail;
}
}
- /* no room in the schedule */
- ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
- list_empty (&stream->td_list) ? "" : "re",
- urb, now, now + mod);
- status = -ENOSPC;
+ /* Tried to schedule too far into the future? */
+ if (unlikely(start - now + span - period
+ >= mod - 2 * SCHEDULE_SLOP)) {
+ ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now, span - period,
+ mod - 2 * SCHEDULE_SLOP);
+ status = -EFBIG;
+ goto fail;
+ }
-fail:
- iso_sched_free (stream, sched);
- urb->hcpriv = NULL;
- return status;
+ stream->next_uframe = start & (mod - 1);
-ready:
/* report high speed start in uframes; full speed, in frames */
urb->start_frame = stream->next_uframe;
if (!stream->highspeed)
urb->start_frame >>= 3;
return 0;
+
+ fail:
+ iso_sched_free(stream, sched);
+ urb->hcpriv = NULL;
+ return status;
}
/*-------------------------------------------------------------------------*/
@@ -1602,7 +1597,7 @@ itd_link_urb (
struct ehci_iso_sched *iso_sched = urb->hcpriv;
struct ehci_itd *itd;
- next_uframe = stream->next_uframe % mod;
+ next_uframe = stream->next_uframe & (mod - 1);
if (unlikely (list_empty(&stream->td_list))) {
ehci_to_hcd(ehci)->self.bandwidth_allocated
@@ -1613,7 +1608,6 @@ itd_link_urb (
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
urb->interval,
next_uframe >> 3, next_uframe & 0x7);
- stream->start = jiffies;
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -1639,14 +1633,13 @@ itd_link_urb (
itd_patch(ehci, itd, iso_sched, packet, uframe);
next_uframe += stream->interval;
- stream->depth += stream->interval;
- next_uframe %= mod;
+ next_uframe &= mod - 1;
packet++;
/* link completed itds into the schedule */
if (((next_uframe >> 3) != frame)
|| packet == urb->number_of_packets) {
- itd_link (ehci, frame % ehci->periodic_size, itd);
+ itd_link(ehci, frame & (ehci->periodic_size - 1), itd);
itd = NULL;
}
}
@@ -1695,7 +1688,6 @@ itd_complete (
t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
itd->hw_transaction [uframe] = 0;
- stream->depth -= stream->interval;
/* report transfer status */
if (unlikely (t & ISO_ERRS)) {
@@ -1815,8 +1807,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
status = -ESHUTDOWN;
goto done_not_linked;
}
@@ -2024,9 +2015,8 @@ sitd_link_urb (
"sched devp %s ep%d%s-iso [%d] %dms/%04x\n",
urb->dev->devpath, stream->bEndpointAddress & 0x0f,
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
- (next_uframe >> 3) % ehci->periodic_size,
+ (next_uframe >> 3) & (ehci->periodic_size - 1),
stream->interval, hc32_to_cpu(ehci, stream->splits));
- stream->start = jiffies;
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -2047,13 +2037,12 @@ sitd_link_urb (
sitd->urb = urb;
sitd_patch(ehci, stream, sitd, sched, packet);
- sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
+ sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
sitd);
next_uframe += stream->interval << 3;
- stream->depth += stream->interval << 3;
}
- stream->next_uframe = next_uframe % mod;
+ stream->next_uframe = next_uframe & (mod - 1);
/* don't need that schedule data any more */
iso_sched_free (stream, sched);
@@ -2111,7 +2100,6 @@ sitd_complete (
desc->actual_length = desc->length - SITD_LENGTH(t);
urb->actual_length += desc->actual_length;
}
- stream->depth -= stream->interval << 3;
/* handle completion now? */
if ((urb_index + 1) != urb->number_of_packets)
@@ -2201,8 +2189,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
status = -ESHUTDOWN;
goto done_not_linked;
}
@@ -2263,7 +2250,7 @@ scan_periodic (struct ehci_hcd *ehci)
now_uframe = ehci->next_uframe;
if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
clock = ehci_readl(ehci, &ehci->regs->frame_index);
- clock_frame = (clock >> 3) % ehci->periodic_size;
+ clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
} else {
clock = now_uframe + mod - 1;
clock_frame = -1;
@@ -2272,7 +2259,7 @@ scan_periodic (struct ehci_hcd *ehci)
free_cached_lists(ehci);
ehci->clock_frame = clock_frame;
}
- clock %= mod;
+ clock &= mod - 1;
clock_frame = clock >> 3;
for (;;) {
@@ -2361,7 +2348,7 @@ restart:
* frame is current.
*/
if (((frame == clock_frame) ||
- (((frame + 1) % ehci->periodic_size)
+ (((frame + 1) & (ehci->periodic_size - 1))
== clock_frame))
&& live
&& (q.sitd->hw_results &
@@ -2428,7 +2415,8 @@ restart:
|| ehci->periodic_sched == 0)
break;
ehci->next_uframe = now_uframe;
- now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
+ now = ehci_readl(ehci, &ehci->regs->frame_index) &
+ (mod - 1);
if (now_uframe == now)
break;
@@ -2441,7 +2429,7 @@ restart:
}
} else {
now_uframe++;
- now_uframe %= mod;
+ now_uframe &= mod - 1;
}
}
}
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index 4899f45..6c8076a 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -140,7 +140,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
/**
* ehci_hcd_xilinx_of_probe - Probe method for the USB host controller
- * @op: pointer to the of_device to which the host controller bound
+ * @op: pointer to the platform_device bound to the host controller
* @match: pointer to of_device_id structure, not used
*
* This function requests resources and sets up appropriate properties for the
@@ -149,7 +149,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
* entry, and sets an appropriate value for hcd->has_tt.
*/
static int __devinit
-ehci_hcd_xilinx_of_probe(struct of_device *op, const struct of_device_id *match)
+ehci_hcd_xilinx_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
@@ -242,12 +242,12 @@ err_rmr:
/**
* ehci_hcd_xilinx_of_remove - shutdown hcd and release resources
- * @op: pointer to of_device structure that is to be removed
+ * @op: pointer to platform_device structure that is to be removed
*
* Remove the hcd structure, and release resources that has been requested
* during probe.
*/
-static int ehci_hcd_xilinx_of_remove(struct of_device *op)
+static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
@@ -266,11 +266,11 @@ static int ehci_hcd_xilinx_of_remove(struct of_device *op)
/**
* ehci_hcd_xilinx_of_shutdown - shutdown the hcd
- * @op: pointer to of_device structure that is to be removed
+ * @op: pointer to platform_device structure that is to be removed
*
* Properly shutdown the hcd, call driver's shutdown routine.
*/
-static int ehci_hcd_xilinx_of_shutdown(struct of_device *op)
+static int ehci_hcd_xilinx_of_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 650a687..bde823f 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -130,6 +130,7 @@ struct ehci_hcd { /* one per controller */
unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1;
unsigned broken_periodic:1;
+ unsigned fs_i_thresh:1; /* Intel iso scheduling */
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@@ -140,7 +141,8 @@ struct ehci_hcd { /* one per controller */
#define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1;
-
+ unsigned has_lpm:1; /* support link power management */
+ unsigned has_ppcd:1; /* support per-port change bits */
u8 sbrn; /* packed release number */
/* irq statistics */
@@ -154,9 +156,6 @@ struct ehci_hcd { /* one per controller */
/* debug files */
#ifdef DEBUG
struct dentry *debug_dir;
- struct dentry *debug_async;
- struct dentry *debug_periodic;
- struct dentry *debug_registers;
#endif
};
@@ -401,15 +400,12 @@ struct ehci_iso_stream {
u32 refcount;
u8 bEndpointAddress;
u8 highspeed;
- u16 depth; /* depth in uframes */
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
struct usb_device *udev;
struct usb_host_endpoint *ep;
/* output of (re)scheduling */
- unsigned long start; /* jiffies */
- unsigned long rescheduled;
int next_uframe;
__hc32 splits;
@@ -538,11 +534,11 @@ struct ehci_fstn {
/* Prepare the PORTSC wakeup flags during controller suspend/resume */
-#define ehci_prepare_ports_for_controller_suspend(ehci) \
- ehci_adjust_port_wakeup_flags(ehci, true);
+#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \
+ ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup);
-#define ehci_prepare_ports_for_controller_resume(ehci) \
- ehci_adjust_port_wakeup_flags(ehci, false);
+#define ehci_prepare_ports_for_controller_resume(ehci) \
+ ehci_adjust_port_wakeup_flags(ehci, false, false);
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index c7c8392..20092a2 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -561,7 +561,7 @@ static const struct hc_driver fhci_driver = {
.hub_control = fhci_hub_control,
};
-static int __devinit of_fhci_probe(struct of_device *ofdev,
+static int __devinit of_fhci_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
@@ -801,7 +801,7 @@ static int __devexit fhci_remove(struct device *dev)
return 0;
}
-static int __devexit of_fhci_remove(struct of_device *ofdev)
+static int __devexit of_fhci_remove(struct platform_device *ofdev)
{
return fhci_remove(&ofdev->dev);
}
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
index 35742f8..9bfac65 100644
--- a/drivers/usb/host/hwa-hc.c
+++ b/drivers/usb/host/hwa-hc.c
@@ -159,7 +159,7 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd)
goto error_set_cluster_id;
usb_hcd->uses_new_polling = 1;
- usb_hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
usb_hcd->state = HC_STATE_RUNNING;
result = 0;
out:
@@ -776,7 +776,7 @@ static int hwahc_probe(struct usb_interface *usb_iface,
goto error_alloc;
}
usb_hcd->wireless = 1;
- usb_hcd->flags |= HCD_FLAG_SAW_IRQ;
+ set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags);
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
hwahc_init(hwahc);
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index ca0e98d..3e56303 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -1521,7 +1521,7 @@ static int imx21_hc_reset(struct usb_hcd *hcd)
return -ETIMEDOUT;
}
spin_unlock_irq(&imx21->lock);
- schedule_timeout(1);
+ schedule_timeout_uninterruptible(1);
spin_lock_irq(&imx21->lock);
}
spin_unlock_irqrestore(&imx21->lock, flags);
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
index d995351..0f97820 100644
--- a/drivers/usb/host/isp1362.h
+++ b/drivers/usb/host/isp1362.h
@@ -8,29 +8,7 @@
/*
* Platform specific compile time options
*/
-#if defined(CONFIG_ARCH_KARO)
-#include <asm/arch/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/karo.h>
-
-#define USE_32BIT 1
-
-
-/* These options are mutually eclusive */
-#define USE_PLATFORM_DELAY 1
-#define USE_NDELAY 0
-/*
- * MAX_ROOT_PORTS: Number of downstream ports
- *
- * The chip has two USB ports, one of which can be configured as
- * an USB device port, so the value of this constant is implementation
- * specific.
- */
-#define MAX_ROOT_PORTS 2
-#define DUMMY_DELAY_ACCESS do {} while (0)
-
-/* insert platform specific definitions for other machines here */
-#elif defined(CONFIG_BLACKFIN)
+#if defined(CONFIG_BLACKFIN)
#include <linux/io.h>
#define USE_32BIT 0
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index dbcafa2..d1a3dfc 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -482,7 +482,6 @@ static int isp1760_run(struct usb_hcd *hcd)
u32 chipid;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
hcd->state = HC_STATE_RUNNING;
isp1760_enable_interrupts(hcd);
@@ -1450,7 +1449,7 @@ static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb,
epnum = urb->ep->desc.bEndpointAddress;
spin_lock_irqsave(&priv->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &priv_to_hcd(priv)->flags)) {
+ if (!HCD_HW_ACCESSIBLE(priv_to_hcd(priv))) {
rc = -ESHUTDOWN;
goto done;
}
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
index ec85d0c..3b28dbfc 100644
--- a/drivers/usb/host/isp1760-if.c
+++ b/drivers/usb/host/isp1760-if.c
@@ -27,7 +27,7 @@
#endif
#ifdef CONFIG_PPC_OF
-static int of_isp1760_probe(struct of_device *dev,
+static int of_isp1760_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct usb_hcd *hcd;
@@ -95,7 +95,7 @@ release_reg:
return ret;
}
-static int of_isp1760_remove(struct of_device *dev)
+static int of_isp1760_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(&dev->dev);
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 8ad2441..36abd2b 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -645,7 +645,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
hcd->product_desc,
hcd_name);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
size -= scnprintf (next, size,
"SUSPENDED (no register access)\n");
goto done;
@@ -687,7 +687,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
next += temp;
temp = scnprintf (next, size, "hub poll timer %s\n",
- ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+ HCD_POLL_RH(ohci_to_hcd(ohci)) ? "ON" : "off");
size -= temp;
next += temp;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 02864a2..c3b4ccc 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -212,7 +212,7 @@ static int ohci_urb_enqueue (
spin_lock_irqsave (&ohci->lock, flags);
/* don't submit to a dead HC */
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
retval = -ENODEV;
goto fail;
}
@@ -685,7 +685,7 @@ retry:
}
/* use rhsc irqs after khubd is fully initialized */
- hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
hcd->uses_new_polling = 1;
/* start controller operations */
@@ -822,7 +822,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
else if (ints & OHCI_INTR_RD) {
ohci_vdbg(ohci, "resume detect\n");
ohci_writel(ohci, OHCI_INTR_RD, &regs->intrstatus);
- hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
if (ohci->autostop) {
spin_lock (&ohci->lock);
ohci_rh_resume (ohci);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 65cac8c..cddcda9 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -284,7 +284,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
spin_lock_irq (&ohci->lock);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
rc = -ESHUTDOWN;
else
rc = ohci_rh_suspend (ohci, 0);
@@ -302,7 +302,7 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
spin_lock_irq (&ohci->lock);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
rc = -ESHUTDOWN;
else
rc = ohci_rh_resume (ohci);
@@ -355,6 +355,11 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd)
ohci_readl(ohci, &ohci->regs->intrenable);
msleep(20);
}
+
+ /* Does the root hub have a port wakeup pending? */
+ if (ohci_readl(ohci, &ohci->regs->intrstatus) &
+ (OHCI_INTR_RD | OHCI_INTR_RHSC))
+ usb_hcd_resume_root_hub(hcd);
}
/* Carry out polling-, autostop-, and autoresume-related state changes */
@@ -364,7 +369,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int poll_rh = 1;
int rhsc_enable;
- /* Some broken controllers never turn off RHCS in the interrupt
+ /* Some broken controllers never turn off RHSC in the interrupt
* status register. For their sake we won't re-enable RHSC
* interrupts if the interrupt bit is already active.
*/
@@ -489,7 +494,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
/* undocumented erratum seen on at least rev D */
@@ -533,8 +538,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
}
}
- hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed,
- any_connected, rhsc_status);
+ if (ohci_root_hub_state_changes(ohci, changed,
+ any_connected, rhsc_status))
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ else
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
done:
spin_unlock_irqrestore (&ohci->lock, flags);
@@ -701,7 +710,7 @@ static int ohci_hub_control (
u32 temp;
int retval = 0;
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
return -ESHUTDOWN;
switch (typeReq) {
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index b8a1148..6bdc8b2 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -392,7 +392,7 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
#ifdef CONFIG_PM
-static int ohci_pci_suspend(struct usb_hcd *hcd)
+static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index cd74bbd..653d6a6 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -329,7 +329,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.type, "isp1301_pnx", I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
- normal_i2c);
+ normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
if (!isp1301_i2c_client) {
err("failed to connect I2C to ISP1301 USB Transceiver");
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index df16591..b2c2dbf 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -81,7 +81,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = {
static int __devinit
-ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
+ohci_hcd_ppc_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
@@ -183,7 +183,7 @@ err_rmr:
return rv;
}
-static int ohci_hcd_ppc_of_remove(struct of_device *op)
+static int ohci_hcd_ppc_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
@@ -201,7 +201,7 @@ static int ohci_hcd_ppc_of_remove(struct of_device *op)
return 0;
}
-static int ohci_hcd_ppc_of_shutdown(struct of_device *op)
+static int ohci_hcd_ppc_of_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c
index 23fd6a8..48ee694 100644
--- a/drivers/usb/host/ohci-ssb.c
+++ b/drivers/usb/host/ohci-ssb.c
@@ -93,8 +93,11 @@ static void ssb_ohci_detach(struct ssb_device *dev)
{
struct usb_hcd *hcd = ssb_get_drvdata(dev);
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
ssb_device_disable(dev, 0);
}
@@ -106,10 +109,52 @@ static int ssb_ohci_attach(struct ssb_device *dev)
int err = -ENOMEM;
u32 tmp, flags = 0;
- if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
- flags |= SSB_OHCI_TMSLOW_HOSTMODE;
+ if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
+ dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
+ return -EOPNOTSUPP;
- ssb_device_enable(dev, flags);
+ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) {
+ /* Put the device into host-mode. */
+ flags |= SSB_OHCI_TMSLOW_HOSTMODE;
+ ssb_device_enable(dev, flags);
+ } else if (dev->id.coreid == SSB_DEV_USB20_HOST) {
+ /*
+ * USB 2.0 special considerations:
+ *
+ * In addition to the standard SSB reset sequence, the Host
+ * Control Register must be programmed to bring the USB core
+ * and various phy components out of reset.
+ */
+ ssb_device_enable(dev, 0);
+ ssb_write32(dev, 0x200, 0x7ff);
+
+ /* Change Flush control reg */
+ tmp = ssb_read32(dev, 0x400);
+ tmp &= ~8;
+ ssb_write32(dev, 0x400, tmp);
+ tmp = ssb_read32(dev, 0x400);
+
+ /* Change Shim control reg */
+ tmp = ssb_read32(dev, 0x304);
+ tmp &= ~0x100;
+ ssb_write32(dev, 0x304, tmp);
+ tmp = ssb_read32(dev, 0x304);
+
+ udelay(1);
+
+ /* Work around for 5354 failures */
+ if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
+ /* Change syn01 reg */
+ tmp = 0x00fe00fe;
+ ssb_write32(dev, 0x894, tmp);
+
+ /* Change syn03 reg */
+ tmp = ssb_read32(dev, 0x89c);
+ tmp |= 0x1;
+ ssb_write32(dev, 0x89c, tmp);
+ }
+ } else
+ ssb_device_enable(dev, 0);
hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
dev_name(dev->dev));
@@ -200,6 +245,7 @@ static int ssb_ohci_resume(struct ssb_device *dev)
static const struct ssb_device_id ssb_ohci_table[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
+ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
SSB_DEVTABLE_END
};
MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index f608dfd..d9c85a2 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -1641,8 +1641,7 @@ static int submit_async(struct oxu_hcd *oxu, struct urb *urb,
#endif
spin_lock_irqsave(&oxu->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &oxu_to_hcd(oxu)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
rc = -ESHUTDOWN;
goto done;
}
@@ -2209,8 +2208,7 @@ static int intr_submit(struct oxu_hcd *oxu, struct urb *urb,
spin_lock_irqsave(&oxu->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &oxu_to_hcd(oxu)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
status = -ESHUTDOWN;
goto done;
}
@@ -2715,7 +2713,6 @@ static int oxu_run(struct usb_hcd *hcd)
u32 temp, hcc_params;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
/* EHCI spec section 4.1 */
retval = ehci_reset(oxu);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index bcf9f0e..990f06b 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -813,8 +813,11 @@ static int sl811h_urb_enqueue(
#endif
/* avoid all allocations within spinlocks */
- if (!hep->hcpriv)
+ if (!hep->hcpriv) {
ep = kzalloc(sizeof *ep, mem_flags);
+ if (ep == NULL)
+ return -ENOMEM;
+ }
spin_lock_irqsave(&sl811->lock, flags);
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index 98cf0b2..6e7fb5f 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -17,7 +17,6 @@
#include "uhci-hcd.h"
-#define uhci_debug_operations (* (const struct file_operations *) NULL)
static struct dentry *uhci_debugfs_root;
#ifdef DEBUG
@@ -495,18 +494,16 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
{
struct uhci_hcd *uhci = inode->i_private;
struct uhci_debug *up;
- int ret = -ENOMEM;
unsigned long flags;
- lock_kernel();
up = kmalloc(sizeof(*up), GFP_KERNEL);
if (!up)
- goto out;
+ return -ENOMEM;
up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
if (!up->data) {
kfree(up);
- goto out;
+ return -ENOMEM;
}
up->size = 0;
@@ -517,10 +514,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
file->private_data = up;
- ret = 0;
-out:
- unlock_kernel();
- return ret;
+ return 0;
}
static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
@@ -528,9 +522,9 @@ static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
struct uhci_debug *up;
loff_t new = -1;
- lock_kernel();
up = file->private_data;
+ /* XXX: atomic 64bit seek access, but that needs to be fixed in the VFS */
switch (whence) {
case 0:
new = off;
@@ -539,11 +533,10 @@ static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
new = file->f_pos + off;
break;
}
- if (new < 0 || new > up->size) {
- unlock_kernel();
+
+ if (new < 0 || new > up->size)
return -EINVAL;
- }
- unlock_kernel();
+
return (file->f_pos = new);
}
@@ -564,7 +557,6 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
return 0;
}
-#undef uhci_debug_operations
static const struct file_operations uhci_debug_operations = {
.owner = THIS_MODULE,
.open = uhci_debug_open,
@@ -572,6 +564,7 @@ static const struct file_operations uhci_debug_operations = {
.read = uhci_debug_read,
.release = uhci_debug_release,
};
+#define UHCI_DEBUG_OPS
#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 6637e52..f52d04d 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -140,7 +140,7 @@ static void finish_reset(struct uhci_hcd *uhci)
uhci->rh_state = UHCI_RH_RESET;
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_to_hcd(uhci)->state = HC_STATE_HALT;
- uhci_to_hcd(uhci)->poll_rh = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
uhci->dead = 0; /* Full reset resurrects the controller */
}
@@ -176,6 +176,8 @@ static void check_and_reset_hc(struct uhci_hcd *uhci)
*/
static void configure_hc(struct uhci_hcd *uhci)
{
+ struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
+
/* Set the frame length to the default: 1 ms exactly */
outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
@@ -191,8 +193,11 @@ static void configure_hc(struct uhci_hcd *uhci)
mb();
/* Enable PIRQ */
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
- USBLEGSUP_DEFAULT);
+ pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+ /* Disable platform-specific non-PME# wakeup */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ pci_write_config_byte(pdev, USBRES_INTEL, 0);
}
@@ -344,7 +349,10 @@ __acquires(uhci->lock)
/* If interrupts don't work and remote wakeup is enabled then
* the suspended root hub needs to be polled.
*/
- uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable);
+ if (!int_enable && wakeup_enable)
+ set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
+ else
+ clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
uhci_scan_schedule(uhci);
uhci_fsbr_off(uhci);
@@ -363,7 +371,7 @@ static void start_rh(struct uhci_hcd *uhci)
uhci->io_addr + USBINTR);
mb();
uhci->rh_state = UHCI_RH_RUNNING;
- uhci_to_hcd(uhci)->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
}
static void wakeup_rh(struct uhci_hcd *uhci)
@@ -589,7 +597,7 @@ static int uhci_start(struct usb_hcd *hcd)
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i;
- struct dentry *dentry;
+ struct dentry __maybe_unused *dentry;
hcd->uses_new_polling = 1;
@@ -599,18 +607,16 @@ static int uhci_start(struct usb_hcd *hcd)
INIT_LIST_HEAD(&uhci->idle_qh_list);
init_waitqueue_head(&uhci->waitqh);
- if (DEBUG_CONFIGURED) {
- dentry = debugfs_create_file(hcd->self.bus_name,
- S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
- uhci, &uhci_debug_operations);
- if (!dentry) {
- dev_err(uhci_dev(uhci), "couldn't create uhci "
- "debugfs entry\n");
- retval = -ENOMEM;
- goto err_create_debug_entry;
- }
- uhci->dentry = dentry;
+#ifdef UHCI_DEBUG_OPS
+ dentry = debugfs_create_file(hcd->self.bus_name,
+ S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
+ uhci, &uhci_debug_operations);
+ if (!dentry) {
+ dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
+ return -ENOMEM;
}
+ uhci->dentry = dentry;
+#endif
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
UHCI_NUMFRAMES * sizeof(*uhci->frame),
@@ -691,7 +697,9 @@ static int uhci_start(struct usb_hcd *hcd)
configure_hc(uhci);
uhci->is_initialized = 1;
+ spin_lock_irq(&uhci->lock);
start_rh(uhci);
+ spin_unlock_irq(&uhci->lock);
return 0;
/*
@@ -722,7 +730,6 @@ err_alloc_frame_cpu:
err_alloc_frame:
debugfs_remove(uhci->dentry);
-err_create_debug_entry:
return retval;
}
@@ -731,7 +738,7 @@ static void uhci_stop(struct usb_hcd *hcd)
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
spin_lock_irq(&uhci->lock);
- if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead)
+ if (HCD_HW_ACCESSIBLE(hcd) && !uhci->dead)
uhci_hc_died(uhci);
uhci_scan_schedule(uhci);
spin_unlock_irq(&uhci->lock);
@@ -748,7 +755,7 @@ static int uhci_rh_suspend(struct usb_hcd *hcd)
int rc = 0;
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
rc = -ESHUTDOWN;
else if (uhci->dead)
; /* Dead controllers tell no tales */
@@ -775,7 +782,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
int rc = 0;
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
rc = -ESHUTDOWN;
else if (!uhci->dead)
wakeup_rh(uhci);
@@ -783,15 +790,16 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
return rc;
}
-static int uhci_pci_suspend(struct usb_hcd *hcd)
+static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
int rc = 0;
dev_dbg(uhci_dev(uhci), "%s\n", __func__);
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
goto done_okay; /* Already suspended or dead */
if (uhci->rh_state > UHCI_RH_SUSPENDED) {
@@ -803,11 +811,15 @@ static int uhci_pci_suspend(struct usb_hcd *hcd)
/* All PCI host controllers are required to disable IRQ generation
* at the source, so we must turn off PIRQ.
*/
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
- mb();
- hcd->poll_rh = 0;
-
- /* FIXME: Enable non-PME# remote wakeup? */
+ pci_write_config_word(pdev, USBLEGSUP, 0);
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* Enable platform-specific non-PME# wakeup */
+ if (do_wakeup) {
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ pci_write_config_byte(pdev, USBRES_INTEL,
+ USBPORT1EN | USBPORT2EN);
+ }
done_okay:
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -826,7 +838,6 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
* even if the controller was dead.
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- mb();
spin_lock_irq(&uhci->lock);
@@ -834,8 +845,6 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
if (hibernated)
uhci_hc_died(uhci);
- /* FIXME: Disable non-PME# remote wakeup? */
-
/* The firmware or a boot kernel may have changed the controller
* settings during a system wakeup. Check it and reconfigure
* to avoid problems.
@@ -845,22 +854,20 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
/* If the controller was dead before, it's back alive now */
configure_hc(uhci);
- if (uhci->rh_state == UHCI_RH_RESET) {
-
- /* The controller had to be reset */
+ /* Tell the core if the controller had to be reset */
+ if (uhci->rh_state == UHCI_RH_RESET)
usb_root_hub_lost_power(hcd->self.root_hub);
- suspend_rh(uhci, UHCI_RH_SUSPENDED);
- }
spin_unlock_irq(&uhci->lock);
/* If interrupts don't work and remote wakeup is enabled then
* the suspended root hub needs to be polled.
*/
- if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) {
- hcd->poll_rh = 1;
- usb_hcd_poll_rh_status(hcd);
- }
+ if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup)
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* Does the root hub have a port wakeup pending? */
+ usb_hcd_poll_rh_status(hcd);
return 0;
}
#endif
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 26bd1b2..49bf279 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -67,12 +67,17 @@
#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
-/* Legacy support register */
+/* PCI legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
+/* PCI Intel-specific resume-enable register */
+#define USBRES_INTEL 0xc4
+#define USBPORT1EN 0x01
+#define USBPORT2EN 0x02
+
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 8270055..6d59c0f 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -190,7 +190,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
spin_lock_irqsave(&uhci->lock, flags);
uhci_scan_schedule(uhci);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
goto done;
uhci_check_ports(uhci);
@@ -200,7 +200,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
case UHCI_RH_SUSPENDING:
case UHCI_RH_SUSPENDED:
/* if port change, ask to be resumed */
- if (status)
+ if (status || uhci->resuming_ports)
usb_hcd_resume_root_hub(hcd);
break;
@@ -246,7 +246,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wPortChange, wPortStatus;
unsigned long flags;
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
return -ETIMEDOUT;
spin_lock_irqsave(&uhci->lock, flags);
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index acd582c..d3ade40 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -565,7 +565,7 @@ static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
qh->unlink_frame = uhci->frame_number;
/* Force an interrupt so we know when the QH is fully unlinked */
- if (list_empty(&uhci->skel_unlink_qh->node))
+ if (list_empty(&uhci->skel_unlink_qh->node) || uhci->is_stopped)
uhci_set_next_interrupt(uhci);
/* Move the QH from its old list to the end of the unlinking list */
@@ -1667,7 +1667,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
qh->advance_jiffies = jiffies;
goto done;
}
- ret = 0;
+ ret = uhci->is_stopped;
}
/* The queue hasn't advanced; check for timeout */
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
index e0d3401..72b6892 100644
--- a/drivers/usb/host/whci/hcd.c
+++ b/drivers/usb/host/whci/hcd.c
@@ -68,7 +68,7 @@ static int whc_start(struct usb_hcd *usb_hcd)
whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
usb_hcd->uses_new_polling = 1;
- usb_hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
usb_hcd->state = HC_STATE_RUNNING;
out:
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
index ab5a14f..dc0ab83 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/usb/host/whci/qset.c
@@ -475,7 +475,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
|| (prev_end & (WHCI_PAGE_SIZE-1))
|| (dma_addr & (WHCI_PAGE_SIZE-1))
|| std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) {
- if (std->len % qset->max_packet != 0)
+ if (std && std->len % qset->max_packet != 0)
return -EINVAL;
std = qset_new_std(whc, qset, urb, mem_flags);
if (std == NULL) {
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 2eb658d..4e51343 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -391,49 +391,6 @@ struct xhci_ring *xhci_stream_id_to_ring(
return ep->stream_info->stream_rings[stream_id];
}
-struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
- unsigned int slot_id, unsigned int ep_index,
- unsigned int stream_id)
-{
- struct xhci_virt_ep *ep;
-
- ep = &xhci->devs[slot_id]->eps[ep_index];
- /* Common case: no streams */
- if (!(ep->ep_state & EP_HAS_STREAMS))
- return ep->ring;
-
- if (stream_id == 0) {
- xhci_warn(xhci,
- "WARN: Slot ID %u, ep index %u has streams, "
- "but URB has no stream ID.\n",
- slot_id, ep_index);
- return NULL;
- }
-
- if (stream_id < ep->stream_info->num_streams)
- return ep->stream_info->stream_rings[stream_id];
-
- xhci_warn(xhci,
- "WARN: Slot ID %u, ep index %u has "
- "stream IDs 1 to %u allocated, "
- "but stream ID %u is requested.\n",
- slot_id, ep_index,
- ep->stream_info->num_streams - 1,
- stream_id);
- return NULL;
-}
-
-/* Get the right ring for the given URB.
- * If the endpoint supports streams, boundary check the URB's stream ID.
- * If the endpoint doesn't support streams, return the singular endpoint ring.
- */
-struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
- struct urb *urb)
-{
- return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
- xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
-}
-
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
static int xhci_test_radix_tree(struct xhci_hcd *xhci,
unsigned int num_streams,
@@ -1112,8 +1069,18 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
/* Set up the endpoint ring */
- virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 1, true, mem_flags);
+ /*
+ * Isochronous endpoint ring needs bigger size because one isoc URB
+ * carries multiple packets and it will insert multiple tds to the
+ * ring.
+ * This should be replaced with dynamic ring resizing in the future.
+ */
+ if (usb_endpoint_xfer_isoc(&ep->desc))
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 8, true, mem_flags);
+ else
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 1, true, mem_flags);
if (!virt_dev->eps[ep_index].new_ring) {
/* Attempt to use the ring cache */
if (virt_dev->num_rings_cached == 0)
@@ -1124,6 +1091,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
virt_dev->num_rings_cached--;
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
}
+ virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
@@ -1389,6 +1357,22 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
return command;
}
+void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv)
+{
+ int last;
+
+ if (!urb_priv)
+ return;
+
+ last = urb_priv->length - 1;
+ if (last >= 0) {
+ int i;
+ for (i = 0; i <= last; i++)
+ kfree(urb_priv->td[i]);
+ }
+ kfree(urb_priv);
+}
+
void xhci_free_command(struct xhci_hcd *xhci,
struct xhci_command *command)
{
@@ -1588,7 +1572,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
unsigned int num_tests;
int i, ret;
- num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]);
+ num_tests = ARRAY_SIZE(simple_test_vector);
for (i = 0; i < num_tests; i++) {
ret = xhci_test_trb_in_td(xhci,
xhci->event_ring->first_seg,
@@ -1601,7 +1585,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
return ret;
}
- num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]);
+ num_tests = ARRAY_SIZE(complex_test_vector);
for (i = 0; i < num_tests; i++) {
ret = xhci_test_trb_in_td(xhci,
complex_test_vector[i].input_seg,
@@ -1617,6 +1601,29 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
return 0;
}
+static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
+{
+ u64 temp;
+ dma_addr_t deq;
+
+ deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue);
+ if (deq == 0 && !in_interrupt())
+ xhci_warn(xhci, "WARN something wrong with SW event ring "
+ "dequeue ptr.\n");
+ /* Update HC event ring dequeue pointer */
+ temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ temp &= ERST_PTR_MASK;
+ /* Don't clear the EHB bit (which is RW1C) because
+ * there might be more events to service.
+ */
+ temp &= ~ERST_EHB;
+ xhci_dbg(xhci, "// Write event ring dequeue pointer, "
+ "preserving EHB bit\n");
+ xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
+ &xhci->ir_set->erst_dequeue);
+}
+
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 11482b6..f7efe02 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -53,6 +53,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval;
+ u32 temp;
hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
@@ -93,6 +94,14 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
return retval;
xhci_dbg(xhci, "Reset complete\n");
+ temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+ if (HCC_64BIT_ADDR(temp)) {
+ xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
+ }
+
xhci_dbg(xhci, "Calling HCD init\n");
/* Initialize HCD and host controller data structures. */
retval = xhci_init(hcd);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index bfc99a9..bc3f4f4 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -301,28 +301,6 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
return 1;
}
-void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
-{
- u64 temp;
- dma_addr_t deq;
-
- deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
- xhci->event_ring->dequeue);
- if (deq == 0 && !in_interrupt())
- xhci_warn(xhci, "WARN something wrong with SW event ring "
- "dequeue ptr.\n");
- /* Update HC event ring dequeue pointer */
- temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
- temp &= ERST_PTR_MASK;
- /* Don't clear the EHB bit (which is RW1C) because
- * there might be more events to service.
- */
- temp &= ~ERST_EHB;
- xhci_dbg(xhci, "// Write event ring dequeue pointer, preserving EHB bit\n");
- xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
- &xhci->ir_set->erst_dequeue);
-}
-
/* Ring the host controller doorbell after placing a command on the ring */
void xhci_ring_cmd_db(struct xhci_hcd *xhci)
{
@@ -359,11 +337,6 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci,
field = xhci_readl(xhci, db_addr) & DB_MASK;
field |= EPI_TO_DB(ep_index) | STREAM_ID_TO_DB(stream_id);
xhci_writel(xhci, field, db_addr);
- /* Flush PCI posted writes - FIXME Matthew Wilcox says this
- * isn't time-critical and we shouldn't make the CPU wait for
- * the flush.
- */
- xhci_readl(xhci, db_addr);
}
}
@@ -419,6 +392,50 @@ static struct xhci_segment *find_trb_seg(
return cur_seg;
}
+
+static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct xhci_virt_ep *ep;
+
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has streams, "
+ "but URB has no stream ID.\n",
+ slot_id, ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ slot_id, ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
+/* Get the right ring for the given URB.
+ * If the endpoint supports streams, boundary check the URB's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
+ struct urb *urb)
+{
+ return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
+ xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
+}
+
/*
* Move the xHC's endpoint ring dequeue pointer past cur_td.
* Record the new state of the xHC's endpoint ring dequeue segment,
@@ -578,16 +595,24 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
struct xhci_td *cur_td, int status, char *adjective)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct urb *urb;
+ struct urb_priv *urb_priv;
- cur_td->urb->hcpriv = NULL;
- usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb);
- xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb);
+ urb = cur_td->urb;
+ urb_priv = urb->hcpriv;
+ urb_priv->td_cnt++;
- spin_unlock(&xhci->lock);
- usb_hcd_giveback_urb(hcd, cur_td->urb, status);
- kfree(cur_td);
- spin_lock(&xhci->lock);
- xhci_dbg(xhci, "%s URB given back\n", adjective);
+ /* Only giveback urb when this is the last td in urb */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
+
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ xhci_urb_free_priv(xhci, urb_priv);
+ spin_lock(&xhci->lock);
+ xhci_dbg(xhci, "%s URB given back\n", adjective);
+ }
}
/*
@@ -1132,7 +1157,6 @@ static void handle_port_status(struct xhci_hcd *xhci,
/* Update event ring dequeue pointer before dropping the lock */
inc_deq(xhci, xhci->event_ring, true);
- xhci_set_hc_event_deq(xhci);
spin_unlock(&xhci->lock);
/* Pass this up to the core */
@@ -1258,6 +1282,421 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
}
/*
+ * Finish the td processing, remove the td from td list;
+ * Return 1 if the urb can be given back.
+ */
+static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status, bool skip)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct urb *urb = NULL;
+ struct xhci_ep_ctx *ep_ctx;
+ int ret = 0;
+ struct urb_priv *urb_priv;
+ u32 trb_comp_code;
+
+ slot_id = TRB_TO_SLOT_ID(event->flags);
+ xdev = xhci->devs[slot_id];
+ ep_index = TRB_TO_EP_ID(event->flags) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+
+ if (skip)
+ goto td_cleanup;
+
+ if (trb_comp_code == COMP_STOP_INVAL ||
+ trb_comp_code == COMP_STOP) {
+ /* The Endpoint Stop Command completion will take care of any
+ * stopped TDs. A stopped TD may be restarted, so don't update
+ * the ring dequeue pointer or take this TD off any lists yet.
+ */
+ ep->stopped_td = td;
+ ep->stopped_trb = event_trb;
+ return 0;
+ } else {
+ if (trb_comp_code == COMP_STALL) {
+ /* The transfer is completed from the driver's
+ * perspective, but we need to issue a set dequeue
+ * command for this stalled endpoint to move the dequeue
+ * pointer past the TD. We can't do that here because
+ * the halt condition must be cleared first. Let the
+ * USB class driver clear the stall later.
+ */
+ ep->stopped_td = td;
+ ep->stopped_trb = event_trb;
+ ep->stopped_stream = ep_ring->stream_id;
+ } else if (xhci_requires_manual_halt_cleanup(xhci,
+ ep_ctx, trb_comp_code)) {
+ /* Other types of errors halt the endpoint, but the
+ * class driver doesn't call usb_reset_endpoint() unless
+ * the error is -EPIPE. Clear the halted status in the
+ * xHCI hardware manually.
+ */
+ xhci_cleanup_halted_endpoint(xhci,
+ slot_id, ep_index, ep_ring->stream_id,
+ td, event_trb);
+ } else {
+ /* Update ring dequeue pointer */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(xhci, ep_ring, false);
+ inc_deq(xhci, ep_ring, false);
+ }
+
+td_cleanup:
+ /* Clean up the endpoint's TD list */
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+
+ /* Do one last check of the actual transfer length.
+ * If the host controller said we transferred more data than
+ * the buffer length, urb->actual_length will be a very big
+ * number (since it's unsigned). Play it safe and say we didn't
+ * transfer anything.
+ */
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ xhci_warn(xhci, "URB transfer length is wrong, "
+ "xHC issue? req. len = %u, "
+ "act. len = %u\n",
+ urb->transfer_buffer_length,
+ urb->actual_length);
+ urb->actual_length = 0;
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ list_del(&td->td_list);
+ /* Was this TD slated to be cancelled but completed anyway? */
+ if (!list_empty(&td->cancelled_td_list))
+ list_del(&td->cancelled_td_list);
+
+ urb_priv->td_cnt++;
+ /* Giveback the urb when all the tds are completed */
+ if (urb_priv->td_cnt == urb_priv->length)
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/*
+ * Process control tds, update urb status and actual_length.
+ */
+static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct xhci_ep_ctx *ep_ctx;
+ u32 trb_comp_code;
+
+ slot_id = TRB_TO_SLOT_ID(event->flags);
+ xdev = xhci->devs[slot_id];
+ ep_index = TRB_TO_EP_ID(event->flags) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+
+ xhci_debug_trb(xhci, xhci->event_ring->dequeue);
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ if (event_trb == ep_ring->dequeue) {
+ xhci_warn(xhci, "WARN: Success on ctrl setup TRB "
+ "without IOC set??\n");
+ *status = -ESHUTDOWN;
+ } else if (event_trb != td->last_trb) {
+ xhci_warn(xhci, "WARN: Success on ctrl data TRB "
+ "without IOC set??\n");
+ *status = -ESHUTDOWN;
+ } else {
+ xhci_dbg(xhci, "Successful control transfer!\n");
+ *status = 0;
+ }
+ break;
+ case COMP_SHORT_TX:
+ xhci_warn(xhci, "WARN: short transfer on control ep\n");
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ break;
+ default:
+ if (!xhci_requires_manual_halt_cleanup(xhci,
+ ep_ctx, trb_comp_code))
+ break;
+ xhci_dbg(xhci, "TRB error code %u, "
+ "halted endpoint index = %u\n",
+ trb_comp_code, ep_index);
+ /* else fall through */
+ case COMP_STALL:
+ /* Did we transfer part of the data (middle) phase? */
+ if (event_trb != ep_ring->dequeue &&
+ event_trb != td->last_trb)
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length
+ - TRB_LEN(event->transfer_len);
+ else
+ td->urb->actual_length = 0;
+
+ xhci_cleanup_halted_endpoint(xhci,
+ slot_id, ep_index, 0, td, event_trb);
+ return finish_td(xhci, td, event_trb, event, ep, status, true);
+ }
+ /*
+ * Did we transfer any data, despite the errors that might have
+ * happened? I.e. did we get past the setup stage?
+ */
+ if (event_trb != ep_ring->dequeue) {
+ /* The event was for the status stage */
+ if (event_trb == td->last_trb) {
+ if (td->urb->actual_length != 0) {
+ /* Don't overwrite a previously set error code
+ */
+ if ((*status == -EINPROGRESS || *status == 0) &&
+ (td->urb->transfer_flags
+ & URB_SHORT_NOT_OK))
+ /* Did we already see a short data
+ * stage? */
+ *status = -EREMOTEIO;
+ } else {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length;
+ }
+ } else {
+ /* Maybe the event was for the data stage? */
+ if (trb_comp_code != COMP_STOP_INVAL) {
+ /* We didn't stop on a link TRB in the middle */
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ TRB_LEN(event->transfer_len);
+ xhci_dbg(xhci, "Waiting for status "
+ "stage event\n");
+ return 0;
+ }
+ }
+ }
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * Process isochronous tds, update urb packet status and actual_length.
+ */
+static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ int idx;
+ int len = 0;
+ int skip_td = 0;
+ union xhci_trb *cur_trb;
+ struct xhci_segment *cur_seg;
+ u32 trb_comp_code;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ urb_priv = td->urb->hcpriv;
+ idx = urb_priv->td_cnt;
+
+ if (ep->skip) {
+ /* The transfer is partly done */
+ *status = -EXDEV;
+ td->urb->iso_frame_desc[idx].status = -EXDEV;
+ } else {
+ /* handle completion code */
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ td->urb->iso_frame_desc[idx].status = 0;
+ xhci_dbg(xhci, "Successful isoc transfer!\n");
+ break;
+ case COMP_SHORT_TX:
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ td->urb->iso_frame_desc[idx].status =
+ -EREMOTEIO;
+ else
+ td->urb->iso_frame_desc[idx].status = 0;
+ break;
+ case COMP_BW_OVER:
+ td->urb->iso_frame_desc[idx].status = -ECOMM;
+ skip_td = 1;
+ break;
+ case COMP_BUFF_OVER:
+ case COMP_BABBLE:
+ td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
+ skip_td = 1;
+ break;
+ case COMP_STALL:
+ td->urb->iso_frame_desc[idx].status = -EPROTO;
+ skip_td = 1;
+ break;
+ case COMP_STOP:
+ case COMP_STOP_INVAL:
+ break;
+ default:
+ td->urb->iso_frame_desc[idx].status = -1;
+ break;
+ }
+ }
+
+ /* calc actual length */
+ if (ep->skip) {
+ td->urb->iso_frame_desc[idx].actual_length = 0;
+ return finish_td(xhci, td, event_trb, event, ep, status, true);
+ }
+
+ if (trb_comp_code == COMP_SUCCESS || skip_td == 1) {
+ td->urb->iso_frame_desc[idx].actual_length =
+ td->urb->iso_frame_desc[idx].length;
+ td->urb->actual_length +=
+ td->urb->iso_frame_desc[idx].length;
+ } else {
+ for (cur_trb = ep_ring->dequeue,
+ cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
+ next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
+ if ((cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
+ (cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
+ len +=
+ TRB_LEN(cur_trb->generic.field[2]);
+ }
+ len += TRB_LEN(cur_trb->generic.field[2]) -
+ TRB_LEN(event->transfer_len);
+
+ if (trb_comp_code != COMP_STOP_INVAL) {
+ td->urb->iso_frame_desc[idx].actual_length = len;
+ td->urb->actual_length += len;
+ }
+ }
+
+ if ((idx == urb_priv->length - 1) && *status == -EINPROGRESS)
+ *status = 0;
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * Process bulk and interrupt tds, update urb status and actual_length.
+ */
+static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ union xhci_trb *cur_trb;
+ struct xhci_segment *cur_seg;
+ u32 trb_comp_code;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ /* Double check that the HW transferred everything. */
+ if (event_trb != td->last_trb) {
+ xhci_warn(xhci, "WARN Successful completion "
+ "on short TX\n");
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ } else {
+ if (usb_endpoint_xfer_bulk(&td->urb->ep->desc))
+ xhci_dbg(xhci, "Successful bulk "
+ "transfer!\n");
+ else
+ xhci_dbg(xhci, "Successful interrupt "
+ "transfer!\n");
+ *status = 0;
+ }
+ break;
+ case COMP_SHORT_TX:
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ break;
+ default:
+ /* Others already handled above */
+ break;
+ }
+ dev_dbg(&td->urb->dev->dev,
+ "ep %#x - asked for %d bytes, "
+ "%d bytes untransferred\n",
+ td->urb->ep->desc.bEndpointAddress,
+ td->urb->transfer_buffer_length,
+ TRB_LEN(event->transfer_len));
+ /* Fast path - was this the last TRB in the TD for this URB? */
+ if (event_trb == td->last_trb) {
+ if (TRB_LEN(event->transfer_len) != 0) {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ TRB_LEN(event->transfer_len);
+ if (td->urb->transfer_buffer_length <
+ td->urb->actual_length) {
+ xhci_warn(xhci, "HC gave bad length "
+ "of %d bytes left\n",
+ TRB_LEN(event->transfer_len));
+ td->urb->actual_length = 0;
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ /* Don't overwrite a previously set error code */
+ if (*status == -EINPROGRESS) {
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ } else {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length;
+ /* Ignore a short packet completion if the
+ * untransferred length was zero.
+ */
+ if (*status == -EREMOTEIO)
+ *status = 0;
+ }
+ } else {
+ /* Slow path - walk the list, starting from the dequeue
+ * pointer, to get the actual length transferred.
+ */
+ td->urb->actual_length = 0;
+ for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
+ cur_trb != event_trb;
+ next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
+ if ((cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
+ (cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
+ td->urb->actual_length +=
+ TRB_LEN(cur_trb->generic.field[2]);
+ }
+ /* If the ring didn't stop on a Link or No-op TRB, add
+ * in the actual bytes transferred from the Normal TRB
+ */
+ if (trb_comp_code != COMP_STOP_INVAL)
+ td->urb->actual_length +=
+ TRB_LEN(cur_trb->generic.field[2]) -
+ TRB_LEN(event->transfer_len);
+ }
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
* At this point, the host controller is probably hosed and should be reset.
@@ -1276,10 +1715,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
union xhci_trb *event_trb;
struct urb *urb = NULL;
int status = -EINPROGRESS;
+ struct urb_priv *urb_priv;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
+ int ret = 0;
- xhci_dbg(xhci, "In %s\n", __func__);
slot_id = TRB_TO_SLOT_ID(event->flags);
xdev = xhci->devs[slot_id];
if (!xdev) {
@@ -1293,51 +1733,16 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep = &xdev->eps[ep_index];
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
- if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
+ if (!ep_ring ||
+ (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
"or incorrect stream ring\n");
return -ENODEV;
}
event_dma = event->buffer;
- /* This TRB should be in the TD at the head of this ring's TD list */
- xhci_dbg(xhci, "%s - checking for list empty\n", __func__);
- if (list_empty(&ep_ring->td_list)) {
- xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
- TRB_TO_SLOT_ID(event->flags), ep_index);
- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
- xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
- urb = NULL;
- goto cleanup;
- }
- xhci_dbg(xhci, "%s - getting list entry\n", __func__);
- td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
-
- /* Is this a TRB in the currently executing TD? */
- xhci_dbg(xhci, "%s - looking for TD\n", __func__);
- event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
- td->last_trb, event_dma);
- xhci_dbg(xhci, "%s - found event_seg = %p\n", __func__, event_seg);
- if (!event_seg) {
- /* HC is busted, give up! */
- xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n");
- return -ESHUTDOWN;
- }
- event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)];
- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
- xhci_dbg(xhci, "Offset 0x00 (buffer lo) = 0x%x\n",
- lower_32_bits(event->buffer));
- xhci_dbg(xhci, "Offset 0x04 (buffer hi) = 0x%x\n",
- upper_32_bits(event->buffer));
- xhci_dbg(xhci, "Offset 0x08 (transfer length) = 0x%x\n",
- (unsigned int) event->transfer_len);
- xhci_dbg(xhci, "Offset 0x0C (flags) = 0x%x\n",
- (unsigned int) event->flags);
-
- /* Look for common error cases */
trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ /* Look for common error cases */
switch (trb_comp_code) {
/* Skip codes that require special handling depending on
* transfer type
@@ -1373,278 +1778,156 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n");
status = -ENOSR;
break;
+ case COMP_BW_OVER:
+ xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n");
+ break;
+ case COMP_BUFF_OVER:
+ xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n");
+ break;
+ case COMP_UNDERRUN:
+ /*
+ * When the Isoch ring is empty, the xHC will generate
+ * a Ring Overrun Event for IN Isoch endpoint or Ring
+ * Underrun Event for OUT Isoch endpoint.
+ */
+ xhci_dbg(xhci, "underrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
+ "still with TDs queued?\n",
+ TRB_TO_SLOT_ID(event->flags), ep_index);
+ goto cleanup;
+ case COMP_OVERRUN:
+ xhci_dbg(xhci, "overrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
+ "still with TDs queued?\n",
+ TRB_TO_SLOT_ID(event->flags), ep_index);
+ goto cleanup;
+ case COMP_MISSED_INT:
+ /*
+ * When encounter missed service error, one or more isoc tds
+ * may be missed by xHC.
+ * Set skip flag of the ep_ring; Complete the missed tds as
+ * short transfer when process the ep_ring next time.
+ */
+ ep->skip = true;
+ xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
+ goto cleanup;
default:
if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
status = 0;
break;
}
- xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n");
- urb = NULL;
+ xhci_warn(xhci, "ERROR Unknown event condition, HC probably "
+ "busted\n");
goto cleanup;
}
- /* Now update the urb's actual_length and give back to the core */
- /* Was this a control transfer? */
- if (usb_endpoint_xfer_control(&td->urb->ep->desc)) {
- xhci_debug_trb(xhci, xhci->event_ring->dequeue);
- switch (trb_comp_code) {
- case COMP_SUCCESS:
- if (event_trb == ep_ring->dequeue) {
- xhci_warn(xhci, "WARN: Success on ctrl setup TRB without IOC set??\n");
- status = -ESHUTDOWN;
- } else if (event_trb != td->last_trb) {
- xhci_warn(xhci, "WARN: Success on ctrl data TRB without IOC set??\n");
- status = -ESHUTDOWN;
- } else {
- xhci_dbg(xhci, "Successful control transfer!\n");
- status = 0;
- }
- break;
- case COMP_SHORT_TX:
- xhci_warn(xhci, "WARN: short transfer on control ep\n");
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- break;
- default:
- if (!xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code))
- break;
- xhci_dbg(xhci, "TRB error code %u, "
- "halted endpoint index = %u\n",
- trb_comp_code, ep_index);
- /* else fall through */
- case COMP_STALL:
- /* Did we transfer part of the data (middle) phase? */
- if (event_trb != ep_ring->dequeue &&
- event_trb != td->last_trb)
- td->urb->actual_length =
- td->urb->transfer_buffer_length
- - TRB_LEN(event->transfer_len);
- else
- td->urb->actual_length = 0;
-
- xhci_cleanup_halted_endpoint(xhci,
- slot_id, ep_index, 0, td, event_trb);
- goto td_cleanup;
- }
- /*
- * Did we transfer any data, despite the errors that might have
- * happened? I.e. did we get past the setup stage?
+ do {
+ /* This TRB should be in the TD at the head of this ring's
+ * TD list.
*/
- if (event_trb != ep_ring->dequeue) {
- /* The event was for the status stage */
- if (event_trb == td->last_trb) {
- if (td->urb->actual_length != 0) {
- /* Don't overwrite a previously set error code */
- if ((status == -EINPROGRESS ||
- status == 0) &&
- (td->urb->transfer_flags
- & URB_SHORT_NOT_OK))
- /* Did we already see a short data stage? */
- status = -EREMOTEIO;
- } else {
- td->urb->actual_length =
- td->urb->transfer_buffer_length;
- }
- } else {
- /* Maybe the event was for the data stage? */
- if (trb_comp_code != COMP_STOP_INVAL) {
- /* We didn't stop on a link TRB in the middle */
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- TRB_LEN(event->transfer_len);
- xhci_dbg(xhci, "Waiting for status stage event\n");
- urb = NULL;
- goto cleanup;
- }
+ if (list_empty(&ep_ring->td_list)) {
+ xhci_warn(xhci, "WARN Event TRB for slot %d ep %d "
+ "with no TDs queued?\n",
+ TRB_TO_SLOT_ID(event->flags), ep_index);
+ xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
+ (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
+ xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
+ if (ep->skip) {
+ ep->skip = false;
+ xhci_dbg(xhci, "td_list is empty while skip "
+ "flag set. Clear skip flag.\n");
}
+ ret = 0;
+ goto cleanup;
}
- } else {
- switch (trb_comp_code) {
- case COMP_SUCCESS:
- /* Double check that the HW transferred everything. */
- if (event_trb != td->last_trb) {
- xhci_warn(xhci, "WARN Successful completion "
- "on short TX\n");
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- } else {
- if (usb_endpoint_xfer_bulk(&td->urb->ep->desc))
- xhci_dbg(xhci, "Successful bulk "
- "transfer!\n");
- else
- xhci_dbg(xhci, "Successful interrupt "
- "transfer!\n");
- status = 0;
- }
- break;
- case COMP_SHORT_TX:
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- break;
- default:
- /* Others already handled above */
- break;
+
+ td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+ /* Is this a TRB in the currently executing TD? */
+ event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
+ td->last_trb, event_dma);
+ if (event_seg && ep->skip) {
+ xhci_dbg(xhci, "Found td. Clear skip flag.\n");
+ ep->skip = false;
+ }
+ if (!event_seg &&
+ (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
+ /* HC is busted, give up! */
+ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
+ "part of current TD\n");
+ return -ESHUTDOWN;
}
- dev_dbg(&td->urb->dev->dev,
- "ep %#x - asked for %d bytes, "
- "%d bytes untransferred\n",
- td->urb->ep->desc.bEndpointAddress,
- td->urb->transfer_buffer_length,
- TRB_LEN(event->transfer_len));
- /* Fast path - was this the last TRB in the TD for this URB? */
- if (event_trb == td->last_trb) {
- if (TRB_LEN(event->transfer_len) != 0) {
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- TRB_LEN(event->transfer_len);
- if (td->urb->transfer_buffer_length <
- td->urb->actual_length) {
- xhci_warn(xhci, "HC gave bad length "
- "of %d bytes left\n",
- TRB_LEN(event->transfer_len));
- td->urb->actual_length = 0;
- if (td->urb->transfer_flags &
- URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- }
- /* Don't overwrite a previously set error code */
- if (status == -EINPROGRESS) {
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- }
- } else {
- td->urb->actual_length = td->urb->transfer_buffer_length;
- /* Ignore a short packet completion if the
- * untransferred length was zero.
- */
- if (status == -EREMOTEIO)
- status = 0;
- }
- } else {
- /* Slow path - walk the list, starting from the dequeue
- * pointer, to get the actual length transferred.
- */
- union xhci_trb *cur_trb;
- struct xhci_segment *cur_seg;
- td->urb->actual_length = 0;
- for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
- cur_trb != event_trb;
- next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((cur_trb->generic.field[3] &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
- (cur_trb->generic.field[3] &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
- td->urb->actual_length +=
- TRB_LEN(cur_trb->generic.field[2]);
- }
- /* If the ring didn't stop on a Link or No-op TRB, add
- * in the actual bytes transferred from the Normal TRB
+ if (event_seg) {
+ event_trb = &event_seg->trbs[(event_dma -
+ event_seg->dma) / sizeof(*event_trb)];
+ /*
+ * No-op TRB should not trigger interrupts.
+ * If event_trb is a no-op TRB, it means the
+ * corresponding TD has been cancelled. Just ignore
+ * the TD.
*/
- if (trb_comp_code != COMP_STOP_INVAL)
- td->urb->actual_length +=
- TRB_LEN(cur_trb->generic.field[2]) -
- TRB_LEN(event->transfer_len);
+ if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
+ == TRB_TYPE(TRB_TR_NOOP)) {
+ xhci_dbg(xhci, "event_trb is a no-op TRB. "
+ "Skip it\n");
+ goto cleanup;
+ }
}
- }
- if (trb_comp_code == COMP_STOP_INVAL ||
- trb_comp_code == COMP_STOP) {
- /* The Endpoint Stop Command completion will take care of any
- * stopped TDs. A stopped TD may be restarted, so don't update
- * the ring dequeue pointer or take this TD off any lists yet.
+
+ /* Now update the urb's actual_length and give back to
+ * the core
*/
- ep->stopped_td = td;
- ep->stopped_trb = event_trb;
- } else {
- if (trb_comp_code == COMP_STALL) {
- /* The transfer is completed from the driver's
- * perspective, but we need to issue a set dequeue
- * command for this stalled endpoint to move the dequeue
- * pointer past the TD. We can't do that here because
- * the halt condition must be cleared first. Let the
- * USB class driver clear the stall later.
- */
- ep->stopped_td = td;
- ep->stopped_trb = event_trb;
- ep->stopped_stream = ep_ring->stream_id;
- } else if (xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code)) {
- /* Other types of errors halt the endpoint, but the
- * class driver doesn't call usb_reset_endpoint() unless
- * the error is -EPIPE. Clear the halted status in the
- * xHCI hardware manually.
- */
- xhci_cleanup_halted_endpoint(xhci,
- slot_id, ep_index, ep_ring->stream_id, td, event_trb);
- } else {
- /* Update ring dequeue pointer */
- while (ep_ring->dequeue != td->last_trb)
- inc_deq(xhci, ep_ring, false);
- inc_deq(xhci, ep_ring, false);
- }
+ if (usb_endpoint_xfer_control(&td->urb->ep->desc))
+ ret = process_ctrl_td(xhci, td, event_trb, event, ep,
+ &status);
+ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
+ ret = process_isoc_td(xhci, td, event_trb, event, ep,
+ &status);
+ else
+ ret = process_bulk_intr_td(xhci, td, event_trb, event,
+ ep, &status);
-td_cleanup:
- /* Clean up the endpoint's TD list */
- urb = td->urb;
- /* Do one last check of the actual transfer length.
- * If the host controller said we transferred more data than
- * the buffer length, urb->actual_length will be a very big
- * number (since it's unsigned). Play it safe and say we didn't
- * transfer anything.
+cleanup:
+ /*
+ * Do not update event ring dequeue pointer if ep->skip is set.
+ * Will roll back to continue process missed tds.
*/
- if (urb->actual_length > urb->transfer_buffer_length) {
- xhci_warn(xhci, "URB transfer length is wrong, "
- "xHC issue? req. len = %u, "
- "act. len = %u\n",
- urb->transfer_buffer_length,
- urb->actual_length);
- urb->actual_length = 0;
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
+ if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
+ inc_deq(xhci, xhci->event_ring, true);
}
- list_del(&td->td_list);
- /* Was this TD slated to be cancelled but completed anyway? */
- if (!list_empty(&td->cancelled_td_list))
- list_del(&td->cancelled_td_list);
- /* Leave the TD around for the reset endpoint function to use
- * (but only if it's not a control endpoint, since we already
- * queued the Set TR dequeue pointer command for stalled
- * control endpoints).
- */
- if (usb_endpoint_xfer_control(&urb->ep->desc) ||
- (trb_comp_code != COMP_STALL &&
- trb_comp_code != COMP_BABBLE)) {
- kfree(td);
+ if (ret) {
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+ /* Leave the TD around for the reset endpoint function
+ * to use(but only if it's not a control endpoint,
+ * since we already queued the Set TR dequeue pointer
+ * command for stalled control endpoints).
+ */
+ if (usb_endpoint_xfer_control(&urb->ep->desc) ||
+ (trb_comp_code != COMP_STALL &&
+ trb_comp_code != COMP_BABBLE))
+ xhci_urb_free_priv(xhci, urb_priv);
+
+ usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
+ xhci_dbg(xhci, "Giveback URB %p, len = %d, "
+ "status = %d\n",
+ urb, urb->actual_length, status);
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
+ spin_lock(&xhci->lock);
}
- urb->hcpriv = NULL;
- }
-cleanup:
- inc_deq(xhci, xhci->event_ring, true);
- xhci_set_hc_event_deq(xhci);
- /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */
- if (urb) {
- usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
- xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n",
- urb, urb->actual_length, status);
- spin_unlock(&xhci->lock);
- usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
- spin_lock(&xhci->lock);
- }
+ /*
+ * If ep->skip is set, it means there are missed tds on the
+ * endpoint ring need to take care of.
+ * Process them as short transfer until reach the td pointed by
+ * the event.
+ */
+ } while (ep->skip && trb_comp_code != COMP_MISSED_INT);
+
return 0;
}
@@ -1652,7 +1935,7 @@ cleanup:
* This function handles all OS-owned events on the event ring. It may drop
* xhci->lock between event processing (e.g. to pass up port status changes).
*/
-void xhci_handle_event(struct xhci_hcd *xhci)
+static void xhci_handle_event(struct xhci_hcd *xhci)
{
union xhci_trb *event;
int update_ptrs = 1;
@@ -1710,15 +1993,130 @@ void xhci_handle_event(struct xhci_hcd *xhci)
return;
}
- if (update_ptrs) {
- /* Update SW and HC event ring dequeue pointer */
+ if (update_ptrs)
+ /* Update SW event ring dequeue pointer */
inc_deq(xhci, xhci->event_ring, true);
- xhci_set_hc_event_deq(xhci);
- }
+
/* Are there more items on the event ring? */
xhci_handle_event(xhci);
}
+/*
+ * xHCI spec says we can get an interrupt, and if the HC has an error condition,
+ * we might get bad data out of the event ring. Section 4.10.2.7 has a list of
+ * indicators of an event TRB error, but we check the status *first* to be safe.
+ */
+irqreturn_t xhci_irq(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ u32 status;
+ union xhci_trb *trb;
+ u64 temp_64;
+ union xhci_trb *event_ring_deq;
+ dma_addr_t deq;
+
+ spin_lock(&xhci->lock);
+ trb = xhci->event_ring->dequeue;
+ /* Check if the xHC generated the interrupt, or the irq is shared */
+ status = xhci_readl(xhci, &xhci->op_regs->status);
+ if (status == 0xffffffff)
+ goto hw_died;
+
+ if (!(status & STS_EINT)) {
+ spin_unlock(&xhci->lock);
+ xhci_warn(xhci, "Spurious interrupt.\n");
+ return IRQ_NONE;
+ }
+ xhci_dbg(xhci, "op reg status = %08x\n", status);
+ xhci_dbg(xhci, "Event ring dequeue ptr:\n");
+ xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
+ (unsigned long long)
+ xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
+ lower_32_bits(trb->link.segment_ptr),
+ upper_32_bits(trb->link.segment_ptr),
+ (unsigned int) trb->link.intr_target,
+ (unsigned int) trb->link.control);
+
+ if (status & STS_FATAL) {
+ xhci_warn(xhci, "WARNING: Host System Error\n");
+ xhci_halt(xhci);
+hw_died:
+ xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+ spin_unlock(&xhci->lock);
+ return -ESHUTDOWN;
+ }
+
+ /*
+ * Clear the op reg interrupt status first,
+ * so we can receive interrupts from other MSI-X interrupters.
+ * Write 1 to clear the interrupt status.
+ */
+ status |= STS_EINT;
+ xhci_writel(xhci, status, &xhci->op_regs->status);
+ /* FIXME when MSI-X is supported and there are multiple vectors */
+ /* Clear the MSI-X event interrupt status */
+
+ if (hcd->irq != -1) {
+ u32 irq_pending;
+ /* Acknowledge the PCI interrupt */
+ irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+ irq_pending |= 0x3;
+ xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending);
+ }
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
+ "Shouldn't IRQs be disabled?\n");
+ /* Clear the event handler busy flag (RW1C);
+ * the event ring should be empty.
+ */
+ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ xhci_write_64(xhci, temp_64 | ERST_EHB,
+ &xhci->ir_set->erst_dequeue);
+ spin_unlock(&xhci->lock);
+
+ return IRQ_HANDLED;
+ }
+
+ event_ring_deq = xhci->event_ring->dequeue;
+ /* FIXME this should be a delayed service routine
+ * that clears the EHB.
+ */
+ xhci_handle_event(xhci);
+
+ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ /* If necessary, update the HW's version of the event ring deq ptr. */
+ if (event_ring_deq != xhci->event_ring->dequeue) {
+ deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue);
+ if (deq == 0)
+ xhci_warn(xhci, "WARN something wrong with SW event "
+ "ring dequeue ptr.\n");
+ /* Update HC event ring dequeue pointer */
+ temp_64 &= ERST_PTR_MASK;
+ temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
+ }
+
+ /* Clear the event handler busy flag (RW1C); event ring is empty. */
+ temp_64 |= ERST_EHB;
+ xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
+
+ spin_unlock(&xhci->lock);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
+{
+ irqreturn_t ret;
+
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ ret = xhci_irq(hcd);
+
+ return ret;
+}
+
/**** Endpoint Ring Operations ****/
/*
@@ -1827,10 +2225,12 @@ static int prepare_transfer(struct xhci_hcd *xhci,
unsigned int stream_id,
unsigned int num_trbs,
struct urb *urb,
- struct xhci_td **td,
+ unsigned int td_index,
gfp_t mem_flags)
{
int ret;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
struct xhci_ring *ep_ring;
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
@@ -1846,24 +2246,29 @@ static int prepare_transfer(struct xhci_hcd *xhci,
num_trbs, mem_flags);
if (ret)
return ret;
- *td = kzalloc(sizeof(struct xhci_td), mem_flags);
- if (!*td)
- return -ENOMEM;
- INIT_LIST_HEAD(&(*td)->td_list);
- INIT_LIST_HEAD(&(*td)->cancelled_td_list);
- ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
- if (unlikely(ret)) {
- kfree(*td);
- return ret;
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[td_index];
+
+ INIT_LIST_HEAD(&td->td_list);
+ INIT_LIST_HEAD(&td->cancelled_td_list);
+
+ if (td_index == 0) {
+ ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
+ if (unlikely(ret)) {
+ xhci_urb_free_priv(xhci, urb_priv);
+ urb->hcpriv = NULL;
+ return ret;
+ }
}
- (*td)->urb = urb;
- urb->hcpriv = (void *) (*td);
+ td->urb = urb;
/* Add this TD to the tail of the endpoint ring's TD list */
- list_add_tail(&(*td)->td_list, &ep_ring->td_list);
- (*td)->start_seg = ep_ring->enq_seg;
- (*td)->first_trb = ep_ring->enqueue;
+ list_add_tail(&td->td_list, &ep_ring->td_list);
+ td->start_seg = ep_ring->enq_seg;
+ td->first_trb = ep_ring->enqueue;
+
+ urb_priv->td[td_index] = td;
return 0;
}
@@ -2002,6 +2407,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
{
struct xhci_ring *ep_ring;
unsigned int num_trbs;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
struct scatterlist *sg;
int num_sgs;
@@ -2022,9 +2428,13 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, &td, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (trb_buff_len < 0)
return trb_buff_len;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
@@ -2145,6 +2555,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
{
struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
int num_trbs;
struct xhci_generic_trb *start_trb;
@@ -2190,10 +2601,13 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, &td, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (ret < 0)
return ret;
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
@@ -2279,6 +2693,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct xhci_generic_trb *start_trb;
int start_cycle;
u32 field, length_field;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
@@ -2306,10 +2721,13 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
num_trbs++;
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, &td, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (ret < 0)
return ret;
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
@@ -2366,6 +2784,224 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return 0;
}
+static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
+ struct urb *urb, int i)
+{
+ int num_trbs = 0;
+ u64 addr, td_len, running_total;
+
+ addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
+ td_len = urb->iso_frame_desc[i].length;
+
+ running_total = TRB_MAX_BUFF_SIZE -
+ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+ if (running_total != 0)
+ num_trbs++;
+
+ while (running_total < td_len) {
+ num_trbs++;
+ running_total += TRB_MAX_BUFF_SIZE;
+ }
+
+ return num_trbs;
+}
+
+/* This is for isoc transfer */
+static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+ int num_tds, trbs_per_td;
+ struct xhci_generic_trb *start_trb;
+ bool first_trb;
+ int start_cycle;
+ u32 field, length_field;
+ int running_total, trb_buff_len, td_len, td_remain_len, ret;
+ u64 start_addr, addr;
+ int i, j;
+
+ ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
+
+ num_tds = urb->number_of_packets;
+ if (num_tds < 1) {
+ xhci_dbg(xhci, "Isoc URB with zero packets?\n");
+ return -EINVAL;
+ }
+
+ if (!in_interrupt())
+ dev_dbg(&urb->dev->dev, "ep %#x - urb len = %#x (%d),"
+ " addr = %#llx, num_tds = %d\n",
+ urb->ep->desc.bEndpointAddress,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer_length,
+ (unsigned long long)urb->transfer_dma,
+ num_tds);
+
+ start_addr = (u64) urb->transfer_dma;
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+
+ /* Queue the first TRB, even if it's zero-length */
+ for (i = 0; i < num_tds; i++) {
+ first_trb = true;
+
+ running_total = 0;
+ addr = start_addr + urb->iso_frame_desc[i].offset;
+ td_len = urb->iso_frame_desc[i].length;
+ td_remain_len = td_len;
+
+ trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
+
+ ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
+ urb->stream_id, trbs_per_td, urb, i, mem_flags);
+ if (ret < 0)
+ return ret;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[i];
+
+ for (j = 0; j < trbs_per_td; j++) {
+ u32 remainder = 0;
+ field = 0;
+
+ if (first_trb) {
+ /* Queue the isoc TRB */
+ field |= TRB_TYPE(TRB_ISOC);
+ /* Assume URB_ISO_ASAP is set */
+ field |= TRB_SIA;
+ if (i > 0)
+ field |= ep_ring->cycle_state;
+ first_trb = false;
+ } else {
+ /* Queue other normal TRBs */
+ field |= TRB_TYPE(TRB_NORMAL);
+ field |= ep_ring->cycle_state;
+ }
+
+ /* Chain all the TRBs together; clear the chain bit in
+ * the last TRB to indicate it's the last TRB in the
+ * chain.
+ */
+ if (j < trbs_per_td - 1) {
+ field |= TRB_CHAIN;
+ } else {
+ td->last_trb = ep_ring->enqueue;
+ field |= TRB_IOC;
+ }
+
+ /* Calculate TRB length */
+ trb_buff_len = TRB_MAX_BUFF_SIZE -
+ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+ if (trb_buff_len > td_remain_len)
+ trb_buff_len = td_remain_len;
+
+ remainder = xhci_td_remainder(td_len - running_total);
+ length_field = TRB_LEN(trb_buff_len) |
+ remainder |
+ TRB_INTR_TARGET(0);
+ queue_trb(xhci, ep_ring, false, false,
+ lower_32_bits(addr),
+ upper_32_bits(addr),
+ length_field,
+ /* We always want to know if the TRB was short,
+ * or we won't get an event when it completes.
+ * (Unless we use event data TRBs, which are a
+ * waste of space and HC resources.)
+ */
+ field | TRB_ISP);
+ running_total += trb_buff_len;
+
+ addr += trb_buff_len;
+ td_remain_len -= trb_buff_len;
+ }
+
+ /* Check TD length */
+ if (running_total != td_len) {
+ xhci_err(xhci, "ISOC TD length unmatch\n");
+ return -EINVAL;
+ }
+ }
+
+ wmb();
+ start_trb->field[3] |= start_cycle;
+
+ ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id);
+ return 0;
+}
+
+/*
+ * Check transfer ring to guarantee there is enough room for the urb.
+ * Update ISO URB start_frame and interval.
+ * Update interval as xhci_queue_intr_tx does. Just use xhci frame_index to
+ * update the urb->start_frame by now.
+ * Always assume URB_ISO_ASAP set, and NEVER use urb->start_frame as input.
+ */
+int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ struct xhci_ep_ctx *ep_ctx;
+ int start_frame;
+ int xhci_interval;
+ int ep_interval;
+ int num_tds, num_trbs, i;
+ int ret;
+
+ xdev = xhci->devs[slot_id];
+ ep_ring = xdev->eps[ep_index].ring;
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+
+ num_trbs = 0;
+ num_tds = urb->number_of_packets;
+ for (i = 0; i < num_tds; i++)
+ num_trbs += count_isoc_trbs_needed(xhci, urb, i);
+
+ /* Check the ring to guarantee there is enough room for the whole urb.
+ * Do not insert any td of the urb to the ring if the check failed.
+ */
+ ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK,
+ num_trbs, mem_flags);
+ if (ret)
+ return ret;
+
+ start_frame = xhci_readl(xhci, &xhci->run_regs->microframe_index);
+ start_frame &= 0x3fff;
+
+ urb->start_frame = start_frame;
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->start_frame >>= 3;
+
+ xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info);
+ ep_interval = urb->interval;
+ /* Convert to microframes */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ ep_interval *= 8;
+ /* FIXME change this to a warning and a suggestion to use the new API
+ * to set the polling interval (once the API is added).
+ */
+ if (xhci_interval != ep_interval) {
+ if (!printk_ratelimit())
+ dev_dbg(&urb->dev->dev, "Driver uses different interval"
+ " (%d microframe%s) than xHCI "
+ "(%d microframe%s)\n",
+ ep_interval,
+ ep_interval == 1 ? "" : "s",
+ xhci_interval,
+ xhci_interval == 1 ? "" : "s");
+ urb->interval = xhci_interval;
+ /* Convert back to frames for LS/FS devices */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->interval /= 8;
+ }
+ return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
+}
+
/**** Command Ring Operations ****/
/* Generic function for queueing a command TRB on the command ring.
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 3998f72..d5c550e 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -20,6 +20,7 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/log2.h>
#include <linux/module.h>
@@ -171,22 +172,84 @@ int xhci_reset(struct xhci_hcd *xhci)
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
}
+/*
+ * Free IRQs
+ * free all IRQs request
+ */
+static void xhci_free_irq(struct xhci_hcd *xhci)
+{
+ int i;
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
-#if 0
-/* Set up MSI-X table for entry 0 (may claim other entries later) */
-static int xhci_setup_msix(struct xhci_hcd *xhci)
+ /* return if using legacy interrupt */
+ if (xhci_to_hcd(xhci)->irq >= 0)
+ return;
+
+ if (xhci->msix_entries) {
+ for (i = 0; i < xhci->msix_count; i++)
+ if (xhci->msix_entries[i].vector)
+ free_irq(xhci->msix_entries[i].vector,
+ xhci_to_hcd(xhci));
+ } else if (pdev->irq >= 0)
+ free_irq(pdev->irq, xhci_to_hcd(xhci));
+
+ return;
+}
+
+/*
+ * Set up MSI
+ */
+static int xhci_setup_msi(struct xhci_hcd *xhci)
{
int ret;
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+
+ ret = pci_enable_msi(pdev);
+ if (ret) {
+ xhci_err(xhci, "failed to allocate MSI entry\n");
+ return ret;
+ }
+
+ ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq,
+ 0, "xhci_hcd", xhci_to_hcd(xhci));
+ if (ret) {
+ xhci_err(xhci, "disable MSI interrupt\n");
+ pci_disable_msi(pdev);
+ }
+
+ return ret;
+}
+
+/*
+ * Set up MSI-X
+ */
+static int xhci_setup_msix(struct xhci_hcd *xhci)
+{
+ int i, ret = 0;
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
- xhci->msix_count = 0;
- /* XXX: did I do this right? ixgbe does kcalloc for more than one */
- xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL);
+ /*
+ * calculate number of msi-x vectors supported.
+ * - HCS_MAX_INTRS: the max number of interrupts the host can handle,
+ * with max number of interrupters based on the xhci HCSPARAMS1.
+ * - num_online_cpus: maximum msi-x vectors per CPUs core.
+ * Add additional 1 vector to ensure always available interrupt.
+ */
+ xhci->msix_count = min(num_online_cpus() + 1,
+ HCS_MAX_INTRS(xhci->hcs_params1));
+
+ xhci->msix_entries =
+ kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
+ GFP_KERNEL);
if (!xhci->msix_entries) {
xhci_err(xhci, "Failed to allocate MSI-X entries\n");
return -ENOMEM;
}
- xhci->msix_entries[0].entry = 0;
+
+ for (i = 0; i < xhci->msix_count; i++) {
+ xhci->msix_entries[i].entry = i;
+ xhci->msix_entries[i].vector = 0;
+ }
ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
if (ret) {
@@ -194,20 +257,19 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
goto free_entries;
}
- /*
- * Pass the xhci pointer value as the request_irq "cookie".
- * If more irqs are added, this will need to be unique for each one.
- */
- ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0,
- "xHCI", xhci_to_hcd(xhci));
- if (ret) {
- xhci_err(xhci, "Failed to allocate MSI-X interrupt\n");
- goto disable_msix;
+ for (i = 0; i < xhci->msix_count; i++) {
+ ret = request_irq(xhci->msix_entries[i].vector,
+ (irq_handler_t)xhci_msi_irq,
+ 0, "xhci_hcd", xhci_to_hcd(xhci));
+ if (ret)
+ goto disable_msix;
}
- xhci_dbg(xhci, "Finished setting up MSI-X\n");
- return 0;
+
+ return ret;
disable_msix:
+ xhci_err(xhci, "disable MSI-X interrupt\n");
+ xhci_free_irq(xhci);
pci_disable_msix(pdev);
free_entries:
kfree(xhci->msix_entries);
@@ -215,21 +277,23 @@ free_entries:
return ret;
}
-/* XXX: code duplication; can xhci_setup_msix call this? */
/* Free any IRQs and disable MSI-X */
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
- if (!xhci->msix_entries)
- return;
- free_irq(xhci->msix_entries[0].vector, xhci);
- pci_disable_msix(pdev);
- kfree(xhci->msix_entries);
- xhci->msix_entries = NULL;
- xhci_dbg(xhci, "Finished cleaning up MSI-X\n");
+ xhci_free_irq(xhci);
+
+ if (xhci->msix_entries) {
+ pci_disable_msix(pdev);
+ kfree(xhci->msix_entries);
+ xhci->msix_entries = NULL;
+ } else {
+ pci_disable_msi(pdev);
+ }
+
+ return;
}
-#endif
/*
* Initialize memory for HCD and xHC (one-time init).
@@ -257,100 +321,8 @@ int xhci_init(struct usb_hcd *hcd)
return retval;
}
-/*
- * Called in interrupt context when there might be work
- * queued on the event ring
- *
- * xhci->lock must be held by caller.
- */
-static void xhci_work(struct xhci_hcd *xhci)
-{
- u32 temp;
- u64 temp_64;
-
- /*
- * Clear the op reg interrupt status first,
- * so we can receive interrupts from other MSI-X interrupters.
- * Write 1 to clear the interrupt status.
- */
- temp = xhci_readl(xhci, &xhci->op_regs->status);
- temp |= STS_EINT;
- xhci_writel(xhci, temp, &xhci->op_regs->status);
- /* FIXME when MSI-X is supported and there are multiple vectors */
- /* Clear the MSI-X event interrupt status */
-
- /* Acknowledge the interrupt */
- temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
- temp |= 0x3;
- xhci_writel(xhci, temp, &xhci->ir_set->irq_pending);
- /* Flush posted writes */
- xhci_readl(xhci, &xhci->ir_set->irq_pending);
-
- if (xhci->xhc_state & XHCI_STATE_DYING)
- xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
- "Shouldn't IRQs be disabled?\n");
- else
- /* FIXME this should be a delayed service routine
- * that clears the EHB.
- */
- xhci_handle_event(xhci);
-
- /* Clear the event handler busy flag (RW1C); the event ring should be empty. */
- temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
- xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue);
- /* Flush posted writes -- FIXME is this necessary? */
- xhci_readl(xhci, &xhci->ir_set->irq_pending);
-}
-
/*-------------------------------------------------------------------------*/
-/*
- * xHCI spec says we can get an interrupt, and if the HC has an error condition,
- * we might get bad data out of the event ring. Section 4.10.2.7 has a list of
- * indicators of an event TRB error, but we check the status *first* to be safe.
- */
-irqreturn_t xhci_irq(struct usb_hcd *hcd)
-{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- u32 temp, temp2;
- union xhci_trb *trb;
-
- spin_lock(&xhci->lock);
- trb = xhci->event_ring->dequeue;
- /* Check if the xHC generated the interrupt, or the irq is shared */
- temp = xhci_readl(xhci, &xhci->op_regs->status);
- temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending);
- if (temp == 0xffffffff && temp2 == 0xffffffff)
- goto hw_died;
-
- if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) {
- spin_unlock(&xhci->lock);
- return IRQ_NONE;
- }
- xhci_dbg(xhci, "op reg status = %08x\n", temp);
- xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2);
- xhci_dbg(xhci, "Event ring dequeue ptr:\n");
- xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
- (unsigned long long)xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
- lower_32_bits(trb->link.segment_ptr),
- upper_32_bits(trb->link.segment_ptr),
- (unsigned int) trb->link.intr_target,
- (unsigned int) trb->link.control);
-
- if (temp & STS_FATAL) {
- xhci_warn(xhci, "WARNING: Host System Error\n");
- xhci_halt(xhci);
-hw_died:
- xhci_to_hcd(xhci)->state = HC_STATE_HALT;
- spin_unlock(&xhci->lock);
- return -ESHUTDOWN;
- }
-
- xhci_work(xhci);
- spin_unlock(&xhci->lock);
-
- return IRQ_HANDLED;
-}
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
void xhci_event_ring_work(unsigned long arg)
@@ -423,21 +395,36 @@ int xhci_run(struct usb_hcd *hcd)
{
u32 temp;
u64 temp_64;
+ u32 ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
void (*doorbell)(struct xhci_hcd *) = NULL;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
xhci_dbg(xhci, "xhci_run\n");
-#if 0 /* FIXME: MSI not setup yet */
- /* Do this at the very last minute */
+ /* unregister the legacy interrupt */
+ if (hcd->irq)
+ free_irq(hcd->irq, hcd);
+ hcd->irq = -1;
+
ret = xhci_setup_msix(xhci);
- if (!ret)
- return ret;
+ if (ret)
+ /* fall back to msi*/
+ ret = xhci_setup_msi(xhci);
+
+ if (ret) {
+ /* fall back to legacy interrupt*/
+ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
+ hcd->irq_descr, hcd);
+ if (ret) {
+ xhci_err(xhci, "request interrupt %d failed\n",
+ pdev->irq);
+ return ret;
+ }
+ hcd->irq = pdev->irq;
+ }
- return -ENOSYS;
-#endif
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
init_timer(&xhci->event_ring_timer);
xhci->event_ring_timer.data = (unsigned long) xhci;
@@ -495,7 +482,6 @@ int xhci_run(struct usb_hcd *hcd)
return -ENODEV;
}
- xhci_dbg(xhci, "// @%p = 0x%x\n", &xhci->op_regs->command, temp);
if (doorbell)
(*doorbell)(xhci);
if (xhci->quirks & XHCI_NEC_HOST)
@@ -522,11 +508,9 @@ void xhci_stop(struct usb_hcd *hcd)
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
xhci_reset(xhci);
+ xhci_cleanup_msix(xhci);
spin_unlock_irq(&xhci->lock);
-#if 0 /* No MSI yet */
- xhci_cleanup_msix(xhci);
-#endif
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
/* Tell the event ring poll function not to reschedule */
xhci->zombie = 1;
@@ -560,11 +544,8 @@ void xhci_shutdown(struct usb_hcd *hcd)
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
- spin_unlock_irq(&xhci->lock);
-
-#if 0
xhci_cleanup_msix(xhci);
-#endif
+ spin_unlock_irq(&xhci->lock);
xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n",
xhci_readl(xhci, &xhci->op_regs->status));
@@ -720,7 +701,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
unsigned long flags;
int ret = 0;
unsigned int slot_id, ep_index;
-
+ struct urb_priv *urb_priv;
+ int size, i;
if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
return -EINVAL;
@@ -734,12 +716,36 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
ret = -EINVAL;
goto exit;
}
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
if (!in_interrupt())
xhci_dbg(xhci, "urb submitted during PCI suspend\n");
ret = -ESHUTDOWN;
goto exit;
}
+
+ if (usb_endpoint_xfer_isoc(&urb->ep->desc))
+ size = urb->number_of_packets;
+ else
+ size = 1;
+
+ urb_priv = kzalloc(sizeof(struct urb_priv) +
+ size * sizeof(struct xhci_td *), mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ for (i = 0; i < size; i++) {
+ urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags);
+ if (!urb_priv->td[i]) {
+ urb_priv->length = i;
+ xhci_urb_free_priv(xhci, urb_priv);
+ return -ENOMEM;
+ }
+ }
+
+ urb_priv->length = size;
+ urb_priv->td_cnt = 0;
+ urb->hcpriv = urb_priv;
+
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
/* Check to see if the max packet size for the default control
* endpoint changed during FS device enumeration
@@ -788,11 +794,18 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
} else {
- ret = -EINVAL;
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
+ ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ spin_unlock_irqrestore(&xhci->lock, flags);
}
exit:
return ret;
dying:
+ xhci_urb_free_priv(xhci, urb_priv);
+ urb->hcpriv = NULL;
xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
"non-responsive xHCI host.\n",
urb->ep->desc.bEndpointAddress, urb);
@@ -800,6 +813,47 @@ dying:
return -ESHUTDOWN;
}
+/* Get the right ring for the given URB.
+ * If the endpoint supports streams, boundary check the URB's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
+ struct urb *urb)
+{
+ unsigned int slot_id;
+ unsigned int ep_index;
+ unsigned int stream_id;
+ struct xhci_virt_ep *ep;
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+ stream_id = urb->stream_id;
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has streams, "
+ "but URB has no stream ID.\n",
+ slot_id, ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ slot_id, ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
/*
* Remove the URB's TD from the endpoint ring. This may cause the HC to stop
* USB transfers, potentially stopping in the middle of a TRB buffer. The HC
@@ -834,9 +888,10 @@ dying:
int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
unsigned long flags;
- int ret;
+ int ret, i;
u32 temp;
struct xhci_hcd *xhci;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
unsigned int ep_index;
struct xhci_ring *ep_ring;
@@ -851,12 +906,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
temp = xhci_readl(xhci, &xhci->op_regs->status);
if (temp == 0xffffffff) {
xhci_dbg(xhci, "HW died, freeing TD.\n");
- td = (struct xhci_td *) urb->hcpriv;
+ urb_priv = urb->hcpriv;
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&xhci->lock, flags);
usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN);
- kfree(td);
+ xhci_urb_free_priv(xhci, urb_priv);
return ret;
}
if (xhci->xhc_state & XHCI_STATE_DYING) {
@@ -884,9 +939,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
xhci_dbg(xhci, "Endpoint ring:\n");
xhci_debug_ring(xhci, ep_ring);
- td = (struct xhci_td *) urb->hcpriv;
- list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+ urb_priv = urb->hcpriv;
+
+ for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
+ td = urb_priv->td[i];
+ list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+ }
+
/* Queue a stop endpoint command, but only if this is
* the first cancellation to be handled.
*/
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 6c7e343..34a60d9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -720,6 +720,14 @@ struct xhci_virt_ep {
struct timer_list stop_cmd_timer;
int stop_cmds_pending;
struct xhci_hcd *xhci;
+ /*
+ * Sometimes the xHC can not process isochronous endpoint ring quickly
+ * enough, and it will miss some isoc tds on the ring and generate
+ * a Missed Service Error Event.
+ * Set skip flag when receive a Missed Service Error Event and
+ * process the missed tds on the endpoint ring.
+ */
+ bool skip;
};
struct xhci_virt_device {
@@ -911,6 +919,9 @@ struct xhci_event_cmd {
/* Control transfer TRB specific fields */
#define TRB_DIR_IN (1<<16)
+/* Isochronous TRB specific fields */
+#define TRB_SIA (1<<31)
+
struct xhci_generic_trb {
u32 field[4];
};
@@ -1082,6 +1093,12 @@ struct xhci_scratchpad {
dma_addr_t *sp_dma_buffers;
};
+struct urb_priv {
+ int length;
+ int td_cnt;
+ struct xhci_td *td[0];
+};
+
/*
* Each segment table entry is 4*32bits long. 1K seems like an ok size:
* (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
@@ -1130,7 +1147,7 @@ struct xhci_hcd {
int page_size;
/* Valid values are 12 to 20, inclusive */
int page_shift;
- /* only one MSI vector for now, but might need more later */
+ /* msi-x vectors */
int msix_count;
struct msix_entry *msix_entries;
/* data structures */
@@ -1327,11 +1344,6 @@ void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
struct xhci_ring *xhci_dma_to_transfer_ring(
struct xhci_virt_ep *ep,
u64 address);
-struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
- struct urb *urb);
-struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
- unsigned int slot_id, unsigned int ep_index,
- unsigned int stream_id);
struct xhci_ring *xhci_stream_id_to_ring(
struct xhci_virt_device *dev,
unsigned int ep_index,
@@ -1339,6 +1351,7 @@ struct xhci_ring *xhci_stream_id_to_ring(
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
bool allocate_in_ctx, bool allocate_completion,
gfp_t mem_flags);
+void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv);
void xhci_free_command(struct xhci_hcd *xhci,
struct xhci_command *command);
@@ -1358,6 +1371,7 @@ void xhci_stop(struct usb_hcd *hcd);
void xhci_shutdown(struct usb_hcd *hcd);
int xhci_get_frame(struct usb_hcd *hcd);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
+irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
@@ -1386,8 +1400,6 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
void *xhci_setup_one_noop(struct xhci_hcd *xhci);
-void xhci_handle_event(struct xhci_hcd *xhci);
-void xhci_set_hc_event_deq(struct xhci_hcd *xhci);
int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
u32 slot_id);
@@ -1401,6 +1413,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
int slot_id, unsigned int ep_index);
int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
int slot_id, unsigned int ep_index);
+int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index);
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
u32 slot_id, bool command_must_succeed);
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 82e1663..aecf380 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -650,7 +650,7 @@ static int ftdi_elan_open(struct inode *inode, struct file *file)
static int ftdi_elan_release(struct inode *inode, struct file *file)
{
- struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ struct usb_ftdi *ftdi = file->private_data;
if (ftdi == NULL)
return -ENODEV;
up(&ftdi->sw_lock); /* decrement the count on our device */
@@ -673,7 +673,7 @@ static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,
int bytes_read = 0;
int retry_on_empty = 10;
int retry_on_timeout = 5;
- struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ struct usb_ftdi *ftdi = file->private_data;
if (ftdi->disconnected > 0) {
return -ENODEV;
}
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 7dc9d3c..2de49c8 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -18,7 +18,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/usb/iowarrior.h>
@@ -61,6 +61,7 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/* Module parameters */
+static DEFINE_MUTEX(iowarrior_mutex);
static int debug = 0;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "debug=1 enables debugging messages");
@@ -282,7 +283,7 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer,
int read_idx;
int offset;
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
/* verify that the device wasn't unplugged */
if (dev == NULL || !dev->present)
@@ -348,7 +349,7 @@ static ssize_t iowarrior_write(struct file *file,
char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
struct urb *int_out_urb = NULL;
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
@@ -483,7 +484,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
int retval;
int io_res; /* checks for bytes read/written and copy_to/from_user results */
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
if (dev == NULL) {
return -ENODEV;
}
@@ -493,7 +494,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
return -ENOMEM;
/* lock this object */
- lock_kernel();
+ mutex_lock(&iowarrior_mutex);
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
@@ -585,7 +586,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
error_out:
/* unlock the device */
mutex_unlock(&dev->mutex);
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
kfree(buffer);
return retval;
}
@@ -602,12 +603,12 @@ static int iowarrior_open(struct inode *inode, struct file *file)
dbg("%s", __func__);
- lock_kernel();
+ mutex_lock(&iowarrior_mutex);
subminor = iminor(inode);
interface = usb_find_interface(&iowarrior_driver, subminor);
if (!interface) {
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
err("%s - error, can't find device for minor %d", __func__,
subminor);
return -ENODEV;
@@ -617,7 +618,7 @@ static int iowarrior_open(struct inode *inode, struct file *file)
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&iowarrior_open_disc_lock);
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
return -ENODEV;
}
@@ -644,7 +645,7 @@ static int iowarrior_open(struct inode *inode, struct file *file)
out:
mutex_unlock(&dev->mutex);
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
return retval;
}
@@ -656,7 +657,7 @@ static int iowarrior_release(struct inode *inode, struct file *file)
struct iowarrior *dev;
int retval = 0;
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
if (dev == NULL) {
return -ENODEV;
}
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 8547bf9..6482c6e 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -448,7 +448,7 @@ static int tower_release (struct inode *inode, struct file *file)
dbg(2, "%s: enter", __func__);
- dev = (struct lego_usb_tower *)file->private_data;
+ dev = file->private_data;
if (dev == NULL) {
dbg(1, "%s: object is NULL", __func__);
@@ -597,7 +597,7 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count,
dbg(2, "%s: enter, count = %Zd", __func__, count);
- dev = (struct lego_usb_tower *)file->private_data;
+ dev = file->private_data;
/* lock this object */
if (mutex_lock_interruptible(&dev->lock)) {
@@ -686,7 +686,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t
dbg(2, "%s: enter, count = %Zd", __func__, count);
- dev = (struct lego_usb_tower *)file->private_data;
+ dev = file->private_data;
/* lock this object */
if (mutex_lock_interruptible(&dev->lock)) {
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
index a85771b..cc13ae6 100644
--- a/drivers/usb/misc/rio500.c
+++ b/drivers/usb/misc/rio500.c
@@ -32,7 +32,7 @@
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/poll.h>
@@ -72,6 +72,7 @@ struct rio_usb_data {
struct mutex lock; /* general race avoidance */
};
+static DEFINE_MUTEX(rio500_mutex);
static struct rio_usb_data rio_instance;
static int open_rio(struct inode *inode, struct file *file)
@@ -79,12 +80,12 @@ static int open_rio(struct inode *inode, struct file *file)
struct rio_usb_data *rio = &rio_instance;
/* against disconnect() */
- lock_kernel();
+ mutex_lock(&rio500_mutex);
mutex_lock(&(rio->lock));
if (rio->isopen || !rio->present) {
mutex_unlock(&(rio->lock));
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
return -EBUSY;
}
rio->isopen = 1;
@@ -94,7 +95,7 @@ static int open_rio(struct inode *inode, struct file *file)
mutex_unlock(&(rio->lock));
dev_info(&rio->rio_dev->dev, "Rio opened.\n");
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
return 0;
}
@@ -491,7 +492,7 @@ static void disconnect_rio(struct usb_interface *intf)
struct rio_usb_data *rio = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
- lock_kernel();
+ mutex_lock(&rio500_mutex);
if (rio) {
usb_deregister_dev(intf, &usb_rio_class);
@@ -501,7 +502,7 @@ static void disconnect_rio(struct usb_interface *intf)
/* better let it finish - the release will do whats needed */
rio->rio_dev = NULL;
mutex_unlock(&(rio->lock));
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
return;
}
kfree(rio->ibuf);
@@ -512,7 +513,7 @@ static void disconnect_rio(struct usb_interface *intf)
rio->present = 0;
mutex_unlock(&(rio->lock));
}
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
}
static const struct usb_device_id rio_table[] = {
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index d25814c..70d00e9 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -2487,7 +2487,7 @@ sisusb_release(struct inode *inode, struct file *file)
{
struct sisusb_usb_data *sisusb;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
@@ -2519,7 +2519,7 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
u16 buf16;
u32 buf32, address;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
@@ -2661,7 +2661,7 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count,
u16 buf16;
u32 buf32, address;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
@@ -2804,7 +2804,7 @@ sisusb_lseek(struct file *file, loff_t offset, int orig)
struct sisusb_usb_data *sisusb;
loff_t ret;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
@@ -2969,7 +2969,7 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
long retval = 0;
u32 __user *argp = (u32 __user *)arg;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 7828c76..d00dde1 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -16,7 +16,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
@@ -30,6 +29,7 @@
#define IOCTL_GET_DRV_VERSION 2
+static DEFINE_MUTEX(lcd_mutex);
static const struct usb_device_id id_table[] = {
{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
{ },
@@ -74,12 +74,12 @@ static int lcd_open(struct inode *inode, struct file *file)
struct usb_interface *interface;
int subminor, r;
- lock_kernel();
+ mutex_lock(&lcd_mutex);
subminor = iminor(inode);
interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) {
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
err ("USBLCD: %s - error, can't find device for minor %d",
__func__, subminor);
return -ENODEV;
@@ -89,7 +89,7 @@ static int lcd_open(struct inode *inode, struct file *file)
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&open_disc_mutex);
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
return -ENODEV;
}
@@ -101,13 +101,13 @@ static int lcd_open(struct inode *inode, struct file *file)
r = usb_autopm_get_interface(interface);
if (r < 0) {
kref_put(&dev->kref, lcd_delete);
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
return r;
}
/* save our object in the file's private structure */
file->private_data = dev;
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
return 0;
}
@@ -116,7 +116,7 @@ static int lcd_release(struct inode *inode, struct file *file)
{
struct usb_lcd *dev;
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
@@ -132,7 +132,7 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l
int retval = 0;
int bytes_read;
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
@@ -158,20 +158,20 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
u16 bcdDevice;
char buf[30];
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
switch (cmd) {
case IOCTL_GET_HARD_VERSION:
- lock_kernel();
+ mutex_lock(&lcd_mutex);
bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
sprintf(buf,"%1d%1d.%1d%1d",
(bcdDevice & 0xF000)>>12,
(bcdDevice & 0xF00)>>8,
(bcdDevice & 0xF0)>>4,
(bcdDevice & 0xF));
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
return -EFAULT;
break;
@@ -217,7 +217,7 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
struct urb *urb = NULL;
char *buf = NULL;
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 16dffe9..eef370e 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -136,7 +136,7 @@ try_iso:
iso_out = e;
}
}
- if ((in && out) || (iso_in && iso_out))
+ if ((in && out) || iso_in || iso_out)
goto found;
}
return -EINVAL;
@@ -162,6 +162,9 @@ found:
dev->in_iso_pipe = usb_rcvisocpipe (udev,
iso_in->desc.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK);
+ }
+
+ if (iso_out) {
dev->iso_out = &iso_out->desc;
dev->out_iso_pipe = usb_sndisocpipe (udev,
iso_out->desc.bEndpointAddress
@@ -1378,7 +1381,6 @@ static void iso_callback (struct urb *urb)
break;
}
}
- simple_free_urb (urb);
ctx->pending--;
if (ctx->pending == 0) {
@@ -1495,6 +1497,7 @@ test_iso_queue (struct usbtest_dev *dev, struct usbtest_param *param,
}
simple_free_urb (urbs [i]);
+ urbs[i] = NULL;
context.pending--;
context.submit_error = 1;
break;
@@ -1504,6 +1507,10 @@ test_iso_queue (struct usbtest_dev *dev, struct usbtest_param *param,
wait_for_completion (&context.done);
+ for (i = 0; i < param->sglen; i++) {
+ if (urbs[i])
+ simple_free_urb(urbs[i]);
+ }
/*
* Isochronous transfers are expected to fail sometimes. As an
* arbitrary limit, we will report an error if any submissions
@@ -1548,6 +1555,7 @@ fail:
* off just killing the userspace task and waiting for it to exit.
*/
+/* No BKL needed */
static int
usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
{
@@ -2170,7 +2178,7 @@ static struct usb_driver usbtest_driver = {
.name = "usbtest",
.id_table = id_table,
.probe = usbtest_probe,
- .ioctl = usbtest_ioctl,
+ .unlocked_ioctl = usbtest_ioctl,
.disconnect = usbtest_disconnect,
.suspend = usbtest_suspend,
.resume = usbtest_resume,
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 61c76b1..44cb37b 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -646,17 +646,14 @@ static int mon_bin_open(struct inode *inode, struct file *file)
size_t size;
int rc;
- lock_kernel();
mutex_lock(&mon_lock);
if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) {
mutex_unlock(&mon_lock);
- unlock_kernel();
return -ENODEV;
}
if (mbus != &mon_bus0 && mbus->u_bus == NULL) {
printk(KERN_ERR TAG ": consistency error on open\n");
mutex_unlock(&mon_lock);
- unlock_kernel();
return -ENODEV;
}
@@ -689,7 +686,6 @@ static int mon_bin_open(struct inode *inode, struct file *file)
file->private_data = rp;
mutex_unlock(&mon_lock);
- unlock_kernel();
return 0;
err_allocbuff:
@@ -698,7 +694,6 @@ err_allocvec:
kfree(rp);
err_alloc:
mutex_unlock(&mon_lock);
- unlock_kernel();
return rc;
}
@@ -954,7 +949,7 @@ static int mon_bin_queued(struct mon_reader_bin *rp)
/*
*/
-static int mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct mon_reader_bin *rp = file->private_data;
// struct mon_bus* mbus = rp->r.m_bus;
@@ -1009,7 +1004,7 @@ static int mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
mutex_lock(&rp->fetch_lock);
spin_lock_irqsave(&rp->b_lock, flags);
- mon_free_buff(rp->b_vec, size/CHUNK_SIZE);
+ mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
kfree(rp->b_vec);
rp->b_vec = vec;
rp->b_size = size;
@@ -1094,19 +1089,6 @@ static int mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return ret;
}
-static long mon_bin_unlocked_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
-
- lock_kernel();
- ret = mon_bin_ioctl(file, cmd, arg);
- unlock_kernel();
-
- return ret;
-}
-
-
#ifdef CONFIG_COMPAT
static long mon_bin_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
@@ -1250,7 +1232,7 @@ static const struct file_operations mon_fops_binary = {
.read = mon_bin_read,
/* .write = mon_text_write, */
.poll = mon_bin_poll,
- .unlocked_ioctl = mon_bin_unlocked_ioctl,
+ .unlocked_ioctl = mon_bin_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mon_bin_compat_ioctl,
#endif
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 3b795c5..540c766 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -704,7 +704,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (int_usb & MUSB_INTR_CONNECT) {
struct usb_hcd *hcd = musb_to_hcd(musb);
- void __iomem *mbase = musb->mregs;
handled = IRQ_HANDLED;
musb->is_active = 1;
@@ -717,9 +716,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
if (is_peripheral_active(musb)) {
/* REVISIT HNP; just force disconnect */
}
- musb_writew(mbase, MUSB_INTRTXE, musb->epmask);
- musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe);
- musb_writeb(mbase, MUSB_INTRUSBE, 0xf7);
+ musb_writew(musb->mregs, MUSB_INTRTXE, musb->epmask);
+ musb_writew(musb->mregs, MUSB_INTRRXE, musb->epmask & 0xfffe);
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
#endif
musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
|USB_PORT_STAT_HIGH_SPEED
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index bba76af..c79a5e3 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -92,29 +92,29 @@ static const struct musb_register_map musb_regmap[] = {
{ "LS_EOF1", 0x7E, 8 },
{ "SOFT_RST", 0x7F, 8 },
{ "DMA_CNTLch0", 0x204, 16 },
- { "DMA_ADDRch0", 0x208, 16 },
- { "DMA_COUNTch0", 0x20C, 16 },
+ { "DMA_ADDRch0", 0x208, 32 },
+ { "DMA_COUNTch0", 0x20C, 32 },
{ "DMA_CNTLch1", 0x214, 16 },
- { "DMA_ADDRch1", 0x218, 16 },
- { "DMA_COUNTch1", 0x21C, 16 },
+ { "DMA_ADDRch1", 0x218, 32 },
+ { "DMA_COUNTch1", 0x21C, 32 },
{ "DMA_CNTLch2", 0x224, 16 },
- { "DMA_ADDRch2", 0x228, 16 },
- { "DMA_COUNTch2", 0x22C, 16 },
+ { "DMA_ADDRch2", 0x228, 32 },
+ { "DMA_COUNTch2", 0x22C, 32 },
{ "DMA_CNTLch3", 0x234, 16 },
- { "DMA_ADDRch3", 0x238, 16 },
- { "DMA_COUNTch3", 0x23C, 16 },
+ { "DMA_ADDRch3", 0x238, 32 },
+ { "DMA_COUNTch3", 0x23C, 32 },
{ "DMA_CNTLch4", 0x244, 16 },
- { "DMA_ADDRch4", 0x248, 16 },
- { "DMA_COUNTch4", 0x24C, 16 },
+ { "DMA_ADDRch4", 0x248, 32 },
+ { "DMA_COUNTch4", 0x24C, 32 },
{ "DMA_CNTLch5", 0x254, 16 },
- { "DMA_ADDRch5", 0x258, 16 },
- { "DMA_COUNTch5", 0x25C, 16 },
+ { "DMA_ADDRch5", 0x258, 32 },
+ { "DMA_COUNTch5", 0x25C, 32 },
{ "DMA_CNTLch6", 0x264, 16 },
- { "DMA_ADDRch6", 0x268, 16 },
- { "DMA_COUNTch6", 0x26C, 16 },
+ { "DMA_ADDRch6", 0x268, 32 },
+ { "DMA_COUNTch6", 0x26C, 32 },
{ "DMA_CNTLch7", 0x274, 16 },
- { "DMA_ADDRch7", 0x278, 16 },
- { "DMA_COUNTch7", 0x27C, 16 },
+ { "DMA_ADDRch7", 0x278, 32 },
+ { "DMA_COUNTch7", 0x27C, 32 },
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 21b9788..59bef8f 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -402,6 +402,9 @@ __acquires(musb->lock)
musb->g.a_alt_hnp_support = 1;
break;
#endif
+ case USB_DEVICE_DEBUG_MODE:
+ handled = 0;
+ break;
stall:
default:
handled = -EINVAL;
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 92e85e0..43233c3 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -244,7 +244,7 @@ int musb_hub_control(
spin_lock_irqsave(&musb->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) {
spin_unlock_irqrestore(&musb->lock, flags);
return -ESHUTDOWN;
}
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index dc66e43..6dc107f 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -173,10 +173,7 @@ static int dma_channel_program(struct dma_channel *channel,
musb_channel->max_packet_sz = packet_sz;
channel->status = MUSB_DMA_STATUS_BUSY;
- if ((mode == 1) && (len >= packet_sz))
- configure_channel(channel, packet_sz, 1, dma_addr, len);
- else
- configure_channel(channel, packet_sz, 0, dma_addr, len);
+ configure_channel(channel, packet_sz, mode, dma_addr, len);
return true;
}
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index e06d65e..2111a24 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -32,8 +32,6 @@
#include <linux/clk.h>
#include <linux/io.h>
-#include <plat/mux.h>
-
#include "musb_core.h"
#include "omap2430.h"
@@ -194,10 +192,6 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
u32 l;
struct omap_musb_board_data *data = board_data;
-#if defined(CONFIG_ARCH_OMAP2430)
- omap_cfg_reg(AE5_2430_USB0HS_STP);
-#endif
-
/* We require some kind of external transceiver, hooked
* up through ULPI. TWL4030-family PMICs include one,
* which needs a driver, drivers aren't always needed.
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 3d2d3e5..3b12895 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -49,8 +49,6 @@ config USB_ULPI
Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards.
- The only chip currently supported is NXP's ISP1504
-
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030
diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c
index d331b22..ccc8195 100644
--- a/drivers/usb/otg/ulpi.c
+++ b/drivers/usb/otg/ulpi.c
@@ -31,30 +31,110 @@
#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
-#define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0)
-
/* ULPI hardcoded IDs, used for probing */
static unsigned int ulpi_ids[] = {
ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */
+ ULPI_ID(0x0424, 0x0006), /* SMSC USB3319 */
};
-static int ulpi_set_flags(struct otg_transceiver *otg)
+static int ulpi_set_otg_flags(struct otg_transceiver *otg)
{
- unsigned int flags = 0;
+ unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
+ ULPI_OTG_CTRL_DM_PULLDOWN;
- if (otg->flags & USB_OTG_PULLUP_ID)
+ if (otg->flags & ULPI_OTG_ID_PULLUP)
flags |= ULPI_OTG_CTRL_ID_PULLUP;
- if (otg->flags & USB_OTG_PULLDOWN_DM)
- flags |= ULPI_OTG_CTRL_DM_PULLDOWN;
+ /*
+ * ULPI Specification rev.1.1 default
+ * for Dp/DmPulldown is enabled.
+ */
+ if (otg->flags & ULPI_OTG_DP_PULLDOWN_DIS)
+ flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
- if (otg->flags & USB_OTG_PULLDOWN_DP)
- flags |= ULPI_OTG_CTRL_DP_PULLDOWN;
+ if (otg->flags & ULPI_OTG_DM_PULLDOWN_DIS)
+ flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
- if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR)
+ if (otg->flags & ULPI_OTG_EXTVBUSIND)
flags |= ULPI_OTG_CTRL_EXTVBUSIND;
- return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL));
+ return otg_io_write(otg, flags, ULPI_OTG_CTRL);
+}
+
+static int ulpi_set_fc_flags(struct otg_transceiver *otg)
+{
+ unsigned int flags = 0;
+
+ /*
+ * ULPI Specification rev.1.1 default
+ * for XcvrSelect is Full Speed.
+ */
+ if (otg->flags & ULPI_FC_HS)
+ flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
+ else if (otg->flags & ULPI_FC_LS)
+ flags |= ULPI_FUNC_CTRL_LOW_SPEED;
+ else if (otg->flags & ULPI_FC_FS4LS)
+ flags |= ULPI_FUNC_CTRL_FS4LS;
+ else
+ flags |= ULPI_FUNC_CTRL_FULL_SPEED;
+
+ if (otg->flags & ULPI_FC_TERMSEL)
+ flags |= ULPI_FUNC_CTRL_TERMSELECT;
+
+ /*
+ * ULPI Specification rev.1.1 default
+ * for OpMode is Normal Operation.
+ */
+ if (otg->flags & ULPI_FC_OP_NODRV)
+ flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ else if (otg->flags & ULPI_FC_OP_DIS_NRZI)
+ flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
+ else if (otg->flags & ULPI_FC_OP_NSYNC_NEOP)
+ flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
+ else
+ flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+
+ /*
+ * ULPI Specification rev.1.1 default
+ * for SuspendM is Powered.
+ */
+ flags |= ULPI_FUNC_CTRL_SUSPENDM;
+
+ return otg_io_write(otg, flags, ULPI_FUNC_CTRL);
+}
+
+static int ulpi_set_ic_flags(struct otg_transceiver *otg)
+{
+ unsigned int flags = 0;
+
+ if (otg->flags & ULPI_IC_AUTORESUME)
+ flags |= ULPI_IFC_CTRL_AUTORESUME;
+
+ if (otg->flags & ULPI_IC_EXTVBUS_INDINV)
+ flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
+
+ if (otg->flags & ULPI_IC_IND_PASSTHRU)
+ flags |= ULPI_IFC_CTRL_PASSTHRU;
+
+ if (otg->flags & ULPI_IC_PROTECT_DIS)
+ flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
+
+ return otg_io_write(otg, flags, ULPI_IFC_CTRL);
+}
+
+static int ulpi_set_flags(struct otg_transceiver *otg)
+{
+ int ret;
+
+ ret = ulpi_set_otg_flags(otg);
+ if (ret)
+ return ret;
+
+ ret = ulpi_set_ic_flags(otg);
+ if (ret)
+ return ret;
+
+ return ulpi_set_fc_flags(otg);
}
static int ulpi_init(struct otg_transceiver *otg)
@@ -81,6 +161,31 @@ static int ulpi_init(struct otg_transceiver *otg)
return -ENODEV;
}
+static int ulpi_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+ unsigned int flags = otg_io_read(otg, ULPI_IFC_CTRL);
+
+ if (!host) {
+ otg->host = NULL;
+ return 0;
+ }
+
+ otg->host = host;
+
+ flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
+ ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
+ ULPI_IFC_CTRL_CARKITMODE);
+
+ if (otg->flags & ULPI_IC_6PIN_SERIAL)
+ flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
+ else if (otg->flags & ULPI_IC_3PIN_SERIAL)
+ flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
+ else if (otg->flags & ULPI_IC_CARKIT)
+ flags |= ULPI_IFC_CTRL_CARKITMODE;
+
+ return otg_io_write(otg, flags, ULPI_IFC_CTRL);
+}
+
static int ulpi_set_vbus(struct otg_transceiver *otg, bool on)
{
unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL);
@@ -88,14 +193,14 @@ static int ulpi_set_vbus(struct otg_transceiver *otg, bool on)
flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
if (on) {
- if (otg->flags & USB_OTG_DRV_VBUS)
+ if (otg->flags & ULPI_OTG_DRVVBUS)
flags |= ULPI_OTG_CTRL_DRVVBUS;
- if (otg->flags & USB_OTG_DRV_VBUS_EXT)
+ if (otg->flags & ULPI_OTG_DRVVBUS_EXT)
flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
}
- return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL));
+ return otg_io_write(otg, flags, ULPI_OTG_CTRL);
}
struct otg_transceiver *
@@ -112,6 +217,7 @@ otg_ulpi_create(struct otg_io_access_ops *ops,
otg->flags = flags;
otg->io_ops = ops;
otg->init = ulpi_init;
+ otg->set_host = ulpi_set_host;
otg->set_vbus = ulpi_set_vbus;
return otg;
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index bd8aab0..916b2b6 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -642,6 +642,15 @@ config USB_SERIAL_ZIO
To compile this driver as a module, choose M here: the
module will be called zio.
+config USB_SERIAL_SSU100
+ tristate "USB Quatech SSU-100 Single Port Serial Driver"
+ help
+ Say Y here if you want to use the Quatech SSU-100 single
+ port usb to serial adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ssu100.
+
config USB_SERIAL_DEBUG
tristate "USB Debugging Device"
help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index e54c728..40ebe17 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o
obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o
obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o
+obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o
obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o
obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o
obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 8b8c797..2bef441 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -126,6 +126,10 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
+ { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
+ { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
+ { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */
+ { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index fd35f73..b92070c 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -609,8 +609,10 @@ static void digi_wakeup_write_lock(struct work_struct *work)
static void digi_wakeup_write(struct usb_serial_port *port)
{
struct tty_struct *tty = tty_port_tty_get(&port->port);
- tty_wakeup(tty);
- tty_kref_put(tty);
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
}
@@ -1682,7 +1684,7 @@ static int digi_read_inb_callback(struct urb *urb)
priv->dp_throttle_restart = 1;
/* receive data */
- if (opcode == DIGI_CMD_RECEIVE_DATA) {
+ if (tty && opcode == DIGI_CMD_RECEIVE_DATA) {
/* get flag from port_status */
flag = 0;
@@ -1763,10 +1765,12 @@ static int digi_read_oob_callback(struct urb *urb)
return -1;
tty = tty_port_tty_get(&port->port);
+
rts = 0;
- rts = tty->termios->c_cflag & CRTSCTS;
+ if (tty)
+ rts = tty->termios->c_cflag & CRTSCTS;
- if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
+ if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
spin_lock(&priv->dp_port_lock);
/* convert from digi flags to termiox flags */
if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index e298dc4..eb12d9b 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -157,6 +157,9 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) },
@@ -746,6 +749,7 @@ static struct usb_device_id id_table_combined [] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index d01946d..6e612c5 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -40,6 +40,11 @@
#define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */
+/* US Interface Navigator (http://www.usinterface.com/) */
+#define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */
+#define FTDI_USINT_WKEY_PID 0xb811 /* Navigator WKEY and FSK lines */
+#define FTDI_USINT_RS232_PID 0xb812 /* Navigator RS232 and CONFIG lines */
+
/* 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 */
@@ -1032,3 +1037,8 @@
#define XVERVE_SIGNALYZER_SH2_PID 0xBCA2
#define XVERVE_SIGNALYZER_SH4_PID 0xBCA4
+/*
+ * Segway Robotic Mobility Platform USB interface (using VID 0x0403)
+ * Submitted by John G. Rogers
+ */
+#define SEGWAY_RMP200_PID 0xe729
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index a817ced..ca92f67 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -208,18 +208,23 @@ retry:
urb->transfer_buffer_length = count;
usb_serial_debug_data(debug, &port->dev, __func__, count,
urb->transfer_buffer);
+ spin_lock_irqsave(&port->lock, flags);
+ port->tx_bytes += count;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ clear_bit(i, &port->write_urbs_free);
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result) {
dev_err(&port->dev, "%s - error submitting urb: %d\n",
__func__, result);
+ set_bit(i, &port->write_urbs_free);
+ spin_lock_irqsave(&port->lock, flags);
+ port->tx_bytes -= count;
+ spin_unlock_irqrestore(&port->lock, flags);
+
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
return result;
}
- clear_bit(i, &port->write_urbs_free);
-
- spin_lock_irqsave(&port->lock, flags);
- port->tx_bytes += count;
- spin_unlock_irqrestore(&port->lock, flags);
/* Try sending off another urb, unless in irq context (in which case
* there will be no free urb). */
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 0fca265..dc47f98 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1298,7 +1298,7 @@ static int download_fw(struct edgeport_serial *serial)
kfree(header);
kfree(rom_desc);
kfree(ti_manuf_desc);
- return status;
+ return -EINVAL;
}
/* verify the write -- must do this in order for
@@ -1321,7 +1321,7 @@ static int download_fw(struct edgeport_serial *serial)
kfree(header);
kfree(rom_desc);
kfree(ti_manuf_desc);
- return status;
+ return -EINVAL;
}
kfree(vheader);
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index 28913fa..4735931 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -534,7 +534,6 @@ static struct usb_device_id ipaq_id_table [] = {
{ USB_DEVICE(0x413C, 0x4009) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x4505, 0x0010) }, /* Smartphone */
{ USB_DEVICE(0x5E04, 0xCE00) }, /* SAGEM Wireless Assistant */
- { USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC smartphone modems */
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 74551cb..efc7211 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -1,6 +1,8 @@
/*
* Infinity Unlimited USB Phoenix driver
*
+ * Copyright (C) 2010 James Courtier-Dutton (James@superbug.co.uk)
+
* Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
*
* Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
@@ -40,7 +42,7 @@ static int debug;
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.11"
+#define DRIVER_VERSION "v0.12"
#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver"
static const struct usb_device_id id_table[] = {
@@ -81,6 +83,9 @@ struct iuu_private {
u8 *dbgbuf; /* debug buffer */
u8 len;
int vcc; /* vcc (either 3 or 5 V) */
+ u32 baud;
+ u32 boost;
+ u32 clk;
};
@@ -157,13 +162,14 @@ static int iuu_tiocmset(struct tty_struct *tty, struct file *file,
port->number, set, clear);
spin_lock_irqsave(&priv->lock, flags);
- if (set & TIOCM_RTS)
- priv->tiostatus = TIOCM_RTS;
- if (!(set & TIOCM_RTS) && priv->tiostatus == TIOCM_RTS) {
+ if ((set & TIOCM_RTS) && !(priv->tiostatus == TIOCM_RTS)) {
dbg("%s TIOCMSET RESET called !!!", __func__);
priv->reset = 1;
}
+ if (set & TIOCM_RTS)
+ priv->tiostatus = TIOCM_RTS;
+
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -851,20 +857,24 @@ static int iuu_uart_off(struct usb_serial_port *port)
return status;
}
-static int iuu_uart_baud(struct usb_serial_port *port, u32 baud,
+static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
u32 *actual, u8 parity)
{
int status;
+ u32 baud;
u8 *dataout;
u8 DataCount = 0;
u8 T1Frekvens = 0;
u8 T1reload = 0;
unsigned int T1FrekvensHZ = 0;
+ dbg("%s - enter baud_base=%d", __func__, baud_base);
dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL);
if (!dataout)
return -ENOMEM;
+ /*baud = (((priv->clk / 35) * baud_base) / 100000); */
+ baud = baud_base;
if (baud < 1200 || baud > 230400) {
kfree(dataout);
@@ -948,15 +958,20 @@ static void iuu_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
const u32 supported_mask = CMSPAR|PARENB|PARODD;
-
+ struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned int cflag = tty->termios->c_cflag;
int status;
u32 actual;
u32 parity;
int csize = CS7;
- int baud = 9600; /* Fixed for the moment */
+ int baud;
u32 newval = cflag & supported_mask;
+ /* Just use the ospeed. ispeed should be the same. */
+ baud = tty->termios->c_ospeed;
+
+ dbg("%s - enter c_ospeed or baud=%d", __func__, baud);
+
/* compute the parity parameter */
parity = 0;
if (cflag & CMSPAR) { /* Using mark space */
@@ -976,15 +991,15 @@ static void iuu_set_termios(struct tty_struct *tty,
/* set it */
status = iuu_uart_baud(port,
- (clockmode == 2) ? 16457 : 9600 * boost / 100,
+ baud * priv->boost / 100,
&actual, parity);
/* set the termios value to the real one, so the user now what has
* changed. We support few fields so its easies to copy the old hw
* settings back over and then adjust them
*/
- if (old_termios)
- tty_termios_copy_hw(tty->termios, old_termios);
+ if (old_termios)
+ tty_termios_copy_hw(tty->termios, old_termios);
if (status != 0) /* Set failed - return old bits */
return;
/* Re-encode speed, parity and csize */
@@ -1018,6 +1033,7 @@ static void iuu_close(struct usb_serial_port *port)
static void iuu_init_termios(struct tty_struct *tty)
{
+ dbg("%s - enter", __func__);
*(tty->termios) = tty_std_termios;
tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600
| TIOCM_CTS | CSTOPB | PARENB;
@@ -1033,10 +1049,16 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
u8 *buf;
int result;
+ int baud;
u32 actual;
struct iuu_private *priv = usb_get_serial_port_data(port);
- dbg("%s - port %d", __func__, port->number);
+ baud = tty->termios->c_ospeed;
+ tty->termios->c_ispeed = baud;
+ /* Re-encode speed */
+ tty_encode_baud_rate(tty, baud, baud);
+
+ dbg("%s - port %d, baud %d", __func__, port->number, baud);
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
@@ -1071,23 +1093,29 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
iuu_uart_on(port);
if (boost < 100)
boost = 100;
+ priv->boost = boost;
+ priv->baud = baud;
switch (clockmode) {
case 2: /* 3.680 Mhz */
+ priv->clk = IUU_CLK_3680000;
iuu_clk(port, IUU_CLK_3680000 * boost / 100);
result =
- iuu_uart_baud(port, 9600 * boost / 100, &actual,
+ iuu_uart_baud(port, baud * boost / 100, &actual,
IUU_PARITY_EVEN);
break;
case 3: /* 6.00 Mhz */
iuu_clk(port, IUU_CLK_6000000 * boost / 100);
+ priv->clk = IUU_CLK_6000000;
+ /* Ratio of 6000000 to 3500000 for baud 9600 */
result =
iuu_uart_baud(port, 16457 * boost / 100, &actual,
IUU_PARITY_EVEN);
break;
default: /* 3.579 Mhz */
iuu_clk(port, IUU_CLK_3579000 * boost / 100);
+ priv->clk = IUU_CLK_3579000;
result =
- iuu_uart_baud(port, 9600 * boost / 100, &actual,
+ iuu_uart_baud(port, baud * boost / 100, &actual,
IUU_PARITY_EVEN);
}
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 5cd30e43..9fc6ea2 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -145,7 +145,10 @@ static void option_instat_callback(struct urb *urb);
#define HUAWEI_PRODUCT_E143D 0x143D
#define HUAWEI_PRODUCT_E143E 0x143E
#define HUAWEI_PRODUCT_E143F 0x143F
+#define HUAWEI_PRODUCT_K4505 0x1464
+#define HUAWEI_PRODUCT_K3765 0x1465
#define HUAWEI_PRODUCT_E14AC 0x14AC
+#define HUAWEI_PRODUCT_ETS1220 0x1803
#define QUANTA_VENDOR_ID 0x0408
#define QUANTA_PRODUCT_Q101 0xEA02
@@ -264,9 +267,6 @@ static void option_instat_callback(struct urb *urb);
#define BANDRICH_PRODUCT_1011 0x1011
#define BANDRICH_PRODUCT_1012 0x1012
-#define AMOI_VENDOR_ID 0x1614
-#define AMOI_PRODUCT_9508 0x0800
-
#define QUALCOMM_VENDOR_ID 0x05C6
#define CMOTECH_VENDOR_ID 0x16d8
@@ -482,8 +482,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) },
- { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, /* Novatel Merlin EX720/V740/X720 */
@@ -1017,6 +1019,13 @@ static int option_probe(struct usb_serial *serial,
serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
return -ENODEV;
+ /* Don't bind network interfaces on Huawei K3765 & K4505 */
+ if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID &&
+ (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 ||
+ serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505) &&
+ serial->interface->cur_altsetting->desc.bInterfaceNumber == 1)
+ return -ENODEV;
+
data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
if (!data)
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
new file mode 100644
index 0000000..6e82d4f
--- /dev/null
+++ b/drivers/usb/serial/ssu100.c
@@ -0,0 +1,698 @@
+/*
+ * usb-serial driver for Quatech SSU-100
+ *
+ * based on ftdi_sio.c and the original serqt_usb.c from Quatech
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define QT_OPEN_CLOSE_CHANNEL 0xca
+#define QT_SET_GET_DEVICE 0xc2
+#define QT_SET_GET_REGISTER 0xc0
+#define QT_GET_SET_PREBUF_TRIG_LVL 0xcc
+#define QT_SET_ATF 0xcd
+#define QT_GET_SET_UART 0xc1
+#define QT_TRANSFER_IN 0xc0
+#define QT_HW_FLOW_CONTROL_MASK 0xc5
+#define QT_SW_FLOW_CONTROL_MASK 0xc6
+
+#define MODEM_CTL_REGISTER 0x04
+#define MODEM_STATUS_REGISTER 0x06
+
+
+#define SERIAL_LSR_OE 0x02
+#define SERIAL_LSR_PE 0x04
+#define SERIAL_LSR_FE 0x08
+#define SERIAL_LSR_BI 0x10
+
+#define SERIAL_LSR_TEMT 0x40
+
+#define SERIAL_MCR_DTR 0x01
+#define SERIAL_MCR_RTS 0x02
+#define SERIAL_MCR_LOOP 0x10
+
+#define SERIAL_MSR_CTS 0x10
+#define SERIAL_MSR_CD 0x80
+#define SERIAL_MSR_RI 0x40
+#define SERIAL_MSR_DSR 0x20
+#define SERIAL_MSR_MASK 0xf0
+
+#define SERIAL_CRTSCTS ((SERIAL_MCR_RTS << 8) | SERIAL_MSR_CTS)
+
+#define SERIAL_8_DATA 0x03
+#define SERIAL_7_DATA 0x02
+#define SERIAL_6_DATA 0x01
+#define SERIAL_5_DATA 0x00
+
+#define SERIAL_ODD_PARITY 0X08
+#define SERIAL_EVEN_PARITY 0X18
+
+#define MAX_BAUD_RATE 460800
+
+#define ATC_DISABLED 0x00
+#define DUPMODE_BITS 0xc0
+#define RR_BITS 0x03
+#define LOOPMODE_BITS 0x41
+#define RS232_MODE 0x00
+#define RTSCTS_TO_CONNECTOR 0x40
+#define CLKS_X4 0x02
+#define FULLPWRBIT 0x00000080
+#define NEXT_BOARD_POWER_BIT 0x00000004
+
+static int debug = 1;
+
+/* Version Information */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
+
+#define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */
+#define QUATECH_SSU100 0xC020 /* SSU100 */
+
+static const struct usb_device_id id_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+static struct usb_driver ssu100_driver = {
+ .name = "ssu100",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+ .suspend = usb_serial_suspend,
+ .resume = usb_serial_resume,
+ .no_dynamic_id = 1,
+ .supports_autosuspend = 1,
+};
+
+struct ssu100_port_private {
+ u8 shadowLSR;
+ u8 shadowMSR;
+ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
+ unsigned short max_packet_size;
+};
+
+static void ssu100_release(struct usb_serial *serial)
+{
+ struct ssu100_port_private *priv = usb_get_serial_port_data(*serial->port);
+
+ dbg("%s", __func__);
+ kfree(priv);
+}
+
+static inline int ssu100_control_msg(struct usb_device *dev,
+ u8 request, u16 data, u16 index)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ request, 0x40, data, index,
+ NULL, 0, 300);
+}
+
+static inline int ssu100_setdevice(struct usb_device *dev, u8 *data)
+{
+ u16 x = ((u16)(data[1] << 8) | (u16)(data[0]));
+
+ return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0);
+}
+
+
+static inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_DEVICE, 0xc0, 0, 0,
+ data, 3, 300);
+}
+
+static inline int ssu100_getregister(struct usb_device *dev,
+ unsigned short uart,
+ unsigned short reg,
+ u8 *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_REGISTER, 0xc0, reg,
+ uart, data, sizeof(*data), 300);
+
+}
+
+
+static inline int ssu100_setregister(struct usb_device *dev,
+ unsigned short uart,
+ u16 data)
+{
+ u16 value = (data << 8) | MODEM_CTL_REGISTER;
+
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ QT_SET_GET_REGISTER, 0x40, value, uart,
+ NULL, 0, 300);
+
+}
+
+#define set_mctrl(dev, set) update_mctrl((dev), (set), 0)
+#define clear_mctrl(dev, clear) update_mctrl((dev), 0, (clear))
+
+/* these do not deal with device that have more than 1 port */
+static inline int update_mctrl(struct usb_device *dev, unsigned int set,
+ unsigned int clear)
+{
+ unsigned urb_value;
+ int result;
+
+ if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+ dbg("%s - DTR|RTS not being set|cleared", __func__);
+ return 0; /* no change */
+ }
+
+ clear &= ~set; /* 'set' takes precedence over 'clear' */
+ urb_value = 0;
+ if (set & TIOCM_DTR)
+ urb_value |= SERIAL_MCR_DTR;
+ if (set & TIOCM_RTS)
+ urb_value |= SERIAL_MCR_RTS;
+
+ result = ssu100_setregister(dev, 0, urb_value);
+ if (result < 0)
+ dbg("%s Error from MODEM_CTRL urb", __func__);
+
+ return result;
+}
+
+static int ssu100_initdevice(struct usb_device *dev)
+{
+ u8 *data;
+ int result = 0;
+
+ dbg("%s", __func__);
+
+ data = kzalloc(3, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ result = ssu100_getdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - get_device failed %i", __func__, result);
+ goto out;
+ }
+
+ data[1] &= ~FULLPWRBIT;
+
+ result = ssu100_setdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - setdevice failed %i", __func__, result);
+ goto out;
+ }
+
+ result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
+ if (result < 0) {
+ dbg("%s - set prebuffer level failed %i", __func__, result);
+ goto out;
+ }
+
+ result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
+ if (result < 0) {
+ dbg("%s - set ATFprebuffer level failed %i", __func__, result);
+ goto out;
+ }
+
+ result = ssu100_getdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - get_device failed %i", __func__, result);
+ goto out;
+ }
+
+ data[0] &= ~(RR_BITS | DUPMODE_BITS);
+ data[0] |= CLKS_X4;
+ data[1] &= ~(LOOPMODE_BITS);
+ data[1] |= RS232_MODE;
+
+ result = ssu100_setdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - setdevice failed %i", __func__, result);
+ goto out;
+ }
+
+out: kfree(data);
+ return result;
+
+}
+
+
+static void ssu100_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios)
+{
+ struct usb_device *dev = port->serial->dev;
+ struct ktermios *termios = tty->termios;
+ u16 baud, divisor, remainder;
+ unsigned int cflag = termios->c_cflag;
+ u16 urb_value = 0; /* will hold the new flags */
+ int result;
+
+ dbg("%s", __func__);
+
+ if (cflag & PARENB) {
+ if (cflag & PARODD)
+ urb_value |= SERIAL_ODD_PARITY;
+ else
+ urb_value |= SERIAL_EVEN_PARITY;
+ }
+
+ switch (cflag & CSIZE) {
+ case CS5:
+ urb_value |= SERIAL_5_DATA;
+ break;
+ case CS6:
+ urb_value |= SERIAL_6_DATA;
+ break;
+ case CS7:
+ urb_value |= SERIAL_7_DATA;
+ break;
+ default:
+ case CS8:
+ urb_value |= SERIAL_8_DATA;
+ break;
+ }
+
+ baud = tty_get_baud_rate(tty);
+ if (!baud)
+ baud = 9600;
+
+ dbg("%s - got baud = %d\n", __func__, baud);
+
+
+ divisor = MAX_BAUD_RATE / baud;
+ remainder = MAX_BAUD_RATE % baud;
+ if (((remainder * 2) >= baud) && (baud != 110))
+ divisor++;
+
+ urb_value = urb_value << 8;
+
+ result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value);
+ if (result < 0)
+ dbg("%s - set uart failed", __func__);
+
+ if (cflag & CRTSCTS)
+ result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+ SERIAL_CRTSCTS, 0);
+ else
+ result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+ 0, 0);
+ if (result < 0)
+ dbg("%s - set HW flow control failed", __func__);
+
+ if (I_IXOFF(tty) || I_IXON(tty)) {
+ u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty)));
+
+ result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+ x, 0);
+ } else
+ result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+ 0, 0);
+
+ if (result < 0)
+ dbg("%s - set SW flow control failed", __func__);
+
+}
+
+
+static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ struct usb_device *dev = port->serial->dev;
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+ u8 *data;
+ int result;
+
+ dbg("%s - port %d", __func__, port->number);
+
+ data = kzalloc(2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_OPEN_CLOSE_CHANNEL,
+ QT_TRANSFER_IN, 0x01,
+ 0, data, 2, 300);
+ if (result < 0) {
+ dbg("%s - open failed %i", __func__, result);
+ kfree(data);
+ return result;
+ }
+
+ priv->shadowLSR = data[0] & (SERIAL_LSR_OE | SERIAL_LSR_PE |
+ SERIAL_LSR_FE | SERIAL_LSR_BI);
+
+ priv->shadowMSR = data[1] & (SERIAL_MSR_CTS | SERIAL_MSR_DSR |
+ SERIAL_MSR_RI | SERIAL_MSR_CD);
+
+ kfree(data);
+
+/* set to 9600 */
+ result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300);
+ if (result < 0)
+ dbg("%s - set uart failed", __func__);
+
+ if (tty)
+ ssu100_set_termios(tty, port, tty->termios);
+
+ return usb_serial_generic_open(tty, port);
+}
+
+static void ssu100_close(struct usb_serial_port *port)
+{
+ dbg("%s", __func__);
+ usb_serial_generic_close(port);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+ struct serial_struct __user *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.line = port->serial->minor;
+ tmp.port = 0;
+ tmp.irq = 0;
+ tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+ tmp.xmit_fifo_size = port->bulk_out_size;
+ tmp.baud_base = 9600;
+ tmp.close_delay = 5*HZ;
+ tmp.closing_wait = 30*HZ;
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int ssu100_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+
+ dbg("%s cmd 0x%04x", __func__, cmd);
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ return get_serial_info(port,
+ (struct serial_struct __user *) arg);
+
+ case TIOCMIWAIT:
+ while (priv != NULL) {
+ u8 prevMSR = priv->shadowMSR & SERIAL_MSR_MASK;
+ interruptible_sleep_on(&priv->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ else {
+ u8 diff = (priv->shadowMSR & SERIAL_MSR_MASK) ^ prevMSR;
+ if (!diff)
+ return -EIO; /* no change => error */
+
+ /* Return 0 if caller wanted to know about
+ these bits */
+
+ if (((arg & TIOCM_RNG) && (diff & SERIAL_MSR_RI)) ||
+ ((arg & TIOCM_DSR) && (diff & SERIAL_MSR_DSR)) ||
+ ((arg & TIOCM_CD) && (diff & SERIAL_MSR_CD)) ||
+ ((arg & TIOCM_CTS) && (diff & SERIAL_MSR_CTS)))
+ return 0;
+ }
+ }
+ return 0;
+
+ default:
+ break;
+ }
+
+ dbg("%s arg not supported", __func__);
+
+ return -ENOIOCTLCMD;
+}
+
+static void ssu100_set_max_packet_size(struct usb_serial_port *port)
+{
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct usb_device *udev = serial->dev;
+
+ struct usb_interface *interface = serial->interface;
+ struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc;
+
+ unsigned num_endpoints;
+ int i;
+
+ num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
+ dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
+
+ for (i = 0; i < num_endpoints; i++) {
+ dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
+ interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+ ep_desc = &interface->cur_altsetting->endpoint[i].desc;
+ }
+
+ /* set max packet size based on descriptor */
+ priv->max_packet_size = ep_desc->wMaxPacketSize;
+
+ dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
+}
+
+static int ssu100_attach(struct usb_serial *serial)
+{
+ struct ssu100_port_private *priv;
+ struct usb_serial_port *port = *serial->port;
+
+ dbg("%s", __func__);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__,
+ sizeof(*priv));
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&priv->delta_msr_wait);
+ usb_set_serial_port_data(port, priv);
+
+ ssu100_set_max_packet_size(port);
+
+ return ssu100_initdevice(serial->dev);
+}
+
+static int ssu100_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_device *dev = port->serial->dev;
+ u8 *d;
+ int r;
+
+ dbg("%s\n", __func__);
+
+ d = kzalloc(2, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ r = ssu100_getregister(dev, 0, MODEM_CTL_REGISTER, d);
+ if (r < 0)
+ goto mget_out;
+
+ r = ssu100_getregister(dev, 0, MODEM_STATUS_REGISTER, d+1);
+ if (r < 0)
+ goto mget_out;
+
+ r = (d[0] & SERIAL_MCR_DTR ? TIOCM_DTR : 0) |
+ (d[0] & SERIAL_MCR_RTS ? TIOCM_RTS : 0) |
+ (d[1] & SERIAL_MSR_CTS ? TIOCM_CTS : 0) |
+ (d[1] & SERIAL_MSR_CD ? TIOCM_CAR : 0) |
+ (d[1] & SERIAL_MSR_RI ? TIOCM_RI : 0) |
+ (d[1] & SERIAL_MSR_DSR ? TIOCM_DSR : 0);
+
+mget_out:
+ kfree(d);
+ return r;
+}
+
+static int ssu100_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_device *dev = port->serial->dev;
+
+ dbg("%s\n", __func__);
+ return update_mctrl(dev, set, clear);
+}
+
+static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct usb_device *dev = port->serial->dev;
+
+ dbg("%s\n", __func__);
+
+ mutex_lock(&port->serial->disc_mutex);
+ if (!port->serial->disconnected) {
+ /* Disable flow control */
+ if (!on &&
+ ssu100_setregister(dev, 0, 0) < 0)
+ dev_err(&port->dev, "error from flowcontrol urb\n");
+ /* drop RTS and DTR */
+ if (on)
+ set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ else
+ clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ }
+ mutex_unlock(&port->serial->disc_mutex);
+}
+
+static int ssu100_process_packet(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ssu100_port_private *priv,
+ char *packet, int len)
+{
+ int i;
+ char flag;
+ char *ch;
+
+ dbg("%s - port %d", __func__, port->number);
+
+ if (len < 4) {
+ dbg("%s - malformed packet", __func__);
+ return 0;
+ }
+
+ if ((packet[0] == 0x1b) && (packet[1] == 0x1b) &&
+ ((packet[2] == 0x00) || (packet[2] == 0x01))) {
+ if (packet[2] == 0x00)
+ priv->shadowLSR = packet[3] & (SERIAL_LSR_OE |
+ SERIAL_LSR_PE |
+ SERIAL_LSR_FE |
+ SERIAL_LSR_BI);
+
+ if (packet[2] == 0x01) {
+ priv->shadowMSR = packet[3];
+ wake_up_interruptible(&priv->delta_msr_wait);
+ }
+
+ len -= 4;
+ ch = packet + 4;
+ } else
+ ch = packet;
+
+ if (!len)
+ return 0; /* status only */
+
+ if (port->port.console && port->sysrq) {
+ for (i = 0; i < len; i++, ch++) {
+ if (!usb_serial_handle_sysrq_char(tty, port, *ch))
+ tty_insert_flip_char(tty, *ch, flag);
+ }
+ } else
+ tty_insert_flip_string_fixed_flag(tty, ch, flag, len);
+
+ return len;
+}
+
+static void ssu100_process_read_urb(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+ char *data = (char *)urb->transfer_buffer;
+ struct tty_struct *tty;
+ int count = 0;
+ int i;
+ int len;
+
+ dbg("%s", __func__);
+
+ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+ return;
+
+ for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
+ len = min_t(int, urb->actual_length - i, priv->max_packet_size);
+ count += ssu100_process_packet(tty, port, priv, &data[i], len);
+ }
+
+ if (count)
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+}
+
+
+static struct usb_serial_driver ssu100_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ssu100",
+ },
+ .description = DRIVER_DESC,
+ .id_table = id_table,
+ .usb_driver = &ssu100_driver,
+ .num_ports = 1,
+ .bulk_in_size = 256,
+ .bulk_out_size = 256,
+ .open = ssu100_open,
+ .close = ssu100_close,
+ .attach = ssu100_attach,
+ .release = ssu100_release,
+ .dtr_rts = ssu100_dtr_rts,
+ .process_read_urb = ssu100_process_read_urb,
+ .tiocmget = ssu100_tiocmget,
+ .tiocmset = ssu100_tiocmset,
+ .ioctl = ssu100_ioctl,
+ .set_termios = ssu100_set_termios,
+};
+
+static int __init ssu100_init(void)
+{
+ int retval;
+
+ dbg("%s", __func__);
+
+ /* register with usb-serial */
+ retval = usb_serial_register(&ssu100_device);
+
+ if (retval)
+ goto failed_usb_sio_register;
+
+ retval = usb_register(&ssu100_driver);
+ if (retval)
+ goto failed_usb_register;
+
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+
+ return 0;
+
+failed_usb_register:
+ usb_serial_deregister(&ssu100_device);
+failed_usb_sio_register:
+ return retval;
+}
+
+static void __exit ssu100_exit(void)
+{
+ usb_deregister(&ssu100_driver);
+ usb_serial_deregister(&ssu100_device);
+}
+
+module_init(ssu100_init);
+module_exit(ssu100_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 941c2d4..2a982e6 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -653,6 +653,7 @@ exit:
return id;
}
+/* Caller must hold table_lock */
static struct usb_serial_driver *search_serial_device(
struct usb_interface *iface)
{
@@ -718,17 +719,23 @@ int usb_serial_probe(struct usb_interface *interface,
int num_ports = 0;
int max_endpoints;
- lock_kernel(); /* guard against unloading a serial driver module */
+ mutex_lock(&table_lock);
type = search_serial_device(interface);
if (!type) {
- unlock_kernel();
+ mutex_unlock(&table_lock);
dbg("none matched");
return -ENODEV;
}
+ if (!try_module_get(type->driver.owner)) {
+ mutex_unlock(&table_lock);
+ dev_err(&interface->dev, "module get failed, exiting\n");
+ return -EIO;
+ }
+ mutex_unlock(&table_lock);
+
serial = create_serial(dev, interface, type);
if (!serial) {
- unlock_kernel();
dev_err(&interface->dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
@@ -737,20 +744,11 @@ int usb_serial_probe(struct usb_interface *interface,
if (type->probe) {
const struct usb_device_id *id;
- if (!try_module_get(type->driver.owner)) {
- unlock_kernel();
- dev_err(&interface->dev,
- "module get failed, exiting\n");
- kfree(serial);
- return -EIO;
- }
-
id = get_iface_id(type, interface);
retval = type->probe(serial, id);
module_put(type->driver.owner);
if (retval) {
- unlock_kernel();
dbg("sub driver rejected device");
kfree(serial);
return retval;
@@ -822,7 +820,6 @@ int usb_serial_probe(struct usb_interface *interface,
* properly during a later invocation of usb_serial_probe
*/
if (num_bulk_in == 0 || num_bulk_out == 0) {
- unlock_kernel();
dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
kfree(serial);
return -ENODEV;
@@ -835,7 +832,6 @@ int usb_serial_probe(struct usb_interface *interface,
if (type == &usb_serial_generic_device) {
num_ports = num_bulk_out;
if (num_ports == 0) {
- unlock_kernel();
dev_err(&interface->dev,
"Generic device with no bulk out, not allowed.\n");
kfree(serial);
@@ -847,7 +843,6 @@ int usb_serial_probe(struct usb_interface *interface,
/* if this device type has a calc_num_ports function, call it */
if (type->calc_num_ports) {
if (!try_module_get(type->driver.owner)) {
- unlock_kernel();
dev_err(&interface->dev,
"module get failed, exiting\n");
kfree(serial);
@@ -878,7 +873,6 @@ int usb_serial_probe(struct usb_interface *interface,
max_endpoints = max(max_endpoints, num_interrupt_out);
max_endpoints = max(max_endpoints, (int)serial->num_ports);
serial->num_port_pointers = max_endpoints;
- unlock_kernel();
dbg("%s - setting up %d port structures for this device",
__func__, max_endpoints);
@@ -1077,6 +1071,8 @@ int usb_serial_probe(struct usb_interface *interface,
dev_set_name(&port->dev, "ttyUSB%d", port->number);
dbg ("%s - registering %s", __func__, dev_name(&port->dev));
port->dev_state = PORT_REGISTERING;
+ device_enable_async_suspend(&port->dev);
+
retval = device_add(&port->dev);
if (retval) {
dev_err(&port->dev, "Error registering port device, "
@@ -1349,6 +1345,7 @@ int usb_serial_register(struct usb_serial_driver *driver)
driver->description = driver->driver.name;
/* Add this device to our list of devices */
+ mutex_lock(&table_lock);
list_add(&driver->driver_list, &usb_serial_driver_list);
retval = usb_serial_bus_register(driver);
@@ -1360,6 +1357,7 @@ int usb_serial_register(struct usb_serial_driver *driver)
printk(KERN_INFO "USB Serial support registered for %s\n",
driver->description);
+ mutex_unlock(&table_lock);
return retval;
}
EXPORT_SYMBOL_GPL(usb_serial_register);
@@ -1370,8 +1368,10 @@ void usb_serial_deregister(struct usb_serial_driver *device)
/* must be called with BKL held */
printk(KERN_INFO "USB Serial deregistering driver %s\n",
device->description);
+ mutex_lock(&table_lock);
list_del(&device->driver_list);
usb_serial_bus_deregister(device);
+ mutex_unlock(&table_lock);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister);
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index 54cc942..6542ca4 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -269,7 +269,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us)
/* The firmware will time-out commands after 20 seconds. Some commands
* can legitimately take longer than this, so we use a different
* command that only waits for the interrupt and then sends status,
- * without having to send a new ATAPI command to the device.
+ * without having to send a new ATAPI command to the device.
*
* NOTE: There is some indication that a data transfer after a timeout
* may not work, but that is a condition that should never happen.
@@ -324,14 +324,14 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us)
/* Find the length we desire to read. */
switch (srb->cmnd[0]) {
- case INQUIRY:
- case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
- case MODE_SENSE:
- case MODE_SENSE_10:
- length = le16_to_cpu(fst->Count);
- break;
- default:
- length = scsi_bufflen(srb);
+ case INQUIRY:
+ case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ length = le16_to_cpu(fst->Count);
+ break;
+ default:
+ length = scsi_bufflen(srb);
}
/* verify that this amount is legal */
@@ -414,7 +414,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us)
/* should never hit here -- filtered in usb.c */
US_DEBUGP ("freecom unimplemented direction: %d\n",
us->srb->sc_data_direction);
- // Return fail, SCSI seems to handle this better.
+ /* Return fail, SCSI seems to handle this better. */
return USB_STOR_TRANSPORT_FAILED;
break;
}
@@ -494,8 +494,7 @@ static void pdump (void *ibuffer, int length)
offset = 0;
}
offset += sprintf (line+offset, "%08x:", i);
- }
- else if ((i & 7) == 0) {
+ } else if ((i & 7) == 0) {
offset += sprintf (line+offset, " -");
}
offset += sprintf (line+offset, " %02x", buffer[i] & 0xff);
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index e9cbc14..6b9982c 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1456,8 +1456,7 @@ static int isd200_init_info(struct us_data *us)
int retStatus = ISD200_GOOD;
struct isd200_info *info;
- info = (struct isd200_info *)
- kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
if (!info)
retStatus = ISD200_ERROR;
else {
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index a7d0bf9..90bb017 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -336,6 +336,7 @@ static int usb_stor_control_thread(void * __us)
else {
US_DEBUG(usb_stor_show_command(us->srb));
us->proto_handler(us->srb, us);
+ usb_mark_last_busy(us->pusb_dev);
}
/* lock access to the state */
@@ -845,6 +846,7 @@ static int usb_stor_scan_thread(void * __us)
/* Should we unbind if no devices were detected? */
}
+ usb_autopm_put_interface(us->pusb_intf);
complete_and_exit(&us->scanning_done, 0);
}
@@ -968,6 +970,7 @@ int usb_stor_probe2(struct us_data *us)
goto BadDevice;
}
+ usb_autopm_get_interface_no_resume(us->pusb_intf);
wake_up_process(th);
return 0;
@@ -1040,6 +1043,7 @@ static struct usb_driver usb_storage_driver = {
.pre_reset = usb_stor_pre_reset,
.post_reset = usb_stor_post_reset,
.id_table = usb_storage_usb_ids,
+ .supports_autosuspend = 1,
.soft_unbind = 1,
};
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index d110588..552679b 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -142,7 +142,7 @@ static int skel_release(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
@@ -162,7 +162,7 @@ static int skel_flush(struct file *file, fl_owner_t id)
struct usb_skel *dev;
int res;
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
@@ -246,7 +246,7 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count,
int rv;
bool ongoing_io;
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
/* if we cannot read at all, return EOF */
if (!dev->bulk_in_urb || !count)
@@ -401,7 +401,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer,
char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER);
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a9ca72f..8b31fdf 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1896,6 +1896,13 @@ config FB_W100
If unsure, say N.
+config SH_MIPI_DSI
+ tristate
+ depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+
+config SH_LCD_MIPI_DSI
+ bool
+
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
@@ -1904,9 +1911,17 @@ config FB_SH_MOBILE_LCDC
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
+ select SH_MIPI_DSI if SH_LCD_MIPI_DSI
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.
+config FB_SH_MOBILE_HDMI
+ tristate "SuperH Mobile HDMI controller support"
+ depends on FB_SH_MOBILE_LCDC
+ select FB_MODE_HELPERS
+ ---help---
+ Driver for the on-chip SH-Mobile HDMI controller.
+
config FB_TMIO
tristate "Toshiba Mobile IO FrameBuffer support"
depends on FB && MFD_CORE
@@ -1931,7 +1946,7 @@ config FB_TMIO_ACCELL
config FB_S3C
tristate "Samsung S3C framebuffer support"
- depends on FB && ARCH_S3C64XX
+ depends on FB && S3C_DEV_FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f56a9ca..485e8ed 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -123,6 +123,8 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
+obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o
+obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-y += omap2/
diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c
index c779663..4dc1346 100644
--- a/drivers/video/bw2.c
+++ b/drivers/video/bw2.c
@@ -273,7 +273,7 @@ static int __devinit bw2_do_default_mode(struct bw2_par *par,
return 0;
}
-static int __devinit bw2_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit bw2_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
struct fb_info *info;
@@ -350,7 +350,7 @@ out_err:
return err;
}
-static int __devexit bw2_remove(struct of_device *op)
+static int __devexit bw2_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct bw2_par *par = info->par;
diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c
index d09fde8..2424953 100644
--- a/drivers/video/cg14.c
+++ b/drivers/video/cg14.c
@@ -446,7 +446,7 @@ static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __devinitdata = {
{ .size = 0 }
};
-static void cg14_unmap_regs(struct of_device *op, struct fb_info *info,
+static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info,
struct cg14_par *par)
{
if (par->regs)
@@ -463,7 +463,7 @@ static void cg14_unmap_regs(struct of_device *op, struct fb_info *info,
info->screen_base, info->fix.smem_len);
}
-static int __devinit cg14_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit cg14_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
struct fb_info *info;
@@ -570,7 +570,7 @@ out_err:
return err;
}
-static int __devexit cg14_remove(struct of_device *op)
+static int __devexit cg14_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct cg14_par *par = info->par;
diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c
index 64aa298..09c0c3c 100644
--- a/drivers/video/cg3.c
+++ b/drivers/video/cg3.c
@@ -346,7 +346,7 @@ static int __devinit cg3_do_default_mode(struct cg3_par *par)
return 0;
}
-static int __devinit cg3_probe(struct of_device *op,
+static int __devinit cg3_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -433,7 +433,7 @@ out_err:
return err;
}
-static int __devexit cg3_remove(struct of_device *op)
+static int __devexit cg3_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct cg3_par *par = info->par;
diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c
index 2389a71..2b5a970 100644
--- a/drivers/video/cg6.c
+++ b/drivers/video/cg6.c
@@ -718,7 +718,7 @@ static void __devinit cg6_chip_init(struct fb_info *info)
sbus_writel(info->var.yres - 1, &fbc->clipmaxy);
}
-static void cg6_unmap_regs(struct of_device *op, struct fb_info *info,
+static void cg6_unmap_regs(struct platform_device *op, struct fb_info *info,
struct cg6_par *par)
{
if (par->fbc)
@@ -737,7 +737,7 @@ static void cg6_unmap_regs(struct of_device *op, struct fb_info *info,
info->fix.smem_len);
}
-static int __devinit cg6_probe(struct of_device *op,
+static int __devinit cg6_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -827,7 +827,7 @@ out_err:
return err;
}
-static int __devexit cg6_remove(struct of_device *op)
+static int __devexit cg6_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct cg6_par *par = info->par;
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index af88651..28b1a83 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -22,7 +22,7 @@
/*
* Accelerated handlers.
*/
-static inline void update_attr(u8 *dst, u8 *src, int attribute,
+static void update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 3b3f574..84f8423 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -283,10 +283,11 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
struct fbcon_ops *ops = info->fbcon_par;
return (info->state != FBINFO_STATE_RUNNING ||
- vc->vc_mode != KD_TEXT || ops->graphics);
+ vc->vc_mode != KD_TEXT || ops->graphics) &&
+ !vt_force_oops_output(vc);
}
-static inline int get_color(struct vc_data *vc, struct fb_info *info,
+static int get_color(struct vc_data *vc, struct fb_info *info,
u16 c, int is_fg)
{
int depth = fb_get_color_depth(&info->var, &info->fix);
@@ -1073,6 +1074,7 @@ static void fbcon_init(struct vc_data *vc, int init)
if (p->userfont)
charcnt = FNTCHARCNT(p->fontdata);
+ vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT);
vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
if (charcnt == 256) {
diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c
index d135671..41b32ae 100644
--- a/drivers/video/console/fbcon_ccw.c
+++ b/drivers/video/console/fbcon_ccw.c
@@ -22,7 +22,7 @@
* Rotation 270 degrees
*/
-static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute,
+static void ccw_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c
index 126110f..6a73782 100644
--- a/drivers/video/console/fbcon_cw.c
+++ b/drivers/video/console/fbcon_cw.c
@@ -22,7 +22,7 @@
* Rotation 90 degrees
*/
-static inline void cw_update_attr(u8 *dst, u8 *src, int attribute,
+static void cw_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c
index 93a3e73..ff0872c 100644
--- a/drivers/video/console/fbcon_ud.c
+++ b/drivers/video/console/fbcon_ud.c
@@ -22,7 +22,7 @@
* Rotation 180 degrees
*/
-static inline void ud_update_attr(u8 *dst, u8 *src, int attribute,
+static void ud_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 182dd6f..54e32c5 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -1108,7 +1108,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
charmap += 4 * cmapsz;
#endif
- unlock_kernel();
spin_lock_irq(&vga_lock);
/* First, the Sequencer */
vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
@@ -1192,7 +1191,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
}
spin_unlock_irq(&vga_lock);
- lock_kernel();
return 0;
}
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index 4a56f46af..815f84b 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -16,7 +16,7 @@
#include <video/vga.h>
-static struct fb_var_screeninfo efifb_defined __initdata = {
+static struct fb_var_screeninfo efifb_defined __devinitdata = {
.activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
@@ -27,7 +27,7 @@ static struct fb_var_screeninfo efifb_defined __initdata = {
.vmode = FB_VMODE_NONINTERLACED,
};
-static struct fb_fix_screeninfo efifb_fix __initdata = {
+static struct fb_fix_screeninfo efifb_fix __devinitdata = {
.id = "EFI VGA",
.type = FB_TYPE_PACKED_PIXELS,
.accel = FB_ACCEL_NONE,
@@ -59,7 +59,7 @@ static struct efifb_dmi_info {
int stride;
int width;
int height;
-} dmi_list[] = {
+} dmi_list[] __initdata = {
[M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 },
[M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */
[M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 },
@@ -83,7 +83,7 @@ static int set_system(const struct dmi_system_id *id);
DMI_MATCH(DMI_PRODUCT_NAME, name) }, \
&dmi_list[enumid] }
-static struct dmi_system_id __initdata dmi_system_table[] = {
+static const struct dmi_system_id dmi_system_table[] __initconst = {
EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17),
/* At least one of these two will be right; maybe both? */
EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20),
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 731fce6..b066475 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1362,6 +1362,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
@@ -1786,7 +1787,7 @@ static int ofonly __read_mostly;
int fb_get_options(char *name, char **option)
{
char *opt, *options = NULL;
- int opt_len, retval = 0;
+ int retval = 0;
int name_len = strlen(name), i;
if (name_len && ofonly && strncmp(name, "offb", 4))
@@ -1796,8 +1797,7 @@ int fb_get_options(char *name, char **option)
for (i = 0; i < FB_MAX; i++) {
if (video_options[i] == NULL)
continue;
- opt_len = strlen(video_options[i]);
- if (!opt_len)
+ if (!video_options[i][0])
continue;
opt = video_options[i];
if (!strncmp(name, opt, name_len) &&
diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c
index f6ecfab..6739b2a 100644
--- a/drivers/video/ffb.c
+++ b/drivers/video/ffb.c
@@ -893,7 +893,7 @@ static void ffb_init_fix(struct fb_info *info)
info->fix.accel = FB_ACCEL_SUN_CREATOR;
}
-static int __devinit ffb_probe(struct of_device *op,
+static int __devinit ffb_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -1023,7 +1023,7 @@ out_err:
return err;
}
-static int __devexit ffb_remove(struct of_device *op)
+static int __devexit ffb_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct ffb_par *par = info->par;
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index e38ad22..8bbbf08 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -1393,7 +1393,7 @@ static void free_irq_local(int irq)
* Power management hooks. Note that we won't be called from IRQ context,
* unlike the blank functions above, so we may sleep.
*/
-static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
+static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct fsl_diu_data *machine_data;
@@ -1403,7 +1403,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int fsl_diu_resume(struct of_device *ofdev)
+static int fsl_diu_resume(struct platform_device *ofdev)
{
struct fsl_diu_data *machine_data;
@@ -1487,7 +1487,7 @@ static ssize_t show_monitor(struct device *device,
return diu_ops.show_monitor_port(machine_data->monitor_port, buf);
}
-static int __devinit fsl_diu_probe(struct of_device *ofdev,
+static int __devinit fsl_diu_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -1667,7 +1667,7 @@ error2:
}
-static int fsl_diu_remove(struct of_device *ofdev)
+static int fsl_diu_remove(struct platform_device *ofdev)
{
struct fsl_diu_data *machine_data;
int i;
diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c
index 15d2001..d885c77 100644
--- a/drivers/video/igafb.c
+++ b/drivers/video/igafb.c
@@ -368,7 +368,7 @@ static int __init iga_init(struct fb_info *info, struct iga_par *par)
return 1;
}
-int __init igafb_init(void)
+static int __init igafb_init(void)
{
struct fb_info *info;
struct pci_dev *pdev;
@@ -531,6 +531,7 @@ int __init igafb_init(void)
iounmap(info->screen_base);
kfree(par->mmap_map);
kfree(info);
+ return -ENODEV;
}
#ifdef CONFIG_SPARC
@@ -556,7 +557,7 @@ int __init igafb_init(void)
return 0;
}
-int __init igafb_setup(char *options)
+static int __init igafb_setup(char *options)
{
char *this_opt;
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 43f0639..5c363d0 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -40,6 +40,12 @@
*/
#define DEBUG_VAR 1
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+ (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \
+ defined(CONFIG_FB_IMX_MODULE))
+#define PWMR_BACKLIGHT_AVAILABLE
+#endif
+
#define DRIVER_NAME "imx-fb"
#define LCDC_SSA 0x00
@@ -175,7 +181,9 @@ struct imxfb_info {
struct imx_fb_videomode *mode;
int num_modes;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
struct backlight_device *bl;
+#endif
void (*lcd_power)(int);
void (*backlight_power)(int);
@@ -450,8 +458,7 @@ static int imxfb_set_par(struct fb_info *info)
return 0;
}
-
-
+#ifdef PWMR_BACKLIGHT_AVAILABLE
static int imxfb_bl_get_brightness(struct backlight_device *bl)
{
struct imxfb_info *fbi = bl_get_data(bl);
@@ -516,6 +523,7 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi)
if (fbi->bl)
backlight_device_unregister(fbi->bl);
}
+#endif
static void imxfb_enable_controller(struct imxfb_info *fbi)
{
@@ -647,6 +655,9 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
fbi->regs + LCDC_SIZE);
writel(fbi->pcr, fbi->regs + LCDC_PCR);
+#ifndef PWMR_BACKLIGHT_AVAILABLE
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+#endif
writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
@@ -847,7 +858,9 @@ static int __init imxfb_probe(struct platform_device *pdev)
imxfb_enable_controller(fbi);
fbi->pdev = pdev;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
imxfb_init_backlight(fbi);
+#endif
return 0;
@@ -885,7 +898,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev)
imxfb_disable_controller(fbi);
+#ifdef PWMR_BACKLIGHT_AVAILABLE
imxfb_exit_backlight(fbi);
+#endif
unregister_framebuffer(info);
pdata = pdev->dev.platform_data;
diff --git a/drivers/video/leo.c b/drivers/video/leo.c
index ad67763..b599e5e 100644
--- a/drivers/video/leo.c
+++ b/drivers/video/leo.c
@@ -529,7 +529,7 @@ static void leo_fixup_var_rgb(struct fb_var_screeninfo *var)
var->transp.length = 0;
}
-static void leo_unmap_regs(struct of_device *op, struct fb_info *info,
+static void leo_unmap_regs(struct platform_device *op, struct fb_info *info,
struct leo_par *par)
{
if (par->lc_ss0_usr)
@@ -547,7 +547,7 @@ static void leo_unmap_regs(struct of_device *op, struct fb_info *info,
of_iounmap(&op->resource[0], info->screen_base, 0x800000);
}
-static int __devinit leo_probe(struct of_device *op,
+static int __devinit leo_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -637,7 +637,7 @@ out_err:
return err;
}
-static int __devexit leo_remove(struct of_device *op)
+static int __devexit leo_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct leo_par *par = info->par;
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c
index 403b144..0fb280e 100644
--- a/drivers/video/matrox/i2c-matroxfb.c
+++ b/drivers/video/matrox/i2c-matroxfb.c
@@ -191,7 +191,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) {
};
i2c_new_probed_device(&m2info->maven.adapter,
- &maven_info, addr_list);
+ &maven_info, addr_list, NULL);
}
}
return m2info;
diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c
index 4e2b8cc..b1c4374 100644
--- a/drivers/video/mb862xx/mb862xxfb.c
+++ b/drivers/video/mb862xx/mb862xxfb.c
@@ -550,7 +550,7 @@ static int mb862xx_gdc_init(struct mb862xxfb_par *par)
return 0;
}
-static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev,
+static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct device_node *np = ofdev->dev.of_node;
@@ -669,7 +669,7 @@ fbrel:
return ret;
}
-static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev)
+static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev)
{
struct fb_info *fbi = dev_get_drvdata(&ofdev->dev);
struct mb862xxfb_par *par = fbi->par;
diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c
index c1ff271..7c316c3 100644
--- a/drivers/video/msm/mddi.c
+++ b/drivers/video/msm/mddi.c
@@ -187,10 +187,8 @@ static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask);
static void mddi_handle_rev_data_avail(struct mddi_info *mddi)
{
- union mddi_rev *rev = mddi->rev_data;
uint32_t rev_data_count;
uint32_t rev_crc_err_count;
- int i;
struct reg_read_info *ri;
size_t prev_offset;
uint16_t length;
@@ -670,7 +668,7 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi)
return 0;
}
-static int __init mddi_probe(struct platform_device *pdev)
+static int __devinit mddi_probe(struct platform_device *pdev)
{
struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
struct mddi_info *mddi = &mddi_info[pdev->id];
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 19c01c6..3c28db0 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -258,7 +258,6 @@ int get_img(struct mdp_img *img, struct fb_info *info,
{
int put_needed, ret = 0;
struct file *file;
- unsigned long vstart;
file = fget_light(img->memory_id, &put_needed);
if (file == NULL)
diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c
index 688b055..b6c3fc2 100644
--- a/drivers/video/p9100.c
+++ b/drivers/video/p9100.c
@@ -249,7 +249,7 @@ static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_no
info->fix.accel = FB_ACCEL_SUN_CGTHREE;
}
-static int __devinit p9100_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit p9100_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
struct fb_info *info;
@@ -326,7 +326,7 @@ out_err:
return err;
}
-static int __devexit p9100_remove(struct of_device *op)
+static int __devexit p9100_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct p9100_par *par = info->par;
diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c
index 72a1f4c..a50e197 100644
--- a/drivers/video/platinumfb.c
+++ b/drivers/video/platinumfb.c
@@ -533,7 +533,7 @@ static int __init platinumfb_setup(char *options)
#define invalidate_cache(addr)
#endif
-static int __devinit platinumfb_probe(struct of_device* odev,
+static int __devinit platinumfb_probe(struct platform_device* odev,
const struct of_device_id *match)
{
struct device_node *dp = odev->dev.of_node;
@@ -646,7 +646,7 @@ static int __devinit platinumfb_probe(struct of_device* odev,
return rc;
}
-static int __devexit platinumfb_remove(struct of_device* odev)
+static int __devexit platinumfb_remove(struct platform_device* odev)
{
struct fb_info *info = dev_get_drvdata(&odev->dev);
struct fb_info_platinum *pinfo = info->par;
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 9682ecc..f9aca9d 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -1,7 +1,7 @@
/* linux/drivers/video/s3c-fb.c
*
* Copyright 2008 Openmoko Inc.
- * Copyright 2008 Simtec Electronics
+ * Copyright 2008-2010 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
@@ -9,7 +9,7 @@
*
* 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.
+ * published by the Free Software FoundatIon.
*/
#include <linux/kernel.h>
@@ -21,9 +21,11 @@
#include <linux/clk.h>
#include <linux/fb.h>
#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
#include <mach/map.h>
-#include <mach/regs-fb.h>
+#include <plat/regs-fb-v4.h>
#include <plat/fb.h>
/* This driver will export a number of framebuffer interfaces depending
@@ -36,9 +38,9 @@
* output timings and as the control for the output power-down state.
*/
-/* note, some of the functions that get called are derived from including
- * <mach/regs-fb.h> as they are specific to the architecture that the code
- * is being built for.
+/* note, the previous use of <mach/regs-fb.h> to get platform specific data
+ * has been replaced by using the platform device name to pick the correct
+ * configuration data for the system.
*/
#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
@@ -48,13 +50,108 @@
__raw_writel(v, r); } while(0)
#endif /* FB_S3C_DEBUG_REGWRITE */
+/* irq_flags bits */
+#define S3C_FB_VSYNC_IRQ_EN 0
+
+#define VSYNC_TIMEOUT_MSEC 50
+
struct s3c_fb;
+#define VALID_BPP(x) (1 << ((x) - 1))
+
+#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))
+#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)
+#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)
+#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)
+#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)
+
+/**
+ * struct s3c_fb_variant - fb variant information
+ * @is_2443: Set if S3C2443/S3C2416 style hardware.
+ * @nr_windows: The number of windows.
+ * @vidtcon: The base for the VIDTCONx registers
+ * @wincon: The base for the WINxCON registers.
+ * @winmap: The base for the WINxMAP registers.
+ * @keycon: The abse for the WxKEYCON registers.
+ * @buf_start: Offset of buffer start registers.
+ * @buf_size: Offset of buffer size registers.
+ * @buf_end: Offset of buffer end registers.
+ * @osd: The base for the OSD registers.
+ * @palette: Address of palette memory, or 0 if none.
+ * @has_prtcon: Set if has PRTCON register.
+ * @has_shadowcon: Set if has SHADOWCON register.
+ */
+struct s3c_fb_variant {
+ unsigned int is_2443:1;
+ unsigned short nr_windows;
+ unsigned short vidtcon;
+ unsigned short wincon;
+ unsigned short winmap;
+ unsigned short keycon;
+ unsigned short buf_start;
+ unsigned short buf_end;
+ unsigned short buf_size;
+ unsigned short osd;
+ unsigned short osd_stride;
+ unsigned short palette[S3C_FB_MAX_WIN];
+
+ unsigned int has_prtcon:1;
+ unsigned int has_shadowcon:1;
+};
+
+/**
+ * struct s3c_fb_win_variant
+ * @has_osd_c: Set if has OSD C register.
+ * @has_osd_d: Set if has OSD D register.
+ * @has_osd_alpha: Set if can change alpha transparency for a window.
+ * @palette_sz: Size of palette in entries.
+ * @palette_16bpp: Set if palette is 16bits wide.
+ * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate
+ * register is located at the given offset from OSD_BASE.
+ * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.
+ *
+ * valid_bpp bit x is set if (x+1)BPP is supported.
+ */
+struct s3c_fb_win_variant {
+ unsigned int has_osd_c:1;
+ unsigned int has_osd_d:1;
+ unsigned int has_osd_alpha:1;
+ unsigned int palette_16bpp:1;
+ unsigned short osd_size_off;
+ unsigned short palette_sz;
+ u32 valid_bpp;
+};
+
+/**
+ * struct s3c_fb_driverdata - per-device type driver data for init time.
+ * @variant: The variant information for this driver.
+ * @win: The window information for each window.
+ */
+struct s3c_fb_driverdata {
+ struct s3c_fb_variant variant;
+ struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];
+};
+
+/**
+ * struct s3c_fb_palette - palette information
+ * @r: Red bitfield.
+ * @g: Green bitfield.
+ * @b: Blue bitfield.
+ * @a: Alpha bitfield.
+ */
+struct s3c_fb_palette {
+ struct fb_bitfield r;
+ struct fb_bitfield g;
+ struct fb_bitfield b;
+ struct fb_bitfield a;
+};
+
/**
* struct s3c_fb_win - per window private data for each framebuffer.
* @windata: The platform data supplied for the window configuration.
* @parent: The hardware that this window is part of.
* @fbinfo: Pointer pack to the framebuffer info for this window.
+ * @varint: The variant information for this window.
* @palette_buffer: Buffer/cache to hold palette entries.
* @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
* @index: The window number of this window.
@@ -65,6 +162,7 @@ struct s3c_fb_win {
struct s3c_fb *parent;
struct fb_info *fbinfo;
struct s3c_fb_palette palette;
+ struct s3c_fb_win_variant variant;
u32 *palette_buffer;
u32 pseudo_palette[16];
@@ -72,37 +170,54 @@ struct s3c_fb_win {
};
/**
+ * struct s3c_fb_vsync - vsync information
+ * @wait: a queue for processes waiting for vsync
+ * @count: vsync interrupt count
+ */
+struct s3c_fb_vsync {
+ wait_queue_head_t wait;
+ unsigned int count;
+};
+
+/**
* struct s3c_fb - overall hardware state of the hardware
* @dev: The device that we bound to, for printing, etc.
* @regs_res: The resource we claimed for the IO registers.
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
* @regs: The mapped hardware registers.
+ * @variant: Variant information for this hardware.
* @enabled: A bitmask of enabled hardware windows.
* @pdata: The platform configuration data passed with the device.
* @windows: The hardware windows that have been claimed.
+ * @irq_no: IRQ line number
+ * @irq_flags: irq flags
+ * @vsync_info: VSYNC-related information (count, queues...)
*/
struct s3c_fb {
struct device *dev;
struct resource *regs_res;
struct clk *bus_clk;
void __iomem *regs;
+ struct s3c_fb_variant variant;
unsigned char enabled;
struct s3c_fb_platdata *pdata;
struct s3c_fb_win *windows[S3C_FB_MAX_WIN];
+
+ int irq_no;
+ unsigned long irq_flags;
+ struct s3c_fb_vsync vsync_info;
};
/**
- * s3c_fb_win_has_palette() - determine if a mode has a palette
- * @win: The window number being queried.
- * @bpp: The number of bits per pixel to test.
- *
- * Work out if the given window supports palletised data at the specified bpp.
+ * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.
+ * @win: The device window.
+ * @bpp: The bit depth.
*/
-static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp)
+static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp)
{
- return s3c_fb_win_pal_size(win) <= (1 << bpp);
+ return win->variant.valid_bpp & VALID_BPP(bpp);
}
/**
@@ -125,7 +240,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres);
var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres);
- if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) {
+ if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {
dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
win->index, var->bits_per_pixel);
return -EINVAL;
@@ -140,7 +255,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
case 2:
case 4:
case 8:
- if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) {
+ if (sfb->variant.palette[win->index] != 0) {
/* non palletised, A:1,R:2,G:3,B:2 mode */
var->red.offset = 4;
var->green.offset = 2;
@@ -255,6 +370,66 @@ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
}
/**
+ * vidosd_set_size() - set OSD size for a window
+ *
+ * @win: the window to set OSD size for
+ * @size: OSD size register value
+ */
+static void vidosd_set_size(struct s3c_fb_win *win, u32 size)
+{
+ struct s3c_fb *sfb = win->parent;
+
+ /* OSD can be set up if osd_size_off != 0 for this window */
+ if (win->variant.osd_size_off)
+ writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)
+ + win->variant.osd_size_off);
+}
+
+/**
+ * vidosd_set_alpha() - set alpha transparency for a window
+ *
+ * @win: the window to set OSD size for
+ * @alpha: alpha register value
+ */
+static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha)
+{
+ struct s3c_fb *sfb = win->parent;
+
+ if (win->variant.has_osd_alpha)
+ writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant));
+}
+
+/**
+ * shadow_protect_win() - disable updating values from shadow registers at vsync
+ *
+ * @win: window to protect registers for
+ * @protect: 1 to protect (disable updates)
+ */
+static void shadow_protect_win(struct s3c_fb_win *win, bool protect)
+{
+ struct s3c_fb *sfb = win->parent;
+ u32 reg;
+
+ if (protect) {
+ if (sfb->variant.has_prtcon) {
+ writel(PRTCON_PROTECT, sfb->regs + PRTCON);
+ } else if (sfb->variant.has_shadowcon) {
+ reg = readl(sfb->regs + SHADOWCON);
+ writel(reg | SHADOWCON_WINx_PROTECT(win->index),
+ sfb->regs + SHADOWCON);
+ }
+ } else {
+ if (sfb->variant.has_prtcon) {
+ writel(0, sfb->regs + PRTCON);
+ } else if (sfb->variant.has_shadowcon) {
+ reg = readl(sfb->regs + SHADOWCON);
+ writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),
+ sfb->regs + SHADOWCON);
+ }
+ }
+}
+
+/**
* s3c_fb_set_par() - framebuffer request to set new framebuffer state.
* @info: The framebuffer to change.
*
@@ -266,14 +441,17 @@ static int s3c_fb_set_par(struct fb_info *info)
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
void __iomem *regs = sfb->regs;
+ void __iomem *buf = regs;
int win_no = win->index;
- u32 osdc_data = 0;
+ u32 alpha = 0;
u32 data;
u32 pagewidth;
int clkdiv;
dev_dbg(sfb->dev, "setting framebuffer parameters\n");
+ shadow_protect_win(win, 1);
+
switch (var->bits_per_pixel) {
case 32:
case 24:
@@ -282,7 +460,7 @@ static int s3c_fb_set_par(struct fb_info *info)
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 8:
- if (s3c_fb_win_has_palette(win_no, 8))
+ if (win->variant.palette_sz >= 256)
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
else
info->fix.visual = FB_VISUAL_TRUECOLOR;
@@ -297,12 +475,15 @@ static int s3c_fb_set_par(struct fb_info *info)
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
+ info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
+ info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
+
/* disable the window whilst we update it */
writel(0, regs + WINCON(win_no));
- /* use window 0 as the basis for the lcd output timings */
+ /* use platform specified window as the basis for the lcd timings */
- if (win_no == 0) {
+ if (win_no == sfb->pdata->default_win) {
clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
data = sfb->pdata->vidcon0;
@@ -315,6 +496,9 @@ static int s3c_fb_set_par(struct fb_info *info)
/* write the timing data to the panel */
+ if (sfb->variant.is_2443)
+ data |= (1 << 5);
+
data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
writel(data, regs + VIDCON0);
@@ -322,53 +506,54 @@ static int s3c_fb_set_par(struct fb_info *info)
VIDTCON0_VFPD(var->lower_margin - 1) |
VIDTCON0_VSPW(var->vsync_len - 1);
- writel(data, regs + VIDTCON0);
+ writel(data, regs + sfb->variant.vidtcon);
data = VIDTCON1_HBPD(var->left_margin - 1) |
VIDTCON1_HFPD(var->right_margin - 1) |
VIDTCON1_HSPW(var->hsync_len - 1);
- writel(data, regs + VIDTCON1);
+ /* VIDTCON1 */
+ writel(data, regs + sfb->variant.vidtcon + 4);
data = VIDTCON2_LINEVAL(var->yres - 1) |
VIDTCON2_HOZVAL(var->xres - 1);
- writel(data, regs + VIDTCON2);
+ writel(data, regs +sfb->variant.vidtcon + 8 );
}
/* write the buffer address */
- writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no));
+ /* start and end registers stride is 8 */
+ buf = regs + win_no * 8;
+
+ writel(info->fix.smem_start, buf + sfb->variant.buf_start);
data = info->fix.smem_start + info->fix.line_length * var->yres;
- writel(data, regs + VIDW_BUF_END(win_no));
+ writel(data, buf + sfb->variant.buf_end);
pagewidth = (var->xres * var->bits_per_pixel) >> 3;
data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
- writel(data, regs + VIDW_BUF_SIZE(win_no));
+ writel(data, regs + sfb->variant.buf_size + (win_no * 4));
/* write 'OSD' registers to control position of framebuffer */
data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
- writel(data, regs + VIDOSD_A(win_no));
+ writel(data, regs + VIDOSD_A(win_no, sfb->variant));
data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
var->xres - 1)) |
VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
- writel(data, regs + VIDOSD_B(win_no));
+ writel(data, regs + VIDOSD_B(win_no, sfb->variant));
data = var->xres * var->yres;
- osdc_data = VIDISD14C_ALPHA1_R(0xf) |
+ alpha = VIDISD14C_ALPHA1_R(0xf) |
VIDISD14C_ALPHA1_G(0xf) |
VIDISD14C_ALPHA1_B(0xf);
- if (s3c_fb_has_osd_d(win_no)) {
- writel(data, regs + VIDOSD_D(win_no));
- writel(osdc_data, regs + VIDOSD_C(win_no));
- } else
- writel(data, regs + VIDOSD_C(win_no));
+ vidosd_set_alpha(win, alpha);
+ vidosd_set_size(win, data);
data = WINCONx_ENWIN;
@@ -424,13 +609,15 @@ static int s3c_fb_set_par(struct fb_info *info)
else
data |= WINCON0_BPPMODE_24BPP_888;
+ data |= WINCONx_WSWP;
data |= WINCONx_BURSTLEN_16WORD;
break;
}
- /* It has no color key control register for window0 */
+ /* Enable the colour keying for the window below this one */
if (win_no > 0) {
u32 keycon0_data = 0, keycon1_data = 0;
+ void __iomem *keycon = regs + sfb->variant.keycon;
keycon0_data = ~(WxKEYCON0_KEYBL_EN |
WxKEYCON0_KEYEN_F |
@@ -438,12 +625,23 @@ static int s3c_fb_set_par(struct fb_info *info)
keycon1_data = WxKEYCON1_COLVAL(0xffffff);
- writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0));
- writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1));
+ keycon += (win_no - 1) * 8;
+
+ writel(keycon0_data, keycon + WKEYCON0);
+ writel(keycon1_data, keycon + WKEYCON1);
+ }
+
+ writel(data, regs + sfb->variant.wincon + (win_no * 4));
+ writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
+
+ /* Enable DMA channel for this window */
+ if (sfb->variant.has_shadowcon) {
+ data = readl(sfb->regs + SHADOWCON);
+ data |= SHADOWCON_CHx_ENABLE(win_no);
+ writel(data, sfb->regs + SHADOWCON);
}
- writel(data, regs + WINCON(win_no));
- writel(0x0, regs + WINxMAP(win_no));
+ shadow_protect_win(win, 0);
return 0;
}
@@ -470,7 +668,7 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb,
void __iomem *palreg;
u32 palcon;
- palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg);
+ palreg = sfb->regs + sfb->variant.palette[win->index];
dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
__func__, win->index, reg, palreg, value);
@@ -480,10 +678,10 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb,
palcon = readl(sfb->regs + WPALCON);
writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
- if (s3c_fb_pal_is16(win->index))
- writew(value, palreg);
+ if (win->variant.palette_16bpp)
+ writew(value, palreg + (reg * 2));
else
- writel(value, palreg);
+ writel(value, palreg + (reg * 4));
writel(palcon, sfb->regs + WPALCON);
}
@@ -532,7 +730,7 @@ static int s3c_fb_setcolreg(unsigned regno,
break;
case FB_VISUAL_PSEUDOCOLOR:
- if (regno < s3c_fb_win_pal_size(win->index)) {
+ if (regno < win->variant.palette_sz) {
val = chan_to_field(red, &win->palette.r);
val |= chan_to_field(green, &win->palette.g);
val |= chan_to_field(blue, &win->palette.b);
@@ -591,7 +789,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
- wincon = readl(sfb->regs + WINCON(index));
+ wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
switch (blank_mode) {
case FB_BLANK_POWERDOWN:
@@ -602,11 +800,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
case FB_BLANK_NORMAL:
/* disable the DMA and display 0x0 (black) */
writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
- sfb->regs + WINxMAP(index));
+ sfb->regs + sfb->variant.winmap + (index * 4));
break;
case FB_BLANK_UNBLANK:
- writel(0x0, sfb->regs + WINxMAP(index));
+ writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
wincon |= WINCONx_ENWIN;
sfb->enabled |= (1 << index);
break;
@@ -617,7 +815,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
return 1;
}
- writel(wincon, sfb->regs + WINCON(index));
+ writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
/* Check the enabled state to see if we need to be running the
* main LCD interface, as if there are no active windows then
@@ -636,12 +834,185 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
/* we're stuck with this until we can do something about overriding
* the power control using the blanking event for a single fb.
*/
- if (index == 0)
+ if (index == sfb->pdata->default_win)
s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
return 0;
}
+/**
+ * s3c_fb_pan_display() - Pan the display.
+ *
+ * Note that the offsets can be written to the device at any time, as their
+ * values are latched at each vsync automatically. This also means that only
+ * the last call to this function will have any effect on next vsync, but
+ * there is no need to sleep waiting for it to prevent tearing.
+ *
+ * @var: The screen information to verify.
+ * @info: The framebuffer device.
+ */
+static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct s3c_fb_win *win = info->par;
+ struct s3c_fb *sfb = win->parent;
+ void __iomem *buf = sfb->regs + win->index * 8;
+ unsigned int start_boff, end_boff;
+
+ /* Offset in bytes to the start of the displayed area */
+ start_boff = var->yoffset * info->fix.line_length;
+ /* X offset depends on the current bpp */
+ if (info->var.bits_per_pixel >= 8) {
+ start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
+ } else {
+ switch (info->var.bits_per_pixel) {
+ case 4:
+ start_boff += var->xoffset >> 1;
+ break;
+ case 2:
+ start_boff += var->xoffset >> 2;
+ break;
+ case 1:
+ start_boff += var->xoffset >> 3;
+ break;
+ default:
+ dev_err(sfb->dev, "invalid bpp\n");
+ return -EINVAL;
+ }
+ }
+ /* Offset in bytes to the end of the displayed area */
+ end_boff = start_boff + var->yres * info->fix.line_length;
+
+ /* Temporarily turn off per-vsync update from shadow registers until
+ * both start and end addresses are updated to prevent corruption */
+ shadow_protect_win(win, 1);
+
+ writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);
+ writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);
+
+ shadow_protect_win(win, 0);
+
+ return 0;
+}
+
+/**
+ * s3c_fb_enable_irq() - enable framebuffer interrupts
+ * @sfb: main hardware state
+ */
+static void s3c_fb_enable_irq(struct s3c_fb *sfb)
+{
+ void __iomem *regs = sfb->regs;
+ u32 irq_ctrl_reg;
+
+ if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
+ /* IRQ disabled, enable it */
+ irq_ctrl_reg = readl(regs + VIDINTCON0);
+
+ irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
+ irq_ctrl_reg |= VIDINTCON0_INT_FRAME;
+
+ irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
+ irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
+ irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
+ irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;
+
+ writel(irq_ctrl_reg, regs + VIDINTCON0);
+ }
+}
+
+/**
+ * s3c_fb_disable_irq() - disable framebuffer interrupts
+ * @sfb: main hardware state
+ */
+static void s3c_fb_disable_irq(struct s3c_fb *sfb)
+{
+ void __iomem *regs = sfb->regs;
+ u32 irq_ctrl_reg;
+
+ if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
+ /* IRQ enabled, disable it */
+ irq_ctrl_reg = readl(regs + VIDINTCON0);
+
+ irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
+ irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;
+
+ writel(irq_ctrl_reg, regs + VIDINTCON0);
+ }
+}
+
+static irqreturn_t s3c_fb_irq(int irq, void *dev_id)
+{
+ struct s3c_fb *sfb = dev_id;
+ void __iomem *regs = sfb->regs;
+ u32 irq_sts_reg;
+
+ irq_sts_reg = readl(regs + VIDINTCON1);
+
+ if (irq_sts_reg & VIDINTCON1_INT_FRAME) {
+
+ /* VSYNC interrupt, accept it */
+ writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);
+
+ sfb->vsync_info.count++;
+ wake_up_interruptible(&sfb->vsync_info.wait);
+ }
+
+ /* We only support waiting for VSYNC for now, so it's safe
+ * to always disable irqs here.
+ */
+ s3c_fb_disable_irq(sfb);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
+ * @sfb: main hardware state
+ * @crtc: head index.
+ */
+static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
+{
+ unsigned long count;
+ int ret;
+
+ if (crtc != 0)
+ return -ENODEV;
+
+ count = sfb->vsync_info.count;
+ s3c_fb_enable_irq(sfb);
+ ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
+ count != sfb->vsync_info.count,
+ msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct s3c_fb_win *win = info->par;
+ struct s3c_fb *sfb = win->parent;
+ int ret;
+ u32 crtc;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (get_user(crtc, (u32 __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = s3c_fb_wait_for_vsync(sfb, crtc);
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
static struct fb_ops s3c_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c_fb_check_var,
@@ -651,9 +1022,33 @@ static struct fb_ops s3c_fb_ops = {
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_pan_display = s3c_fb_pan_display,
+ .fb_ioctl = s3c_fb_ioctl,
};
/**
+ * s3c_fb_missing_pixclock() - calculates pixel clock
+ * @mode: The video mode to change.
+ *
+ * Calculate the pixel clock when none has been given through platform data.
+ */
+static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode)
+{
+ u64 pixclk = 1000000000000ULL;
+ u32 div;
+
+ div = mode->left_margin + mode->hsync_len + mode->right_margin +
+ mode->xres;
+ div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +
+ mode->yres;
+ div *= mode->refresh ? : 60;
+
+ do_div(pixclk, div);
+
+ mode->pixclock = pixclk;
+}
+
+/**
* s3c_fb_alloc_memory() - allocate display memory for framebuffer window
* @sfb: The base resources for the hardware.
* @win: The window to initialise memory for.
@@ -711,7 +1106,8 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
struct fb_info *fbi = win->fbinfo;
- dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
+ if (fbi->screen_base)
+ dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
fbi->screen_base, fbi->fix.smem_start);
}
@@ -724,9 +1120,18 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
*/
static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
+ u32 data;
+
if (win->fbinfo) {
+ if (sfb->variant.has_shadowcon) {
+ data = readl(sfb->regs + SHADOWCON);
+ data &= ~SHADOWCON_CHx_ENABLE(win->index);
+ data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);
+ writel(data, sfb->regs + SHADOWCON);
+ }
unregister_framebuffer(win->fbinfo);
- fb_dealloc_cmap(&win->fbinfo->cmap);
+ if (win->fbinfo->cmap.len)
+ fb_dealloc_cmap(&win->fbinfo->cmap);
s3c_fb_free_memory(sfb, win);
framebuffer_release(win->fbinfo);
}
@@ -735,12 +1140,14 @@ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
/**
* s3c_fb_probe_win() - register an hardware window
* @sfb: The base resources for the hardware
+ * @variant: The variant information for this window.
* @res: Pointer to where to place the resultant window.
*
* Allocate and do the basic initialisation for one of the hardware's graphics
* windows.
*/
static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
+ struct s3c_fb_win_variant *variant,
struct s3c_fb_win **res)
{
struct fb_var_screeninfo *var;
@@ -751,9 +1158,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
int palette_size;
int ret;
- dev_dbg(sfb->dev, "probing window %d\n", win_no);
+ dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
+
+ init_waitqueue_head(&sfb->vsync_info.wait);
- palette_size = s3c_fb_win_pal_size(win_no);
+ palette_size = variant->palette_sz * 4;
fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
palette_size * sizeof(u32), sfb->dev);
@@ -770,7 +1179,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
WARN_ON(windata->win_mode.yres == 0);
win = fbinfo->par;
+ *res = win;
var = &fbinfo->var;
+ win->variant = *variant;
win->fbinfo = fbinfo;
win->parent = sfb;
win->windata = windata;
@@ -784,7 +1195,24 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
}
/* setup the r/b/g positions for the window's palette */
- s3c_fb_init_palette(win_no, &win->palette);
+ if (win->variant.palette_16bpp) {
+ /* Set RGB 5:6:5 as default */
+ win->palette.r.offset = 11;
+ win->palette.r.length = 5;
+ win->palette.g.offset = 5;
+ win->palette.g.length = 6;
+ win->palette.b.offset = 0;
+ win->palette.b.length = 5;
+
+ } else {
+ /* Set 8bpp or 8bpp and 1bit alpha */
+ win->palette.r.offset = 16;
+ win->palette.r.length = 8;
+ win->palette.g.offset = 8;
+ win->palette.g.length = 8;
+ win->palette.b.offset = 0;
+ win->palette.b.length = 8;
+ }
/* setup the initial video mode from the window */
fb_videomode_to_var(&fbinfo->var, initmode);
@@ -808,7 +1236,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
/* create initial colour map */
- ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1);
+ ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
if (ret == 0)
fb_set_cmap(&fbinfo->cmap, fbinfo);
else
@@ -826,7 +1254,6 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
return ret;
}
- *res = win;
dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
return 0;
@@ -842,18 +1269,19 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
{
void __iomem *regs = sfb->regs;
-
- writel(0, regs + WINCON(win));
- writel(0xffffff, regs + WxKEYCONy(win, 0));
- writel(0xffffff, regs + WxKEYCONy(win, 1));
-
- writel(0, regs + VIDOSD_A(win));
- writel(0, regs + VIDOSD_B(win));
- writel(0, regs + VIDOSD_C(win));
+ u32 reg;
+
+ writel(0, regs + sfb->variant.wincon + (win * 4));
+ writel(0, regs + VIDOSD_A(win, sfb->variant));
+ writel(0, regs + VIDOSD_B(win, sfb->variant));
+ writel(0, regs + VIDOSD_C(win, sfb->variant));
+ reg = readl(regs + SHADOWCON);
+ writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON);
}
static int __devinit s3c_fb_probe(struct platform_device *pdev)
{
+ struct s3c_fb_driverdata *fbdrv;
struct device *dev = &pdev->dev;
struct s3c_fb_platdata *pd;
struct s3c_fb *sfb;
@@ -861,6 +1289,13 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
int win;
int ret = 0;
+ fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data;
+
+ if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
+ dev_err(dev, "too many windows, cannot attach\n");
+ return -EINVAL;
+ }
+
pd = pdev->dev.platform_data;
if (!pd) {
dev_err(dev, "no platform data specified\n");
@@ -873,8 +1308,11 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
+
sfb->dev = dev;
sfb->pdata = pd;
+ sfb->variant = fbdrv->variant;
sfb->bus_clk = clk_get(dev, "lcd");
if (IS_ERR(sfb->bus_clk)) {
@@ -906,6 +1344,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
goto err_req_region;
}
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "failed to acquire irq resource\n");
+ ret = -ENOENT;
+ goto err_ioremap;
+ }
+ sfb->irq_no = res->start;
+ ret = request_irq(sfb->irq_no, s3c_fb_irq,
+ 0, "s3c_fb", sfb);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ goto err_ioremap;
+ }
+
dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
/* setup gpio and output polarity controls */
@@ -916,21 +1368,34 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
/* zero all windows before we do anything */
- for (win = 0; win < S3C_FB_MAX_WIN; win++)
+ for (win = 0; win < fbdrv->variant.nr_windows; win++)
s3c_fb_clear_win(sfb, win);
+ /* initialise colour key controls */
+ for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
+ void __iomem *regs = sfb->regs + sfb->variant.keycon;
+
+ regs += (win * 8);
+ writel(0xffffff, regs + WKEYCON0);
+ writel(0xffffff, regs + WKEYCON1);
+ }
+
/* we have the register setup, start allocating framebuffers */
- for (win = 0; win < S3C_FB_MAX_WIN; win++) {
+ for (win = 0; win < fbdrv->variant.nr_windows; win++) {
if (!pd->win[win])
continue;
- ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]);
+ if (!pd->win[win]->win_mode.pixclock)
+ s3c_fb_missing_pixclock(&pd->win[win]->win_mode);
+
+ ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
+ &sfb->windows[win]);
if (ret < 0) {
dev_err(dev, "failed to create window %d\n", win);
for (; win >= 0; win--)
s3c_fb_release_win(sfb, sfb->windows[win]);
- goto err_ioremap;
+ goto err_irq;
}
}
@@ -938,6 +1403,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
return 0;
+err_irq:
+ free_irq(sfb->irq_no, sfb);
+
err_ioremap:
iounmap(sfb->regs);
@@ -970,6 +1438,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
if (sfb->windows[win])
s3c_fb_release_win(sfb, sfb->windows[win]);
+ free_irq(sfb->irq_no, sfb);
+
iounmap(sfb->regs);
clk_disable(sfb->bus_clk);
@@ -1016,9 +1486,17 @@ static int s3c_fb_resume(struct platform_device *pdev)
writel(pd->vidcon1, sfb->regs + VIDCON1);
/* zero all windows before we do anything */
- for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++)
+ for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
s3c_fb_clear_win(sfb, win_no);
+ for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
+ void __iomem *regs = sfb->regs + sfb->variant.keycon;
+
+ regs += (win_no * 8);
+ writel(0xffffff, regs + WKEYCON0);
+ writel(0xffffff, regs + WKEYCON1);
+ }
+
/* restore framebuffers */
for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
win = sfb->windows[win_no];
@@ -1036,11 +1514,208 @@ static int s3c_fb_resume(struct platform_device *pdev)
#define s3c_fb_resume NULL
#endif
+
+#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
+#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
+
+static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {
+ [0] = {
+ .has_osd_c = 1,
+ .osd_size_off = 0x8,
+ .palette_sz = 256,
+ .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
+ },
+ [1] = {
+ .has_osd_c = 1,
+ .has_osd_d = 1,
+ .osd_size_off = 0x12,
+ .has_osd_alpha = 1,
+ .palette_sz = 256,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [2] = {
+ .has_osd_c = 1,
+ .has_osd_d = 1,
+ .osd_size_off = 0x12,
+ .has_osd_alpha = 1,
+ .palette_sz = 16,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [3] = {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 16,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP124 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [4] = {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 4,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) |
+ VALID_BPP(16) | VALID_BPP(18) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_64xx = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x400,
+ [1] = 0x800,
+ [2] = 0x300,
+ [3] = 0x320,
+ [4] = 0x340,
+ },
+
+ .has_prtcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_prtcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_shadowcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+/* S3C2443/S3C2416 style hardware */
+static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
+ .variant = {
+ .nr_windows = 2,
+ .is_2443 = 1,
+
+ .vidtcon = 0x08,
+ .wincon = 0x14,
+ .winmap = 0xd0,
+ .keycon = 0xb0,
+ .osd = 0x28,
+ .osd_stride = 12,
+ .buf_start = 0x64,
+ .buf_size = 0x94,
+ .buf_end = 0x7c,
+
+ .palette = {
+ [0] = 0x400,
+ [1] = 0x800,
+ },
+ },
+ .win[0] = &(struct s3c_fb_win_variant) {
+ .palette_sz = 256,
+ .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
+ },
+ .win[1] = &(struct s3c_fb_win_variant) {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 256,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25) |
+ VALID_BPP(28)),
+ },
+};
+
+static struct platform_device_id s3c_fb_driver_ids[] = {
+ {
+ .name = "s3c-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_64xx,
+ }, {
+ .name = "s5pc100-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5pc100,
+ }, {
+ .name = "s5pv210-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5pv210,
+ }, {
+ .name = "s3c2443-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s3c2443,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
+
static struct platform_driver s3c_fb_driver = {
.probe = s3c_fb_probe,
.remove = __devexit_p(s3c_fb_remove),
.suspend = s3c_fb_suspend,
.resume = s3c_fb_resume,
+ .id_table = s3c_fb_driver_ids,
.driver = {
.name = "s3c-fb",
.owner = THIS_MODULE,
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
new file mode 100644
index 0000000..5699ce0
--- /dev/null
+++ b/drivers/video/sh_mipi_dsi.c
@@ -0,0 +1,505 @@
+/*
+ * Renesas SH-mobile MIPI DSI support
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <video/mipi_display.h>
+#include <video/sh_mipi_dsi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define CMTSRTCTR 0x80d0
+#define CMTSRTREQ 0x8070
+
+#define DSIINTE 0x0060
+
+/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */
+#define MAX_SH_MIPI_DSI 2
+
+struct sh_mipi {
+ void __iomem *base;
+ struct clk *dsit_clk;
+ struct clk *dsip_clk;
+};
+
+static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
+
+/* Protect the above array */
+static DEFINE_MUTEX(array_lock);
+
+static struct sh_mipi *sh_mipi_by_handle(int handle)
+{
+ if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0)
+ return NULL;
+
+ return mipi_dsi[handle];
+}
+
+static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd,
+ u8 cmd, u8 param)
+{
+ u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8);
+ int cnt = 100;
+
+ /* transmit a short packet to LCD panel */
+ iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */
+ iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */
+
+ while ((ioread32(mipi->base + 0x8070) & 1) && --cnt)
+ udelay(1);
+
+ return cnt ? 0 : -ETIMEDOUT;
+}
+
+#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \
+ -EINVAL : (c) - 1)
+
+static int sh_mipi_dcs(int handle, u8 cmd)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
+}
+
+static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd,
+ param);
+}
+
+static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
+{
+ /*
+ * enable LCDC data tx, transition to LPS after completion of each HS
+ * packet
+ */
+ iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */
+}
+
+static void sh_mipi_shutdown(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static void mipi_display_on(void *arg, struct fb_info *info)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, true);
+}
+
+static void mipi_display_off(void *arg)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static int __init sh_mipi_setup(struct sh_mipi *mipi,
+ struct sh_mipi_dsi_info *pdata)
+{
+ void __iomem *base = mipi->base;
+ struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
+ u32 pctype, datatype, pixfmt;
+ u32 linelength;
+ bool yuv;
+
+ /* Select data format */
+ switch (pdata->data_format) {
+ case MIPI_RGB888:
+ pctype = 0;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB565:
+ pctype = 1;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_RGB666_LP:
+ pctype = 2;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB666:
+ pctype = 3;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_BGR888:
+ pctype = 8;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR565:
+ pctype = 9;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_BGR666_LP:
+ pctype = 0xa;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR666:
+ pctype = 0xb;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_YUYV:
+ pctype = 4;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_UYVY:
+ pctype = 5;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_YUV420_L:
+ pctype = 6;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ linelength = (ch->lcd_cfg.xres * 12 + 7) / 8;
+ yuv = true;
+ break;
+ case MIPI_YUV420:
+ pctype = 7;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ /* Length of U/V line */
+ linelength = (ch->lcd_cfg.xres + 1) / 2;
+ yuv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((yuv && ch->interface_type != YUV422) ||
+ (!yuv && ch->interface_type != RGB24))
+ return -EINVAL;
+
+ /* reset DSI link */
+ iowrite32(0x00000001, base); /* SYSCTRL */
+ /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
+ udelay(50);
+ iowrite32(0x00000000, base); /* SYSCTRL */
+
+ /* setup DSI link */
+
+ /*
+ * Default = ULPS enable |
+ * Contention detection enabled |
+ * EoT packet transmission enable |
+ * CRC check enable |
+ * ECC check enable
+ * additionally enable first two lanes
+ */
+ iowrite32(0x00003703, base + 0x04); /* SYSCONF */
+ /*
+ * T_wakeup = 0x7000
+ * T_hs-trail = 3
+ * T_hs-prepare = 3
+ * T_clk-trail = 3
+ * T_clk-prepare = 2
+ */
+ iowrite32(0x70003332, base + 0x08); /* TIMSET */
+ /* no responses requested */
+ iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */
+ /* request response to packets of type 0x28 */
+ iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */
+ /* High-speed transmission timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */
+ /* LP reception timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */
+ /* Turn-around timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */
+ /* Peripheral reset timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */
+ /* Enable timeout counters */
+ iowrite32(0x00000f00, base + 0x30); /* DSICTRL */
+ /* Interrupts not used, disable all */
+ iowrite32(0, base + DSIINTE);
+ /* DSI-Tx bias on */
+ iowrite32(0x00000001, base + 0x70); /* PHYCTRL */
+ udelay(200);
+ /* Deassert resets, power on, set multiplier */
+ iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */
+
+ /* setup l-bridge */
+
+ /*
+ * Enable transmission of all packets,
+ * transmit LPS after each HS packet completion
+ */
+ iowrite32(0x00000006, base + 0x8000); /* DTCTR */
+ /* VSYNC width = 2 (<< 17) */
+ iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */
+ /*
+ * Non-burst mode with sync pulses: VSE and HSE are output,
+ * HSA period allowed, no commands in LP
+ */
+ iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */
+ /*
+ * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see
+ * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default
+ * (unused, since VMCTR2[HSABM] = 0)
+ */
+ iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */
+
+ msleep(5);
+
+ /* setup LCD panel */
+
+ /* cf. drivers/video/omap/lcd_mipid.c */
+ sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(120);
+ /*
+ * [7] - Page Address Mode
+ * [6] - Column Address Mode
+ * [5] - Page / Column Address Mode
+ * [4] - Display Device Line Refresh Order
+ * [3] - RGB/BGR Order
+ * [2] - Display Data Latch Data Order
+ * [1] - Flip Horizontal
+ * [0] - Flip Vertical
+ */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+ /* cf. set_data_lines() */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
+ pixfmt << 4);
+ sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int __init sh_mipi_probe(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi;
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ unsigned long rate, f_current;
+ int idx = pdev->id, ret;
+ char dsip_clk[] = "dsi.p_clk";
+
+ if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
+ return -ENODEV;
+
+ mutex_lock(&array_lock);
+ if (idx < 0)
+ for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
+ ;
+
+ if (idx == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EBUSY;
+ goto efindslot;
+ }
+
+ mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
+ if (!mipi) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "MIPI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ mipi->base = ioremap(res->start, resource_size(res));
+ if (!mipi->base) {
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
+ if (IS_ERR(mipi->dsit_clk)) {
+ ret = PTR_ERR(mipi->dsit_clk);
+ goto eclktget;
+ }
+
+ f_current = clk_get_rate(mipi->dsit_clk);
+ /* 80MHz required by the datasheet */
+ rate = clk_round_rate(mipi->dsit_clk, 80000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsit_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esettrate;
+
+ dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
+
+ sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
+ mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
+ if (IS_ERR(mipi->dsip_clk)) {
+ ret = PTR_ERR(mipi->dsip_clk);
+ goto eclkpget;
+ }
+
+ f_current = clk_get_rate(mipi->dsip_clk);
+ /* Between 10 and 50MHz */
+ rate = clk_round_rate(mipi->dsip_clk, 24000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsip_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esetprate;
+
+ dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
+
+ msleep(10);
+
+ ret = clk_enable(mipi->dsit_clk);
+ if (ret < 0)
+ goto eclkton;
+
+ ret = clk_enable(mipi->dsip_clk);
+ if (ret < 0)
+ goto eclkpon;
+
+ mipi_dsi[idx] = mipi;
+
+ ret = sh_mipi_setup(mipi, pdata);
+ if (ret < 0)
+ goto emipisetup;
+
+ mutex_unlock(&array_lock);
+ platform_set_drvdata(pdev, mipi);
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = mipi;
+ pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
+
+ return 0;
+
+emipisetup:
+ mipi_dsi[idx] = NULL;
+ clk_disable(mipi->dsip_clk);
+eclkpon:
+ clk_disable(mipi->dsit_clk);
+eclkton:
+esetprate:
+ clk_put(mipi->dsip_clk);
+eclkpget:
+esettrate:
+ clk_put(mipi->dsit_clk);
+eclktget:
+ iounmap(mipi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ kfree(mipi);
+ealloc:
+efindslot:
+ mutex_unlock(&array_lock);
+
+ return ret;
+}
+
+static int __exit sh_mipi_remove(struct platform_device *pdev)
+{
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+ int i, ret;
+
+ mutex_lock(&array_lock);
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++)
+ ;
+
+ if (i == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ mipi_dsi[i] = NULL;
+ }
+
+ mutex_unlock(&array_lock);
+
+ if (ret < 0)
+ return ret;
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ clk_disable(mipi->dsip_clk);
+ clk_disable(mipi->dsit_clk);
+ clk_put(mipi->dsit_clk);
+ clk_put(mipi->dsip_clk);
+ iounmap(mipi->base);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(mipi);
+
+ return 0;
+}
+
+static struct platform_driver sh_mipi_driver = {
+ .remove = __exit_p(sh_mipi_remove),
+ .shutdown = sh_mipi_shutdown,
+ .driver = {
+ .name = "sh-mipi-dsi",
+ },
+};
+
+static int __init sh_mipi_init(void)
+{
+ return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe);
+}
+module_init(sh_mipi_init);
+
+static void __exit sh_mipi_exit(void)
+{
+ platform_driver_unregister(&sh_mipi_driver);
+}
+module_exit(sh_mipi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
new file mode 100644
index 0000000..2fde08c
--- /dev/null
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -0,0 +1,1028 @@
+/*
+ * SH-Mobile High-Definition Multimedia Interface (HDMI) driver
+ * for SLISHDMI13T and SLIPHDMIT IP cores
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <video/sh_mobile_hdmi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define HDMI_SYSTEM_CTRL 0x00 /* System control */
+#define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control,
+ bits 19..16 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8 0x02 /* bits 15..8 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0 0x03 /* bits 7..0 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS 0x04 /* SPDIF audio sampling frequency,
+ bits 19..16 of Internal CTS */
+#define HDMI_INTERNAL_CTS_15_8 0x05 /* bits 15..8 of Internal CTS */
+#define HDMI_INTERNAL_CTS_7_0 0x06 /* bits 7..0 of Internal CTS */
+#define HDMI_EXTERNAL_CTS_19_16 0x07 /* External CTS */
+#define HDMI_EXTERNAL_CTS_15_8 0x08 /* External CTS */
+#define HDMI_EXTERNAL_CTS_7_0 0x09 /* External CTS */
+#define HDMI_AUDIO_SETTING_1 0x0A /* Audio setting.1 */
+#define HDMI_AUDIO_SETTING_2 0x0B /* Audio setting.2 */
+#define HDMI_I2S_AUDIO_SET 0x0C /* I2S audio setting */
+#define HDMI_DSD_AUDIO_SET 0x0D /* DSD audio setting */
+#define HDMI_DEBUG_MONITOR_1 0x0E /* Debug monitor.1 */
+#define HDMI_DEBUG_MONITOR_2 0x0F /* Debug monitor.2 */
+#define HDMI_I2S_INPUT_PIN_SWAP 0x10 /* I2S input pin swap */
+#define HDMI_AUDIO_STATUS_BITS_SETTING_1 0x11 /* Audio status bits setting.1 */
+#define HDMI_AUDIO_STATUS_BITS_SETTING_2 0x12 /* Audio status bits setting.2 */
+#define HDMI_CATEGORY_CODE 0x13 /* Category code */
+#define HDMI_SOURCE_NUM_AUDIO_WORD_LEN 0x14 /* Source number/Audio word length */
+#define HDMI_AUDIO_VIDEO_SETTING_1 0x15 /* Audio/Video setting.1 */
+#define HDMI_VIDEO_SETTING_1 0x16 /* Video setting.1 */
+#define HDMI_DEEP_COLOR_MODES 0x17 /* Deep Color Modes */
+
+/* 12 16- and 10-bit Color space conversion parameters: 0x18..0x2f */
+#define HDMI_COLOR_SPACE_CONVERSION_PARAMETERS 0x18
+
+#define HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS 0x30 /* External video parameter settings */
+#define HDMI_EXTERNAL_H_TOTAL_7_0 0x31 /* External horizontal total (LSB) */
+#define HDMI_EXTERNAL_H_TOTAL_11_8 0x32 /* External horizontal total (MSB) */
+#define HDMI_EXTERNAL_H_BLANK_7_0 0x33 /* External horizontal blank (LSB) */
+#define HDMI_EXTERNAL_H_BLANK_9_8 0x34 /* External horizontal blank (MSB) */
+#define HDMI_EXTERNAL_H_DELAY_7_0 0x35 /* External horizontal delay (LSB) */
+#define HDMI_EXTERNAL_H_DELAY_9_8 0x36 /* External horizontal delay (MSB) */
+#define HDMI_EXTERNAL_H_DURATION_7_0 0x37 /* External horizontal duration (LSB) */
+#define HDMI_EXTERNAL_H_DURATION_9_8 0x38 /* External horizontal duration (MSB) */
+#define HDMI_EXTERNAL_V_TOTAL_7_0 0x39 /* External vertical total (LSB) */
+#define HDMI_EXTERNAL_V_TOTAL_9_8 0x3A /* External vertical total (MSB) */
+#define HDMI_AUDIO_VIDEO_SETTING_2 0x3B /* Audio/Video setting.2 */
+#define HDMI_EXTERNAL_V_BLANK 0x3D /* External vertical blank */
+#define HDMI_EXTERNAL_V_DELAY 0x3E /* External vertical delay */
+#define HDMI_EXTERNAL_V_DURATION 0x3F /* External vertical duration */
+#define HDMI_CTRL_PKT_MANUAL_SEND_CONTROL 0x40 /* Control packet manual send control */
+#define HDMI_CTRL_PKT_AUTO_SEND 0x41 /* Control packet auto send with VSYNC control */
+#define HDMI_AUTO_CHECKSUM_OPTION 0x42 /* Auto checksum option */
+#define HDMI_VIDEO_SETTING_2 0x45 /* Video setting.2 */
+#define HDMI_OUTPUT_OPTION 0x46 /* Output option */
+#define HDMI_SLIPHDMIT_PARAM_OPTION 0x51 /* SLIPHDMIT parameter option */
+#define HDMI_HSYNC_PMENT_AT_EMB_7_0 0x52 /* HSYNC placement at embedded sync (LSB) */
+#define HDMI_HSYNC_PMENT_AT_EMB_15_8 0x53 /* HSYNC placement at embedded sync (MSB) */
+#define HDMI_VSYNC_PMENT_AT_EMB_7_0 0x54 /* VSYNC placement at embedded sync (LSB) */
+#define HDMI_VSYNC_PMENT_AT_EMB_14_8 0x55 /* VSYNC placement at embedded sync (MSB) */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_1 0x56 /* SLIPHDMIT parameter settings.1 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_2 0x57 /* SLIPHDMIT parameter settings.2 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_3 0x58 /* SLIPHDMIT parameter settings.3 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_5 0x59 /* SLIPHDMIT parameter settings.5 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_6 0x5A /* SLIPHDMIT parameter settings.6 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_7 0x5B /* SLIPHDMIT parameter settings.7 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_8 0x5C /* SLIPHDMIT parameter settings.8 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_9 0x5D /* SLIPHDMIT parameter settings.9 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_10 0x5E /* SLIPHDMIT parameter settings.10 */
+#define HDMI_CTRL_PKT_BUF_INDEX 0x5F /* Control packet buffer index */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB0 0x60 /* Control packet data buffer access window - HB0 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB1 0x61 /* Control packet data buffer access window - HB1 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB2 0x62 /* Control packet data buffer access window - HB2 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB0 0x63 /* Control packet data buffer access window - PB0 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB1 0x64 /* Control packet data buffer access window - PB1 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB2 0x65 /* Control packet data buffer access window - PB2 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB3 0x66 /* Control packet data buffer access window - PB3 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB4 0x67 /* Control packet data buffer access window - PB4 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB5 0x68 /* Control packet data buffer access window - PB5 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB6 0x69 /* Control packet data buffer access window - PB6 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB7 0x6A /* Control packet data buffer access window - PB7 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB8 0x6B /* Control packet data buffer access window - PB8 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB9 0x6C /* Control packet data buffer access window - PB9 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB10 0x6D /* Control packet data buffer access window - PB10 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB11 0x6E /* Control packet data buffer access window - PB11 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB12 0x6F /* Control packet data buffer access window - PB12 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB13 0x70 /* Control packet data buffer access window - PB13 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB14 0x71 /* Control packet data buffer access window - PB14 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB15 0x72 /* Control packet data buffer access window - PB15 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB16 0x73 /* Control packet data buffer access window - PB16 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB17 0x74 /* Control packet data buffer access window - PB17 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB18 0x75 /* Control packet data buffer access window - PB18 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB19 0x76 /* Control packet data buffer access window - PB19 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB20 0x77 /* Control packet data buffer access window - PB20 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB21 0x78 /* Control packet data buffer access window - PB21 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB22 0x79 /* Control packet data buffer access window - PB22 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB23 0x7A /* Control packet data buffer access window - PB23 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB24 0x7B /* Control packet data buffer access window - PB24 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB25 0x7C /* Control packet data buffer access window - PB25 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB26 0x7D /* Control packet data buffer access window - PB26 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB27 0x7E /* Control packet data buffer access window - PB27 */
+#define HDMI_EDID_KSV_FIFO_ACCESS_WINDOW 0x80 /* EDID/KSV FIFO access window */
+#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_7_0 0x81 /* DDC bus access frequency control (LSB) */
+#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_15_8 0x82 /* DDC bus access frequency control (MSB) */
+#define HDMI_INTERRUPT_MASK_1 0x92 /* Interrupt mask.1 */
+#define HDMI_INTERRUPT_MASK_2 0x93 /* Interrupt mask.2 */
+#define HDMI_INTERRUPT_STATUS_1 0x94 /* Interrupt status.1 */
+#define HDMI_INTERRUPT_STATUS_2 0x95 /* Interrupt status.2 */
+#define HDMI_INTERRUPT_MASK_3 0x96 /* Interrupt mask.3 */
+#define HDMI_INTERRUPT_MASK_4 0x97 /* Interrupt mask.4 */
+#define HDMI_INTERRUPT_STATUS_3 0x98 /* Interrupt status.3 */
+#define HDMI_INTERRUPT_STATUS_4 0x99 /* Interrupt status.4 */
+#define HDMI_SOFTWARE_HDCP_CONTROL_1 0x9A /* Software HDCP control.1 */
+#define HDMI_FRAME_COUNTER 0x9C /* Frame counter */
+#define HDMI_FRAME_COUNTER_FOR_RI_CHECK 0x9D /* Frame counter for Ri check */
+#define HDMI_HDCP_CONTROL 0xAF /* HDCP control */
+#define HDMI_RI_FRAME_COUNT_REGISTER 0xB2 /* Ri frame count register */
+#define HDMI_DDC_BUS_CONTROL 0xB7 /* DDC bus control */
+#define HDMI_HDCP_STATUS 0xB8 /* HDCP status */
+#define HDMI_SHA0 0xB9 /* sha0 */
+#define HDMI_SHA1 0xBA /* sha1 */
+#define HDMI_SHA2 0xBB /* sha2 */
+#define HDMI_SHA3 0xBC /* sha3 */
+#define HDMI_SHA4 0xBD /* sha4 */
+#define HDMI_BCAPS_READ 0xBE /* BCAPS read / debug */
+#define HDMI_AKSV_BKSV_7_0_MONITOR 0xBF /* AKSV/BKSV[7:0] monitor */
+#define HDMI_AKSV_BKSV_15_8_MONITOR 0xC0 /* AKSV/BKSV[15:8] monitor */
+#define HDMI_AKSV_BKSV_23_16_MONITOR 0xC1 /* AKSV/BKSV[23:16] monitor */
+#define HDMI_AKSV_BKSV_31_24_MONITOR 0xC2 /* AKSV/BKSV[31:24] monitor */
+#define HDMI_AKSV_BKSV_39_32_MONITOR 0xC3 /* AKSV/BKSV[39:32] monitor */
+#define HDMI_EDID_SEGMENT_POINTER 0xC4 /* EDID segment pointer */
+#define HDMI_EDID_WORD_ADDRESS 0xC5 /* EDID word address */
+#define HDMI_EDID_DATA_FIFO_ADDRESS 0xC6 /* EDID data FIFO address */
+#define HDMI_NUM_OF_HDMI_DEVICES 0xC7 /* Number of HDMI devices */
+#define HDMI_HDCP_ERROR_CODE 0xC8 /* HDCP error code */
+#define HDMI_100MS_TIMER_SET 0xC9 /* 100ms timer setting */
+#define HDMI_5SEC_TIMER_SET 0xCA /* 5sec timer setting */
+#define HDMI_RI_READ_COUNT 0xCB /* Ri read count */
+#define HDMI_AN_SEED 0xCC /* An seed */
+#define HDMI_MAX_NUM_OF_RCIVRS_ALLOWED 0xCD /* Maximum number of receivers allowed */
+#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_1 0xCE /* HDCP memory access control.1 */
+#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_2 0xCF /* HDCP memory access control.2 */
+#define HDMI_HDCP_CONTROL_2 0xD0 /* HDCP Control 2 */
+#define HDMI_HDCP_KEY_MEMORY_CONTROL 0xD2 /* HDCP Key Memory Control */
+#define HDMI_COLOR_SPACE_CONV_CONFIG_1 0xD3 /* Color space conversion configuration.1 */
+#define HDMI_VIDEO_SETTING_3 0xD4 /* Video setting.3 */
+#define HDMI_RI_7_0 0xD5 /* Ri[7:0] */
+#define HDMI_RI_15_8 0xD6 /* Ri[15:8] */
+#define HDMI_PJ 0xD7 /* Pj */
+#define HDMI_SHA_RD 0xD8 /* sha_rd */
+#define HDMI_RI_7_0_SAVED 0xD9 /* Ri[7:0] saved */
+#define HDMI_RI_15_8_SAVED 0xDA /* Ri[15:8] saved */
+#define HDMI_PJ_SAVED 0xDB /* Pj saved */
+#define HDMI_NUM_OF_DEVICES 0xDC /* Number of devices */
+#define HDMI_HOT_PLUG_MSENS_STATUS 0xDF /* Hot plug/MSENS status */
+#define HDMI_BCAPS_WRITE 0xE0 /* bcaps */
+#define HDMI_BSTAT_7_0 0xE1 /* bstat[7:0] */
+#define HDMI_BSTAT_15_8 0xE2 /* bstat[15:8] */
+#define HDMI_BKSV_7_0 0xE3 /* bksv[7:0] */
+#define HDMI_BKSV_15_8 0xE4 /* bksv[15:8] */
+#define HDMI_BKSV_23_16 0xE5 /* bksv[23:16] */
+#define HDMI_BKSV_31_24 0xE6 /* bksv[31:24] */
+#define HDMI_BKSV_39_32 0xE7 /* bksv[39:32] */
+#define HDMI_AN_7_0 0xE8 /* An[7:0] */
+#define HDMI_AN_15_8 0xE9 /* An [15:8] */
+#define HDMI_AN_23_16 0xEA /* An [23:16] */
+#define HDMI_AN_31_24 0xEB /* An [31:24] */
+#define HDMI_AN_39_32 0xEC /* An [39:32] */
+#define HDMI_AN_47_40 0xED /* An [47:40] */
+#define HDMI_AN_55_48 0xEE /* An [55:48] */
+#define HDMI_AN_63_56 0xEF /* An [63:56] */
+#define HDMI_PRODUCT_ID 0xF0 /* Product ID */
+#define HDMI_REVISION_ID 0xF1 /* Revision ID */
+#define HDMI_TEST_MODE 0xFE /* Test mode */
+
+enum hotplug_state {
+ HDMI_HOTPLUG_DISCONNECTED,
+ HDMI_HOTPLUG_CONNECTED,
+ HDMI_HOTPLUG_EDID_DONE,
+};
+
+struct sh_hdmi {
+ void __iomem *base;
+ enum hotplug_state hp_state;
+ struct clk *hdmi_clk;
+ struct device *dev;
+ struct fb_info *info;
+ struct delayed_work edid_work;
+ struct fb_var_screeninfo var;
+};
+
+static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg)
+{
+ iowrite8(data, hdmi->base + reg);
+}
+
+static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg)
+{
+ return ioread8(hdmi->base + reg);
+}
+
+/* External video parameter settings */
+static void hdmi_external_video_param(struct sh_hdmi *hdmi)
+{
+ struct fb_var_screeninfo *var = &hdmi->var;
+ u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset;
+ u8 sync = 0;
+
+ htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len;
+
+ hdelay = var->hsync_len + var->left_margin;
+ hblank = var->right_margin + hdelay;
+
+ /*
+ * Vertical timing looks a bit different in Figure 18,
+ * but let's try the same first by setting offset = 0
+ */
+ vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+
+ vdelay = var->vsync_len + var->upper_margin;
+ vblank = var->lower_margin + vdelay;
+ voffset = min(var->upper_margin / 2, 6U);
+
+ /*
+ * [3]: VSYNC polarity: Positive
+ * [2]: HSYNC polarity: Positive
+ * [1]: Interlace/Progressive: Progressive
+ * [0]: External video settings enable: used.
+ */
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ sync |= 4;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ sync |= 8;
+
+ pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n",
+ htotal, hblank, hdelay, var->hsync_len,
+ vtotal, vblank, vdelay, var->vsync_len, sync);
+
+ hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
+
+ hdmi_write(hdmi, htotal, HDMI_EXTERNAL_H_TOTAL_7_0);
+ hdmi_write(hdmi, htotal >> 8, HDMI_EXTERNAL_H_TOTAL_11_8);
+
+ hdmi_write(hdmi, hblank, HDMI_EXTERNAL_H_BLANK_7_0);
+ hdmi_write(hdmi, hblank >> 8, HDMI_EXTERNAL_H_BLANK_9_8);
+
+ hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0);
+ hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8);
+
+ hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0);
+ hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8);
+
+ hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0);
+ hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8);
+
+ hdmi_write(hdmi, vblank, HDMI_EXTERNAL_V_BLANK);
+
+ hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY);
+
+ hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
+
+ /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */
+}
+
+/**
+ * sh_hdmi_video_config()
+ */
+static void sh_hdmi_video_config(struct sh_hdmi *hdmi)
+{
+ /*
+ * [7:4]: Audio sampling frequency: 48kHz
+ * [3:1]: Input video format: RGB and YCbCr 4:4:4 (Y on Green)
+ * [0]: Internal/External DE select: internal
+ */
+ hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1);
+
+ /*
+ * [7:6]: Video output format: RGB 4:4:4
+ * [5:4]: Input video data width: 8 bit
+ * [3:1]: EAV/SAV location: channel 1
+ * [0]: Video input color space: RGB
+ */
+ hdmi_write(hdmi, 0x34, HDMI_VIDEO_SETTING_1);
+
+ /*
+ * [7:6]: Together with bit [6] of HDMI_AUDIO_VIDEO_SETTING_2, which is
+ * left at 0 by default, this configures 24bpp and sets the Color Depth
+ * (CD) field in the General Control Packet
+ */
+ hdmi_write(hdmi, 0x20, HDMI_DEEP_COLOR_MODES);
+}
+
+/**
+ * sh_hdmi_audio_config()
+ */
+static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
+{
+ /*
+ * [7:4] L/R data swap control
+ * [3:0] appropriate N[19:16]
+ */
+ hdmi_write(hdmi, 0x00, HDMI_L_R_DATA_SWAP_CTRL_RPKT);
+ /* appropriate N[15:8] */
+ hdmi_write(hdmi, 0x18, HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8);
+ /* appropriate N[7:0] */
+ hdmi_write(hdmi, 0x00, HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0);
+
+ /* [7:4] 48 kHz SPDIF not used */
+ hdmi_write(hdmi, 0x20, HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS);
+
+ /*
+ * [6:5] set required down sampling rate if required
+ * [4:3] set required audio source
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1);
+
+ /* [3:0] set sending channel number for channel status */
+ hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2);
+
+ /*
+ * [5:2] set valid I2S source input pin
+ * [1:0] set input I2S source mode
+ */
+ hdmi_write(hdmi, 0x04, HDMI_I2S_AUDIO_SET);
+
+ /* [7:4] set valid DSD source input pin */
+ hdmi_write(hdmi, 0x00, HDMI_DSD_AUDIO_SET);
+
+ /* [7:0] set appropriate I2S input pin swap settings if required */
+ hdmi_write(hdmi, 0x00, HDMI_I2S_INPUT_PIN_SWAP);
+
+ /*
+ * [7] set validity bit for channel status
+ * [3:0] set original sample frequency for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_1);
+
+ /*
+ * [7] set value for channel status
+ * [6] set value for channel status
+ * [5] set copyright bit for channel status
+ * [4:2] set additional information for channel status
+ * [1:0] set clock accuracy for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_2);
+
+ /* [7:0] set category code for channel status */
+ hdmi_write(hdmi, 0x00, HDMI_CATEGORY_CODE);
+
+ /*
+ * [7:4] set source number for channel status
+ * [3:0] set word length for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_SOURCE_NUM_AUDIO_WORD_LEN);
+
+ /* [7:4] set sample frequency for channel status */
+ hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1);
+}
+
+/**
+ * sh_hdmi_phy_config()
+ */
+static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
+{
+ /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
+ hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3);
+ /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */
+ hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5);
+ hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6);
+ hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7);
+ hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
+ hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
+ hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
+}
+
+/**
+ * sh_hdmi_avi_infoframe_setup() - Auxiliary Video Information InfoFrame CONTROL PACKET
+ */
+static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi)
+{
+ /* AVI InfoFrame */
+ hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x82 */
+ hdmi_write(hdmi, 0x82, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+
+ /* Version = 0x02 */
+ hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+
+ /* Length = 13 (0x0D) */
+ hdmi_write(hdmi, 0x0D, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* N. A. Checksum */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0);
+
+ /*
+ * Y = RGB
+ * A0 = No Data
+ * B = Bar Data not valid
+ * S = No Data
+ */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1);
+
+ /*
+ * C = No Data
+ * M = 16:9 Picture Aspect Ratio
+ * R = Same as picture aspect ratio
+ */
+ hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2);
+
+ /*
+ * ITC = No Data
+ * EC = xvYCC601
+ * Q = Default (depends on video format)
+ * SC = No Known non_uniform Scaling
+ */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
+
+ /*
+ * VIC = 1280 x 720p: ignored if external config is used
+ * Send 2 for 720 x 480p, 16 for 1080p
+ */
+ hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
+
+ /* PR = No Repetition */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5);
+
+ /* Line Number of End of Top Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6);
+
+ /* Line Number of End of Top Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7);
+
+ /* Line Number of Start of Bottom Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8);
+
+ /* Line Number of Start of Bottom Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9);
+
+ /* Pixel Number of End of Left Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10);
+
+ /* Pixel Number of End of Left Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB11);
+
+ /* Pixel Number of Start of Right Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB12);
+
+ /* Pixel Number of Start of Right Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB13);
+}
+
+/**
+ * sh_hdmi_audio_infoframe_setup() - Audio InfoFrame of CONTROL PACKET
+ */
+static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi)
+{
+ /* Audio InfoFrame */
+ hdmi_write(hdmi, 0x08, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x84 */
+ hdmi_write(hdmi, 0x84, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+
+ /* Version Number = 0x01 */
+ hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+
+ /* 0 Length = 10 (0x0A) */
+ hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* n. a. Checksum */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0);
+
+ /* Audio Channel Count = Refer to Stream Header */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1);
+
+ /* Refer to Stream Header */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB2);
+
+ /* Format depends on coding type (i.e. CT0...CT3) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
+
+ /* Speaker Channel Allocation = Front Right + Front Left */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
+
+ /* Level Shift Value = 0 dB, Down - mix is permitted or no information */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5);
+
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10);
+}
+
+/**
+ * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET
+ */
+static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* Gamut Metadata Packet */
+ hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x0A */
+ hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* Gamut Packet is not used, so default value */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Gamut Packet is not used, so default value */
+ hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* GBD bytes 0 through 27 */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP)
+ */
+static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* Audio Content Protection Packet (ACP) */
+ hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x04 */
+ hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* ACP_Type */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* GBD bytes 0 through 27 */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_isrc1_setup() - ISRC1 Packet
+ */
+static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* ISRC1 Packet */
+ hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x05 */
+ hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* PB0 UPC_EAN_ISRC_0-15 */
+ /* Bytes PB16-PB27 shall be set to a value of 0. */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_isrc2_setup() - ISRC2 Packet
+ */
+static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* ISRC2 Packet */
+ hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* HB0 Packet Type = 0x06 */
+ hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* PB0 UPC_EAN_ISRC_16-31 */
+ /* Bytes PB16-PB27 shall be set to a value of 0. */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_configure() - Initialise HDMI for output
+ */
+static void sh_hdmi_configure(struct sh_hdmi *hdmi)
+{
+ /* Configure video format */
+ sh_hdmi_video_config(hdmi);
+
+ /* Configure audio format */
+ sh_hdmi_audio_config(hdmi);
+
+ /* Configure PHY */
+ sh_hdmi_phy_config(hdmi);
+
+ /* Auxiliary Video Information (AVI) InfoFrame */
+ sh_hdmi_avi_infoframe_setup(hdmi);
+
+ /* Audio InfoFrame */
+ sh_hdmi_audio_infoframe_setup(hdmi);
+
+ /* Gamut Metadata packet */
+ sh_hdmi_gamut_metadata_setup(hdmi);
+
+ /* Audio Content Protection (ACP) Packet */
+ sh_hdmi_acp_setup(hdmi);
+
+ /* ISRC1 Packet */
+ sh_hdmi_isrc1_setup(hdmi);
+
+ /* ISRC2 Packet */
+ sh_hdmi_isrc2_setup(hdmi);
+
+ /*
+ * Control packet auto send with VSYNC control: auto send
+ * General control, Gamut metadata, ISRC, and ACP packets
+ */
+ hdmi_write(hdmi, 0x8E, HDMI_CTRL_PKT_AUTO_SEND);
+
+ /* FIXME */
+ msleep(10);
+
+ /* PS mode b->d, reset PLLA and PLLB */
+ hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL);
+
+ udelay(10);
+
+ hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL);
+}
+
+static void sh_hdmi_read_edid(struct sh_hdmi *hdmi)
+{
+ struct fb_var_screeninfo *var = &hdmi->var;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+ struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg;
+ unsigned long height = var->height, width = var->width;
+ int i;
+ u8 edid[128];
+
+ /* Read EDID */
+ pr_debug("Read back EDID code:");
+ for (i = 0; i < 128; i++) {
+ edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW);
+#ifdef DEBUG
+ if ((i % 16) == 0) {
+ printk(KERN_CONT "\n");
+ printk(KERN_DEBUG "%02X | %02X", i, edid[i]);
+ } else {
+ printk(KERN_CONT " %02X", edid[i]);
+ }
+#endif
+ }
+#ifdef DEBUG
+ printk(KERN_CONT "\n");
+#endif
+ fb_parse_edid(edid, var);
+ pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n",
+ var->left_margin, var->xres, var->right_margin, var->hsync_len,
+ var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
+ PICOS2KHZ(var->pixclock));
+
+ /* FIXME: Use user-provided configuration instead of EDID */
+ var->width = width;
+ var->xres = lcd_cfg->xres;
+ var->xres_virtual = lcd_cfg->xres;
+ var->left_margin = lcd_cfg->left_margin;
+ var->right_margin = lcd_cfg->right_margin;
+ var->hsync_len = lcd_cfg->hsync_len;
+ var->height = height;
+ var->yres = lcd_cfg->yres;
+ var->yres_virtual = lcd_cfg->yres * 2;
+ var->upper_margin = lcd_cfg->upper_margin;
+ var->lower_margin = lcd_cfg->lower_margin;
+ var->vsync_len = lcd_cfg->vsync_len;
+ var->sync = lcd_cfg->sync;
+ var->pixclock = lcd_cfg->pixclock;
+
+ hdmi_external_video_param(hdmi);
+}
+
+static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id)
+{
+ struct sh_hdmi *hdmi = dev_id;
+ u8 status1, status2, mask1, mask2;
+
+ /* mode_b and PLLA and PLLB reset */
+ hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL);
+
+ /* How long shall reset be held? */
+ udelay(10);
+
+ /* mode_b and PLLA and PLLB reset release */
+ hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL);
+
+ status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1);
+ status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2);
+
+ mask1 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_1);
+ mask2 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_2);
+
+ /* Correct would be to ack only set bits, but the datasheet requires 0xff */
+ hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_1);
+ hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2);
+
+ if (printk_ratelimit())
+ pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n",
+ irq, status1, mask1, status2, mask2);
+
+ if (!((status1 & mask1) | (status2 & mask2))) {
+ return IRQ_NONE;
+ } else if (status1 & 0xc0) {
+ u8 msens;
+
+ /* Datasheet specifies 10ms... */
+ udelay(500);
+
+ msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS);
+ pr_debug("MSENS 0x%x\n", msens);
+ /* Check, if hot plug & MSENS pin status are both high */
+ if ((msens & 0xC0) == 0xC0) {
+ /* Display plug in */
+ hdmi->hp_state = HDMI_HOTPLUG_CONNECTED;
+
+ /* Set EDID word address */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ /* Set EDID segment pointer */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ /* Enable EDID interrupt */
+ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1);
+ } else if (!(status1 & 0x80)) {
+ /* Display unplug, beware multiple interrupts */
+ if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED)
+ schedule_delayed_work(&hdmi->edid_work, 0);
+
+ hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
+ /* display_off will switch back to mode_a */
+ }
+ } else if (status1 & 2) {
+ /* EDID error interrupt: retry */
+ /* Set EDID word address */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ /* Set EDID segment pointer */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ } else if (status1 & 4) {
+ /* Disable EDID interrupt */
+ hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1);
+ hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE;
+ schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hdmi_display_on(void *arg, struct fb_info *info)
+{
+ struct sh_hdmi *hdmi = arg;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ if (info->var.xres != 1280 || info->var.yres != 720) {
+ dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n",
+ info->var.xres, info->var.yres);
+ return;
+ }
+
+ pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state);
+ /*
+ * FIXME: not a good place to store fb_info. And we cannot nullify it
+ * even on monitor disconnect. What should the lifecycle be?
+ */
+ hdmi->info = info;
+ switch (hdmi->hp_state) {
+ case HDMI_HOTPLUG_EDID_DONE:
+ /* PS mode d->e. All functions are active */
+ hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL);
+ pr_debug("HDMI running\n");
+ break;
+ case HDMI_HOTPLUG_DISCONNECTED:
+ info->state = FBINFO_STATE_SUSPENDED;
+ default:
+ hdmi->var = info->var;
+ }
+}
+
+static void hdmi_display_off(void *arg)
+{
+ struct sh_hdmi *hdmi = arg;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ pr_debug("%s(%p)\n", __func__, pdata->lcd_dev);
+ /* PS mode e->a */
+ hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL);
+}
+
+/* Hotplug interrupt occurred, read EDID */
+static void edid_work_fn(struct work_struct *work)
+{
+ struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work);
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ pr_debug("%s(%p): begin, hotplug status %d\n", __func__,
+ pdata->lcd_dev, hdmi->hp_state);
+
+ if (!pdata->lcd_dev)
+ return;
+
+ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
+ pm_runtime_get_sync(hdmi->dev);
+ /* A device has been plugged in */
+ sh_hdmi_read_edid(hdmi);
+ msleep(10);
+ sh_hdmi_configure(hdmi);
+ /* Switched to another (d) power-save mode */
+ msleep(10);
+
+ if (!hdmi->info)
+ return;
+
+ acquire_console_sem();
+
+ /* HDMI plug in */
+ hdmi->info->var = hdmi->var;
+ if (hdmi->info->state != FBINFO_STATE_RUNNING)
+ fb_set_suspend(hdmi->info, 0);
+ else
+ hdmi_display_on(hdmi, hdmi->info);
+
+ release_console_sem();
+ } else {
+ if (!hdmi->info)
+ return;
+
+ acquire_console_sem();
+
+ /* HDMI disconnect */
+ fb_set_suspend(hdmi->info, 1);
+
+ release_console_sem();
+ pm_runtime_put(hdmi->dev);
+ }
+
+ pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev);
+}
+
+static int __init sh_hdmi_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0), ret;
+ struct sh_hdmi *hdmi;
+ long rate;
+
+ if (!res || !pdata || irq < 0)
+ return -ENODEV;
+
+ hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi) {
+ dev_err(&pdev->dev, "Cannot allocate device data\n");
+ return -ENOMEM;
+ }
+
+ hdmi->dev = &pdev->dev;
+
+ hdmi->hdmi_clk = clk_get(&pdev->dev, "ick");
+ if (IS_ERR(hdmi->hdmi_clk)) {
+ ret = PTR_ERR(hdmi->hdmi_clk);
+ dev_err(&pdev->dev, "Unable to get clock: %d\n", ret);
+ goto egetclk;
+ }
+
+ rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000;
+
+ rate = clk_round_rate(hdmi->hdmi_clk, rate);
+ if (rate < 0) {
+ ret = rate;
+ dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate);
+ goto erate;
+ }
+
+ ret = clk_set_rate(hdmi->hdmi_clk, rate);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret);
+ goto erate;
+ }
+
+ pr_debug("HDMI set frequency %lu\n", rate);
+
+ ret = clk_enable(hdmi->hdmi_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret);
+ goto eclkenable;
+ }
+
+ dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate);
+
+ if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "HDMI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ hdmi->base = ioremap(res->start, resource_size(res));
+ if (!hdmi->base) {
+ dev_err(&pdev->dev, "HDMI register region already claimed\n");
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ platform_set_drvdata(pdev, hdmi);
+
+#if 1
+ /* Product and revision IDs are 0 in sh-mobile version */
+ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
+ hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
+#endif
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = hdmi;
+ pdata->lcd_chan->board_cfg.display_on = hdmi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = hdmi_display_off;
+
+ INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ ret = request_irq(irq, sh_hdmi_hotplug, 0,
+ dev_name(&pdev->dev), hdmi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request irq: %d\n", ret);
+ goto ereqirq;
+ }
+
+ return 0;
+
+ereqirq:
+ pm_runtime_disable(&pdev->dev);
+ iounmap(hdmi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ clk_disable(hdmi->hdmi_clk);
+eclkenable:
+erate:
+ clk_put(hdmi->hdmi_clk);
+egetclk:
+ kfree(hdmi);
+
+ return ret;
+}
+
+static int __exit sh_hdmi_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
+ struct sh_hdmi *hdmi = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0);
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ free_irq(irq, hdmi);
+ pm_runtime_disable(&pdev->dev);
+ cancel_delayed_work_sync(&hdmi->edid_work);
+ clk_disable(hdmi->hdmi_clk);
+ clk_put(hdmi->hdmi_clk);
+ iounmap(hdmi->base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(hdmi);
+
+ return 0;
+}
+
+static struct platform_driver sh_hdmi_driver = {
+ .remove = __exit_p(sh_hdmi_remove),
+ .driver = {
+ .name = "sh-mobile-hdmi",
+ },
+};
+
+static int __init sh_hdmi_init(void)
+{
+ return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe);
+}
+module_init(sh_hdmi_init);
+
+static void __exit sh_hdmi_exit(void)
+{
+ platform_driver_unregister(&sh_hdmi_driver);
+}
+module_exit(sh_hdmi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 12c451a..d72075a 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -56,6 +56,7 @@ static int lcdc_shared_regs[] = {
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
+ LDHAJR,
NR_CH_REGS };
static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
@@ -74,6 +75,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
[LDVLNR] = 0x450,
[LDVSYNR] = 0x454,
[LDPMR] = 0x460,
+ [LDHAJR] = 0x4a0,
};
static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
@@ -137,6 +139,7 @@ struct sh_mobile_lcdc_priv {
struct clk *dot_clk;
unsigned long lddckr;
struct sh_mobile_lcdc_chan ch[2];
+ struct notifier_block notifier;
unsigned long saved_shared_regs[NR_SHARED_REGS];
int started;
};
@@ -404,6 +407,56 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
}
+static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
+{
+ struct fb_var_screeninfo *var = &ch->info->var;
+ unsigned long h_total, hsync_pos;
+ u32 tmp;
+
+ tmp = ch->ldmt1r_value;
+ tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
+ tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
+ lcdc_write_chan(ch, LDMT1R, tmp);
+
+ /* setup SYS bus */
+ lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
+ lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+
+ /* horizontal configuration */
+ h_total = var->xres + var->hsync_len +
+ var->left_margin + var->right_margin;
+ tmp = h_total / 8; /* HTCN */
+ tmp |= (var->xres / 8) << 16; /* HDCN */
+ lcdc_write_chan(ch, LDHCNR, tmp);
+
+ hsync_pos = var->xres + var->right_margin;
+ tmp = hsync_pos / 8; /* HSYNP */
+ tmp |= (var->hsync_len / 8) << 16; /* HSYNW */
+ lcdc_write_chan(ch, LDHSYNR, tmp);
+
+ /* vertical configuration */
+ tmp = var->yres + var->vsync_len +
+ var->upper_margin + var->lower_margin; /* VTLN */
+ tmp |= var->yres << 16; /* VDLN */
+ lcdc_write_chan(ch, LDVLNR, tmp);
+
+ tmp = var->yres + var->lower_margin; /* VSYNP */
+ tmp |= var->vsync_len << 16; /* VSYNW */
+ lcdc_write_chan(ch, LDVSYNR, tmp);
+
+ /* Adjust horizontal synchronisation for HDMI */
+ tmp = ((var->xres & 7) << 24) |
+ ((h_total & 7) << 16) |
+ ((var->hsync_len & 7) << 8) |
+ hsync_pos;
+ lcdc_write_chan(ch, LDHAJR, tmp);
+}
+
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
@@ -470,49 +523,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
if (!ch->enabled)
continue;
- tmp = ch->ldmt1r_value;
- tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
- tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
- lcdc_write_chan(ch, LDMT1R, tmp);
-
- /* setup SYS bus */
- lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
- lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
-
- /* horizontal configuration */
- tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
- tmp += lcd_cfg->left_margin;
- tmp += lcd_cfg->right_margin;
- tmp /= 8; /* HTCN */
- tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
- lcdc_write_chan(ch, LDHCNR, tmp);
-
- tmp = lcd_cfg->xres;
- tmp += lcd_cfg->right_margin;
- tmp /= 8; /* HSYNP */
- tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
- lcdc_write_chan(ch, LDHSYNR, tmp);
+ sh_mobile_lcdc_geometry(ch);
/* power supply */
lcdc_write_chan(ch, LDPMR, 0);
- /* vertical configuration */
- tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
- tmp += lcd_cfg->upper_margin;
- tmp += lcd_cfg->lower_margin; /* VTLN */
- tmp |= lcd_cfg->yres << 16; /* VDLN */
- lcdc_write_chan(ch, LDVLNR, tmp);
-
- tmp = lcd_cfg->yres;
- tmp += lcd_cfg->lower_margin; /* VSYNP */
- tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
- lcdc_write_chan(ch, LDVSYNR, tmp);
-
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->setup_sys)
ret = board_cfg->setup_sys(board_cfg->board_data, ch,
@@ -577,7 +592,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_on)
- board_cfg->display_on(board_cfg->board_data);
+ board_cfg->display_on(board_cfg->board_data, ch->info);
}
return 0;
@@ -943,6 +958,62 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
.runtime_resume = sh_mobile_lcdc_runtime_resume,
};
+static int sh_mobile_lcdc_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
+ struct fb_var_screeninfo *var;
+
+ if (&ch->lcdc->notifier != nb)
+ return 0;
+
+ dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
+ __func__, action, event->data);
+
+ switch(action) {
+ case FB_EVENT_SUSPEND:
+ if (board_cfg->display_off)
+ board_cfg->display_off(board_cfg->board_data);
+ pm_runtime_put(info->device);
+ break;
+ case FB_EVENT_RESUME:
+ var = &info->var;
+
+ /* HDMI must be enabled before LCDC configuration */
+ if (board_cfg->display_on)
+ board_cfg->display_on(board_cfg->board_data, ch->info);
+
+ /* Check if the new display is not in our modelist */
+ if (ch->info->modelist.next &&
+ !fb_match_mode(var, &ch->info->modelist)) {
+ struct fb_videomode mode;
+ int ret;
+
+ /* Can we handle this display? */
+ if (var->xres > ch->cfg.lcd_cfg.xres ||
+ var->yres > ch->cfg.lcd_cfg.yres)
+ return -ENOMEM;
+
+ /* Add to the modelist */
+ fb_var_to_videomode(&mode, var);
+ ret = fb_add_videomode(&mode, &ch->info->modelist);
+ if (ret < 0)
+ return ret;
+ }
+
+ pm_runtime_get_sync(info->device);
+
+ sh_mobile_lcdc_geometry(ch);
+
+ break;
+ }
+
+ return 0;
+}
+
static int sh_mobile_lcdc_remove(struct platform_device *pdev);
static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
@@ -1020,15 +1091,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ priv->base = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->base)
+ goto err1;
+
error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
if (error) {
dev_err(&pdev->dev, "unable to setup clocks\n");
goto err1;
}
- priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);
-
for (i = 0; i < j; i++) {
+ struct fb_var_screeninfo *var;
+ struct fb_videomode *lcd_cfg;
cfg = &priv->ch[i].cfg;
priv->ch[i].info = framebuffer_alloc(0, &pdev->dev);
@@ -1039,22 +1114,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
}
info = priv->ch[i].info;
+ var = &info->var;
+ lcd_cfg = &cfg->lcd_cfg;
info->fbops = &sh_mobile_lcdc_ops;
- info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
- info->var.yres = cfg->lcd_cfg.yres;
+ var->xres = var->xres_virtual = lcd_cfg->xres;
+ var->yres = lcd_cfg->yres;
/* Default Y virtual resolution is 2x panel size */
- info->var.yres_virtual = info->var.yres * 2;
- info->var.width = cfg->lcd_size_cfg.width;
- info->var.height = cfg->lcd_size_cfg.height;
- info->var.activate = FB_ACTIVATE_NOW;
- error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp);
+ var->yres_virtual = var->yres * 2;
+ var->width = cfg->lcd_size_cfg.width;
+ var->height = cfg->lcd_size_cfg.height;
+ var->activate = FB_ACTIVATE_NOW;
+ var->left_margin = lcd_cfg->left_margin;
+ var->right_margin = lcd_cfg->right_margin;
+ var->upper_margin = lcd_cfg->upper_margin;
+ var->lower_margin = lcd_cfg->lower_margin;
+ var->hsync_len = lcd_cfg->hsync_len;
+ var->vsync_len = lcd_cfg->vsync_len;
+ var->sync = lcd_cfg->sync;
+ var->pixclock = lcd_cfg->pixclock;
+
+ error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
if (error)
break;
info->fix = sh_mobile_lcdc_fix;
- info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
+ info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8);
info->fix.smem_len = info->fix.line_length *
- info->var.yres_virtual;
+ var->yres_virtual;
buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
&priv->ch[i].dma_handle, GFP_KERNEL);
@@ -1119,10 +1205,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
ch->cfg.bpp);
/* deferred io mode: disable clock to save power */
- if (info->fbdefio)
+ if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
sh_mobile_lcdc_clk_off(priv);
}
+ /* Failure ignored */
+ priv->notifier.notifier_call = sh_mobile_lcdc_notify;
+ fb_register_client(&priv->notifier);
+
return 0;
err1:
sh_mobile_lcdc_remove(pdev);
@@ -1136,6 +1226,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
struct fb_info *info;
int i;
+ fb_unregister_client(&priv->notifier);
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
if (priv->ch[i].info && priv->ch[i].info->dev)
unregister_framebuffer(priv->ch[i].info);
diff --git a/drivers/video/sunxvr1000.c b/drivers/video/sunxvr1000.c
index 7288934..5dbe06a 100644
--- a/drivers/video/sunxvr1000.c
+++ b/drivers/video/sunxvr1000.c
@@ -111,7 +111,7 @@ static int __devinit gfb_set_fbinfo(struct gfb_info *gp)
return 0;
}
-static int __devinit gfb_probe(struct of_device *op,
+static int __devinit gfb_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -172,7 +172,7 @@ err_out:
return err;
}
-static int __devexit gfb_remove(struct of_device *op)
+static int __devexit gfb_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct gfb_info *gp = info->par;
diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c
index f375e0d..77ad279 100644
--- a/drivers/video/tcx.c
+++ b/drivers/video/tcx.c
@@ -342,7 +342,7 @@ tcx_init_fix(struct fb_info *info, int linebytes)
info->fix.accel = FB_ACCEL_SUN_TCX;
}
-static void tcx_unmap_regs(struct of_device *op, struct fb_info *info,
+static void tcx_unmap_regs(struct platform_device *op, struct fb_info *info,
struct tcx_par *par)
{
if (par->tec)
@@ -362,7 +362,7 @@ static void tcx_unmap_regs(struct of_device *op, struct fb_info *info,
info->screen_base, info->fix.smem_len);
}
-static int __devinit tcx_probe(struct of_device *op,
+static int __devinit tcx_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -486,7 +486,7 @@ out_err:
return err;
}
-static int __devexit tcx_remove(struct of_device *op)
+static int __devexit tcx_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct tcx_par *par = info->par;
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index 7b8839e..52ec095 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -1977,8 +1977,7 @@ static void __devexit uvesafb_exit(void)
module_exit(uvesafb_exit);
-#define param_get_scroll NULL
-static int param_set_scroll(const char *val, struct kernel_param *kp)
+static int param_set_scroll(const char *val, const struct kernel_param *kp)
{
ypan = 0;
@@ -1993,7 +1992,9 @@ static int param_set_scroll(const char *val, struct kernel_param *kp)
return 0;
}
-
+static struct kernel_param_ops param_ops_scroll = {
+ .set = param_set_scroll,
+};
#define param_check_scroll(name, p) __param_check(name, p, void)
module_param_named(scroll, ypan, scroll, 0);
diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c
index d31dc96..85d76ec 100644
--- a/drivers/video/vt8623fb.c
+++ b/drivers/video/vt8623fb.c
@@ -726,7 +726,9 @@ static int __devinit vt8623_pci_probe(struct pci_dev *dev, const struct pci_devi
/* Prepare startup mode */
+ kparam_block_sysfs_write(mode_option);
rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8);
+ kparam_unblock_sysfs_write(mode_option);
if (! ((rc == 1) || (rc == 2))) {
rc = -EINVAL;
dev_err(info->device, "mode %s not found\n", mode_option);
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index 29b5daa..0c9ce88 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -397,7 +397,7 @@ static int xilinxfb_release(struct device *dev)
*/
static int __devinit
-xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match)
+xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match)
{
const u32 *prop;
u32 *p;
@@ -477,7 +477,7 @@ xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match)
return -ENODEV;
}
-static int __devexit xilinxfb_of_remove(struct of_device *op)
+static int __devexit xilinxfb_of_remove(struct platform_device *op)
{
return xilinxfb_release(&op->dev);
}
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b04b184..b036677 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -73,6 +73,13 @@ config WM8350_WATCHDOG
# ARM Architecture
+config ARM_SP805_WATCHDOG
+ tristate "ARM SP805 Watchdog"
+ depends on ARM_AMBA
+ help
+ ARM Primecell SP805 Watchdog timer. This will reboot your system when
+ the timeout is reached.
+
config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog"
depends on ARCH_AT91RM9200
@@ -401,6 +408,17 @@ config ALIM7101_WDT
Most people will say N.
+config F71808E_WDT
+ tristate "Fintek F71808E and F71882FG Watchdog"
+ depends on X86 && EXPERIMENTAL
+ help
+ This is the driver for the hardware watchdog on the Fintek
+ F71808E and F71882FG Super I/O controllers.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module. The module will be called f71808e_wdt.
+
+
config GEODE_WDT
tristate "AMD Geode CS5535/CS5536 Watchdog"
depends on CS5535_MFGPT
@@ -556,16 +574,21 @@ config IT87_WDT
be called it87_wdt.
config HP_WATCHDOG
- tristate "HP Proliant iLO 2 Hardware Watchdog Timer"
+ tristate "HP Proliant iLO2+ Hardware Watchdog Timer"
depends on X86
help
A software monitoring watchdog and NMI sourcing driver. This driver
- will detect lockups and provide stack trace. Also, when an NMI
- occurs this driver will make the necessary BIOS calls to log
- the cause of the NMI. This is a driver that will only load on a
- HP ProLiant system with a minimum of iLO2 support.
- To compile this driver as a module, choose M here: the
- module will be called hpwdt.
+ will detect lockups and provide a stack trace. This is a driver that
+ will only load on a HP ProLiant system with a minimum of iLO2 support.
+ To compile this driver as a module, choose M here: the module will be
+ called hpwdt.
+
+config HPWDT_NMI_DECODING
+ bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
+ depends on HP_WATCHDOG
+ help
+ When an NMI occurs this feature will make the necessary BIOS calls to
+ log the cause of the NMI.
config SC1200_WDT
tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e30289a..8374503 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ALPHA Architecture
# ARM Architecture
+obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
@@ -66,6 +67,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
+obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
index 30a2512..566343b 100644
--- a/drivers/watchdog/cpwd.c
+++ b/drivers/watchdog/cpwd.c
@@ -526,7 +526,7 @@ static const struct file_operations cpwd_fops = {
.release = cpwd_release,
};
-static int __devinit cpwd_probe(struct of_device *op,
+static int __devinit cpwd_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *options;
@@ -639,7 +639,7 @@ out_free:
goto out;
}
-static int __devexit cpwd_remove(struct of_device *op)
+static int __devexit cpwd_remove(struct platform_device *op)
{
struct cpwd *p = dev_get_drvdata(&op->dev);
int i;
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
new file mode 100644
index 0000000..7e5c266
--- /dev/null
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -0,0 +1,768 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
+ * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
+ * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> *
+ * *
+ * 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/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define DRVNAME "f71808e_wdt"
+
+#define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */
+#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_DEVREV 0x22 /* Device revision */
+#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
+#define SIO_F71808_ID 0x0901 /* Chipset ID */
+#define SIO_F71858_ID 0x0507 /* Chipset ID */
+#define SIO_F71862_ID 0x0601 /* Chipset ID */
+#define SIO_F71882_ID 0x0541 /* Chipset ID */
+#define SIO_F71889_ID 0x0723 /* Chipset ID */
+
+#define F71882FG_REG_START 0x01
+
+#define F71808FG_REG_WDO_CONF 0xf0
+#define F71808FG_REG_WDT_CONF 0xf5
+#define F71808FG_REG_WD_TIME 0xf6
+
+#define F71808FG_FLAG_WDOUT_EN 7
+
+#define F71808FG_FLAG_WDTMOUT_STS 5
+#define F71808FG_FLAG_WD_EN 5
+#define F71808FG_FLAG_WD_PULSE 4
+#define F71808FG_FLAG_WD_UNIT 3
+
+/* Default values */
+#define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */
+#define WATCHDOG_MAX_TIMEOUT (60 * 255)
+#define WATCHDOG_PULSE_WIDTH 125 /* 125 ms, default pulse width for
+ watchdog signal */
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static const int max_timeout = WATCHDOG_MAX_TIMEOUT;
+static int timeout = 60; /* default timeout in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <="
+ __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
+module_param(pulse_width, uint, 0);
+MODULE_PARM_DESC(pulse_width,
+ "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
+ " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0444);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+static unsigned int start_withtimeout;
+module_param(start_withtimeout, uint, 0);
+MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
+ " given initial timeout. Zero (default) disables this feature.");
+
+enum chips { f71808fg, f71858fg, f71862fg, f71882fg, f71889fg };
+
+static const char *f71808e_names[] = {
+ "f71808fg",
+ "f71858fg",
+ "f71862fg",
+ "f71882fg",
+ "f71889fg",
+};
+
+/* Super-I/O Function prototypes */
+static inline int superio_inb(int base, int reg);
+static inline int superio_inw(int base, int reg);
+static inline void superio_outb(int base, int reg, u8 val);
+static inline void superio_set_bit(int base, int reg, int bit);
+static inline void superio_clear_bit(int base, int reg, int bit);
+static inline int superio_enter(int base);
+static inline void superio_select(int base, int ld);
+static inline void superio_exit(int base);
+
+struct watchdog_data {
+ unsigned short sioaddr;
+ enum chips type;
+ unsigned long opened;
+ struct mutex lock;
+ char expect_close;
+ struct watchdog_info ident;
+
+ unsigned short timeout;
+ u8 timer_val; /* content for the wd_time register */
+ char minutes_mode;
+ u8 pulse_val; /* pulse width flag */
+ char pulse_mode; /* enable pulse output mode? */
+ char caused_reboot; /* last reboot was by the watchdog */
+};
+
+static struct watchdog_data watchdog = {
+ .lock = __MUTEX_INITIALIZER(watchdog.lock),
+};
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+ int val;
+ val = superio_inb(base, reg) << 8;
+ val |= superio_inb(base, reg + 1);
+ return val;
+}
+
+static inline void superio_outb(int base, int reg, u8 val)
+{
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static inline void superio_set_bit(int base, int reg, int bit)
+{
+ unsigned long val = superio_inb(base, reg);
+ __set_bit(bit, &val);
+ superio_outb(base, reg, val);
+}
+
+static inline void superio_clear_bit(int base, int reg, int bit)
+{
+ unsigned long val = superio_inb(base, reg);
+ __clear_bit(bit, &val);
+ superio_outb(base, reg, val);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident */
+ if (!request_muxed_region(base, 2, DRVNAME)) {
+ printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
+ (int)base);
+ return -EBUSY;
+ }
+
+ /* according to the datasheet the key must be send twice! */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+static int watchdog_set_timeout(int timeout)
+{
+ if (timeout <= 0
+ || timeout > max_timeout) {
+ printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&watchdog.lock);
+
+ watchdog.timeout = timeout;
+ if (timeout > 0xff) {
+ watchdog.timer_val = DIV_ROUND_UP(timeout, 60);
+ watchdog.minutes_mode = true;
+ } else {
+ watchdog.timer_val = timeout;
+ watchdog.minutes_mode = false;
+ }
+
+ mutex_unlock(&watchdog.lock);
+
+ return 0;
+}
+
+static int watchdog_set_pulse_width(unsigned int pw)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+
+ if (pw <= 1) {
+ watchdog.pulse_val = 0;
+ } else if (pw <= 25) {
+ watchdog.pulse_val = 1;
+ } else if (pw <= 125) {
+ watchdog.pulse_val = 2;
+ } else if (pw <= 5000) {
+ watchdog.pulse_val = 3;
+ } else {
+ printk(KERN_ERR DRVNAME ": pulse width out of range\n");
+ err = -EINVAL;
+ goto exit_unlock;
+ }
+
+ watchdog.pulse_mode = pw;
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return err;
+}
+
+static int watchdog_keepalive(void)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ if (watchdog.minutes_mode)
+ /* select minutes for timer units */
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ else
+ /* select seconds for timer units */
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+
+ /* Set timer value */
+ superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME,
+ watchdog.timer_val);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return err;
+}
+
+static int watchdog_start(void)
+{
+ /* Make sure we don't die as soon as the watchdog is enabled below */
+ int err = watchdog_keepalive();
+ if (err)
+ return err;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ /* Watchdog pin configuration */
+ switch (watchdog.type) {
+ case f71808fg:
+ /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
+ superio_clear_bit(watchdog.sioaddr, 0x2a, 3);
+ superio_clear_bit(watchdog.sioaddr, 0x2b, 3);
+ break;
+
+ case f71882fg:
+ /* Set pin 56 to WDTRST# */
+ superio_set_bit(watchdog.sioaddr, 0x29, 1);
+ break;
+
+ default:
+ /*
+ * 'default' label to shut up the compiler and catch
+ * programmer errors
+ */
+ err = -ENODEV;
+ goto exit_superio;
+ }
+
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+ superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF,
+ F71808FG_FLAG_WDOUT_EN);
+
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_EN);
+
+ if (watchdog.pulse_mode) {
+ /* Select "pulse" output mode with given duration */
+ u8 wdt_conf = superio_inb(watchdog.sioaddr,
+ F71808FG_REG_WDT_CONF);
+
+ /* Set WD_PSWIDTH bits (1:0) */
+ wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03);
+ /* Set WD_PULSE to "pulse" mode */
+ wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
+
+ superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ wdt_conf);
+ } else {
+ /* Select "level" output mode */
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_PULSE);
+ }
+
+exit_superio:
+ superio_exit(watchdog.sioaddr);
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+
+ return err;
+}
+
+static int watchdog_stop(void)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_EN);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+
+ return err;
+}
+
+static int watchdog_get_status(void)
+{
+ int status = 0;
+
+ mutex_lock(&watchdog.lock);
+ status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0;
+ mutex_unlock(&watchdog.lock);
+
+ return status;
+}
+
+static bool watchdog_is_running(void)
+{
+ /*
+ * if we fail to determine the watchdog's status assume it to be
+ * running to be on the safe side
+ */
+ bool is_running = true;
+
+ mutex_lock(&watchdog.lock);
+ if (superio_enter(watchdog.sioaddr))
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0))
+ && (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF)
+ & F71808FG_FLAG_WD_EN);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return is_running;
+}
+
+/* /dev/watchdog api */
+
+static int watchdog_open(struct inode *inode, struct file *file)
+{
+ int err;
+
+ /* If the watchdog is alive we don't need to start it again */
+ if (test_and_set_bit(0, &watchdog.opened))
+ return -EBUSY;
+
+ err = watchdog_start();
+ if (err) {
+ clear_bit(0, &watchdog.opened);
+ return err;
+ }
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ watchdog.expect_close = 0;
+ return nonseekable_open(inode, file);
+}
+
+static int watchdog_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &watchdog.opened);
+
+ if (!watchdog.expect_close) {
+ watchdog_keepalive();
+ printk(KERN_CRIT DRVNAME
+ ": Unexpected close, not stopping watchdog!\n");
+ } else if (!nowayout) {
+ watchdog_stop();
+ }
+ return 0;
+}
+
+/*
+ * watchdog_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t watchdog_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ bool expect_close = false;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ expect_close = (c == 'V');
+ }
+
+ /* Properly order writes across fork()ed processes */
+ mutex_lock(&watchdog.lock);
+ watchdog.expect_close = expect_close;
+ mutex_unlock(&watchdog.lock);
+ }
+
+ /* someone wrote to us, we should restart timer */
+ watchdog_keepalive();
+ }
+ return count;
+}
+
+/*
+ * watchdog_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ */
+static long watchdog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int status;
+ int new_options;
+ int new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &watchdog.ident,
+ sizeof(watchdog.ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ status = watchdog_get_status();
+ if (status < 0)
+ return status;
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD)
+ watchdog_stop();
+
+ if (new_options & WDIOS_ENABLECARD)
+ return watchdog_start();
+
+
+ case WDIOC_KEEPALIVE:
+ watchdog_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+
+ if (watchdog_set_timeout(new_timeout))
+ return -EINVAL;
+
+ watchdog_keepalive();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(watchdog.timeout, uarg.i);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+static int watchdog_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ watchdog_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = watchdog_open,
+ .release = watchdog_release,
+ .write = watchdog_write,
+ .unlocked_ioctl = watchdog_ioctl,
+};
+
+static struct miscdevice watchdog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &watchdog_fops,
+};
+
+static struct notifier_block watchdog_notifier = {
+ .notifier_call = watchdog_notify_sys,
+};
+
+static int __init watchdog_init(int sioaddr)
+{
+ int wdt_conf, err = 0;
+
+ /* No need to lock watchdog.lock here because no entry points
+ * into the module have been registered yet.
+ */
+ watchdog.sioaddr = sioaddr;
+ watchdog.ident.options = WDIOC_SETTIMEOUT
+ | WDIOF_MAGICCLOSE
+ | WDIOF_KEEPALIVEPING;
+
+ snprintf(watchdog.ident.identity,
+ sizeof(watchdog.ident.identity), "%s watchdog",
+ f71808e_names[watchdog.type]);
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
+ watchdog.caused_reboot = wdt_conf & F71808FG_FLAG_WDTMOUT_STS;
+
+ superio_exit(sioaddr);
+
+ err = watchdog_set_timeout(timeout);
+ if (err)
+ return err;
+ err = watchdog_set_pulse_width(pulse_width);
+ if (err)
+ return err;
+
+ err = register_reboot_notifier(&watchdog_notifier);
+ if (err)
+ return err;
+
+ err = misc_register(&watchdog_miscdev);
+ if (err) {
+ printk(KERN_ERR DRVNAME
+ ": cannot register miscdev on minor=%d\n",
+ watchdog_miscdev.minor);
+ goto exit_reboot;
+ }
+
+ if (start_withtimeout) {
+ if (start_withtimeout <= 0
+ || start_withtimeout > max_timeout) {
+ printk(KERN_ERR DRVNAME
+ ": starting timeout out of range\n");
+ err = -EINVAL;
+ goto exit_miscdev;
+ }
+
+ err = watchdog_start();
+ if (err) {
+ printk(KERN_ERR DRVNAME
+ ": cannot start watchdog timer\n");
+ goto exit_miscdev;
+ }
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ if (start_withtimeout > 0xff) {
+ /* select minutes for timer units */
+ superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ superio_outb(sioaddr, F71808FG_REG_WD_TIME,
+ DIV_ROUND_UP(start_withtimeout, 60));
+ } else {
+ /* select seconds for timer units */
+ superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ superio_outb(sioaddr, F71808FG_REG_WD_TIME,
+ start_withtimeout);
+ }
+
+ superio_exit(sioaddr);
+ mutex_unlock(&watchdog.lock);
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ printk(KERN_INFO DRVNAME
+ ": watchdog started with initial timeout of %u sec\n",
+ start_withtimeout);
+ }
+
+ return 0;
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+exit_miscdev:
+ misc_deregister(&watchdog_miscdev);
+exit_reboot:
+ unregister_reboot_notifier(&watchdog_notifier);
+
+ return err;
+}
+
+static int __init f71808e_find(int sioaddr)
+{
+ u16 devid;
+ int err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ devid = superio_inw(sioaddr, SIO_REG_MANID);
+ if (devid != SIO_FINTEK_ID) {
+ pr_debug(DRVNAME ": Not a Fintek device\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_F71808_ID:
+ watchdog.type = f71808fg;
+ break;
+ case SIO_F71882_ID:
+ watchdog.type = f71882fg;
+ break;
+ case SIO_F71862_ID:
+ case SIO_F71889_ID:
+ /* These have a watchdog, though it isn't implemented (yet). */
+ err = -ENOSYS;
+ goto exit;
+ case SIO_F71858_ID:
+ /* Confirmed (by datasheet) not to have a watchdog. */
+ err = -ENODEV;
+ goto exit;
+ default:
+ printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n",
+ (unsigned int)devid);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n",
+ f71808e_names[watchdog.type],
+ (int)superio_inb(sioaddr, SIO_REG_DEVREV));
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init f71808e_init(void)
+{
+ static const unsigned short addrs[] = { 0x2e, 0x4e };
+ int err = -ENODEV;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(addrs); i++) {
+ err = f71808e_find(addrs[i]);
+ if (err == 0)
+ break;
+ }
+ if (i == ARRAY_SIZE(addrs))
+ return err;
+
+ return watchdog_init(addrs[i]);
+}
+
+static void __exit f71808e_exit(void)
+{
+ if (watchdog_is_running()) {
+ printk(KERN_WARNING DRVNAME
+ ": Watchdog timer still running, stopping it\n");
+ watchdog_stop();
+ }
+ misc_deregister(&watchdog_miscdev);
+ unregister_reboot_notifier(&watchdog_notifier);
+}
+
+MODULE_DESCRIPTION("F71808E Watchdog Driver");
+MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>");
+MODULE_LICENSE("GPL");
+
+module_init(f71808e_init);
+module_exit(f71808e_exit);
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c
index 1df284f..9c21d19 100644
--- a/drivers/watchdog/gef_wdt.c
+++ b/drivers/watchdog/gef_wdt.c
@@ -260,7 +260,7 @@ static struct miscdevice gef_wdt_miscdev = {
};
-static int __devinit gef_wdt_probe(struct of_device *dev,
+static int __devinit gef_wdt_probe(struct platform_device *dev,
const struct of_device_id *match)
{
int timeout = 10;
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 809e716..3d77116 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -16,38 +16,55 @@
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/nmi.h>
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
-#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/kdebug.h>
#include <linux/moduleparam.h>
-#include <linux/notifier.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
-#include <linux/reboot.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
+#ifdef CONFIG_HPWDT_NMI_DECODING
#include <linux/dmi.h>
-#include <linux/efi.h>
-#include <linux/string.h>
-#include <linux/bootmem.h>
-#include <asm/desc.h>
+#include <linux/spinlock.h>
+#include <linux/nmi.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
#include <asm/cacheflush.h>
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+#define HPWDT_VERSION "1.2.0"
+#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
+#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
+#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
+#define DEFAULT_MARGIN 30
+
+static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
+static unsigned int reload; /* the computed soft_margin */
+static int nowayout = WATCHDOG_NOWAYOUT;
+static char expect_release;
+static unsigned long hpwdt_is_open;
+
+static void __iomem *pci_mem_addr; /* the PCI-memory address */
+static unsigned long __iomem *hpwdt_timer_reg;
+static unsigned long __iomem *hpwdt_timer_con;
+static struct pci_device_id hpwdt_devices[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
+ { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
+ {0}, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, hpwdt_devices);
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
#define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */
#define CRU_BIOS_SIGNATURE_VALUE 0x55524324
#define PCI_BIOS32_PARAGRAPH_LEN 16
#define PCI_ROM_BASE1 0x000F0000
#define ROM_SIZE 0x10000
-#define HPWDT_VERSION "1.1.1"
struct bios32_service_dir {
u32 signature;
@@ -112,37 +129,17 @@ struct cmn_registers {
u32 reflags;
} __attribute__((packed));
-#define DEFAULT_MARGIN 30
-static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
-static unsigned int reload; /* the computed soft_margin */
-static int nowayout = WATCHDOG_NOWAYOUT;
-static char expect_release;
-static unsigned long hpwdt_is_open;
+static unsigned int hpwdt_nmi_decoding;
static unsigned int allow_kdump;
-static unsigned int hpwdt_nmi_sourcing;
static unsigned int priority; /* hpwdt at end of die_notify list */
-
-static void __iomem *pci_mem_addr; /* the PCI-memory address */
-static unsigned long __iomem *hpwdt_timer_reg;
-static unsigned long __iomem *hpwdt_timer_con;
-
static DEFINE_SPINLOCK(rom_lock);
-
static void *cru_rom_addr;
-
static struct cmn_registers cmn_regs;
-static struct pci_device_id hpwdt_devices[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) },
- { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) },
- {0}, /* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, hpwdt_devices);
-
extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs,
unsigned long *pRomEntry);
-#ifndef CONFIG_X86_64
+#ifdef CONFIG_X86_32
/* --32 Bit Bios------------------------------------------------------------ */
#define HPWDT_ARCH 32
@@ -246,8 +243,8 @@ static int __devinit cru_detect(unsigned long map_entry,
physical_bios_offset);
printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n",
cru_length);
- printk(KERN_DEBUG "hpwdt: CRU Mapped Address: 0x%x\n",
- (unsigned int)&cru_rom_addr);
+ printk(KERN_DEBUG "hpwdt: CRU Mapped Address: %p\n",
+ &cru_rom_addr);
}
iounmap(bios32_map);
return retval;
@@ -331,8 +328,9 @@ static int __devinit detect_cru_service(void)
iounmap(p);
return rc;
}
-
-#else
+/* ------------------------------------------------------------------------- */
+#endif /* CONFIG_X86_32 */
+#ifdef CONFIG_X86_64
/* --64 Bit Bios------------------------------------------------------------ */
#define HPWDT_ARCH 64
@@ -410,17 +408,16 @@ static int __devinit detect_cru_service(void)
/* if cru_rom_addr has been set then we found a CRU service */
return ((cru_rom_addr != NULL) ? 0 : -ENODEV);
}
-
/* ------------------------------------------------------------------------- */
-
-#endif
+#endif /* CONFIG_X86_64 */
+#endif /* CONFIG_HPWDT_NMI_DECODING */
/*
* Watchdog operations
*/
static void hpwdt_start(void)
{
- reload = (soft_margin * 1000) / 128;
+ reload = SECS_TO_TICKS(soft_margin);
iowrite16(reload, hpwdt_timer_reg);
iowrite16(0x85, hpwdt_timer_con);
}
@@ -441,8 +438,7 @@ static void hpwdt_ping(void)
static int hpwdt_change_timer(int new_margin)
{
- /* Arbitrary, can't find the card's limits */
- if (new_margin < 5 || new_margin > 600) {
+ if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) {
printk(KERN_WARNING
"hpwdt: New value passed in is invalid: %d seconds.\n",
new_margin);
@@ -453,11 +449,17 @@ static int hpwdt_change_timer(int new_margin)
printk(KERN_DEBUG
"hpwdt: New timer passed in is %d seconds.\n",
new_margin);
- reload = (soft_margin * 1000) / 128;
+ reload = SECS_TO_TICKS(soft_margin);
return 0;
}
+static int hpwdt_time_left(void)
+{
+ return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
+}
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
/*
* NMI Handler
*/
@@ -468,26 +470,29 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason,
static int die_nmi_called;
if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI)
- return NOTIFY_OK;
-
- if (hpwdt_nmi_sourcing) {
- spin_lock_irqsave(&rom_lock, rom_pl);
- if (!die_nmi_called)
- asminline_call(&cmn_regs, cru_rom_addr);
- die_nmi_called = 1;
- spin_unlock_irqrestore(&rom_lock, rom_pl);
- if (cmn_regs.u1.ral == 0) {
- printk(KERN_WARNING "hpwdt: An NMI occurred, "
- "but unable to determine source.\n");
- } else {
- if (allow_kdump)
- hpwdt_stop();
- panic("An NMI occurred, please see the Integrated "
- "Management Log for details.\n");
- }
+ goto out;
+
+ if (!hpwdt_nmi_decoding)
+ goto out;
+
+ spin_lock_irqsave(&rom_lock, rom_pl);
+ if (!die_nmi_called)
+ asminline_call(&cmn_regs, cru_rom_addr);
+ die_nmi_called = 1;
+ spin_unlock_irqrestore(&rom_lock, rom_pl);
+ if (cmn_regs.u1.ral == 0) {
+ printk(KERN_WARNING "hpwdt: An NMI occurred, "
+ "but unable to determine source.\n");
+ } else {
+ if (allow_kdump)
+ hpwdt_stop();
+ panic("An NMI occurred, please see the Integrated "
+ "Management Log for details.\n");
}
+out:
return NOTIFY_OK;
}
+#endif /* CONFIG_HPWDT_NMI_DECODING */
/*
* /dev/watchdog handling
@@ -557,7 +562,7 @@ static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
- .identity = "HP iLO2 HW Watchdog Timer",
+ .identity = "HP iLO2+ HW Watchdog Timer",
};
static long hpwdt_ioctl(struct file *file, unsigned int cmd,
@@ -599,6 +604,10 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd,
case WDIOC_GETTIMEOUT:
ret = put_user(soft_margin, p);
break;
+
+ case WDIOC_GETTIMELEFT:
+ ret = put_user(hpwdt_time_left(), p);
+ break;
}
return ret;
}
@@ -621,80 +630,45 @@ static struct miscdevice hpwdt_miscdev = {
.fops = &hpwdt_fops,
};
+#ifdef CONFIG_HPWDT_NMI_DECODING
static struct notifier_block die_notifier = {
.notifier_call = hpwdt_pretimeout,
.priority = 0,
};
+#endif /* CONFIG_HPWDT_NMI_DECODING */
/*
* Init & Exit
*/
+#ifdef CONFIG_HPWDT_NMI_DECODING
#ifdef ARCH_HAS_NMI_WATCHDOG
-static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev)
+static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
{
/*
* If nmi_watchdog is turned off then we can turn on
- * our nmi sourcing capability.
+ * our nmi decoding capability.
*/
if (!nmi_watchdog_active())
- hpwdt_nmi_sourcing = 1;
+ hpwdt_nmi_decoding = 1;
else
- dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this "
+ dev_warn(&dev->dev, "NMI decoding is disabled. To enable this "
"functionality you must reboot with nmi_watchdog=0 "
"and load the hpwdt driver with priority=1.\n");
}
#else
-static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev)
+static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
{
- dev_warn(&dev->dev, "NMI sourcing is disabled. "
+ dev_warn(&dev->dev, "NMI decoding is disabled. "
"Your kernel does not support a NMI Watchdog.\n");
}
-#endif
+#endif /* ARCH_HAS_NMI_WATCHDOG */
-static int __devinit hpwdt_init_one(struct pci_dev *dev,
- const struct pci_device_id *ent)
+static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
{
int retval;
/*
- * Check if we can do NMI sourcing or not
- */
- hpwdt_check_nmi_sourcing(dev);
-
- /*
- * First let's find out if we are on an iLO2 server. We will
- * not run on a legacy ASM box.
- * So we only support the G5 ProLiant servers and higher.
- */
- if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) {
- dev_warn(&dev->dev,
- "This server does not have an iLO2 ASIC.\n");
- return -ENODEV;
- }
-
- if (pci_enable_device(dev)) {
- dev_warn(&dev->dev,
- "Not possible to enable PCI Device: 0x%x:0x%x.\n",
- ent->vendor, ent->device);
- return -ENODEV;
- }
-
- pci_mem_addr = pci_iomap(dev, 1, 0x80);
- if (!pci_mem_addr) {
- dev_warn(&dev->dev,
- "Unable to detect the iLO2 server memory.\n");
- retval = -ENOMEM;
- goto error_pci_iomap;
- }
- hpwdt_timer_reg = pci_mem_addr + 0x70;
- hpwdt_timer_con = pci_mem_addr + 0x72;
-
- /* Make sure that we have a valid soft_margin */
- if (hpwdt_change_timer(soft_margin))
- hpwdt_change_timer(DEFAULT_MARGIN);
-
- /*
* We need to map the ROM to get the CRU service.
* For 32 bit Operating Systems we need to go through the 32 Bit
* BIOS Service Directory
@@ -705,7 +679,7 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
dev_warn(&dev->dev,
"Unable to detect the %d Bit CRU Service.\n",
HPWDT_ARCH);
- goto error_get_cru;
+ return retval;
}
/*
@@ -728,9 +702,87 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
dev_warn(&dev->dev,
"Unable to register a die notifier (err=%d).\n",
retval);
- goto error_die_notifier;
+ if (cru_rom_addr)
+ iounmap(cru_rom_addr);
}
+ dev_info(&dev->dev,
+ "HP Watchdog Timer Driver: NMI decoding initialized"
+ ", allow kernel dump: %s (default = 0/OFF)"
+ ", priority: %s (default = 0/LAST).\n",
+ (allow_kdump == 0) ? "OFF" : "ON",
+ (priority == 0) ? "LAST" : "FIRST");
+ return 0;
+}
+
+static void __devexit hpwdt_exit_nmi_decoding(void)
+{
+ unregister_die_notifier(&die_notifier);
+ if (cru_rom_addr)
+ iounmap(cru_rom_addr);
+}
+#else /* !CONFIG_HPWDT_NMI_DECODING */
+static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+{
+}
+
+static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
+{
+ return 0;
+}
+
+static void __devexit hpwdt_exit_nmi_decoding(void)
+{
+}
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+static int __devinit hpwdt_init_one(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ int retval;
+
+ /*
+ * Check if we can do NMI decoding or not
+ */
+ hpwdt_check_nmi_decoding(dev);
+
+ /*
+ * First let's find out if we are on an iLO2+ server. We will
+ * not run on a legacy ASM box.
+ * So we only support the G5 ProLiant servers and higher.
+ */
+ if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) {
+ dev_warn(&dev->dev,
+ "This server does not have an iLO2+ ASIC.\n");
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(dev)) {
+ dev_warn(&dev->dev,
+ "Not possible to enable PCI Device: 0x%x:0x%x.\n",
+ ent->vendor, ent->device);
+ return -ENODEV;
+ }
+
+ pci_mem_addr = pci_iomap(dev, 1, 0x80);
+ if (!pci_mem_addr) {
+ dev_warn(&dev->dev,
+ "Unable to detect the iLO2+ server memory.\n");
+ retval = -ENOMEM;
+ goto error_pci_iomap;
+ }
+ hpwdt_timer_reg = pci_mem_addr + 0x70;
+ hpwdt_timer_con = pci_mem_addr + 0x72;
+
+ /* Make sure that we have a valid soft_margin */
+ if (hpwdt_change_timer(soft_margin))
+ hpwdt_change_timer(DEFAULT_MARGIN);
+
+ /* Initialize NMI Decoding functionality */
+ retval = hpwdt_init_nmi_decoding(dev);
+ if (retval != 0)
+ goto error_init_nmi_decoding;
+
retval = misc_register(&hpwdt_miscdev);
if (retval < 0) {
dev_warn(&dev->dev,
@@ -739,23 +791,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
goto error_misc_register;
}
- printk(KERN_INFO
- "hp Watchdog Timer Driver: %s"
- ", timer margin: %d seconds (nowayout=%d)"
- ", allow kernel dump: %s (default = 0/OFF)"
- ", priority: %s (default = 0/LAST).\n",
- HPWDT_VERSION, soft_margin, nowayout,
- (allow_kdump == 0) ? "OFF" : "ON",
- (priority == 0) ? "LAST" : "FIRST");
-
+ dev_info(&dev->dev, "HP Watchdog Timer Driver: %s"
+ ", timer margin: %d seconds (nowayout=%d).\n",
+ HPWDT_VERSION, soft_margin, nowayout);
return 0;
error_misc_register:
- unregister_die_notifier(&die_notifier);
-error_die_notifier:
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
-error_get_cru:
+ hpwdt_exit_nmi_decoding();
+error_init_nmi_decoding:
pci_iounmap(dev, pci_mem_addr);
error_pci_iomap:
pci_disable_device(dev);
@@ -768,10 +811,7 @@ static void __devexit hpwdt_exit(struct pci_dev *dev)
hpwdt_stop();
misc_deregister(&hpwdt_miscdev);
- unregister_die_notifier(&die_notifier);
-
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
+ hpwdt_exit_nmi_decoding();
pci_iounmap(dev, pci_mem_addr);
pci_disable_device(dev);
}
@@ -802,16 +842,18 @@ MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(soft_margin, int, 0);
MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
-module_param(allow_kdump, int, 0);
-MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
-
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#ifdef CONFIG_HPWDT_NMI_DECODING
+module_param(allow_kdump, int, 0);
+MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
+
module_param(priority, int, 0);
MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last"
" (default = 0/Last)\n");
+#endif /* !CONFIG_HPWDT_NMI_DECODING */
module_init(hpwdt_init);
module_exit(hpwdt_cleanup);
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 4cda64d..8fa213c 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -185,7 +185,7 @@ static struct miscdevice mpc8xxx_wdt_miscdev = {
.fops = &mpc8xxx_wdt_fops,
};
-static int __devinit mpc8xxx_wdt_probe(struct of_device *ofdev,
+static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int ret;
@@ -238,7 +238,7 @@ err_unmap:
return ret;
}
-static int __devexit mpc8xxx_wdt_remove(struct of_device *ofdev)
+static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev)
{
mpc8xxx_wdt_pr_warn("watchdog removed");
del_timer_sync(&wdt_timer);
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
index 4082b4a..3faee1a 100644
--- a/drivers/watchdog/riowd.c
+++ b/drivers/watchdog/riowd.c
@@ -172,7 +172,7 @@ static struct miscdevice riowd_miscdev = {
.fops = &riowd_fops
};
-static int __devinit riowd_probe(struct of_device *op,
+static int __devinit riowd_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct riowd *p;
@@ -219,7 +219,7 @@ out:
return err;
}
-static int __devexit riowd_remove(struct of_device *op)
+static int __devexit riowd_remove(struct platform_device *op)
{
struct riowd *p = dev_get_drvdata(&op->dev);
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 3009325..ae53662 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -532,21 +532,22 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
- s3c2410wdt_cpufreq_deregister();
-
- release_resource(wdt_mem);
- kfree(wdt_mem);
- wdt_mem = NULL;
+ misc_deregister(&s3c2410wdt_miscdev);
- free_irq(wdt_irq->start, dev);
- wdt_irq = NULL;
+ s3c2410wdt_cpufreq_deregister();
clk_disable(wdt_clock);
clk_put(wdt_clock);
wdt_clock = NULL;
+ free_irq(wdt_irq->start, dev);
+ wdt_irq = NULL;
+
iounmap(wdt_base);
- misc_deregister(&s3c2410wdt_miscdev);
+
+ release_resource(wdt_mem);
+ kfree(wdt_mem);
+ wdt_mem = NULL;
return 0;
}
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
index 9c40f48..0461858 100644
--- a/drivers/watchdog/sch311x_wdt.c
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -425,6 +425,8 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
val = therm_trip ? 0x06 : 0x04;
outb(val, sch311x_wdt_data.runtime_reg + RESGEN);
+ sch311x_wdt_miscdev.parent = dev;
+
err = misc_register(&sch311x_wdt_miscdev);
if (err != 0) {
dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n",
@@ -432,8 +434,6 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
goto exit_release_region3;
}
- sch311x_wdt_miscdev.parent = dev;
-
dev_info(dev,
"SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
new file mode 100644
index 0000000..9127eda
--- /dev/null
+++ b/drivers/watchdog/sp805_wdt.c
@@ -0,0 +1,387 @@
+/*
+ * drivers/char/watchdog/sp805-wdt.c
+ *
+ * Watchdog driver for ARM SP805 watchdog module
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/device.h>
+#include <linux/resource.h>
+#include <linux/amba/bus.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+/* default timeout in seconds */
+#define DEFAULT_TIMEOUT 60
+
+#define MODULE_NAME "sp805-wdt"
+
+/* watchdog register offsets and masks */
+#define WDTLOAD 0x000
+ #define LOAD_MIN 0x00000001
+ #define LOAD_MAX 0xFFFFFFFF
+#define WDTVALUE 0x004
+#define WDTCONTROL 0x008
+ /* control register masks */
+ #define INT_ENABLE (1 << 0)
+ #define RESET_ENABLE (1 << 1)
+#define WDTINTCLR 0x00C
+#define WDTRIS 0x010
+#define WDTMIS 0x014
+ #define INT_MASK (1 << 0)
+#define WDTLOCK 0xC00
+ #define UNLOCK 0x1ACCE551
+ #define LOCK 0x00000001
+
+/**
+ * struct sp805_wdt: sp805 wdt device structure
+ *
+ * lock: spin lock protecting dev structure and io access
+ * base: base address of wdt
+ * clk: clock structure of wdt
+ * dev: amba device structure of wdt
+ * status: current status of wdt
+ * load_val: load value to be set for current timeout
+ * timeout: current programmed timeout
+ */
+struct sp805_wdt {
+ spinlock_t lock;
+ void __iomem *base;
+ struct clk *clk;
+ struct amba_device *adev;
+ unsigned long status;
+ #define WDT_BUSY 0
+ #define WDT_CAN_BE_CLOSED 1
+ unsigned int load_val;
+ unsigned int timeout;
+};
+
+/* local variables */
+static struct sp805_wdt *wdt;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/* This routine finds load value that will reset system in required timout */
+static void wdt_setload(unsigned int timeout)
+{
+ u64 load, rate;
+
+ rate = clk_get_rate(wdt->clk);
+
+ /*
+ * sp805 runs counter with given value twice, after the end of first
+ * counter it gives an interrupt and then starts counter again. If
+ * interrupt already occured then it resets the system. This is why
+ * load is half of what should be required.
+ */
+ load = div_u64(rate, 2) * timeout - 1;
+
+ load = (load > LOAD_MAX) ? LOAD_MAX : load;
+ load = (load < LOAD_MIN) ? LOAD_MIN : load;
+
+ spin_lock(&wdt->lock);
+ wdt->load_val = load;
+ /* roundup timeout to closest positive integer value */
+ wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
+ spin_unlock(&wdt->lock);
+}
+
+/* returns number of seconds left for reset to occur */
+static u32 wdt_timeleft(void)
+{
+ u64 load, rate;
+
+ rate = clk_get_rate(wdt->clk);
+
+ spin_lock(&wdt->lock);
+ load = readl(wdt->base + WDTVALUE);
+
+ /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
+ if (!(readl(wdt->base + WDTRIS) & INT_MASK))
+ load += wdt->load_val + 1;
+ spin_unlock(&wdt->lock);
+
+ return div_u64(load, rate);
+}
+
+/* enables watchdog timers reset */
+static void wdt_enable(void)
+{
+ spin_lock(&wdt->lock);
+
+ writel(UNLOCK, wdt->base + WDTLOCK);
+ writel(wdt->load_val, wdt->base + WDTLOAD);
+ writel(INT_MASK, wdt->base + WDTINTCLR);
+ writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
+ writel(LOCK, wdt->base + WDTLOCK);
+
+ spin_unlock(&wdt->lock);
+}
+
+/* disables watchdog timers reset */
+static void wdt_disable(void)
+{
+ spin_lock(&wdt->lock);
+
+ writel(UNLOCK, wdt->base + WDTLOCK);
+ writel(0, wdt->base + WDTCONTROL);
+ writel(0, wdt->base + WDTLOAD);
+ writel(LOCK, wdt->base + WDTLOCK);
+
+ spin_unlock(&wdt->lock);
+}
+
+static ssize_t sp805_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ /* Check for Magic Close character */
+ if (c == 'V') {
+ set_bit(WDT_CAN_BE_CLOSED,
+ &wdt->status);
+ break;
+ }
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = MODULE_NAME,
+};
+
+static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ unsigned int timeout;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(timeout, (unsigned int *)arg);
+ if (ret)
+ break;
+
+ wdt_setload(timeout);
+
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(wdt->timeout, (unsigned int *)arg);
+ break;
+ case WDIOC_GETTIMELEFT:
+ ret = put_user(wdt_timeleft(), (unsigned int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int sp805_wdt_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+ if (test_and_set_bit(WDT_BUSY, &wdt->status))
+ return -EBUSY;
+
+ ret = clk_enable(wdt->clk);
+ if (ret) {
+ dev_err(&wdt->adev->dev, "clock enable fail");
+ goto err;
+ }
+
+ wdt_enable();
+
+ /* can not be closed, once enabled */
+ clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
+ return nonseekable_open(inode, file);
+
+err:
+ clear_bit(WDT_BUSY, &wdt->status);
+ return ret;
+}
+
+static int sp805_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
+ clear_bit(WDT_BUSY, &wdt->status);
+ dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
+ return 0;
+ }
+
+ wdt_disable();
+ clk_disable(wdt->clk);
+ clear_bit(WDT_BUSY, &wdt->status);
+
+ return 0;
+}
+
+static const struct file_operations sp805_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sp805_wdt_write,
+ .unlocked_ioctl = sp805_wdt_ioctl,
+ .open = sp805_wdt_open,
+ .release = sp805_wdt_release,
+};
+
+static struct miscdevice sp805_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sp805_wdt_fops,
+};
+
+static int __devinit
+sp805_wdt_probe(struct amba_device *adev, struct amba_id *id)
+{
+ int ret = 0;
+
+ if (!request_mem_region(adev->res.start, resource_size(&adev->res),
+ "sp805_wdt")) {
+ dev_warn(&adev->dev, "Failed to get memory region resource\n");
+ ret = -ENOENT;
+ goto err;
+ }
+
+ wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+ if (!wdt) {
+ dev_warn(&adev->dev, "Kzalloc failed\n");
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ wdt->clk = clk_get(&adev->dev, NULL);
+ if (IS_ERR(wdt->clk)) {
+ dev_warn(&adev->dev, "Clock not found\n");
+ ret = PTR_ERR(wdt->clk);
+ goto err_clk_get;
+ }
+
+ wdt->base = ioremap(adev->res.start, resource_size(&adev->res));
+ if (!wdt->base) {
+ ret = -ENOMEM;
+ dev_warn(&adev->dev, "ioremap fail\n");
+ goto err_ioremap;
+ }
+
+ wdt->adev = adev;
+ spin_lock_init(&wdt->lock);
+ wdt_setload(DEFAULT_TIMEOUT);
+
+ ret = misc_register(&sp805_wdt_miscdev);
+ if (ret < 0) {
+ dev_warn(&adev->dev, "cannot register misc device\n");
+ goto err_misc_register;
+ }
+
+ dev_info(&adev->dev, "registration successful\n");
+ return 0;
+
+err_misc_register:
+ iounmap(wdt->base);
+err_ioremap:
+ clk_put(wdt->clk);
+err_clk_get:
+ kfree(wdt);
+ wdt = NULL;
+err_kzalloc:
+ release_mem_region(adev->res.start, resource_size(&adev->res));
+err:
+ dev_err(&adev->dev, "Probe Failed!!!\n");
+ return ret;
+}
+
+static int __devexit sp805_wdt_remove(struct amba_device *adev)
+{
+ misc_deregister(&sp805_wdt_miscdev);
+ iounmap(wdt->base);
+ clk_put(wdt->clk);
+ kfree(wdt);
+ release_mem_region(adev->res.start, resource_size(&adev->res));
+
+ return 0;
+}
+
+static struct amba_id sp805_wdt_ids[] __initdata = {
+ {
+ .id = 0x00141805,
+ .mask = 0x00ffffff,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver sp805_wdt_driver = {
+ .drv = {
+ .name = MODULE_NAME,
+ },
+ .id_table = sp805_wdt_ids,
+ .probe = sp805_wdt_probe,
+ .remove = __devexit_p(sp805_wdt_remove),
+};
+
+static int __init sp805_wdt_init(void)
+{
+ return amba_driver_register(&sp805_wdt_driver);
+}
+module_init(sp805_wdt_init);
+
+static void __exit sp805_wdt_exit(void)
+{
+ amba_driver_unregister(&sp805_wdt_driver);
+}
+module_exit(sp805_wdt_exit);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Set to 1 to keep watchdog running after device release");
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
index 7b22e3c..6130c88 100644
--- a/drivers/watchdog/wdt_pci.c
+++ b/drivers/watchdog/wdt_pci.c
@@ -60,19 +60,6 @@
#define PFX "wdt_pci: "
-/*
- * Until Access I/O gets their application for a PCI vendor ID approved,
- * I don't think that it's appropriate to move these constants into the
- * regular pci_ids.h file. -- JPN 2000/01/18
- */
-
-#ifndef PCI_VENDOR_ID_ACCESSIO
-#define PCI_VENDOR_ID_ACCESSIO 0x494f
-#endif
-#ifndef PCI_DEVICE_ID_WDG_CSM
-#define PCI_DEVICE_ID_WDG_CSM 0x22c0
-#endif
-
/* We can only use 1 card due to the /dev/watchdog restriction */
static int dev_count;
@@ -743,7 +730,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
static struct pci_device_id wdtpci_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_ACCESSIO,
- .device = PCI_DEVICE_ID_WDG_CSM,
+ .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index 0a88269..60d71e9 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -71,4 +71,9 @@ config XEN_PLATFORM_PCI
initializing xenbus and grant_table when running in a Xen HVM
domain. As a consequence this driver is required to run any Xen PV
frontend on Xen HVM.
+
+config SWIOTLB_XEN
+ def_bool y
+ depends on SWIOTLB
+
endmenu
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index e392fb7..fcaf838 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o
obj-$(CONFIG_XENFS) += xenfs/
obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
obj-$(CONFIG_XEN_PLATFORM_PCI) += platform-pci.o
+obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 1a0d8c2..500290b 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -85,13 +85,6 @@ static struct sys_device balloon_sysdev;
static int register_balloon(struct sys_device *sysdev);
-/*
- * Protects atomic reservation decrease/increase against concurrent increases.
- * Also protects non-atomic updates of current_pages and driver_pages, and
- * balloon lists.
- */
-static DEFINE_SPINLOCK(balloon_lock);
-
static struct balloon_stats balloon_stats;
/* We increase/decrease in batches which fit in a page */
@@ -210,7 +203,7 @@ static int increase_reservation(unsigned long nr_pages)
if (nr_pages > ARRAY_SIZE(frame_list))
nr_pages = ARRAY_SIZE(frame_list);
- spin_lock_irqsave(&balloon_lock, flags);
+ spin_lock_irqsave(&xen_reservation_lock, flags);
page = balloon_first_page();
for (i = 0; i < nr_pages; i++) {
@@ -254,7 +247,7 @@ static int increase_reservation(unsigned long nr_pages)
balloon_stats.current_pages += rc;
out:
- spin_unlock_irqrestore(&balloon_lock, flags);
+ spin_unlock_irqrestore(&xen_reservation_lock, flags);
return rc < 0 ? rc : rc != nr_pages;
}
@@ -299,7 +292,7 @@ static int decrease_reservation(unsigned long nr_pages)
kmap_flush_unused();
flush_tlb_all();
- spin_lock_irqsave(&balloon_lock, flags);
+ spin_lock_irqsave(&xen_reservation_lock, flags);
/* No more mappings: invalidate P2M and add to balloon. */
for (i = 0; i < nr_pages; i++) {
@@ -315,7 +308,7 @@ static int decrease_reservation(unsigned long nr_pages)
balloon_stats.current_pages -= nr_pages;
- spin_unlock_irqrestore(&balloon_lock, flags);
+ spin_unlock_irqrestore(&xen_reservation_lock, flags);
return need_sleep;
}
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
new file mode 100644
index 0000000..54469c3
--- /dev/null
+++ b/drivers/xen/swiotlb-xen.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2010
+ * by Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+ *
+ * This code provides a IOMMU for Xen PV guests with PCI passthrough.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 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.
+ *
+ * PV guests under Xen are running in an non-contiguous memory architecture.
+ *
+ * When PCI pass-through is utilized, this necessitates an IOMMU for
+ * translating bus (DMA) to virtual and vice-versa and also providing a
+ * mechanism to have contiguous pages for device drivers operations (say DMA
+ * operations).
+ *
+ * Specifically, under Xen the Linux idea of pages is an illusion. It
+ * assumes that pages start at zero and go up to the available memory. To
+ * help with that, the Linux Xen MMU provides a lookup mechanism to
+ * translate the page frame numbers (PFN) to machine frame numbers (MFN)
+ * and vice-versa. The MFN are the "real" frame numbers. Furthermore
+ * memory is not contiguous. Xen hypervisor stitches memory for guests
+ * from different pools, which means there is no guarantee that PFN==MFN
+ * and PFN+1==MFN+1. Lastly with Xen 4.0, pages (in debug mode) are
+ * allocated in descending order (high to low), meaning the guest might
+ * never get any MFN's under the 4GB mark.
+ *
+ */
+
+#include <linux/bootmem.h>
+#include <linux/dma-mapping.h>
+#include <xen/swiotlb-xen.h>
+#include <xen/page.h>
+#include <xen/xen-ops.h>
+/*
+ * Used to do a quick range check in swiotlb_tbl_unmap_single and
+ * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this
+ * API.
+ */
+
+static char *xen_io_tlb_start, *xen_io_tlb_end;
+static unsigned long xen_io_tlb_nslabs;
+/*
+ * Quick lookup value of the bus address of the IOTLB.
+ */
+
+u64 start_dma_addr;
+
+static dma_addr_t xen_phys_to_bus(phys_addr_t paddr)
+{
+ return phys_to_machine(XPADDR(paddr)).maddr;;
+}
+
+static phys_addr_t xen_bus_to_phys(dma_addr_t baddr)
+{
+ return machine_to_phys(XMADDR(baddr)).paddr;
+}
+
+static dma_addr_t xen_virt_to_bus(void *address)
+{
+ return xen_phys_to_bus(virt_to_phys(address));
+}
+
+static int check_pages_physically_contiguous(unsigned long pfn,
+ unsigned int offset,
+ size_t length)
+{
+ unsigned long next_mfn;
+ int i;
+ int nr_pages;
+
+ next_mfn = pfn_to_mfn(pfn);
+ nr_pages = (offset + length + PAGE_SIZE-1) >> PAGE_SHIFT;
+
+ for (i = 1; i < nr_pages; i++) {
+ if (pfn_to_mfn(++pfn) != ++next_mfn)
+ return 0;
+ }
+ return 1;
+}
+
+static int range_straddles_page_boundary(phys_addr_t p, size_t size)
+{
+ unsigned long pfn = PFN_DOWN(p);
+ unsigned int offset = p & ~PAGE_MASK;
+
+ if (offset + size <= PAGE_SIZE)
+ return 0;
+ if (check_pages_physically_contiguous(pfn, offset, size))
+ return 0;
+ return 1;
+}
+
+static int is_xen_swiotlb_buffer(dma_addr_t dma_addr)
+{
+ unsigned long mfn = PFN_DOWN(dma_addr);
+ unsigned long pfn = mfn_to_local_pfn(mfn);
+ phys_addr_t paddr;
+
+ /* If the address is outside our domain, it CAN
+ * have the same virtual address as another address
+ * in our domain. Therefore _only_ check address within our domain.
+ */
+ if (pfn_valid(pfn)) {
+ paddr = PFN_PHYS(pfn);
+ return paddr >= virt_to_phys(xen_io_tlb_start) &&
+ paddr < virt_to_phys(xen_io_tlb_end);
+ }
+ return 0;
+}
+
+static int max_dma_bits = 32;
+
+static int
+xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs)
+{
+ int i, rc;
+ int dma_bits;
+
+ dma_bits = get_order(IO_TLB_SEGSIZE << IO_TLB_SHIFT) + PAGE_SHIFT;
+
+ i = 0;
+ do {
+ int slabs = min(nslabs - i, (unsigned long)IO_TLB_SEGSIZE);
+
+ do {
+ rc = xen_create_contiguous_region(
+ (unsigned long)buf + (i << IO_TLB_SHIFT),
+ get_order(slabs << IO_TLB_SHIFT),
+ dma_bits);
+ } while (rc && dma_bits++ < max_dma_bits);
+ if (rc)
+ return rc;
+
+ i += slabs;
+ } while (i < nslabs);
+ return 0;
+}
+
+void __init xen_swiotlb_init(int verbose)
+{
+ unsigned long bytes;
+ int rc;
+
+ xen_io_tlb_nslabs = (64 * 1024 * 1024 >> IO_TLB_SHIFT);
+ xen_io_tlb_nslabs = ALIGN(xen_io_tlb_nslabs, IO_TLB_SEGSIZE);
+
+ bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT;
+
+ /*
+ * Get IO TLB memory from any location.
+ */
+ xen_io_tlb_start = alloc_bootmem(bytes);
+ if (!xen_io_tlb_start)
+ panic("Cannot allocate SWIOTLB buffer");
+
+ xen_io_tlb_end = xen_io_tlb_start + bytes;
+ /*
+ * And replace that memory with pages under 4GB.
+ */
+ rc = xen_swiotlb_fixup(xen_io_tlb_start,
+ bytes,
+ xen_io_tlb_nslabs);
+ if (rc)
+ goto error;
+
+ start_dma_addr = xen_virt_to_bus(xen_io_tlb_start);
+ swiotlb_init_with_tbl(xen_io_tlb_start, xen_io_tlb_nslabs, verbose);
+
+ return;
+error:
+ panic("DMA(%d): Failed to exchange pages allocated for DMA with Xen! "\
+ "We either don't have the permission or you do not have enough"\
+ "free memory under 4GB!\n", rc);
+}
+
+void *
+xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flags)
+{
+ void *ret;
+ int order = get_order(size);
+ u64 dma_mask = DMA_BIT_MASK(32);
+ unsigned long vstart;
+
+ /*
+ * Ignore region specifiers - the kernel's ideas of
+ * pseudo-phys memory layout has nothing to do with the
+ * machine physical layout. We can't allocate highmem
+ * because we can't return a pointer to it.
+ */
+ flags &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+ if (dma_alloc_from_coherent(hwdev, size, dma_handle, &ret))
+ return ret;
+
+ vstart = __get_free_pages(flags, order);
+ ret = (void *)vstart;
+
+ if (hwdev && hwdev->coherent_dma_mask)
+ dma_mask = dma_alloc_coherent_mask(hwdev, flags);
+
+ if (ret) {
+ if (xen_create_contiguous_region(vstart, order,
+ fls64(dma_mask)) != 0) {
+ free_pages(vstart, order);
+ return NULL;
+ }
+ memset(ret, 0, size);
+ *dma_handle = virt_to_machine(ret).maddr;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_alloc_coherent);
+
+void
+xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
+ dma_addr_t dev_addr)
+{
+ int order = get_order(size);
+
+ if (dma_release_from_coherent(hwdev, order, vaddr))
+ return;
+
+ xen_destroy_contiguous_region((unsigned long)vaddr, order);
+ free_pages((unsigned long)vaddr, order);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_free_coherent);
+
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode. The
+ * physical address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory until
+ * either xen_swiotlb_unmap_page or xen_swiotlb_dma_sync_single is performed.
+ */
+dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ phys_addr_t phys = page_to_phys(page) + offset;
+ dma_addr_t dev_addr = xen_phys_to_bus(phys);
+ void *map;
+
+ BUG_ON(dir == DMA_NONE);
+ /*
+ * If the address happens to be in the device's DMA window,
+ * we can safely return the device addr and not worry about bounce
+ * buffering it.
+ */
+ if (dma_capable(dev, dev_addr, size) &&
+ !range_straddles_page_boundary(phys, size) && !swiotlb_force)
+ return dev_addr;
+
+ /*
+ * Oh well, have to allocate and map a bounce buffer.
+ */
+ map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, size, dir);
+ if (!map)
+ return DMA_ERROR_CODE;
+
+ dev_addr = xen_virt_to_bus(map);
+
+ /*
+ * Ensure that the address returned is DMA'ble
+ */
+ if (!dma_capable(dev, dev_addr, size))
+ panic("map_single: bounce buffer is not DMA'ble");
+
+ return dev_addr;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_map_page);
+
+/*
+ * Unmap a single streaming mode DMA translation. The dma_addr and size must
+ * match what was provided for in a previous xen_swiotlb_map_page call. All
+ * other usages are undefined.
+ *
+ * After this call, reads by the cpu to the buffer are guaranteed to see
+ * whatever the device wrote there.
+ */
+static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ phys_addr_t paddr = xen_bus_to_phys(dev_addr);
+
+ BUG_ON(dir == DMA_NONE);
+
+ /* NOTE: We use dev_addr here, not paddr! */
+ if (is_xen_swiotlb_buffer(dev_addr)) {
+ swiotlb_tbl_unmap_single(hwdev, phys_to_virt(paddr), size, dir);
+ return;
+ }
+
+ if (dir != DMA_FROM_DEVICE)
+ return;
+
+ /*
+ * phys_to_virt doesn't work with hihgmem page but we could
+ * call dma_mark_clean() with hihgmem page here. However, we
+ * are fine since dma_mark_clean() is null on POWERPC. We can
+ * make dma_mark_clean() take a physical address if necessary.
+ */
+ dma_mark_clean(phys_to_virt(paddr), size);
+}
+
+void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ xen_unmap_single(hwdev, dev_addr, size, dir);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_page);
+
+/*
+ * Make physical memory consistent for a single streaming mode DMA translation
+ * after a transfer.
+ *
+ * If you perform a xen_swiotlb_map_page() but wish to interrogate the buffer
+ * using the cpu, yet do not wish to teardown the dma mapping, you must
+ * call this function before doing so. At the next point you give the dma
+ * address back to the card, you must first perform a
+ * xen_swiotlb_dma_sync_for_device, and then the device again owns the buffer
+ */
+static void
+xen_swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir,
+ enum dma_sync_target target)
+{
+ phys_addr_t paddr = xen_bus_to_phys(dev_addr);
+
+ BUG_ON(dir == DMA_NONE);
+
+ /* NOTE: We use dev_addr here, not paddr! */
+ if (is_xen_swiotlb_buffer(dev_addr)) {
+ swiotlb_tbl_sync_single(hwdev, phys_to_virt(paddr), size, dir,
+ target);
+ return;
+ }
+
+ if (dir != DMA_FROM_DEVICE)
+ return;
+
+ dma_mark_clean(phys_to_virt(paddr), size);
+}
+
+void
+xen_swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_cpu);
+
+void
+xen_swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_device);
+
+/*
+ * Map a set of buffers described by scatterlist in streaming mode for DMA.
+ * This is the scatter-gather version of the above xen_swiotlb_map_page
+ * interface. Here the scatter gather list elements are each tagged with the
+ * appropriate dma address and length. They are obtained via
+ * sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ * DMA address/length pairs than there are SG table elements.
+ * (for example via virtual mapping capabilities)
+ * The routine returns the number of addr/length pairs actually
+ * used, at most nents.
+ *
+ * Device ownership issues as mentioned above for xen_swiotlb_map_page are the
+ * same here.
+ */
+int
+xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct scatterlist *sg;
+ int i;
+
+ BUG_ON(dir == DMA_NONE);
+
+ for_each_sg(sgl, sg, nelems, i) {
+ phys_addr_t paddr = sg_phys(sg);
+ dma_addr_t dev_addr = xen_phys_to_bus(paddr);
+
+ if (swiotlb_force ||
+ !dma_capable(hwdev, dev_addr, sg->length) ||
+ range_straddles_page_boundary(paddr, sg->length)) {
+ void *map = swiotlb_tbl_map_single(hwdev,
+ start_dma_addr,
+ sg_phys(sg),
+ sg->length, dir);
+ if (!map) {
+ /* Don't panic here, we expect map_sg users
+ to do proper error handling. */
+ xen_swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir,
+ attrs);
+ sgl[0].dma_length = 0;
+ return DMA_ERROR_CODE;
+ }
+ sg->dma_address = xen_virt_to_bus(map);
+ } else
+ sg->dma_address = dev_addr;
+ sg->dma_length = sg->length;
+ }
+ return nelems;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg_attrs);
+
+int
+xen_swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems,
+ enum dma_data_direction dir)
+{
+ return xen_swiotlb_map_sg_attrs(hwdev, sgl, nelems, dir, NULL);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg);
+
+/*
+ * Unmap a set of streaming mode DMA translations. Again, cpu read rules
+ * concerning calls here are the same as for swiotlb_unmap_page() above.
+ */
+void
+xen_swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct scatterlist *sg;
+ int i;
+
+ BUG_ON(dir == DMA_NONE);
+
+ for_each_sg(sgl, sg, nelems, i)
+ xen_unmap_single(hwdev, sg->dma_address, sg->dma_length, dir);
+
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg_attrs);
+
+void
+xen_swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems,
+ enum dma_data_direction dir)
+{
+ return xen_swiotlb_unmap_sg_attrs(hwdev, sgl, nelems, dir, NULL);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg);
+
+/*
+ * Make physical memory consistent for a set of streaming mode DMA translations
+ * after a transfer.
+ *
+ * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules
+ * and usage.
+ */
+static void
+xen_swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ enum dma_sync_target target)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nelems, i)
+ xen_swiotlb_sync_single(hwdev, sg->dma_address,
+ sg->dma_length, dir, target);
+}
+
+void
+xen_swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_sg_for_cpu);
+
+void
+xen_swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_sg_for_device);
+
+int
+xen_swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr)
+{
+ return !dma_addr;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_dma_mapping_error);
+
+/*
+ * Return whether the given device DMA address mask can be supported
+ * properly. For example, if your device can only drive the low 24-bits
+ * during bus mastering, then you would pass 0x00ffffff as the mask to
+ * this function.
+ */
+int
+xen_swiotlb_dma_supported(struct device *hwdev, u64 mask)
+{
+ return xen_virt_to_bus(xen_io_tlb_end - 1) <= mask;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_dma_supported);
diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c
index 7b3e973..7e49527 100644
--- a/drivers/xen/xenbus/xenbus_client.c
+++ b/drivers/xen/xenbus/xenbus_client.c
@@ -133,17 +133,12 @@ int xenbus_watch_pathfmt(struct xenbus_device *dev,
}
EXPORT_SYMBOL_GPL(xenbus_watch_pathfmt);
+static void xenbus_switch_fatal(struct xenbus_device *, int, int,
+ const char *, ...);
-/**
- * xenbus_switch_state
- * @dev: xenbus device
- * @state: new state
- *
- * Advertise in the store a change of the given driver to the given new_state.
- * Return 0 on success, or -errno on error. On error, the device will switch
- * to XenbusStateClosing, and the error will be saved in the store.
- */
-int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state)
+static int
+__xenbus_switch_state(struct xenbus_device *dev,
+ enum xenbus_state state, int depth)
{
/* We check whether the state is currently set to the given value, and
if not, then the state is set. We don't want to unconditionally
@@ -152,35 +147,65 @@ int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state)
to it, as the device will be tearing down, and we don't want to
resurrect that directory.
- Note that, because of this cached value of our state, this function
- will not work inside a Xenstore transaction (something it was
- trying to in the past) because dev->state would not get reset if
- the transaction was aborted.
-
+ Note that, because of this cached value of our state, this
+ function will not take a caller's Xenstore transaction
+ (something it was trying to in the past) because dev->state
+ would not get reset if the transaction was aborted.
*/
+ struct xenbus_transaction xbt;
int current_state;
- int err;
+ int err, abort;
if (state == dev->state)
return 0;
- err = xenbus_scanf(XBT_NIL, dev->nodename, "state", "%d",
- &current_state);
- if (err != 1)
+again:
+ abort = 1;
+
+ err = xenbus_transaction_start(&xbt);
+ if (err) {
+ xenbus_switch_fatal(dev, depth, err, "starting transaction");
return 0;
+ }
+
+ err = xenbus_scanf(xbt, dev->nodename, "state", "%d", &current_state);
+ if (err != 1)
+ goto abort;
- err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%d", state);
+ err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
if (err) {
- if (state != XenbusStateClosing) /* Avoid looping */
- xenbus_dev_fatal(dev, err, "writing new state");
- return err;
+ xenbus_switch_fatal(dev, depth, err, "writing new state");
+ goto abort;
}
- dev->state = state;
+ abort = 0;
+abort:
+ err = xenbus_transaction_end(xbt, abort);
+ if (err) {
+ if (err == -EAGAIN && !abort)
+ goto again;
+ xenbus_switch_fatal(dev, depth, err, "ending transaction");
+ } else
+ dev->state = state;
return 0;
}
+
+/**
+ * xenbus_switch_state
+ * @dev: xenbus device
+ * @state: new state
+ *
+ * Advertise in the store a change of the given driver to the given new_state.
+ * Return 0 on success, or -errno on error. On error, the device will switch
+ * to XenbusStateClosing, and the error will be saved in the store.
+ */
+int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state)
+{
+ return __xenbus_switch_state(dev, state, 0);
+}
+
EXPORT_SYMBOL_GPL(xenbus_switch_state);
int xenbus_frontend_closed(struct xenbus_device *dev)
@@ -284,6 +309,23 @@ void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...)
EXPORT_SYMBOL_GPL(xenbus_dev_fatal);
/**
+ * Equivalent to xenbus_dev_fatal(dev, err, fmt, args), but helps
+ * avoiding recursion within xenbus_switch_state.
+ */
+static void xenbus_switch_fatal(struct xenbus_device *dev, int depth, int err,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xenbus_va_dev_error(dev, err, fmt, ap);
+ va_end(ap);
+
+ if (!depth)
+ __xenbus_switch_state(dev, XenbusStateClosing, 1);
+}
+
+/**
* xenbus_grant_ring
* @dev: xenbus device
* @ring_mfn: mfn of ring to grant
diff --git a/drivers/zorro/proc.c b/drivers/zorro/proc.c
index 3c7046d..cafc504 100644
--- a/drivers/zorro/proc.c
+++ b/drivers/zorro/proc.c
@@ -22,8 +22,9 @@ static loff_t
proc_bus_zorro_lseek(struct file *file, loff_t off, int whence)
{
loff_t new = -1;
+ struct inode *inode = file->f_path.dentry->d_inode;
- lock_kernel();
+ mutex_lock(&inode->i_mutex);
switch (whence) {
case 0:
new = off;
@@ -35,12 +36,12 @@ proc_bus_zorro_lseek(struct file *file, loff_t off, int whence)
new = sizeof(struct ConfigDev) + off;
break;
}
- if (new < 0 || new > sizeof(struct ConfigDev)) {
- unlock_kernel();
- return -EINVAL;
- }
- unlock_kernel();
- return (file->f_pos = new);
+ if (new < 0 || new > sizeof(struct ConfigDev))
+ new = -EINVAL;
+ else
+ file->f_pos = new;
+ mutex_unlock(&inode->i_mutex);
+ return new;
}
static ssize_t
@@ -67,7 +68,7 @@ proc_bus_zorro_read(struct file *file, char __user *buf, size_t nbytes, loff_t *
cd.cd_BoardAddr = (void *)zorro_resource_start(z);
cd.cd_BoardSize = zorro_resource_len(z);
- if (copy_to_user(buf, &cd, nbytes))
+ if (copy_to_user(buf, (void *)&cd + pos, nbytes))
return -EFAULT;
*ppos += nbytes;