summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/base/cpu.c2
-rw-r--r--drivers/base/node.c196
-rw-r--r--drivers/block/drbd/drbd_nl.c3
-rw-r--r--drivers/block/floppy.c5
-rw-r--r--drivers/block/xd.c30
-rw-r--r--drivers/char/efirtc.c1
-rw-r--r--drivers/char/hvc_iucv.c2
-rw-r--r--drivers/char/ipmi/ipmi_kcs_sm.c2
-rw-r--r--drivers/char/mem.c161
-rw-r--r--drivers/char/misc.c26
-rw-r--r--drivers/char/nvram.c14
-rw-r--r--drivers/char/random.c10
-rw-r--r--drivers/char/sysrq.c2
-rw-r--r--drivers/char/vt.c50
-rw-r--r--drivers/clocksource/Kconfig9
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/cs5535-clockevt.c197
-rw-r--r--drivers/cpufreq/cpufreq.c16
-rw-r--r--drivers/cpufreq/freq_table.c12
-rw-r--r--drivers/cpuidle/governors/ladder.c3
-rw-r--r--drivers/crypto/padlock-aes.c12
-rw-r--r--drivers/dma/at_hdmac.c2
-rw-r--r--drivers/dma/dmaengine.c36
-rw-r--r--drivers/dma/dw_dmac.c2
-rw-r--r--drivers/dma/txx9dmac.c2
-rw-r--r--drivers/edac/amd64_edac.c46
-rw-r--r--drivers/edac/i5100_edac.c252
-rw-r--r--drivers/firmware/Kconfig4
-rw-r--r--drivers/firmware/dmi_scan.c5
-rw-r--r--drivers/gpio/Kconfig16
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/adp5520-gpio.c36
-rw-r--r--drivers/gpio/cs5535-gpio.c355
-rw-r--r--drivers/gpio/gpiolib.c161
-rw-r--r--drivers/gpio/langwell_gpio.c2
-rw-r--r--drivers/gpio/timbgpio.c342
-rw-r--r--drivers/gpio/twl4030-gpio.c20
-rw-r--r--drivers/gpio/wm831x-gpio.c17
-rw-r--r--drivers/hwmon/Kconfig22
-rw-r--r--drivers/hwmon/adm1021.c11
-rw-r--r--drivers/hwmon/adm1025.c12
-rw-r--r--drivers/hwmon/adm1026.c11
-rw-r--r--drivers/hwmon/adm1029.c14
-rw-r--r--drivers/hwmon/adm1031.c9
-rw-r--r--drivers/hwmon/adm9240.c9
-rw-r--r--drivers/hwmon/ads7828.c13
-rw-r--r--drivers/hwmon/adt7462.c11
-rw-r--r--drivers/hwmon/adt7470.c11
-rw-r--r--drivers/hwmon/adt7473.c11
-rw-r--r--drivers/hwmon/adt7475.c6
-rw-r--r--drivers/hwmon/applesmc.c2
-rw-r--r--drivers/hwmon/asb100.c11
-rw-r--r--drivers/hwmon/atxp1.c11
-rw-r--r--drivers/hwmon/dme1737.c10
-rw-r--r--drivers/hwmon/ds1621.c9
-rw-r--r--drivers/hwmon/f75375s.c9
-rw-r--r--drivers/hwmon/fschmd.c9
-rw-r--r--drivers/hwmon/gl518sm.c11
-rw-r--r--drivers/hwmon/gl520sm.c13
-rw-r--r--drivers/hwmon/lis3lv02d.c231
-rw-r--r--drivers/hwmon/lis3lv02d.h51
-rw-r--r--drivers/hwmon/lm63.c15
-rw-r--r--drivers/hwmon/lm73.c9
-rw-r--r--drivers/hwmon/lm75.c14
-rw-r--r--drivers/hwmon/lm77.c12
-rw-r--r--drivers/hwmon/lm78.c9
-rw-r--r--drivers/hwmon/lm80.c13
-rw-r--r--drivers/hwmon/lm83.c12
-rw-r--r--drivers/hwmon/lm85.c16
-rw-r--r--drivers/hwmon/lm87.c12
-rw-r--r--drivers/hwmon/lm90.c14
-rw-r--r--drivers/hwmon/lm92.c9
-rw-r--r--drivers/hwmon/lm93.c8
-rw-r--r--drivers/hwmon/lm95241.c9
-rw-r--r--drivers/hwmon/max1619.c14
-rw-r--r--drivers/hwmon/max6650.c10
-rw-r--r--drivers/hwmon/pcf8591.c5
-rw-r--r--drivers/hwmon/smsc47m192.c11
-rw-r--r--drivers/hwmon/thmc50.c8
-rw-r--r--drivers/hwmon/tmp401.c9
-rw-r--r--drivers/hwmon/tmp421.c7
-rw-r--r--drivers/hwmon/w83627ehf.c72
-rw-r--r--drivers/hwmon/w83781d.c10
-rw-r--r--drivers/hwmon/w83791d.c9
-rw-r--r--drivers/hwmon/w83792d.c9
-rw-r--r--drivers/hwmon/w83793.c9
-rw-r--r--drivers/hwmon/w83l785ts.c14
-rw-r--r--drivers/hwmon/w83l786ng.c10
-rw-r--r--drivers/i2c/busses/i2c-pxa.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c2
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c2
-rw-r--r--drivers/i2c/i2c-core.c52
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c3
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/adp5520-keys.c220
-rw-r--r--drivers/input/keyboard/adp5588-keys.c2
-rw-r--r--drivers/input/keyboard/sh_keysc.c2
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c6
-rw-r--r--drivers/input/misc/bfin_rotary.c2
-rw-r--r--drivers/input/misc/pcf50633-input.c7
-rw-r--r--drivers/input/misc/pcspkr.c2
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c4
-rw-r--r--drivers/input/touchscreen/pcap_ts.c2
-rw-r--r--drivers/isdn/hardware/avm/avm_cs.c3
-rw-r--r--drivers/isdn/hisax/avma1_cs.c3
-rw-r--r--drivers/isdn/hisax/elsa_cs.c2
-rw-r--r--drivers/isdn/hisax/sedlbauer_cs.c2
-rw-r--r--drivers/isdn/hisax/teles_cs.c2
-rw-r--r--drivers/leds/led-class.c2
-rw-r--r--drivers/leds/ledtrig-timer.c4
-rw-r--r--drivers/lguest/x86/core.c6
-rw-r--r--drivers/md/Kconfig9
-rw-r--r--drivers/md/bitmap.c449
-rw-r--r--drivers/md/bitmap.h19
-rw-r--r--drivers/md/dm-crypt.c207
-rw-r--r--drivers/md/dm-exception-store.c33
-rw-r--r--drivers/md/dm-exception-store.h62
-rw-r--r--drivers/md/dm-io.c120
-rw-r--r--drivers/md/dm-ioctl.c123
-rw-r--r--drivers/md/dm-kcopyd.c5
-rw-r--r--drivers/md/dm-log.c77
-rw-r--r--drivers/md/dm-mpath.c95
-rw-r--r--drivers/md/dm-raid1.c219
-rw-r--r--drivers/md/dm-region-hash.c31
-rw-r--r--drivers/md/dm-snap-persistent.c195
-rw-r--r--drivers/md/dm-snap-transient.c24
-rw-r--r--drivers/md/dm-snap.c1279
-rw-r--r--drivers/md/dm-sysfs.c10
-rw-r--r--drivers/md/dm-table.c9
-rw-r--r--drivers/md/dm-uevent.c9
-rw-r--r--drivers/md/dm.c643
-rw-r--r--drivers/md/dm.h13
-rw-r--r--drivers/md/faulty.c1
-rw-r--r--drivers/md/linear.c3
-rw-r--r--drivers/md/md.c413
-rw-r--r--drivers/md/md.h51
-rw-r--r--drivers/md/multipath.c3
-rw-r--r--drivers/md/raid0.c3
-rw-r--r--drivers/md/raid1.c217
-rw-r--r--drivers/md/raid1.h5
-rw-r--r--drivers/md/raid10.c116
-rw-r--r--drivers/md/raid5.c63
-rw-r--r--drivers/md/raid6algos.c20
-rw-r--r--drivers/media/IR/Kconfig9
-rw-r--r--drivers/media/IR/Makefile5
-rw-r--r--drivers/media/IR/ir-functions.c (renamed from drivers/media/common/ir-functions.c)20
-rw-r--r--drivers/media/IR/ir-keymaps.c (renamed from drivers/media/common/ir-keymaps.c)219
-rw-r--r--drivers/media/IR/ir-keytable.c (renamed from drivers/media/common/ir-keytable.c)90
-rw-r--r--drivers/media/Kconfig1
-rw-r--r--drivers/media/Makefile2
-rw-r--r--drivers/media/common/Makefile2
-rw-r--r--drivers/media/common/saa7146_fops.c60
-rw-r--r--drivers/media/dvb/dm1105/dm1105.c14
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig8
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700.h26
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c101
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c725
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-common.c15
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h3
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c456
-rw-r--r--drivers/media/dvb/dvb-usb/friio-fe.c10
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk-fe.c4
-rw-r--r--drivers/media/dvb/frontends/Kconfig9
-rw-r--r--drivers/media/dvb/frontends/Makefile1
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c4
-rw-r--r--drivers/media/dvb/frontends/dib0070.c674
-rw-r--r--drivers/media/dvb/frontends/dib0070.h4
-rw-r--r--drivers/media/dvb/frontends/dib0090.c1522
-rw-r--r--drivers/media/dvb/frontends/dib0090.h108
-rw-r--r--drivers/media/dvb/frontends/dib8000.c137
-rw-r--r--drivers/media/dvb/frontends/dib8000.h32
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c15
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h71
-rw-r--r--drivers/media/dvb/frontends/lgs8gxx.c4
-rw-r--r--drivers/media/dvb/frontends/lnbp21.c28
-rw-r--r--drivers/media/dvb/frontends/stv0900_core.c13
-rw-r--r--drivers/media/dvb/frontends/stv090x.c7
-rw-r--r--drivers/media/dvb/frontends/stv090x.h2
-rw-r--r--drivers/media/dvb/siano/smsdvb.c4
-rw-r--r--drivers/media/dvb/siano/smssdio.c8
-rw-r--r--drivers/media/dvb/siano/smsusb.c18
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c52
-rw-r--r--drivers/media/radio/Kconfig7
-rw-r--r--drivers/media/radio/radio-aimslab.c4
-rw-r--r--drivers/media/radio/radio-aztech.c4
-rw-r--r--drivers/media/radio/radio-gemtek-pci.c4
-rw-r--r--drivers/media/radio/radio-maestro.c4
-rw-r--r--drivers/media/radio/radio-maxiradio.c4
-rw-r--r--drivers/media/radio/radio-mr800.c4
-rw-r--r--drivers/media/radio/radio-rtrack2.c4
-rw-r--r--drivers/media/radio/radio-sf16fmi.c82
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c4
-rw-r--r--drivers/media/radio/radio-tea5764.c12
-rw-r--r--drivers/media/radio/radio-terratec.c4
-rw-r--r--drivers/media/radio/radio-trust.c4
-rw-r--r--drivers/media/radio/radio-typhoon.c4
-rw-r--r--drivers/media/radio/radio-zoltrix.c4
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c98
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c219
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c97
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h5
-rw-r--r--drivers/media/video/Kconfig10
-rw-r--r--drivers/media/video/Makefile3
-rw-r--r--drivers/media/video/arv.c5
-rw-r--r--drivers/media/video/au0828/au0828-video.c36
-rw-r--r--drivers/media/video/au0828/au0828.h1
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c41
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c4
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c9
-rw-r--r--drivers/media/video/c-qcam.c4
-rw-r--r--drivers/media/video/cafe_ccic.c1
-rw-r--r--drivers/media/video/cpia.c221
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c34
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c4
-rw-r--r--drivers/media/video/cx18/cx18-streams.c20
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c23
-rw-r--r--drivers/media/video/cx231xx/cx231xx-core.c26
-rw-r--r--drivers/media/video/cx231xx/cx231xx-input.c10
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c58
-rw-r--r--drivers/media/video/cx231xx/cx231xx.h2
-rw-r--r--drivers/media/video/cx23885/cimax2.c107
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c29
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c11
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c9
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c57
-rw-r--r--drivers/media/video/cx23885/cx23885.h3
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c26
-rw-r--r--drivers/media/video/cx88/cx88-input.c9
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c15
-rw-r--r--drivers/media/video/cx88/cx88-video.c68
-rw-r--r--drivers/media/video/cx88/cx88.h2
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c8
-rw-r--r--drivers/media/video/davinci/vpif.c2
-rw-r--r--drivers/media/video/davinci/vpif_capture.c2
-rw-r--r--drivers/media/video/davinci/vpif_display.c1
-rw-r--r--drivers/media/video/davinci/vpss.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c7
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c30
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c68
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c59
-rw-r--r--drivers/media/video/em28xx/em28xx.h5
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c45
-rw-r--r--drivers/media/video/gspca/conex.c4
-rw-r--r--drivers/media/video/gspca/etoms.c4
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi1320.c2
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi2020.c2
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c14
-rw-r--r--drivers/media/video/gspca/gspca.c67
-rw-r--r--drivers/media/video/gspca/gspca.h10
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c4
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c2
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c10
-rw-r--r--drivers/media/video/gspca/mr97310a.c2
-rw-r--r--drivers/media/video/gspca/ov519.c14
-rw-r--r--drivers/media/video/gspca/pac7302.c25
-rw-r--r--drivers/media/video/gspca/pac7311.c4
-rw-r--r--drivers/media/video/gspca/sn9c20x.c12
-rw-r--r--drivers/media/video/gspca/sonixb.c4
-rw-r--r--drivers/media/video/gspca/spca506.c4
-rw-r--r--drivers/media/video/gspca/stk014.c106
-rw-r--r--drivers/media/video/gspca/sunplus.c237
-rw-r--r--drivers/media/video/gspca/zc3xx.c36
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c16
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/video/ir-kbd-i2c.c12
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c19
-rw-r--r--drivers/media/video/meye.c1
-rw-r--r--drivers/media/video/mt9m001.c205
-rw-r--r--drivers/media/video/mt9m111.c189
-rw-r--r--drivers/media/video/mt9t031.c281
-rw-r--r--drivers/media/video/mt9t112.c1177
-rw-r--r--drivers/media/video/mt9v022.c228
-rw-r--r--drivers/media/video/mx1_camera.c126
-rw-r--r--drivers/media/video/mx3_camera.c296
-rw-r--r--drivers/media/video/omap24xxcam.c10
-rw-r--r--drivers/media/video/ov511.c9
-rw-r--r--drivers/media/video/ov772x.c233
-rw-r--r--drivers/media/video/ov9640.c107
-rw-r--r--drivers/media/video/pms.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c21
-rw-r--r--drivers/media/video/pwc/pwc-if.c5
-rw-r--r--drivers/media/video/pxa_camera.c335
-rw-r--r--drivers/media/video/rj54n1cb0c.c474
-rw-r--r--drivers/media/video/s2255drv.c55
-rw-r--r--drivers/media/video/saa5246a.c1
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c22
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c19
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c20
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c71
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c39
-rw-r--r--drivers/media/video/se401.c4
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c522
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c47
-rw-r--r--drivers/media/video/soc_camera.c106
-rw-r--r--drivers/media/video/soc_camera_platform.c40
-rw-r--r--drivers/media/video/soc_mediabus.c157
-rw-r--r--drivers/media/video/stk-webcam.c9
-rw-r--r--drivers/media/video/stradis.c4
-rw-r--r--drivers/media/video/stv680.c5
-rw-r--r--drivers/media/video/tw9910.c337
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c4
-rw-r--r--drivers/media/video/usbvideo/vicam.c5
-rw-r--r--drivers/media/video/usbvision/usbvision-i2c.c4
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c34
-rw-r--r--drivers/media/video/uvc/uvc_driver.c1
-rw-r--r--drivers/media/video/uvc/uvc_video.c2
-rw-r--r--drivers/media/video/v4l2-common.c47
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c6
-rw-r--r--drivers/media/video/v4l2-dev.c22
-rw-r--r--drivers/media/video/v4l2-ioctl.c147
-rw-r--r--drivers/media/video/videobuf-dma-contig.c6
-rw-r--r--drivers/media/video/vino.c1
-rw-r--r--drivers/media/video/vivi.c22
-rw-r--r--drivers/media/video/w9968cf.c34
-rw-r--r--drivers/media/video/zc0301/zc0301_core.c44
-rw-r--r--drivers/media/video/zoran/zoran_driver.c1
-rw-r--r--drivers/media/video/zr364xx.c5
-rw-r--r--drivers/mfd/88pm8607.c302
-rw-r--r--drivers/mfd/Kconfig41
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/ab3100-core.c3
-rw-r--r--drivers/mfd/ab4500-core.c208
-rw-r--r--drivers/mfd/adp5520.c379
-rw-r--r--drivers/mfd/asic3.c2
-rw-r--r--drivers/mfd/ezx-pcap.c1
-rw-r--r--drivers/mfd/mc13783-core.c757
-rw-r--r--drivers/mfd/pcf50633-adc.c5
-rw-r--r--drivers/mfd/pcf50633-core.c76
-rw-r--r--drivers/mfd/tps65010.c30
-rw-r--r--drivers/mfd/twl-core.c (renamed from drivers/mfd/twl4030-core.c)385
-rw-r--r--drivers/mfd/twl4030-codec.c10
-rw-r--r--drivers/mfd/twl4030-irq.c158
-rw-r--r--drivers/mfd/twl4030-power.c126
-rw-r--r--drivers/mfd/twl6030-irq.c299
-rw-r--r--drivers/mfd/wm831x-core.c232
-rw-r--r--drivers/mfd/wm831x-irq.c209
-rw-r--r--drivers/mfd/wm8350-core.c771
-rw-r--r--drivers/mfd/wm8350-irq.c529
-rw-r--r--drivers/mfd/wm8350-regmap.c8
-rw-r--r--drivers/misc/Kconfig48
-rw-r--r--drivers/misc/Makefile3
-rw-r--r--drivers/misc/ad525x_dpot.c666
-rw-r--r--drivers/misc/cs5535-mfgpt.c370
-rw-r--r--drivers/misc/eeprom/eeprom.c8
-rw-r--r--drivers/misc/ics932s401.c11
-rw-r--r--drivers/misc/ioc4.c16
-rw-r--r--drivers/misc/sgi-gru/gru.h11
-rw-r--r--drivers/misc/sgi-gru/gru_instructions.h144
-rw-r--r--drivers/misc/sgi-gru/grufault.c311
-rw-r--r--drivers/misc/sgi-gru/grufile.c290
-rw-r--r--drivers/misc/sgi-gru/gruhandles.c70
-rw-r--r--drivers/misc/sgi-gru/gruhandles.h37
-rw-r--r--drivers/misc/sgi-gru/grukdump.c13
-rw-r--r--drivers/misc/sgi-gru/grukservices.c211
-rw-r--r--drivers/misc/sgi-gru/grukservices.h14
-rw-r--r--drivers/misc/sgi-gru/grulib.h21
-rw-r--r--drivers/misc/sgi-gru/grumain.c228
-rw-r--r--drivers/misc/sgi-gru/gruprocfs.c42
-rw-r--r--drivers/misc/sgi-gru/grutables.h75
-rw-r--r--drivers/misc/sgi-gru/grutlbpurge.c14
-rw-r--r--drivers/misc/sgi-xp/xp.h1
-rw-r--r--drivers/misc/sgi-xp/xp_main.c3
-rw-r--r--drivers/misc/sgi-xp/xp_sn2.c10
-rw-r--r--drivers/misc/sgi-xp/xp_uv.c33
-rw-r--r--drivers/misc/sgi-xp/xpc_partition.c13
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c46
-rw-r--r--drivers/misc/ti_dac7512.c101
-rw-r--r--drivers/mmc/core/Kconfig4
-rw-r--r--drivers/mmc/core/core.c16
-rw-r--r--drivers/mmc/core/core.h2
-rw-r--r--drivers/mmc/core/mmc.c23
-rw-r--r--drivers/mmc/core/sd.c21
-rw-r--r--drivers/mmc/core/sdio_cis.c167
-rw-r--r--drivers/mmc/host/Kconfig27
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/atmel-mci.c141
-rw-r--r--drivers/mmc/host/bfin_sdh.c639
-rw-r--r--drivers/mmc/host/davinci_mmc.c1349
-rw-r--r--drivers/mmc/host/mxcmmc.c10
-rw-r--r--drivers/mmc/host/omap.c10
-rw-r--r--drivers/mmc/host/pxamci.c2
-rw-r--r--drivers/mmc/host/s3cmci.c13
-rw-r--r--drivers/mmc/host/sdhci-pci.c75
-rw-r--r--drivers/mmc/host/tmio_mmc.c2
-rw-r--r--drivers/mtd/nand/nomadik_nand.c2
-rw-r--r--drivers/net/3c59x.c2
-rw-r--r--drivers/net/chelsio/sge.c5
-rw-r--r--drivers/net/dm9000.c2
-rw-r--r--drivers/net/loopback.c2
-rw-r--r--drivers/net/mlx4/alloc.c37
-rw-r--r--drivers/net/pcmcia/axnet_cs.c2
-rw-r--r--drivers/net/pcmcia/fmvj18x_cs.c2
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c2
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c2
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c2
-rw-r--r--drivers/net/r8169.c2
-rw-r--r--drivers/net/smsc911x.c2
-rw-r--r--drivers/net/veth.c7
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c2
-rw-r--r--drivers/oprofile/cpu_buffer.c19
-rw-r--r--drivers/oprofile/cpu_buffer.h4
-rw-r--r--drivers/oprofile/oprofile_stats.c4
-rw-r--r--drivers/parisc/pdc_stable.c9
-rw-r--r--drivers/parport/parport_pc.c2
-rw-r--r--drivers/pci/pcie/portdrv_pci.c2
-rw-r--r--drivers/pcmcia/Kconfig8
-rw-r--r--drivers/pcmcia/Makefile2
-rw-r--r--drivers/pcmcia/cardbus.c23
-rw-r--r--drivers/pcmcia/cistpl.c181
-rw-r--r--drivers/pcmcia/cs.c12
-rw-r--r--drivers/pcmcia/ds.c66
-rw-r--r--drivers/pcmcia/pcmcia_ioctl.c45
-rw-r--r--drivers/pcmcia/pcmcia_resource.c37
-rw-r--r--drivers/pcmcia/pxa2xx_base.c21
-rw-r--r--drivers/pcmcia/pxa2xx_base.h3
-rw-r--r--drivers/pcmcia/pxa2xx_palmtc.c2
-rw-r--r--drivers/pcmcia/pxa2xx_stargate2.c2
-rw-r--r--drivers/pcmcia/pxa2xx_viper.c119
-rw-r--r--drivers/pcmcia/rsrc_mgr.c14
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c315
-rw-r--r--drivers/pcmcia/socket_sysfs.c6
-rw-r--r--drivers/pcmcia/yenta_socket.c149
-rw-r--r--drivers/platform/x86/acerhdf.c2
-rw-r--r--drivers/platform/x86/eeepc-laptop.c2
-rw-r--r--drivers/platform/x86/hp-wmi.c2
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c7
-rw-r--r--drivers/pnp/interface.c36
-rw-r--r--drivers/pnp/pnpbios/proc.c204
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/pcf50633-charger.c231
-rw-r--r--drivers/power/power_supply_sysfs.c5
-rw-r--r--drivers/power/wm831x_backup.c233
-rw-r--r--drivers/power/wm831x_power.c144
-rw-r--r--drivers/power/wm8350_power.c63
-rw-r--r--drivers/power/wm97xx_battery.c2
-rw-r--r--drivers/regulator/Kconfig2
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/pcf50633-regulator.c5
-rw-r--r--drivers/regulator/twl-regulator.c (renamed from drivers/regulator/twl4030-regulator.c)287
-rw-r--r--drivers/regulator/wm8350-regulator.c10
-rw-r--r--drivers/rtc/Kconfig34
-rw-r--r--drivers/rtc/Makefile5
-rw-r--r--drivers/rtc/rtc-at32ap700x.c4
-rw-r--r--drivers/rtc/rtc-bq32k.c204
-rw-r--r--drivers/rtc/rtc-bq4802.c3
-rw-r--r--drivers/rtc/rtc-cmos.c78
-rw-r--r--drivers/rtc/rtc-ds1302.c1
-rw-r--r--drivers/rtc/rtc-ds1305.c14
-rw-r--r--drivers/rtc/rtc-ds1307.c2
-rw-r--r--drivers/rtc/rtc-ds1511.c148
-rw-r--r--drivers/rtc/rtc-ds1553.c149
-rw-r--r--drivers/rtc/rtc-ds1742.c59
-rw-r--r--drivers/rtc/rtc-m48t35.c16
-rw-r--r--drivers/rtc/rtc-m48t59.c11
-rw-r--r--drivers/rtc/rtc-mc13783.c262
-rw-r--r--drivers/rtc/rtc-mv.c157
-rw-r--r--drivers/rtc/rtc-nuc900.c342
-rw-r--r--drivers/rtc/rtc-omap.c47
-rw-r--r--drivers/rtc/rtc-pcf50633.c10
-rw-r--r--drivers/rtc/rtc-pcf8563.c4
-rw-r--r--drivers/rtc/rtc-pcf8583.c3
-rw-r--r--drivers/rtc/rtc-pl031.c23
-rw-r--r--drivers/rtc/rtc-pxa.c2
-rw-r--r--drivers/rtc/rtc-sa1100.c2
-rw-r--r--drivers/rtc/rtc-sh.c2
-rw-r--r--drivers/rtc/rtc-stk17ta8.c124
-rw-r--r--drivers/rtc/rtc-twl.c (renamed from drivers/rtc/rtc-twl4030.c)284
-rw-r--r--drivers/rtc/rtc-tx4939.c51
-rw-r--r--drivers/rtc/rtc-v3020.c8
-rw-r--r--drivers/rtc/rtc-vr41xx.c4
-rw-r--r--drivers/rtc/rtc-wm831x.c2
-rw-r--r--drivers/rtc/rtc-wm8350.c39
-rw-r--r--drivers/rtc/rtc-x1205.c53
-rw-r--r--drivers/s390/block/dasd_proc.c5
-rw-r--r--drivers/s390/block/dcssblk.c2
-rw-r--r--drivers/s390/block/xpram.c2
-rw-r--r--drivers/s390/char/monreader.c2
-rw-r--r--drivers/s390/char/monwriter.c2
-rw-r--r--drivers/s390/char/sclp.c2
-rw-r--r--drivers/s390/char/sclp_cmd.c2
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/cio/ccwgroup.c2
-rw-r--r--drivers/s390/cio/css.c2
-rw-r--r--drivers/s390/cio/device.c2
-rw-r--r--drivers/s390/net/netiucv.c10
-rw-r--r--drivers/s390/net/smsgiucv.c2
-rw-r--r--drivers/scsi/ipr.c4
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c4
-rw-r--r--drivers/serial/ioc3_serial.c4
-rw-r--r--drivers/serial/ioc4_serial.c20
-rw-r--r--drivers/serial/pxa.c2
-rw-r--r--drivers/serial/sh-sci.c2
-rw-r--r--drivers/sn/ioc3.c17
-rw-r--r--drivers/spi/Kconfig40
-rw-r--r--drivers/spi/Makefile5
-rw-r--r--drivers/spi/au1550_spi.c10
-rw-r--r--drivers/spi/mpc52xx_spi.c86
-rw-r--r--drivers/spi/omap_spi_100k.c635
-rw-r--r--drivers/spi/pxa2xx_spi.c2
-rw-r--r--drivers/spi/spi_imx.c35
-rw-r--r--drivers/spi/spi_mpc8xxx.c2
-rw-r--r--drivers/spi/spi_nuc900.c504
-rw-r--r--drivers/spi/spi_s3c24xx.c2
-rw-r--r--drivers/spi/spi_sh_msiof.c691
-rw-r--r--drivers/spi/spidev.c6
-rw-r--r--drivers/spi/xilinx_spi.c355
-rw-r--r--drivers/spi/xilinx_spi.h32
-rw-r--r--drivers/spi/xilinx_spi_of.c134
-rw-r--r--drivers/spi/xilinx_spi_pltfm.c102
-rw-r--r--drivers/staging/cx25821/cx25821-audups11.c33
-rw-r--r--drivers/staging/cx25821/cx25821-video.c6
-rw-r--r--drivers/staging/cx25821/cx25821-video0.c33
-rw-r--r--drivers/staging/cx25821/cx25821-video1.c33
-rw-r--r--drivers/staging/cx25821/cx25821-video2.c34
-rw-r--r--drivers/staging/cx25821/cx25821-video3.c34
-rw-r--r--drivers/staging/cx25821/cx25821-video4.c34
-rw-r--r--drivers/staging/cx25821/cx25821-video5.c34
-rw-r--r--drivers/staging/cx25821/cx25821-video6.c34
-rw-r--r--drivers/staging/cx25821/cx25821-video7.c34
-rw-r--r--drivers/staging/cx25821/cx25821-videoioctl.c32
-rw-r--r--drivers/staging/cx25821/cx25821-vidups10.c33
-rw-r--r--drivers/staging/cx25821/cx25821-vidups9.c33
-rw-r--r--drivers/staging/go7007/go7007-v4l2.c5
-rw-r--r--drivers/uio/uio_pdrv_genirq.c2
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.h2
-rw-r--r--drivers/usb/core/usb.c25
-rw-r--r--drivers/usb/host/ehci-au1xxx.c2
-rw-r--r--drivers/usb/host/isp1362-hcd.c26
-rw-r--r--drivers/usb/host/ohci-au1xxx.c2
-rw-r--r--drivers/usb/host/ohci-pxa27x.c2
-rw-r--r--drivers/usb/host/r8a66597-hcd.c2
-rw-r--r--drivers/usb/musb/musb_core.c2
-rw-r--r--drivers/usb/otg/twl4030-usb.c38
-rw-r--r--drivers/video/Kconfig15
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/atafb.c3
-rw-r--r--drivers/video/backlight/adp5520_bl.c123
-rw-r--r--drivers/video/backlight/da903x_bl.c2
-rw-r--r--drivers/video/backlight/lcd.c4
-rw-r--r--drivers/video/bfin-lq035q1-fb.c826
-rw-r--r--drivers/video/bfin-t350mcqb-fb.c32
-rw-r--r--drivers/video/clps711xfb.c50
-rw-r--r--drivers/video/da8xx-fb.c175
-rw-r--r--drivers/video/display/display-sysfs.c2
-rw-r--r--drivers/video/ep93xx-fb.c2
-rw-r--r--drivers/video/geode/display_gx.c4
-rw-r--r--drivers/video/geode/gxfb.h2
-rw-r--r--drivers/video/geode/gxfb_core.c2
-rw-r--r--drivers/video/geode/lxfb.h12
-rw-r--r--drivers/video/geode/lxfb_ops.c4
-rw-r--r--drivers/video/geode/suspend_gx.c2
-rw-r--r--drivers/video/geode/video_gx.c2
-rw-r--r--drivers/video/hitfb.c2
-rw-r--r--drivers/video/i810/i810_dvt.c53
-rw-r--r--drivers/video/intelfb/intelfbdrv.c3
-rw-r--r--drivers/video/intelfb/intelfbhw.c47
-rw-r--r--drivers/video/intelfb/intelfbhw.h1
-rw-r--r--drivers/video/matrox/g450_pll.c3
-rw-r--r--drivers/video/maxinefb.c3
-rw-r--r--drivers/video/mb862xx/Makefile2
-rw-r--r--drivers/video/mb862xx/mb862xxfb.c14
-rw-r--r--drivers/video/mb862xx/mb862xxfb.h2
-rw-r--r--drivers/video/mb862xx/mb862xxfb_accel.c331
-rw-r--r--drivers/video/mb862xx/mb862xxfb_accel.h203
-rw-r--r--drivers/video/modedb.c24
-rw-r--r--drivers/video/omap/lcd_2430sdp.c4
-rw-r--r--drivers/video/output.c2
-rw-r--r--drivers/video/pmag-ba-fb.c3
-rw-r--r--drivers/video/pmagb-b-fb.c3
-rw-r--r--drivers/video/pxafb.c7
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c2
-rw-r--r--drivers/video/sis/sis_main.c2
-rw-r--r--drivers/video/sm501fb.c249
-rw-r--r--drivers/video/via/lcd.c40
-rw-r--r--drivers/video/via/viafbdev.c2
-rw-r--r--drivers/watchdog/adx_wdt.c2
-rw-r--r--drivers/watchdog/twl4030_wdt.c4
582 files changed, 29838 insertions, 10108 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 26e434ad373c..8a07363417ed 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -96,6 +96,8 @@ source "drivers/edac/Kconfig"
source "drivers/rtc/Kconfig"
+source "drivers/clocksource/Kconfig"
+
source "drivers/dma/Kconfig"
source "drivers/dca/Kconfig"
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 27fd775375b0..958bd1540c30 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -131,7 +131,7 @@ static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute
* boot up and this data does not change there after. Hence this
* operation should be safe. No locking required.
*/
- addr = __pa(per_cpu_ptr(crash_notes, cpunum));
+ addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum));
rc = sprintf(buf, "%Lx\n", addr);
return rc;
}
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 1fe5536d404f..70122791683d 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -173,6 +173,47 @@ static ssize_t node_read_distance(struct sys_device * dev,
}
static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL);
+#ifdef CONFIG_HUGETLBFS
+/*
+ * hugetlbfs per node attributes registration interface:
+ * When/if hugetlb[fs] subsystem initializes [sometime after this module],
+ * it will register its per node attributes for all online nodes with
+ * memory. It will also call register_hugetlbfs_with_node(), below, to
+ * register its attribute registration functions with this node driver.
+ * Once these hooks have been initialized, the node driver will call into
+ * the hugetlb module to [un]register attributes for hot-plugged nodes.
+ */
+static node_registration_func_t __hugetlb_register_node;
+static node_registration_func_t __hugetlb_unregister_node;
+
+static inline bool hugetlb_register_node(struct node *node)
+{
+ if (__hugetlb_register_node &&
+ node_state(node->sysdev.id, N_HIGH_MEMORY)) {
+ __hugetlb_register_node(node);
+ return true;
+ }
+ return false;
+}
+
+static inline void hugetlb_unregister_node(struct node *node)
+{
+ if (__hugetlb_unregister_node)
+ __hugetlb_unregister_node(node);
+}
+
+void register_hugetlbfs_with_node(node_registration_func_t doregister,
+ node_registration_func_t unregister)
+{
+ __hugetlb_register_node = doregister;
+ __hugetlb_unregister_node = unregister;
+}
+#else
+static inline void hugetlb_register_node(struct node *node) {}
+
+static inline void hugetlb_unregister_node(struct node *node) {}
+#endif
+
/*
* register_node - Setup a sysfs device for a node.
@@ -196,6 +237,8 @@ int register_node(struct node *node, int num, struct node *parent)
sysdev_create_file(&node->sysdev, &attr_distance);
scan_unevictable_register_node(node);
+
+ hugetlb_register_node(node);
}
return error;
}
@@ -216,6 +259,7 @@ void unregister_node(struct node *node)
sysdev_remove_file(&node->sysdev, &attr_distance);
scan_unevictable_unregister_node(node);
+ hugetlb_unregister_node(node); /* no-op, if memoryless node */
sysdev_unregister(&node->sysdev);
}
@@ -227,26 +271,43 @@ struct node node_devices[MAX_NUMNODES];
*/
int register_cpu_under_node(unsigned int cpu, unsigned int nid)
{
- if (node_online(nid)) {
- struct sys_device *obj = get_cpu_sysdev(cpu);
- if (!obj)
- return 0;
- return sysfs_create_link(&node_devices[nid].sysdev.kobj,
- &obj->kobj,
- kobject_name(&obj->kobj));
- }
+ int ret;
+ struct sys_device *obj;
- return 0;
+ if (!node_online(nid))
+ return 0;
+
+ obj = get_cpu_sysdev(cpu);
+ if (!obj)
+ return 0;
+
+ ret = sysfs_create_link(&node_devices[nid].sysdev.kobj,
+ &obj->kobj,
+ kobject_name(&obj->kobj));
+ if (ret)
+ return ret;
+
+ return sysfs_create_link(&obj->kobj,
+ &node_devices[nid].sysdev.kobj,
+ kobject_name(&node_devices[nid].sysdev.kobj));
}
int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
{
- if (node_online(nid)) {
- struct sys_device *obj = get_cpu_sysdev(cpu);
- if (obj)
- sysfs_remove_link(&node_devices[nid].sysdev.kobj,
- kobject_name(&obj->kobj));
- }
+ struct sys_device *obj;
+
+ if (!node_online(nid))
+ return 0;
+
+ obj = get_cpu_sysdev(cpu);
+ if (!obj)
+ return 0;
+
+ sysfs_remove_link(&node_devices[nid].sysdev.kobj,
+ kobject_name(&obj->kobj));
+ sysfs_remove_link(&obj->kobj,
+ kobject_name(&node_devices[nid].sysdev.kobj));
+
return 0;
}
@@ -268,6 +329,7 @@ static int get_nid_for_pfn(unsigned long pfn)
/* register memory section under specified node if it spans that node */
int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
{
+ int ret;
unsigned long pfn, sect_start_pfn, sect_end_pfn;
if (!mem_blk)
@@ -284,9 +346,15 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
continue;
if (page_nid != nid)
continue;
- return sysfs_create_link_nowarn(&node_devices[nid].sysdev.kobj,
+ ret = sysfs_create_link_nowarn(&node_devices[nid].sysdev.kobj,
&mem_blk->sysdev.kobj,
kobject_name(&mem_blk->sysdev.kobj));
+ if (ret)
+ return ret;
+
+ return sysfs_create_link_nowarn(&mem_blk->sysdev.kobj,
+ &node_devices[nid].sysdev.kobj,
+ kobject_name(&node_devices[nid].sysdev.kobj));
}
/* mem section does not span the specified node */
return 0;
@@ -295,12 +363,16 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
/* unregister memory section under all nodes that it spans */
int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
{
- nodemask_t unlinked_nodes;
+ NODEMASK_ALLOC(nodemask_t, unlinked_nodes, GFP_KERNEL);
unsigned long pfn, sect_start_pfn, sect_end_pfn;
- if (!mem_blk)
+ if (!mem_blk) {
+ NODEMASK_FREE(unlinked_nodes);
return -EFAULT;
- nodes_clear(unlinked_nodes);
+ }
+ if (!unlinked_nodes)
+ return -ENOMEM;
+ nodes_clear(*unlinked_nodes);
sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index);
sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
@@ -311,11 +383,14 @@ int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
continue;
if (!node_online(nid))
continue;
- if (node_test_and_set(nid, unlinked_nodes))
+ if (node_test_and_set(nid, *unlinked_nodes))
continue;
sysfs_remove_link(&node_devices[nid].sysdev.kobj,
kobject_name(&mem_blk->sysdev.kobj));
+ sysfs_remove_link(&mem_blk->sysdev.kobj,
+ kobject_name(&node_devices[nid].sysdev.kobj));
}
+ NODEMASK_FREE(unlinked_nodes);
return 0;
}
@@ -345,9 +420,77 @@ static int link_mem_sections(int nid)
}
return err;
}
-#else
+
+#ifdef CONFIG_HUGETLBFS
+/*
+ * Handle per node hstate attribute [un]registration on transistions
+ * to/from memoryless state.
+ */
+static void node_hugetlb_work(struct work_struct *work)
+{
+ struct node *node = container_of(work, struct node, node_work);
+
+ /*
+ * We only get here when a node transitions to/from memoryless state.
+ * We can detect which transition occurred by examining whether the
+ * node has memory now. hugetlb_register_node() already check this
+ * so we try to register the attributes. If that fails, then the
+ * node has transitioned to memoryless, try to unregister the
+ * attributes.
+ */
+ if (!hugetlb_register_node(node))
+ hugetlb_unregister_node(node);
+}
+
+static void init_node_hugetlb_work(int nid)
+{
+ INIT_WORK(&node_devices[nid].node_work, node_hugetlb_work);
+}
+
+static int node_memory_callback(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ struct memory_notify *mnb = arg;
+ int nid = mnb->status_change_nid;
+
+ switch (action) {
+ case MEM_ONLINE:
+ case MEM_OFFLINE:
+ /*
+ * offload per node hstate [un]registration to a work thread
+ * when transitioning to/from memoryless state.
+ */
+ if (nid != NUMA_NO_NODE)
+ schedule_work(&node_devices[nid].node_work);
+ break;
+
+ case MEM_GOING_ONLINE:
+ case MEM_GOING_OFFLINE:
+ case MEM_CANCEL_ONLINE:
+ case MEM_CANCEL_OFFLINE:
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+#endif /* CONFIG_HUGETLBFS */
+#else /* !CONFIG_MEMORY_HOTPLUG_SPARSE */
+
static int link_mem_sections(int nid) { return 0; }
-#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+
+#if !defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || \
+ !defined(CONFIG_HUGETLBFS)
+static inline int node_memory_callback(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ return NOTIFY_OK;
+}
+
+static void init_node_hugetlb_work(int nid) { }
+
+#endif
int register_one_node(int nid)
{
@@ -371,6 +514,9 @@ int register_one_node(int nid)
/* link memory sections under this node */
error = link_mem_sections(nid);
+
+ /* initialize work queue for memory hot plug */
+ init_node_hugetlb_work(nid);
}
return error;
@@ -460,13 +606,17 @@ static int node_states_init(void)
return err;
}
+#define NODE_CALLBACK_PRI 2 /* lower than SLAB */
static int __init register_node_type(void)
{
int ret;
ret = sysdev_class_register(&node_class);
- if (!ret)
+ if (!ret) {
ret = node_states_init();
+ hotplug_memory_notifier(node_memory_callback,
+ NODE_CALLBACK_PRI);
+ }
/*
* Note: we're not going to unregister the node class if we fail
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 436a090b532b..4e0726aa53b0 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1271,8 +1271,7 @@ static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
goto fail;
}
- if (crypto_tfm_alg_type(crypto_hash_tfm(tfm))
- != CRYPTO_ALG_TYPE_HASH) {
+ if (crypto_tfm_alg_type(crypto_hash_tfm(tfm)) != CRYPTO_ALG_TYPE_SHASH) {
retcode = ERR_AUTH_ALG_ND;
goto fail;
}
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 5c01f747571b..3266b4f65daa 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -3497,6 +3497,9 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
return -EPERM;
+ if (WARN_ON(size < 0 || size > sizeof(inparam)))
+ return -EINVAL;
+
/* copyin */
CLEARSTRUCT(&inparam);
if (_IOC_DIR(cmd) & _IOC_WRITE)
@@ -4162,7 +4165,7 @@ static int floppy_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops floppy_pm_ops = {
+static const struct dev_pm_ops floppy_pm_ops = {
.resume = floppy_resume,
.restore = floppy_resume,
};
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index 0877d3628fda..d1fd032e7514 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -169,13 +169,6 @@ static int __init xd_init(void)
init_timer (&xd_watchdog_int); xd_watchdog_int.function = xd_watchdog;
- if (!xd_dma_buffer)
- xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200);
- if (!xd_dma_buffer) {
- printk(KERN_ERR "xd: Out of memory.\n");
- return -ENOMEM;
- }
-
err = -EBUSY;
if (register_blkdev(XT_DISK_MAJOR, "xd"))
goto out1;
@@ -202,6 +195,19 @@ static int __init xd_init(void)
xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma);
}
+ /*
+ * With the drive detected, xd_maxsectors should now be known.
+ * If xd_maxsectors is 0, nothing was detected and we fall through
+ * to return -ENODEV
+ */
+ if (!xd_dma_buffer && xd_maxsectors) {
+ xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200);
+ if (!xd_dma_buffer) {
+ printk(KERN_ERR "xd: Out of memory.\n");
+ goto out3;
+ }
+ }
+
err = -ENODEV;
if (!xd_drives)
goto out3;
@@ -249,15 +255,17 @@ out4:
for (i = 0; i < xd_drives; i++)
put_disk(xd_gendisk[i]);
out3:
- release_region(xd_iobase,4);
+ if (xd_maxsectors)
+ release_region(xd_iobase,4);
+
+ if (xd_dma_buffer)
+ xd_dma_mem_free((unsigned long)xd_dma_buffer,
+ xd_maxsectors * 0x200);
out2:
blk_cleanup_queue(xd_queue);
out1a:
unregister_blkdev(XT_DISK_MAJOR, "xd");
out1:
- if (xd_dma_buffer)
- xd_dma_mem_free((unsigned long)xd_dma_buffer,
- xd_maxsectors * 0x200);
return err;
Enomem:
err = -ENOMEM;
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index 26a47dc88f61..53c524e7b829 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -285,6 +285,7 @@ static const struct file_operations efi_rtc_fops = {
.unlocked_ioctl = efi_rtc_ioctl,
.open = efi_rtc_open,
.release = efi_rtc_close,
+ .llseek = no_llseek,
};
static struct miscdevice efi_rtc_dev= {
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index b8a5d654d3d0..fe62bd0e17b7 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -931,7 +931,7 @@ static struct hv_ops hvc_iucv_ops = {
};
/* Suspend / resume device operations */
-static struct dev_pm_ops hvc_iucv_pm_ops = {
+static const struct dev_pm_ops hvc_iucv_pm_ops = {
.freeze = hvc_iucv_pm_freeze,
.thaw = hvc_iucv_pm_restore_thaw,
.restore = hvc_iucv_pm_restore_thaw,
diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c
index 80704875794c..cf82fedae099 100644
--- a/drivers/char/ipmi/ipmi_kcs_sm.c
+++ b/drivers/char/ipmi/ipmi_kcs_sm.c
@@ -370,7 +370,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
return SI_SM_IDLE;
case KCS_START_OP:
- if (state != KCS_IDLE) {
+ if (state != KCS_IDLE_STATE) {
start_error_recovery(kcs,
"State machine not idle at start");
break;
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index fba76fb55abf..be832b6f8279 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -34,6 +34,16 @@
# include <linux/efi.h>
#endif
+static inline unsigned long size_inside_page(unsigned long start,
+ unsigned long size)
+{
+ unsigned long sz;
+
+ sz = PAGE_SIZE - (start & (PAGE_SIZE - 1));
+
+ return min(sz, size);
+}
+
/*
* Architectures vary in how they handle caching for addresses
* outside of main memory.
@@ -126,9 +136,7 @@ static ssize_t read_mem(struct file * file, char __user * buf,
#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
/* we don't have page 0 mapped on sparc and m68k.. */
if (p < PAGE_SIZE) {
- sz = PAGE_SIZE - p;
- if (sz > count)
- sz = count;
+ sz = size_inside_page(p, count);
if (sz > 0) {
if (clear_user(buf, sz))
return -EFAULT;
@@ -141,15 +149,9 @@ static ssize_t read_mem(struct file * file, char __user * buf,
#endif
while (count > 0) {
- /*
- * Handle first page in case it's not aligned
- */
- if (-p & (PAGE_SIZE - 1))
- sz = -p & (PAGE_SIZE - 1);
- else
- sz = PAGE_SIZE;
+ unsigned long remaining;
- sz = min_t(unsigned long, sz, count);
+ sz = size_inside_page(p, count);
if (!range_is_allowed(p >> PAGE_SHIFT, count))
return -EPERM;
@@ -163,12 +165,10 @@ static ssize_t read_mem(struct file * file, char __user * buf,
if (!ptr)
return -EFAULT;
- if (copy_to_user(buf, ptr, sz)) {
- unxlate_dev_mem_ptr(p, ptr);
- return -EFAULT;
- }
-
+ remaining = copy_to_user(buf, ptr, sz);
unxlate_dev_mem_ptr(p, ptr);
+ if (remaining)
+ return -EFAULT;
buf += sz;
p += sz;
@@ -196,9 +196,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
/* we don't have page 0 mapped on sparc and m68k.. */
if (p < PAGE_SIZE) {
- unsigned long sz = PAGE_SIZE - p;
- if (sz > count)
- sz = count;
+ sz = size_inside_page(p, count);
/* Hmm. Do something? */
buf += sz;
p += sz;
@@ -208,15 +206,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
#endif
while (count > 0) {
- /*
- * Handle first page in case it's not aligned
- */
- if (-p & (PAGE_SIZE - 1))
- sz = -p & (PAGE_SIZE - 1);
- else
- sz = PAGE_SIZE;
-
- sz = min_t(unsigned long, sz, count);
+ sz = size_inside_page(p, count);
if (!range_is_allowed(p >> PAGE_SHIFT, sz))
return -EPERM;
@@ -234,16 +224,14 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
}
copied = copy_from_user(ptr, buf, sz);
+ unxlate_dev_mem_ptr(p, ptr);
if (copied) {
written += sz - copied;
- unxlate_dev_mem_ptr(p, ptr);
if (written)
break;
return -EFAULT;
}
- unxlate_dev_mem_ptr(p, ptr);
-
buf += sz;
p += sz;
count -= sz;
@@ -417,27 +405,18 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
/* we don't have page 0 mapped on sparc and m68k.. */
if (p < PAGE_SIZE && low_count > 0) {
- size_t tmp = PAGE_SIZE - p;
- if (tmp > low_count) tmp = low_count;
- if (clear_user(buf, tmp))
+ sz = size_inside_page(p, low_count);
+ if (clear_user(buf, sz))
return -EFAULT;
- buf += tmp;
- p += tmp;
- read += tmp;
- low_count -= tmp;
- count -= tmp;
+ buf += sz;
+ p += sz;
+ read += sz;
+ low_count -= sz;
+ count -= sz;
}
#endif
while (low_count > 0) {
- /*
- * Handle first page in case it's not aligned
- */
- if (-p & (PAGE_SIZE - 1))
- sz = -p & (PAGE_SIZE - 1);
- else
- sz = PAGE_SIZE;
-
- sz = min_t(unsigned long, sz, low_count);
+ sz = size_inside_page(p, low_count);
/*
* On ia64 if a page has been mapped somewhere as
@@ -461,21 +440,18 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
if (!kbuf)
return -ENOMEM;
while (count > 0) {
- int len = count;
-
- if (len > PAGE_SIZE)
- len = PAGE_SIZE;
- len = vread(kbuf, (char *)p, len);
- if (!len)
+ sz = size_inside_page(p, count);
+ sz = vread(kbuf, (char *)p, sz);
+ if (!sz)
break;
- if (copy_to_user(buf, kbuf, len)) {
+ if (copy_to_user(buf, kbuf, sz)) {
free_page((unsigned long)kbuf);
return -EFAULT;
}
- count -= len;
- buf += len;
- read += len;
- p += len;
+ count -= sz;
+ buf += sz;
+ read += sz;
+ p += sz;
}
free_page((unsigned long)kbuf);
}
@@ -485,7 +461,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
static inline ssize_t
-do_write_kmem(void *p, unsigned long realp, const char __user * buf,
+do_write_kmem(unsigned long p, const char __user *buf,
size_t count, loff_t *ppos)
{
ssize_t written, sz;
@@ -494,14 +470,11 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
written = 0;
#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
/* we don't have page 0 mapped on sparc and m68k.. */
- if (realp < PAGE_SIZE) {
- unsigned long sz = PAGE_SIZE - realp;
- if (sz > count)
- sz = count;
+ if (p < PAGE_SIZE) {
+ sz = size_inside_page(p, count);
/* Hmm. Do something? */
buf += sz;
p += sz;
- realp += sz;
count -= sz;
written += sz;
}
@@ -509,22 +482,15 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
while (count > 0) {
char *ptr;
- /*
- * Handle first page in case it's not aligned
- */
- if (-realp & (PAGE_SIZE - 1))
- sz = -realp & (PAGE_SIZE - 1);
- else
- sz = PAGE_SIZE;
- sz = min_t(unsigned long, sz, count);
+ sz = size_inside_page(p, count);
/*
* On ia64 if a page has been mapped somewhere as
* uncached, then it must also be accessed uncached
* by the kernel or data corruption may occur
*/
- ptr = xlate_dev_kmem_ptr(p);
+ ptr = xlate_dev_kmem_ptr((char *)p);
copied = copy_from_user(ptr, buf, sz);
if (copied) {
@@ -535,7 +501,6 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
}
buf += sz;
p += sz;
- realp += sz;
count -= sz;
written += sz;
}
@@ -554,19 +519,14 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
unsigned long p = *ppos;
ssize_t wrote = 0;
ssize_t virtr = 0;
- ssize_t written;
char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
if (p < (unsigned long) high_memory) {
-
- wrote = count;
- if (count > (unsigned long) high_memory - p)
- wrote = (unsigned long) high_memory - p;
-
- written = do_write_kmem((void*)p, p, buf, wrote, ppos);
- if (written != wrote)
- return written;
- wrote = written;
+ unsigned long to_write = min_t(unsigned long, count,
+ (unsigned long)high_memory - p);
+ wrote = do_write_kmem(p, buf, to_write, ppos);
+ if (wrote != to_write)
+ return wrote;
p += wrote;
buf += wrote;
count -= wrote;
@@ -577,24 +537,21 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
if (!kbuf)
return wrote ? wrote : -ENOMEM;
while (count > 0) {
- int len = count;
-
- if (len > PAGE_SIZE)
- len = PAGE_SIZE;
- if (len) {
- written = copy_from_user(kbuf, buf, len);
- if (written) {
- if (wrote + virtr)
- break;
- free_page((unsigned long)kbuf);
- return -EFAULT;
- }
+ unsigned long sz = size_inside_page(p, count);
+ unsigned long n;
+
+ n = copy_from_user(kbuf, buf, sz);
+ if (n) {
+ if (wrote + virtr)
+ break;
+ free_page((unsigned long)kbuf);
+ return -EFAULT;
}
- len = vwrite(kbuf, (char *)p, len);
- count -= len;
- buf += len;
- virtr += len;
- p += len;
+ sz = vwrite(kbuf, (char *)p, sz);
+ count -= sz;
+ buf += sz;
+ virtr += sz;
+ p += sz;
}
free_page((unsigned long)kbuf);
}
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 96f1cd086dd2..94a136e96c06 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -60,9 +60,7 @@ static DEFINE_MUTEX(misc_mtx);
* Assigned numbers, used for dynamic minors
*/
#define DYNAMIC_MINORS 64 /* like dynamic majors */
-static unsigned char misc_minors[DYNAMIC_MINORS / 8];
-
-extern int pmu_device_init(void);
+static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
#ifdef CONFIG_PROC_FS
static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
@@ -198,24 +196,23 @@ int misc_register(struct miscdevice * misc)
}
if (misc->minor == MISC_DYNAMIC_MINOR) {
- int i = DYNAMIC_MINORS;
- while (--i >= 0)
- if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
- break;
- if (i<0) {
+ int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
+ if (i >= DYNAMIC_MINORS) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
- misc->minor = i;
+ misc->minor = DYNAMIC_MINORS - i - 1;
+ set_bit(i, misc_minors);
}
- if (misc->minor < DYNAMIC_MINORS)
- misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
+ int i = DYNAMIC_MINORS - misc->minor - 1;
+ if (i < DYNAMIC_MINORS && i >= 0)
+ clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device);
goto out;
}
@@ -242,7 +239,7 @@ int misc_register(struct miscdevice * misc)
int misc_deregister(struct miscdevice *misc)
{
- int i = misc->minor;
+ int i = DYNAMIC_MINORS - misc->minor - 1;
if (list_empty(&misc->list))
return -EINVAL;
@@ -250,9 +247,8 @@ int misc_deregister(struct miscdevice *misc)
mutex_lock(&misc_mtx);
list_del(&misc->list);
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
- if (i < DYNAMIC_MINORS && i>0) {
- misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
- }
+ if (i < DYNAMIC_MINORS && i >= 0)
+ clear_bit(i, misc_minors);
mutex_unlock(&misc_mtx);
return 0;
}
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 4008e2ce73c1..fdbcc9fd6d31 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -264,10 +264,16 @@ static ssize_t nvram_write(struct file *file, const char __user *buf,
unsigned char contents[NVRAM_BYTES];
unsigned i = *ppos;
unsigned char *tmp;
- int len;
- len = (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count;
- if (copy_from_user(contents, buf, len))
+ if (i >= NVRAM_BYTES)
+ return 0; /* Past EOF */
+
+ if (count > NVRAM_BYTES - i)
+ count = NVRAM_BYTES - i;
+ if (count > NVRAM_BYTES)
+ return -EFAULT; /* Can't happen, but prove it to gcc */
+
+ if (copy_from_user(contents, buf, count))
return -EFAULT;
spin_lock_irq(&rtc_lock);
@@ -275,7 +281,7 @@ static ssize_t nvram_write(struct file *file, const char __user *buf,
if (!__nvram_check_checksum())
goto checksum_err;
- for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
+ for (tmp = contents; count--; ++i, ++tmp)
__nvram_write_byte(*tmp, i);
__nvram_set_checksum();
diff --git a/drivers/char/random.c b/drivers/char/random.c
index dcd08635cf1b..8258982b49ec 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1245,12 +1245,8 @@ static int proc_do_uuid(ctl_table *table, int write,
if (uuid[8] == 0)
generate_random_uuid(uuid);
- sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x",
- uuid[0], uuid[1], uuid[2], uuid[3],
- uuid[4], uuid[5], uuid[6], uuid[7],
- uuid[8], uuid[9], uuid[10], uuid[11],
- uuid[12], uuid[13], uuid[14], uuid[15]);
+ sprintf(buf, "%pU", uuid);
+
fake_table.data = buf;
fake_table.maxlen = sizeof(buf);
@@ -1310,7 +1306,7 @@ ctl_table random_table[] = {
/********************************************************************
*
- * Random funtions for networking
+ * Random functions for networking
*
********************************************************************/
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index 44203ff599da..1ae2de7d8b4f 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -339,7 +339,7 @@ static struct sysrq_key_op sysrq_term_op = {
static void moom_callback(struct work_struct *ignored)
{
- out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0);
+ out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0, NULL);
}
static DECLARE_WORK(moom_work, moom_callback);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 1e3d728dbf7e..50faa1fb0f06 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -164,6 +164,9 @@ module_param(default_utf8, int, S_IRUGO | S_IWUSR);
int global_cursor_default = -1;
module_param(global_cursor_default, int, S_IRUGO | S_IWUSR);
+static int cur_default = CUR_DEFAULT;
+module_param(cur_default, int, S_IRUGO | S_IWUSR);
+
/*
* ignore_poke: don't unblank the screen when things are typed. This is
* mainly for the privacy of braille terminal users.
@@ -184,12 +187,10 @@ static DECLARE_WORK(console_work, console_callback);
* fg_console is the current virtual console,
* last_console is the last used one,
* want_console is the console we want to switch to,
- * kmsg_redirect is the console for kernel messages,
*/
int fg_console;
int last_console;
int want_console = -1;
-int kmsg_redirect;
/*
* For each existing display, we have a pointer to console currently visible
@@ -1638,7 +1639,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
/* do not do set_leds here because this causes an endless tasklet loop
when the keyboard hasn't been initialized yet */
- vc->vc_cursor_type = CUR_DEFAULT;
+ vc->vc_cursor_type = cur_default;
vc->vc_complement_mask = vc->vc_s_complement_mask;
default_attr(vc);
@@ -1840,7 +1841,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
if (vc->vc_par[0])
vc->vc_cursor_type = vc->vc_par[0] | (vc->vc_par[1] << 8) | (vc->vc_par[2] << 16);
else
- vc->vc_cursor_type = CUR_DEFAULT;
+ vc->vc_cursor_type = cur_default;
return;
}
break;
@@ -2434,6 +2435,37 @@ struct tty_driver *console_driver;
#ifdef CONFIG_VT_CONSOLE
+/**
+ * vt_kmsg_redirect() - Sets/gets the kernel message console
+ * @new: The new virtual terminal number or -1 if the console should stay
+ * unchanged
+ *
+ * By default, the kernel messages are always printed on the current virtual
+ * console. However, the user may modify that default with the
+ * TIOCL_SETKMSGREDIRECT ioctl call.
+ *
+ * This function sets the kernel message console to be @new. It returns the old
+ * virtual console number. The virtual terminal number 0 (both as parameter and
+ * return value) means no redirection (i.e. always printed on the currently
+ * active console).
+ *
+ * The parameter -1 means that only the current console is returned, but the
+ * value is not modified. You may use the macro vt_get_kmsg_redirect() in that
+ * case to make the code more understandable.
+ *
+ * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores
+ * the parameter and always returns 0.
+ */
+int vt_kmsg_redirect(int new)
+{
+ static int kmsg_con;
+
+ if (new != -1)
+ return xchg(&kmsg_con, new);
+ else
+ return kmsg_con;
+}
+
/*
* Console on virtual terminal
*
@@ -2448,6 +2480,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
const ushort *start;
ushort cnt = 0;
ushort myx;
+ int kmsg_console;
/* console busy or not yet initialized */
if (!printable)
@@ -2455,8 +2488,9 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
if (!spin_trylock(&printing_lock))
return;
- if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
- vc = vc_cons[kmsg_redirect - 1].d;
+ kmsg_console = vt_get_kmsg_redirect();
+ if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
+ vc = vc_cons[kmsg_console - 1].d;
/* read `x' only after setting currcons properly (otherwise
the `x' macro will read the x of the foreground console). */
@@ -2613,7 +2647,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
ret = set_vesa_blanking(p);
break;
case TIOCL_GETKMSGREDIRECT:
- data = kmsg_redirect;
+ data = vt_get_kmsg_redirect();
ret = __put_user(data, p);
break;
case TIOCL_SETKMSGREDIRECT:
@@ -2623,7 +2657,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
if (get_user(data, p+1))
ret = -EFAULT;
else
- kmsg_redirect = data;
+ vt_kmsg_redirect(data);
}
break;
case TIOCL_GETFGCONSOLE:
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
new file mode 100644
index 000000000000..08f726c5fee5
--- /dev/null
+++ b/drivers/clocksource/Kconfig
@@ -0,0 +1,9 @@
+config CS5535_CLOCK_EVENT_SRC
+ tristate "CS5535/CS5536 high-res timer (MFGPT) events"
+ depends on GENERIC_TIME && GENERIC_CLOCKEVENTS && CS5535_MFGPT
+ help
+ This driver provides a clock event source based on the MFGPT
+ timer(s) in the CS5535 and CS5536 companion chips.
+ MFGPTs have a better resolution and max interval than the
+ generic PIT, and are suitable for use as high-res timers.
+
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index eef216f7f61d..be61ece6330b 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
+obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
new file mode 100644
index 000000000000..27d20fac19d1
--- /dev/null
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -0,0 +1,197 @@
+/*
+ * Clock event driver for the CS5535/CS5536
+ *
+ * Copyright (C) 2006, Advanced Micro Devices, Inc.
+ * Copyright (C) 2007 Andres Salomon <dilinger@debian.org>
+ * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/cs5535.h>
+#include <linux/clockchips.h>
+
+#define DRV_NAME "cs5535-clockevt"
+
+static int timer_irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
+module_param_named(irq, timer_irq, int, 0644);
+MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
+
+/*
+ * We are using the 32.768kHz input clock - it's the only one that has the
+ * ranges we find desirable. The following table lists the suitable
+ * divisors and the associated Hz, minimum interval and the maximum interval:
+ *
+ * Divisor Hz Min Delta (s) Max Delta (s)
+ * 1 32768 .00048828125 2.000
+ * 2 16384 .0009765625 4.000
+ * 4 8192 .001953125 8.000
+ * 8 4096 .00390625 16.000
+ * 16 2048 .0078125 32.000
+ * 32 1024 .015625 64.000
+ * 64 512 .03125 128.000
+ * 128 256 .0625 256.000
+ * 256 128 .125 512.000
+ */
+
+static unsigned int cs5535_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
+static struct cs5535_mfgpt_timer *cs5535_event_clock;
+
+/* Selected from the table above */
+
+#define MFGPT_DIVISOR 16
+#define MFGPT_SCALE 4 /* divisor = 2^(scale) */
+#define MFGPT_HZ (32768 / MFGPT_DIVISOR)
+#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
+
+/*
+ * The MFPGT timers on the CS5536 provide us with suitable timers to use
+ * as clock event sources - not as good as a HPET or APIC, but certainly
+ * better than the PIT. This isn't a general purpose MFGPT driver, but
+ * a simplified one designed specifically to act as a clock event source.
+ * For full details about the MFGPT, please consult the CS5536 data sheet.
+ */
+
+static void disable_timer(struct cs5535_mfgpt_timer *timer)
+{
+ /* avoid races by clearing CMP1 and CMP2 unconditionally */
+ cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
+ (uint16_t) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 |
+ MFGPT_SETUP_CMP2);
+}
+
+static void start_timer(struct cs5535_mfgpt_timer *timer, uint16_t delta)
+{
+ cs5535_mfgpt_write(timer, MFGPT_REG_CMP2, delta);
+ cs5535_mfgpt_write(timer, MFGPT_REG_COUNTER, 0);
+
+ cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
+ MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
+}
+
+static void mfgpt_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ disable_timer(cs5535_event_clock);
+
+ if (mode == CLOCK_EVT_MODE_PERIODIC)
+ start_timer(cs5535_event_clock, MFGPT_PERIODIC);
+
+ cs5535_tick_mode = mode;
+}
+
+static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
+{
+ start_timer(cs5535_event_clock, delta);
+ return 0;
+}
+
+static struct clock_event_device cs5535_clockevent = {
+ .name = DRV_NAME,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = mfgpt_set_mode,
+ .set_next_event = mfgpt_next_event,
+ .rating = 250,
+ .cpumask = cpu_all_mask,
+ .shift = 32
+};
+
+static irqreturn_t mfgpt_tick(int irq, void *dev_id)
+{
+ uint16_t val = cs5535_mfgpt_read(cs5535_event_clock, MFGPT_REG_SETUP);
+
+ /* See if the interrupt was for us */
+ if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
+ return IRQ_NONE;
+
+ /* Turn off the clock (and clear the event) */
+ disable_timer(cs5535_event_clock);
+
+ if (cs5535_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
+ return IRQ_HANDLED;
+
+ /* Clear the counter */
+ cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_COUNTER, 0);
+
+ /* Restart the clock in periodic mode */
+
+ if (cs5535_tick_mode == CLOCK_EVT_MODE_PERIODIC)
+ cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP,
+ MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
+
+ cs5535_clockevent.event_handler(&cs5535_clockevent);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction mfgptirq = {
+ .handler = mfgpt_tick,
+ .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
+ .name = DRV_NAME,
+};
+
+static int __init cs5535_mfgpt_init(void)
+{
+ struct cs5535_mfgpt_timer *timer;
+ int ret;
+ uint16_t val;
+
+ timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
+ if (!timer) {
+ printk(KERN_ERR DRV_NAME ": Could not allocate MFPGT timer\n");
+ return -ENODEV;
+ }
+ cs5535_event_clock = timer;
+
+ /* Set up the IRQ on the MFGPT side */
+ if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) {
+ printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n",
+ timer_irq);
+ return -EIO;
+ }
+
+ /* And register it with the kernel */
+ ret = setup_irq(timer_irq, &mfgptirq);
+ if (ret) {
+ printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n");
+ goto err;
+ }
+
+ /* Set the clock scale and enable the event mode for CMP2 */
+ val = MFGPT_SCALE | (3 << 8);
+
+ cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
+
+ /* Set up the clock event */
+ cs5535_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC,
+ cs5535_clockevent.shift);
+ cs5535_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
+ &cs5535_clockevent);
+ cs5535_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
+ &cs5535_clockevent);
+
+ printk(KERN_INFO DRV_NAME
+ ": Registering MFGPT timer as a clock event, using IRQ %d\n",
+ timer_irq);
+ clockevents_register_device(&cs5535_clockevent);
+
+ return 0;
+
+err:
+ cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq);
+ printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n");
+ return -EIO;
+}
+
+module_init(cs5535_mfgpt_init);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
+MODULE_DESCRIPTION("CS5535/CS5536 MFGPT clock event driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index f20668c09ce0..67bc2ece7b4b 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -64,14 +64,14 @@ static DEFINE_SPINLOCK(cpufreq_driver_lock);
* - Lock should not be held across
* __cpufreq_governor(data, CPUFREQ_GOV_STOP);
*/
-static DEFINE_PER_CPU(int, policy_cpu);
+static DEFINE_PER_CPU(int, cpufreq_policy_cpu);
static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem);
#define lock_policy_rwsem(mode, cpu) \
int lock_policy_rwsem_##mode \
(int cpu) \
{ \
- int policy_cpu = per_cpu(policy_cpu, cpu); \
+ int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \
BUG_ON(policy_cpu == -1); \
down_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \
if (unlikely(!cpu_online(cpu))) { \
@@ -90,7 +90,7 @@ EXPORT_SYMBOL_GPL(lock_policy_rwsem_write);
void unlock_policy_rwsem_read(int cpu)
{
- int policy_cpu = per_cpu(policy_cpu, cpu);
+ int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu);
BUG_ON(policy_cpu == -1);
up_read(&per_cpu(cpu_policy_rwsem, policy_cpu));
}
@@ -98,7 +98,7 @@ EXPORT_SYMBOL_GPL(unlock_policy_rwsem_read);
void unlock_policy_rwsem_write(int cpu)
{
- int policy_cpu = per_cpu(policy_cpu, cpu);
+ int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu);
BUG_ON(policy_cpu == -1);
up_write(&per_cpu(cpu_policy_rwsem, policy_cpu));
}
@@ -818,7 +818,7 @@ static int cpufreq_add_dev_policy(unsigned int cpu,
/* Set proper policy_cpu */
unlock_policy_rwsem_write(cpu);
- per_cpu(policy_cpu, cpu) = managed_policy->cpu;
+ per_cpu(cpufreq_policy_cpu, cpu) = managed_policy->cpu;
if (lock_policy_rwsem_write(cpu) < 0) {
/* Should not go through policy unlock path */
@@ -932,7 +932,7 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
if (!cpu_online(j))
continue;
per_cpu(cpufreq_cpu_data, j) = policy;
- per_cpu(policy_cpu, j) = policy->cpu;
+ per_cpu(cpufreq_policy_cpu, j) = policy->cpu;
}
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
@@ -1020,7 +1020,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
cpumask_copy(policy->cpus, cpumask_of(cpu));
/* Initially set CPU itself as the policy_cpu */
- per_cpu(policy_cpu, cpu) = cpu;
+ per_cpu(cpufreq_policy_cpu, cpu) = cpu;
ret = (lock_policy_rwsem_write(cpu) < 0);
WARN_ON(ret);
@@ -2002,7 +2002,7 @@ static int __init cpufreq_core_init(void)
int cpu;
for_each_possible_cpu(cpu) {
- per_cpu(policy_cpu, cpu) = -1;
+ per_cpu(cpufreq_policy_cpu, cpu) = -1;
init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
}
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index a9bd3a05a684..05432216e224 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -174,7 +174,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
-static DEFINE_PER_CPU(struct cpufreq_frequency_table *, show_table);
+static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table);
/**
* show_available_freqs - show available frequencies for the specified CPU
*/
@@ -185,10 +185,10 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
ssize_t count = 0;
struct cpufreq_frequency_table *table;
- if (!per_cpu(show_table, cpu))
+ if (!per_cpu(cpufreq_show_table, cpu))
return -ENODEV;
- table = per_cpu(show_table, cpu);
+ table = per_cpu(cpufreq_show_table, cpu);
for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
@@ -217,20 +217,20 @@ void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
unsigned int cpu)
{
dprintk("setting show_table for cpu %u to %p\n", cpu, table);
- per_cpu(show_table, cpu) = table;
+ per_cpu(cpufreq_show_table, cpu) = table;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);
void cpufreq_frequency_table_put_attr(unsigned int cpu)
{
dprintk("clearing show_table for cpu %u\n", cpu);
- per_cpu(show_table, cpu) = NULL;
+ per_cpu(cpufreq_show_table, cpu) = NULL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
{
- return per_cpu(show_table, cpu);
+ return per_cpu(cpufreq_show_table, cpu);
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
index a4bec3f919aa..1c1ceb4f218f 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -69,9 +69,6 @@ static int ladder_select_state(struct cpuidle_device *dev)
int last_residency, last_idx = ldev->last_state_idx;
int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY);
- if (unlikely(!ldev))
- return 0;
-
/* Special case when user has set very strict latency requirement */
if (unlikely(latency_req == 0)) {
ladder_do_selection(ldev, last_idx, 0);
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index 84c51e177269..8c2f3703ec85 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -64,7 +64,7 @@ struct aes_ctx {
u32 *D;
};
-static DEFINE_PER_CPU(struct cword *, last_cword);
+static DEFINE_PER_CPU(struct cword *, paes_last_cword);
/* Tells whether the ACE is capable to generate
the extended key for a given key_len. */
@@ -152,9 +152,9 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
ok:
for_each_online_cpu(cpu)
- if (&ctx->cword.encrypt == per_cpu(last_cword, cpu) ||
- &ctx->cword.decrypt == per_cpu(last_cword, cpu))
- per_cpu(last_cword, cpu) = NULL;
+ if (&ctx->cword.encrypt == per_cpu(paes_last_cword, cpu) ||
+ &ctx->cword.decrypt == per_cpu(paes_last_cword, cpu))
+ per_cpu(paes_last_cword, cpu) = NULL;
return 0;
}
@@ -166,7 +166,7 @@ static inline void padlock_reset_key(struct cword *cword)
{
int cpu = raw_smp_processor_id();
- if (cword != per_cpu(last_cword, cpu))
+ if (cword != per_cpu(paes_last_cword, cpu))
#ifndef CONFIG_X86_64
asm volatile ("pushfl; popfl");
#else
@@ -176,7 +176,7 @@ static inline void padlock_reset_key(struct cword *cword)
static inline void padlock_store_cword(struct cword *cword)
{
- per_cpu(last_cword, raw_smp_processor_id()) = cword;
+ per_cpu(paes_last_cword, raw_smp_processor_id()) = cword;
}
/*
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index c52ac9efd0bf..f15112569c1d 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1188,7 +1188,7 @@ static int at_dma_resume_noirq(struct device *dev)
return 0;
}
-static struct dev_pm_ops at_dma_dev_pm_ops = {
+static const struct dev_pm_ops at_dma_dev_pm_ops = {
.suspend_noirq = at_dma_suspend_noirq,
.resume_noirq = at_dma_resume_noirq,
};
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 8f99354082ce..6f51a0a7a8bb 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -326,14 +326,7 @@ arch_initcall(dma_channel_table_init);
*/
struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type)
{
- struct dma_chan *chan;
- int cpu;
-
- cpu = get_cpu();
- chan = per_cpu_ptr(channel_table[tx_type], cpu)->chan;
- put_cpu();
-
- return chan;
+ return this_cpu_read(channel_table[tx_type]->chan);
}
EXPORT_SYMBOL(dma_find_channel);
@@ -857,7 +850,6 @@ dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest,
struct dma_async_tx_descriptor *tx;
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
- int cpu;
unsigned long flags;
dma_src = dma_map_single(dev->dev, src, len, DMA_TO_DEVICE);
@@ -876,10 +868,10 @@ dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest,
tx->callback = NULL;
cookie = tx->tx_submit(tx);
- cpu = get_cpu();
- per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
- per_cpu_ptr(chan->local, cpu)->memcpy_count++;
- put_cpu();
+ preempt_disable();
+ __this_cpu_add(chan->local->bytes_transferred, len);
+ __this_cpu_inc(chan->local->memcpy_count);
+ preempt_enable();
return cookie;
}
@@ -906,7 +898,6 @@ dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page,
struct dma_async_tx_descriptor *tx;
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
- int cpu;
unsigned long flags;
dma_src = dma_map_single(dev->dev, kdata, len, DMA_TO_DEVICE);
@@ -923,10 +914,10 @@ dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page,
tx->callback = NULL;
cookie = tx->tx_submit(tx);
- cpu = get_cpu();
- per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
- per_cpu_ptr(chan->local, cpu)->memcpy_count++;
- put_cpu();
+ preempt_disable();
+ __this_cpu_add(chan->local->bytes_transferred, len);
+ __this_cpu_inc(chan->local->memcpy_count);
+ preempt_enable();
return cookie;
}
@@ -955,7 +946,6 @@ dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg,
struct dma_async_tx_descriptor *tx;
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
- int cpu;
unsigned long flags;
dma_src = dma_map_page(dev->dev, src_pg, src_off, len, DMA_TO_DEVICE);
@@ -973,10 +963,10 @@ dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg,
tx->callback = NULL;
cookie = tx->tx_submit(tx);
- cpu = get_cpu();
- per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
- per_cpu_ptr(chan->local, cpu)->memcpy_count++;
- put_cpu();
+ preempt_disable();
+ __this_cpu_add(chan->local->bytes_transferred, len);
+ __this_cpu_inc(chan->local->memcpy_count);
+ preempt_enable();
return cookie;
}
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c
index 2eea823516a7..285bed0fe17b 100644
--- a/drivers/dma/dw_dmac.c
+++ b/drivers/dma/dw_dmac.c
@@ -1427,7 +1427,7 @@ static int dw_resume_noirq(struct device *dev)
return 0;
}
-static struct dev_pm_ops dw_dev_pm_ops = {
+static const struct dev_pm_ops dw_dev_pm_ops = {
.suspend_noirq = dw_suspend_noirq,
.resume_noirq = dw_resume_noirq,
};
diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c
index fb6bb64e8861..3ebc61067e54 100644
--- a/drivers/dma/txx9dmac.c
+++ b/drivers/dma/txx9dmac.c
@@ -1313,7 +1313,7 @@ static int txx9dmac_resume_noirq(struct device *dev)
}
-static struct dev_pm_ops txx9dmac_dev_pm_ops = {
+static const struct dev_pm_ops txx9dmac_dev_pm_ops = {
.suspend_noirq = txx9dmac_suspend_noirq,
.resume_noirq = txx9dmac_resume_noirq,
};
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 5fdd6daa40ea..df5b68433f34 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -13,6 +13,8 @@ module_param(report_gart_errors, int, 0644);
static int ecc_enable_override;
module_param(ecc_enable_override, int, 0644);
+static struct msr *msrs;
+
/* Lookup table for all possible MC control instances */
struct amd64_pvt;
static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES];
@@ -2495,8 +2497,7 @@ static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid)
static bool amd64_nb_mce_bank_enabled_on_node(int nid)
{
cpumask_var_t mask;
- struct msr *msrs;
- int cpu, nbe, idx = 0;
+ int cpu, nbe;
bool ret = false;
if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
@@ -2507,32 +2508,22 @@ static bool amd64_nb_mce_bank_enabled_on_node(int nid)
get_cpus_on_this_dct_cpumask(mask, nid);
- msrs = kzalloc(sizeof(struct msr) * cpumask_weight(mask), GFP_KERNEL);
- if (!msrs) {
- amd64_printk(KERN_WARNING, "%s: error allocating msrs\n",
- __func__);
- free_cpumask_var(mask);
- return false;
- }
-
rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
for_each_cpu(cpu, mask) {
- nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE;
+ struct msr *reg = per_cpu_ptr(msrs, cpu);
+ nbe = reg->l & K8_MSR_MCGCTL_NBE;
debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
- cpu, msrs[idx].q,
+ cpu, reg->q,
(nbe ? "enabled" : "disabled"));
if (!nbe)
goto out;
-
- idx++;
}
ret = true;
out:
- kfree(msrs);
free_cpumask_var(mask);
return ret;
}
@@ -2540,8 +2531,7 @@ out:
static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on)
{
cpumask_var_t cmask;
- struct msr *msrs = NULL;
- int cpu, idx = 0;
+ int cpu;
if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
amd64_printk(KERN_WARNING, "%s: error allocating mask\n",
@@ -2551,34 +2541,27 @@ static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on)
get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id);
- msrs = kzalloc(sizeof(struct msr) * cpumask_weight(cmask), GFP_KERNEL);
- if (!msrs) {
- amd64_printk(KERN_WARNING, "%s: error allocating msrs\n",
- __func__);
- return -ENOMEM;
- }
-
rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
for_each_cpu(cpu, cmask) {
+ struct msr *reg = per_cpu_ptr(msrs, cpu);
+
if (on) {
- if (msrs[idx].l & K8_MSR_MCGCTL_NBE)
+ if (reg->l & K8_MSR_MCGCTL_NBE)
pvt->flags.ecc_report = 1;
- msrs[idx].l |= K8_MSR_MCGCTL_NBE;
+ reg->l |= K8_MSR_MCGCTL_NBE;
} else {
/*
* Turn off ECC reporting only when it was off before
*/
if (!pvt->flags.ecc_report)
- msrs[idx].l &= ~K8_MSR_MCGCTL_NBE;
+ reg->l &= ~K8_MSR_MCGCTL_NBE;
}
- idx++;
}
wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
- kfree(msrs);
free_cpumask_var(cmask);
return 0;
@@ -3036,6 +3019,8 @@ static int __init amd64_edac_init(void)
if (cache_k8_northbridges() < 0)
return err;
+ msrs = msrs_alloc();
+
err = pci_register_driver(&amd64_pci_driver);
if (err)
return err;
@@ -3071,6 +3056,9 @@ static void __exit amd64_edac_exit(void)
edac_pci_release_generic_ctl(amd64_ctl_pci);
pci_unregister_driver(&amd64_pci_driver);
+
+ msrs_free(msrs);
+ msrs = NULL;
}
module_init(amd64_edac_init);
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index 22db05a67bfb..7785d8ffa404 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -9,6 +9,11 @@
* Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
* http://download.intel.com/design/chipsets/datashts/318378.pdf
*
+ * The intel 5100 has two independent channels. EDAC core currently
+ * can not reflect this configuration so instead the chip-select
+ * rows for each respective channel are layed out one after another,
+ * the first half belonging to channel 0, the second half belonging
+ * to channel 1.
*/
#include <linux/module.h>
#include <linux/init.h>
@@ -25,6 +30,8 @@
/* device 16, func 1 */
#define I5100_MC 0x40 /* Memory Control Register */
+#define I5100_MC_SCRBEN_MASK (1 << 7)
+#define I5100_MC_SCRBDONE_MASK (1 << 4)
#define I5100_MS 0x44 /* Memory Status Register */
#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
@@ -72,11 +79,21 @@
/* bit field accessors */
+static inline u32 i5100_mc_scrben(u32 mc)
+{
+ return mc >> 7 & 1;
+}
+
static inline u32 i5100_mc_errdeten(u32 mc)
{
return mc >> 5 & 1;
}
+static inline u32 i5100_mc_scrbdone(u32 mc)
+{
+ return mc >> 4 & 1;
+}
+
static inline u16 i5100_spddata_rdo(u16 a)
{
return a >> 15 & 1;
@@ -265,42 +282,43 @@ static inline u32 i5100_recmemb_ras(u32 a)
}
/* some generic limits */
-#define I5100_MAX_RANKS_PER_CTLR 6
-#define I5100_MAX_CTLRS 2
+#define I5100_MAX_RANKS_PER_CHAN 6
+#define I5100_CHANNELS 2
#define I5100_MAX_RANKS_PER_DIMM 4
#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */
-#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4
+#define I5100_MAX_DIMM_SLOTS_PER_CHAN 4
#define I5100_MAX_RANK_INTERLEAVE 4
#define I5100_MAX_DMIRS 5
+#define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ)
struct i5100_priv {
/* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
- int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR];
+ int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
/*
* mainboard chip select map -- maps i5100 chip selects to
* DIMM slot chip selects. In the case of only 4 ranks per
- * controller, the mapping is fairly obvious but not unique.
- * we map -1 -> NC and assume both controllers use the same
+ * channel, the mapping is fairly obvious but not unique.
+ * we map -1 -> NC and assume both channels use the same
* map...
*
*/
- int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM];
+ int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
/* memory interleave range */
struct {
u64 limit;
unsigned way[2];
- } mir[I5100_MAX_CTLRS];
+ } mir[I5100_CHANNELS];
/* adjusted memory interleave range register */
- unsigned amir[I5100_MAX_CTLRS];
+ unsigned amir[I5100_CHANNELS];
/* dimm interleave range */
struct {
unsigned rank[I5100_MAX_RANK_INTERLEAVE];
u64 limit;
- } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS];
+ } dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
/* memory technology registers... */
struct {
@@ -310,30 +328,33 @@ struct i5100_priv {
unsigned numbank; /* 2 or 3 lines */
unsigned numrow; /* 13 .. 16 lines */
unsigned numcol; /* 11 .. 12 lines */
- } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR];
+ } mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
u64 tolm; /* top of low memory in bytes */
- unsigned ranksperctlr; /* number of ranks per controller */
+ unsigned ranksperchan; /* number of ranks per channel */
struct pci_dev *mc; /* device 16 func 1 */
struct pci_dev *ch0mm; /* device 21 func 0 */
struct pci_dev *ch1mm; /* device 22 func 0 */
+
+ struct delayed_work i5100_scrubbing;
+ int scrub_enable;
};
-/* map a rank/ctlr to a slot number on the mainboard */
+/* map a rank/chan to a slot number on the mainboard */
static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
- int ctlr, int rank)
+ int chan, int rank)
{
const struct i5100_priv *priv = mci->pvt_info;
int i;
- for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
+ for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
int j;
- const int numrank = priv->dimm_numrank[ctlr][i];
+ const int numrank = priv->dimm_numrank[chan][i];
for (j = 0; j < numrank; j++)
if (priv->dimm_csmap[i][j] == rank)
- return i * 2 + ctlr;
+ return i * 2 + chan;
}
return -1;
@@ -374,32 +395,32 @@ static const char *i5100_err_msg(unsigned err)
return "none";
}
-/* convert csrow index into a rank (per controller -- 0..5) */
+/* convert csrow index into a rank (per channel -- 0..5) */
static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
{
const struct i5100_priv *priv = mci->pvt_info;
- return csrow % priv->ranksperctlr;
+ return csrow % priv->ranksperchan;
}
-/* convert csrow index into a controller (0..1) */
-static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow)
+/* convert csrow index into a channel (0..1) */
+static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow)
{
const struct i5100_priv *priv = mci->pvt_info;
- return csrow / priv->ranksperctlr;
+ return csrow / priv->ranksperchan;
}
static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
- int ctlr, int rank)
+ int chan, int rank)
{
const struct i5100_priv *priv = mci->pvt_info;
- return ctlr * priv->ranksperctlr + rank;
+ return chan * priv->ranksperchan + rank;
}
static void i5100_handle_ce(struct mem_ctl_info *mci,
- int ctlr,
+ int chan,
unsigned bank,
unsigned rank,
unsigned long syndrome,
@@ -407,12 +428,12 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
unsigned ras,
const char *msg)
{
- const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
+ const int csrow = i5100_rank_to_csrow(mci, chan, rank);
printk(KERN_ERR
- "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
+ "CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
- ctlr, bank, rank, syndrome, cas, ras,
+ chan, bank, rank, syndrome, cas, ras,
csrow, mci->csrows[csrow].channels[0].label, msg);
mci->ce_count++;
@@ -421,7 +442,7 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
}
static void i5100_handle_ue(struct mem_ctl_info *mci,
- int ctlr,
+ int chan,
unsigned bank,
unsigned rank,
unsigned long syndrome,
@@ -429,23 +450,23 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
unsigned ras,
const char *msg)
{
- const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
+ const int csrow = i5100_rank_to_csrow(mci, chan, rank);
printk(KERN_ERR
- "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
+ "UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
- ctlr, bank, rank, syndrome, cas, ras,
+ chan, bank, rank, syndrome, cas, ras,
csrow, mci->csrows[csrow].channels[0].label, msg);
mci->ue_count++;
mci->csrows[csrow].ue_count++;
}
-static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
+static void i5100_read_log(struct mem_ctl_info *mci, int chan,
u32 ferr, u32 nerr)
{
struct i5100_priv *priv = mci->pvt_info;
- struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm;
+ struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
u32 dw;
u32 dw2;
unsigned syndrome = 0;
@@ -484,7 +505,7 @@ static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
else
msg = i5100_err_msg(nerr);
- i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
+ i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
}
if (i5100_validlog_nrecmemvalid(dw)) {
@@ -506,7 +527,7 @@ static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
else
msg = i5100_err_msg(nerr);
- i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
+ i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
}
pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
@@ -534,6 +555,80 @@ static void i5100_check_error(struct mem_ctl_info *mci)
}
}
+/* The i5100 chipset will scrub the entire memory once, then
+ * set a done bit. Continuous scrubbing is achieved by enqueing
+ * delayed work to a workqueue, checking every few minutes if
+ * the scrubbing has completed and if so reinitiating it.
+ */
+
+static void i5100_refresh_scrubbing(struct work_struct *work)
+{
+ struct delayed_work *i5100_scrubbing = container_of(work,
+ struct delayed_work,
+ work);
+ struct i5100_priv *priv = container_of(i5100_scrubbing,
+ struct i5100_priv,
+ i5100_scrubbing);
+ u32 dw;
+
+ pci_read_config_dword(priv->mc, I5100_MC, &dw);
+
+ if (priv->scrub_enable) {
+
+ pci_read_config_dword(priv->mc, I5100_MC, &dw);
+
+ if (i5100_mc_scrbdone(dw)) {
+ dw |= I5100_MC_SCRBEN_MASK;
+ pci_write_config_dword(priv->mc, I5100_MC, dw);
+ pci_read_config_dword(priv->mc, I5100_MC, &dw);
+ }
+
+ schedule_delayed_work(&(priv->i5100_scrubbing),
+ I5100_SCRUB_REFRESH_RATE);
+ }
+}
+/*
+ * The bandwidth is based on experimentation, feel free to refine it.
+ */
+static int i5100_set_scrub_rate(struct mem_ctl_info *mci,
+ u32 *bandwidth)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ u32 dw;
+
+ pci_read_config_dword(priv->mc, I5100_MC, &dw);
+ if (*bandwidth) {
+ priv->scrub_enable = 1;
+ dw |= I5100_MC_SCRBEN_MASK;
+ schedule_delayed_work(&(priv->i5100_scrubbing),
+ I5100_SCRUB_REFRESH_RATE);
+ } else {
+ priv->scrub_enable = 0;
+ dw &= ~I5100_MC_SCRBEN_MASK;
+ cancel_delayed_work(&(priv->i5100_scrubbing));
+ }
+ pci_write_config_dword(priv->mc, I5100_MC, dw);
+
+ pci_read_config_dword(priv->mc, I5100_MC, &dw);
+
+ *bandwidth = 5900000 * i5100_mc_scrben(dw);
+
+ return 0;
+}
+
+static int i5100_get_scrub_rate(struct mem_ctl_info *mci,
+ u32 *bandwidth)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ u32 dw;
+
+ pci_read_config_dword(priv->mc, I5100_MC, &dw);
+
+ *bandwidth = 5900000 * i5100_mc_scrben(dw);
+
+ return 0;
+}
+
static struct pci_dev *pci_get_device_func(unsigned vendor,
unsigned device,
unsigned func)
@@ -557,19 +652,19 @@ static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci,
int csrow)
{
struct i5100_priv *priv = mci->pvt_info;
- const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow);
- const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow);
+ const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow);
+ const unsigned chan = i5100_csrow_to_chan(mci, csrow);
unsigned addr_lines;
/* dimm present? */
- if (!priv->mtr[ctlr][ctlr_rank].present)
+ if (!priv->mtr[chan][chan_rank].present)
return 0ULL;
addr_lines =
I5100_DIMM_ADDR_LINES +
- priv->mtr[ctlr][ctlr_rank].numcol +
- priv->mtr[ctlr][ctlr_rank].numrow +
- priv->mtr[ctlr][ctlr_rank].numbank;
+ priv->mtr[chan][chan_rank].numcol +
+ priv->mtr[chan][chan_rank].numrow +
+ priv->mtr[chan][chan_rank].numbank;
return (unsigned long)
((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
@@ -581,11 +676,11 @@ static void __devinit i5100_init_mtr(struct mem_ctl_info *mci)
struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
int i;
- for (i = 0; i < I5100_MAX_CTLRS; i++) {
+ for (i = 0; i < I5100_CHANNELS; i++) {
int j;
struct pci_dev *pdev = mms[i];
- for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) {
+ for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
const unsigned addr =
(j < 4) ? I5100_MTR_0 + j * 2 :
I5100_MTR_4 + (j - 4) * 2;
@@ -644,7 +739,6 @@ static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
* fill dimm chip select map
*
* FIXME:
- * o only valid for 4 ranks per controller
* o not the only way to may chip selects to dimm slots
* o investigate if there is some way to obtain this map from the bios
*/
@@ -653,9 +747,7 @@ static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
struct i5100_priv *priv = mci->pvt_info;
int i;
- WARN_ON(priv->ranksperctlr != 4);
-
- for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
+ for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
int j;
for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
@@ -663,12 +755,21 @@ static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
}
/* only 2 chip selects per slot... */
- priv->dimm_csmap[0][0] = 0;
- priv->dimm_csmap[0][1] = 3;
- priv->dimm_csmap[1][0] = 1;
- priv->dimm_csmap[1][1] = 2;
- priv->dimm_csmap[2][0] = 2;
- priv->dimm_csmap[3][0] = 3;
+ if (priv->ranksperchan == 4) {
+ priv->dimm_csmap[0][0] = 0;
+ priv->dimm_csmap[0][1] = 3;
+ priv->dimm_csmap[1][0] = 1;
+ priv->dimm_csmap[1][1] = 2;
+ priv->dimm_csmap[2][0] = 2;
+ priv->dimm_csmap[3][0] = 3;
+ } else {
+ priv->dimm_csmap[0][0] = 0;
+ priv->dimm_csmap[0][1] = 1;
+ priv->dimm_csmap[1][0] = 2;
+ priv->dimm_csmap[1][1] = 3;
+ priv->dimm_csmap[2][0] = 4;
+ priv->dimm_csmap[2][1] = 5;
+ }
}
static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
@@ -677,10 +778,10 @@ static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
struct i5100_priv *priv = mci->pvt_info;
int i;
- for (i = 0; i < I5100_MAX_CTLRS; i++) {
+ for (i = 0; i < I5100_CHANNELS; i++) {
int j;
- for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) {
+ for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
u8 rank;
if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
@@ -720,7 +821,7 @@ static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
pci_read_config_word(pdev, I5100_AMIR_1, &w);
priv->amir[1] = w;
- for (i = 0; i < I5100_MAX_CTLRS; i++) {
+ for (i = 0; i < I5100_CHANNELS; i++) {
int j;
for (j = 0; j < 5; j++) {
@@ -747,7 +848,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
for (i = 0; i < mci->nr_csrows; i++) {
const unsigned long npages = i5100_npages(mci, i);
- const unsigned cntlr = i5100_csrow_to_cntlr(mci, i);
+ const unsigned chan = i5100_csrow_to_chan(mci, i);
const unsigned rank = i5100_csrow_to_rank(mci, i);
if (!npages)
@@ -765,7 +866,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
mci->csrows[i].grain = 32;
mci->csrows[i].csrow_idx = i;
mci->csrows[i].dtype =
- (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8;
+ (priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8;
mci->csrows[i].ue_count = 0;
mci->csrows[i].ce_count = 0;
mci->csrows[i].mtype = MEM_RDDR2;
@@ -777,7 +878,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
mci->csrows[i].channels[0].csrow = mci->csrows + i;
snprintf(mci->csrows[i].channels[0].label,
sizeof(mci->csrows[i].channels[0].label),
- "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank));
+ "DIMM%u", i5100_rank_to_slot(mci, chan, rank));
total_pages += npages;
}
@@ -815,13 +916,6 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
pci_read_config_dword(pdev, I5100_MS, &dw);
ranksperch = !!(dw & (1 << 8)) * 2 + 4;
- if (ranksperch != 4) {
- /* FIXME: get 6 ranks / controller to work - need hw... */
- printk(KERN_INFO "i5100_edac: unsupported configuration.\n");
- ret = -ENODEV;
- goto bail_pdev;
- }
-
/* enable error reporting... */
pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
@@ -864,11 +958,21 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
mci->dev = &pdev->dev;
priv = mci->pvt_info;
- priv->ranksperctlr = ranksperch;
+ priv->ranksperchan = ranksperch;
priv->mc = pdev;
priv->ch0mm = ch0mm;
priv->ch1mm = ch1mm;
+ INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing);
+
+ /* If scrubbing was already enabled by the bios, start maintaining it */
+ pci_read_config_dword(pdev, I5100_MC, &dw);
+ if (i5100_mc_scrben(dw)) {
+ priv->scrub_enable = 1;
+ schedule_delayed_work(&(priv->i5100_scrubbing),
+ I5100_SCRUB_REFRESH_RATE);
+ }
+
i5100_init_dimm_layout(pdev, mci);
i5100_init_interleaving(pdev, mci);
@@ -882,6 +986,8 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
mci->ctl_page_to_phys = NULL;
mci->edac_check = i5100_check_error;
+ mci->set_sdram_scrub_rate = i5100_set_scrub_rate;
+ mci->get_sdram_scrub_rate = i5100_get_scrub_rate;
i5100_init_csrows(mci);
@@ -897,12 +1003,14 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
if (edac_mc_add_mc(mci)) {
ret = -ENODEV;
- goto bail_mc;
+ goto bail_scrub;
}
return ret;
-bail_mc:
+bail_scrub:
+ priv->scrub_enable = 0;
+ cancel_delayed_work_sync(&(priv->i5100_scrubbing));
edac_mc_free(mci);
bail_disable_ch1:
@@ -935,6 +1043,10 @@ static void __devexit i5100_remove_one(struct pci_dev *pdev)
return;
priv = mci->pvt_info;
+
+ priv->scrub_enable = 0;
+ cancel_delayed_work_sync(&(priv->i5100_scrubbing));
+
pci_disable_device(pdev);
pci_disable_device(priv->ch0mm);
pci_disable_device(priv->ch1mm);
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index ebb9e51deb0c..1b03ba1d0834 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -7,7 +7,7 @@ menu "Firmware Drivers"
config EDD
tristate "BIOS Enhanced Disk Drive calls determine boot disk"
- depends on !IA64
+ depends on X86
help
Say Y or M here if you want to enable BIOS Enhanced Disk Drive
Services real mode BIOS calls to determine which disk
@@ -28,7 +28,7 @@ config EDD_OFF
config FIRMWARE_MEMMAP
bool "Add firmware-provided memory map to sysfs" if EMBEDDED
- default (X86_64 || X86_32)
+ default X86
help
Add the firmware-provided (unmodified) memory map to /sys/firmware/memmap.
That memory map is used for example by kexec to set up parameter area
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 3a2ccb09e2f8..31b983d9462c 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -169,10 +169,7 @@ static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, int inde
if (!s)
return;
- sprintf(s,
- "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
- d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+ sprintf(s, "%pUB", d);
dmi_ident[slot] = s;
}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2ad0128c63c6..a019b49ecc9b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -174,6 +174,16 @@ config GPIO_ADP5520
comment "PCI GPIO expanders:"
+config GPIO_CS5535
+ tristate "AMD CS5535/CS5536 GPIO support"
+ depends on PCI && !CS5535_GPIO
+ help
+ The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
+ can be used for quite a number of things. The CS5535/6 is found on
+ AMD Geode and Lemote Yeeloong devices.
+
+ If unsure, say N.
+
config GPIO_BT8XX
tristate "BT8XX GPIO abuser"
depends on PCI && VIDEO_BT848=n
@@ -196,6 +206,12 @@ config GPIO_LANGWELL
help
Say Y here to support Intel Moorestown platform GPIO.
+config GPIO_TIMBERDALE
+ bool "Support for timberdale GPIO IP"
+ depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM
+ ---help---
+ Add support for the GPIO IP in the timberdale FPGA.
+
comment "SPI GPIO expanders:"
config GPIO_MAX7301
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 00a532c9a1e2..52fe4cf734c7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -13,9 +13,11 @@ 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_TIMBERDALE) += timbgpio.o
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o
+obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o
obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/adp5520-gpio.c
index ad05bbc7ffd5..0f93105873cd 100644
--- a/drivers/gpio/adp5520-gpio.c
+++ b/drivers/gpio/adp5520-gpio.c
@@ -34,9 +34,9 @@ static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
*/
if (test_bit(off, &dev->output))
- adp5520_read(dev->master, GPIO_OUT, &reg_val);
+ adp5520_read(dev->master, ADP5520_GPIO_OUT, &reg_val);
else
- adp5520_read(dev->master, GPIO_IN, &reg_val);
+ adp5520_read(dev->master, ADP5520_GPIO_IN, &reg_val);
return !!(reg_val & dev->lut[off]);
}
@@ -48,9 +48,9 @@ static void adp5520_gpio_set_value(struct gpio_chip *chip,
dev = container_of(chip, struct adp5520_gpio, gpio_chip);
if (val)
- adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
+ adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
else
- adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
+ adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]);
}
static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
@@ -60,7 +60,8 @@ static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
clear_bit(off, &dev->output);
- return adp5520_clr_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
+ return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2,
+ dev->lut[off]);
}
static int adp5520_gpio_direction_output(struct gpio_chip *chip,
@@ -73,18 +74,21 @@ static int adp5520_gpio_direction_output(struct gpio_chip *chip,
set_bit(off, &dev->output);
if (val)
- ret |= adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT,
+ dev->lut[off]);
else
- ret |= adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
+ ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT,
+ dev->lut[off]);
- ret |= adp5520_set_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2,
+ dev->lut[off]);
return ret;
}
static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
{
- struct adp5520_gpio_platfrom_data *pdata = pdev->dev.platform_data;
+ struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data;
struct adp5520_gpio *dev;
struct gpio_chip *gc;
int ret, i, gpios;
@@ -129,20 +133,20 @@ static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
gc->label = pdev->name;
gc->owner = THIS_MODULE;
- ret = adp5520_clr_bits(dev->master, GPIO_CFG_1,
+ ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1,
pdata->gpio_en_mask);
- if (pdata->gpio_en_mask & GPIO_C3)
- ctl_mask |= C3_MODE;
+ if (pdata->gpio_en_mask & ADP5520_GPIO_C3)
+ ctl_mask |= ADP5520_C3_MODE;
- if (pdata->gpio_en_mask & GPIO_R3)
- ctl_mask |= R3_MODE;
+ if (pdata->gpio_en_mask & ADP5520_GPIO_R3)
+ ctl_mask |= ADP5520_R3_MODE;
if (ctl_mask)
- ret = adp5520_set_bits(dev->master, LED_CONTROL,
+ ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
ctl_mask);
- ret |= adp5520_set_bits(dev->master, GPIO_PULLUP,
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
pdata->gpio_pullup_mask);
if (ret) {
diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/cs5535-gpio.c
new file mode 100644
index 000000000000..0fdbe94f24a3
--- /dev/null
+++ b/drivers/gpio/cs5535-gpio.c
@@ -0,0 +1,355 @@
+/*
+ * AMD CS5535/CS5536 GPIO driver
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ * Copyright (C) 2007-2009 Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/cs5535.h>
+
+#define DRV_NAME "cs5535-gpio"
+#define GPIO_BAR 1
+
+/*
+ * Some GPIO pins
+ * 31-29,23 : reserved (always mask out)
+ * 28 : Power Button
+ * 26 : PME#
+ * 22-16 : LPC
+ * 14,15 : SMBus
+ * 9,8 : UART1
+ * 7 : PCI INTB
+ * 3,4 : UART2/DDC
+ * 2 : IDE_IRQ0
+ * 1 : AC_BEEP
+ * 0 : PCI INTA
+ *
+ * If a mask was not specified, allow all except
+ * reserved and Power Button
+ */
+#define GPIO_DEFAULT_MASK 0x0F7FFFFF
+
+static ulong mask = GPIO_DEFAULT_MASK;
+module_param_named(mask, mask, ulong, 0444);
+MODULE_PARM_DESC(mask, "GPIO channel mask.");
+
+static struct cs5535_gpio_chip {
+ struct gpio_chip chip;
+ resource_size_t base;
+
+ struct pci_dev *pdev;
+ spinlock_t lock;
+} cs5535_gpio_chip;
+
+/*
+ * The CS5535/CS5536 GPIOs support a number of extra features not defined
+ * by the gpio_chip API, so these are exported. For a full list of the
+ * registers, see include/linux/cs5535.h.
+ */
+
+static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
+ unsigned int reg)
+{
+ if (offset < 16)
+ /* low bank register */
+ outl(1 << offset, chip->base + reg);
+ else
+ /* high bank register */
+ outl(1 << (offset - 16), chip->base + 0x80 + reg);
+}
+
+void cs5535_gpio_set(unsigned offset, unsigned int reg)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __cs5535_gpio_set(chip, offset, reg);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_set);
+
+static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
+ unsigned int reg)
+{
+ if (offset < 16)
+ /* low bank register */
+ outl(1 << (offset + 16), chip->base + reg);
+ else
+ /* high bank register */
+ outl(1 << offset, chip->base + 0x80 + reg);
+}
+
+void cs5535_gpio_clear(unsigned offset, unsigned int reg)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __cs5535_gpio_clear(chip, offset, reg);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
+
+int cs5535_gpio_isset(unsigned offset, unsigned int reg)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ unsigned long flags;
+ long val;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ if (offset < 16)
+ /* low bank register */
+ val = inl(chip->base + reg);
+ else {
+ /* high bank register */
+ val = inl(chip->base + 0x80 + reg);
+ offset -= 16;
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return (val & (1 << offset)) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
+
+/*
+ * Generic gpio_chip API support.
+ */
+
+static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
+{
+ struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ /* check if this pin is available */
+ if ((mask & (1 << offset)) == 0) {
+ dev_info(&chip->pdev->dev,
+ "pin %u is not available (check mask)\n", offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ /* disable output aux 1 & 2 on this pin */
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
+
+ /* disable input aux 1 on this pin */
+ __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return cs5535_gpio_isset(offset, GPIO_OUTPUT_VAL);
+}
+
+static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ if (val)
+ cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
+ else
+ cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
+}
+
+static int chip_direction_input(struct gpio_chip *c, unsigned offset)
+{
+ struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
+{
+ struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+
+ __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
+ if (val)
+ __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
+ else
+ __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
+
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static char *cs5535_gpio_names[] = {
+ "GPIO0", "GPIO1", "GPIO2", "GPIO3",
+ "GPIO4", "GPIO5", "GPIO6", "GPIO7",
+ "GPIO8", "GPIO9", "GPIO10", "GPIO11",
+ "GPIO12", "GPIO13", "GPIO14", "GPIO15",
+ "GPIO16", "GPIO17", "GPIO18", "GPIO19",
+ "GPIO20", "GPIO21", "GPIO22", NULL,
+ "GPIO24", "GPIO25", "GPIO26", "GPIO27",
+ "GPIO28", NULL, NULL, NULL,
+};
+
+static struct cs5535_gpio_chip cs5535_gpio_chip = {
+ .chip = {
+ .owner = THIS_MODULE,
+ .label = DRV_NAME,
+
+ .base = 0,
+ .ngpio = 32,
+ .names = cs5535_gpio_names,
+ .request = chip_gpio_request,
+
+ .get = chip_gpio_get,
+ .set = chip_gpio_set,
+
+ .direction_input = chip_direction_input,
+ .direction_output = chip_direction_output,
+ },
+};
+
+static int __init cs5535_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ int err;
+ ulong mask_orig = mask;
+
+ /* There are two ways to get the GPIO base address; one is by
+ * fetching it from MSR_LBAR_GPIO, the other is by reading the
+ * PCI BAR info. The latter method is easier (especially across
+ * different architectures), so we'll stick with that for now. If
+ * it turns out to be unreliable in the face of crappy BIOSes, we
+ * can always go back to using MSRs.. */
+
+ err = pci_enable_device_io(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "can't enable device IO\n");
+ goto done;
+ }
+
+ err = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
+ goto done;
+ }
+
+ /* set up the driver-specific struct */
+ cs5535_gpio_chip.base = pci_resource_start(pdev, GPIO_BAR);
+ cs5535_gpio_chip.pdev = pdev;
+ spin_lock_init(&cs5535_gpio_chip.lock);
+
+ dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", GPIO_BAR,
+ (unsigned long long) cs5535_gpio_chip.base);
+
+ /* mask out reserved pins */
+ mask &= 0x1F7FFFFF;
+
+ /* do not allow pin 28, Power Button, as there's special handling
+ * in the PMC needed. (note 12, p. 48) */
+ mask &= ~(1 << 28);
+
+ if (mask_orig != mask)
+ dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
+ mask_orig, mask);
+
+ /* finally, register with the generic GPIO API */
+ err = gpiochip_add(&cs5535_gpio_chip.chip);
+ if (err)
+ goto release_region;
+
+ dev_info(&pdev->dev, DRV_NAME ": GPIO support successfully loaded.\n");
+ return 0;
+
+release_region:
+ pci_release_region(pdev, GPIO_BAR);
+done:
+ return err;
+}
+
+static void __exit cs5535_gpio_remove(struct pci_dev *pdev)
+{
+ int err;
+
+ err = gpiochip_remove(&cs5535_gpio_chip.chip);
+ if (err) {
+ /* uhh? */
+ dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
+ }
+ pci_release_region(pdev, GPIO_BAR);
+}
+
+static struct pci_device_id cs5535_gpio_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, cs5535_gpio_pci_tbl);
+
+/*
+ * We can't use the standard PCI driver registration stuff here, since
+ * that allows only one driver to bind to each PCI device (and we want
+ * multiple drivers to be able to bind to the device). Instead, manually
+ * scan for the PCI device, request a single region, and keep track of the
+ * devices that we're using.
+ */
+
+static int __init cs5535_gpio_scan_pci(void)
+{
+ struct pci_dev *pdev;
+ int err = -ENODEV;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs5535_gpio_pci_tbl); i++) {
+ pdev = pci_get_device(cs5535_gpio_pci_tbl[i].vendor,
+ cs5535_gpio_pci_tbl[i].device, NULL);
+ if (pdev) {
+ err = cs5535_gpio_probe(pdev, &cs5535_gpio_pci_tbl[i]);
+ if (err)
+ pci_dev_put(pdev);
+
+ /* we only support a single CS5535/6 southbridge */
+ break;
+ }
+ }
+
+ return err;
+}
+
+static void __exit cs5535_gpio_free_pci(void)
+{
+ cs5535_gpio_remove(cs5535_gpio_chip.pdev);
+ pci_dev_put(cs5535_gpio_chip.pdev);
+}
+
+static int __init cs5535_gpio_init(void)
+{
+ return cs5535_gpio_scan_pci();
+}
+
+static void __exit cs5535_gpio_exit(void)
+{
+ cs5535_gpio_free_pci();
+}
+
+module_init(cs5535_gpio_init);
+module_exit(cs5535_gpio_exit);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
+MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50de0f5750d8..a25ad284a272 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -53,6 +53,7 @@ struct gpio_desc {
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
#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 */
@@ -210,6 +211,11 @@ static DEFINE_MUTEX(sysfs_lock);
* * configures behavior of poll(2) on /value
* * available only if pin can generate IRQs on input
* * is read/write as "none", "falling", "rising", or "both"
+ * /active_low
+ * * configures polarity of /value
+ * * is read/write as zero/nonzero
+ * * also affects existing and subsequent "falling" and "rising"
+ * /edge configuration
*/
static ssize_t gpio_direction_show(struct device *dev,
@@ -255,7 +261,7 @@ static ssize_t gpio_direction_store(struct device *dev,
return status ? : size;
}
-static const DEVICE_ATTR(direction, 0644,
+static /* const */ DEVICE_ATTR(direction, 0644,
gpio_direction_show, gpio_direction_store);
static ssize_t gpio_value_show(struct device *dev,
@@ -267,10 +273,17 @@ static ssize_t gpio_value_show(struct device *dev,
mutex_lock(&sysfs_lock);
- if (!test_bit(FLAG_EXPORT, &desc->flags))
+ if (!test_bit(FLAG_EXPORT, &desc->flags)) {
status = -EIO;
- else
- status = sprintf(buf, "%d\n", !!gpio_get_value_cansleep(gpio));
+ } else {
+ int value;
+
+ value = !!gpio_get_value_cansleep(gpio);
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+
+ status = sprintf(buf, "%d\n", value);
+ }
mutex_unlock(&sysfs_lock);
return status;
@@ -294,6 +307,8 @@ static ssize_t gpio_value_store(struct device *dev,
status = strict_strtol(buf, 0, &value);
if (status == 0) {
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
gpio_set_value_cansleep(gpio, value != 0);
status = size;
}
@@ -303,7 +318,7 @@ static ssize_t gpio_value_store(struct device *dev,
return status;
}
-static /*const*/ DEVICE_ATTR(value, 0644,
+static const DEVICE_ATTR(value, 0644,
gpio_value_show, gpio_value_store);
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
@@ -352,9 +367,11 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
irq_flags = IRQF_SHARED;
if (test_bit(FLAG_TRIG_FALL, &gpio_flags))
- irq_flags |= IRQF_TRIGGER_FALLING;
+ irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
if (test_bit(FLAG_TRIG_RISE, &gpio_flags))
- irq_flags |= IRQF_TRIGGER_RISING;
+ irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
if (!pdesc) {
pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
@@ -475,9 +492,79 @@ found:
static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
+static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
+ int value)
+{
+ int status = 0;
+
+ if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
+ return 0;
+
+ if (value)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ else
+ clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
+
+ /* reconfigure poll(2) support if enabled on one edge only */
+ if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^
+ !!test_bit(FLAG_TRIG_FALL, &desc->flags))) {
+ unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK;
+
+ gpio_setup_irq(desc, dev, 0);
+ status = gpio_setup_irq(desc, dev, trigger_flags);
+ }
+
+ return status;
+}
+
+static ssize_t gpio_active_low_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n",
+ !!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
+
+ mutex_unlock(&sysfs_lock);
+
+ return status;
+}
+
+static ssize_t gpio_active_low_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags)) {
+ status = -EIO;
+ } else {
+ long value;
+
+ status = strict_strtol(buf, 0, &value);
+ if (status == 0)
+ status = sysfs_set_active_low(desc, dev, value != 0);
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+ return status ? : size;
+}
+
+static const DEVICE_ATTR(active_low, 0644,
+ gpio_active_low_show, gpio_active_low_store);
+
static const struct attribute *gpio_attrs[] = {
- &dev_attr_direction.attr,
&dev_attr_value.attr,
+ &dev_attr_active_low.attr,
NULL,
};
@@ -662,12 +749,12 @@ int gpio_export(unsigned gpio, bool direction_may_change)
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
desc, ioname ? ioname : "gpio%d", gpio);
if (!IS_ERR(dev)) {
- if (direction_may_change)
- status = sysfs_create_group(&dev->kobj,
+ status = sysfs_create_group(&dev->kobj,
&gpio_attr_group);
- else
+
+ if (!status && direction_may_change)
status = device_create_file(dev,
- &dev_attr_value);
+ &dev_attr_direction);
if (!status && gpio_to_irq(gpio) >= 0
&& (direction_may_change
@@ -744,6 +831,55 @@ done:
}
EXPORT_SYMBOL_GPL(gpio_export_link);
+
+/**
+ * gpio_sysfs_set_active_low - set the polarity of gpio sysfs value
+ * @gpio: gpio to change
+ * @value: non-zero to use active low, i.e. inverted values
+ *
+ * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute.
+ * The GPIO does not have to be exported yet. If poll(2) support has
+ * been enabled for either rising or falling edge, it will be
+ * reconfigured to follow the new polarity.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpio_sysfs_set_active_low(unsigned gpio, int value)
+{
+ struct gpio_desc *desc;
+ struct device *dev = NULL;
+ int status = -EINVAL;
+
+ if (!gpio_is_valid(gpio))
+ goto done;
+
+ mutex_lock(&sysfs_lock);
+
+ desc = &gpio_desc[gpio];
+
+ if (test_bit(FLAG_EXPORT, &desc->flags)) {
+ struct device *dev;
+
+ dev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (dev == NULL) {
+ status = -ENODEV;
+ goto unlock;
+ }
+ }
+
+ status = sysfs_set_active_low(desc, dev, value);
+
+unlock:
+ mutex_unlock(&sysfs_lock);
+
+done:
+ if (status)
+ pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low);
+
/**
* gpio_unexport - reverse effect of gpio_export()
* @gpio: gpio to make unavailable
@@ -1094,6 +1230,7 @@ void gpio_free(unsigned gpio)
}
desc_set_label(desc, NULL);
module_put(desc->chip->owner);
+ clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
} else
WARN_ON(extra_checks);
diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/langwell_gpio.c
index 4baf3d7d0f8e..6c0ebbdc659e 100644
--- a/drivers/gpio/langwell_gpio.c
+++ b/drivers/gpio/langwell_gpio.c
@@ -123,7 +123,7 @@ static int lnw_irq_type(unsigned irq, unsigned type)
void __iomem *grer = (void __iomem *)(&lnw->reg_base->GRER[reg]);
void __iomem *gfer = (void __iomem *)(&lnw->reg_base->GFER[reg]);
- if (gpio < 0 || gpio > lnw->chip.ngpio)
+ if (gpio >= lnw->chip.ngpio)
return -EINVAL;
spin_lock_irqsave(&lnw->lock, flags);
if (type & IRQ_TYPE_EDGE_RISING)
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c
new file mode 100644
index 000000000000..a4d344ba8e5c
--- /dev/null
+++ b/drivers/gpio/timbgpio.c
@@ -0,0 +1,342 @@
+/*
+ * timbgpio.c timberdale FPGA GPIO driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA GPIO
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/timb_gpio.h>
+#include <linux/interrupt.h>
+
+#define DRIVER_NAME "timb-gpio"
+
+#define TGPIOVAL 0x00
+#define TGPIODIR 0x04
+#define TGPIO_IER 0x08
+#define TGPIO_ISR 0x0c
+#define TGPIO_IPR 0x10
+#define TGPIO_ICR 0x14
+#define TGPIO_FLR 0x18
+#define TGPIO_LVR 0x1c
+
+struct timbgpio {
+ void __iomem *membase;
+ spinlock_t lock; /* mutual exclusion */
+ struct gpio_chip gpio;
+ int irq_base;
+};
+
+static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index,
+ unsigned offset, bool enabled)
+{
+ struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
+ u32 reg;
+
+ spin_lock(&tgpio->lock);
+ reg = ioread32(tgpio->membase + offset);
+
+ if (enabled)
+ reg |= (1 << index);
+ else
+ reg &= ~(1 << index);
+
+ iowrite32(reg, tgpio->membase + offset);
+ spin_unlock(&tgpio->lock);
+
+ return 0;
+}
+
+static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ return timbgpio_update_bit(gpio, nr, TGPIODIR, true);
+}
+
+static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
+ u32 value;
+
+ value = ioread32(tgpio->membase + TGPIOVAL);
+ return (value & (1 << nr)) ? 1 : 0;
+}
+
+static int timbgpio_gpio_direction_output(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ return timbgpio_update_bit(gpio, nr, TGPIODIR, false);
+}
+
+static void timbgpio_gpio_set(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0);
+}
+
+static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+ struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
+
+ if (tgpio->irq_base <= 0)
+ return -EINVAL;
+
+ return tgpio->irq_base + offset;
+}
+
+/*
+ * GPIO IRQ
+ */
+static void timbgpio_irq_disable(unsigned irq)
+{
+ struct timbgpio *tgpio = get_irq_chip_data(irq);
+ int offset = irq - tgpio->irq_base;
+
+ timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0);
+}
+
+static void timbgpio_irq_enable(unsigned irq)
+{
+ struct timbgpio *tgpio = get_irq_chip_data(irq);
+ int offset = irq - tgpio->irq_base;
+
+ timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1);
+}
+
+static int timbgpio_irq_type(unsigned irq, unsigned trigger)
+{
+ struct timbgpio *tgpio = get_irq_chip_data(irq);
+ int offset = irq - tgpio->irq_base;
+ unsigned long flags;
+ u32 lvr, flr;
+
+ if (offset < 0 || offset > tgpio->gpio.ngpio)
+ return -EINVAL;
+
+ spin_lock_irqsave(&tgpio->lock, flags);
+
+ lvr = ioread32(tgpio->membase + TGPIO_LVR);
+ flr = ioread32(tgpio->membase + TGPIO_FLR);
+
+ if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+ flr &= ~(1 << offset);
+ if (trigger & IRQ_TYPE_LEVEL_HIGH)
+ lvr |= 1 << offset;
+ else
+ lvr &= ~(1 << offset);
+ }
+
+ if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+ return -EINVAL;
+ else {
+ flr |= 1 << offset;
+ /* opposite compared to the datasheet, but it mirrors the
+ * reality
+ */
+ if (trigger & IRQ_TYPE_EDGE_FALLING)
+ lvr |= 1 << offset;
+ else
+ lvr &= ~(1 << offset);
+ }
+
+ iowrite32(lvr, tgpio->membase + TGPIO_LVR);
+ iowrite32(flr, tgpio->membase + TGPIO_FLR);
+ iowrite32(1 << offset, tgpio->membase + TGPIO_ICR);
+ spin_unlock_irqrestore(&tgpio->lock, flags);
+
+ return 0;
+}
+
+static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct timbgpio *tgpio = get_irq_data(irq);
+ unsigned long ipr;
+ int offset;
+
+ desc->chip->ack(irq);
+ ipr = ioread32(tgpio->membase + TGPIO_IPR);
+ iowrite32(ipr, tgpio->membase + TGPIO_ICR);
+
+ for_each_bit(offset, &ipr, tgpio->gpio.ngpio)
+ generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
+}
+
+static struct irq_chip timbgpio_irqchip = {
+ .name = "GPIO",
+ .enable = timbgpio_irq_enable,
+ .disable = timbgpio_irq_disable,
+ .set_type = timbgpio_irq_type,
+};
+
+static int __devinit timbgpio_probe(struct platform_device *pdev)
+{
+ int err, i;
+ struct gpio_chip *gc;
+ struct timbgpio *tgpio;
+ struct resource *iomem;
+ struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
+ int irq = platform_get_irq(pdev, 0);
+
+ if (!pdata || pdata->nr_pins > 32) {
+ err = -EINVAL;
+ goto err_mem;
+ }
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iomem) {
+ err = -EINVAL;
+ goto err_mem;
+ }
+
+ tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL);
+ if (!tgpio) {
+ err = -EINVAL;
+ goto err_mem;
+ }
+ tgpio->irq_base = pdata->irq_base;
+
+ spin_lock_init(&tgpio->lock);
+
+ if (!request_mem_region(iomem->start, resource_size(iomem),
+ DRIVER_NAME)) {
+ err = -EBUSY;
+ goto err_request;
+ }
+
+ tgpio->membase = ioremap(iomem->start, resource_size(iomem));
+ if (!tgpio->membase) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ gc = &tgpio->gpio;
+
+ gc->label = dev_name(&pdev->dev);
+ gc->owner = THIS_MODULE;
+ gc->dev = &pdev->dev;
+ gc->direction_input = timbgpio_gpio_direction_input;
+ gc->get = timbgpio_gpio_get;
+ gc->direction_output = timbgpio_gpio_direction_output;
+ gc->set = timbgpio_gpio_set;
+ gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL;
+ gc->dbg_show = NULL;
+ gc->base = pdata->gpio_base;
+ gc->ngpio = pdata->nr_pins;
+ gc->can_sleep = 0;
+
+ err = gpiochip_add(gc);
+ if (err)
+ goto err_chipadd;
+
+ platform_set_drvdata(pdev, tgpio);
+
+ /* make sure to disable interrupts */
+ iowrite32(0x0, tgpio->membase + TGPIO_IER);
+
+ if (irq < 0 || tgpio->irq_base <= 0)
+ return 0;
+
+ for (i = 0; i < pdata->nr_pins; i++) {
+ set_irq_chip_and_handler_name(tgpio->irq_base + i,
+ &timbgpio_irqchip, handle_simple_irq, "mux");
+ set_irq_chip_data(tgpio->irq_base + i, tgpio);
+#ifdef CONFIG_ARM
+ set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE);
+#endif
+ }
+
+ set_irq_data(irq, tgpio);
+ set_irq_chained_handler(irq, timbgpio_irq);
+
+ return 0;
+
+err_chipadd:
+ iounmap(tgpio->membase);
+err_ioremap:
+ release_mem_region(iomem->start, resource_size(iomem));
+err_request:
+ kfree(tgpio);
+err_mem:
+ printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err);
+
+ return err;
+}
+
+static int __devexit timbgpio_remove(struct platform_device *pdev)
+{
+ int err;
+ struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timbgpio *tgpio = platform_get_drvdata(pdev);
+ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq >= 0 && tgpio->irq_base > 0) {
+ int i;
+ for (i = 0; i < pdata->nr_pins; i++) {
+ set_irq_chip(tgpio->irq_base + i, NULL);
+ set_irq_chip_data(tgpio->irq_base + i, NULL);
+ }
+
+ set_irq_handler(irq, NULL);
+ set_irq_data(irq, NULL);
+ }
+
+ err = gpiochip_remove(&tgpio->gpio);
+ if (err)
+ printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n");
+
+ iounmap(tgpio->membase);
+ release_mem_region(iomem->start, resource_size(iomem));
+ kfree(tgpio);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver timbgpio_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = timbgpio_probe,
+ .remove = timbgpio_remove,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init timbgpio_init(void)
+{
+ return platform_driver_register(&timbgpio_platform_driver);
+}
+
+static void __exit timbgpio_exit(void)
+{
+ platform_driver_unregister(&timbgpio_platform_driver);
+}
+
+module_init(timbgpio_init);
+module_exit(timbgpio_exit);
+
+MODULE_DESCRIPTION("Timberdale GPIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Mocean Laboratories");
+MODULE_ALIAS("platform:"DRIVER_NAME);
+
diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c
index 49384a7c5492..7fe881e2bdfb 100644
--- a/drivers/gpio/twl4030-gpio.c
+++ b/drivers/gpio/twl4030-gpio.c
@@ -34,7 +34,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
/*
@@ -80,7 +80,7 @@ static unsigned int gpio_usage_count;
*/
static inline int gpio_twl4030_write(u8 address, u8 data)
{
- return twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
+ return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
}
/*----------------------------------------------------------------------*/
@@ -117,7 +117,7 @@ static inline int gpio_twl4030_read(u8 address)
u8 data;
int ret = 0;
- ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
+ ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
return (ret < 0) ? ret : data;
}
@@ -142,7 +142,7 @@ static void twl4030_led_set_value(int led, int value)
cached_leden &= ~mask;
else
cached_leden |= mask;
- status = twl4030_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
+ status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
TWL4030_LED_LEDEN);
mutex_unlock(&gpio_lock);
}
@@ -223,23 +223,23 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
}
/* initialize PWM to always-drive */
- status = twl4030_i2c_write_u8(module, 0x7f,
+ status = twl_i2c_write_u8(module, 0x7f,
TWL4030_PWMx_PWMxOFF);
if (status < 0)
goto done;
- status = twl4030_i2c_write_u8(module, 0x7f,
+ status = twl_i2c_write_u8(module, 0x7f,
TWL4030_PWMx_PWMxON);
if (status < 0)
goto done;
/* init LED to not-driven (high) */
module = TWL4030_MODULE_LED;
- status = twl4030_i2c_read_u8(module, &cached_leden,
+ status = twl_i2c_read_u8(module, &cached_leden,
TWL4030_LED_LEDEN);
if (status < 0)
goto done;
cached_leden &= ~ledclr_mask;
- status = twl4030_i2c_write_u8(module, cached_leden,
+ status = twl_i2c_write_u8(module, cached_leden,
TWL4030_LED_LEDEN);
if (status < 0)
goto done;
@@ -370,7 +370,7 @@ static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs)
message[i] = bit_mask;
}
- return twl4030_i2c_write(TWL4030_MODULE_GPIO, message,
+ return twl_i2c_write(TWL4030_MODULE_GPIO, message,
REG_GPIOPUPDCTR1, 5);
}
@@ -387,7 +387,7 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
debounce >>= 8;
message[3] = (debounce & 0x03);
- return twl4030_i2c_write(TWL4030_MODULE_GPIO, message,
+ return twl_i2c_write(TWL4030_MODULE_GPIO, message,
REG_GPIO_DEBEN1, 3);
}
diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c
index f9c09a54ec7f..b4468b616890 100644
--- a/drivers/gpio/wm831x-gpio.c
+++ b/drivers/gpio/wm831x-gpio.c
@@ -22,8 +22,7 @@
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/gpio.h>
-
-#define WM831X_GPIO_MAX 16
+#include <linux/mfd/wm831x/irq.h>
struct wm831x_gpio {
struct wm831x *wm831x;
@@ -80,6 +79,17 @@ static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
value << offset);
}
+static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+
+ if (!wm831x->irq_base)
+ return -EINVAL;
+
+ return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
+}
+
#ifdef CONFIG_DEBUG_FS
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
@@ -175,6 +185,7 @@ static struct gpio_chip template_chip = {
.get = wm831x_gpio_get,
.direction_output = wm831x_gpio_direction_out,
.set = wm831x_gpio_set,
+ .to_irq = wm831x_gpio_to_irq,
.dbg_show = wm831x_gpio_dbg_show,
.can_sleep = 1,
};
@@ -192,7 +203,7 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
wm831x_gpio->wm831x = wm831x;
wm831x_gpio->gpio_chip = template_chip;
- wm831x_gpio->gpio_chip.ngpio = WM831X_GPIO_MAX;
+ wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio;
wm831x_gpio->gpio_chip.dev = &pdev->dev;
if (pdata && pdata->gpio_base)
wm831x_gpio->gpio_chip.base = pdata->gpio_base;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9e640c62ebd9..95ccbe377f9c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1046,25 +1046,27 @@ config SENSORS_ATK0110
will be called asus_atk0110.
config SENSORS_LIS3LV02D
- tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer"
+ tristate "STMicroeletronics LIS3* three-axis digital accelerometer"
depends on INPUT
select INPUT_POLLDEV
select NEW_LEDS
select LEDS_CLASS
default n
help
- This driver provides support for the LIS3LV02Dx accelerometer. In
- particular, it can be found in a number of HP laptops, which have the
- "Mobile Data Protection System 3D" or "3D DriveGuard" feature. On such
- systems the driver should load automatically (via ACPI). The
- accelerometer might also be found in other systems, connected via SPI
- or I2C. The accelerometer data is readable via
- /sys/devices/platform/lis3lv02d.
+ This driver provides support for the LIS3* accelerometers, such as the
+ LIS3LV02DL or the LIS331DL. In particular, it can be found in a number
+ of HP laptops, which have the "Mobile Data Protection System 3D" or
+ "3D DriveGuard" feature. On such systems the driver should load
+ automatically (via ACPI alias). The accelerometer might also be found
+ in other systems, connected via SPI or I2C. The accelerometer data is
+ readable via /sys/devices/platform/lis3lv02d.
This driver also provides an absolute input class device, allowing
- the laptop to act as a pinball machine-esque joystick. On HP laptops,
+ a laptop to act as a pinball machine-esque joystick. It provides also
+ a misc device which can be used to detect free-fall. On HP laptops,
if the led infrastructure is activated, support for a led indicating
- disk protection will be provided as hp:red:hddprotection.
+ disk protection will be provided as hp::hddprotect. For more
+ information on the feature, refer to Documentation/hwmon/lis3lv02d.
This driver can also be built as modules. If so, the core module
will be called lis3lv02d and a specific module for HP laptops will be
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 33acf29531af..1ad0a885c5a5 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -34,9 +34,8 @@
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm,
- mc1066);
+enum chips {
+ adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066 };
/* adm1021 constants specified below */
@@ -97,7 +96,7 @@ struct adm1021_data {
static int adm1021_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adm1021_detect(struct i2c_client *client, int kind,
+static int adm1021_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm1021_init_client(struct i2c_client *client);
static int adm1021_remove(struct i2c_client *client);
@@ -130,7 +129,7 @@ static struct i2c_driver adm1021_driver = {
.remove = adm1021_remove,
.id_table = adm1021_id,
.detect = adm1021_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static ssize_t show_temp(struct device *dev,
@@ -284,7 +283,7 @@ static const struct attribute_group adm1021_group = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1021_detect(struct i2c_client *client, int kind,
+static int adm1021_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
index db6ac2b04f6f..251b63165e2a 100644
--- a/drivers/hwmon/adm1025.c
+++ b/drivers/hwmon/adm1025.c
@@ -64,11 +64,7 @@
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_2(adm1025, ne1619);
+enum chips { adm1025, ne1619 };
/*
* The ADM1025 registers
@@ -111,7 +107,7 @@ static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
static int adm1025_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adm1025_detect(struct i2c_client *client, int kind,
+static int adm1025_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm1025_init_client(struct i2c_client *client);
static int adm1025_remove(struct i2c_client *client);
@@ -137,7 +133,7 @@ static struct i2c_driver adm1025_driver = {
.remove = adm1025_remove,
.id_table = adm1025_id,
.detect = adm1025_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -409,7 +405,7 @@ static const struct attribute_group adm1025_group_in4 = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1025_detect(struct i2c_client *client, int kind,
+static int adm1025_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index fb5363985e21..65335b268fa9 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -37,9 +37,6 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adm1026);
-
static int gpio_input[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -293,7 +290,7 @@ struct adm1026_data {
static int adm1026_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adm1026_detect(struct i2c_client *client, int kind,
+static int adm1026_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int adm1026_remove(struct i2c_client *client);
static int adm1026_read_value(struct i2c_client *client, u8 reg);
@@ -305,7 +302,7 @@ static void adm1026_init_client(struct i2c_client *client);
static const struct i2c_device_id adm1026_id[] = {
- { "adm1026", adm1026 },
+ { "adm1026", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1026_id);
@@ -319,7 +316,7 @@ static struct i2c_driver adm1026_driver = {
.remove = adm1026_remove,
.id_table = adm1026_id,
.detect = adm1026_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int adm1026_read_value(struct i2c_client *client, u8 reg)
@@ -1650,7 +1647,7 @@ static const struct attribute_group adm1026_group_in8_9 = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1026_detect(struct i2c_client *client, int kind,
+static int adm1026_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index ef91e2a4a567..0b8a3b145bd2 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -44,12 +44,6 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
};
/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(adm1029);
-
-/*
* The ADM1029 registers
* Manufacturer ID is 0x41 for Analog Devices
*/
@@ -117,7 +111,7 @@ static const u8 ADM1029_REG_FAN_DIV[] = {
static int adm1029_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adm1029_detect(struct i2c_client *client, int kind,
+static int adm1029_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int adm1029_remove(struct i2c_client *client);
static struct adm1029_data *adm1029_update_device(struct device *dev);
@@ -128,7 +122,7 @@ static int adm1029_init_client(struct i2c_client *client);
*/
static const struct i2c_device_id adm1029_id[] = {
- { "adm1029", adm1029 },
+ { "adm1029", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1029_id);
@@ -142,7 +136,7 @@ static struct i2c_driver adm1029_driver = {
.remove = adm1029_remove,
.id_table = adm1029_id,
.detect = adm1029_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -297,7 +291,7 @@ static const struct attribute_group adm1029_group = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1029_detect(struct i2c_client *client, int kind,
+static int adm1029_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 0e722175aae0..1644b92e7cc4 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -64,8 +64,7 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(adm1030, adm1031);
+enum chips { adm1030, adm1031 };
typedef u8 auto_chan_table_t[8][2];
@@ -102,7 +101,7 @@ struct adm1031_data {
static int adm1031_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adm1031_detect(struct i2c_client *client, int kind,
+static int adm1031_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm1031_init_client(struct i2c_client *client);
static int adm1031_remove(struct i2c_client *client);
@@ -125,7 +124,7 @@ static struct i2c_driver adm1031_driver = {
.remove = adm1031_remove,
.id_table = adm1031_id,
.detect = adm1031_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
@@ -813,7 +812,7 @@ static const struct attribute_group adm1031_group_opt = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1031_detect(struct i2c_client *client, int kind,
+static int adm1031_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 20e0481cc206..0727ad250793 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -55,8 +55,7 @@
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_3(adm9240, ds1780, lm81);
+enum chips { adm9240, ds1780, lm81 };
/* ADM9240 registers */
#define ADM9240_REG_MAN_ID 0x3e
@@ -132,7 +131,7 @@ static inline unsigned int AOUT_FROM_REG(u8 reg)
static int adm9240_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adm9240_detect(struct i2c_client *client, int kind,
+static int adm9240_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm9240_init_client(struct i2c_client *client);
static int adm9240_remove(struct i2c_client *client);
@@ -156,7 +155,7 @@ static struct i2c_driver adm9240_driver = {
.remove = adm9240_remove,
.id_table = adm9240_id,
.detect = adm9240_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* per client data */
@@ -545,7 +544,7 @@ static const struct attribute_group adm9240_group = {
/*** sensor chip detect and driver install ***/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm9240_detect(struct i2c_client *new_client, int kind,
+static int adm9240_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 451977bca7d6..aac85f3aed50 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -47,10 +47,7 @@
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(ads7828);
-
-/* Other module parameters */
+/* Module parameters */
static int se_input = 1; /* Default is SE, 0 == diff */
static int int_vref = 1; /* Default is internal ref ON */
static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */
@@ -72,7 +69,7 @@ struct ads7828_data {
};
/* Function declaration - necessary due to function dependencies */
-static int ads7828_detect(struct i2c_client *client, int kind,
+static int ads7828_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int ads7828_probe(struct i2c_client *client,
const struct i2c_device_id *id);
@@ -168,7 +165,7 @@ static int ads7828_remove(struct i2c_client *client)
}
static const struct i2c_device_id ads7828_id[] = {
- { "ads7828", ads7828 },
+ { "ads7828", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ads7828_id);
@@ -183,11 +180,11 @@ static struct i2c_driver ads7828_driver = {
.remove = ads7828_remove,
.id_table = ads7828_id,
.detect = ads7828_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int ads7828_detect(struct i2c_client *client, int kind,
+static int ads7828_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c
index f9c9562b6a94..a1a7ef14b519 100644
--- a/drivers/hwmon/adt7462.c
+++ b/drivers/hwmon/adt7462.c
@@ -32,9 +32,6 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7462);
-
/* ADT7462 registers */
#define ADT7462_REG_DEVICE 0x3D
#define ADT7462_REG_VENDOR 0x3E
@@ -237,12 +234,12 @@ struct adt7462_data {
static int adt7462_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adt7462_detect(struct i2c_client *client, int kind,
+static int adt7462_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int adt7462_remove(struct i2c_client *client);
static const struct i2c_device_id adt7462_id[] = {
- { "adt7462", adt7462 },
+ { "adt7462", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7462_id);
@@ -256,7 +253,7 @@ static struct i2c_driver adt7462_driver = {
.remove = adt7462_remove,
.id_table = adt7462_id,
.detect = adt7462_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -1902,7 +1899,7 @@ static struct attribute *adt7462_attr[] =
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7462_detect(struct i2c_client *client, int kind,
+static int adt7462_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 32b1750a6890..3445ce1cba81 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -33,9 +33,6 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7470);
-
/* ADT7470 registers */
#define ADT7470_REG_BASE_ADDR 0x20
#define ADT7470_REG_TEMP_BASE_ADDR 0x20
@@ -177,12 +174,12 @@ struct adt7470_data {
static int adt7470_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adt7470_detect(struct i2c_client *client, int kind,
+static int adt7470_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int adt7470_remove(struct i2c_client *client);
static const struct i2c_device_id adt7470_id[] = {
- { "adt7470", adt7470 },
+ { "adt7470", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7470_id);
@@ -196,7 +193,7 @@ static struct i2c_driver adt7470_driver = {
.remove = adt7470_remove,
.id_table = adt7470_id,
.detect = adt7470_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -1225,7 +1222,7 @@ static struct attribute *adt7470_attr[] =
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7470_detect(struct i2c_client *client, int kind,
+static int adt7470_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c
index aea244db974e..434576f61c84 100644
--- a/drivers/hwmon/adt7473.c
+++ b/drivers/hwmon/adt7473.c
@@ -32,9 +32,6 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7473);
-
/* ADT7473 registers */
#define ADT7473_REG_BASE_ADDR 0x20
@@ -166,12 +163,12 @@ struct adt7473_data {
static int adt7473_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int adt7473_detect(struct i2c_client *client, int kind,
+static int adt7473_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int adt7473_remove(struct i2c_client *client);
static const struct i2c_device_id adt7473_id[] = {
- { "adt7473", adt7473 },
+ { "adt7473", 0 },
{ }
};
@@ -184,7 +181,7 @@ static struct i2c_driver adt7473_driver = {
.remove = adt7473_remove,
.id_table = adt7473_id,
.detect = adt7473_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -1085,7 +1082,7 @@ static struct attribute *adt7473_attr[] =
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7473_detect(struct i2c_client *client, int kind,
+static int adt7473_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index 99abfddedbc3..a0c385145686 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -148,7 +148,7 @@
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-I2C_CLIENT_INSMOD_4(adt7473, adt7475, adt7476, adt7490);
+enum chips { adt7473, adt7475, adt7476, adt7490 };
static const struct i2c_device_id adt7475_id[] = {
{ "adt7473", adt7473 },
@@ -1172,7 +1172,7 @@ static struct attribute_group in4_attr_group = { .attrs = in4_attrs };
static struct attribute_group in5_attr_group = { .attrs = in5_attrs };
static struct attribute_group vid_attr_group = { .attrs = vid_attrs };
-static int adt7475_detect(struct i2c_client *client, int kind,
+static int adt7475_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
@@ -1412,7 +1412,7 @@ static struct i2c_driver adt7475_driver = {
.remove = adt7475_remove,
.id_table = adt7475_id,
.detect = adt7475_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static void adt7475_read_hystersis(struct i2c_client *client)
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 7ea6a8f66056..c1605b528e8f 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -518,7 +518,7 @@ static int applesmc_pm_restore(struct device *dev)
return applesmc_pm_resume(dev);
}
-static struct dev_pm_ops applesmc_pm_ops = {
+static const struct dev_pm_ops applesmc_pm_ops = {
.resume = applesmc_pm_resume,
.restore = applesmc_pm_restore,
};
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 480f80ea1fa0..7dada559b3a1 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -51,9 +51,6 @@
/* I2C addresses to scan */
static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(asb100);
-
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
@@ -209,14 +206,14 @@ static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val);
static int asb100_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int asb100_detect(struct i2c_client *client, int kind,
+static int asb100_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int asb100_remove(struct i2c_client *client);
static struct asb100_data *asb100_update_device(struct device *dev);
static void asb100_init_client(struct i2c_client *client);
static const struct i2c_device_id asb100_id[] = {
- { "asb100", asb100 },
+ { "asb100", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, asb100_id);
@@ -230,7 +227,7 @@ static struct i2c_driver asb100_driver = {
.remove = asb100_remove,
.id_table = asb100_id,
.detect = asb100_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* 7 Voltages */
@@ -697,7 +694,7 @@ ERROR_SC_2:
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int asb100_detect(struct i2c_client *client, int kind,
+static int asb100_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index d6b490d3e36f..94cadc19f0c5 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -44,17 +44,14 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
-I2C_CLIENT_INSMOD_1(atxp1);
-
static int atxp1_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int atxp1_remove(struct i2c_client *client);
static struct atxp1_data * atxp1_update_device(struct device *dev);
-static int atxp1_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info);
static const struct i2c_device_id atxp1_id[] = {
- { "atxp1", atxp1 },
+ { "atxp1", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, atxp1_id);
@@ -68,7 +65,7 @@ static struct i2c_driver atxp1_driver = {
.remove = atxp1_remove,
.id_table = atxp1_id,
.detect = atxp1_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
struct atxp1_data {
@@ -275,7 +272,7 @@ static const struct attribute_group atxp1_group = {
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int atxp1_detect(struct i2c_client *new_client, int kind,
+static int atxp1_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index 4377bb0cc526..823dd28a902c 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -57,11 +57,7 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
/* Addresses to scan */
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(dme1737, sch5027);
-
-/* ISA chip types */
-enum isa_chips { sch311x = sch5027 + 1 };
+enum chips { dme1737, sch5027, sch311x };
/* ---------------------------------------------------------------------
* Registers
@@ -2208,7 +2204,7 @@ exit:
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int dme1737_i2c_detect(struct i2c_client *client, int kind,
+static int dme1737_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
@@ -2318,7 +2314,7 @@ static struct i2c_driver dme1737_i2c_driver = {
.remove = dme1737_i2c_remove,
.id_table = dme1737_id,
.detect = dme1737_i2c_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* ---------------------------------------------------------------------
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index 2a4c6a05b14f..e11363467a8d 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -38,7 +38,6 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(ds1621);
static int polarity = -1;
module_param(polarity, int, 0);
MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low");
@@ -224,7 +223,7 @@ static const struct attribute_group ds1621_group = {
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int ds1621_detect(struct i2c_client *client, int kind,
+static int ds1621_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
@@ -305,8 +304,8 @@ static int ds1621_remove(struct i2c_client *client)
}
static const struct i2c_device_id ds1621_id[] = {
- { "ds1621", ds1621 },
- { "ds1625", ds1621 },
+ { "ds1621", 0 },
+ { "ds1625", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1621_id);
@@ -321,7 +320,7 @@ static struct i2c_driver ds1621_driver = {
.remove = ds1621_remove,
.id_table = ds1621_id,
.detect = ds1621_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init ds1621_init(void)
diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
index 40dfbcd3f3f2..277398f9c938 100644
--- a/drivers/hwmon/f75375s.c
+++ b/drivers/hwmon/f75375s.c
@@ -39,8 +39,7 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(f75373, f75375);
+enum chips { f75373, f75375 };
/* Fintek F75375 registers */
#define F75375_REG_CONFIG0 0x0
@@ -113,7 +112,7 @@ struct f75375_data {
s8 temp_max_hyst[2];
};
-static int f75375_detect(struct i2c_client *client, int kind,
+static int f75375_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int f75375_probe(struct i2c_client *client,
const struct i2c_device_id *id);
@@ -135,7 +134,7 @@ static struct i2c_driver f75375_driver = {
.remove = f75375_remove,
.id_table = f75375_id,
.detect = f75375_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static inline int f75375_read8(struct i2c_client *client, u8 reg)
@@ -677,7 +676,7 @@ static int f75375_remove(struct i2c_client *client)
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int f75375_detect(struct i2c_client *client, int kind,
+static int f75375_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index 281829cd1533..bd0fc67e804b 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -56,7 +56,8 @@ static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-I2C_CLIENT_INSMOD_7(fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl);
+
+enum chips { fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl };
/*
* The FSCHMD registers and other defines
@@ -221,7 +222,7 @@ static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 };
static int fschmd_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int fschmd_detect(struct i2c_client *client, int kind,
+static int fschmd_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int fschmd_remove(struct i2c_client *client);
static struct fschmd_data *fschmd_update_device(struct device *dev);
@@ -251,7 +252,7 @@ static struct i2c_driver fschmd_driver = {
.remove = fschmd_remove,
.id_table = fschmd_id,
.detect = fschmd_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -1000,7 +1001,7 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy)
}
}
-static int fschmd_detect(struct i2c_client *client, int _kind,
+static int fschmd_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
enum chips kind;
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index 1d69458aa0b6..e7ae5743e181 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -46,8 +46,7 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(gl518sm_r00, gl518sm_r80);
+enum chips { gl518sm_r00, gl518sm_r80 };
/* Many GL518 constants specified below */
@@ -139,8 +138,7 @@ struct gl518_data {
static int gl518_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int gl518_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info);
static void gl518_init_client(struct i2c_client *client);
static int gl518_remove(struct i2c_client *client);
static int gl518_read_value(struct i2c_client *client, u8 reg);
@@ -163,7 +161,7 @@ static struct i2c_driver gl518_driver = {
.remove = gl518_remove,
.id_table = gl518_id,
.detect = gl518_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -484,8 +482,7 @@ static const struct attribute_group gl518_group_r80 = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int gl518_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int rev;
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index 92b5720ceaff..ec588026f0a9 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -41,9 +41,6 @@ MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=tempe
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(gl520sm);
-
/* Many GL520 constants specified below
One of the inputs can be configured as either temp or voltage.
That's why _TEMP2 and _IN4 access the same register
@@ -81,8 +78,7 @@ static const u8 GL520_REG_TEMP_MAX_HYST[] = { 0x06, 0x18 };
static int gl520_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int gl520_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info);
static void gl520_init_client(struct i2c_client *client);
static int gl520_remove(struct i2c_client *client);
static int gl520_read_value(struct i2c_client *client, u8 reg);
@@ -91,7 +87,7 @@ static struct gl520_data *gl520_update_device(struct device *dev);
/* Driver data */
static const struct i2c_device_id gl520_id[] = {
- { "gl520sm", gl520sm },
+ { "gl520sm", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, gl520_id);
@@ -105,7 +101,7 @@ static struct i2c_driver gl520_driver = {
.remove = gl520_remove,
.id_table = gl520_id,
.detect = gl520_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* Client data */
@@ -681,8 +677,7 @@ static const struct attribute_group gl520_group_opt = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int gl520_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c
index cf5afb9a10ab..b2f2277cad3c 100644
--- a/drivers/hwmon/lis3lv02d.c
+++ b/drivers/hwmon/lis3lv02d.c
@@ -43,13 +43,30 @@
#define MDPS_POLL_INTERVAL 50
/*
* The sensor can also generate interrupts (DRDY) but it's pretty pointless
- * because their are generated even if the data do not change. So it's better
+ * because they are generated even if the data do not change. So it's better
* to keep the interrupt for the free-fall event. The values are updated at
* 40Hz (at the lowest frequency), but as it can be pretty time consuming on
* some low processor, we poll the sensor only at 20Hz... enough for the
* joystick.
*/
+#define LIS3_PWRON_DELAY_WAI_12B (5000)
+#define LIS3_PWRON_DELAY_WAI_8B (3000)
+
+/*
+ * LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG
+ * LIS302D spec says: 18 mG / digit
+ * LIS3_ACCURACY is used to increase accuracy of the intermediate
+ * calculation results.
+ */
+#define LIS3_ACCURACY 1024
+/* Sensitivity values for -2G +2G scale */
+#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
+#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
+
+#define LIS3_DEFAULT_FUZZ 3
+#define LIS3_DEFAULT_FLAT 3
+
struct lis3lv02d lis3_dev = {
.misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),
};
@@ -65,7 +82,7 @@ static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg)
return lo;
}
-static s16 lis3lv02d_read_16(struct lis3lv02d *lis3, int reg)
+static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)
{
u8 lo, hi;
@@ -102,16 +119,106 @@ static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3])
static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
{
int position[3];
+ int i;
+ mutex_lock(&lis3->mutex);
position[0] = lis3->read_data(lis3, OUTX);
position[1] = lis3->read_data(lis3, OUTY);
position[2] = lis3->read_data(lis3, OUTZ);
+ mutex_unlock(&lis3->mutex);
+
+ for (i = 0; i < 3; i++)
+ position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;
*x = lis3lv02d_get_axis(lis3->ac.x, position);
*y = lis3lv02d_get_axis(lis3->ac.y, position);
*z = lis3lv02d_get_axis(lis3->ac.z, position);
}
+/* conversion btw sampling rate and the register values */
+static int lis3_12_rates[4] = {40, 160, 640, 2560};
+static int lis3_8_rates[2] = {100, 400};
+
+/* ODR is Output Data Rate */
+static int lis3lv02d_get_odr(void)
+{
+ u8 ctrl;
+ int shift;
+
+ lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
+ ctrl &= lis3_dev.odr_mask;
+ shift = ffs(lis3_dev.odr_mask) - 1;
+ return lis3_dev.odrs[(ctrl >> shift)];
+}
+
+static int lis3lv02d_set_odr(int rate)
+{
+ u8 ctrl;
+ int i, len, shift;
+
+ lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
+ ctrl &= ~lis3_dev.odr_mask;
+ len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
+ shift = ffs(lis3_dev.odr_mask) - 1;
+
+ for (i = 0; i < len; i++)
+ if (lis3_dev.odrs[i] == rate) {
+ lis3_dev.write(&lis3_dev, CTRL_REG1,
+ ctrl | (i << shift));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
+{
+ u8 reg;
+ s16 x, y, z;
+ u8 selftest;
+ int ret;
+
+ mutex_lock(&lis3->mutex);
+ if (lis3_dev.whoami == WAI_12B)
+ selftest = CTRL1_ST;
+ else
+ selftest = CTRL1_STP;
+
+ lis3->read(lis3, CTRL_REG1, &reg);
+ lis3->write(lis3, CTRL_REG1, (reg | selftest));
+ msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
+ /* Read directly to avoid axis remap */
+ x = lis3->read_data(lis3, OUTX);
+ y = lis3->read_data(lis3, OUTY);
+ z = lis3->read_data(lis3, OUTZ);
+
+ /* back to normal settings */
+ lis3->write(lis3, CTRL_REG1, reg);
+ msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
+ results[0] = x - lis3->read_data(lis3, OUTX);
+ results[1] = y - lis3->read_data(lis3, OUTY);
+ results[2] = z - lis3->read_data(lis3, OUTZ);
+
+ ret = 0;
+ if (lis3->pdata) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ /* Check against selftest acceptance limits */
+ if ((results[i] < lis3->pdata->st_min_limits[i]) ||
+ (results[i] > lis3->pdata->st_max_limits[i])) {
+ ret = -EIO;
+ goto fail;
+ }
+ }
+ }
+
+ /* test passed */
+fail:
+ mutex_unlock(&lis3->mutex);
+ return ret;
+}
+
void lis3lv02d_poweroff(struct lis3lv02d *lis3)
{
/* disable X,Y,Z axis and power down */
@@ -125,14 +232,19 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->init(lis3);
+ /* LIS3 power on delay is quite long */
+ msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
/*
* Common configuration
- * BDU: LSB and MSB values are not updated until both have been read.
- * So the value read will always be correct.
+ * BDU: (12 bits sensors only) LSB and MSB values are not updated until
+ * both have been read. So the value read will always be correct.
*/
- lis3->read(lis3, CTRL_REG2, &reg);
- reg |= CTRL2_BDU;
- lis3->write(lis3, CTRL_REG2, reg);
+ if (lis3->whoami == WAI_12B) {
+ lis3->read(lis3, CTRL_REG2, &reg);
+ reg |= CTRL2_BDU;
+ lis3->write(lis3, CTRL_REG2, reg);
+ }
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
@@ -273,22 +385,17 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
int x, y, z;
lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
- input_report_abs(pidev->input, ABS_X, x - lis3_dev.xcalib);
- input_report_abs(pidev->input, ABS_Y, y - lis3_dev.ycalib);
- input_report_abs(pidev->input, ABS_Z, z - lis3_dev.zcalib);
-}
-
-
-static inline void lis3lv02d_calibrate_joystick(void)
-{
- lis3lv02d_get_xyz(&lis3_dev,
- &lis3_dev.xcalib, &lis3_dev.ycalib, &lis3_dev.zcalib);
+ input_report_abs(pidev->input, ABS_X, x);
+ input_report_abs(pidev->input, ABS_Y, y);
+ input_report_abs(pidev->input, ABS_Z, z);
+ input_sync(pidev->input);
}
int lis3lv02d_joystick_enable(void)
{
struct input_dev *input_dev;
int err;
+ int max_val, fuzz, flat;
if (lis3_dev.idev)
return -EINVAL;
@@ -301,8 +408,6 @@ int lis3lv02d_joystick_enable(void)
lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
input_dev = lis3_dev.idev->input;
- lis3lv02d_calibrate_joystick();
-
input_dev->name = "ST LIS3LV02DL Accelerometer";
input_dev->phys = DRIVER_NAME "/input0";
input_dev->id.bustype = BUS_HOST;
@@ -310,9 +415,12 @@ int lis3lv02d_joystick_enable(void)
input_dev->dev.parent = &lis3_dev.pdev->dev;
set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_X, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3);
- input_set_abs_params(input_dev, ABS_Y, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3);
- input_set_abs_params(input_dev, ABS_Z, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3);
+ max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
+ fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY;
+ flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY;
+ input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
+ input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
+ input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
err = input_register_polled_device(lis3_dev.idev);
if (err) {
@@ -332,11 +440,23 @@ void lis3lv02d_joystick_disable(void)
if (lis3_dev.irq)
misc_deregister(&lis3lv02d_misc_device);
input_unregister_polled_device(lis3_dev.idev);
+ input_free_polled_device(lis3_dev.idev);
lis3_dev.idev = NULL;
}
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
/* Sysfs stuff */
+static ssize_t lis3lv02d_selftest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int result;
+ s16 values[3];
+
+ result = lis3lv02d_selftest(&lis3_dev, values);
+ return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL",
+ values[0], values[1], values[2]);
+}
+
static ssize_t lis3lv02d_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -346,41 +466,35 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
}
-static ssize_t lis3lv02d_calibrate_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t lis3lv02d_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "(%d,%d,%d)\n", lis3_dev.xcalib, lis3_dev.ycalib, lis3_dev.zcalib);
+ return sprintf(buf, "%d\n", lis3lv02d_get_odr());
}
-static ssize_t lis3lv02d_calibrate_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t lis3lv02d_rate_set(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
- lis3lv02d_calibrate_joystick();
- return count;
-}
+ unsigned long rate;
-/* conversion btw sampling rate and the register values */
-static int lis3lv02dl_df_val[4] = {40, 160, 640, 2560};
-static ssize_t lis3lv02d_rate_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u8 ctrl;
- int val;
+ if (strict_strtoul(buf, 0, &rate))
+ return -EINVAL;
- lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
- val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4;
- return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]);
+ if (lis3lv02d_set_odr(rate))
+ return -EINVAL;
+
+ return count;
}
+static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL);
static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL);
-static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show,
- lis3lv02d_calibrate_store);
-static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL);
+static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show,
+ lis3lv02d_rate_set);
static struct attribute *lis3lv02d_attributes[] = {
+ &dev_attr_selftest.attr,
&dev_attr_position.attr,
- &dev_attr_calibrate.attr,
&dev_attr_rate.attr,
NULL
};
@@ -409,22 +523,30 @@ EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
/*
* Initialise the accelerometer and the various subsystems.
- * Should be rather independant of the bus system.
+ * Should be rather independent of the bus system.
*/
int lis3lv02d_init_device(struct lis3lv02d *dev)
{
dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
switch (dev->whoami) {
- case LIS_DOUBLE_ID:
- printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n");
- dev->read_data = lis3lv02d_read_16;
+ case WAI_12B:
+ printk(KERN_INFO DRIVER_NAME ": 12 bits sensor found\n");
+ dev->read_data = lis3lv02d_read_12;
dev->mdps_max_val = 2048;
+ dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
+ dev->odrs = lis3_12_rates;
+ dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
+ dev->scale = LIS3_SENSITIVITY_12B;
break;
- case LIS_SINGLE_ID:
- printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n");
+ case WAI_8B:
+ printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n");
dev->read_data = lis3lv02d_read_8;
dev->mdps_max_val = 128;
+ dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ dev->odrs = lis3_8_rates;
+ dev->odr_mask = CTRL1_DR;
+ dev->scale = LIS3_SENSITIVITY_8B;
break;
default:
printk(KERN_ERR DRIVER_NAME
@@ -432,6 +554,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
return -EINVAL;
}
+ mutex_init(&dev->mutex);
+
lis3lv02d_add_fs(dev);
lis3lv02d_poweron(dev);
@@ -443,7 +567,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
if (dev->pdata) {
struct lis3lv02d_platform_data *p = dev->pdata;
- if (p->click_flags && (dev->whoami == LIS_SINGLE_ID)) {
+ if (p->click_flags && (dev->whoami == WAI_8B)) {
dev->write(dev, CLICK_CFG, p->click_flags);
dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
dev->write(dev, CLICK_LATENCY, p->click_latency);
@@ -454,7 +578,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
(p->click_thresh_y << 4));
}
- if (p->wakeup_flags && (dev->whoami == LIS_SINGLE_ID)) {
+ if (p->wakeup_flags && (dev->whoami == WAI_8B)) {
dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
/* default to 2.5ms for now */
@@ -484,4 +608,3 @@ EXPORT_SYMBOL_GPL(lis3lv02d_init_device);
MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
MODULE_LICENSE("GPL");
-
diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h
index 3e1ff46f72d3..e6a01f44709b 100644
--- a/drivers/hwmon/lis3lv02d.h
+++ b/drivers/hwmon/lis3lv02d.h
@@ -2,7 +2,7 @@
* lis3lv02d.h - ST LIS3LV02DL accelerometer driver
*
* Copyright (C) 2007-2008 Yan Burman
- * Copyright (C) 2008 Eric Piel
+ * Copyright (C) 2008-2009 Eric Piel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,20 +22,18 @@
#include <linux/input-polldev.h>
/*
- * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to
- * be connected via SPI. There exists also several similar chips (such as LIS302DL or
- * LIS3L02DQ) and they have slightly different registers, but we can provide a
- * common interface for all of them.
- * They can also be connected via I²C.
+ * This driver tries to support the "digital" accelerometer chips from
+ * STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL,
+ * LIS35DE, or LIS202DL. They are very similar in terms of programming, with
+ * almost the same registers. In addition to differing on physical properties,
+ * they differ on the number of axes (2/3), precision (8/12 bits), and special
+ * features (freefall detection, click...). Unfortunately, not all the
+ * differences can be probed via a register.
+ * They can be connected either via I²C or SPI.
*/
#include <linux/lis3lv02d.h>
-/* 2-byte registers */
-#define LIS_DOUBLE_ID 0x3A /* LIS3LV02D[LQ] */
-/* 1-byte registers */
-#define LIS_SINGLE_ID 0x3B /* LIS[32]02DL and others */
-
enum lis3_reg {
WHO_AM_I = 0x0F,
OFFSET_X = 0x16,
@@ -94,7 +92,13 @@ enum lis3lv02d_reg {
DD_THSE_H = 0x3F,
};
-enum lis3lv02d_ctrl1 {
+enum lis3_who_am_i {
+ WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
+ WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
+ WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
+};
+
+enum lis3lv02d_ctrl1_12b {
CTRL1_Xen = 0x01,
CTRL1_Yen = 0x02,
CTRL1_Zen = 0x04,
@@ -104,6 +108,16 @@ enum lis3lv02d_ctrl1 {
CTRL1_PD0 = 0x40,
CTRL1_PD1 = 0x80,
};
+
+/* Delta to ctrl1_12b version */
+enum lis3lv02d_ctrl1_8b {
+ CTRL1_STM = 0x08,
+ CTRL1_STP = 0x10,
+ CTRL1_FS = 0x20,
+ CTRL1_PD = 0x40,
+ CTRL1_DR = 0x80,
+};
+
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
@@ -194,16 +208,20 @@ struct lis3lv02d {
int (*write) (struct lis3lv02d *lis3, int reg, u8 val);
int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret);
- u8 whoami; /* 3Ah: 2-byte registries, 3Bh: 1-byte registries */
+ int *odrs; /* Supported output data rates */
+ u8 odr_mask; /* ODR bit mask */
+ u8 whoami; /* indicates measurement precision */
s16 (*read_data) (struct lis3lv02d *lis3, int reg);
int mdps_max_val;
+ int pwron_delay;
+ int scale; /*
+ * relationship between 1 LBS and mG
+ * (1/1000th of earth gravity)
+ */
struct input_polled_dev *idev; /* input device */
struct platform_device *pdev; /* platform device */
atomic_t count; /* interrupt count after last read */
- int xcalib; /* calibrated null value for x */
- int ycalib; /* calibrated null value for y */
- int zcalib; /* calibrated null value for z */
struct axis_conversion ac; /* hw -> logical axis */
u32 irq; /* IRQ number */
@@ -212,6 +230,7 @@ struct lis3lv02d {
unsigned long misc_opened; /* bit0: whether the device is open */
struct lis3lv02d_platform_data *pdata; /* for passing board config */
+ struct mutex mutex; /* Serialize poll and selftest */
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 5da66ab04f74..bf81aff7051d 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -56,12 +56,6 @@
static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(lm63);
-
-/*
* The LM63 registers
*/
@@ -134,8 +128,7 @@ static int lm63_remove(struct i2c_client *client);
static struct lm63_data *lm63_update_device(struct device *dev);
-static int lm63_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
static void lm63_init_client(struct i2c_client *client);
/*
@@ -143,7 +136,7 @@ static void lm63_init_client(struct i2c_client *client);
*/
static const struct i2c_device_id lm63_id[] = {
- { "lm63", lm63 },
+ { "lm63", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm63_id);
@@ -157,7 +150,7 @@ static struct i2c_driver lm63_driver = {
.remove = lm63_remove,
.id_table = lm63_id,
.detect = lm63_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -423,7 +416,7 @@ static const struct attribute_group lm63_group_fan1 = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm63_detect(struct i2c_client *new_client, int kind,
+static int lm63_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
index 0bf8b2a8e9f0..c5f39ba103c0 100644
--- a/drivers/hwmon/lm73.c
+++ b/drivers/hwmon/lm73.c
@@ -27,9 +27,6 @@
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
0x4d, 0x4e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm73);
-
/* LM73 registers */
#define LM73_REG_INPUT 0x00
#define LM73_REG_CONF 0x01
@@ -145,13 +142,13 @@ static int lm73_remove(struct i2c_client *client)
}
static const struct i2c_device_id lm73_ids[] = {
- { "lm73", lm73 },
+ { "lm73", 0 },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, lm73_ids);
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm73_detect(struct i2c_client *new_client, int kind,
+static int lm73_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
@@ -182,7 +179,7 @@ static struct i2c_driver lm73_driver = {
.remove = lm73_remove,
.id_table = lm73_ids,
.detect = lm73_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* module glue */
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index e392548cccb8..8ae2cfe2d827 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -32,15 +32,12 @@
/*
* This driver handles the LM75 and compatible digital temperature sensors.
- * Only types which are _not_ listed in I2C_CLIENT_INSMOD_*() need to be
- * listed here. We start at 9 since I2C_CLIENT_INSMOD_*() currently allow
- * definition of up to 8 chip types (plus zero).
*/
enum lm75_type { /* keep sorted in alphabetical order */
- ds1775 = 9,
+ ds1775,
ds75,
- /* lm75 -- in I2C_CLIENT_INSMOD_1() */
+ lm75,
lm75a,
max6625,
max6626,
@@ -58,9 +55,6 @@ enum lm75_type { /* keep sorted in alphabetical order */
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm75);
-
/* The LM75 registers */
#define LM75_REG_CONF 0x01
@@ -234,7 +228,7 @@ static const struct i2c_device_id lm75_ids[] = {
MODULE_DEVICE_TABLE(i2c, lm75_ids);
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm75_detect(struct i2c_client *new_client, int kind,
+static int lm75_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
@@ -295,7 +289,7 @@ static struct i2c_driver lm75_driver = {
.remove = lm75_remove,
.id_table = lm75_ids,
.detect = lm75_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*-----------------------------------------------------------------------*/
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index ac067fd19482..b28a297be50c 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -39,9 +39,6 @@
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm77);
-
/* The LM77 registers */
#define LM77_REG_TEMP 0x00
#define LM77_REG_CONF 0x01
@@ -66,8 +63,7 @@ struct lm77_data {
static int lm77_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int lm77_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info);
static void lm77_init_client(struct i2c_client *client);
static int lm77_remove(struct i2c_client *client);
static u16 lm77_read_value(struct i2c_client *client, u8 reg);
@@ -77,7 +73,7 @@ static struct lm77_data *lm77_update_device(struct device *dev);
static const struct i2c_device_id lm77_id[] = {
- { "lm77", lm77 },
+ { "lm77", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm77_id);
@@ -92,7 +88,7 @@ static struct i2c_driver lm77_driver = {
.remove = lm77_remove,
.id_table = lm77_id,
.detect = lm77_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* straight from the datasheet */
@@ -245,7 +241,7 @@ static const struct attribute_group lm77_group = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm77_detect(struct i2c_client *new_client, int kind,
+static int lm77_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 5978291cebb3..cadcbd90ff3b 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -41,8 +41,7 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END };
static unsigned short isa_address = 0x290;
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(lm78, lm79);
+enum chips { lm78, lm79 };
/* Many LM78 constants specified below */
@@ -142,7 +141,7 @@ struct lm78_data {
};
-static int lm78_i2c_detect(struct i2c_client *client, int kind,
+static int lm78_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int lm78_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id);
@@ -173,7 +172,7 @@ static struct i2c_driver lm78_driver = {
.remove = lm78_i2c_remove,
.id_table = lm78_i2c_id,
.detect = lm78_i2c_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static struct platform_driver lm78_isa_driver = {
@@ -558,7 +557,7 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
return 1;
}
-static int lm78_i2c_detect(struct i2c_client *client, int kind,
+static int lm78_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
int i;
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index bcffc1899403..18a0e6c5fe88 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -35,9 +35,6 @@
static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm80);
-
/* Many LM80 constants specified below */
/* The LM80 registers */
@@ -133,8 +130,7 @@ struct lm80_data {
static int lm80_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int lm80_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info);
static void lm80_init_client(struct i2c_client *client);
static int lm80_remove(struct i2c_client *client);
static struct lm80_data *lm80_update_device(struct device *dev);
@@ -146,7 +142,7 @@ static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
*/
static const struct i2c_device_id lm80_id[] = {
- { "lm80", lm80 },
+ { "lm80", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm80_id);
@@ -160,7 +156,7 @@ static struct i2c_driver lm80_driver = {
.remove = lm80_remove,
.id_table = lm80_id,
.detect = lm80_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -447,8 +443,7 @@ static const struct attribute_group lm80_group = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm80_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int i, cur;
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index 08b03e6ed0b7..8290476aee4a 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -51,11 +51,7 @@
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_2(lm83, lm82);
+enum chips { lm83, lm82 };
/*
* The LM83 registers
@@ -118,7 +114,7 @@ static const u8 LM83_REG_W_HIGH[] = {
* Functions declaration
*/
-static int lm83_detect(struct i2c_client *new_client, int kind,
+static int lm83_detect(struct i2c_client *new_client,
struct i2c_board_info *info);
static int lm83_probe(struct i2c_client *client,
const struct i2c_device_id *id);
@@ -145,7 +141,7 @@ static struct i2c_driver lm83_driver = {
.remove = lm83_remove,
.id_table = lm83_id,
.detect = lm83_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -291,7 +287,7 @@ static const struct attribute_group lm83_group_opt = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm83_detect(struct i2c_client *new_client, int kind,
+static int lm83_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index d56da2e74708..b3841a615595 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -38,9 +38,11 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_7(lm85b, lm85c, adm1027, adt7463, adt7468, emc6d100,
- emc6d102);
+enum chips {
+ any_chip, lm85b, lm85c,
+ adm1027, adt7463, adt7468,
+ emc6d100, emc6d102
+};
/* The LM85 registers */
@@ -323,8 +325,7 @@ struct lm85_data {
struct lm85_zone zone[3];
};
-static int lm85_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info);
static int lm85_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int lm85_remove(struct i2c_client *client);
@@ -357,7 +358,7 @@ static struct i2c_driver lm85_driver = {
.remove = lm85_remove,
.id_table = lm85_id,
.detect = lm85_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
@@ -1156,8 +1157,7 @@ static int lm85_is_fake(struct i2c_client *client)
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm85_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 4929b1815eee..f1e6e7512ffa 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -74,11 +74,7 @@
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_2(lm87, adm1024);
+enum chips { lm87, adm1024 };
/*
* The LM87 registers
@@ -158,7 +154,7 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
static int lm87_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int lm87_detect(struct i2c_client *new_client, int kind,
+static int lm87_detect(struct i2c_client *new_client,
struct i2c_board_info *info);
static void lm87_init_client(struct i2c_client *client);
static int lm87_remove(struct i2c_client *client);
@@ -184,7 +180,7 @@ static struct i2c_driver lm87_driver = {
.remove = lm87_remove,
.id_table = lm87_id,
.detect = lm87_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -662,7 +658,7 @@ static const struct attribute_group lm87_group_opt = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm87_detect(struct i2c_client *new_client, int kind,
+static int lm87_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index b7c905f50ed4..7c9bdc167426 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -93,12 +93,7 @@
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
- max6646);
+enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 };
/*
* The LM90 registers
@@ -152,8 +147,7 @@ I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
* Functions declaration
*/
-static int lm90_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info);
+static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info);
static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static void lm90_init_client(struct i2c_client *client);
@@ -192,7 +186,7 @@ static struct i2c_driver lm90_driver = {
.remove = lm90_remove,
.id_table = lm90_id,
.detect = lm90_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -656,7 +650,7 @@ static int lm90_read_reg(struct i2c_client* client, u8 reg, u8 *value)
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm90_detect(struct i2c_client *new_client, int kind,
+static int lm90_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 47ac698709dc..7c31e6205f85 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -54,9 +54,6 @@
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm92);
-
/* The LM92 registers */
#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
#define LM92_REG_TEMP 0x00 /* 16-bit, RO */
@@ -319,7 +316,7 @@ static const struct attribute_group lm92_group = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm92_detect(struct i2c_client *new_client, int kind,
+static int lm92_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
@@ -401,7 +398,7 @@ static int lm92_remove(struct i2c_client *client)
*/
static const struct i2c_device_id lm92_id[] = {
- { "lm92", lm92 },
+ { "lm92", 0 },
/* max6635 could be added here */
{ }
};
@@ -416,7 +413,7 @@ static struct i2c_driver lm92_driver = {
.remove = lm92_remove,
.id_table = lm92_id,
.detect = lm92_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init sensors_lm92_init(void)
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index 124dd7cea54c..6669255aadcf 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -145,7 +145,6 @@
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm93);
static int disable_block;
module_param(disable_block, bool, 0);
@@ -2501,8 +2500,7 @@ static void lm93_init_client(struct i2c_client *client)
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm93_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int mfr, ver;
@@ -2603,7 +2601,7 @@ static int lm93_remove(struct i2c_client *client)
}
static const struct i2c_device_id lm93_id[] = {
- { "lm93", lm93 },
+ { "lm93", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm93_id);
@@ -2617,7 +2615,7 @@ static struct i2c_driver lm93_driver = {
.remove = lm93_remove,
.id_table = lm93_id,
.detect = lm93_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init lm93_init(void)
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index 906b896cf1d0..8fc8eb8cba47 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -39,9 +39,6 @@
static const unsigned short normal_i2c[] = {
0x19, 0x2a, 0x2b, I2C_CLIENT_END};
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm95241);
-
/* LM95241 registers */
#define LM95241_REG_R_MAN_ID 0xFE
#define LM95241_REG_R_CHIP_ID 0xFF
@@ -310,7 +307,7 @@ static const struct attribute_group lm95241_group = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm95241_detect(struct i2c_client *new_client, int kind,
+static int lm95241_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
@@ -446,7 +443,7 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95241_id[] = {
- { "lm95241", lm95241 },
+ { "lm95241", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95241_id);
@@ -460,7 +457,7 @@ static struct i2c_driver lm95241_driver = {
.remove = lm95241_remove,
.id_table = lm95241_id,
.detect = lm95241_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init sensors_lm95241_init(void)
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index 7fcf5ff89e7f..022ded098100 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -41,12 +41,6 @@ static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(max1619);
-
-/*
* The MAX1619 registers
*/
@@ -88,7 +82,7 @@ static int temp_to_reg(int val)
static int max1619_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int max1619_detect(struct i2c_client *client, int kind,
+static int max1619_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void max1619_init_client(struct i2c_client *client);
static int max1619_remove(struct i2c_client *client);
@@ -99,7 +93,7 @@ static struct max1619_data *max1619_update_device(struct device *dev);
*/
static const struct i2c_device_id max1619_id[] = {
- { "max1619", max1619 },
+ { "max1619", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max1619_id);
@@ -113,7 +107,7 @@ static struct i2c_driver max1619_driver = {
.remove = max1619_remove,
.id_table = max1619_id,
.detect = max1619_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -226,7 +220,7 @@ static const struct attribute_group max1619_group = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int max1619_detect(struct i2c_client *client, int kind,
+static int max1619_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 1da561e0cb37..a0160ee5caef 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -62,8 +62,6 @@ module_param(fan_voltage, int, S_IRUGO);
module_param(prescaler, int, S_IRUGO);
module_param(clock, int, S_IRUGO);
-I2C_CLIENT_INSMOD_1(max6650);
-
/*
* MAX 6650/6651 registers
*/
@@ -116,7 +114,7 @@ I2C_CLIENT_INSMOD_1(max6650);
static int max6650_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int max6650_detect(struct i2c_client *client, int kind,
+static int max6650_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int max6650_init_client(struct i2c_client *client);
static int max6650_remove(struct i2c_client *client);
@@ -127,7 +125,7 @@ static struct max6650_data *max6650_update_device(struct device *dev);
*/
static const struct i2c_device_id max6650_id[] = {
- { "max6650", max6650 },
+ { "max6650", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6650_id);
@@ -141,7 +139,7 @@ static struct i2c_driver max6650_driver = {
.remove = max6650_remove,
.id_table = max6650_id,
.detect = max6650_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -528,7 +526,7 @@ static struct attribute_group max6650_attr_grp = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int max6650_detect(struct i2c_client *client, int kind,
+static int max6650_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c
index 1d7ffebd679d..d44787949851 100644
--- a/drivers/hwmon/pcf8591.c
+++ b/drivers/hwmon/pcf8591.c
@@ -29,7 +29,6 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(pcf8591);
static int input_mode;
module_param(input_mode, int, 0);
@@ -169,7 +168,7 @@ static const struct attribute_group pcf8591_attr_group_opt = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int pcf8591_detect(struct i2c_client *client, int kind,
+static int pcf8591_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
@@ -299,7 +298,7 @@ static struct i2c_driver pcf8591_driver = {
.class = I2C_CLASS_HWMON, /* Nearest choice */
.detect = pcf8591_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init pcf8591_init(void)
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
index 4d88c045781c..40b26673d87f 100644
--- a/drivers/hwmon/smsc47m192.c
+++ b/drivers/hwmon/smsc47m192.c
@@ -36,9 +36,6 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(smsc47m192);
-
/* SMSC47M192 registers */
#define SMSC47M192_REG_IN(nr) ((nr)<6 ? (0x20 + (nr)) : \
(0x50 + (nr) - 6))
@@ -115,13 +112,13 @@ struct smsc47m192_data {
static int smsc47m192_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int smsc47m192_detect(struct i2c_client *client, int kind,
+static int smsc47m192_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int smsc47m192_remove(struct i2c_client *client);
static struct smsc47m192_data *smsc47m192_update_device(struct device *dev);
static const struct i2c_device_id smsc47m192_id[] = {
- { "smsc47m192", smsc47m192 },
+ { "smsc47m192", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, smsc47m192_id);
@@ -135,7 +132,7 @@ static struct i2c_driver smsc47m192_driver = {
.remove = smsc47m192_remove,
.id_table = smsc47m192_id,
.detect = smsc47m192_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* Voltages */
@@ -481,7 +478,7 @@ static void smsc47m192_init_client(struct i2c_client *client)
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int smsc47m192_detect(struct i2c_client *client, int kind,
+static int smsc47m192_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c
index 4b793849c738..7dfb4dec4c5f 100644
--- a/drivers/hwmon/thmc50.c
+++ b/drivers/hwmon/thmc50.c
@@ -35,7 +35,7 @@ MODULE_LICENSE("GPL");
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(thmc50, adm1022);
+enum chips { thmc50, adm1022 };
static unsigned short adm1022_temp3[16];
static unsigned int adm1022_temp3_num;
@@ -84,7 +84,7 @@ struct thmc50_data {
u8 alarms;
};
-static int thmc50_detect(struct i2c_client *client, int kind,
+static int thmc50_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int thmc50_probe(struct i2c_client *client,
const struct i2c_device_id *id);
@@ -108,7 +108,7 @@ static struct i2c_driver thmc50_driver = {
.remove = thmc50_remove,
.id_table = thmc50_id,
.detect = thmc50_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static ssize_t show_analog_out(struct device *dev,
@@ -286,7 +286,7 @@ static const struct attribute_group temp3_group = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int thmc50_detect(struct i2c_client *client, int kind,
+static int thmc50_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
unsigned company;
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index ee9673467c4a..a13b30e8d8d8 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -42,8 +42,7 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(tmp401, tmp411);
+enum chips { tmp401, tmp411 };
/*
* The TMP401 registers, note some registers have different addresses for
@@ -98,7 +97,7 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int tmp401_detect(struct i2c_client *client, int kind,
+static int tmp401_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int tmp401_remove(struct i2c_client *client);
static struct tmp401_data *tmp401_update_device(struct device *dev);
@@ -123,7 +122,7 @@ static struct i2c_driver tmp401_driver = {
.remove = tmp401_remove,
.id_table = tmp401_id,
.detect = tmp401_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -488,7 +487,7 @@ static void tmp401_init_client(struct i2c_client *client)
i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
}
-static int tmp401_detect(struct i2c_client *client, int _kind,
+static int tmp401_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
enum chips kind;
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index bb5464a289ca..4f7c051e2d7b 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -39,8 +39,7 @@
static unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f,
I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_3(tmp421, tmp422, tmp423);
+enum chips { tmp421, tmp422, tmp423 };
/* The TMP421 registers */
#define TMP421_CONFIG_REG_1 0x09
@@ -223,7 +222,7 @@ static int tmp421_init_client(struct i2c_client *client)
return 0;
}
-static int tmp421_detect(struct i2c_client *client, int _kind,
+static int tmp421_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
enum chips kind;
@@ -322,7 +321,7 @@ static struct i2c_driver tmp421_driver = {
.remove = tmp421_remove,
.id_table = tmp421_id,
.detect = tmp421_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init tmp421_init(void)
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index bb5e78748783..0dcaba9b7189 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -5,6 +5,7 @@
Copyright (C) 2006 Yuan Mu (Winbond),
Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
+ Daniel J Blueman <daniel.blueman@gmail.com>
Shamelessly ripped from the w83627hf driver
Copyright (C) 2003 Mark Studebaker
@@ -177,12 +178,15 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
#define W83627EHF_REG_ALARM3 0x45B
/* SmartFan registers */
+#define W83627EHF_REG_FAN_STEPUP_TIME 0x0f
+#define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e
+
/* DC or PWM output fan configuration */
static const u8 W83627EHF_REG_PWM_ENABLE[] = {
0x04, /* SYS FAN0 output mode and PWM mode */
0x04, /* CPU FAN0 output mode and PWM mode */
0x12, /* AUX FAN mode */
- 0x62, /* CPU fan1 mode */
+ 0x62, /* CPU FAN1 mode */
};
static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
@@ -193,10 +197,12 @@ static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
-
/* Advanced Fan control, some values are common for all fans */
-static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
-static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
+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 };
/*
* Conversions
@@ -295,14 +301,19 @@ struct w83627ehf_data {
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
u8 pwm_enable[4]; /* 1->manual
- 2->thermal cruise (also called SmartFan I) */
+ 2->thermal cruise mode (also called SmartFan I)
+ 3->fan speed cruise mode
+ 4->variable thermal cruise (also called SmartFan III) */
u8 pwm_num; /* number of pwm */
u8 pwm[4];
u8 target_temp[4];
u8 tolerance[4];
- u8 fan_min_output[4]; /* minimum fan speed */
- u8 fan_stop_time[4];
+ u8 fan_start_output[4]; /* minimum fan speed when spinning up */
+ u8 fan_stop_output[4]; /* minimum fan speed when spinning down */
+ u8 fan_stop_time[4]; /* time at minimum before disabling fan */
+ u8 fan_max_output[4]; /* maximum fan speed */
+ u8 fan_step_output[4]; /* rate of change output value */
u8 vid;
u8 vrm;
@@ -529,8 +540,10 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
& 3) + 1;
data->pwm[i] = w83627ehf_read_value(data,
W83627EHF_REG_PWM[i]);
- data->fan_min_output[i] = w83627ehf_read_value(data,
- W83627EHF_REG_FAN_MIN_OUTPUT[i]);
+ data->fan_start_output[i] = w83627ehf_read_value(data,
+ W83627EHF_REG_FAN_START_OUTPUT[i]);
+ data->fan_stop_output[i] = w83627ehf_read_value(data,
+ W83627EHF_REG_FAN_STOP_OUTPUT[i]);
data->fan_stop_time[i] = w83627ehf_read_value(data,
W83627EHF_REG_FAN_STOP_TIME[i]);
data->target_temp[i] =
@@ -976,7 +989,7 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
u32 val = simple_strtoul(buf, NULL, 10);
u16 reg;
- if (!val || (val > 2)) /* only modes 1 and 2 are supported */
+ if (!val || (val > 4))
return -EINVAL;
mutex_lock(&data->update_lock);
reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]);
@@ -1118,7 +1131,10 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
return count; \
}
-fan_functions(fan_min_output, FAN_MIN_OUTPUT)
+fan_functions(fan_start_output, FAN_START_OUTPUT)
+fan_functions(fan_stop_output, FAN_STOP_OUTPUT)
+fan_functions(fan_max_output, FAN_MAX_OUTPUT)
+fan_functions(fan_step_output, FAN_STEP_OUTPUT)
#define fan_time_functions(reg, REG) \
static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -1161,8 +1177,14 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
store_fan_stop_time, 3),
- SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
- store_fan_min_output, 3),
+ SENSOR_ATTR(pwm4_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+ store_fan_start_output, 3),
+ SENSOR_ATTR(pwm4_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+ store_fan_stop_output, 3),
+ SENSOR_ATTR(pwm4_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+ store_fan_max_output, 3),
+ SENSOR_ATTR(pwm4_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+ store_fan_step_output, 3),
};
static struct sensor_device_attribute sda_sf3_arrays[] = {
@@ -1172,12 +1194,24 @@ static struct sensor_device_attribute sda_sf3_arrays[] = {
store_fan_stop_time, 1),
SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
store_fan_stop_time, 2),
- SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
- store_fan_min_output, 0),
- SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
- store_fan_min_output, 1),
- SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
- store_fan_min_output, 2),
+ SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+ store_fan_start_output, 0),
+ SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+ store_fan_start_output, 1),
+ SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+ store_fan_start_output, 2),
+ SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+ store_fan_stop_output, 0),
+ SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+ 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 */
+ 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),
};
static ssize_t
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index 7ab7967da0a0..05f9225b6f94 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -56,9 +56,10 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f);
+enum chips { w83781d, w83782d, w83783s, as99127f };
+
+/* Insmod parameters */
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
@@ -1051,8 +1052,7 @@ w83781d_create_files(struct device *dev, int kind, int is_isa)
/* Return 0 if detection is successful, -ENODEV otherwise */
static int
-w83781d_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+w83781d_detect(struct i2c_client *client, struct i2c_board_info *info)
{
int val1, val2;
struct w83781d_data *isa = w83781d_data_if_isa();
@@ -1537,7 +1537,7 @@ static struct i2c_driver w83781d_driver = {
.remove = w83781d_remove,
.id_table = w83781d_ids,
.detect = w83781d_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 0410bf12c521..400a88bde278 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -52,7 +52,6 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83791d);
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
@@ -326,7 +325,7 @@ struct w83791d_data {
static int w83791d_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int w83791d_detect(struct i2c_client *client, int kind,
+static int w83791d_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int w83791d_remove(struct i2c_client *client);
@@ -341,7 +340,7 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev);
static void w83791d_init_client(struct i2c_client *client);
static const struct i2c_device_id w83791d_id[] = {
- { "w83791d", w83791d },
+ { "w83791d", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83791d_id);
@@ -355,7 +354,7 @@ static struct i2c_driver w83791d_driver = {
.remove = w83791d_remove,
.id_table = w83791d_id,
.detect = w83791d_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/* following are the sysfs callback functions */
@@ -1259,7 +1258,7 @@ error_sc_0:
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83791d_detect(struct i2c_client *client, int kind,
+static int w83791d_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 38978851333f..679718e6b017 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -50,7 +50,6 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83792d);
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
@@ -302,7 +301,7 @@ struct w83792d_data {
static int w83792d_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int w83792d_detect(struct i2c_client *client, int kind,
+static int w83792d_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int w83792d_remove(struct i2c_client *client);
static struct w83792d_data *w83792d_update_device(struct device *dev);
@@ -314,7 +313,7 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev);
static void w83792d_init_client(struct i2c_client *client);
static const struct i2c_device_id w83792d_id[] = {
- { "w83792d", w83792d },
+ { "w83792d", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83792d_id);
@@ -328,7 +327,7 @@ static struct i2c_driver w83792d_driver = {
.remove = w83792d_remove,
.id_table = w83792d_id,
.detect = w83792d_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static inline long in_count_from_reg(int nr, struct w83792d_data *data)
@@ -1263,7 +1262,7 @@ static const struct attribute_group w83792d_group = {
/* Return 0 if detection is successful, -ENODEV otherwise */
static int
-w83792d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
+w83792d_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int val1, val2;
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 80a2191bf127..9a2022b67495 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -41,7 +41,6 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83793);
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
@@ -230,7 +229,7 @@ static u8 w83793_read_value(struct i2c_client *client, u16 reg);
static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value);
static int w83793_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int w83793_detect(struct i2c_client *client, int kind,
+static int w83793_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int w83793_remove(struct i2c_client *client);
static void w83793_init_client(struct i2c_client *client);
@@ -238,7 +237,7 @@ static void w83793_update_nonvolatile(struct device *dev);
static struct w83793_data *w83793_update_device(struct device *dev);
static const struct i2c_device_id w83793_id[] = {
- { "w83793", w83793 },
+ { "w83793", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83793_id);
@@ -252,7 +251,7 @@ static struct i2c_driver w83793_driver = {
.remove = w83793_remove,
.id_table = w83793_id,
.detect = w83793_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static ssize_t
@@ -1161,7 +1160,7 @@ ERROR_SC_0:
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83793_detect(struct i2c_client *client, int kind,
+static int w83793_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
u8 tmp, bank, chip_id;
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index 9b6c4c10fba7..20781def65ed 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -52,12 +52,6 @@
static const unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(w83l785ts);
-
-/*
* The W83L785TS-S registers
* Manufacturer ID is 0x5CA3 for Winbond.
*/
@@ -83,7 +77,7 @@ I2C_CLIENT_INSMOD_1(w83l785ts);
static int w83l785ts_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int w83l785ts_detect(struct i2c_client *client, int kind,
+static int w83l785ts_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int w83l785ts_remove(struct i2c_client *client);
static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval);
@@ -94,7 +88,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev);
*/
static const struct i2c_device_id w83l785ts_id[] = {
- { "w83l785ts", w83l785ts },
+ { "w83l785ts", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83l785ts_id);
@@ -108,7 +102,7 @@ static struct i2c_driver w83l785ts_driver = {
.remove = w83l785ts_remove,
.id_table = w83l785ts_id,
.detect = w83l785ts_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
/*
@@ -146,7 +140,7 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1);
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83l785ts_detect(struct i2c_client *client, int kind,
+static int w83l785ts_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c
index 27da7d2b15fb..0254e181893d 100644
--- a/drivers/hwmon/w83l786ng.c
+++ b/drivers/hwmon/w83l786ng.c
@@ -38,7 +38,6 @@
static const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END };
/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83l786ng);
static int reset;
module_param(reset, bool, 0);
@@ -147,14 +146,14 @@ struct w83l786ng_data {
static int w83l786ng_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int w83l786ng_detect(struct i2c_client *client, int kind,
+static int w83l786ng_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int w83l786ng_remove(struct i2c_client *client);
static void w83l786ng_init_client(struct i2c_client *client);
static struct w83l786ng_data *w83l786ng_update_device(struct device *dev);
static const struct i2c_device_id w83l786ng_id[] = {
- { "w83l786ng", w83l786ng },
+ { "w83l786ng", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83l786ng_id);
@@ -168,7 +167,7 @@ static struct i2c_driver w83l786ng_driver = {
.remove = w83l786ng_remove,
.id_table = w83l786ng_id,
.detect = w83l786ng_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static u8
@@ -586,8 +585,7 @@ static const struct attribute_group w83l786ng_group = {
};
static int
-w83l786ng_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
u16 man_id;
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index 049555777f67..7647a20523a0 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1155,7 +1155,7 @@ static int i2c_pxa_resume_noirq(struct device *dev)
return 0;
}
-static struct dev_pm_ops i2c_pxa_dev_pm_ops = {
+static const struct dev_pm_ops i2c_pxa_dev_pm_ops = {
.suspend_noirq = i2c_pxa_suspend_noirq,
.resume_noirq = i2c_pxa_resume_noirq,
};
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 96aafb91b69a..1d8c98613fa0 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -967,7 +967,7 @@ static int s3c24xx_i2c_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
+static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
.suspend_noirq = s3c24xx_i2c_suspend_noirq,
.resume = s3c24xx_i2c_resume,
};
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 86a9d4e81472..ccc46418ef7f 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -647,7 +647,7 @@ static int sh_mobile_i2c_runtime_nop(struct device *dev)
return 0;
}
-static struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
+static const struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
.runtime_suspend = sh_mobile_i2c_runtime_nop,
.runtime_resume = sh_mobile_i2c_runtime_nop,
};
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 4f34823e86b1..0ac2f90ab840 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -155,6 +155,35 @@ static void i2c_device_shutdown(struct device *dev)
driver->shutdown(client);
}
+#ifdef CONFIG_SUSPEND
+static int i2c_device_pm_suspend(struct device *dev)
+{
+ const struct dev_pm_ops *pm;
+
+ if (!dev->driver)
+ return 0;
+ pm = dev->driver->pm;
+ if (!pm || !pm->suspend)
+ return 0;
+ return pm->suspend(dev);
+}
+
+static int i2c_device_pm_resume(struct device *dev)
+{
+ const struct dev_pm_ops *pm;
+
+ if (!dev->driver)
+ return 0;
+ pm = dev->driver->pm;
+ if (!pm || !pm->resume)
+ return 0;
+ return pm->resume(dev);
+}
+#else
+#define i2c_device_pm_suspend NULL
+#define i2c_device_pm_resume NULL
+#endif
+
static int i2c_device_suspend(struct device *dev, pm_message_t mesg)
{
struct i2c_client *client = i2c_verify_client(dev);
@@ -219,6 +248,11 @@ static const struct attribute_group *i2c_dev_attr_groups[] = {
NULL
};
+const static struct dev_pm_ops i2c_device_pm_ops = {
+ .suspend = i2c_device_pm_suspend,
+ .resume = i2c_device_pm_resume,
+};
+
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
@@ -227,6 +261,7 @@ struct bus_type i2c_bus_type = {
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
+ .pm = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
@@ -1184,7 +1219,7 @@ static int i2c_detect_address(struct i2c_client *temp_client,
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
- err = driver->detect(temp_client, -1, &info);
+ err = driver->detect(temp_client, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */
@@ -1214,13 +1249,13 @@ static int i2c_detect_address(struct i2c_client *temp_client,
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
- const struct i2c_client_address_data *address_data;
+ const unsigned short *address_list;
struct i2c_client *temp_client;
int i, err = 0;
int adap_id = i2c_adapter_id(adapter);
- address_data = driver->address_data;
- if (!driver->detect || !address_data)
+ address_list = driver->address_list;
+ if (!driver->detect || !address_list)
return 0;
/* Set up a temporary client to help detect callback */
@@ -1235,7 +1270,7 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
/* Stop here if we can't use SMBUS_QUICK */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
- if (address_data->normal_i2c[0] == I2C_CLIENT_END)
+ if (address_list[0] == I2C_CLIENT_END)
goto exit_free;
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
@@ -1244,11 +1279,10 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
goto exit_free;
}
- for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
+ 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_data->normal_i2c[i]);
- temp_client->addr = address_data->normal_i2c[i];
+ "addr 0x%02x\n", adap_id, address_list[i]);
+ temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
if (err)
goto exit_free;
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 4b89b791be6a..42be0b15084b 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -826,8 +826,7 @@ static void __cpuinit take_over_work(struct ehca_comp_pool *pool, int cpu)
cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
list_del(&cq->entry);
- __queue_comp_task(cq, per_cpu_ptr(pool->cpu_comp_tasks,
- smp_processor_id()));
+ __queue_comp_task(cq, this_cpu_ptr(pool->cpu_comp_tasks));
}
spin_unlock_irqrestore(&cct->task_lock, flags_cct);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 203b88a82b56..02c836e11813 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -24,6 +24,16 @@ config KEYBOARD_AAED2000
To compile this driver as a module, choose M here: the
module will be called aaed2000_kbd.
+config KEYBOARD_ADP5520
+ tristate "Keypad Support for ADP5520 PMIC"
+ depends on PMIC_ADP5520
+ help
+ This option enables support for the keypad scan matrix
+ on Analog Devices ADP5520 PMICs.
+
+ To compile this driver as a module, choose M here: the module will
+ be called adp5520-keys.
+
config KEYBOARD_ADP5588
tristate "ADP5588 I2C QWERTY Keypad and IO Expander"
depends on I2C
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 68c017235ce9..78654ef65206 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
diff --git a/drivers/input/keyboard/adp5520-keys.c b/drivers/input/keyboard/adp5520-keys.c
new file mode 100644
index 000000000000..a7ba27fb4109
--- /dev/null
+++ b/drivers/input/keyboard/adp5520-keys.c
@@ -0,0 +1,220 @@
+/*
+ * Keypad driver for Analog Devices ADP5520 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/adp5520.h>
+
+struct adp5520_keys {
+ struct input_dev *input;
+ struct notifier_block notifier;
+ struct device *master;
+ unsigned short keycode[ADP5520_KEYMAPSIZE];
+};
+
+static void adp5520_keys_report_event(struct adp5520_keys *dev,
+ unsigned short keymask, int value)
+{
+ int i;
+
+ for (i = 0; i < ADP5520_MAXKEYS; i++)
+ if (keymask & (1 << i))
+ input_report_key(dev->input, dev->keycode[i], value);
+
+ input_sync(dev->input);
+}
+
+static int adp5520_keys_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct adp5520_keys *dev;
+ uint8_t reg_val_lo, reg_val_hi;
+ unsigned short keymask;
+
+ dev = container_of(nb, struct adp5520_keys, notifier);
+
+ if (event & ADP5520_KP_INT) {
+ adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
+ adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
+
+ keymask = (reg_val_hi << 8) | reg_val_lo;
+ /* Read twice to clear */
+ adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
+ adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
+ keymask |= (reg_val_hi << 8) | reg_val_lo;
+ adp5520_keys_report_event(dev, keymask, 1);
+ }
+
+ if (event & ADP5520_KR_INT) {
+ adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
+ adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
+
+ keymask = (reg_val_hi << 8) | reg_val_lo;
+ /* Read twice to clear */
+ adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
+ adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
+ keymask |= (reg_val_hi << 8) | reg_val_lo;
+ adp5520_keys_report_event(dev, keymask, 0);
+ }
+
+ return 0;
+}
+
+static int __devinit adp5520_keys_probe(struct platform_device *pdev)
+{
+ struct adp5520_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct input_dev *input;
+ struct adp5520_keys *dev;
+ int ret, i;
+ unsigned char en_mask, ctl_mask = 0;
+
+ if (pdev->id != ID_ADP5520) {
+ dev_err(&pdev->dev, "only ADP5520 supports Keypad\n");
+ return -EINVAL;
+ }
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -EINVAL;
+ }
+
+ if (!(pdata->rows_en_mask && pdata->cols_en_mask))
+ return -EINVAL;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&pdev->dev, "failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->master = pdev->dev.parent;
+ dev->input = input;
+
+ input->name = pdev->name;
+ input->phys = "adp5520-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input_set_drvdata(input, dev);
+
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x5520;
+ input->id.version = 0x0001;
+
+ input->keycodesize = sizeof(dev->keycode[0]);
+ input->keycodemax = pdata->keymapsize;
+ input->keycode = dev->keycode;
+
+ memcpy(dev->keycode, pdata->keymap,
+ pdata->keymapsize * input->keycodesize);
+
+ /* setup input device */
+ __set_bit(EV_KEY, input->evbit);
+
+ if (pdata->repeat)
+ __set_bit(EV_REP, input->evbit);
+
+ for (i = 0; i < input->keycodemax; i++)
+ __set_bit(dev->keycode[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register input device\n");
+ goto err;
+ }
+
+ en_mask = pdata->rows_en_mask | pdata->cols_en_mask;
+
+ ret = adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_1, en_mask);
+
+ if (en_mask & ADP5520_COL_C3)
+ ctl_mask |= ADP5520_C3_MODE;
+
+ if (en_mask & ADP5520_ROW_R3)
+ ctl_mask |= ADP5520_R3_MODE;
+
+ if (ctl_mask)
+ ret |= adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
+ ctl_mask);
+
+ ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
+ pdata->rows_en_mask);
+
+ if (ret) {
+ dev_err(&pdev->dev, "failed to write\n");
+ ret = -EIO;
+ goto err1;
+ }
+
+ dev->notifier.notifier_call = adp5520_keys_notifier;
+ ret = adp5520_register_notifier(dev->master, &dev->notifier,
+ ADP5520_KP_IEN | ADP5520_KR_IEN);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register notifier\n");
+ goto err1;
+ }
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+err1:
+ input_unregister_device(input);
+ input = NULL;
+err:
+ input_free_device(input);
+ kfree(dev);
+ return ret;
+}
+
+static int __devexit adp5520_keys_remove(struct platform_device *pdev)
+{
+ struct adp5520_keys *dev = platform_get_drvdata(pdev);
+
+ adp5520_unregister_notifier(dev->master, &dev->notifier,
+ ADP5520_KP_IEN | ADP5520_KR_IEN);
+
+ input_unregister_device(dev->input);
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver adp5520_keys_driver = {
+ .driver = {
+ .name = "adp5520-keys",
+ .owner = THIS_MODULE,
+ },
+ .probe = adp5520_keys_probe,
+ .remove = __devexit_p(adp5520_keys_remove),
+};
+
+static int __init adp5520_keys_init(void)
+{
+ return platform_driver_register(&adp5520_keys_driver);
+}
+module_init(adp5520_keys_init);
+
+static void __exit adp5520_keys_exit(void)
+{
+ platform_driver_unregister(&adp5520_keys_driver);
+}
+module_exit(adp5520_keys_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Keys ADP5520 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-keys");
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index d48c808d5928..1edb596d927b 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -319,7 +319,7 @@ static int adp5588_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops adp5588_dev_pm_ops = {
+static const struct dev_pm_ops adp5588_dev_pm_ops = {
.suspend = adp5588_suspend,
.resume = adp5588_resume,
};
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 076111fc72d2..8e9380bfed40 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -295,7 +295,7 @@ static int sh_keysc_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops sh_keysc_dev_pm_ops = {
+static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
.suspend = sh_keysc_suspend,
.resume = sh_keysc_resume,
};
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
index 9a2977c21696..eeaa7acb9cfc 100644
--- a/drivers/input/keyboard/twl4030_keypad.c
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -31,7 +31,7 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
/*
@@ -133,7 +133,7 @@ struct twl4030_keypad {
static int twl4030_kpread(struct twl4030_keypad *kp,
u8 *data, u32 reg, u8 num_bytes)
{
- int ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
+ int ret = twl_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
if (ret < 0)
dev_warn(kp->dbg_dev,
@@ -145,7 +145,7 @@ static int twl4030_kpread(struct twl4030_keypad *kp,
static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
{
- int ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
+ int ret = twl_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
if (ret < 0)
dev_warn(kp->dbg_dev,
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
index 690f3fafa03b..61d10177fa83 100644
--- a/drivers/input/misc/bfin_rotary.c
+++ b/drivers/input/misc/bfin_rotary.c
@@ -247,7 +247,7 @@ static int bfin_rotary_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops bfin_rotary_pm_ops = {
+static const struct dev_pm_ops bfin_rotary_pm_ops = {
.suspend = bfin_rotary_suspend,
.resume = bfin_rotary_resume,
};
diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c
index 039dcb00ebd9..008de0c5834b 100644
--- a/drivers/input/misc/pcf50633-input.c
+++ b/drivers/input/misc/pcf50633-input.c
@@ -55,7 +55,6 @@ pcf50633_input_irq(int irq, void *data)
static int __devinit pcf50633_input_probe(struct platform_device *pdev)
{
struct pcf50633_input *input;
- struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
struct input_dev *input_dev;
int ret;
@@ -71,7 +70,7 @@ static int __devinit pcf50633_input_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, input);
- input->pcf = pdata->pcf;
+ input->pcf = dev_to_pcf50633(pdev->dev.parent);
input->input_dev = input_dev;
input_dev->name = "PCF50633 PMU events";
@@ -85,9 +84,9 @@ static int __devinit pcf50633_input_probe(struct platform_device *pdev)
kfree(input);
return ret;
}
- pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYR,
+ pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
pcf50633_input_irq, input);
- pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYF,
+ pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
pcf50633_input_irq, input);
return 0;
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
index 21cb755a54fb..ea4e1fd12651 100644
--- a/drivers/input/misc/pcspkr.c
+++ b/drivers/input/misc/pcspkr.c
@@ -127,7 +127,7 @@ static void pcspkr_shutdown(struct platform_device *dev)
pcspkr_event(NULL, EV_SND, SND_BELL, 0);
}
-static struct dev_pm_ops pcspkr_pm_ops = {
+static const struct dev_pm_ops pcspkr_pm_ops = {
.suspend = pcspkr_suspend,
};
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index f5fc9974a111..bdde5c889035 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -27,7 +27,7 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#define PWR_PWRON_IRQ (1 << 0)
@@ -49,7 +49,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr)
local_irq_enable();
#endif
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
STS_HW_CONDITIONS);
if (!err) {
input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
index 67fcd33595de..b79097e3028a 100644
--- a/drivers/input/touchscreen/pcap_ts.c
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -233,7 +233,7 @@ static int pcap_ts_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops pcap_ts_pm_ops = {
+static const struct dev_pm_ops pcap_ts_pm_ops = {
.suspend = pcap_ts_suspend,
.resume = pcap_ts_resume,
};
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
index 5a6ae646a636..94b796d84053 100644
--- a/drivers/isdn/hardware/avm/avm_cs.c
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -108,8 +108,7 @@ static int avmcs_probe(struct pcmcia_device *p_dev)
p_dev->io.NumPorts2 = 0;
/* Interrupt setup */
- p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
/* General socket configuration */
p_dev->conf.Attributes = CONF_ENABLE_IRQ;
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
index f9bdff39cf4a..e5deb15cf40c 100644
--- a/drivers/isdn/hisax/avma1_cs.c
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -120,8 +120,7 @@ static int avma1cs_probe(struct pcmcia_device *p_dev)
p_dev->io.IOAddrLines = 5;
/* Interrupt setup */
- p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
/* General socket configuration */
p_dev->conf.Attributes = CONF_ENABLE_IRQ;
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
index a2f709f53974..c9a30b1c9237 100644
--- a/drivers/isdn/hisax/elsa_cs.c
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -137,7 +137,7 @@ static int elsa_cs_probe(struct pcmcia_device *link)
local->cardnr = -1;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = NULL;
/*
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
index af5d393cc2d0..7836ec3c7f86 100644
--- a/drivers/isdn/hisax/sedlbauer_cs.c
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -144,7 +144,7 @@ static int sedlbauer_probe(struct pcmcia_device *link)
link->priv = local;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = NULL;
/*
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
index ea705394ce2b..b0c5976cbdb3 100644
--- a/drivers/isdn/hisax/teles_cs.c
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -127,7 +127,7 @@ static int teles_probe(struct pcmcia_device *link)
link->priv = local;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = NULL;
/*
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index f2cc13d76810..782f95822eab 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -50,7 +50,7 @@ static ssize_t led_brightness_store(struct device *dev,
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
- if (*after && isspace(*after))
+ if (isspace(*after))
count++;
if (count == size) {
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
index 3b83406de752..38b3378be442 100644
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/ledtrig-timer.c
@@ -83,7 +83,7 @@ static ssize_t led_delay_on_store(struct device *dev,
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
- if (*after && isspace(*after))
+ if (isspace(*after))
count++;
if (count == size) {
@@ -127,7 +127,7 @@ static ssize_t led_delay_off_store(struct device *dev,
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
- if (*after && isspace(*after))
+ if (isspace(*after))
count++;
if (count == size) {
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 6ae388849a3b..fb2b7ef7868e 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -69,7 +69,7 @@ static struct lguest_pages *lguest_pages(unsigned int cpu)
(SWITCHER_ADDR + SHARED_SWITCHER_PAGES*PAGE_SIZE))[cpu]);
}
-static DEFINE_PER_CPU(struct lg_cpu *, last_cpu);
+static DEFINE_PER_CPU(struct lg_cpu *, lg_last_cpu);
/*S:010
* We approach the Switcher.
@@ -90,8 +90,8 @@ static void copy_in_guest_info(struct lg_cpu *cpu, struct lguest_pages *pages)
* meanwhile). If that's not the case, we pretend everything in the
* Guest has changed.
*/
- if (__get_cpu_var(last_cpu) != cpu || cpu->last_pages != pages) {
- __get_cpu_var(last_cpu) = cpu;
+ if (__get_cpu_var(lg_last_cpu) != cpu || cpu->last_pages != pages) {
+ __get_cpu_var(lg_last_cpu) = cpu;
cpu->last_pages = pages;
cpu->changed = CHANGED_ALL;
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 2158377a1359..acb3a4e404ff 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -185,11 +185,10 @@ config MD_MULTIPATH
tristate "Multipath I/O support"
depends on BLK_DEV_MD
help
- Multipath-IO is the ability of certain devices to address the same
- physical disk over multiple 'IO paths'. The code ensures that such
- paths can be defined and handled at runtime, and ensures that a
- transparent failover to the backup path(s) happens if a IO errors
- arrives on the primary path.
+ MD_MULTIPATH provides a simple multi-path personality for use
+ the MD framework. It is not under active development. New
+ projects should consider using DM_MULTIPATH which has more
+ features and more testing.
If unsure, say N.
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 60e2b322db11..26ac8aad0b19 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -212,7 +212,7 @@ static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
*/
/* IO operations when bitmap is stored near all superblocks */
-static struct page *read_sb_page(mddev_t *mddev, long offset,
+static struct page *read_sb_page(mddev_t *mddev, loff_t offset,
struct page *page,
unsigned long index, int size)
{
@@ -287,27 +287,36 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
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 (bitmap->offset < 0) {
+ 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 (bitmap->offset
+ 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 + bitmap->offset)
+ > 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
- + bitmap->offset
+ + offset
+ page->index*(PAGE_SIZE/512) + size/512
> rdev->data_offset)
/* bitmap runs in to data */
@@ -316,7 +325,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
/* DATA METADATA BITMAP - no problems */
}
md_super_write(mddev, rdev,
- rdev->sb_start + bitmap->offset
+ rdev->sb_start + offset
+ page->index * (PAGE_SIZE/512),
size,
page);
@@ -488,6 +497,8 @@ void bitmap_update_sb(struct bitmap *bitmap)
if (!bitmap || !bitmap->mddev) /* no bitmap for this array */
return;
+ if (bitmap->mddev->bitmap_info.external)
+ return;
spin_lock_irqsave(&bitmap->lock, flags);
if (!bitmap->sb_page) { /* no superblock */
spin_unlock_irqrestore(&bitmap->lock, flags);
@@ -501,6 +512,9 @@ void bitmap_update_sb(struct bitmap *bitmap)
bitmap->events_cleared = bitmap->mddev->events;
sb->events_cleared = cpu_to_le64(bitmap->events_cleared);
}
+ /* Just in case these have been changed via sysfs: */
+ sb->daemon_sleep = cpu_to_le32(bitmap->mddev->bitmap_info.daemon_sleep/HZ);
+ sb->write_behind = cpu_to_le32(bitmap->mddev->bitmap_info.max_write_behind);
kunmap_atomic(sb, KM_USER0);
write_page(bitmap, bitmap->sb_page, 1);
}
@@ -550,7 +564,8 @@ static int bitmap_read_sb(struct bitmap *bitmap)
bitmap->sb_page = read_page(bitmap->file, 0, bitmap, bytes);
} else {
- bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset,
+ bitmap->sb_page = read_sb_page(bitmap->mddev,
+ bitmap->mddev->bitmap_info.offset,
NULL,
0, sizeof(bitmap_super_t));
}
@@ -563,7 +578,7 @@ static int bitmap_read_sb(struct bitmap *bitmap)
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
chunksize = le32_to_cpu(sb->chunksize);
- daemon_sleep = le32_to_cpu(sb->daemon_sleep);
+ daemon_sleep = le32_to_cpu(sb->daemon_sleep) * HZ;
write_behind = le32_to_cpu(sb->write_behind);
/* verify that the bitmap-specific fields are valid */
@@ -576,7 +591,7 @@ static int bitmap_read_sb(struct bitmap *bitmap)
reason = "bitmap chunksize too small";
else if ((1 << ffz(~chunksize)) != chunksize)
reason = "bitmap chunksize not a power of 2";
- else if (daemon_sleep < 1 || daemon_sleep > MAX_SCHEDULE_TIMEOUT / HZ)
+ else if (daemon_sleep < 1 || daemon_sleep > MAX_SCHEDULE_TIMEOUT)
reason = "daemon sleep period out of range";
else if (write_behind > COUNTER_MAX)
reason = "write-behind limit out of range (0 - 16383)";
@@ -610,10 +625,9 @@ static int bitmap_read_sb(struct bitmap *bitmap)
}
success:
/* assign fields using values from superblock */
- bitmap->chunksize = chunksize;
- bitmap->daemon_sleep = daemon_sleep;
- bitmap->daemon_lastrun = jiffies;
- bitmap->max_write_behind = write_behind;
+ bitmap->mddev->bitmap_info.chunksize = chunksize;
+ bitmap->mddev->bitmap_info.daemon_sleep = daemon_sleep;
+ bitmap->mddev->bitmap_info.max_write_behind = write_behind;
bitmap->flags |= le32_to_cpu(sb->state);
if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN)
bitmap->flags |= BITMAP_HOSTENDIAN;
@@ -664,16 +678,26 @@ static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
* general bitmap file operations
*/
+/*
+ * on-disk bitmap:
+ *
+ * Use one bit per "chunk" (block set). We do the disk I/O on the bitmap
+ * file a page at a time. There's a superblock at the start of the file.
+ */
/* calculate the index of the page that contains this bit */
-static inline unsigned long file_page_index(unsigned long chunk)
+static inline unsigned long file_page_index(struct bitmap *bitmap, unsigned long chunk)
{
- return CHUNK_BIT_OFFSET(chunk) >> PAGE_BIT_SHIFT;
+ if (!bitmap->mddev->bitmap_info.external)
+ chunk += sizeof(bitmap_super_t) << 3;
+ return chunk >> PAGE_BIT_SHIFT;
}
/* calculate the (bit) offset of this bit within a page */
-static inline unsigned long file_page_offset(unsigned long chunk)
+static inline unsigned long file_page_offset(struct bitmap *bitmap, unsigned long chunk)
{
- return CHUNK_BIT_OFFSET(chunk) & (PAGE_BITS - 1);
+ if (!bitmap->mddev->bitmap_info.external)
+ chunk += sizeof(bitmap_super_t) << 3;
+ return chunk & (PAGE_BITS - 1);
}
/*
@@ -686,8 +710,9 @@ static inline unsigned long file_page_offset(unsigned long chunk)
static inline struct page *filemap_get_page(struct bitmap *bitmap,
unsigned long chunk)
{
- if (file_page_index(chunk) >= bitmap->file_pages) return NULL;
- return bitmap->filemap[file_page_index(chunk) - file_page_index(0)];
+ if (file_page_index(bitmap, chunk) >= bitmap->file_pages) return NULL;
+ return bitmap->filemap[file_page_index(bitmap, chunk)
+ - file_page_index(bitmap, 0)];
}
@@ -710,7 +735,7 @@ static void bitmap_file_unmap(struct bitmap *bitmap)
spin_unlock_irqrestore(&bitmap->lock, flags);
while (pages--)
- if (map[pages]->index != 0) /* 0 is sb_page, release it below */
+ if (map[pages] != sb_page) /* 0 is sb_page, release it below */
free_buffers(map[pages]);
kfree(map);
kfree(attr);
@@ -821,7 +846,7 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
page = filemap_get_page(bitmap, chunk);
if (!page) return;
- bit = file_page_offset(chunk);
+ bit = file_page_offset(bitmap, chunk);
/* set the bit */
kaddr = kmap_atomic(page, KM_USER0);
@@ -907,7 +932,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
chunks = bitmap->chunks;
file = bitmap->file;
- BUG_ON(!file && !bitmap->offset);
+ BUG_ON(!file && !bitmap->mddev->bitmap_info.offset);
#ifdef INJECT_FAULTS_3
outofdate = 1;
@@ -919,14 +944,17 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
"recovery\n", bmname(bitmap));
bytes = (chunks + 7) / 8;
+ if (!bitmap->mddev->bitmap_info.external)
+ bytes += sizeof(bitmap_super_t);
- num_pages = (bytes + sizeof(bitmap_super_t) + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ num_pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
- if (file && i_size_read(file->f_mapping->host) < bytes + sizeof(bitmap_super_t)) {
+ if (file && i_size_read(file->f_mapping->host) < bytes) {
printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
bmname(bitmap),
(unsigned long) i_size_read(file->f_mapping->host),
- bytes + sizeof(bitmap_super_t));
+ bytes);
goto err;
}
@@ -947,17 +975,16 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
for (i = 0; i < chunks; i++) {
int b;
- index = file_page_index(i);
- bit = file_page_offset(i);
+ index = file_page_index(bitmap, i);
+ bit = file_page_offset(bitmap, i);
if (index != oldindex) { /* this is a new page, read it in */
int count;
/* unmap the old page, we're done with it */
if (index == num_pages-1)
- count = bytes + sizeof(bitmap_super_t)
- - index * PAGE_SIZE;
+ count = bytes - index * PAGE_SIZE;
else
count = PAGE_SIZE;
- if (index == 0) {
+ if (index == 0 && bitmap->sb_page) {
/*
* if we're here then the superblock page
* contains some bits (PAGE_SIZE != sizeof sb)
@@ -967,14 +994,15 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
offset = sizeof(bitmap_super_t);
if (!file)
read_sb_page(bitmap->mddev,
- bitmap->offset,
+ bitmap->mddev->bitmap_info.offset,
page,
index, count);
} else if (file) {
page = read_page(file, index, bitmap, count);
offset = 0;
} else {
- page = read_sb_page(bitmap->mddev, bitmap->offset,
+ page = read_sb_page(bitmap->mddev,
+ bitmap->mddev->bitmap_info.offset,
NULL,
index, count);
offset = 0;
@@ -1078,23 +1106,32 @@ static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
* out to disk
*/
-void bitmap_daemon_work(struct bitmap *bitmap)
+void bitmap_daemon_work(mddev_t *mddev)
{
+ struct bitmap *bitmap;
unsigned long j;
unsigned long flags;
struct page *page = NULL, *lastpage = NULL;
int blocks;
void *paddr;
- if (bitmap == NULL)
+ /* Use a mutex to guard daemon_work against
+ * bitmap_destroy.
+ */
+ mutex_lock(&mddev->bitmap_info.mutex);
+ bitmap = mddev->bitmap;
+ if (bitmap == NULL) {
+ mutex_unlock(&mddev->bitmap_info.mutex);
return;
- if (time_before(jiffies, bitmap->daemon_lastrun + bitmap->daemon_sleep*HZ))
+ }
+ if (time_before(jiffies, bitmap->daemon_lastrun
+ + bitmap->mddev->bitmap_info.daemon_sleep))
goto done;
bitmap->daemon_lastrun = jiffies;
if (bitmap->allclean) {
bitmap->mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
- return;
+ goto done;
}
bitmap->allclean = 1;
@@ -1142,7 +1179,8 @@ void bitmap_daemon_work(struct bitmap *bitmap)
/* We are possibly going to clear some bits, so make
* sure that events_cleared is up-to-date.
*/
- if (bitmap->need_sync) {
+ if (bitmap->need_sync &&
+ bitmap->mddev->bitmap_info.external == 0) {
bitmap_super_t *sb;
bitmap->need_sync = 0;
sb = kmap_atomic(bitmap->sb_page, KM_USER0);
@@ -1152,7 +1190,8 @@ void bitmap_daemon_work(struct bitmap *bitmap)
write_page(bitmap, bitmap->sb_page, 1);
}
spin_lock_irqsave(&bitmap->lock, flags);
- clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
+ if (!bitmap->need_sync)
+ clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
}
bmc = bitmap_get_counter(bitmap,
(sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
@@ -1167,7 +1206,7 @@ void bitmap_daemon_work(struct bitmap *bitmap)
if (*bmc == 2) {
*bmc=1; /* maybe clear the bit next time */
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
- } else if (*bmc == 1) {
+ } else if (*bmc == 1 && !bitmap->need_sync) {
/* we can clear the bit */
*bmc = 0;
bitmap_count_page(bitmap,
@@ -1177,9 +1216,11 @@ void bitmap_daemon_work(struct bitmap *bitmap)
/* clear the bit */
paddr = kmap_atomic(page, KM_USER0);
if (bitmap->flags & BITMAP_HOSTENDIAN)
- clear_bit(file_page_offset(j), paddr);
+ clear_bit(file_page_offset(bitmap, j),
+ paddr);
else
- ext2_clear_bit(file_page_offset(j), paddr);
+ ext2_clear_bit(file_page_offset(bitmap, j),
+ paddr);
kunmap_atomic(paddr, KM_USER0);
}
} else
@@ -1202,7 +1243,9 @@ void bitmap_daemon_work(struct bitmap *bitmap)
done:
if (bitmap->allclean == 0)
- bitmap->mddev->thread->timeout = bitmap->daemon_sleep * HZ;
+ bitmap->mddev->thread->timeout =
+ bitmap->mddev->bitmap_info.daemon_sleep;
+ mutex_unlock(&mddev->bitmap_info.mutex);
}
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
@@ -1332,6 +1375,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);
}
if (!success && ! (*bmc & NEEDED_MASK))
@@ -1470,7 +1514,7 @@ void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
return;
}
if (time_before(jiffies, (bitmap->last_end_sync
- + bitmap->daemon_sleep * HZ)))
+ + bitmap->mddev->bitmap_info.daemon_sleep)))
return;
wait_event(bitmap->mddev->recovery_wait,
atomic_read(&bitmap->mddev->recovery_active) == 0);
@@ -1522,6 +1566,12 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
sector_t sec = (sector_t)chunk << CHUNK_BLOCK_SHIFT(bitmap);
bitmap_set_memory_bits(bitmap, sec, 1);
bitmap_file_set_bit(bitmap, sec);
+ if (sec < bitmap->mddev->recovery_cp)
+ /* We are asserting that the array is dirty,
+ * so move the recovery_cp address back so
+ * that it is obvious that it is dirty
+ */
+ bitmap->mddev->recovery_cp = sec;
}
}
@@ -1531,7 +1581,7 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
void bitmap_flush(mddev_t *mddev)
{
struct bitmap *bitmap = mddev->bitmap;
- int sleep;
+ long sleep;
if (!bitmap) /* there was no bitmap */
return;
@@ -1539,12 +1589,13 @@ void bitmap_flush(mddev_t *mddev)
/* run the daemon_work three time to ensure everything is flushed
* that can be
*/
- sleep = bitmap->daemon_sleep;
- bitmap->daemon_sleep = 0;
- bitmap_daemon_work(bitmap);
- bitmap_daemon_work(bitmap);
- bitmap_daemon_work(bitmap);
- bitmap->daemon_sleep = sleep;
+ sleep = mddev->bitmap_info.daemon_sleep * 2;
+ bitmap->daemon_lastrun -= sleep;
+ bitmap_daemon_work(mddev);
+ bitmap->daemon_lastrun -= sleep;
+ bitmap_daemon_work(mddev);
+ bitmap->daemon_lastrun -= sleep;
+ bitmap_daemon_work(mddev);
bitmap_update_sb(bitmap);
}
@@ -1574,6 +1625,7 @@ static void bitmap_free(struct bitmap *bitmap)
kfree(bp);
kfree(bitmap);
}
+
void bitmap_destroy(mddev_t *mddev)
{
struct bitmap *bitmap = mddev->bitmap;
@@ -1581,10 +1633,15 @@ void bitmap_destroy(mddev_t *mddev)
if (!bitmap) /* there was no bitmap */
return;
+ mutex_lock(&mddev->bitmap_info.mutex);
mddev->bitmap = NULL; /* disconnect from the md device */
+ mutex_unlock(&mddev->bitmap_info.mutex);
if (mddev->thread)
mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
+ if (bitmap->sysfs_can_clear)
+ sysfs_put(bitmap->sysfs_can_clear);
+
bitmap_free(bitmap);
}
@@ -1598,16 +1655,17 @@ int bitmap_create(mddev_t *mddev)
sector_t blocks = mddev->resync_max_sectors;
unsigned long chunks;
unsigned long pages;
- struct file *file = mddev->bitmap_file;
+ struct file *file = mddev->bitmap_info.file;
int err;
sector_t start;
+ struct sysfs_dirent *bm;
BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
- if (!file && !mddev->bitmap_offset) /* bitmap disabled, nothing to do */
+ if (!file && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
return 0;
- BUG_ON(file && mddev->bitmap_offset);
+ BUG_ON(file && mddev->bitmap_info.offset);
bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
if (!bitmap)
@@ -1620,8 +1678,14 @@ int bitmap_create(mddev_t *mddev)
bitmap->mddev = mddev;
+ bm = sysfs_get_dirent(mddev->kobj.sd, "bitmap");
+ if (bm) {
+ bitmap->sysfs_can_clear = sysfs_get_dirent(bm, "can_clear");
+ sysfs_put(bm);
+ } else
+ bitmap->sysfs_can_clear = NULL;
+
bitmap->file = file;
- bitmap->offset = mddev->bitmap_offset;
if (file) {
get_file(file);
/* As future accesses to this file will use bmap,
@@ -1630,12 +1694,22 @@ int bitmap_create(mddev_t *mddev)
*/
vfs_fsync(file, file->f_dentry, 1);
}
- /* read superblock from bitmap file (this sets bitmap->chunksize) */
- err = bitmap_read_sb(bitmap);
+ /* read superblock from bitmap file (this sets mddev->bitmap_info.chunksize) */
+ if (!mddev->bitmap_info.external)
+ err = bitmap_read_sb(bitmap);
+ else {
+ err = 0;
+ if (mddev->bitmap_info.chunksize == 0 ||
+ mddev->bitmap_info.daemon_sleep == 0)
+ /* chunksize and time_base need to be
+ * set first. */
+ err = -EINVAL;
+ }
if (err)
goto error;
- bitmap->chunkshift = ffz(~bitmap->chunksize);
+ bitmap->daemon_lastrun = jiffies;
+ 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) >>
@@ -1677,7 +1751,8 @@ int bitmap_create(mddev_t *mddev)
mddev->bitmap = bitmap;
- mddev->thread->timeout = bitmap->daemon_sleep * HZ;
+ mddev->thread->timeout = mddev->bitmap_info.daemon_sleep;
+ md_wakeup_thread(mddev->thread);
bitmap_update_sb(bitmap);
@@ -1688,6 +1763,264 @@ int bitmap_create(mddev_t *mddev)
return err;
}
+static ssize_t
+location_show(mddev_t *mddev, char *page)
+{
+ ssize_t len;
+ if (mddev->bitmap_info.file) {
+ len = sprintf(page, "file");
+ } else if (mddev->bitmap_info.offset) {
+ len = sprintf(page, "%+lld", (long long)mddev->bitmap_info.offset);
+ } else
+ len = sprintf(page, "none");
+ len += sprintf(page+len, "\n");
+ return len;
+}
+
+static ssize_t
+location_store(mddev_t *mddev, const char *buf, size_t len)
+{
+
+ if (mddev->pers) {
+ if (!mddev->pers->quiesce)
+ return -EBUSY;
+ if (mddev->recovery || mddev->sync_thread)
+ return -EBUSY;
+ }
+
+ if (mddev->bitmap || mddev->bitmap_info.file ||
+ mddev->bitmap_info.offset) {
+ /* bitmap already configured. Only option is to clear it */
+ if (strncmp(buf, "none", 4) != 0)
+ return -EBUSY;
+ if (mddev->pers) {
+ mddev->pers->quiesce(mddev, 1);
+ bitmap_destroy(mddev);
+ mddev->pers->quiesce(mddev, 0);
+ }
+ mddev->bitmap_info.offset = 0;
+ if (mddev->bitmap_info.file) {
+ struct file *f = mddev->bitmap_info.file;
+ mddev->bitmap_info.file = NULL;
+ restore_bitmap_write_access(f);
+ fput(f);
+ }
+ } else {
+ /* No bitmap, OK to set a location */
+ long long offset;
+ if (strncmp(buf, "none", 4) == 0)
+ /* nothing to be done */;
+ else if (strncmp(buf, "file:", 5) == 0) {
+ /* Not supported yet */
+ return -EINVAL;
+ } else {
+ int rv;
+ if (buf[0] == '+')
+ rv = strict_strtoll(buf+1, 10, &offset);
+ else
+ rv = strict_strtoll(buf, 10, &offset);
+ if (rv)
+ return rv;
+ if (offset == 0)
+ return -EINVAL;
+ if (mddev->bitmap_info.external == 0 &&
+ mddev->major_version == 0 &&
+ offset != mddev->bitmap_info.default_offset)
+ return -EINVAL;
+ mddev->bitmap_info.offset = offset;
+ if (mddev->pers) {
+ mddev->pers->quiesce(mddev, 1);
+ rv = bitmap_create(mddev);
+ if (rv) {
+ bitmap_destroy(mddev);
+ mddev->bitmap_info.offset = 0;
+ }
+ mddev->pers->quiesce(mddev, 0);
+ if (rv)
+ return rv;
+ }
+ }
+ }
+ if (!mddev->external) {
+ /* Ensure new bitmap info is stored in
+ * metadata promptly.
+ */
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
+ md_wakeup_thread(mddev->thread);
+ }
+ return len;
+}
+
+static struct md_sysfs_entry bitmap_location =
+__ATTR(location, S_IRUGO|S_IWUSR, location_show, location_store);
+
+static ssize_t
+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));
+ len += sprintf(page+len, "\n");
+ return len;
+}
+
+static ssize_t
+timeout_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ /* timeout can be set at any time */
+ unsigned long timeout;
+ int rv = strict_strtoul_scaled(buf, &timeout, 4);
+ if (rv)
+ return rv;
+
+ /* just to make sure we don't overflow... */
+ if (timeout >= LONG_MAX / HZ)
+ return -EINVAL;
+
+ timeout = timeout * HZ / 10000;
+
+ if (timeout >= MAX_SCHEDULE_TIMEOUT)
+ timeout = MAX_SCHEDULE_TIMEOUT-1;
+ if (timeout < 1)
+ timeout = 1;
+ mddev->bitmap_info.daemon_sleep = timeout;
+ if (mddev->thread) {
+ /* if thread->timeout is MAX_SCHEDULE_TIMEOUT, then
+ * the bitmap is all clean and we don't need to
+ * adjust the timeout right now
+ */
+ if (mddev->thread->timeout < MAX_SCHEDULE_TIMEOUT) {
+ mddev->thread->timeout = timeout;
+ md_wakeup_thread(mddev->thread);
+ }
+ }
+ return len;
+}
+
+static struct md_sysfs_entry bitmap_timeout =
+__ATTR(time_base, S_IRUGO|S_IWUSR, timeout_show, timeout_store);
+
+static ssize_t
+backlog_show(mddev_t *mddev, char *page)
+{
+ return sprintf(page, "%lu\n", mddev->bitmap_info.max_write_behind);
+}
+
+static ssize_t
+backlog_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ unsigned long backlog;
+ int rv = strict_strtoul(buf, 10, &backlog);
+ if (rv)
+ return rv;
+ if (backlog > COUNTER_MAX)
+ return -EINVAL;
+ mddev->bitmap_info.max_write_behind = backlog;
+ return len;
+}
+
+static struct md_sysfs_entry bitmap_backlog =
+__ATTR(backlog, S_IRUGO|S_IWUSR, backlog_show, backlog_store);
+
+static ssize_t
+chunksize_show(mddev_t *mddev, char *page)
+{
+ return sprintf(page, "%lu\n", mddev->bitmap_info.chunksize);
+}
+
+static ssize_t
+chunksize_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ /* Can only be changed when no bitmap is active */
+ int rv;
+ unsigned long csize;
+ if (mddev->bitmap)
+ return -EBUSY;
+ rv = strict_strtoul(buf, 10, &csize);
+ if (rv)
+ return rv;
+ if (csize < 512 ||
+ !is_power_of_2(csize))
+ return -EINVAL;
+ mddev->bitmap_info.chunksize = csize;
+ return len;
+}
+
+static struct md_sysfs_entry bitmap_chunksize =
+__ATTR(chunksize, S_IRUGO|S_IWUSR, chunksize_show, chunksize_store);
+
+static ssize_t metadata_show(mddev_t *mddev, char *page)
+{
+ return sprintf(page, "%s\n", (mddev->bitmap_info.external
+ ? "external" : "internal"));
+}
+
+static ssize_t metadata_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ if (mddev->bitmap ||
+ mddev->bitmap_info.file ||
+ mddev->bitmap_info.offset)
+ return -EBUSY;
+ if (strncmp(buf, "external", 8) == 0)
+ mddev->bitmap_info.external = 1;
+ else if (strncmp(buf, "internal", 8) == 0)
+ mddev->bitmap_info.external = 0;
+ else
+ return -EINVAL;
+ return len;
+}
+
+static struct md_sysfs_entry bitmap_metadata =
+__ATTR(metadata, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
+
+static ssize_t can_clear_show(mddev_t *mddev, char *page)
+{
+ int len;
+ if (mddev->bitmap)
+ len = sprintf(page, "%s\n", (mddev->bitmap->need_sync ?
+ "false" : "true"));
+ else
+ len = sprintf(page, "\n");
+ return len;
+}
+
+static ssize_t can_clear_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ if (mddev->bitmap == NULL)
+ return -ENOENT;
+ if (strncmp(buf, "false", 5) == 0)
+ mddev->bitmap->need_sync = 1;
+ else if (strncmp(buf, "true", 4) == 0) {
+ if (mddev->degraded)
+ return -EBUSY;
+ mddev->bitmap->need_sync = 0;
+ } else
+ return -EINVAL;
+ return len;
+}
+
+static struct md_sysfs_entry bitmap_can_clear =
+__ATTR(can_clear, S_IRUGO|S_IWUSR, can_clear_show, can_clear_store);
+
+static struct attribute *md_bitmap_attrs[] = {
+ &bitmap_location.attr,
+ &bitmap_timeout.attr,
+ &bitmap_backlog.attr,
+ &bitmap_chunksize.attr,
+ &bitmap_metadata.attr,
+ &bitmap_can_clear.attr,
+ NULL
+};
+struct attribute_group md_bitmap_group = {
+ .name = "bitmap",
+ .attrs = md_bitmap_attrs,
+};
+
+
/* the bitmap API -- for raid personalities */
EXPORT_SYMBOL(bitmap_startwrite);
EXPORT_SYMBOL(bitmap_endwrite);
diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h
index e98900671ca9..cb821d76d1b4 100644
--- a/drivers/md/bitmap.h
+++ b/drivers/md/bitmap.h
@@ -106,7 +106,7 @@ typedef __u16 bitmap_counter_t;
#define BITMAP_BLOCK_SHIFT 9
/* how many blocks per chunk? (this is variable) */
-#define CHUNK_BLOCK_RATIO(bitmap) ((bitmap)->chunksize >> BITMAP_BLOCK_SHIFT)
+#define CHUNK_BLOCK_RATIO(bitmap) ((bitmap)->mddev->bitmap_info.chunksize >> BITMAP_BLOCK_SHIFT)
#define CHUNK_BLOCK_SHIFT(bitmap) ((bitmap)->chunkshift - BITMAP_BLOCK_SHIFT)
#define CHUNK_BLOCK_MASK(bitmap) (CHUNK_BLOCK_RATIO(bitmap) - 1)
@@ -118,16 +118,6 @@ typedef __u16 bitmap_counter_t;
(CHUNK_BLOCK_SHIFT(bitmap) + PAGE_COUNTER_SHIFT - 1)
#define PAGEPTR_BLOCK_MASK(bitmap) (PAGEPTR_BLOCK_RATIO(bitmap) - 1)
-/*
- * on-disk bitmap:
- *
- * Use one bit per "chunk" (block set). We do the disk I/O on the bitmap
- * file a page at a time. There's a superblock at the start of the file.
- */
-
-/* map chunks (bits) to file pages - offset by the size of the superblock */
-#define CHUNK_BIT_OFFSET(chunk) ((chunk) + (sizeof(bitmap_super_t) << 3))
-
#endif
/*
@@ -209,7 +199,6 @@ struct bitmap {
int counter_bits; /* how many bits per block counter */
/* bitmap chunksize -- how much data does each bit represent? */
- unsigned long chunksize;
unsigned long chunkshift; /* chunksize = 2^chunkshift (for bitops) */
unsigned long chunks; /* total number of data chunks for the array */
@@ -226,7 +215,6 @@ struct bitmap {
/* bitmap spinlock */
spinlock_t lock;
- long offset; /* offset from superblock if file is NULL */
struct file *file; /* backing disk file */
struct page *sb_page; /* cached copy of the bitmap file superblock */
struct page **filemap; /* list of cache pages for the file */
@@ -238,7 +226,6 @@ struct bitmap {
int allclean;
- unsigned long max_write_behind; /* write-behind mode */
atomic_t behind_writes;
/*
@@ -246,7 +233,6 @@ struct bitmap {
* file, cleaning up bits and flushing out pages to disk as necessary
*/
unsigned long daemon_lastrun; /* jiffies of last run */
- unsigned long daemon_sleep; /* how many seconds between updates? */
unsigned long last_end_sync; /* when we lasted called end_sync to
* update bitmap with resync progress */
@@ -254,6 +240,7 @@ struct bitmap {
wait_queue_head_t write_wait;
wait_queue_head_t overflow_wait;
+ struct sysfs_dirent *sysfs_can_clear;
};
/* the bitmap API */
@@ -282,7 +269,7 @@ void bitmap_close_sync(struct bitmap *bitmap);
void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector);
void bitmap_unplug(struct bitmap *bitmap);
-void bitmap_daemon_work(struct bitmap *bitmap);
+void bitmap_daemon_work(mddev_t *mddev);
#endif
#endif
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index e412980763bd..a93637223c8d 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2003 Christophe Saout <christophe@saout.de>
* Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -71,10 +71,21 @@ struct crypt_iv_operations {
int (*ctr)(struct crypt_config *cc, struct dm_target *ti,
const char *opts);
void (*dtr)(struct crypt_config *cc);
- const char *(*status)(struct crypt_config *cc);
+ int (*init)(struct crypt_config *cc);
+ int (*wipe)(struct crypt_config *cc);
int (*generator)(struct crypt_config *cc, u8 *iv, sector_t sector);
};
+struct iv_essiv_private {
+ struct crypto_cipher *tfm;
+ struct crypto_hash *hash_tfm;
+ u8 *salt;
+};
+
+struct iv_benbi_private {
+ int shift;
+};
+
/*
* Crypt: maps a linear range of a block device
* and encrypts / decrypts at the same time.
@@ -102,8 +113,8 @@ struct crypt_config {
struct crypt_iv_operations *iv_gen_ops;
char *iv_mode;
union {
- struct crypto_cipher *essiv_tfm;
- int benbi_shift;
+ struct iv_essiv_private essiv;
+ struct iv_benbi_private benbi;
} iv_gen_private;
sector_t iv_offset;
unsigned int iv_size;
@@ -147,6 +158,9 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io);
* plain: the initial vector is the 32-bit little-endian version of the sector
* number, padded with zeros if necessary.
*
+ * plain64: the initial vector is the 64-bit little-endian version of the sector
+ * number, padded with zeros if necessary.
+ *
* essiv: "encrypted sector|salt initial vector", the sector number is
* encrypted with the bulk cipher using a salt as key. The salt
* should be derived from the bulk cipher's key via hashing.
@@ -169,88 +183,123 @@ static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
return 0;
}
-static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
- const char *opts)
+static int crypt_iv_plain64_gen(struct crypt_config *cc, u8 *iv,
+ sector_t sector)
{
- struct crypto_cipher *essiv_tfm;
- struct crypto_hash *hash_tfm;
+ memset(iv, 0, cc->iv_size);
+ *(u64 *)iv = cpu_to_le64(sector);
+
+ return 0;
+}
+
+/* Initialise ESSIV - compute salt but no local memory allocations */
+static int crypt_iv_essiv_init(struct crypt_config *cc)
+{
+ struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
struct hash_desc desc;
struct scatterlist sg;
- unsigned int saltsize;
- u8 *salt;
int err;
- if (opts == NULL) {
+ sg_init_one(&sg, cc->key, cc->key_size);
+ desc.tfm = essiv->hash_tfm;
+ desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ err = crypto_hash_digest(&desc, &sg, cc->key_size, essiv->salt);
+ if (err)
+ return err;
+
+ return crypto_cipher_setkey(essiv->tfm, essiv->salt,
+ crypto_hash_digestsize(essiv->hash_tfm));
+}
+
+/* Wipe salt and reset key derived from volume key */
+static int crypt_iv_essiv_wipe(struct crypt_config *cc)
+{
+ struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
+ unsigned salt_size = crypto_hash_digestsize(essiv->hash_tfm);
+
+ memset(essiv->salt, 0, salt_size);
+
+ return crypto_cipher_setkey(essiv->tfm, essiv->salt, salt_size);
+}
+
+static void crypt_iv_essiv_dtr(struct crypt_config *cc)
+{
+ struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
+
+ crypto_free_cipher(essiv->tfm);
+ essiv->tfm = NULL;
+
+ crypto_free_hash(essiv->hash_tfm);
+ essiv->hash_tfm = NULL;
+
+ kzfree(essiv->salt);
+ essiv->salt = NULL;
+}
+
+static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
+ const char *opts)
+{
+ struct crypto_cipher *essiv_tfm = NULL;
+ struct crypto_hash *hash_tfm = NULL;
+ u8 *salt = NULL;
+ int err;
+
+ if (!opts) {
ti->error = "Digest algorithm missing for ESSIV mode";
return -EINVAL;
}
- /* Hash the cipher key with the given hash algorithm */
+ /* Allocate hash algorithm */
hash_tfm = crypto_alloc_hash(opts, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hash_tfm)) {
ti->error = "Error initializing ESSIV hash";
- return PTR_ERR(hash_tfm);
+ err = PTR_ERR(hash_tfm);
+ goto bad;
}
- saltsize = crypto_hash_digestsize(hash_tfm);
- salt = kmalloc(saltsize, GFP_KERNEL);
- if (salt == NULL) {
+ salt = kzalloc(crypto_hash_digestsize(hash_tfm), GFP_KERNEL);
+ if (!salt) {
ti->error = "Error kmallocing salt storage in ESSIV";
- crypto_free_hash(hash_tfm);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto bad;
}
- sg_init_one(&sg, cc->key, cc->key_size);
- desc.tfm = hash_tfm;
- desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
- err = crypto_hash_digest(&desc, &sg, cc->key_size, salt);
- crypto_free_hash(hash_tfm);
-
- if (err) {
- ti->error = "Error calculating hash in ESSIV";
- kfree(salt);
- return err;
- }
-
- /* Setup the essiv_tfm with the given salt */
+ /* Allocate essiv_tfm */
essiv_tfm = crypto_alloc_cipher(cc->cipher, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(essiv_tfm)) {
ti->error = "Error allocating crypto tfm for ESSIV";
- kfree(salt);
- return PTR_ERR(essiv_tfm);
+ err = PTR_ERR(essiv_tfm);
+ goto bad;
}
if (crypto_cipher_blocksize(essiv_tfm) !=
crypto_ablkcipher_ivsize(cc->tfm)) {
ti->error = "Block size of ESSIV cipher does "
"not match IV size of block cipher";
- crypto_free_cipher(essiv_tfm);
- kfree(salt);
- return -EINVAL;
+ err = -EINVAL;
+ goto bad;
}
- err = crypto_cipher_setkey(essiv_tfm, salt, saltsize);
- if (err) {
- ti->error = "Failed to set key for ESSIV cipher";
- crypto_free_cipher(essiv_tfm);
- kfree(salt);
- return err;
- }
- kfree(salt);
- cc->iv_gen_private.essiv_tfm = essiv_tfm;
+ cc->iv_gen_private.essiv.salt = salt;
+ cc->iv_gen_private.essiv.tfm = essiv_tfm;
+ cc->iv_gen_private.essiv.hash_tfm = hash_tfm;
+
return 0;
-}
-static void crypt_iv_essiv_dtr(struct crypt_config *cc)
-{
- crypto_free_cipher(cc->iv_gen_private.essiv_tfm);
- cc->iv_gen_private.essiv_tfm = NULL;
+bad:
+ if (essiv_tfm && !IS_ERR(essiv_tfm))
+ crypto_free_cipher(essiv_tfm);
+ if (hash_tfm && !IS_ERR(hash_tfm))
+ crypto_free_hash(hash_tfm);
+ kfree(salt);
+ return err;
}
static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
{
memset(iv, 0, cc->iv_size);
*(u64 *)iv = cpu_to_le64(sector);
- crypto_cipher_encrypt_one(cc->iv_gen_private.essiv_tfm, iv, iv);
+ crypto_cipher_encrypt_one(cc->iv_gen_private.essiv.tfm, iv, iv);
return 0;
}
@@ -273,7 +322,7 @@ static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti,
return -EINVAL;
}
- cc->iv_gen_private.benbi_shift = 9 - log;
+ cc->iv_gen_private.benbi.shift = 9 - log;
return 0;
}
@@ -288,7 +337,7 @@ static int crypt_iv_benbi_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
memset(iv, 0, cc->iv_size - sizeof(u64)); /* rest is cleared below */
- val = cpu_to_be64(((u64)sector << cc->iv_gen_private.benbi_shift) + 1);
+ val = cpu_to_be64(((u64)sector << cc->iv_gen_private.benbi.shift) + 1);
put_unaligned(val, (__be64 *)(iv + cc->iv_size - sizeof(u64)));
return 0;
@@ -305,9 +354,15 @@ static struct crypt_iv_operations crypt_iv_plain_ops = {
.generator = crypt_iv_plain_gen
};
+static struct crypt_iv_operations crypt_iv_plain64_ops = {
+ .generator = crypt_iv_plain64_gen
+};
+
static struct crypt_iv_operations crypt_iv_essiv_ops = {
.ctr = crypt_iv_essiv_ctr,
.dtr = crypt_iv_essiv_dtr,
+ .init = crypt_iv_essiv_init,
+ .wipe = crypt_iv_essiv_wipe,
.generator = crypt_iv_essiv_gen
};
@@ -934,14 +989,14 @@ static int crypt_set_key(struct crypt_config *cc, char *key)
set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
- return 0;
+ return crypto_ablkcipher_setkey(cc->tfm, cc->key, cc->key_size);
}
static int crypt_wipe_key(struct crypt_config *cc)
{
clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
memset(&cc->key, 0, cc->key_size * sizeof(u8));
- return 0;
+ return crypto_ablkcipher_setkey(cc->tfm, cc->key, cc->key_size);
}
/*
@@ -983,11 +1038,6 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return -ENOMEM;
}
- if (crypt_set_key(cc, argv[1])) {
- ti->error = "Error decoding key";
- goto bad_cipher;
- }
-
/* Compatibility mode for old dm-crypt cipher strings */
if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) {
chainmode = "cbc";
@@ -1015,6 +1065,11 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
strcpy(cc->chainmode, chainmode);
cc->tfm = tfm;
+ if (crypt_set_key(cc, argv[1]) < 0) {
+ ti->error = "Error decoding and setting key";
+ goto bad_ivmode;
+ }
+
/*
* Choose ivmode. Valid modes: "plain", "essiv:<esshash>", "benbi".
* See comments at iv code
@@ -1024,6 +1079,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
cc->iv_gen_ops = NULL;
else if (strcmp(ivmode, "plain") == 0)
cc->iv_gen_ops = &crypt_iv_plain_ops;
+ else if (strcmp(ivmode, "plain64") == 0)
+ cc->iv_gen_ops = &crypt_iv_plain64_ops;
else if (strcmp(ivmode, "essiv") == 0)
cc->iv_gen_ops = &crypt_iv_essiv_ops;
else if (strcmp(ivmode, "benbi") == 0)
@@ -1039,6 +1096,12 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
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;
+ }
+
cc->iv_size = crypto_ablkcipher_ivsize(tfm);
if (cc->iv_size)
/* at least a 64 bit sector number should fit in our buffer */
@@ -1085,11 +1148,6 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad_bs;
}
- if (crypto_ablkcipher_setkey(tfm, cc->key, key_size) < 0) {
- ti->error = "Error setting key";
- goto bad_device;
- }
-
if (sscanf(argv[2], "%llu", &tmpll) != 1) {
ti->error = "Invalid iv_offset sector";
goto bad_device;
@@ -1278,6 +1336,7 @@ static void crypt_resume(struct dm_target *ti)
static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
{
struct crypt_config *cc = ti->private;
+ int ret = -EINVAL;
if (argc < 2)
goto error;
@@ -1287,10 +1346,22 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
DMWARN("not suspended during key manipulation.");
return -EINVAL;
}
- if (argc == 3 && !strnicmp(argv[1], MESG_STR("set")))
- return crypt_set_key(cc, argv[2]);
- if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe")))
+ if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) {
+ ret = crypt_set_key(cc, argv[2]);
+ if (ret)
+ return ret;
+ if (cc->iv_gen_ops && cc->iv_gen_ops->init)
+ ret = cc->iv_gen_ops->init(cc);
+ return ret;
+ }
+ if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) {
+ if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) {
+ ret = cc->iv_gen_ops->wipe(cc);
+ if (ret)
+ return ret;
+ }
return crypt_wipe_key(cc);
+ }
}
error:
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index 7dbe652efb5a..2b7907b6dd09 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -172,7 +172,8 @@ 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(store->cow->bdev) >> 9)) {
+ if (chunk_size %
+ (bdev_logical_block_size(dm_snap_cow(store->snap)->bdev) >> 9)) {
*error = "Chunk size is not a multiple of device blocksize";
return -EINVAL;
}
@@ -190,6 +191,7 @@ int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
}
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
+ struct dm_snapshot *snap,
unsigned *args_used,
struct dm_exception_store **store)
{
@@ -198,7 +200,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
struct dm_exception_store *tmp_store;
char persistent;
- if (argc < 3) {
+ if (argc < 2) {
ti->error = "Insufficient exception store arguments";
return -EINVAL;
}
@@ -209,14 +211,15 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
return -ENOMEM;
}
- persistent = toupper(*argv[1]);
+ persistent = toupper(*argv[0]);
if (persistent == 'P')
type = get_type("P");
else if (persistent == 'N')
type = get_type("N");
else {
ti->error = "Persistent flag is not P or N";
- return -EINVAL;
+ r = -EINVAL;
+ goto bad_type;
}
if (!type) {
@@ -226,32 +229,23 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
}
tmp_store->type = type;
- tmp_store->ti = ti;
-
- r = dm_get_device(ti, argv[0], 0, 0,
- FMODE_READ | FMODE_WRITE, &tmp_store->cow);
- if (r) {
- ti->error = "Cannot get COW device";
- goto bad_cow;
- }
+ tmp_store->snap = snap;
- r = set_chunk_size(tmp_store, argv[2], &ti->error);
+ r = set_chunk_size(tmp_store, argv[1], &ti->error);
if (r)
- goto bad_ctr;
+ goto bad;
r = type->ctr(tmp_store, 0, NULL);
if (r) {
ti->error = "Exception store type constructor failed";
- goto bad_ctr;
+ goto bad;
}
- *args_used = 3;
+ *args_used = 2;
*store = tmp_store;
return 0;
-bad_ctr:
- dm_put_device(ti, tmp_store->cow);
-bad_cow:
+bad:
put_type(type);
bad_type:
kfree(tmp_store);
@@ -262,7 +256,6 @@ EXPORT_SYMBOL(dm_exception_store_create);
void dm_exception_store_destroy(struct dm_exception_store *store)
{
store->type->dtr(store);
- dm_put_device(store->ti, store->cow);
put_type(store->type);
kfree(store);
}
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index 8a223a48802c..e8dfa06af3ba 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -26,7 +26,7 @@ typedef sector_t chunk_t;
* of chunks that follow contiguously. Remaining bits hold the number of the
* chunk within the device.
*/
-struct dm_snap_exception {
+struct dm_exception {
struct list_head hash_list;
chunk_t old_chunk;
@@ -64,17 +64,34 @@ struct dm_exception_store_type {
* Find somewhere to store the next exception.
*/
int (*prepare_exception) (struct dm_exception_store *store,
- struct dm_snap_exception *e);
+ struct dm_exception *e);
/*
* Update the metadata with this exception.
*/
void (*commit_exception) (struct dm_exception_store *store,
- struct dm_snap_exception *e,
+ struct dm_exception *e,
void (*callback) (void *, int success),
void *callback_context);
/*
+ * Returns 0 if the exception store is empty.
+ *
+ * If there are exceptions still to be merged, sets
+ * *last_old_chunk and *last_new_chunk to the most recent
+ * still-to-be-merged chunk and returns the number of
+ * consecutive previous ones.
+ */
+ int (*prepare_merge) (struct dm_exception_store *store,
+ chunk_t *last_old_chunk, chunk_t *last_new_chunk);
+
+ /*
+ * Clear the last n exceptions.
+ * nr_merged must be <= the value returned by prepare_merge.
+ */
+ int (*commit_merge) (struct dm_exception_store *store, int nr_merged);
+
+ /*
* The snapshot is invalid, note this in the metadata.
*/
void (*drop_snapshot) (struct dm_exception_store *store);
@@ -86,19 +103,19 @@ struct dm_exception_store_type {
/*
* Return how full the snapshot is.
*/
- void (*fraction_full) (struct dm_exception_store *store,
- sector_t *numerator,
- sector_t *denominator);
+ void (*usage) (struct dm_exception_store *store,
+ sector_t *total_sectors, sector_t *sectors_allocated,
+ sector_t *metadata_sectors);
/* For internal device-mapper use only. */
struct list_head list;
};
+struct dm_snapshot;
+
struct dm_exception_store {
struct dm_exception_store_type *type;
- struct dm_target *ti;
-
- struct dm_dev *cow;
+ struct dm_snapshot *snap;
/* Size of data blocks saved - must be a power of 2 */
unsigned chunk_size;
@@ -109,6 +126,11 @@ struct dm_exception_store {
};
/*
+ * Obtain the cow device used by a given snapshot.
+ */
+struct dm_dev *dm_snap_cow(struct dm_snapshot *snap);
+
+/*
* Funtions to manipulate consecutive chunks
*/
# if defined(CONFIG_LBDAF) || (BITS_PER_LONG == 64)
@@ -120,18 +142,25 @@ static inline chunk_t dm_chunk_number(chunk_t chunk)
return chunk & (chunk_t)((1ULL << DM_CHUNK_NUMBER_BITS) - 1ULL);
}
-static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e)
+static inline unsigned dm_consecutive_chunk_count(struct dm_exception *e)
{
return e->new_chunk >> DM_CHUNK_NUMBER_BITS;
}
-static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
+static inline void dm_consecutive_chunk_count_inc(struct dm_exception *e)
{
e->new_chunk += (1ULL << DM_CHUNK_NUMBER_BITS);
BUG_ON(!dm_consecutive_chunk_count(e));
}
+static inline void dm_consecutive_chunk_count_dec(struct dm_exception *e)
+{
+ BUG_ON(!dm_consecutive_chunk_count(e));
+
+ e->new_chunk -= (1ULL << DM_CHUNK_NUMBER_BITS);
+}
+
# else
# define DM_CHUNK_CONSECUTIVE_BITS 0
@@ -140,12 +169,16 @@ static inline chunk_t dm_chunk_number(chunk_t chunk)
return chunk;
}
-static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e)
+static inline unsigned dm_consecutive_chunk_count(struct dm_exception *e)
{
return 0;
}
-static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
+static inline void dm_consecutive_chunk_count_inc(struct dm_exception *e)
+{
+}
+
+static inline void dm_consecutive_chunk_count_dec(struct dm_exception *e)
{
}
@@ -162,7 +195,7 @@ static inline sector_t get_dev_size(struct block_device *bdev)
static inline chunk_t sector_to_chunk(struct dm_exception_store *store,
sector_t sector)
{
- return (sector & ~store->chunk_mask) >> store->chunk_shift;
+ return sector >> store->chunk_shift;
}
int dm_exception_store_type_register(struct dm_exception_store_type *type);
@@ -173,6 +206,7 @@ int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
char **error);
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
+ struct dm_snapshot *snap,
unsigned *args_used,
struct dm_exception_store **store);
void dm_exception_store_destroy(struct dm_exception_store *store);
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 3a2e6a2f8bdd..10f457ca6af2 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -5,6 +5,8 @@
* This file is released under the GPL.
*/
+#include "dm.h"
+
#include <linux/device-mapper.h>
#include <linux/bio.h>
@@ -14,12 +16,19 @@
#include <linux/slab.h>
#include <linux/dm-io.h>
+#define DM_MSG_PREFIX "io"
+
+#define DM_IO_MAX_REGIONS BITS_PER_LONG
+
struct dm_io_client {
mempool_t *pool;
struct bio_set *bios;
};
-/* FIXME: can we shrink this ? */
+/*
+ * Aligning 'struct io' reduces the number of bits required to store
+ * its address. Refer to store_io_and_region_in_bio() below.
+ */
struct io {
unsigned long error_bits;
unsigned long eopnotsupp_bits;
@@ -28,7 +37,9 @@ struct io {
struct dm_io_client *client;
io_notify_fn callback;
void *context;
-};
+} __attribute__((aligned(DM_IO_MAX_REGIONS)));
+
+static struct kmem_cache *_dm_io_cache;
/*
* io contexts are only dynamically allocated for asynchronous
@@ -53,7 +64,7 @@ struct dm_io_client *dm_io_client_create(unsigned num_pages)
if (!client)
return ERR_PTR(-ENOMEM);
- client->pool = mempool_create_kmalloc_pool(ios, sizeof(struct io));
+ client->pool = mempool_create_slab_pool(ios, _dm_io_cache);
if (!client->pool)
goto bad;
@@ -88,18 +99,29 @@ EXPORT_SYMBOL(dm_io_client_destroy);
/*-----------------------------------------------------------------
* We need to keep track of which region a bio is doing io for.
- * In order to save a memory allocation we store this the last
- * bvec which we know is unused (blech).
- * XXX This is ugly and can OOPS with some configs... find another way.
+ * To avoid a memory allocation to store just 5 or 6 bits, we
+ * ensure the 'struct io' pointer is aligned so enough low bits are
+ * always zero and then combine it with the region number directly in
+ * bi_private.
*---------------------------------------------------------------*/
-static inline void bio_set_region(struct bio *bio, unsigned region)
+static void store_io_and_region_in_bio(struct bio *bio, struct io *io,
+ unsigned region)
{
- bio->bi_io_vec[bio->bi_max_vecs].bv_len = region;
+ if (unlikely(!IS_ALIGNED((unsigned long)io, DM_IO_MAX_REGIONS))) {
+ DMCRIT("Unaligned struct io pointer %p", io);
+ BUG();
+ }
+
+ bio->bi_private = (void *)((unsigned long)io | region);
}
-static inline unsigned bio_get_region(struct bio *bio)
+static void retrieve_io_and_region_from_bio(struct bio *bio, struct io **io,
+ unsigned *region)
{
- return bio->bi_io_vec[bio->bi_max_vecs].bv_len;
+ unsigned long val = (unsigned long)bio->bi_private;
+
+ *io = (void *)(val & -(unsigned long)DM_IO_MAX_REGIONS);
+ *region = val & (DM_IO_MAX_REGIONS - 1);
}
/*-----------------------------------------------------------------
@@ -140,10 +162,8 @@ static void endio(struct bio *bio, int error)
/*
* The bio destructor in bio_put() may use the io object.
*/
- io = bio->bi_private;
- region = bio_get_region(bio);
+ retrieve_io_and_region_from_bio(bio, &io, &region);
- bio->bi_max_vecs++;
bio_put(bio);
dec_count(io, region, error);
@@ -243,7 +263,10 @@ static void vm_dp_init(struct dpages *dp, void *data)
static void dm_bio_destructor(struct bio *bio)
{
- struct io *io = bio->bi_private;
+ unsigned region;
+ struct io *io;
+
+ retrieve_io_and_region_from_bio(bio, &io, &region);
bio_free(bio, io->client->bios);
}
@@ -286,26 +309,23 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where,
unsigned num_bvecs;
sector_t remaining = where->count;
- while (remaining) {
+ /*
+ * where->count may be zero if rw holds a write barrier and we
+ * need to send a zero-sized barrier.
+ */
+ do {
/*
- * Allocate a suitably sized-bio: we add an extra
- * bvec for bio_get/set_region() and decrement bi_max_vecs
- * to hide it from bio_add_page().
+ * Allocate a suitably sized-bio.
*/
num_bvecs = dm_sector_div_up(remaining,
(PAGE_SIZE >> SECTOR_SHIFT));
- num_bvecs = 1 + min_t(int, bio_get_nr_vecs(where->bdev),
- num_bvecs);
- if (unlikely(num_bvecs > BIO_MAX_PAGES))
- num_bvecs = BIO_MAX_PAGES;
+ num_bvecs = min_t(int, bio_get_nr_vecs(where->bdev), num_bvecs);
bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, io->client->bios);
bio->bi_sector = where->sector + (where->count - remaining);
bio->bi_bdev = where->bdev;
bio->bi_end_io = endio;
- bio->bi_private = io;
bio->bi_destructor = dm_bio_destructor;
- bio->bi_max_vecs--;
- bio_set_region(bio, region);
+ store_io_and_region_in_bio(bio, io, region);
/*
* Try and add as many pages as possible.
@@ -323,7 +343,7 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where,
atomic_inc(&io->count);
submit_bio(rw, bio);
- }
+ } while (remaining);
}
static void dispatch_io(int rw, unsigned int num_regions,
@@ -333,6 +353,8 @@ static void dispatch_io(int rw, unsigned int num_regions,
int i;
struct dpages old_pages = *dp;
+ BUG_ON(num_regions > DM_IO_MAX_REGIONS);
+
if (sync)
rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
@@ -342,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)
+ if (where[i].count || (rw & (1 << BIO_RW_BARRIER)))
do_region(rw, i, where + i, dp, io);
}
@@ -357,7 +379,14 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
struct dm_io_region *where, int rw, struct dpages *dp,
unsigned long *error_bits)
{
- struct io io;
+ /*
+ * gcc <= 4.3 can't do the alignment for stack variables, so we must
+ * align it on our own.
+ * volatile prevents the optimizer from removing or reusing
+ * "io_" field from the stack frame (allowed in ANSI C).
+ */
+ volatile char io_[sizeof(struct io) + __alignof__(struct io) - 1];
+ struct io *io = (struct io *)PTR_ALIGN(&io_, __alignof__(struct io));
if (num_regions > 1 && (rw & RW_MASK) != WRITE) {
WARN_ON(1);
@@ -365,33 +394,33 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
}
retry:
- io.error_bits = 0;
- io.eopnotsupp_bits = 0;
- atomic_set(&io.count, 1); /* see dispatch_io() */
- io.sleeper = current;
- io.client = client;
+ io->error_bits = 0;
+ io->eopnotsupp_bits = 0;
+ atomic_set(&io->count, 1); /* see dispatch_io() */
+ io->sleeper = current;
+ io->client = client;
- dispatch_io(rw, num_regions, where, dp, &io, 1);
+ dispatch_io(rw, num_regions, where, dp, io, 1);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
- if (!atomic_read(&io.count))
+ if (!atomic_read(&io->count))
break;
io_schedule();
}
set_current_state(TASK_RUNNING);
- if (io.eopnotsupp_bits && (rw & (1 << BIO_RW_BARRIER))) {
+ if (io->eopnotsupp_bits && (rw & (1 << BIO_RW_BARRIER))) {
rw &= ~(1 << BIO_RW_BARRIER);
goto retry;
}
if (error_bits)
- *error_bits = io.error_bits;
+ *error_bits = io->error_bits;
- return io.error_bits ? -EIO : 0;
+ return io->error_bits ? -EIO : 0;
}
static int async_io(struct dm_io_client *client, unsigned int num_regions,
@@ -472,3 +501,18 @@ int dm_io(struct dm_io_request *io_req, unsigned num_regions,
&dp, io_req->notify.fn, io_req->notify.context);
}
EXPORT_SYMBOL(dm_io);
+
+int __init dm_io_init(void)
+{
+ _dm_io_cache = KMEM_CACHE(io, 0);
+ if (!_dm_io_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void dm_io_exit(void)
+{
+ kmem_cache_destroy(_dm_io_cache);
+ _dm_io_cache = NULL;
+}
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a67942931582..1d669322b27c 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -56,6 +56,11 @@ static void dm_hash_remove_all(int keep_open_devices);
*/
static DECLARE_RWSEM(_hash_lock);
+/*
+ * Protects use of mdptr to obtain hash cell name and uuid from mapped device.
+ */
+static DEFINE_MUTEX(dm_hash_cells_mutex);
+
static void init_buckets(struct list_head *buckets)
{
unsigned int i;
@@ -206,7 +211,9 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi
list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
}
dm_get(md);
+ mutex_lock(&dm_hash_cells_mutex);
dm_set_mdptr(md, cell);
+ mutex_unlock(&dm_hash_cells_mutex);
up_write(&_hash_lock);
return 0;
@@ -224,9 +231,11 @@ static void __hash_remove(struct hash_cell *hc)
/* remove from the dev hash */
list_del(&hc->uuid_list);
list_del(&hc->name_list);
+ mutex_lock(&dm_hash_cells_mutex);
dm_set_mdptr(hc->md, NULL);
+ mutex_unlock(&dm_hash_cells_mutex);
- table = dm_get_table(hc->md);
+ table = dm_get_live_table(hc->md);
if (table) {
dm_table_event(table);
dm_table_put(table);
@@ -321,13 +330,15 @@ static int dm_hash_rename(uint32_t cookie, const char *old, const char *new)
*/
list_del(&hc->name_list);
old_name = hc->name;
+ mutex_lock(&dm_hash_cells_mutex);
hc->name = new_name;
+ mutex_unlock(&dm_hash_cells_mutex);
list_add(&hc->name_list, _name_buckets + hash_str(new_name));
/*
* Wake up any dm event waiters.
*/
- table = dm_get_table(hc->md);
+ table = dm_get_live_table(hc->md);
if (table) {
dm_table_event(table);
dm_table_put(table);
@@ -512,8 +523,6 @@ static int list_versions(struct dm_ioctl *param, size_t param_size)
return 0;
}
-
-
static int check_name(const char *name)
{
if (strchr(name, '/')) {
@@ -525,6 +534,40 @@ static int check_name(const char *name)
}
/*
+ * On successful return, the caller must not attempt to acquire
+ * _hash_lock without first calling dm_table_put, because dm_table_destroy
+ * waits for this dm_table_put and could be called under this lock.
+ */
+static struct dm_table *dm_get_inactive_table(struct mapped_device *md)
+{
+ struct hash_cell *hc;
+ struct dm_table *table = NULL;
+
+ down_read(&_hash_lock);
+ hc = dm_get_mdptr(md);
+ if (!hc || hc->md != md) {
+ DMWARN("device has been removed from the dev hash table.");
+ goto out;
+ }
+
+ table = hc->new_map;
+ if (table)
+ dm_table_get(table);
+
+out:
+ up_read(&_hash_lock);
+
+ return table;
+}
+
+static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
+ struct dm_ioctl *param)
+{
+ return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
+ dm_get_inactive_table(md) : dm_get_live_table(md);
+}
+
+/*
* Fills in a dm_ioctl structure, ready for sending back to
* userland.
*/
@@ -536,7 +579,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
DM_ACTIVE_PRESENT_FLAG);
- if (dm_suspended(md))
+ if (dm_suspended_md(md))
param->flags |= DM_SUSPEND_FLAG;
param->dev = huge_encode_dev(disk_devt(disk));
@@ -548,18 +591,30 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
*/
param->open_count = dm_open_count(md);
- if (get_disk_ro(disk))
- param->flags |= DM_READONLY_FLAG;
-
param->event_nr = dm_get_event_nr(md);
+ param->target_count = 0;
- table = dm_get_table(md);
+ table = dm_get_live_table(md);
if (table) {
- param->flags |= DM_ACTIVE_PRESENT_FLAG;
- param->target_count = dm_table_get_num_targets(table);
+ if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
+ if (get_disk_ro(disk))
+ param->flags |= DM_READONLY_FLAG;
+ param->target_count = dm_table_get_num_targets(table);
+ }
dm_table_put(table);
- } else
- param->target_count = 0;
+
+ param->flags |= DM_ACTIVE_PRESENT_FLAG;
+ }
+
+ if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
+ table = dm_get_inactive_table(md);
+ if (table) {
+ if (!(dm_table_get_mode(table) & FMODE_WRITE))
+ param->flags |= DM_READONLY_FLAG;
+ param->target_count = dm_table_get_num_targets(table);
+ dm_table_put(table);
+ }
+ }
return 0;
}
@@ -634,9 +689,9 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
* Sneakily write in both the name and the uuid
* while we have the cell.
*/
- strncpy(param->name, hc->name, sizeof(param->name));
+ strlcpy(param->name, hc->name, sizeof(param->name));
if (hc->uuid)
- strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1);
+ strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
else
param->uuid[0] = '\0';
@@ -784,7 +839,7 @@ static int do_suspend(struct dm_ioctl *param)
if (param->flags & DM_NOFLUSH_FLAG)
suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
- if (!dm_suspended(md))
+ if (!dm_suspended_md(md))
r = dm_suspend(md, suspend_flags);
if (!r)
@@ -800,7 +855,7 @@ static int do_resume(struct dm_ioctl *param)
unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
struct hash_cell *hc;
struct mapped_device *md;
- struct dm_table *new_map;
+ struct dm_table *new_map, *old_map = NULL;
down_write(&_hash_lock);
@@ -826,14 +881,14 @@ static int do_resume(struct dm_ioctl *param)
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
if (param->flags & DM_NOFLUSH_FLAG)
suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
- if (!dm_suspended(md))
+ if (!dm_suspended_md(md))
dm_suspend(md, suspend_flags);
- r = dm_swap_table(md, new_map);
- if (r) {
+ old_map = dm_swap_table(md, new_map);
+ if (IS_ERR(old_map)) {
dm_table_destroy(new_map);
dm_put(md);
- return r;
+ return PTR_ERR(old_map);
}
if (dm_table_get_mode(new_map) & FMODE_WRITE)
@@ -842,9 +897,11 @@ static int do_resume(struct dm_ioctl *param)
set_disk_ro(dm_disk(md), 1);
}
- if (dm_suspended(md))
+ if (dm_suspended_md(md))
r = dm_resume(md);
+ if (old_map)
+ dm_table_destroy(old_map);
if (!r) {
dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr);
@@ -982,7 +1039,7 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size)
if (r)
goto out;
- table = dm_get_table(md);
+ table = dm_get_live_or_inactive_table(md, param);
if (table) {
retrieve_status(table, param, param_size);
dm_table_put(table);
@@ -1215,7 +1272,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size)
if (r)
goto out;
- table = dm_get_table(md);
+ table = dm_get_live_or_inactive_table(md, param);
if (table) {
retrieve_deps(table, param, param_size);
dm_table_put(table);
@@ -1244,13 +1301,13 @@ static int table_status(struct dm_ioctl *param, size_t param_size)
if (r)
goto out;
- table = dm_get_table(md);
+ table = dm_get_live_or_inactive_table(md, param);
if (table) {
retrieve_status(table, param, param_size);
dm_table_put(table);
}
- out:
+out:
dm_put(md);
return r;
}
@@ -1288,10 +1345,15 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
goto out;
}
- table = dm_get_table(md);
+ table = dm_get_live_table(md);
if (!table)
goto out_argv;
+ if (dm_deleting_md(md)) {
+ r = -ENXIO;
+ goto out_table;
+ }
+
ti = dm_table_find_target(table, tmsg->sector);
if (!dm_target_is_valid(ti)) {
DMWARN("Target message sector outside device.");
@@ -1303,6 +1365,7 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
r = -EINVAL;
}
+ out_table:
dm_table_put(table);
out_argv:
kfree(argv);
@@ -1582,8 +1645,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
if (!md)
return -ENXIO;
- dm_get(md);
- down_read(&_hash_lock);
+ mutex_lock(&dm_hash_cells_mutex);
hc = dm_get_mdptr(md);
if (!hc || hc->md != md) {
r = -ENXIO;
@@ -1596,8 +1658,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
strcpy(uuid, hc->uuid ? : "");
out:
- up_read(&_hash_lock);
- dm_put(md);
+ mutex_unlock(&dm_hash_cells_mutex);
return r;
}
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 3e3fc06cb861..addf83475040 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -450,7 +450,10 @@ static void dispatch_job(struct kcopyd_job *job)
{
struct dm_kcopyd_client *kc = job->kc;
atomic_inc(&kc->nr_jobs);
- push(&kc->pages_jobs, job);
+ if (unlikely(!job->source.count))
+ push(&kc->complete_jobs, job);
+ else
+ push(&kc->pages_jobs, job);
wake(kc);
}
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index 9443896ede07..7035582786fb 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -145,8 +145,9 @@ int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type)
EXPORT_SYMBOL(dm_dirty_log_type_unregister);
struct dm_dirty_log *dm_dirty_log_create(const char *type_name,
- struct dm_target *ti,
- unsigned int argc, char **argv)
+ struct dm_target *ti,
+ int (*flush_callback_fn)(struct dm_target *ti),
+ unsigned int argc, char **argv)
{
struct dm_dirty_log_type *type;
struct dm_dirty_log *log;
@@ -161,6 +162,7 @@ struct dm_dirty_log *dm_dirty_log_create(const char *type_name,
return NULL;
}
+ log->flush_callback_fn = flush_callback_fn;
log->type = type;
if (type->ctr(log, ti, argc, argv)) {
kfree(log);
@@ -208,7 +210,9 @@ struct log_header {
struct log_c {
struct dm_target *ti;
- int touched;
+ int touched_dirtied;
+ int touched_cleaned;
+ int flush_failed;
uint32_t region_size;
unsigned int region_count;
region_t sync_count;
@@ -233,6 +237,7 @@ struct log_c {
* Disk log fields
*/
int log_dev_failed;
+ int log_dev_flush_failed;
struct dm_dev *log_dev;
struct log_header header;
@@ -253,14 +258,14 @@ static inline void log_set_bit(struct log_c *l,
uint32_t *bs, unsigned bit)
{
ext2_set_bit(bit, (unsigned long *) bs);
- l->touched = 1;
+ l->touched_cleaned = 1;
}
static inline void log_clear_bit(struct log_c *l,
uint32_t *bs, unsigned bit)
{
ext2_clear_bit(bit, (unsigned long *) bs);
- l->touched = 1;
+ l->touched_dirtied = 1;
}
/*----------------------------------------------------------------
@@ -287,6 +292,19 @@ static int rw_header(struct log_c *lc, int rw)
return dm_io(&lc->io_req, 1, &lc->header_location, NULL);
}
+static int flush_header(struct log_c *lc)
+{
+ struct dm_io_region null_location = {
+ .bdev = lc->header_location.bdev,
+ .sector = 0,
+ .count = 0,
+ };
+
+ lc->io_req.bi_rw = WRITE_BARRIER;
+
+ return dm_io(&lc->io_req, 1, &null_location, NULL);
+}
+
static int read_header(struct log_c *log)
{
int r;
@@ -378,7 +396,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
}
lc->ti = ti;
- lc->touched = 0;
+ lc->touched_dirtied = 0;
+ lc->touched_cleaned = 0;
+ lc->flush_failed = 0;
lc->region_size = region_size;
lc->region_count = region_count;
lc->sync = sync;
@@ -406,6 +426,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
} else {
lc->log_dev = dev;
lc->log_dev_failed = 0;
+ lc->log_dev_flush_failed = 0;
lc->header_location.bdev = lc->log_dev->bdev;
lc->header_location.sector = 0;
@@ -614,6 +635,11 @@ static int disk_resume(struct dm_dirty_log *log)
/* write the new header */
r = rw_header(lc, WRITE);
+ if (!r) {
+ r = flush_header(lc);
+ if (r)
+ lc->log_dev_flush_failed = 1;
+ }
if (r) {
DMWARN("%s: Failed to write header on dirty region log device",
lc->log_dev->name);
@@ -656,18 +682,40 @@ static int core_flush(struct dm_dirty_log *log)
static int disk_flush(struct dm_dirty_log *log)
{
- int r;
- struct log_c *lc = (struct log_c *) log->context;
+ int r, i;
+ struct log_c *lc = log->context;
/* only write if the log has changed */
- if (!lc->touched)
+ if (!lc->touched_cleaned && !lc->touched_dirtied)
return 0;
+ if (lc->touched_cleaned && log->flush_callback_fn &&
+ log->flush_callback_fn(lc->ti)) {
+ /*
+ * At this point it is impossible to determine which
+ * regions are clean and which are dirty (without
+ * re-reading the log off disk). So mark all of them
+ * dirty.
+ */
+ lc->flush_failed = 1;
+ for (i = 0; i < lc->region_count; i++)
+ log_clear_bit(lc, lc->clean_bits, i);
+ }
+
r = rw_header(lc, WRITE);
if (r)
fail_log_device(lc);
- else
- lc->touched = 0;
+ else {
+ if (lc->touched_dirtied) {
+ r = flush_header(lc);
+ if (r) {
+ lc->log_dev_flush_failed = 1;
+ fail_log_device(lc);
+ } else
+ lc->touched_dirtied = 0;
+ }
+ lc->touched_cleaned = 0;
+ }
return r;
}
@@ -681,7 +729,8 @@ static void core_mark_region(struct dm_dirty_log *log, region_t region)
static void core_clear_region(struct dm_dirty_log *log, region_t region)
{
struct log_c *lc = (struct log_c *) log->context;
- log_set_bit(lc, lc->clean_bits, region);
+ if (likely(!lc->flush_failed))
+ log_set_bit(lc, lc->clean_bits, region);
}
static int core_get_resync_work(struct dm_dirty_log *log, region_t *region)
@@ -762,7 +811,9 @@ static int disk_status(struct dm_dirty_log *log, status_type_t status,
switch(status) {
case STATUSTYPE_INFO:
DMEMIT("3 %s %s %c", log->type->name, lc->log_dev->name,
- lc->log_dev_failed ? 'D' : 'A');
+ lc->log_dev_flush_failed ? 'F' :
+ lc->log_dev_failed ? 'D' :
+ 'A');
break;
case STATUSTYPE_TABLE:
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index dce971dbdfa3..e81345a1d08f 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -93,6 +93,10 @@ struct multipath {
* can resubmit bios on error.
*/
mempool_t *mpio_pool;
+
+ struct mutex work_mutex;
+
+ unsigned suspended; /* Don't create new I/O internally when set. */
};
/*
@@ -198,6 +202,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
m->queue_io = 1;
INIT_WORK(&m->process_queued_ios, process_queued_ios);
INIT_WORK(&m->trigger_event, trigger_event);
+ mutex_init(&m->work_mutex);
m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
if (!m->mpio_pool) {
kfree(m);
@@ -885,13 +890,18 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
return r;
}
-static void multipath_dtr(struct dm_target *ti)
+static void flush_multipath_work(void)
{
- struct multipath *m = (struct multipath *) ti->private;
-
flush_workqueue(kmpath_handlerd);
flush_workqueue(kmultipathd);
flush_scheduled_work();
+}
+
+static void multipath_dtr(struct dm_target *ti)
+{
+ struct multipath *m = ti->private;
+
+ flush_multipath_work();
free_multipath(m);
}
@@ -1261,6 +1271,16 @@ static void multipath_presuspend(struct dm_target *ti)
queue_if_no_path(m, 0, 1);
}
+static void multipath_postsuspend(struct dm_target *ti)
+{
+ struct multipath *m = ti->private;
+
+ mutex_lock(&m->work_mutex);
+ m->suspended = 1;
+ flush_multipath_work();
+ mutex_unlock(&m->work_mutex);
+}
+
/*
* Restore the queue_if_no_path setting.
*/
@@ -1269,6 +1289,10 @@ static void multipath_resume(struct dm_target *ti)
struct multipath *m = (struct multipath *) ti->private;
unsigned long flags;
+ mutex_lock(&m->work_mutex);
+ m->suspended = 0;
+ mutex_unlock(&m->work_mutex);
+
spin_lock_irqsave(&m->lock, flags);
m->queue_if_no_path = m->saved_queue_if_no_path;
spin_unlock_irqrestore(&m->lock, flags);
@@ -1397,51 +1421,71 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
{
- int r;
+ int r = -EINVAL;
struct dm_dev *dev;
struct multipath *m = (struct multipath *) ti->private;
action_fn action;
+ mutex_lock(&m->work_mutex);
+
+ if (m->suspended) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ if (dm_suspended(ti)) {
+ r = -EBUSY;
+ goto out;
+ }
+
if (argc == 1) {
- if (!strnicmp(argv[0], MESG_STR("queue_if_no_path")))
- return queue_if_no_path(m, 1, 0);
- else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path")))
- return queue_if_no_path(m, 0, 0);
+ if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) {
+ r = queue_if_no_path(m, 1, 0);
+ goto out;
+ } else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) {
+ r = queue_if_no_path(m, 0, 0);
+ goto out;
+ }
}
- if (argc != 2)
- goto error;
+ if (argc != 2) {
+ DMWARN("Unrecognised multipath message received.");
+ goto out;
+ }
- if (!strnicmp(argv[0], MESG_STR("disable_group")))
- return bypass_pg_num(m, argv[1], 1);
- else if (!strnicmp(argv[0], MESG_STR("enable_group")))
- return bypass_pg_num(m, argv[1], 0);
- else if (!strnicmp(argv[0], MESG_STR("switch_group")))
- return switch_pg_num(m, argv[1]);
- else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
+ if (!strnicmp(argv[0], MESG_STR("disable_group"))) {
+ r = bypass_pg_num(m, argv[1], 1);
+ goto out;
+ } else if (!strnicmp(argv[0], MESG_STR("enable_group"))) {
+ r = bypass_pg_num(m, argv[1], 0);
+ goto out;
+ } else if (!strnicmp(argv[0], MESG_STR("switch_group"))) {
+ r = switch_pg_num(m, argv[1]);
+ goto out;
+ } else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
action = reinstate_path;
else if (!strnicmp(argv[0], MESG_STR("fail_path")))
action = fail_path;
- else
- goto error;
+ else {
+ DMWARN("Unrecognised multipath message received.");
+ goto out;
+ }
r = dm_get_device(ti, argv[1], ti->begin, ti->len,
dm_table_get_mode(ti->table), &dev);
if (r) {
DMWARN("message: error getting device %s",
argv[1]);
- return -EINVAL;
+ goto out;
}
r = action_dev(m, dev, action);
dm_put_device(ti, dev);
+out:
+ mutex_unlock(&m->work_mutex);
return r;
-
-error:
- DMWARN("Unrecognised multipath message received.");
- return -EINVAL;
}
static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
@@ -1567,13 +1611,14 @@ out:
*---------------------------------------------------------------*/
static struct target_type multipath_target = {
.name = "multipath",
- .version = {1, 1, 0},
+ .version = {1, 1, 1},
.module = THIS_MODULE,
.ctr = multipath_ctr,
.dtr = multipath_dtr,
.map_rq = multipath_map,
.rq_end_io = multipath_end_io,
.presuspend = multipath_presuspend,
+ .postsuspend = multipath_postsuspend,
.resume = multipath_resume,
.status = multipath_status,
.message = multipath_message,
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index cc9dc79b0784..ad779bd13aec 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -35,6 +35,7 @@ static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
*---------------------------------------------------------------*/
enum dm_raid1_error {
DM_RAID1_WRITE_ERROR,
+ DM_RAID1_FLUSH_ERROR,
DM_RAID1_SYNC_ERROR,
DM_RAID1_READ_ERROR
};
@@ -57,6 +58,7 @@ struct mirror_set {
struct bio_list reads;
struct bio_list writes;
struct bio_list failures;
+ struct bio_list holds; /* bios are waiting until suspend */
struct dm_region_hash *rh;
struct dm_kcopyd_client *kcopyd_client;
@@ -67,6 +69,7 @@ struct mirror_set {
region_t nr_regions;
int in_sync;
int log_failure;
+ int leg_failure;
atomic_t suspend;
atomic_t default_mirror; /* Default mirror */
@@ -179,6 +182,17 @@ static void set_default_mirror(struct mirror *m)
atomic_set(&ms->default_mirror, m - m0);
}
+static struct mirror *get_valid_mirror(struct mirror_set *ms)
+{
+ struct mirror *m;
+
+ for (m = ms->mirror; m < ms->mirror + ms->nr_mirrors; m++)
+ if (!atomic_read(&m->error_count))
+ return m;
+
+ return NULL;
+}
+
/* fail_mirror
* @m: mirror device to fail
* @error_type: one of the enum's, DM_RAID1_*_ERROR
@@ -198,6 +212,8 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type)
struct mirror_set *ms = m->ms;
struct mirror *new;
+ ms->leg_failure = 1;
+
/*
* error_count is used for nothing more than a
* simple way to tell if a device has encountered
@@ -224,19 +240,50 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type)
goto out;
}
- for (new = ms->mirror; new < ms->mirror + ms->nr_mirrors; new++)
- if (!atomic_read(&new->error_count)) {
- set_default_mirror(new);
- break;
- }
-
- if (unlikely(new == ms->mirror + ms->nr_mirrors))
+ new = get_valid_mirror(ms);
+ if (new)
+ set_default_mirror(new);
+ else
DMWARN("All sides of mirror have failed.");
out:
schedule_work(&ms->trigger_event);
}
+static int mirror_flush(struct dm_target *ti)
+{
+ struct mirror_set *ms = ti->private;
+ unsigned long error_bits;
+
+ unsigned int i;
+ struct dm_io_region io[ms->nr_mirrors];
+ struct mirror *m;
+ struct dm_io_request io_req = {
+ .bi_rw = WRITE_BARRIER,
+ .mem.type = DM_IO_KMEM,
+ .mem.ptr.bvec = NULL,
+ .client = ms->io_client,
+ };
+
+ for (i = 0, m = ms->mirror; i < ms->nr_mirrors; i++, m++) {
+ io[i].bdev = m->dev->bdev;
+ io[i].sector = 0;
+ io[i].count = 0;
+ }
+
+ error_bits = -1;
+ dm_io(&io_req, ms->nr_mirrors, io, &error_bits);
+ if (unlikely(error_bits != 0)) {
+ for (i = 0; i < ms->nr_mirrors; i++)
+ if (test_bit(i, &error_bits))
+ fail_mirror(ms->mirror + i,
+ DM_RAID1_FLUSH_ERROR);
+ return -EIO;
+ }
+
+ return 0;
+}
+
/*-----------------------------------------------------------------
* Recovery.
*
@@ -396,6 +443,8 @@ static int mirror_available(struct mirror_set *ms, struct bio *bio)
*/
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);
}
@@ -413,6 +462,27 @@ static void map_region(struct dm_io_region *io, struct mirror *m,
io->count = bio->bi_size >> 9;
}
+static void hold_bio(struct mirror_set *ms, struct bio *bio)
+{
+ /*
+ * If device is suspended, complete the bio.
+ */
+ if (atomic_read(&ms->suspend)) {
+ if (dm_noflush_suspending(ms->ti))
+ bio_endio(bio, DM_ENDIO_REQUEUE);
+ else
+ bio_endio(bio, -EIO);
+ return;
+ }
+
+ /*
+ * Hold bio until the suspend is complete.
+ */
+ spin_lock_irq(&ms->lock);
+ bio_list_add(&ms->holds, bio);
+ spin_unlock_irq(&ms->lock);
+}
+
/*-----------------------------------------------------------------
* Reads
*---------------------------------------------------------------*/
@@ -511,7 +581,6 @@ static void write_callback(unsigned long error, void *context)
unsigned i, ret = 0;
struct bio *bio = (struct bio *) context;
struct mirror_set *ms;
- int uptodate = 0;
int should_wake = 0;
unsigned long flags;
@@ -524,36 +593,27 @@ static void write_callback(unsigned long error, void *context)
* This way we handle both writes to SYNC and NOSYNC
* regions with the same code.
*/
- if (likely(!error))
- goto out;
+ if (likely(!error)) {
+ bio_endio(bio, ret);
+ return;
+ }
for (i = 0; i < ms->nr_mirrors; i++)
if (test_bit(i, &error))
fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR);
- else
- uptodate = 1;
- if (unlikely(!uptodate)) {
- DMERR("All replicated volumes dead, failing I/O");
- /* None of the writes succeeded, fail the I/O. */
- ret = -EIO;
- } else if (errors_handled(ms)) {
- /*
- * Need to raise event. Since raising
- * events can block, we need to do it in
- * the main thread.
- */
- spin_lock_irqsave(&ms->lock, flags);
- if (!ms->failures.head)
- should_wake = 1;
- bio_list_add(&ms->failures, bio);
- spin_unlock_irqrestore(&ms->lock, flags);
- if (should_wake)
- wakeup_mirrord(ms);
- return;
- }
-out:
- bio_endio(bio, ret);
+ /*
+ * Need to raise event. Since raising
+ * events can block, we need to do it in
+ * the main thread.
+ */
+ spin_lock_irqsave(&ms->lock, flags);
+ if (!ms->failures.head)
+ should_wake = 1;
+ bio_list_add(&ms->failures, bio);
+ spin_unlock_irqrestore(&ms->lock, flags);
+ if (should_wake)
+ wakeup_mirrord(ms);
}
static void do_write(struct mirror_set *ms, struct bio *bio)
@@ -562,7 +622,7 @@ static void do_write(struct mirror_set *ms, struct bio *bio)
struct dm_io_region io[ms->nr_mirrors], *dest = io;
struct mirror *m;
struct dm_io_request io_req = {
- .bi_rw = WRITE,
+ .bi_rw = WRITE | (bio->bi_rw & WRITE_BARRIER),
.mem.type = DM_IO_BVEC,
.mem.ptr.bvec = bio->bi_io_vec + bio->bi_idx,
.notify.fn = write_callback,
@@ -603,6 +663,11 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
bio_list_init(&requeue);
while ((bio = bio_list_pop(writes))) {
+ if (unlikely(bio_empty_barrier(bio))) {
+ bio_list_add(&sync, bio);
+ continue;
+ }
+
region = dm_rh_bio_to_region(ms->rh, bio);
if (log->type->is_remote_recovering &&
@@ -672,8 +737,12 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
dm_rh_delay(ms->rh, bio);
while ((bio = bio_list_pop(&nosync))) {
- map_bio(get_default_mirror(ms), bio);
- generic_make_request(bio);
+ if (unlikely(ms->leg_failure) && errors_handled(ms))
+ hold_bio(ms, bio);
+ else {
+ map_bio(get_default_mirror(ms), bio);
+ generic_make_request(bio);
+ }
}
}
@@ -681,20 +750,12 @@ static void do_failures(struct mirror_set *ms, struct bio_list *failures)
{
struct bio *bio;
- if (!failures->head)
- return;
-
- if (!ms->log_failure) {
- while ((bio = bio_list_pop(failures))) {
- ms->in_sync = 0;
- dm_rh_mark_nosync(ms->rh, bio, bio->bi_size, 0);
- }
+ if (likely(!failures->head))
return;
- }
/*
* If the log has failed, unattempted writes are being
- * put on the failures list. We can't issue those writes
+ * put on the holds list. We can't issue those writes
* until a log has been marked, so we must store them.
*
* If a 'noflush' suspend is in progress, we can requeue
@@ -709,23 +770,27 @@ static void do_failures(struct mirror_set *ms, struct bio_list *failures)
* for us to treat them the same and requeue them
* as well.
*/
- if (dm_noflush_suspending(ms->ti)) {
- while ((bio = bio_list_pop(failures)))
- bio_endio(bio, DM_ENDIO_REQUEUE);
- return;
- }
+ while ((bio = bio_list_pop(failures))) {
+ if (!ms->log_failure) {
+ ms->in_sync = 0;
+ dm_rh_mark_nosync(ms->rh, bio);
+ }
- if (atomic_read(&ms->suspend)) {
- while ((bio = bio_list_pop(failures)))
+ /*
+ * If all the legs are dead, fail the I/O.
+ * If we have been told to handle errors, hold the bio
+ * and wait for userspace to deal with the problem.
+ * Otherwise pretend that the I/O succeeded. (This would
+ * be wrong if the failed leg returned after reboot and
+ * got replicated back to the good legs.)
+ */
+ if (!get_valid_mirror(ms))
bio_endio(bio, -EIO);
- return;
+ else if (errors_handled(ms))
+ hold_bio(ms, bio);
+ else
+ bio_endio(bio, 0);
}
-
- spin_lock_irq(&ms->lock);
- bio_list_merge(&ms->failures, failures);
- spin_unlock_irq(&ms->lock);
-
- delayed_wake(ms);
}
static void trigger_event(struct work_struct *work)
@@ -784,12 +849,17 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
}
spin_lock_init(&ms->lock);
+ bio_list_init(&ms->reads);
+ bio_list_init(&ms->writes);
+ bio_list_init(&ms->failures);
+ bio_list_init(&ms->holds);
ms->ti = ti;
ms->nr_mirrors = nr_mirrors;
ms->nr_regions = dm_sector_div_up(ti->len, region_size);
ms->in_sync = 0;
ms->log_failure = 0;
+ ms->leg_failure = 0;
atomic_set(&ms->suspend, 0);
atomic_set(&ms->default_mirror, DEFAULT_MIRROR);
@@ -889,7 +959,8 @@ static struct dm_dirty_log *create_dirty_log(struct dm_target *ti,
return NULL;
}
- dl = dm_dirty_log_create(argv[0], ti, param_count, argv + 2);
+ dl = dm_dirty_log_create(argv[0], ti, mirror_flush, param_count,
+ argv + 2);
if (!dl) {
ti->error = "Error creating mirror dirty log";
return NULL;
@@ -995,6 +1066,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->private = ms;
ti->split_io = dm_rh_get_region_size(ms->rh);
+ ti->num_flush_requests = 1;
ms->kmirrord_wq = create_singlethread_workqueue("kmirrord");
if (!ms->kmirrord_wq) {
@@ -1122,7 +1194,8 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio,
* We need to dec pending if this was a write.
*/
if (rw == WRITE) {
- dm_rh_dec(ms->rh, map_context->ll);
+ if (likely(!bio_empty_barrier(bio)))
+ dm_rh_dec(ms->rh, map_context->ll);
return error;
}
@@ -1180,6 +1253,9 @@ static void mirror_presuspend(struct dm_target *ti)
struct mirror_set *ms = (struct mirror_set *) ti->private;
struct dm_dirty_log *log = dm_rh_dirty_log(ms->rh);
+ struct bio_list holds;
+ struct bio *bio;
+
atomic_set(&ms->suspend, 1);
/*
@@ -1202,6 +1278,22 @@ static void mirror_presuspend(struct dm_target *ti)
* we know that all of our I/O has been pushed.
*/
flush_workqueue(ms->kmirrord_wq);
+
+ /*
+ * Now set ms->suspend is set and the workqueue flushed, no more
+ * entries can be added to ms->hold list, so process it.
+ *
+ * Bios can still arrive concurrently with or after this
+ * presuspend function, but they cannot join the hold list
+ * because ms->suspend is set.
+ */
+ spin_lock_irq(&ms->lock);
+ holds = ms->holds;
+ bio_list_init(&ms->holds);
+ spin_unlock_irq(&ms->lock);
+
+ while ((bio = bio_list_pop(&holds)))
+ hold_bio(ms, bio);
}
static void mirror_postsuspend(struct dm_target *ti)
@@ -1244,7 +1336,8 @@ static char device_status_char(struct mirror *m)
if (!atomic_read(&(m->error_count)))
return 'A';
- return (test_bit(DM_RAID1_WRITE_ERROR, &(m->error_type))) ? 'D' :
+ return (test_bit(DM_RAID1_FLUSH_ERROR, &(m->error_type))) ? 'F' :
+ (test_bit(DM_RAID1_WRITE_ERROR, &(m->error_type))) ? 'D' :
(test_bit(DM_RAID1_SYNC_ERROR, &(m->error_type))) ? 'S' :
(test_bit(DM_RAID1_READ_ERROR, &(m->error_type))) ? 'R' : 'U';
}
diff --git a/drivers/md/dm-region-hash.c b/drivers/md/dm-region-hash.c
index 36dbe29f2fd6..5f19ceb6fe91 100644
--- a/drivers/md/dm-region-hash.c
+++ b/drivers/md/dm-region-hash.c
@@ -79,6 +79,11 @@ struct dm_region_hash {
struct list_head recovered_regions;
struct list_head failed_recovered_regions;
+ /*
+ * If there was a barrier failure no regions can be marked clean.
+ */
+ int barrier_failure;
+
void *context;
sector_t target_begin;
@@ -211,6 +216,7 @@ struct dm_region_hash *dm_region_hash_create(
INIT_LIST_HEAD(&rh->quiesced_regions);
INIT_LIST_HEAD(&rh->recovered_regions);
INIT_LIST_HEAD(&rh->failed_recovered_regions);
+ rh->barrier_failure = 0;
rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS,
sizeof(struct dm_region));
@@ -377,8 +383,6 @@ static void complete_resync_work(struct dm_region *reg, int success)
/* dm_rh_mark_nosync
* @ms
* @bio
- * @done
- * @error
*
* The bio was written on some mirror(s) but failed on other mirror(s).
* We can successfully endio the bio but should avoid the region being
@@ -386,8 +390,7 @@ static void complete_resync_work(struct dm_region *reg, int success)
*
* This function is _not_ safe in interrupt context!
*/
-void dm_rh_mark_nosync(struct dm_region_hash *rh,
- struct bio *bio, unsigned done, int error)
+void dm_rh_mark_nosync(struct dm_region_hash *rh, struct bio *bio)
{
unsigned long flags;
struct dm_dirty_log *log = rh->log;
@@ -395,6 +398,11 @@ void dm_rh_mark_nosync(struct dm_region_hash *rh,
region_t region = dm_rh_bio_to_region(rh, bio);
int recovering = 0;
+ if (bio_empty_barrier(bio)) {
+ rh->barrier_failure = 1;
+ return;
+ }
+
/* We must inform the log that the sync count has changed. */
log->type->set_region_sync(log, region, 0);
@@ -419,7 +427,6 @@ void dm_rh_mark_nosync(struct dm_region_hash *rh,
BUG_ON(!list_empty(&reg->list));
spin_unlock_irqrestore(&rh->region_lock, flags);
- bio_endio(bio, error);
if (recovering)
complete_resync_work(reg, 0);
}
@@ -515,8 +522,11 @@ void dm_rh_inc_pending(struct dm_region_hash *rh, struct bio_list *bios)
{
struct bio *bio;
- for (bio = bios->head; bio; bio = bio->bi_next)
+ for (bio = bios->head; bio; bio = bio->bi_next) {
+ if (bio_empty_barrier(bio))
+ continue;
rh_inc(rh, dm_rh_bio_to_region(rh, bio));
+ }
}
EXPORT_SYMBOL_GPL(dm_rh_inc_pending);
@@ -544,7 +554,14 @@ void dm_rh_dec(struct dm_region_hash *rh, region_t region)
*/
/* do nothing for DM_RH_NOSYNC */
- if (reg->state == DM_RH_RECOVERING) {
+ if (unlikely(rh->barrier_failure)) {
+ /*
+ * If a write barrier failed some time ago, we
+ * don't know whether or not this write made it
+ * to the disk, so we must resync the device.
+ */
+ reg->state = DM_RH_NOSYNC;
+ } else if (reg->state == DM_RH_RECOVERING) {
list_add_tail(&reg->list, &rh->quiesced_regions);
} else if (reg->state == DM_RH_DIRTY) {
reg->state = DM_RH_CLEAN;
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 0c746420c008..7d08879689ac 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -55,6 +55,8 @@
*/
#define SNAPSHOT_DISK_VERSION 1
+#define NUM_SNAPSHOT_HDR_CHUNKS 1
+
struct disk_header {
uint32_t magic;
@@ -120,7 +122,22 @@ struct pstore {
/*
* The next free chunk for an exception.
+ *
+ * When creating exceptions, all the chunks here and above are
+ * free. It holds the next chunk to be allocated. On rare
+ * occasions (e.g. after a system crash) holes can be left in
+ * the exception store because chunks can be committed out of
+ * order.
+ *
+ * When merging exceptions, it does not necessarily mean all the
+ * chunks here and above are free. It holds the value it would
+ * have held if all chunks had been committed in order of
+ * allocation. Consequently the value may occasionally be
+ * slightly too low, but since it's only used for 'status' and
+ * it can never reach its minimum value too early this doesn't
+ * matter.
*/
+
chunk_t next_free;
/*
@@ -214,7 +231,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
int metadata)
{
struct dm_io_region where = {
- .bdev = ps->store->cow->bdev,
+ .bdev = dm_snap_cow(ps->store->snap)->bdev,
.sector = ps->store->chunk_size * chunk,
.count = ps->store->chunk_size,
};
@@ -294,7 +311,8 @@ static int read_header(struct pstore *ps, int *new_snapshot)
*/
if (!ps->store->chunk_size) {
ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
- bdev_logical_block_size(ps->store->cow->bdev) >> 9);
+ bdev_logical_block_size(dm_snap_cow(ps->store->snap)->
+ bdev) >> 9);
ps->store->chunk_mask = ps->store->chunk_size - 1;
ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1;
chunk_size_supplied = 0;
@@ -408,6 +426,15 @@ static void write_exception(struct pstore *ps,
e->new_chunk = cpu_to_le64(de->new_chunk);
}
+static void clear_exception(struct pstore *ps, uint32_t index)
+{
+ struct disk_exception *e = get_exception(ps, index);
+
+ /* clear it */
+ e->old_chunk = 0;
+ e->new_chunk = 0;
+}
+
/*
* Registers the exceptions that are present in the current area.
* 'full' is filled in to indicate if the area has been
@@ -489,11 +516,23 @@ static struct pstore *get_info(struct dm_exception_store *store)
return (struct pstore *) store->context;
}
-static void persistent_fraction_full(struct dm_exception_store *store,
- sector_t *numerator, sector_t *denominator)
+static void persistent_usage(struct dm_exception_store *store,
+ sector_t *total_sectors,
+ sector_t *sectors_allocated,
+ sector_t *metadata_sectors)
{
- *numerator = get_info(store)->next_free * store->chunk_size;
- *denominator = get_dev_size(store->cow->bdev);
+ struct pstore *ps = get_info(store);
+
+ *sectors_allocated = ps->next_free * store->chunk_size;
+ *total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
+
+ /*
+ * First chunk is the fixed header.
+ * Then there are (ps->current_area + 1) metadata chunks, each one
+ * separated from the next by ps->exceptions_per_area data chunks.
+ */
+ *metadata_sectors = (ps->current_area + 1 + NUM_SNAPSHOT_HDR_CHUNKS) *
+ store->chunk_size;
}
static void persistent_dtr(struct dm_exception_store *store)
@@ -552,44 +591,40 @@ static int persistent_read_metadata(struct dm_exception_store *store,
ps->current_area = 0;
zero_memory_area(ps);
r = zero_disk_area(ps, 0);
- if (r) {
+ if (r)
DMWARN("zero_disk_area(0) failed");
- return r;
- }
- } else {
- /*
- * Sanity checks.
- */
- if (ps->version != SNAPSHOT_DISK_VERSION) {
- DMWARN("unable to handle snapshot disk version %d",
- ps->version);
- return -EINVAL;
- }
+ return r;
+ }
+ /*
+ * Sanity checks.
+ */
+ if (ps->version != SNAPSHOT_DISK_VERSION) {
+ DMWARN("unable to handle snapshot disk version %d",
+ ps->version);
+ return -EINVAL;
+ }
- /*
- * Metadata are valid, but snapshot is invalidated
- */
- if (!ps->valid)
- return 1;
+ /*
+ * Metadata are valid, but snapshot is invalidated
+ */
+ if (!ps->valid)
+ return 1;
- /*
- * Read the metadata.
- */
- r = read_exceptions(ps, callback, callback_context);
- if (r)
- return r;
- }
+ /*
+ * Read the metadata.
+ */
+ r = read_exceptions(ps, callback, callback_context);
- return 0;
+ return r;
}
static int persistent_prepare_exception(struct dm_exception_store *store,
- struct dm_snap_exception *e)
+ struct dm_exception *e)
{
struct pstore *ps = get_info(store);
uint32_t stride;
chunk_t next_free;
- sector_t size = get_dev_size(store->cow->bdev);
+ sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
/* Is there enough room ? */
if (size < ((ps->next_free + 1) * store->chunk_size))
@@ -611,7 +646,7 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
}
static void persistent_commit_exception(struct dm_exception_store *store,
- struct dm_snap_exception *e,
+ struct dm_exception *e,
void (*callback) (void *, int success),
void *callback_context)
{
@@ -672,6 +707,85 @@ static void persistent_commit_exception(struct dm_exception_store *store,
ps->callback_count = 0;
}
+static int persistent_prepare_merge(struct dm_exception_store *store,
+ chunk_t *last_old_chunk,
+ chunk_t *last_new_chunk)
+{
+ struct pstore *ps = get_info(store);
+ struct disk_exception de;
+ int nr_consecutive;
+ int r;
+
+ /*
+ * When current area is empty, move back to preceding area.
+ */
+ if (!ps->current_committed) {
+ /*
+ * Have we finished?
+ */
+ if (!ps->current_area)
+ return 0;
+
+ ps->current_area--;
+ r = area_io(ps, READ);
+ if (r < 0)
+ return r;
+ ps->current_committed = ps->exceptions_per_area;
+ }
+
+ read_exception(ps, ps->current_committed - 1, &de);
+ *last_old_chunk = de.old_chunk;
+ *last_new_chunk = de.new_chunk;
+
+ /*
+ * Find number of consecutive chunks within the current area,
+ * working backwards.
+ */
+ for (nr_consecutive = 1; nr_consecutive < ps->current_committed;
+ nr_consecutive++) {
+ read_exception(ps, ps->current_committed - 1 - nr_consecutive,
+ &de);
+ if (de.old_chunk != *last_old_chunk - nr_consecutive ||
+ de.new_chunk != *last_new_chunk - nr_consecutive)
+ break;
+ }
+
+ return nr_consecutive;
+}
+
+static int persistent_commit_merge(struct dm_exception_store *store,
+ int nr_merged)
+{
+ int r, i;
+ struct pstore *ps = get_info(store);
+
+ BUG_ON(nr_merged > ps->current_committed);
+
+ for (i = 0; i < nr_merged; i++)
+ clear_exception(ps, ps->current_committed - 1 - i);
+
+ r = area_io(ps, WRITE);
+ if (r < 0)
+ return r;
+
+ ps->current_committed -= nr_merged;
+
+ /*
+ * At this stage, only persistent_usage() uses ps->next_free, so
+ * we make no attempt to keep ps->next_free strictly accurate
+ * as exceptions may have been committed out-of-order originally.
+ * Once a snapshot has become merging, we set it to the value it
+ * would have held had all the exceptions been committed in order.
+ *
+ * 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;
+
+ return 0;
+}
+
static void persistent_drop_snapshot(struct dm_exception_store *store)
{
struct pstore *ps = get_info(store);
@@ -697,7 +811,7 @@ static int persistent_ctr(struct dm_exception_store *store,
ps->area = NULL;
ps->zero_area = NULL;
ps->header_area = NULL;
- ps->next_free = 2; /* skipping the header and first area */
+ ps->next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1; /* header and 1st area */
ps->current_committed = 0;
ps->callback_count = 0;
@@ -726,8 +840,7 @@ static unsigned persistent_status(struct dm_exception_store *store,
case STATUSTYPE_INFO:
break;
case STATUSTYPE_TABLE:
- DMEMIT(" %s P %llu", store->cow->name,
- (unsigned long long)store->chunk_size);
+ DMEMIT(" P %llu", (unsigned long long)store->chunk_size);
}
return sz;
@@ -741,8 +854,10 @@ static struct dm_exception_store_type _persistent_type = {
.read_metadata = persistent_read_metadata,
.prepare_exception = persistent_prepare_exception,
.commit_exception = persistent_commit_exception,
+ .prepare_merge = persistent_prepare_merge,
+ .commit_merge = persistent_commit_merge,
.drop_snapshot = persistent_drop_snapshot,
- .fraction_full = persistent_fraction_full,
+ .usage = persistent_usage,
.status = persistent_status,
};
@@ -754,8 +869,10 @@ static struct dm_exception_store_type _persistent_compat_type = {
.read_metadata = persistent_read_metadata,
.prepare_exception = persistent_prepare_exception,
.commit_exception = persistent_commit_exception,
+ .prepare_merge = persistent_prepare_merge,
+ .commit_merge = persistent_commit_merge,
.drop_snapshot = persistent_drop_snapshot,
- .fraction_full = persistent_fraction_full,
+ .usage = persistent_usage,
.status = persistent_status,
};
diff --git a/drivers/md/dm-snap-transient.c b/drivers/md/dm-snap-transient.c
index cde5aa558e6d..a0898a66a2f8 100644
--- a/drivers/md/dm-snap-transient.c
+++ b/drivers/md/dm-snap-transient.c
@@ -36,10 +36,10 @@ static int transient_read_metadata(struct dm_exception_store *store,
}
static int transient_prepare_exception(struct dm_exception_store *store,
- struct dm_snap_exception *e)
+ struct dm_exception *e)
{
struct transient_c *tc = store->context;
- sector_t size = get_dev_size(store->cow->bdev);
+ sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
if (size < (tc->next_free + store->chunk_size))
return -1;
@@ -51,7 +51,7 @@ static int transient_prepare_exception(struct dm_exception_store *store,
}
static void transient_commit_exception(struct dm_exception_store *store,
- struct dm_snap_exception *e,
+ struct dm_exception *e,
void (*callback) (void *, int success),
void *callback_context)
{
@@ -59,11 +59,14 @@ static void transient_commit_exception(struct dm_exception_store *store,
callback(callback_context, 1);
}
-static void transient_fraction_full(struct dm_exception_store *store,
- sector_t *numerator, sector_t *denominator)
+static void transient_usage(struct dm_exception_store *store,
+ sector_t *total_sectors,
+ sector_t *sectors_allocated,
+ sector_t *metadata_sectors)
{
- *numerator = ((struct transient_c *) store->context)->next_free;
- *denominator = get_dev_size(store->cow->bdev);
+ *sectors_allocated = ((struct transient_c *) store->context)->next_free;
+ *total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
+ *metadata_sectors = 0;
}
static int transient_ctr(struct dm_exception_store *store,
@@ -91,8 +94,7 @@ static unsigned transient_status(struct dm_exception_store *store,
case STATUSTYPE_INFO:
break;
case STATUSTYPE_TABLE:
- DMEMIT(" %s N %llu", store->cow->name,
- (unsigned long long)store->chunk_size);
+ DMEMIT(" N %llu", (unsigned long long)store->chunk_size);
}
return sz;
@@ -106,7 +108,7 @@ static struct dm_exception_store_type _transient_type = {
.read_metadata = transient_read_metadata,
.prepare_exception = transient_prepare_exception,
.commit_exception = transient_commit_exception,
- .fraction_full = transient_fraction_full,
+ .usage = transient_usage,
.status = transient_status,
};
@@ -118,7 +120,7 @@ static struct dm_exception_store_type _transient_compat_type = {
.read_metadata = transient_read_metadata,
.prepare_exception = transient_prepare_exception,
.commit_exception = transient_commit_exception,
- .fraction_full = transient_fraction_full,
+ .usage = transient_usage,
.status = transient_status,
};
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 3a3ba46e6d4b..ee8eb283650d 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -25,6 +25,11 @@
#define DM_MSG_PREFIX "snapshots"
+static const char dm_snapshot_merge_target_name[] = "snapshot-merge";
+
+#define dm_target_is_snapshot_merge(ti) \
+ ((ti)->type->name == dm_snapshot_merge_target_name)
+
/*
* The percentage increment we will wake up users at
*/
@@ -49,7 +54,7 @@
#define DM_TRACKED_CHUNK_HASH(x) ((unsigned long)(x) & \
(DM_TRACKED_CHUNK_HASH_SIZE - 1))
-struct exception_table {
+struct dm_exception_table {
uint32_t hash_mask;
unsigned hash_shift;
struct list_head *table;
@@ -59,22 +64,31 @@ struct dm_snapshot {
struct rw_semaphore lock;
struct dm_dev *origin;
+ struct dm_dev *cow;
+
+ struct dm_target *ti;
/* List of snapshots per Origin */
struct list_head list;
- /* You can't use a snapshot if this is 0 (e.g. if full) */
+ /*
+ * You can't use a snapshot if this is 0 (e.g. if full).
+ * A snapshot-merge target never clears this.
+ */
int valid;
/* Origin writes don't trigger exceptions until this is set */
int active;
+ /* Whether or not owning mapped_device is suspended */
+ int suspended;
+
mempool_t *pending_pool;
atomic_t pending_exceptions_count;
- struct exception_table pending;
- struct exception_table complete;
+ struct dm_exception_table pending;
+ struct dm_exception_table complete;
/*
* pe_lock protects all pending_exception operations and access
@@ -95,8 +109,51 @@ struct dm_snapshot {
mempool_t *tracked_chunk_pool;
spinlock_t tracked_chunk_lock;
struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
+
+ /*
+ * The merge operation failed if this flag is set.
+ * Failure modes are handled as follows:
+ * - I/O error reading the header
+ * => don't load the target; abort.
+ * - Header does not have "valid" flag set
+ * => use the origin; forget about the snapshot.
+ * - I/O error when reading exceptions
+ * => don't load the target; abort.
+ * (We can't use the intermediate origin state.)
+ * - I/O error while merging
+ * => stop merging; set merge_failed; process I/O normally.
+ */
+ int merge_failed;
+
+ /* Wait for events based on state_bits */
+ unsigned long state_bits;
+
+ /* Range of chunks currently being merged. */
+ chunk_t first_merging_chunk;
+ int num_merging_chunks;
+
+ /*
+ * Incoming bios that overlap with chunks being merged must wait
+ * for them to be committed.
+ */
+ struct bio_list bios_queued_during_merge;
};
+/*
+ * state_bits:
+ * RUNNING_MERGE - Merge operation is in progress.
+ * SHUTDOWN_MERGE - Set to signal that merge needs to be stopped;
+ * cleared afterwards.
+ */
+#define RUNNING_MERGE 0
+#define SHUTDOWN_MERGE 1
+
+struct dm_dev *dm_snap_cow(struct dm_snapshot *s)
+{
+ return s->cow;
+}
+EXPORT_SYMBOL(dm_snap_cow);
+
static struct workqueue_struct *ksnapd;
static void flush_queued_bios(struct work_struct *work);
@@ -116,7 +173,7 @@ static int bdev_equal(struct block_device *lhs, struct block_device *rhs)
}
struct dm_snap_pending_exception {
- struct dm_snap_exception e;
+ struct dm_exception e;
/*
* Origin buffers waiting for this to complete are held
@@ -125,28 +182,6 @@ struct dm_snap_pending_exception {
struct bio_list origin_bios;
struct bio_list snapshot_bios;
- /*
- * Short-term queue of pending exceptions prior to submission.
- */
- struct list_head list;
-
- /*
- * The primary pending_exception is the one that holds
- * the ref_count and the list of origin_bios for a
- * group of pending_exceptions. It is always last to get freed.
- * These fields get set up when writing to the origin.
- */
- struct dm_snap_pending_exception *primary_pe;
-
- /*
- * Number of pending_exceptions processing this chunk.
- * When this drops to zero we must complete the origin bios.
- * If incrementing or decrementing this, hold pe->snap->lock for
- * the sibling concerned and not pe->primary_pe->snap->lock unless
- * they are the same.
- */
- atomic_t ref_count;
-
/* Pointer back to snapshot context */
struct dm_snapshot *snap;
@@ -222,6 +257,16 @@ static int __chunk_is_tracked(struct dm_snapshot *s, chunk_t chunk)
}
/*
+ * This conflicting I/O is extremely improbable in the caller,
+ * so msleep(1) is sufficient and there is no need for a wait queue.
+ */
+static void __check_for_conflicting_io(struct dm_snapshot *s, chunk_t chunk)
+{
+ while (__chunk_is_tracked(s, chunk))
+ msleep(1);
+}
+
+/*
* One of these per registered origin, held in the snapshot_origins hash
*/
struct origin {
@@ -243,6 +288,10 @@ struct origin {
static struct list_head *_origins;
static struct rw_semaphore _origins_lock;
+static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done);
+static DEFINE_SPINLOCK(_pending_exceptions_done_spinlock);
+static uint64_t _pending_exceptions_done_count;
+
static int init_origin_hash(void)
{
int i;
@@ -291,22 +340,144 @@ static void __insert_origin(struct origin *o)
}
/*
+ * _origins_lock must be held when calling this function.
+ * Returns number of snapshots registered using the supplied cow device, plus:
+ * snap_src - a snapshot suitable for use as a source of exception handover
+ * snap_dest - a snapshot capable of receiving exception handover.
+ * snap_merge - an existing snapshot-merge target linked to the same origin.
+ * There can be at most one snapshot-merge target. The parameter is optional.
+ *
+ * Possible return values and states of snap_src and snap_dest.
+ * 0: NULL, NULL - first new snapshot
+ * 1: snap_src, NULL - normal snapshot
+ * 2: snap_src, snap_dest - waiting for handover
+ * 2: snap_src, NULL - handed over, waiting for old to be deleted
+ * 1: NULL, snap_dest - source got destroyed without handover
+ */
+static int __find_snapshots_sharing_cow(struct dm_snapshot *snap,
+ struct dm_snapshot **snap_src,
+ struct dm_snapshot **snap_dest,
+ struct dm_snapshot **snap_merge)
+{
+ struct dm_snapshot *s;
+ struct origin *o;
+ int count = 0;
+ int active;
+
+ o = __lookup_origin(snap->origin->bdev);
+ if (!o)
+ goto out;
+
+ list_for_each_entry(s, &o->snapshots, list) {
+ if (dm_target_is_snapshot_merge(s->ti) && snap_merge)
+ *snap_merge = s;
+ if (!bdev_equal(s->cow->bdev, snap->cow->bdev))
+ continue;
+
+ down_read(&s->lock);
+ active = s->active;
+ up_read(&s->lock);
+
+ if (active) {
+ if (snap_src)
+ *snap_src = s;
+ } else if (snap_dest)
+ *snap_dest = s;
+
+ count++;
+ }
+
+out:
+ return count;
+}
+
+/*
+ * On success, returns 1 if this snapshot is a handover destination,
+ * otherwise returns 0.
+ */
+static int __validate_exception_handover(struct dm_snapshot *snap)
+{
+ struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
+ struct dm_snapshot *snap_merge = NULL;
+
+ /* Does snapshot need exceptions handed over to it? */
+ if ((__find_snapshots_sharing_cow(snap, &snap_src, &snap_dest,
+ &snap_merge) == 2) ||
+ snap_dest) {
+ snap->ti->error = "Snapshot cow pairing for exception "
+ "table handover failed";
+ return -EINVAL;
+ }
+
+ /*
+ * If no snap_src was found, snap cannot become a handover
+ * destination.
+ */
+ if (!snap_src)
+ return 0;
+
+ /*
+ * Non-snapshot-merge handover?
+ */
+ if (!dm_target_is_snapshot_merge(snap->ti))
+ return 1;
+
+ /*
+ * Do not allow more than one merging snapshot.
+ */
+ if (snap_merge) {
+ snap->ti->error = "A snapshot is already merging.";
+ return -EINVAL;
+ }
+
+ if (!snap_src->store->type->prepare_merge ||
+ !snap_src->store->type->commit_merge) {
+ snap->ti->error = "Snapshot exception store does not "
+ "support snapshot-merge.";
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static void __insert_snapshot(struct origin *o, struct dm_snapshot *s)
+{
+ struct dm_snapshot *l;
+
+ /* Sort the list according to chunk size, largest-first smallest-last */
+ list_for_each_entry(l, &o->snapshots, list)
+ if (l->store->chunk_size < s->store->chunk_size)
+ break;
+ list_add_tail(&s->list, &l->list);
+}
+
+/*
* Make a note of the snapshot and its origin so we can look it
* up when the origin has a write on it.
+ *
+ * Also validate snapshot exception store handovers.
+ * On success, returns 1 if this registration is a handover destination,
+ * otherwise returns 0.
*/
static int register_snapshot(struct dm_snapshot *snap)
{
- struct dm_snapshot *l;
- struct origin *o, *new_o;
+ struct origin *o, *new_o = NULL;
struct block_device *bdev = snap->origin->bdev;
+ int r = 0;
new_o = kmalloc(sizeof(*new_o), GFP_KERNEL);
if (!new_o)
return -ENOMEM;
down_write(&_origins_lock);
- o = __lookup_origin(bdev);
+ r = __validate_exception_handover(snap);
+ if (r < 0) {
+ kfree(new_o);
+ goto out;
+ }
+
+ o = __lookup_origin(bdev);
if (o)
kfree(new_o);
else {
@@ -320,14 +491,27 @@ static int register_snapshot(struct dm_snapshot *snap)
__insert_origin(o);
}
- /* Sort the list according to chunk size, largest-first smallest-last */
- list_for_each_entry(l, &o->snapshots, list)
- if (l->store->chunk_size < snap->store->chunk_size)
- break;
- list_add_tail(&snap->list, &l->list);
+ __insert_snapshot(o, snap);
+
+out:
+ up_write(&_origins_lock);
+
+ return r;
+}
+
+/*
+ * Move snapshot to correct place in list according to chunk size.
+ */
+static void reregister_snapshot(struct dm_snapshot *s)
+{
+ struct block_device *bdev = s->origin->bdev;
+
+ down_write(&_origins_lock);
+
+ list_del(&s->list);
+ __insert_snapshot(__lookup_origin(bdev), s);
up_write(&_origins_lock);
- return 0;
}
static void unregister_snapshot(struct dm_snapshot *s)
@@ -338,7 +522,7 @@ static void unregister_snapshot(struct dm_snapshot *s)
o = __lookup_origin(s->origin->bdev);
list_del(&s->list);
- if (list_empty(&o->snapshots)) {
+ if (o && list_empty(&o->snapshots)) {
list_del(&o->hash_list);
kfree(o);
}
@@ -351,8 +535,8 @@ static void unregister_snapshot(struct dm_snapshot *s)
* The lowest hash_shift bits of the chunk number are ignored, allowing
* some consecutive chunks to be grouped together.
*/
-static int init_exception_table(struct exception_table *et, uint32_t size,
- unsigned hash_shift)
+static int dm_exception_table_init(struct dm_exception_table *et,
+ uint32_t size, unsigned hash_shift)
{
unsigned int i;
@@ -368,10 +552,11 @@ static int init_exception_table(struct exception_table *et, uint32_t size,
return 0;
}
-static void exit_exception_table(struct exception_table *et, struct kmem_cache *mem)
+static void dm_exception_table_exit(struct dm_exception_table *et,
+ struct kmem_cache *mem)
{
struct list_head *slot;
- struct dm_snap_exception *ex, *next;
+ struct dm_exception *ex, *next;
int i, size;
size = et->hash_mask + 1;
@@ -385,19 +570,12 @@ static void exit_exception_table(struct exception_table *et, struct kmem_cache *
vfree(et->table);
}
-static uint32_t exception_hash(struct exception_table *et, chunk_t chunk)
+static uint32_t exception_hash(struct dm_exception_table *et, chunk_t chunk)
{
return (chunk >> et->hash_shift) & et->hash_mask;
}
-static void insert_exception(struct exception_table *eh,
- struct dm_snap_exception *e)
-{
- struct list_head *l = &eh->table[exception_hash(eh, e->old_chunk)];
- list_add(&e->hash_list, l);
-}
-
-static void remove_exception(struct dm_snap_exception *e)
+static void dm_remove_exception(struct dm_exception *e)
{
list_del(&e->hash_list);
}
@@ -406,11 +584,11 @@ static void remove_exception(struct dm_snap_exception *e)
* Return the exception data for a sector, or NULL if not
* remapped.
*/
-static struct dm_snap_exception *lookup_exception(struct exception_table *et,
- chunk_t chunk)
+static struct dm_exception *dm_lookup_exception(struct dm_exception_table *et,
+ chunk_t chunk)
{
struct list_head *slot;
- struct dm_snap_exception *e;
+ struct dm_exception *e;
slot = &et->table[exception_hash(et, chunk)];
list_for_each_entry (e, slot, hash_list)
@@ -421,9 +599,9 @@ static struct dm_snap_exception *lookup_exception(struct exception_table *et,
return NULL;
}
-static struct dm_snap_exception *alloc_exception(void)
+static struct dm_exception *alloc_completed_exception(void)
{
- struct dm_snap_exception *e;
+ struct dm_exception *e;
e = kmem_cache_alloc(exception_cache, GFP_NOIO);
if (!e)
@@ -432,7 +610,7 @@ static struct dm_snap_exception *alloc_exception(void)
return e;
}
-static void free_exception(struct dm_snap_exception *e)
+static void free_completed_exception(struct dm_exception *e)
{
kmem_cache_free(exception_cache, e);
}
@@ -457,12 +635,11 @@ static void free_pending_exception(struct dm_snap_pending_exception *pe)
atomic_dec(&s->pending_exceptions_count);
}
-static void insert_completed_exception(struct dm_snapshot *s,
- struct dm_snap_exception *new_e)
+static void dm_insert_exception(struct dm_exception_table *eh,
+ struct dm_exception *new_e)
{
- struct exception_table *eh = &s->complete;
struct list_head *l;
- struct dm_snap_exception *e = NULL;
+ struct dm_exception *e = NULL;
l = &eh->table[exception_hash(eh, new_e->old_chunk)];
@@ -478,7 +655,7 @@ static void insert_completed_exception(struct dm_snapshot *s,
new_e->new_chunk == (dm_chunk_number(e->new_chunk) +
dm_consecutive_chunk_count(e) + 1)) {
dm_consecutive_chunk_count_inc(e);
- free_exception(new_e);
+ free_completed_exception(new_e);
return;
}
@@ -488,7 +665,7 @@ static void insert_completed_exception(struct dm_snapshot *s,
dm_consecutive_chunk_count_inc(e);
e->old_chunk--;
e->new_chunk--;
- free_exception(new_e);
+ free_completed_exception(new_e);
return;
}
@@ -507,9 +684,9 @@ out:
static int dm_add_exception(void *context, chunk_t old, chunk_t new)
{
struct dm_snapshot *s = context;
- struct dm_snap_exception *e;
+ struct dm_exception *e;
- e = alloc_exception();
+ e = alloc_completed_exception();
if (!e)
return -ENOMEM;
@@ -518,11 +695,30 @@ static int dm_add_exception(void *context, chunk_t old, chunk_t new)
/* Consecutive_count is implicitly initialised to zero */
e->new_chunk = new;
- insert_completed_exception(s, e);
+ dm_insert_exception(&s->complete, e);
return 0;
}
+#define min_not_zero(l, r) (((l) == 0) ? (r) : (((r) == 0) ? (l) : min(l, r)))
+
+/*
+ * Return a minimum chunk size of all snapshots that have the specified origin.
+ * Return zero if the origin has no snapshots.
+ */
+static sector_t __minimum_chunk_size(struct origin *o)
+{
+ struct dm_snapshot *snap;
+ unsigned chunk_size = 0;
+
+ if (o)
+ list_for_each_entry(snap, &o->snapshots, list)
+ chunk_size = min_not_zero(chunk_size,
+ snap->store->chunk_size);
+
+ return chunk_size;
+}
+
/*
* Hard coded magic.
*/
@@ -546,16 +742,18 @@ static int init_hash_tables(struct dm_snapshot *s)
* Calculate based on the size of the original volume or
* the COW volume...
*/
- cow_dev_size = get_dev_size(s->store->cow->bdev);
+ cow_dev_size = get_dev_size(s->cow->bdev);
origin_dev_size = get_dev_size(s->origin->bdev);
max_buckets = calc_max_buckets();
hash_size = min(origin_dev_size, cow_dev_size) >> s->store->chunk_shift;
hash_size = min(hash_size, max_buckets);
+ if (hash_size < 64)
+ hash_size = 64;
hash_size = rounddown_pow_of_two(hash_size);
- if (init_exception_table(&s->complete, hash_size,
- DM_CHUNK_CONSECUTIVE_BITS))
+ if (dm_exception_table_init(&s->complete, hash_size,
+ DM_CHUNK_CONSECUTIVE_BITS))
return -ENOMEM;
/*
@@ -566,14 +764,284 @@ static int init_hash_tables(struct dm_snapshot *s)
if (hash_size < 64)
hash_size = 64;
- if (init_exception_table(&s->pending, hash_size, 0)) {
- exit_exception_table(&s->complete, exception_cache);
+ if (dm_exception_table_init(&s->pending, hash_size, 0)) {
+ dm_exception_table_exit(&s->complete, exception_cache);
return -ENOMEM;
}
return 0;
}
+static void merge_shutdown(struct dm_snapshot *s)
+{
+ clear_bit_unlock(RUNNING_MERGE, &s->state_bits);
+ smp_mb__after_clear_bit();
+ wake_up_bit(&s->state_bits, RUNNING_MERGE);
+}
+
+static struct bio *__release_queued_bios_after_merge(struct dm_snapshot *s)
+{
+ s->first_merging_chunk = 0;
+ s->num_merging_chunks = 0;
+
+ return bio_list_get(&s->bios_queued_during_merge);
+}
+
+/*
+ * Remove one chunk from the index of completed exceptions.
+ */
+static int __remove_single_exception_chunk(struct dm_snapshot *s,
+ chunk_t old_chunk)
+{
+ struct dm_exception *e;
+
+ e = dm_lookup_exception(&s->complete, old_chunk);
+ if (!e) {
+ DMERR("Corruption detected: exception for block %llu is "
+ "on disk but not in memory",
+ (unsigned long long)old_chunk);
+ return -EINVAL;
+ }
+
+ /*
+ * If this is the only chunk using this exception, remove exception.
+ */
+ if (!dm_consecutive_chunk_count(e)) {
+ dm_remove_exception(e);
+ free_completed_exception(e);
+ return 0;
+ }
+
+ /*
+ * The chunk may be either at the beginning or the end of a
+ * group of consecutive chunks - never in the middle. We are
+ * removing chunks in the opposite order to that in which they
+ * were added, so this should always be true.
+ * Decrement the consecutive chunk counter and adjust the
+ * starting point if necessary.
+ */
+ if (old_chunk == e->old_chunk) {
+ e->old_chunk++;
+ e->new_chunk++;
+ } else if (old_chunk != e->old_chunk +
+ dm_consecutive_chunk_count(e)) {
+ DMERR("Attempt to merge block %llu from the "
+ "middle of a chunk range [%llu - %llu]",
+ (unsigned long long)old_chunk,
+ (unsigned long long)e->old_chunk,
+ (unsigned long long)
+ e->old_chunk + dm_consecutive_chunk_count(e));
+ return -EINVAL;
+ }
+
+ dm_consecutive_chunk_count_dec(e);
+
+ return 0;
+}
+
+static void flush_bios(struct bio *bio);
+
+static int remove_single_exception_chunk(struct dm_snapshot *s)
+{
+ struct bio *b = NULL;
+ int r;
+ chunk_t old_chunk = s->first_merging_chunk + s->num_merging_chunks - 1;
+
+ down_write(&s->lock);
+
+ /*
+ * Process chunks (and associated exceptions) in reverse order
+ * so that dm_consecutive_chunk_count_dec() accounting works.
+ */
+ do {
+ r = __remove_single_exception_chunk(s, old_chunk);
+ if (r)
+ goto out;
+ } while (old_chunk-- > s->first_merging_chunk);
+
+ b = __release_queued_bios_after_merge(s);
+
+out:
+ up_write(&s->lock);
+ if (b)
+ flush_bios(b);
+
+ return r;
+}
+
+static int origin_write_extent(struct dm_snapshot *merging_snap,
+ sector_t sector, unsigned chunk_size);
+
+static void merge_callback(int read_err, unsigned long write_err,
+ void *context);
+
+static uint64_t read_pending_exceptions_done_count(void)
+{
+ uint64_t pending_exceptions_done;
+
+ spin_lock(&_pending_exceptions_done_spinlock);
+ pending_exceptions_done = _pending_exceptions_done_count;
+ spin_unlock(&_pending_exceptions_done_spinlock);
+
+ return pending_exceptions_done;
+}
+
+static void increment_pending_exceptions_done_count(void)
+{
+ spin_lock(&_pending_exceptions_done_spinlock);
+ _pending_exceptions_done_count++;
+ spin_unlock(&_pending_exceptions_done_spinlock);
+
+ wake_up_all(&_pending_exceptions_done);
+}
+
+static void snapshot_merge_next_chunks(struct dm_snapshot *s)
+{
+ int i, linear_chunks;
+ chunk_t old_chunk, new_chunk;
+ struct dm_io_region src, dest;
+ sector_t io_size;
+ uint64_t previous_count;
+
+ BUG_ON(!test_bit(RUNNING_MERGE, &s->state_bits));
+ if (unlikely(test_bit(SHUTDOWN_MERGE, &s->state_bits)))
+ goto shut;
+
+ /*
+ * valid flag never changes during merge, so no lock required.
+ */
+ if (!s->valid) {
+ DMERR("Snapshot is invalid: can't merge");
+ goto shut;
+ }
+
+ linear_chunks = s->store->type->prepare_merge(s->store, &old_chunk,
+ &new_chunk);
+ if (linear_chunks <= 0) {
+ if (linear_chunks < 0) {
+ DMERR("Read error in exception store: "
+ "shutting down merge");
+ down_write(&s->lock);
+ s->merge_failed = 1;
+ up_write(&s->lock);
+ }
+ goto shut;
+ }
+
+ /* Adjust old_chunk and new_chunk to reflect start of linear region */
+ old_chunk = old_chunk + 1 - linear_chunks;
+ new_chunk = new_chunk + 1 - linear_chunks;
+
+ /*
+ * Use one (potentially large) I/O to copy all 'linear_chunks'
+ * from the exception store to the origin
+ */
+ io_size = linear_chunks * s->store->chunk_size;
+
+ dest.bdev = s->origin->bdev;
+ dest.sector = chunk_to_sector(s->store, old_chunk);
+ dest.count = min(io_size, get_dev_size(dest.bdev) - dest.sector);
+
+ src.bdev = s->cow->bdev;
+ src.sector = chunk_to_sector(s->store, new_chunk);
+ src.count = dest.count;
+
+ /*
+ * Reallocate any exceptions needed in other snapshots then
+ * wait for the pending exceptions to complete.
+ * Each time any pending exception (globally on the system)
+ * completes we are woken and repeat the process to find out
+ * if we can proceed. While this may not seem a particularly
+ * efficient algorithm, it is not expected to have any
+ * significant impact on performance.
+ */
+ previous_count = read_pending_exceptions_done_count();
+ while (origin_write_extent(s, dest.sector, io_size)) {
+ wait_event(_pending_exceptions_done,
+ (read_pending_exceptions_done_count() !=
+ previous_count));
+ /* Retry after the wait, until all exceptions are done. */
+ previous_count = read_pending_exceptions_done_count();
+ }
+
+ down_write(&s->lock);
+ s->first_merging_chunk = old_chunk;
+ s->num_merging_chunks = linear_chunks;
+ up_write(&s->lock);
+
+ /* Wait until writes to all 'linear_chunks' drain */
+ for (i = 0; i < linear_chunks; i++)
+ __check_for_conflicting_io(s, old_chunk + i);
+
+ dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s);
+ return;
+
+shut:
+ merge_shutdown(s);
+}
+
+static void error_bios(struct bio *bio);
+
+static void merge_callback(int read_err, unsigned long write_err, void *context)
+{
+ struct dm_snapshot *s = context;
+ struct bio *b = NULL;
+
+ if (read_err || write_err) {
+ if (read_err)
+ DMERR("Read error: shutting down merge.");
+ else
+ DMERR("Write error: shutting down merge.");
+ goto shut;
+ }
+
+ if (s->store->type->commit_merge(s->store,
+ s->num_merging_chunks) < 0) {
+ DMERR("Write error in exception store: shutting down merge");
+ goto shut;
+ }
+
+ if (remove_single_exception_chunk(s) < 0)
+ goto shut;
+
+ snapshot_merge_next_chunks(s);
+
+ return;
+
+shut:
+ down_write(&s->lock);
+ s->merge_failed = 1;
+ b = __release_queued_bios_after_merge(s);
+ up_write(&s->lock);
+ error_bios(b);
+
+ merge_shutdown(s);
+}
+
+static void start_merge(struct dm_snapshot *s)
+{
+ if (!test_and_set_bit(RUNNING_MERGE, &s->state_bits))
+ snapshot_merge_next_chunks(s);
+}
+
+static int wait_schedule(void *ptr)
+{
+ schedule();
+
+ return 0;
+}
+
+/*
+ * Stop the merging process and wait until it finishes.
+ */
+static void stop_merge(struct dm_snapshot *s)
+{
+ set_bit(SHUTDOWN_MERGE, &s->state_bits);
+ wait_on_bit(&s->state_bits, RUNNING_MERGE, wait_schedule,
+ TASK_UNINTERRUPTIBLE);
+ clear_bit(SHUTDOWN_MERGE, &s->state_bits);
+}
+
/*
* Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
*/
@@ -582,50 +1050,73 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
struct dm_snapshot *s;
int i;
int r = -EINVAL;
- char *origin_path;
- struct dm_exception_store *store;
- unsigned args_used;
+ char *origin_path, *cow_path;
+ unsigned args_used, num_flush_requests = 1;
+ fmode_t origin_mode = FMODE_READ;
if (argc != 4) {
ti->error = "requires exactly 4 arguments";
r = -EINVAL;
- goto bad_args;
+ goto bad;
+ }
+
+ if (dm_target_is_snapshot_merge(ti)) {
+ num_flush_requests = 2;
+ origin_mode = FMODE_WRITE;
}
origin_path = argv[0];
argv++;
argc--;
- r = dm_exception_store_create(ti, argc, argv, &args_used, &store);
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s) {
+ ti->error = "Cannot allocate snapshot context private "
+ "structure";
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ cow_path = argv[0];
+ argv++;
+ argc--;
+
+ r = dm_get_device(ti, cow_path, 0, 0,
+ FMODE_READ | FMODE_WRITE, &s->cow);
+ if (r) {
+ ti->error = "Cannot get COW device";
+ goto bad_cow;
+ }
+
+ r = dm_exception_store_create(ti, argc, argv, s, &args_used, &s->store);
if (r) {
ti->error = "Couldn't create exception store";
r = -EINVAL;
- goto bad_args;
+ goto bad_store;
}
argv += args_used;
argc -= args_used;
- s = kmalloc(sizeof(*s), GFP_KERNEL);
- if (!s) {
- ti->error = "Cannot allocate snapshot context private "
- "structure";
- r = -ENOMEM;
- goto bad_snap;
- }
-
- r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
+ r = dm_get_device(ti, origin_path, 0, ti->len, origin_mode, &s->origin);
if (r) {
ti->error = "Cannot get origin device";
goto bad_origin;
}
- s->store = store;
+ s->ti = ti;
s->valid = 1;
s->active = 0;
+ s->suspended = 0;
atomic_set(&s->pending_exceptions_count, 0);
init_rwsem(&s->lock);
+ INIT_LIST_HEAD(&s->list);
spin_lock_init(&s->pe_lock);
+ s->state_bits = 0;
+ s->merge_failed = 0;
+ s->first_merging_chunk = 0;
+ s->num_merging_chunks = 0;
+ bio_list_init(&s->bios_queued_during_merge);
/* Allocate hash table for COW data */
if (init_hash_tables(s)) {
@@ -659,39 +1150,55 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
spin_lock_init(&s->tracked_chunk_lock);
- /* Metadata must only be loaded into one table at once */
+ bio_list_init(&s->queued_bios);
+ INIT_WORK(&s->queued_bios_work, flush_queued_bios);
+
+ ti->private = s;
+ ti->num_flush_requests = num_flush_requests;
+
+ /* Add snapshot to the list of snapshots for this origin */
+ /* Exceptions aren't triggered till snapshot_resume() is called */
+ r = register_snapshot(s);
+ if (r == -ENOMEM) {
+ ti->error = "Snapshot origin struct allocation failed";
+ goto bad_load_and_register;
+ } else if (r < 0) {
+ /* invalid handover, register_snapshot has set ti->error */
+ goto bad_load_and_register;
+ }
+
+ /*
+ * Metadata must only be loaded into one table at once, so skip this
+ * if metadata will be handed over during resume.
+ * Chunk size will be set during the handover - set it to zero to
+ * ensure it's ignored.
+ */
+ if (r > 0) {
+ s->store->chunk_size = 0;
+ return 0;
+ }
+
r = s->store->type->read_metadata(s->store, dm_add_exception,
(void *)s);
if (r < 0) {
ti->error = "Failed to read snapshot metadata";
- goto bad_load_and_register;
+ goto bad_read_metadata;
} else if (r > 0) {
s->valid = 0;
DMWARN("Snapshot is marked invalid.");
}
- bio_list_init(&s->queued_bios);
- INIT_WORK(&s->queued_bios_work, flush_queued_bios);
-
if (!s->store->chunk_size) {
ti->error = "Chunk size not set";
- goto bad_load_and_register;
- }
-
- /* Add snapshot to the list of snapshots for this origin */
- /* Exceptions aren't triggered till snapshot_resume() is called */
- if (register_snapshot(s)) {
- r = -EINVAL;
- ti->error = "Cannot register snapshot origin";
- goto bad_load_and_register;
+ goto bad_read_metadata;
}
-
- ti->private = s;
ti->split_io = s->store->chunk_size;
- ti->num_flush_requests = 1;
return 0;
+bad_read_metadata:
+ unregister_snapshot(s);
+
bad_load_and_register:
mempool_destroy(s->tracked_chunk_pool);
@@ -702,19 +1209,22 @@ bad_pending_pool:
dm_kcopyd_client_destroy(s->kcopyd_client);
bad_kcopyd:
- exit_exception_table(&s->pending, pending_cache);
- exit_exception_table(&s->complete, exception_cache);
+ dm_exception_table_exit(&s->pending, pending_cache);
+ dm_exception_table_exit(&s->complete, exception_cache);
bad_hash_tables:
dm_put_device(ti, s->origin);
bad_origin:
- kfree(s);
+ dm_exception_store_destroy(s->store);
-bad_snap:
- dm_exception_store_destroy(store);
+bad_store:
+ dm_put_device(ti, s->cow);
+
+bad_cow:
+ kfree(s);
-bad_args:
+bad:
return r;
}
@@ -723,8 +1233,39 @@ static void __free_exceptions(struct dm_snapshot *s)
dm_kcopyd_client_destroy(s->kcopyd_client);
s->kcopyd_client = NULL;
- exit_exception_table(&s->pending, pending_cache);
- exit_exception_table(&s->complete, exception_cache);
+ dm_exception_table_exit(&s->pending, pending_cache);
+ dm_exception_table_exit(&s->complete, exception_cache);
+}
+
+static void __handover_exceptions(struct dm_snapshot *snap_src,
+ struct dm_snapshot *snap_dest)
+{
+ union {
+ struct dm_exception_table table_swap;
+ struct dm_exception_store *store_swap;
+ } u;
+
+ /*
+ * Swap all snapshot context information between the two instances.
+ */
+ u.table_swap = snap_dest->complete;
+ snap_dest->complete = snap_src->complete;
+ snap_src->complete = u.table_swap;
+
+ u.store_swap = snap_dest->store;
+ snap_dest->store = snap_src->store;
+ snap_src->store = u.store_swap;
+
+ snap_dest->store->snap = snap_dest;
+ snap_src->store->snap = snap_src;
+
+ snap_dest->ti->split_io = snap_dest->store->chunk_size;
+ snap_dest->valid = snap_src->valid;
+
+ /*
+ * Set source invalid to ensure it receives no further I/O.
+ */
+ snap_src->valid = 0;
}
static void snapshot_dtr(struct dm_target *ti)
@@ -733,9 +1274,24 @@ static void snapshot_dtr(struct dm_target *ti)
int i;
#endif
struct dm_snapshot *s = ti->private;
+ struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
flush_workqueue(ksnapd);
+ down_read(&_origins_lock);
+ /* Check whether exception handover must be cancelled */
+ (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
+ if (snap_src && snap_dest && (s == snap_src)) {
+ down_write(&snap_dest->lock);
+ snap_dest->valid = 0;
+ up_write(&snap_dest->lock);
+ DMERR("Cancelling snapshot handover.");
+ }
+ up_read(&_origins_lock);
+
+ if (dm_target_is_snapshot_merge(ti))
+ stop_merge(s);
+
/* Prevent further origin writes from using this snapshot. */
/* After this returns there can be no new kcopyd jobs. */
unregister_snapshot(s);
@@ -763,6 +1319,8 @@ static void snapshot_dtr(struct dm_target *ti)
dm_exception_store_destroy(s->store);
+ dm_put_device(ti, s->cow);
+
kfree(s);
}
@@ -795,6 +1353,26 @@ static void flush_queued_bios(struct work_struct *work)
flush_bios(queued_bios);
}
+static int do_origin(struct dm_dev *origin, struct bio *bio);
+
+/*
+ * Flush a list of buffers.
+ */
+static void retry_origin_bios(struct dm_snapshot *s, struct bio *bio)
+{
+ struct bio *n;
+ int r;
+
+ while (bio) {
+ n = bio->bi_next;
+ bio->bi_next = NULL;
+ r = do_origin(s->origin, bio);
+ if (r == DM_MAPIO_REMAPPED)
+ generic_make_request(bio);
+ bio = n;
+ }
+}
+
/*
* Error a list of buffers.
*/
@@ -825,45 +1403,12 @@ static void __invalidate_snapshot(struct dm_snapshot *s, int err)
s->valid = 0;
- dm_table_event(s->store->ti->table);
-}
-
-static void get_pending_exception(struct dm_snap_pending_exception *pe)
-{
- atomic_inc(&pe->ref_count);
-}
-
-static struct bio *put_pending_exception(struct dm_snap_pending_exception *pe)
-{
- struct dm_snap_pending_exception *primary_pe;
- struct bio *origin_bios = NULL;
-
- primary_pe = pe->primary_pe;
-
- /*
- * If this pe is involved in a write to the origin and
- * it is the last sibling to complete then release
- * the bios for the original write to the origin.
- */
- if (primary_pe &&
- atomic_dec_and_test(&primary_pe->ref_count)) {
- origin_bios = bio_list_get(&primary_pe->origin_bios);
- free_pending_exception(primary_pe);
- }
-
- /*
- * Free the pe if it's not linked to an origin write or if
- * it's not itself a primary pe.
- */
- if (!primary_pe || primary_pe != pe)
- free_pending_exception(pe);
-
- return origin_bios;
+ dm_table_event(s->ti->table);
}
static void pending_complete(struct dm_snap_pending_exception *pe, int success)
{
- struct dm_snap_exception *e;
+ struct dm_exception *e;
struct dm_snapshot *s = pe->snap;
struct bio *origin_bios = NULL;
struct bio *snapshot_bios = NULL;
@@ -877,7 +1422,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
goto out;
}
- e = alloc_exception();
+ e = alloc_completed_exception();
if (!e) {
down_write(&s->lock);
__invalidate_snapshot(s, -ENOMEM);
@@ -888,28 +1433,27 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
down_write(&s->lock);
if (!s->valid) {
- free_exception(e);
+ free_completed_exception(e);
error = 1;
goto out;
}
- /*
- * Check for conflicting reads. This is extremely improbable,
- * so msleep(1) is sufficient and there is no need for a wait queue.
- */
- while (__chunk_is_tracked(s, pe->e.old_chunk))
- msleep(1);
+ /* Check for conflicting reads */
+ __check_for_conflicting_io(s, pe->e.old_chunk);
/*
* Add a proper exception, and remove the
* in-flight exception from the list.
*/
- insert_completed_exception(s, e);
+ dm_insert_exception(&s->complete, e);
out:
- remove_exception(&pe->e);
+ dm_remove_exception(&pe->e);
snapshot_bios = bio_list_get(&pe->snapshot_bios);
- origin_bios = put_pending_exception(pe);
+ origin_bios = bio_list_get(&pe->origin_bios);
+ free_pending_exception(pe);
+
+ increment_pending_exceptions_done_count();
up_write(&s->lock);
@@ -919,7 +1463,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
else
flush_bios(snapshot_bios);
- flush_bios(origin_bios);
+ retry_origin_bios(s, origin_bios);
}
static void commit_callback(void *context, int success)
@@ -963,7 +1507,7 @@ static void start_copy(struct dm_snap_pending_exception *pe)
src.sector = chunk_to_sector(s->store, pe->e.old_chunk);
src.count = min((sector_t)s->store->chunk_size, dev_size - src.sector);
- dest.bdev = s->store->cow->bdev;
+ dest.bdev = s->cow->bdev;
dest.sector = chunk_to_sector(s->store, pe->e.new_chunk);
dest.count = src.count;
@@ -975,7 +1519,7 @@ static void start_copy(struct dm_snap_pending_exception *pe)
static struct dm_snap_pending_exception *
__lookup_pending_exception(struct dm_snapshot *s, chunk_t chunk)
{
- struct dm_snap_exception *e = lookup_exception(&s->pending, chunk);
+ struct dm_exception *e = dm_lookup_exception(&s->pending, chunk);
if (!e)
return NULL;
@@ -1006,8 +1550,6 @@ __find_pending_exception(struct dm_snapshot *s,
pe->e.old_chunk = chunk;
bio_list_init(&pe->origin_bios);
bio_list_init(&pe->snapshot_bios);
- pe->primary_pe = NULL;
- atomic_set(&pe->ref_count, 0);
pe->started = 0;
if (s->store->type->prepare_exception(s->store, &pe->e)) {
@@ -1015,16 +1557,15 @@ __find_pending_exception(struct dm_snapshot *s,
return NULL;
}
- get_pending_exception(pe);
- insert_exception(&s->pending, &pe->e);
+ dm_insert_exception(&s->pending, &pe->e);
return pe;
}
-static void remap_exception(struct dm_snapshot *s, struct dm_snap_exception *e,
+static void remap_exception(struct dm_snapshot *s, struct dm_exception *e,
struct bio *bio, chunk_t chunk)
{
- bio->bi_bdev = s->store->cow->bdev;
+ bio->bi_bdev = s->cow->bdev;
bio->bi_sector = chunk_to_sector(s->store,
dm_chunk_number(e->new_chunk) +
(chunk - e->old_chunk)) +
@@ -1035,14 +1576,14 @@ static void remap_exception(struct dm_snapshot *s, struct dm_snap_exception *e,
static int snapshot_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
- struct dm_snap_exception *e;
+ struct dm_exception *e;
struct dm_snapshot *s = ti->private;
int r = DM_MAPIO_REMAPPED;
chunk_t chunk;
struct dm_snap_pending_exception *pe = NULL;
if (unlikely(bio_empty_barrier(bio))) {
- bio->bi_bdev = s->store->cow->bdev;
+ bio->bi_bdev = s->cow->bdev;
return DM_MAPIO_REMAPPED;
}
@@ -1063,7 +1604,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
}
/* If the block is already remapped - use that, else remap it */
- e = lookup_exception(&s->complete, chunk);
+ e = dm_lookup_exception(&s->complete, chunk);
if (e) {
remap_exception(s, e, bio, chunk);
goto out_unlock;
@@ -1087,7 +1628,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
goto out_unlock;
}
- e = lookup_exception(&s->complete, chunk);
+ e = dm_lookup_exception(&s->complete, chunk);
if (e) {
free_pending_exception(pe);
remap_exception(s, e, bio, chunk);
@@ -1125,6 +1666,78 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
return r;
}
+/*
+ * A snapshot-merge target behaves like a combination of a snapshot
+ * target and a snapshot-origin target. It only generates new
+ * exceptions in other snapshots and not in the one that is being
+ * merged.
+ *
+ * For each chunk, if there is an existing exception, it is used to
+ * redirect I/O to the cow device. Otherwise I/O is sent to the origin,
+ * which in turn might generate exceptions in other snapshots.
+ * If merging is currently taking place on the chunk in question, the
+ * I/O is deferred by adding it to s->bios_queued_during_merge.
+ */
+static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ struct dm_exception *e;
+ struct dm_snapshot *s = ti->private;
+ int r = DM_MAPIO_REMAPPED;
+ chunk_t chunk;
+
+ if (unlikely(bio_empty_barrier(bio))) {
+ if (!map_context->flush_request)
+ bio->bi_bdev = s->origin->bdev;
+ else
+ bio->bi_bdev = s->cow->bdev;
+ map_context->ptr = NULL;
+ return DM_MAPIO_REMAPPED;
+ }
+
+ chunk = sector_to_chunk(s->store, bio->bi_sector);
+
+ down_write(&s->lock);
+
+ /* Full merging snapshots are redirected to the origin */
+ if (!s->valid)
+ goto redirect_to_origin;
+
+ /* If the block is already remapped - use that */
+ e = dm_lookup_exception(&s->complete, chunk);
+ if (e) {
+ /* Queue writes overlapping with chunks being merged */
+ if (bio_rw(bio) == WRITE &&
+ chunk >= s->first_merging_chunk &&
+ chunk < (s->first_merging_chunk +
+ s->num_merging_chunks)) {
+ bio->bi_bdev = s->origin->bdev;
+ bio_list_add(&s->bios_queued_during_merge, bio);
+ r = DM_MAPIO_SUBMITTED;
+ goto out_unlock;
+ }
+
+ remap_exception(s, e, bio, chunk);
+
+ if (bio_rw(bio) == WRITE)
+ map_context->ptr = track_chunk(s, chunk);
+ goto out_unlock;
+ }
+
+redirect_to_origin:
+ bio->bi_bdev = s->origin->bdev;
+
+ if (bio_rw(bio) == WRITE) {
+ up_write(&s->lock);
+ return do_origin(s->origin, bio);
+ }
+
+out_unlock:
+ up_write(&s->lock);
+
+ return r;
+}
+
static int snapshot_end_io(struct dm_target *ti, struct bio *bio,
int error, union map_info *map_context)
{
@@ -1137,40 +1750,135 @@ static int snapshot_end_io(struct dm_target *ti, struct bio *bio,
return 0;
}
+static void snapshot_merge_presuspend(struct dm_target *ti)
+{
+ struct dm_snapshot *s = ti->private;
+
+ stop_merge(s);
+}
+
+static void snapshot_postsuspend(struct dm_target *ti)
+{
+ struct dm_snapshot *s = ti->private;
+
+ down_write(&s->lock);
+ s->suspended = 1;
+ up_write(&s->lock);
+}
+
+static int snapshot_preresume(struct dm_target *ti)
+{
+ int r = 0;
+ struct dm_snapshot *s = ti->private;
+ struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
+
+ down_read(&_origins_lock);
+ (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
+ if (snap_src && snap_dest) {
+ down_read(&snap_src->lock);
+ if (s == snap_src) {
+ DMERR("Unable to resume snapshot source until "
+ "handover completes.");
+ r = -EINVAL;
+ } else if (!snap_src->suspended) {
+ DMERR("Unable to perform snapshot handover until "
+ "source is suspended.");
+ r = -EINVAL;
+ }
+ up_read(&snap_src->lock);
+ }
+ up_read(&_origins_lock);
+
+ return r;
+}
+
static void snapshot_resume(struct dm_target *ti)
{
struct dm_snapshot *s = ti->private;
+ struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
+
+ down_read(&_origins_lock);
+ (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
+ if (snap_src && snap_dest) {
+ down_write(&snap_src->lock);
+ down_write_nested(&snap_dest->lock, SINGLE_DEPTH_NESTING);
+ __handover_exceptions(snap_src, snap_dest);
+ up_write(&snap_dest->lock);
+ up_write(&snap_src->lock);
+ }
+ up_read(&_origins_lock);
+
+ /* Now we have correct chunk size, reregister */
+ reregister_snapshot(s);
down_write(&s->lock);
s->active = 1;
+ s->suspended = 0;
up_write(&s->lock);
}
+static sector_t get_origin_minimum_chunksize(struct block_device *bdev)
+{
+ sector_t min_chunksize;
+
+ down_read(&_origins_lock);
+ min_chunksize = __minimum_chunk_size(__lookup_origin(bdev));
+ up_read(&_origins_lock);
+
+ return min_chunksize;
+}
+
+static void snapshot_merge_resume(struct dm_target *ti)
+{
+ struct dm_snapshot *s = ti->private;
+
+ /*
+ * Handover exceptions from existing snapshot.
+ */
+ snapshot_resume(ti);
+
+ /*
+ * snapshot-merge acts as an origin, so set ti->split_io
+ */
+ ti->split_io = get_origin_minimum_chunksize(s->origin->bdev);
+
+ start_merge(s);
+}
+
static int snapshot_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen)
{
unsigned sz = 0;
struct dm_snapshot *snap = ti->private;
- down_write(&snap->lock);
-
switch (type) {
case STATUSTYPE_INFO:
+
+ down_write(&snap->lock);
+
if (!snap->valid)
DMEMIT("Invalid");
+ else if (snap->merge_failed)
+ DMEMIT("Merge failed");
else {
- if (snap->store->type->fraction_full) {
- sector_t numerator, denominator;
- snap->store->type->fraction_full(snap->store,
- &numerator,
- &denominator);
- DMEMIT("%llu/%llu",
- (unsigned long long)numerator,
- (unsigned long long)denominator);
+ if (snap->store->type->usage) {
+ sector_t total_sectors, sectors_allocated,
+ metadata_sectors;
+ snap->store->type->usage(snap->store,
+ &total_sectors,
+ &sectors_allocated,
+ &metadata_sectors);
+ DMEMIT("%llu/%llu %llu",
+ (unsigned long long)sectors_allocated,
+ (unsigned long long)total_sectors,
+ (unsigned long long)metadata_sectors);
}
else
DMEMIT("Unknown");
}
+
+ up_write(&snap->lock);
+
break;
case STATUSTYPE_TABLE:
@@ -1179,14 +1887,12 @@ static int snapshot_status(struct dm_target *ti, status_type_t type,
* to make private copies if the output is to
* make sense.
*/
- DMEMIT("%s", snap->origin->name);
+ DMEMIT("%s %s", snap->origin->name, snap->cow->name);
snap->store->type->status(snap->store, type, result + sz,
maxlen - sz);
break;
}
- up_write(&snap->lock);
-
return 0;
}
@@ -1202,17 +1908,36 @@ static int snapshot_iterate_devices(struct dm_target *ti,
/*-----------------------------------------------------------------
* Origin methods
*---------------------------------------------------------------*/
-static int __origin_write(struct list_head *snapshots, struct bio *bio)
+
+/*
+ * If no exceptions need creating, DM_MAPIO_REMAPPED is returned and any
+ * supplied bio was ignored. The caller may submit it immediately.
+ * (No remapping actually occurs as the origin is always a direct linear
+ * map.)
+ *
+ * If further exceptions are required, DM_MAPIO_SUBMITTED is returned
+ * and any supplied bio is added to a list to be submitted once all
+ * the necessary exceptions exist.
+ */
+static int __origin_write(struct list_head *snapshots, sector_t sector,
+ struct bio *bio)
{
- int r = DM_MAPIO_REMAPPED, first = 0;
+ int r = DM_MAPIO_REMAPPED;
struct dm_snapshot *snap;
- struct dm_snap_exception *e;
- struct dm_snap_pending_exception *pe, *next_pe, *primary_pe = NULL;
+ struct dm_exception *e;
+ struct dm_snap_pending_exception *pe;
+ struct dm_snap_pending_exception *pe_to_start_now = NULL;
+ struct dm_snap_pending_exception *pe_to_start_last = NULL;
chunk_t chunk;
- LIST_HEAD(pe_queue);
/* Do all the snapshots on this origin */
list_for_each_entry (snap, snapshots, list) {
+ /*
+ * Don't make new exceptions in a merging snapshot
+ * because it has effectively been deleted
+ */
+ if (dm_target_is_snapshot_merge(snap->ti))
+ continue;
down_write(&snap->lock);
@@ -1221,24 +1946,21 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
goto next_snapshot;
/* Nothing to do if writing beyond end of snapshot */
- if (bio->bi_sector >= dm_table_get_size(snap->store->ti->table))
+ if (sector >= dm_table_get_size(snap->ti->table))
goto next_snapshot;
/*
* Remember, different snapshots can have
* different chunk sizes.
*/
- chunk = sector_to_chunk(snap->store, bio->bi_sector);
+ chunk = sector_to_chunk(snap->store, sector);
/*
* Check exception table to see if block
* is already remapped in this snapshot
* and trigger an exception if not.
- *
- * ref_count is initialised to 1 so pending_complete()
- * won't destroy the primary_pe while we're inside this loop.
*/
- e = lookup_exception(&snap->complete, chunk);
+ e = dm_lookup_exception(&snap->complete, chunk);
if (e)
goto next_snapshot;
@@ -1253,7 +1975,7 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
goto next_snapshot;
}
- e = lookup_exception(&snap->complete, chunk);
+ e = dm_lookup_exception(&snap->complete, chunk);
if (e) {
free_pending_exception(pe);
goto next_snapshot;
@@ -1266,59 +1988,43 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
}
}
- if (!primary_pe) {
- /*
- * Either every pe here has same
- * primary_pe or none has one yet.
- */
- if (pe->primary_pe)
- primary_pe = pe->primary_pe;
- else {
- primary_pe = pe;
- first = 1;
- }
-
- bio_list_add(&primary_pe->origin_bios, bio);
+ r = DM_MAPIO_SUBMITTED;
- r = DM_MAPIO_SUBMITTED;
- }
+ /*
+ * If an origin bio was supplied, queue it to wait for the
+ * completion of this exception, and start this one last,
+ * at the end of the function.
+ */
+ if (bio) {
+ bio_list_add(&pe->origin_bios, bio);
+ bio = NULL;
- if (!pe->primary_pe) {
- pe->primary_pe = primary_pe;
- get_pending_exception(primary_pe);
+ if (!pe->started) {
+ pe->started = 1;
+ pe_to_start_last = pe;
+ }
}
if (!pe->started) {
pe->started = 1;
- list_add_tail(&pe->list, &pe_queue);
+ pe_to_start_now = pe;
}
next_snapshot:
up_write(&snap->lock);
- }
- if (!primary_pe)
- return r;
-
- /*
- * If this is the first time we're processing this chunk and
- * ref_count is now 1 it means all the pending exceptions
- * got completed while we were in the loop above, so it falls to
- * us here to remove the primary_pe and submit any origin_bios.
- */
-
- if (first && atomic_dec_and_test(&primary_pe->ref_count)) {
- flush_bios(bio_list_get(&primary_pe->origin_bios));
- free_pending_exception(primary_pe);
- /* If we got here, pe_queue is necessarily empty. */
- return r;
+ if (pe_to_start_now) {
+ start_copy(pe_to_start_now);
+ pe_to_start_now = NULL;
+ }
}
/*
- * Now that we have a complete pe list we can start the copying.
+ * Submit the exception against which the bio is queued last,
+ * to give the other exceptions a head start.
*/
- list_for_each_entry_safe(pe, next_pe, &pe_queue, list)
- start_copy(pe);
+ if (pe_to_start_last)
+ start_copy(pe_to_start_last);
return r;
}
@@ -1334,13 +2040,48 @@ static int do_origin(struct dm_dev *origin, struct bio *bio)
down_read(&_origins_lock);
o = __lookup_origin(origin->bdev);
if (o)
- r = __origin_write(&o->snapshots, bio);
+ r = __origin_write(&o->snapshots, bio->bi_sector, bio);
up_read(&_origins_lock);
return r;
}
/*
+ * Trigger exceptions in all non-merging snapshots.
+ *
+ * The chunk size of the merging snapshot may be larger than the chunk
+ * size of some other snapshot so we may need to reallocate multiple
+ * chunks in other snapshots.
+ *
+ * We scan all the overlapping exceptions in the other snapshots.
+ * Returns 1 if anything was reallocated and must be waited for,
+ * otherwise returns 0.
+ *
+ * size must be a multiple of merging_snap's chunk_size.
+ */
+static int origin_write_extent(struct dm_snapshot *merging_snap,
+ sector_t sector, unsigned size)
+{
+ int must_wait = 0;
+ sector_t n;
+ struct origin *o;
+
+ /*
+ * The origin's __minimum_chunk_size() got stored in split_io
+ * by snapshot_merge_resume().
+ */
+ down_read(&_origins_lock);
+ o = __lookup_origin(merging_snap->origin->bdev);
+ for (n = 0; n < size; n += merging_snap->ti->split_io)
+ if (__origin_write(&o->snapshots, sector + n, NULL) ==
+ DM_MAPIO_SUBMITTED)
+ must_wait = 1;
+ up_read(&_origins_lock);
+
+ return must_wait;
+}
+
+/*
* Origin: maps a linear range of a device, with hooks for snapshotting.
*/
@@ -1391,8 +2132,6 @@ static int origin_map(struct dm_target *ti, struct bio *bio,
return (bio_rw(bio) == WRITE) ? do_origin(dev, bio) : DM_MAPIO_REMAPPED;
}
-#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r))
-
/*
* Set the target "split_io" field to the minimum of all the snapshots'
* chunk sizes.
@@ -1400,19 +2139,8 @@ static int origin_map(struct dm_target *ti, struct bio *bio,
static void origin_resume(struct dm_target *ti)
{
struct dm_dev *dev = ti->private;
- struct dm_snapshot *snap;
- struct origin *o;
- unsigned chunk_size = 0;
-
- down_read(&_origins_lock);
- o = __lookup_origin(dev->bdev);
- if (o)
- list_for_each_entry (snap, &o->snapshots, list)
- chunk_size = min_not_zero(chunk_size,
- snap->store->chunk_size);
- up_read(&_origins_lock);
- ti->split_io = chunk_size;
+ ti->split_io = get_origin_minimum_chunksize(dev->bdev);
}
static int origin_status(struct dm_target *ti, status_type_t type, char *result,
@@ -1455,17 +2183,35 @@ static struct target_type origin_target = {
static struct target_type snapshot_target = {
.name = "snapshot",
- .version = {1, 7, 0},
+ .version = {1, 9, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
.map = snapshot_map,
.end_io = snapshot_end_io,
+ .postsuspend = snapshot_postsuspend,
+ .preresume = snapshot_preresume,
.resume = snapshot_resume,
.status = snapshot_status,
.iterate_devices = snapshot_iterate_devices,
};
+static struct target_type merge_target = {
+ .name = dm_snapshot_merge_target_name,
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = snapshot_ctr,
+ .dtr = snapshot_dtr,
+ .map = snapshot_merge_map,
+ .end_io = snapshot_end_io,
+ .presuspend = snapshot_merge_presuspend,
+ .postsuspend = snapshot_postsuspend,
+ .preresume = snapshot_preresume,
+ .resume = snapshot_merge_resume,
+ .status = snapshot_status,
+ .iterate_devices = snapshot_iterate_devices,
+};
+
static int __init dm_snapshot_init(void)
{
int r;
@@ -1477,7 +2223,7 @@ static int __init dm_snapshot_init(void)
}
r = dm_register_target(&snapshot_target);
- if (r) {
+ if (r < 0) {
DMERR("snapshot target register failed %d", r);
goto bad_register_snapshot_target;
}
@@ -1485,34 +2231,40 @@ static int __init dm_snapshot_init(void)
r = dm_register_target(&origin_target);
if (r < 0) {
DMERR("Origin target register failed %d", r);
- goto bad1;
+ goto bad_register_origin_target;
+ }
+
+ r = dm_register_target(&merge_target);
+ if (r < 0) {
+ DMERR("Merge target register failed %d", r);
+ goto bad_register_merge_target;
}
r = init_origin_hash();
if (r) {
DMERR("init_origin_hash failed.");
- goto bad2;
+ goto bad_origin_hash;
}
- exception_cache = KMEM_CACHE(dm_snap_exception, 0);
+ exception_cache = KMEM_CACHE(dm_exception, 0);
if (!exception_cache) {
DMERR("Couldn't create exception cache.");
r = -ENOMEM;
- goto bad3;
+ goto bad_exception_cache;
}
pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0);
if (!pending_cache) {
DMERR("Couldn't create pending cache.");
r = -ENOMEM;
- goto bad4;
+ goto bad_pending_cache;
}
tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0);
if (!tracked_chunk_cache) {
DMERR("Couldn't create cache to track chunks in use.");
r = -ENOMEM;
- goto bad5;
+ goto bad_tracked_chunk_cache;
}
ksnapd = create_singlethread_workqueue("ksnapd");
@@ -1526,19 +2278,21 @@ static int __init dm_snapshot_init(void)
bad_pending_pool:
kmem_cache_destroy(tracked_chunk_cache);
-bad5:
+bad_tracked_chunk_cache:
kmem_cache_destroy(pending_cache);
-bad4:
+bad_pending_cache:
kmem_cache_destroy(exception_cache);
-bad3:
+bad_exception_cache:
exit_origin_hash();
-bad2:
+bad_origin_hash:
+ dm_unregister_target(&merge_target);
+bad_register_merge_target:
dm_unregister_target(&origin_target);
-bad1:
+bad_register_origin_target:
dm_unregister_target(&snapshot_target);
-
bad_register_snapshot_target:
dm_exception_store_exit();
+
return r;
}
@@ -1548,6 +2302,7 @@ static void __exit dm_snapshot_exit(void)
dm_unregister_target(&snapshot_target);
dm_unregister_target(&origin_target);
+ dm_unregister_target(&merge_target);
exit_origin_hash();
kmem_cache_destroy(pending_cache);
diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c
index 4b045903a4e2..f53392df7b97 100644
--- a/drivers/md/dm-sysfs.c
+++ b/drivers/md/dm-sysfs.c
@@ -59,7 +59,7 @@ static ssize_t dm_attr_uuid_show(struct mapped_device *md, char *buf)
static ssize_t dm_attr_suspended_show(struct mapped_device *md, char *buf)
{
- sprintf(buf, "%d\n", dm_suspended(md));
+ sprintf(buf, "%d\n", dm_suspended_md(md));
return strlen(buf);
}
@@ -80,12 +80,20 @@ static struct sysfs_ops dm_sysfs_ops = {
};
/*
+ * The sysfs structure is embedded in md struct, nothing to do here
+ */
+static void dm_sysfs_release(struct kobject *kobj)
+{
+}
+
+/*
* dm kobject is embedded in mapped_device structure
* no need to define release function here
*/
static struct kobj_type dm_ktype = {
.sysfs_ops = &dm_sysfs_ops,
.default_attrs = dm_attrs,
+ .release = dm_sysfs_release
};
/*
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 1a6cb3c7822e..be625475cf6d 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -12,6 +12,7 @@
#include <linux/blkdev.h>
#include <linux/namei.h>
#include <linux/ctype.h>
+#include <linux/string.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
@@ -237,6 +238,9 @@ void dm_table_destroy(struct dm_table *t)
{
unsigned int i;
+ if (!t)
+ return;
+
while (atomic_read(&t->holders))
msleep(1);
smp_mb();
@@ -600,11 +604,8 @@ int dm_split_args(int *argc, char ***argvp, char *input)
return -ENOMEM;
while (1) {
- start = end;
-
/* Skip whitespace */
- while (*start && isspace(*start))
- start++;
+ start = skip_spaces(end);
if (!*start)
break; /* success, we hit the end */
diff --git a/drivers/md/dm-uevent.c b/drivers/md/dm-uevent.c
index 6f65883aef12..c7c555a8c7b2 100644
--- a/drivers/md/dm-uevent.c
+++ b/drivers/md/dm-uevent.c
@@ -139,14 +139,13 @@ void dm_send_uevents(struct list_head *events, struct kobject *kobj)
list_del_init(&event->elist);
/*
- * Need to call dm_copy_name_and_uuid from here for now.
- * Context of previous var adds and locking used for
- * hash_cell not compatable.
+ * When a device is being removed this copy fails and we
+ * discard these unsent events.
*/
if (dm_copy_name_and_uuid(event->md, event->name,
event->uuid)) {
- DMERR("%s: dm_copy_name_and_uuid() failed",
- __func__);
+ DMINFO("%s: skipping sending uevent for lost device",
+ __func__);
goto uevent_free;
}
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 724efc63904d..3167480b532c 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -143,9 +143,19 @@ struct mapped_device {
int barrier_error;
/*
+ * Protect barrier_error from concurrent endio processing
+ * in request-based dm.
+ */
+ spinlock_t barrier_error_lock;
+
+ /*
* Processing queue (flush/barriers)
*/
struct workqueue_struct *wq;
+ struct work_struct barrier_work;
+
+ /* A pointer to the currently processing pre/post flush request */
+ struct request *flush_request;
/*
* The current mapping.
@@ -178,9 +188,6 @@ struct mapped_device {
/* forced geometry settings */
struct hd_geometry geometry;
- /* marker of flush suspend for request-based dm */
- struct request suspend_rq;
-
/* For saving the address of __make_request for request based dm */
make_request_fn *saved_make_request_fn;
@@ -275,6 +282,7 @@ static int (*_inits[])(void) __initdata = {
dm_target_init,
dm_linear_init,
dm_stripe_init,
+ dm_io_init,
dm_kcopyd_init,
dm_interface_init,
};
@@ -284,6 +292,7 @@ static void (*_exits[])(void) = {
dm_target_exit,
dm_linear_exit,
dm_stripe_exit,
+ dm_io_exit,
dm_kcopyd_exit,
dm_interface_exit,
};
@@ -320,6 +329,11 @@ static void __exit dm_exit(void)
/*
* Block device functions
*/
+int dm_deleting_md(struct mapped_device *md)
+{
+ return test_bit(DMF_DELETING, &md->flags);
+}
+
static int dm_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mapped_device *md;
@@ -331,7 +345,7 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode)
goto out;
if (test_bit(DMF_FREEING, &md->flags) ||
- test_bit(DMF_DELETING, &md->flags)) {
+ dm_deleting_md(md)) {
md = NULL;
goto out;
}
@@ -388,7 +402,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct mapped_device *md = bdev->bd_disk->private_data;
- struct dm_table *map = dm_get_table(md);
+ struct dm_table *map = dm_get_live_table(md);
struct dm_target *tgt;
int r = -ENOTTY;
@@ -401,7 +415,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
tgt = dm_table_get_target(map, 0);
- if (dm_suspended(md)) {
+ if (dm_suspended_md(md)) {
r = -EAGAIN;
goto out;
}
@@ -430,9 +444,10 @@ static void free_tio(struct mapped_device *md, struct dm_target_io *tio)
mempool_free(tio, md->tio_pool);
}
-static struct dm_rq_target_io *alloc_rq_tio(struct mapped_device *md)
+static struct dm_rq_target_io *alloc_rq_tio(struct mapped_device *md,
+ gfp_t gfp_mask)
{
- return mempool_alloc(md->tio_pool, GFP_ATOMIC);
+ return mempool_alloc(md->tio_pool, gfp_mask);
}
static void free_rq_tio(struct dm_rq_target_io *tio)
@@ -450,6 +465,12 @@ static void free_bio_info(struct dm_rq_clone_bio_info *info)
mempool_free(info, info->tio->md->io_pool);
}
+static int md_in_flight(struct mapped_device *md)
+{
+ return atomic_read(&md->pending[READ]) +
+ atomic_read(&md->pending[WRITE]);
+}
+
static void start_io_acct(struct dm_io *io)
{
struct mapped_device *md = io->md;
@@ -512,7 +533,7 @@ static void queue_io(struct mapped_device *md, struct bio *bio)
* function to access the md->map field, and make sure they call
* dm_table_put() when finished.
*/
-struct dm_table *dm_get_table(struct mapped_device *md)
+struct dm_table *dm_get_live_table(struct mapped_device *md)
{
struct dm_table *t;
unsigned long flags;
@@ -716,28 +737,38 @@ static void end_clone_bio(struct bio *clone, int error)
blk_update_request(tio->orig, 0, nr_bytes);
}
+static void store_barrier_error(struct mapped_device *md, int error)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&md->barrier_error_lock, flags);
+ /*
+ * Basically, the first error is taken, but:
+ * -EOPNOTSUPP supersedes any I/O error.
+ * Requeue request supersedes any I/O error but -EOPNOTSUPP.
+ */
+ if (!md->barrier_error || error == -EOPNOTSUPP ||
+ (md->barrier_error != -EOPNOTSUPP &&
+ error == DM_ENDIO_REQUEUE))
+ md->barrier_error = error;
+ spin_unlock_irqrestore(&md->barrier_error_lock, flags);
+}
+
/*
* Don't touch any member of the md after calling this function because
* the md may be freed in dm_put() at the end of this function.
* Or do dm_get() before calling this function and dm_put() later.
*/
-static void rq_completed(struct mapped_device *md, int run_queue)
+static void rq_completed(struct mapped_device *md, int rw, int run_queue)
{
- int wakeup_waiters = 0;
- struct request_queue *q = md->queue;
- unsigned long flags;
-
- spin_lock_irqsave(q->queue_lock, flags);
- if (!queue_in_flight(q))
- wakeup_waiters = 1;
- spin_unlock_irqrestore(q->queue_lock, flags);
+ atomic_dec(&md->pending[rw]);
/* nudge anyone waiting on suspend queue */
- if (wakeup_waiters)
+ if (!md_in_flight(md))
wake_up(&md->wait);
if (run_queue)
- blk_run_queue(q);
+ blk_run_queue(md->queue);
/*
* dm_put() must be at the end of this function. See the comment above
@@ -753,6 +784,44 @@ static void free_rq_clone(struct request *clone)
free_rq_tio(tio);
}
+/*
+ * Complete the clone and the original request.
+ * Must be called without queue lock.
+ */
+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);
+ 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) {
+ rq->errors = clone->errors;
+ rq->resid_len = clone->resid_len;
+
+ if (rq->sense)
+ /*
+ * We are using the sense buffer of the original
+ * request.
+ * So setting the length of the sense data is enough.
+ */
+ rq->sense_len = clone->sense_len;
+ }
+
+ free_rq_clone(clone);
+
+ if (unlikely(is_barrier)) {
+ if (unlikely(error))
+ store_barrier_error(md, error);
+ run_queue = 0;
+ } else
+ blk_end_request_all(rq, error);
+
+ rq_completed(md, rw, run_queue);
+}
+
static void dm_unprep_request(struct request *rq)
{
struct request *clone = rq->special;
@@ -768,12 +837,23 @@ static void dm_unprep_request(struct request *rq)
*/
void dm_requeue_unmapped_request(struct request *clone)
{
+ int rw = rq_data_dir(clone);
struct dm_rq_target_io *tio = clone->end_io_data;
struct mapped_device *md = tio->md;
struct request *rq = tio->orig;
struct request_queue *q = rq->q;
unsigned long flags;
+ if (unlikely(blk_barrier_rq(clone))) {
+ /*
+ * Barrier clones share an original request.
+ * Leave it to dm_end_request(), which handles this special
+ * case.
+ */
+ dm_end_request(clone, DM_ENDIO_REQUEUE);
+ return;
+ }
+
dm_unprep_request(rq);
spin_lock_irqsave(q->queue_lock, flags);
@@ -782,7 +862,7 @@ void dm_requeue_unmapped_request(struct request *clone)
blk_requeue_request(q, rq);
spin_unlock_irqrestore(q->queue_lock, flags);
- rq_completed(md, 0);
+ rq_completed(md, rw, 0);
}
EXPORT_SYMBOL_GPL(dm_requeue_unmapped_request);
@@ -815,34 +895,28 @@ static void start_queue(struct request_queue *q)
spin_unlock_irqrestore(q->queue_lock, flags);
}
-/*
- * Complete the clone and the original request.
- * Must be called without queue lock.
- */
-static void dm_end_request(struct request *clone, int error)
+static void dm_done(struct request *clone, int error, bool mapped)
{
+ int r = error;
struct dm_rq_target_io *tio = clone->end_io_data;
- struct mapped_device *md = tio->md;
- struct request *rq = tio->orig;
+ dm_request_endio_fn rq_end_io = tio->ti->type->rq_end_io;
- if (blk_pc_request(rq)) {
- rq->errors = clone->errors;
- rq->resid_len = clone->resid_len;
+ if (mapped && rq_end_io)
+ r = rq_end_io(tio->ti, clone, error, &tio->info);
- if (rq->sense)
- /*
- * We are using the sense buffer of the original
- * request.
- * So setting the length of the sense data is enough.
- */
- rq->sense_len = clone->sense_len;
+ if (r <= 0)
+ /* The target wants to complete the I/O */
+ dm_end_request(clone, r);
+ else if (r == DM_ENDIO_INCOMPLETE)
+ /* The target will handle the I/O */
+ return;
+ else if (r == DM_ENDIO_REQUEUE)
+ /* The target wants to requeue the I/O */
+ dm_requeue_unmapped_request(clone);
+ else {
+ DMWARN("unimplemented target endio return value: %d", r);
+ BUG();
}
-
- free_rq_clone(clone);
-
- blk_end_request_all(rq, error);
-
- rq_completed(md, 1);
}
/*
@@ -850,27 +924,14 @@ static void dm_end_request(struct request *clone, int error)
*/
static void dm_softirq_done(struct request *rq)
{
+ bool mapped = true;
struct request *clone = rq->completion_data;
struct dm_rq_target_io *tio = clone->end_io_data;
- dm_request_endio_fn rq_end_io = tio->ti->type->rq_end_io;
- int error = tio->error;
- if (!(rq->cmd_flags & REQ_FAILED) && rq_end_io)
- error = rq_end_io(tio->ti, clone, error, &tio->info);
+ if (rq->cmd_flags & REQ_FAILED)
+ mapped = false;
- if (error <= 0)
- /* The target wants to complete the I/O */
- dm_end_request(clone, error);
- else if (error == DM_ENDIO_INCOMPLETE)
- /* The target will handle the I/O */
- return;
- else if (error == DM_ENDIO_REQUEUE)
- /* The target wants to requeue the I/O */
- dm_requeue_unmapped_request(clone);
- else {
- DMWARN("unimplemented target endio return value: %d", error);
- BUG();
- }
+ dm_done(clone, tio->error, mapped);
}
/*
@@ -882,6 +943,19 @@ 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))) {
+ /*
+ * Barrier clones share an original request. So can't use
+ * softirq_done with the original.
+ * Pass the clone to dm_done() directly in this special case.
+ * It is safe (even if clone->q->queue_lock is held here)
+ * because there is no I/O dispatching during the completion
+ * of barrier clone.
+ */
+ dm_done(clone, error, true);
+ return;
+ }
+
tio->error = error;
rq->completion_data = clone;
blk_complete_request(rq);
@@ -898,6 +972,17 @@ 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))) {
+ /*
+ * Barrier clones share an original request.
+ * Leave it to dm_end_request(), which handles this special
+ * case.
+ */
+ BUG_ON(error > 0);
+ dm_end_request(clone, error);
+ return;
+ }
+
rq->cmd_flags |= REQ_FAILED;
dm_complete_request(clone, error);
}
@@ -1214,7 +1299,7 @@ static void __split_and_process_bio(struct mapped_device *md, struct bio *bio)
struct clone_info ci;
int error = 0;
- ci.map = dm_get_table(md);
+ ci.map = dm_get_live_table(md);
if (unlikely(!ci.map)) {
if (!bio_rw_flagged(bio, BIO_RW_BARRIER))
bio_io_error(bio);
@@ -1255,7 +1340,7 @@ static int dm_merge_bvec(struct request_queue *q,
struct bio_vec *biovec)
{
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_table(md);
+ struct dm_table *map = dm_get_live_table(md);
struct dm_target *ti;
sector_t max_sectors;
int max_size = 0;
@@ -1352,11 +1437,6 @@ static int dm_make_request(struct request_queue *q, struct bio *bio)
{
struct mapped_device *md = q->queuedata;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
- bio_endio(bio, -EOPNOTSUPP);
- return 0;
- }
-
return md->saved_make_request_fn(q, bio); /* call __make_request() */
}
@@ -1375,6 +1455,25 @@ 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)
+ return true;
+ else
+ return false;
+}
+
void dm_dispatch_request(struct request *rq)
{
int r;
@@ -1420,25 +1519,54 @@ static int dm_rq_bio_constructor(struct bio *bio, struct bio *bio_orig,
static int setup_clone(struct request *clone, struct request *rq,
struct dm_rq_target_io *tio)
{
- int r = blk_rq_prep_clone(clone, rq, tio->md->bs, GFP_ATOMIC,
- dm_rq_bio_constructor, tio);
+ int r;
- if (r)
- return r;
+ if (dm_rq_is_flush_request(rq)) {
+ blk_rq_init(NULL, clone);
+ clone->cmd_type = REQ_TYPE_FS;
+ clone->cmd_flags |= (REQ_HARDBARRIER | WRITE);
+ } else {
+ r = blk_rq_prep_clone(clone, rq, tio->md->bs, GFP_ATOMIC,
+ dm_rq_bio_constructor, tio);
+ if (r)
+ return r;
+
+ clone->cmd = rq->cmd;
+ clone->cmd_len = rq->cmd_len;
+ clone->sense = rq->sense;
+ clone->buffer = rq->buffer;
+ }
- clone->cmd = rq->cmd;
- clone->cmd_len = rq->cmd_len;
- clone->sense = rq->sense;
- clone->buffer = rq->buffer;
clone->end_io = end_clone_request;
clone->end_io_data = tio;
return 0;
}
-static int dm_rq_flush_suspending(struct mapped_device *md)
+static struct request *clone_rq(struct request *rq, struct mapped_device *md,
+ gfp_t gfp_mask)
{
- return !md->suspend_rq.special;
+ struct request *clone;
+ struct dm_rq_target_io *tio;
+
+ tio = alloc_rq_tio(md, gfp_mask);
+ if (!tio)
+ return NULL;
+
+ tio->md = md;
+ tio->ti = NULL;
+ tio->orig = rq;
+ tio->error = 0;
+ memset(&tio->info, 0, sizeof(tio->info));
+
+ clone = &tio->clone;
+ if (setup_clone(clone, rq, tio)) {
+ /* -ENOMEM */
+ free_rq_tio(tio);
+ return NULL;
+ }
+
+ return clone;
}
/*
@@ -1447,39 +1575,19 @@ static int dm_rq_flush_suspending(struct mapped_device *md)
static int dm_prep_fn(struct request_queue *q, struct request *rq)
{
struct mapped_device *md = q->queuedata;
- struct dm_rq_target_io *tio;
struct request *clone;
- if (unlikely(rq == &md->suspend_rq)) {
- if (dm_rq_flush_suspending(md))
- return BLKPREP_OK;
- else
- /* The flush suspend was interrupted */
- return BLKPREP_KILL;
- }
+ if (unlikely(dm_rq_is_flush_request(rq)))
+ return BLKPREP_OK;
if (unlikely(rq->special)) {
DMWARN("Already has something in rq->special.");
return BLKPREP_KILL;
}
- tio = alloc_rq_tio(md); /* Only one for each original request */
- if (!tio)
- /* -ENOMEM */
- return BLKPREP_DEFER;
-
- tio->md = md;
- tio->ti = NULL;
- tio->orig = rq;
- tio->error = 0;
- memset(&tio->info, 0, sizeof(tio->info));
-
- clone = &tio->clone;
- if (setup_clone(clone, rq, tio)) {
- /* -ENOMEM */
- free_rq_tio(tio);
+ clone = clone_rq(rq, md, GFP_ATOMIC);
+ if (!clone)
return BLKPREP_DEFER;
- }
rq->special = clone;
rq->cmd_flags |= REQ_DONTPREP;
@@ -1487,11 +1595,10 @@ static int dm_prep_fn(struct request_queue *q, struct request *rq)
return BLKPREP_OK;
}
-static void map_request(struct dm_target *ti, struct request *rq,
+static void map_request(struct dm_target *ti, struct request *clone,
struct mapped_device *md)
{
int r;
- struct request *clone = rq->special;
struct dm_rq_target_io *tio = clone->end_io_data;
/*
@@ -1511,6 +1618,8 @@ static void map_request(struct dm_target *ti, struct request *rq,
break;
case DM_MAPIO_REMAPPED:
/* The target has remapped the I/O so dispatch it */
+ trace_block_rq_remap(clone->q, clone, disk_devt(dm_disk(md)),
+ blk_rq_pos(tio->orig));
dm_dispatch_request(clone);
break;
case DM_MAPIO_REQUEUE:
@@ -1536,29 +1645,26 @@ static void map_request(struct dm_target *ti, struct request *rq,
static void dm_request_fn(struct request_queue *q)
{
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_table(md);
+ struct dm_table *map = dm_get_live_table(md);
struct dm_target *ti;
- struct request *rq;
+ struct request *rq, *clone;
/*
- * For noflush suspend, check blk_queue_stopped() to immediately
- * quit I/O dispatching.
+ * For suspend, check blk_queue_stopped() and increment
+ * ->pending within a single queue_lock not to increment the
+ * number of in-flight I/Os after the queue is stopped in
+ * dm_suspend().
*/
while (!blk_queue_plugged(q) && !blk_queue_stopped(q)) {
rq = blk_peek_request(q);
if (!rq)
goto plug_and_out;
- if (unlikely(rq == &md->suspend_rq)) { /* Flush suspend maker */
- if (queue_in_flight(q))
- /* Not quiet yet. Wait more */
- goto plug_and_out;
-
- /* This device should be quiet now */
- __stop_queue(q);
+ if (unlikely(dm_rq_is_flush_request(rq))) {
+ BUG_ON(md->flush_request);
+ md->flush_request = rq;
blk_start_request(rq);
- __blk_end_request_all(rq, 0);
- wake_up(&md->wait);
+ queue_work(md->wq, &md->barrier_work);
goto out;
}
@@ -1567,8 +1673,11 @@ static void dm_request_fn(struct request_queue *q)
goto plug_and_out;
blk_start_request(rq);
+ clone = rq->special;
+ atomic_inc(&md->pending[rq_data_dir(clone)]);
+
spin_unlock(q->queue_lock);
- map_request(ti, rq, md);
+ map_request(ti, clone, md);
spin_lock_irq(q->queue_lock);
}
@@ -1595,7 +1704,7 @@ static int dm_lld_busy(struct request_queue *q)
{
int r;
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_table(md);
+ struct dm_table *map = dm_get_live_table(md);
if (!map || test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags))
r = 1;
@@ -1610,7 +1719,7 @@ static int dm_lld_busy(struct request_queue *q)
static void dm_unplug_all(struct request_queue *q)
{
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_table(md);
+ struct dm_table *map = dm_get_live_table(md);
if (map) {
if (dm_request_based(md))
@@ -1628,7 +1737,7 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
struct dm_table *map;
if (!test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) {
- map = dm_get_table(md);
+ map = dm_get_live_table(md);
if (map) {
/*
* Request-based dm cares about only own queue for
@@ -1725,6 +1834,7 @@ out:
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);
/*
* Allocate and initialise a blank device with a given minor.
@@ -1754,6 +1864,7 @@ static struct mapped_device *alloc_dev(int minor)
init_rwsem(&md->io_lock);
mutex_init(&md->suspend_lock);
spin_lock_init(&md->deferred_lock);
+ spin_lock_init(&md->barrier_error_lock);
rwlock_init(&md->map_lock);
atomic_set(&md->holders, 1);
atomic_set(&md->open_count, 0);
@@ -1788,6 +1899,8 @@ static struct mapped_device *alloc_dev(int minor)
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);
md->disk = alloc_disk(1);
if (!md->disk)
@@ -1797,6 +1910,7 @@ static struct mapped_device *alloc_dev(int minor)
atomic_set(&md->pending[1], 0);
init_waitqueue_head(&md->wait);
INIT_WORK(&md->work, dm_wq_work);
+ INIT_WORK(&md->barrier_work, dm_rq_barrier_work);
init_waitqueue_head(&md->eventq);
md->disk->major = _major;
@@ -1921,9 +2035,13 @@ static void __set_size(struct mapped_device *md, sector_t size)
mutex_unlock(&md->bdev->bd_inode->i_mutex);
}
-static int __bind(struct mapped_device *md, struct dm_table *t,
- struct queue_limits *limits)
+/*
+ * Returns old map, which caller must destroy.
+ */
+static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
+ struct queue_limits *limits)
{
+ struct dm_table *old_map;
struct request_queue *q = md->queue;
sector_t size;
unsigned long flags;
@@ -1938,11 +2056,6 @@ static int __bind(struct mapped_device *md, struct dm_table *t,
__set_size(md, size);
- if (!size) {
- dm_table_destroy(t);
- return 0;
- }
-
dm_table_event_callback(t, event_callback, md);
/*
@@ -1958,26 +2071,31 @@ static int __bind(struct mapped_device *md, struct dm_table *t,
__bind_mempools(md, t);
write_lock_irqsave(&md->map_lock, flags);
+ old_map = md->map;
md->map = t;
dm_table_set_restrictions(t, q, limits);
write_unlock_irqrestore(&md->map_lock, flags);
- return 0;
+ return old_map;
}
-static void __unbind(struct mapped_device *md)
+/*
+ * Returns unbound table for the caller to free.
+ */
+static struct dm_table *__unbind(struct mapped_device *md)
{
struct dm_table *map = md->map;
unsigned long flags;
if (!map)
- return;
+ return NULL;
dm_table_event_callback(map, NULL, NULL);
write_lock_irqsave(&md->map_lock, flags);
md->map = NULL;
write_unlock_irqrestore(&md->map_lock, flags);
- dm_table_destroy(map);
+
+ return map;
}
/*
@@ -2059,18 +2177,18 @@ void dm_put(struct mapped_device *md)
BUG_ON(test_bit(DMF_FREEING, &md->flags));
if (atomic_dec_and_lock(&md->holders, &_minor_lock)) {
- map = dm_get_table(md);
+ 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)) {
+ if (!dm_suspended_md(md)) {
dm_table_presuspend_targets(map);
dm_table_postsuspend_targets(map);
}
dm_sysfs_exit(md);
dm_table_put(map);
- __unbind(md);
+ dm_table_destroy(__unbind(md));
free_dev(md);
}
}
@@ -2080,8 +2198,6 @@ static int dm_wait_for_completion(struct mapped_device *md, int interruptible)
{
int r = 0;
DECLARE_WAITQUEUE(wait, current);
- struct request_queue *q = md->queue;
- unsigned long flags;
dm_unplug_all(md->queue);
@@ -2091,15 +2207,7 @@ static int dm_wait_for_completion(struct mapped_device *md, int interruptible)
set_current_state(interruptible);
smp_mb();
- if (dm_request_based(md)) {
- spin_lock_irqsave(q->queue_lock, flags);
- if (!queue_in_flight(q) && blk_queue_stopped(q)) {
- spin_unlock_irqrestore(q->queue_lock, flags);
- break;
- }
- spin_unlock_irqrestore(q->queue_lock, flags);
- } else if (!atomic_read(&md->pending[0]) &&
- !atomic_read(&md->pending[1]))
+ if (!md_in_flight(md))
break;
if (interruptible == TASK_INTERRUPTIBLE &&
@@ -2194,98 +2302,106 @@ static void dm_queue_flush(struct mapped_device *md)
queue_work(md->wq, &md->work);
}
-/*
- * Swap in a new table (destroying old one).
- */
-int dm_swap_table(struct mapped_device *md, struct dm_table *table)
+static void dm_rq_set_flush_nr(struct request *clone, unsigned flush_nr)
{
- struct queue_limits limits;
- int r = -EINVAL;
+ struct dm_rq_target_io *tio = clone->end_io_data;
- mutex_lock(&md->suspend_lock);
+ tio->info.flush_request = flush_nr;
+}
- /* device must be suspended */
- if (!dm_suspended(md))
- goto out;
+/* Issue barrier requests to targets and wait for their completion. */
+static int dm_rq_barrier(struct mapped_device *md)
+{
+ int i, j;
+ struct dm_table *map = dm_get_live_table(md);
+ unsigned num_targets = dm_table_get_num_targets(map);
+ struct dm_target *ti;
+ struct request *clone;
- r = dm_calculate_queue_limits(table, &limits);
- if (r)
- goto out;
+ md->barrier_error = 0;
- /* 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;
+ for (i = 0; i < num_targets; i++) {
+ 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);
+ atomic_inc(&md->pending[rq_data_dir(clone)]);
+ map_request(ti, clone, md);
+ }
}
- __unbind(md);
- r = __bind(md, table, &limits);
-
-out:
- mutex_unlock(&md->suspend_lock);
- return r;
-}
+ dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE);
+ dm_table_put(map);
-static void dm_rq_invalidate_suspend_marker(struct mapped_device *md)
-{
- md->suspend_rq.special = (void *)0x1;
+ return md->barrier_error;
}
-static void dm_rq_abort_suspend(struct mapped_device *md, int noflush)
+static void dm_rq_barrier_work(struct work_struct *work)
{
+ int error;
+ struct mapped_device *md = container_of(work, struct mapped_device,
+ barrier_work);
struct request_queue *q = md->queue;
+ struct request *rq;
unsigned long flags;
- spin_lock_irqsave(q->queue_lock, flags);
- if (!noflush)
- dm_rq_invalidate_suspend_marker(md);
- __start_queue(q);
- spin_unlock_irqrestore(q->queue_lock, flags);
-}
+ /*
+ * Hold the md reference here and leave it at the last part so that
+ * the md can't be deleted by device opener when the barrier request
+ * completes.
+ */
+ dm_get(md);
-static void dm_rq_start_suspend(struct mapped_device *md, int noflush)
-{
- struct request *rq = &md->suspend_rq;
- struct request_queue *q = md->queue;
+ error = dm_rq_barrier(md);
- if (noflush)
- stop_queue(q);
- else {
- blk_rq_init(q, rq);
- blk_insert_request(q, rq, 0, NULL);
- }
+ rq = md->flush_request;
+ md->flush_request = NULL;
+
+ if (error == DM_ENDIO_REQUEUE) {
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_requeue_request(q, rq);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ } else
+ blk_end_request_all(rq, error);
+
+ blk_run_queue(q);
+
+ dm_put(md);
}
-static int dm_rq_suspend_available(struct mapped_device *md, int noflush)
+/*
+ * Swap in a new table, returning the old one for the caller to destroy.
+ */
+struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
{
- int r = 1;
- struct request *rq = &md->suspend_rq;
- struct request_queue *q = md->queue;
- unsigned long flags;
+ struct dm_table *map = ERR_PTR(-EINVAL);
+ struct queue_limits limits;
+ int r;
- if (noflush)
- return r;
+ mutex_lock(&md->suspend_lock);
- /* The marker must be protected by queue lock if it is in use */
- spin_lock_irqsave(q->queue_lock, flags);
- if (unlikely(rq->ref_count)) {
- /*
- * This can happen, when the previous flush suspend was
- * interrupted, the marker is still in the queue and
- * this flush suspend has been invoked, because we don't
- * remove the marker at the time of suspend interruption.
- * We have only one marker per mapped_device, so we can't
- * start another flush suspend while it is in use.
- */
- BUG_ON(!rq->special); /* The marker should be invalidated */
- DMWARN("Invalidating the previous flush suspend is still in"
- " progress. Please retry later.");
- r = 0;
+ /* device must be suspended */
+ if (!dm_suspended_md(md))
+ goto out;
+
+ r = dm_calculate_queue_limits(table, &limits);
+ if (r) {
+ map = ERR_PTR(r);
+ goto out;
}
- spin_unlock_irqrestore(q->queue_lock, flags);
- return r;
+ /* 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:
+ mutex_unlock(&md->suspend_lock);
+ return map;
}
/*
@@ -2330,49 +2446,11 @@ static void unlock_fs(struct mapped_device *md)
/*
* Suspend mechanism in request-based dm.
*
- * After the suspend starts, further incoming requests are kept in
- * the request_queue and deferred.
- * Remaining requests in the request_queue at the start of suspend are flushed
- * if it is flush suspend.
- * The suspend completes when the following conditions have been satisfied,
- * so wait for it:
- * 1. q->in_flight is 0 (which means no in_flight request)
- * 2. queue has been stopped (which means no request dispatching)
- *
+ * 1. Flush all I/Os by lock_fs() if needed.
+ * 2. Stop dispatching any I/O by stopping the request_queue.
+ * 3. Wait for all in-flight I/Os to be completed or requeued.
*
- * Noflush suspend
- * ---------------
- * Noflush suspend doesn't need to dispatch remaining requests.
- * So stop the queue immediately. Then, wait for all in_flight requests
- * to be completed or requeued.
- *
- * To abort noflush suspend, start the queue.
- *
- *
- * Flush suspend
- * -------------
- * Flush suspend needs to dispatch remaining requests. So stop the queue
- * after the remaining requests are completed. (Requeued request must be also
- * re-dispatched and completed. Until then, we can't stop the queue.)
- *
- * During flushing the remaining requests, further incoming requests are also
- * inserted to the same queue. To distinguish which requests are to be
- * flushed, we insert a marker request to the queue at the time of starting
- * flush suspend, like a barrier.
- * The dispatching is blocked when the marker is found on the top of the queue.
- * And the queue is stopped when all in_flight requests are completed, since
- * that means the remaining requests are completely flushed.
- * Then, the marker is removed from the queue.
- *
- * To abort flush suspend, we also need to take care of the marker, not only
- * starting the queue.
- * We don't remove the marker forcibly from the queue since it's against
- * the block-layer manner. Instead, we put a invalidated mark on the marker.
- * When the invalidated marker is found on the top of the queue, it is
- * immediately removed from the queue, so it doesn't block dispatching.
- * Because we have only one marker per mapped_device, we can't start another
- * flush suspend until the invalidated marker is removed from the queue.
- * So fail and return with -EBUSY in such a case.
+ * To abort suspend, start the request_queue.
*/
int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
{
@@ -2383,17 +2461,12 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
mutex_lock(&md->suspend_lock);
- if (dm_suspended(md)) {
+ if (dm_suspended_md(md)) {
r = -EINVAL;
goto out_unlock;
}
- if (dm_request_based(md) && !dm_rq_suspend_available(md, noflush)) {
- r = -EBUSY;
- goto out_unlock;
- }
-
- map = dm_get_table(md);
+ map = dm_get_live_table(md);
/*
* DMF_NOFLUSH_SUSPENDING must be set before presuspend.
@@ -2406,8 +2479,10 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
dm_table_presuspend_targets(map);
/*
- * Flush I/O to the device. noflush supersedes do_lockfs,
- * because lock_fs() needs to flush I/Os.
+ * Flush I/O to the device.
+ * Any I/O submitted after lock_fs() may not be flushed.
+ * noflush takes precedence over do_lockfs.
+ * (lock_fs() flushes I/Os and waits for them to complete.)
*/
if (!noflush && do_lockfs) {
r = lock_fs(md);
@@ -2436,10 +2511,15 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
set_bit(DMF_QUEUE_IO_TO_THREAD, &md->flags);
up_write(&md->io_lock);
- flush_workqueue(md->wq);
-
+ /*
+ * Request-based dm uses md->wq for barrier (dm_rq_barrier_work) which
+ * can be kicked until md->queue is stopped. So stop md->queue before
+ * flushing md->wq.
+ */
if (dm_request_based(md))
- dm_rq_start_suspend(md, noflush);
+ stop_queue(md->queue);
+
+ flush_workqueue(md->wq);
/*
* At this point no more requests are entering target request routines.
@@ -2458,7 +2538,7 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
dm_queue_flush(md);
if (dm_request_based(md))
- dm_rq_abort_suspend(md, noflush);
+ start_queue(md->queue);
unlock_fs(md);
goto out; /* pushback list is already flushed, so skip flush */
@@ -2470,10 +2550,10 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
* requests are being added to md->deferred list.
*/
- dm_table_postsuspend_targets(map);
-
set_bit(DMF_SUSPENDED, &md->flags);
+ dm_table_postsuspend_targets(map);
+
out:
dm_table_put(map);
@@ -2488,10 +2568,10 @@ int dm_resume(struct mapped_device *md)
struct dm_table *map = NULL;
mutex_lock(&md->suspend_lock);
- if (!dm_suspended(md))
+ if (!dm_suspended_md(md))
goto out;
- map = dm_get_table(md);
+ map = dm_get_live_table(md);
if (!map || !dm_table_get_size(map))
goto out;
@@ -2592,18 +2672,29 @@ struct mapped_device *dm_get_from_kobject(struct kobject *kobj)
return NULL;
if (test_bit(DMF_FREEING, &md->flags) ||
- test_bit(DMF_DELETING, &md->flags))
+ dm_deleting_md(md))
return NULL;
dm_get(md);
return md;
}
-int dm_suspended(struct mapped_device *md)
+int dm_suspended_md(struct mapped_device *md)
{
return test_bit(DMF_SUSPENDED, &md->flags);
}
+int dm_suspended(struct dm_target *ti)
+{
+ struct mapped_device *md = dm_table_get_md(ti->table);
+ int r = dm_suspended_md(md);
+
+ dm_put(md);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_suspended);
+
int dm_noflush_suspending(struct dm_target *ti)
{
struct mapped_device *md = dm_table_get_md(ti->table);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index a7663eba17e2..8dadaa5bc396 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -89,6 +89,16 @@ int dm_target_iterate(void (*iter_func)(struct target_type *tt,
int dm_split_args(int *argc, char ***argvp, char *input);
/*
+ * Is this mapped_device being deleted?
+ */
+int dm_deleting_md(struct mapped_device *md);
+
+/*
+ * Is this mapped_device suspended?
+ */
+int dm_suspended_md(struct mapped_device *md);
+
+/*
* The device-mapper can be driven through one of two interfaces;
* ioctl or filesystem, depending which patch you have applied.
*/
@@ -118,6 +128,9 @@ int dm_lock_for_deletion(struct mapped_device *md);
void dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
unsigned cookie);
+int dm_io_init(void);
+void dm_io_exit(void);
+
int dm_kcopyd_init(void);
void dm_kcopyd_exit(void);
diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c
index 87d88dbb667f..713acd02ab39 100644
--- a/drivers/md/faulty.c
+++ b/drivers/md/faulty.c
@@ -360,6 +360,7 @@ static void raid_exit(void)
module_init(raid_init);
module_exit(raid_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Fault injection personality for MD");
MODULE_ALIAS("md-personality-10"); /* faulty */
MODULE_ALIAS("md-faulty");
MODULE_ALIAS("md-level--5");
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 1ceceb334d5e..00435bd20699 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -292,7 +292,7 @@ static int linear_make_request (struct request_queue *q, struct bio *bio)
int cpu;
if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
- bio_endio(bio, -EOPNOTSUPP);
+ md_barrier_request(mddev, bio);
return 0;
}
@@ -383,6 +383,7 @@ static void linear_exit (void)
module_init(linear_init);
module_exit(linear_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Linear device concatenation personality for MD");
MODULE_ALIAS("md-personality-1"); /* LINEAR - deprecated*/
MODULE_ALIAS("md-linear");
MODULE_ALIAS("md-level--1");
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 5f154ef1e4be..f4f5f82f9f53 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -39,11 +39,13 @@
#include <linux/buffer_head.h> /* for invalidate_bdev */
#include <linux/poll.h>
#include <linux/ctype.h>
+#include <linux/string.h>
#include <linux/hdreg.h>
#include <linux/proc_fs.h>
#include <linux/random.h>
#include <linux/reboot.h>
#include <linux/file.h>
+#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/raid/md_p.h>
#include <linux/raid/md_u.h>
@@ -68,6 +70,12 @@ static DECLARE_WAIT_QUEUE_HEAD(resync_wait);
#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); }
/*
+ * Default number of read corrections we'll attempt on an rdev
+ * before ejecting it from the array. We divide the read error
+ * count by 2 for every hour elapsed between read errors.
+ */
+#define MD_DEFAULT_MAX_CORRECTED_READ_ERRORS 20
+/*
* Current RAID-1,4,5 parallel reconstruction 'guaranteed speed limit'
* is 1000 KB/sec, so the extra system load does not show up that much.
* Increase it if you want to have more _guaranteed_ speed. Note that
@@ -213,12 +221,12 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
return 0;
}
rcu_read_lock();
- if (mddev->suspended) {
+ if (mddev->suspended || mddev->barrier) {
DEFINE_WAIT(__wait);
for (;;) {
prepare_to_wait(&mddev->sb_wait, &__wait,
TASK_UNINTERRUPTIBLE);
- if (!mddev->suspended)
+ if (!mddev->suspended && !mddev->barrier)
break;
rcu_read_unlock();
schedule();
@@ -260,10 +268,110 @@ static void mddev_resume(mddev_t *mddev)
int mddev_congested(mddev_t *mddev, int bits)
{
+ if (mddev->barrier)
+ return 1;
return mddev->suspended;
}
EXPORT_SYMBOL(mddev_congested);
+/*
+ * Generic barrier handling for md
+ */
+
+#define POST_REQUEST_BARRIER ((void*)1)
+
+static void md_end_barrier(struct bio *bio, int err)
+{
+ mdk_rdev_t *rdev = bio->bi_private;
+ mddev_t *mddev = rdev->mddev;
+ if (err == -EOPNOTSUPP && mddev->barrier != POST_REQUEST_BARRIER)
+ set_bit(BIO_EOPNOTSUPP, &mddev->barrier->bi_flags);
+
+ rdev_dec_pending(rdev, mddev);
+
+ if (atomic_dec_and_test(&mddev->flush_pending)) {
+ if (mddev->barrier == POST_REQUEST_BARRIER) {
+ /* This was a post-request barrier */
+ mddev->barrier = NULL;
+ wake_up(&mddev->sb_wait);
+ } else
+ /* The pre-request barrier has finished */
+ schedule_work(&mddev->barrier_work);
+ }
+ bio_put(bio);
+}
+
+static void submit_barriers(mddev_t *mddev)
+{
+ mdk_rdev_t *rdev;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(rdev, &mddev->disks, same_set)
+ if (rdev->raid_disk >= 0 &&
+ !test_bit(Faulty, &rdev->flags)) {
+ /* Take two references, one is dropped
+ * when request finishes, one after
+ * we reclaim rcu_read_lock
+ */
+ struct bio *bi;
+ atomic_inc(&rdev->nr_pending);
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ bi = bio_alloc(GFP_KERNEL, 0);
+ bi->bi_end_io = md_end_barrier;
+ bi->bi_private = rdev;
+ bi->bi_bdev = rdev->bdev;
+ atomic_inc(&mddev->flush_pending);
+ submit_bio(WRITE_BARRIER, bi);
+ rcu_read_lock();
+ rdev_dec_pending(rdev, mddev);
+ }
+ rcu_read_unlock();
+}
+
+static void md_submit_barrier(struct work_struct *ws)
+{
+ mddev_t *mddev = container_of(ws, mddev_t, barrier_work);
+ struct bio *bio = mddev->barrier;
+
+ atomic_set(&mddev->flush_pending, 1);
+
+ if (test_bit(BIO_EOPNOTSUPP, &bio->bi_flags))
+ bio_endio(bio, -EOPNOTSUPP);
+ else if (bio->bi_size == 0)
+ /* an empty barrier - all done */
+ bio_endio(bio, 0);
+ else {
+ bio->bi_rw &= ~(1<<BIO_RW_BARRIER);
+ if (mddev->pers->make_request(mddev->queue, bio))
+ generic_make_request(bio);
+ mddev->barrier = POST_REQUEST_BARRIER;
+ submit_barriers(mddev);
+ }
+ if (atomic_dec_and_test(&mddev->flush_pending)) {
+ mddev->barrier = NULL;
+ wake_up(&mddev->sb_wait);
+ }
+}
+
+void md_barrier_request(mddev_t *mddev, struct bio *bio)
+{
+ spin_lock_irq(&mddev->write_lock);
+ wait_event_lock_irq(mddev->sb_wait,
+ !mddev->barrier,
+ mddev->write_lock, /*nothing*/);
+ mddev->barrier = bio;
+ spin_unlock_irq(&mddev->write_lock);
+
+ atomic_set(&mddev->flush_pending, 1);
+ INIT_WORK(&mddev->barrier_work, md_submit_barrier);
+
+ submit_barriers(mddev);
+
+ if (atomic_dec_and_test(&mddev->flush_pending))
+ schedule_work(&mddev->barrier_work);
+}
+EXPORT_SYMBOL(md_barrier_request);
static inline mddev_t *mddev_get(mddev_t *mddev)
{
@@ -363,6 +471,7 @@ static mddev_t * mddev_find(dev_t unit)
mutex_init(&new->open_mutex);
mutex_init(&new->reconfig_mutex);
+ mutex_init(&new->bitmap_info.mutex);
INIT_LIST_HEAD(&new->disks);
INIT_LIST_HEAD(&new->all_mddevs);
init_timer(&new->safemode_timer);
@@ -370,6 +479,7 @@ static mddev_t * mddev_find(dev_t unit)
atomic_set(&new->openers, 0);
atomic_set(&new->active_io, 0);
spin_lock_init(&new->write_lock);
+ atomic_set(&new->flush_pending, 0);
init_waitqueue_head(&new->sb_wait);
init_waitqueue_head(&new->recovery_wait);
new->reshape_position = MaxSector;
@@ -748,7 +858,7 @@ struct super_type {
*/
int md_check_no_bitmap(mddev_t *mddev)
{
- if (!mddev->bitmap_file && !mddev->bitmap_offset)
+ if (!mddev->bitmap_info.file && !mddev->bitmap_info.offset)
return 0;
printk(KERN_ERR "%s: bitmaps are not supported for %s\n",
mdname(mddev), mddev->pers->name);
@@ -876,8 +986,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->raid_disks = sb->raid_disks;
mddev->dev_sectors = sb->size * 2;
mddev->events = ev1;
- mddev->bitmap_offset = 0;
- mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
+ mddev->bitmap_info.offset = 0;
+ mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
if (mddev->minor_version >= 91) {
mddev->reshape_position = sb->reshape_position;
@@ -911,8 +1021,9 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->max_disks = MD_SB_DISKS;
if (sb->state & (1<<MD_SB_BITMAP_PRESENT) &&
- mddev->bitmap_file == NULL)
- mddev->bitmap_offset = mddev->default_bitmap_offset;
+ mddev->bitmap_info.file == NULL)
+ mddev->bitmap_info.offset =
+ mddev->bitmap_info.default_offset;
} else if (mddev->pers == NULL) {
/* Insist on good event counter while assembling */
@@ -1029,7 +1140,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->layout = mddev->layout;
sb->chunk_size = mddev->chunk_sectors << 9;
- if (mddev->bitmap && mddev->bitmap_file == NULL)
+ if (mddev->bitmap && mddev->bitmap_info.file == NULL)
sb->state |= (1<<MD_SB_BITMAP_PRESENT);
sb->disks[0].state = (1<<MD_DISK_REMOVED);
@@ -1107,7 +1218,7 @@ super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
{
if (num_sectors && num_sectors < rdev->mddev->dev_sectors)
return 0; /* component must fit device */
- if (rdev->mddev->bitmap_offset)
+ if (rdev->mddev->bitmap_info.offset)
return 0; /* can't move bitmap */
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
if (!num_sectors || num_sectors > rdev->sb_start)
@@ -1286,8 +1397,8 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->raid_disks = le32_to_cpu(sb->raid_disks);
mddev->dev_sectors = le64_to_cpu(sb->size);
mddev->events = ev1;
- mddev->bitmap_offset = 0;
- mddev->default_bitmap_offset = 1024 >> 9;
+ mddev->bitmap_info.offset = 0;
+ mddev->bitmap_info.default_offset = 1024 >> 9;
mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
memcpy(mddev->uuid, sb->set_uuid, 16);
@@ -1295,8 +1406,9 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->max_disks = (4096-256)/2;
if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) &&
- mddev->bitmap_file == NULL )
- mddev->bitmap_offset = (__s32)le32_to_cpu(sb->bitmap_offset);
+ mddev->bitmap_info.file == NULL )
+ mddev->bitmap_info.offset =
+ (__s32)le32_to_cpu(sb->bitmap_offset);
if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
mddev->reshape_position = le64_to_cpu(sb->reshape_position);
@@ -1390,19 +1502,17 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->level = cpu_to_le32(mddev->level);
sb->layout = cpu_to_le32(mddev->layout);
- if (mddev->bitmap && mddev->bitmap_file == NULL) {
- sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
+ if (mddev->bitmap && mddev->bitmap_info.file == NULL) {
+ sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_info.offset);
sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET);
}
if (rdev->raid_disk >= 0 &&
!test_bit(In_sync, &rdev->flags)) {
- if (rdev->recovery_offset > 0) {
- sb->feature_map |=
- cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET);
- sb->recovery_offset =
- cpu_to_le64(rdev->recovery_offset);
- }
+ sb->feature_map |=
+ cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET);
+ sb->recovery_offset =
+ cpu_to_le64(rdev->recovery_offset);
}
if (mddev->reshape_position != MaxSector) {
@@ -1436,7 +1546,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->dev_roles[i] = cpu_to_le16(0xfffe);
else if (test_bit(In_sync, &rdev2->flags))
sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk);
- else if (rdev2->raid_disk >= 0 && rdev2->recovery_offset > 0)
+ else if (rdev2->raid_disk >= 0)
sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk);
else
sb->dev_roles[i] = cpu_to_le16(0xffff);
@@ -1458,7 +1568,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
max_sectors -= rdev->data_offset;
if (!num_sectors || num_sectors > max_sectors)
num_sectors = max_sectors;
- } else if (rdev->mddev->bitmap_offset) {
+ } else if (rdev->mddev->bitmap_info.offset) {
/* minor version 0 with bitmap we can't move */
return 0;
} else {
@@ -1826,15 +1936,11 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
uuid = sb->set_uuid;
printk(KERN_INFO
- "md: SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x"
- ":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n"
+ "md: SB: (V:%u) (F:0x%08x) Array-ID:<%pU>\n"
"md: Name: \"%s\" CT:%llu\n",
le32_to_cpu(sb->major_version),
le32_to_cpu(sb->feature_map),
- uuid[0], uuid[1], uuid[2], uuid[3],
- uuid[4], uuid[5], uuid[6], uuid[7],
- uuid[8], uuid[9], uuid[10], uuid[11],
- uuid[12], uuid[13], uuid[14], uuid[15],
+ uuid,
sb->set_name,
(unsigned long long)le64_to_cpu(sb->ctime)
& MD_SUPERBLOCK_1_TIME_SEC_MASK);
@@ -1843,8 +1949,7 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
printk(KERN_INFO
"md: L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu"
" RO:%llu\n"
- "md: Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x"
- ":%02x%02x%02x%02x%02x%02x\n"
+ "md: Dev:%08x UUID: %pU\n"
"md: (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n"
"md: (MaxDev:%u) \n",
le32_to_cpu(sb->level),
@@ -1857,10 +1962,7 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
(unsigned long long)le64_to_cpu(sb->super_offset),
(unsigned long long)le64_to_cpu(sb->recovery_offset),
le32_to_cpu(sb->dev_number),
- uuid[0], uuid[1], uuid[2], uuid[3],
- uuid[4], uuid[5], uuid[6], uuid[7],
- uuid[8], uuid[9], uuid[10], uuid[11],
- uuid[12], uuid[13], uuid[14], uuid[15],
+ uuid,
sb->devflags,
(unsigned long long)le64_to_cpu(sb->utime) & MD_SUPERBLOCK_1_TIME_SEC_MASK,
(unsigned long long)le64_to_cpu(sb->events),
@@ -2442,12 +2544,49 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
static struct rdev_sysfs_entry rdev_size =
__ATTR(size, S_IRUGO|S_IWUSR, rdev_size_show, rdev_size_store);
+
+static ssize_t recovery_start_show(mdk_rdev_t *rdev, char *page)
+{
+ unsigned long long recovery_start = rdev->recovery_offset;
+
+ if (test_bit(In_sync, &rdev->flags) ||
+ recovery_start == MaxSector)
+ return sprintf(page, "none\n");
+
+ return sprintf(page, "%llu\n", recovery_start);
+}
+
+static ssize_t recovery_start_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+ unsigned long long recovery_start;
+
+ if (cmd_match(buf, "none"))
+ recovery_start = MaxSector;
+ else if (strict_strtoull(buf, 10, &recovery_start))
+ return -EINVAL;
+
+ if (rdev->mddev->pers &&
+ rdev->raid_disk >= 0)
+ return -EBUSY;
+
+ rdev->recovery_offset = recovery_start;
+ if (recovery_start == MaxSector)
+ set_bit(In_sync, &rdev->flags);
+ else
+ clear_bit(In_sync, &rdev->flags);
+ return len;
+}
+
+static struct rdev_sysfs_entry rdev_recovery_start =
+__ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store);
+
static struct attribute *rdev_default_attrs[] = {
&rdev_state.attr,
&rdev_errors.attr,
&rdev_slot.attr,
&rdev_offset.attr,
&rdev_size.attr,
+ &rdev_recovery_start.attr,
NULL,
};
static ssize_t
@@ -2549,6 +2688,8 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
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);
@@ -2659,6 +2800,47 @@ static void analyze_sbs(mddev_t * mddev)
}
}
+/* Read a fixed-point number.
+ * Numbers in sysfs attributes should be in "standard" units where
+ * possible, so time should be in seconds.
+ * However we internally use a a much smaller unit such as
+ * milliseconds or jiffies.
+ * This function takes a decimal number with a possible fractional
+ * component, and produces an integer which is the result of
+ * multiplying that number by 10^'scale'.
+ * all without any floating-point arithmetic.
+ */
+int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale)
+{
+ unsigned long result = 0;
+ long decimals = -1;
+ while (isdigit(*cp) || (*cp == '.' && decimals < 0)) {
+ if (*cp == '.')
+ decimals = 0;
+ else if (decimals < scale) {
+ unsigned int value;
+ value = *cp - '0';
+ result = result * 10 + value;
+ if (decimals >= 0)
+ decimals++;
+ }
+ cp++;
+ }
+ if (*cp == '\n')
+ cp++;
+ if (*cp)
+ return -EINVAL;
+ if (decimals < 0)
+ decimals = 0;
+ while (decimals < scale) {
+ result *= 10;
+ decimals ++;
+ }
+ *res = result;
+ return 0;
+}
+
+
static void md_safemode_timeout(unsigned long data);
static ssize_t
@@ -2670,31 +2852,10 @@ safe_delay_show(mddev_t *mddev, char *page)
static ssize_t
safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
{
- int scale=1;
- int dot=0;
- int i;
unsigned long msec;
- char buf[30];
- /* remove a period, and count digits after it */
- if (len >= sizeof(buf))
- return -EINVAL;
- strlcpy(buf, cbuf, sizeof(buf));
- for (i=0; i<len; i++) {
- if (dot) {
- if (isdigit(buf[i])) {
- buf[i-1] = buf[i];
- scale *= 10;
- }
- buf[i] = 0;
- } else if (buf[i] == '.') {
- dot=1;
- buf[i] = 0;
- }
- }
- if (strict_strtoul(buf, 10, &msec) < 0)
+ if (strict_strtoul_scaled(cbuf, &msec, 3) < 0)
return -EINVAL;
- msec = (msec * 1000) / scale;
if (msec == 0)
mddev->safemode_delay = 0;
else {
@@ -2970,7 +3131,9 @@ resync_start_store(mddev_t *mddev, const char *buf, size_t len)
if (mddev->pers)
return -EBUSY;
- if (!*buf || (*e && *e != '\n'))
+ if (cmd_match(buf, "none"))
+ n = MaxSector;
+ else if (!*buf || (*e && *e != '\n'))
return -EINVAL;
mddev->recovery_cp = n;
@@ -3166,6 +3329,29 @@ static struct md_sysfs_entry md_array_state =
__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
static ssize_t
+max_corrected_read_errors_show(mddev_t *mddev, char *page) {
+ return sprintf(page, "%d\n",
+ atomic_read(&mddev->max_corr_read_errors));
+}
+
+static ssize_t
+max_corrected_read_errors_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ char *e;
+ unsigned long n = simple_strtoul(buf, &e, 10);
+
+ if (*buf && (*e == 0 || *e == '\n')) {
+ atomic_set(&mddev->max_corr_read_errors, n);
+ return len;
+ }
+ return -EINVAL;
+}
+
+static struct md_sysfs_entry max_corr_read_errors =
+__ATTR(max_read_errors, S_IRUGO|S_IWUSR, max_corrected_read_errors_show,
+ max_corrected_read_errors_store);
+
+static ssize_t
null_show(mddev_t *mddev, char *page)
{
return -EINVAL;
@@ -3246,8 +3432,7 @@ bitmap_store(mddev_t *mddev, const char *buf, size_t len)
}
if (*end && !isspace(*end)) break;
bitmap_dirty_bits(mddev->bitmap, chunk, end_chunk);
- buf = end;
- while (isspace(*buf)) buf++;
+ buf = skip_spaces(end);
}
bitmap_unplug(mddev->bitmap); /* flush the bits to disk */
out:
@@ -3790,6 +3975,7 @@ static struct attribute *md_default_attrs[] = {
&md_array_state.attr,
&md_reshape_position.attr,
&md_array_size.attr,
+ &max_corr_read_errors.attr,
NULL,
};
@@ -3894,6 +4080,7 @@ static void mddev_delayed_delete(struct work_struct *ws)
mddev->sysfs_action = NULL;
mddev->private = NULL;
}
+ sysfs_remove_group(&mddev->kobj, &md_bitmap_group);
kobject_del(&mddev->kobj);
kobject_put(&mddev->kobj);
}
@@ -3985,6 +4172,8 @@ static int md_alloc(dev_t dev, char *name)
disk->disk_name);
error = 0;
}
+ if (sysfs_create_group(&mddev->kobj, &md_bitmap_group))
+ printk(KERN_DEBUG "pointless warning\n");
abort:
mutex_unlock(&disks_mutex);
if (!error) {
@@ -4206,6 +4395,8 @@ static int do_md_run(mddev_t * mddev)
mddev->ro = 0;
atomic_set(&mddev->writes_pending,0);
+ atomic_set(&mddev->max_corr_read_errors,
+ MD_DEFAULT_MAX_CORRECTED_READ_ERRORS);
mddev->safemode = 0;
mddev->safemode_timer.function = md_safemode_timeout;
mddev->safemode_timer.data = (unsigned long) mddev;
@@ -4310,7 +4501,7 @@ static int deny_bitmap_write_access(struct file * file)
return 0;
}
-static void restore_bitmap_write_access(struct file *file)
+void restore_bitmap_write_access(struct file *file)
{
struct inode *inode = file->f_mapping->host;
@@ -4405,12 +4596,12 @@ out:
printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
bitmap_destroy(mddev);
- if (mddev->bitmap_file) {
- restore_bitmap_write_access(mddev->bitmap_file);
- fput(mddev->bitmap_file);
- mddev->bitmap_file = NULL;
+ if (mddev->bitmap_info.file) {
+ restore_bitmap_write_access(mddev->bitmap_info.file);
+ fput(mddev->bitmap_info.file);
+ mddev->bitmap_info.file = NULL;
}
- mddev->bitmap_offset = 0;
+ mddev->bitmap_info.offset = 0;
/* make sure all md_delayed_delete calls have finished */
flush_scheduled_work();
@@ -4451,6 +4642,11 @@ out:
mddev->degraded = 0;
mddev->barriers_work = 0;
mddev->safemode = 0;
+ mddev->bitmap_info.offset = 0;
+ mddev->bitmap_info.default_offset = 0;
+ mddev->bitmap_info.chunksize = 0;
+ mddev->bitmap_info.daemon_sleep = 0;
+ mddev->bitmap_info.max_write_behind = 0;
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
if (mddev->hold_active == UNTIL_STOP)
mddev->hold_active = 0;
@@ -4636,7 +4832,7 @@ static int get_array_info(mddev_t * mddev, void __user * arg)
info.state = 0;
if (mddev->in_sync)
info.state = (1<<MD_SB_CLEAN);
- if (mddev->bitmap && mddev->bitmap_offset)
+ if (mddev->bitmap && mddev->bitmap_info.offset)
info.state = (1<<MD_SB_BITMAP_PRESENT);
info.active_disks = insync;
info.working_disks = working;
@@ -4994,23 +5190,23 @@ static int set_bitmap_file(mddev_t *mddev, int fd)
if (fd >= 0) {
if (mddev->bitmap)
return -EEXIST; /* cannot add when bitmap is present */
- mddev->bitmap_file = fget(fd);
+ mddev->bitmap_info.file = fget(fd);
- if (mddev->bitmap_file == NULL) {
+ if (mddev->bitmap_info.file == NULL) {
printk(KERN_ERR "%s: error: failed to get bitmap file\n",
mdname(mddev));
return -EBADF;
}
- err = deny_bitmap_write_access(mddev->bitmap_file);
+ err = deny_bitmap_write_access(mddev->bitmap_info.file);
if (err) {
printk(KERN_ERR "%s: error: bitmap file is already in use\n",
mdname(mddev));
- fput(mddev->bitmap_file);
- mddev->bitmap_file = NULL;
+ fput(mddev->bitmap_info.file);
+ mddev->bitmap_info.file = NULL;
return err;
}
- mddev->bitmap_offset = 0; /* file overrides offset */
+ mddev->bitmap_info.offset = 0; /* file overrides offset */
} else if (mddev->bitmap == NULL)
return -ENOENT; /* cannot remove what isn't there */
err = 0;
@@ -5025,11 +5221,11 @@ static int set_bitmap_file(mddev_t *mddev, int fd)
mddev->pers->quiesce(mddev, 0);
}
if (fd < 0) {
- if (mddev->bitmap_file) {
- restore_bitmap_write_access(mddev->bitmap_file);
- fput(mddev->bitmap_file);
+ if (mddev->bitmap_info.file) {
+ restore_bitmap_write_access(mddev->bitmap_info.file);
+ fput(mddev->bitmap_info.file);
}
- mddev->bitmap_file = NULL;
+ mddev->bitmap_info.file = NULL;
}
return err;
@@ -5096,8 +5292,8 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
mddev->flags = 0;
set_bit(MD_CHANGE_DEVS, &mddev->flags);
- mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
- mddev->bitmap_offset = 0;
+ mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+ mddev->bitmap_info.offset = 0;
mddev->reshape_position = MaxSector;
@@ -5197,7 +5393,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
int state = 0;
/* calculate expected state,ignoring low bits */
- if (mddev->bitmap && mddev->bitmap_offset)
+ if (mddev->bitmap && mddev->bitmap_info.offset)
state |= (1 << MD_SB_BITMAP_PRESENT);
if (mddev->major_version != info->major_version ||
@@ -5256,9 +5452,10 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
/* add the bitmap */
if (mddev->bitmap)
return -EEXIST;
- if (mddev->default_bitmap_offset == 0)
+ if (mddev->bitmap_info.default_offset == 0)
return -EINVAL;
- mddev->bitmap_offset = mddev->default_bitmap_offset;
+ mddev->bitmap_info.offset =
+ mddev->bitmap_info.default_offset;
mddev->pers->quiesce(mddev, 1);
rv = bitmap_create(mddev);
if (rv)
@@ -5273,7 +5470,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
mddev->pers->quiesce(mddev, 1);
bitmap_destroy(mddev);
mddev->pers->quiesce(mddev, 0);
- mddev->bitmap_offset = 0;
+ mddev->bitmap_info.offset = 0;
}
}
md_update_sb(mddev, 1);
@@ -5524,6 +5721,25 @@ done:
abort:
return err;
}
+#ifdef CONFIG_COMPAT
+static int md_compat_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case HOT_REMOVE_DISK:
+ case HOT_ADD_DISK:
+ case SET_DISK_FAULTY:
+ case SET_BITMAP_FILE:
+ /* These take in integer arg, do not convert */
+ break;
+ default:
+ arg = (unsigned long)compat_ptr(arg);
+ break;
+ }
+
+ return md_ioctl(bdev, mode, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
static int md_open(struct block_device *bdev, fmode_t mode)
{
@@ -5589,6 +5805,9 @@ static const struct block_device_operations md_fops =
.open = md_open,
.release = md_release,
.ioctl = md_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = md_compat_ioctl,
+#endif
.getgeo = md_getgeo,
.media_changed = md_media_changed,
.revalidate_disk= md_revalidate,
@@ -5982,14 +6201,14 @@ static int md_seq_show(struct seq_file *seq, void *v)
unsigned long chunk_kb;
unsigned long flags;
spin_lock_irqsave(&bitmap->lock, flags);
- chunk_kb = bitmap->chunksize >> 10;
+ chunk_kb = mddev->bitmap_info.chunksize >> 10;
seq_printf(seq, "bitmap: %lu/%lu pages [%luKB], "
"%lu%s chunk",
bitmap->pages - bitmap->missing_pages,
bitmap->pages,
(bitmap->pages - bitmap->missing_pages)
<< (PAGE_SHIFT - 10),
- chunk_kb ? chunk_kb : bitmap->chunksize,
+ chunk_kb ? chunk_kb : mddev->bitmap_info.chunksize,
chunk_kb ? "KB" : "B");
if (bitmap->file) {
seq_printf(seq, ", file: ");
@@ -6338,12 +6557,14 @@ void md_do_sync(mddev_t *mddev)
/* recovery follows the physical size of devices */
max_sectors = mddev->dev_sectors;
j = MaxSector;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rcu_read_lock();
+ list_for_each_entry_rcu(rdev, &mddev->disks, same_set)
if (rdev->raid_disk >= 0 &&
!test_bit(Faulty, &rdev->flags) &&
!test_bit(In_sync, &rdev->flags) &&
rdev->recovery_offset < j)
j = rdev->recovery_offset;
+ rcu_read_unlock();
}
printk(KERN_INFO "md: %s of RAID array %s\n", desc, mdname(mddev));
@@ -6380,6 +6601,7 @@ void md_do_sync(mddev_t *mddev)
desc, mdname(mddev));
mddev->curr_resync = j;
}
+ mddev->curr_resync_completed = mddev->curr_resync;
while (j < max_sectors) {
sector_t sectors;
@@ -6512,22 +6734,29 @@ void md_do_sync(mddev_t *mddev)
} else {
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
mddev->curr_resync = MaxSector;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rcu_read_lock();
+ list_for_each_entry_rcu(rdev, &mddev->disks, same_set)
if (rdev->raid_disk >= 0 &&
!test_bit(Faulty, &rdev->flags) &&
!test_bit(In_sync, &rdev->flags) &&
rdev->recovery_offset < mddev->curr_resync)
rdev->recovery_offset = mddev->curr_resync;
+ rcu_read_unlock();
}
}
set_bit(MD_CHANGE_DEVS, &mddev->flags);
skip:
+ if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
+ /* We completed so min/max setting can be forgotten if used. */
+ if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+ mddev->resync_min = 0;
+ mddev->resync_max = MaxSector;
+ } else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+ mddev->resync_min = mddev->curr_resync_completed;
mddev->curr_resync = 0;
- mddev->curr_resync_completed = 0;
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
- /* We completed so max setting can be forgotten. */
- mddev->resync_max = MaxSector;
+ mddev->curr_resync_completed = 0;
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
wake_up(&resync_wait);
set_bit(MD_RECOVERY_DONE, &mddev->recovery);
@@ -6590,6 +6819,7 @@ static int remove_and_add_spares(mddev_t *mddev)
nm, mdname(mddev));
spares++;
md_new_event(mddev);
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
} else
break;
}
@@ -6625,7 +6855,7 @@ void md_check_recovery(mddev_t *mddev)
if (mddev->bitmap)
- bitmap_daemon_work(mddev->bitmap);
+ bitmap_daemon_work(mddev);
if (mddev->ro)
return;
@@ -6995,5 +7225,6 @@ EXPORT_SYMBOL(md_unregister_thread);
EXPORT_SYMBOL(md_wakeup_thread);
EXPORT_SYMBOL(md_check_recovery);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MD RAID framework");
MODULE_ALIAS("md");
MODULE_ALIAS_BLOCKDEV_MAJOR(MD_MAJOR);
diff --git a/drivers/md/md.h b/drivers/md/md.h
index f184b69ef337..8e4c75c00d46 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -97,6 +97,9 @@ struct mdk_rdev_s
atomic_t read_errors; /* number of consecutive read errors that
* we have tried to ignore.
*/
+ struct timespec last_read_error; /* monotonic time since our
+ * last read error
+ */
atomic_t corrected_errors; /* number of corrected read errors,
* for reporting to userspace and storing
* in superblock.
@@ -280,17 +283,38 @@ struct mddev_s
unsigned int max_write_behind; /* 0 = sync */
struct bitmap *bitmap; /* the bitmap for the device */
- struct file *bitmap_file; /* the bitmap file */
- long bitmap_offset; /* offset from superblock of
- * start of bitmap. May be
- * negative, but not '0'
- */
- long default_bitmap_offset; /* this is the offset to use when
- * hot-adding a bitmap. It should
- * eventually be settable by sysfs.
- */
-
+ struct {
+ struct file *file; /* the bitmap file */
+ loff_t offset; /* offset from superblock of
+ * start of bitmap. May be
+ * negative, but not '0'
+ * For external metadata, offset
+ * from start of device.
+ */
+ loff_t default_offset; /* this is the offset to use when
+ * hot-adding a bitmap. It should
+ * eventually be settable by sysfs.
+ */
+ struct mutex mutex;
+ unsigned long chunksize;
+ unsigned long daemon_sleep; /* how many seconds between updates? */
+ unsigned long max_write_behind; /* write-behind mode */
+ int external;
+ } bitmap_info;
+
+ atomic_t max_corr_read_errors; /* max read retries */
struct list_head all_mddevs;
+
+ /* Generic barrier handling.
+ * If there is a pending barrier request, all other
+ * writes are blocked while the devices are flushed.
+ * The last to finish a flush schedules a worker to
+ * submit the barrier request (without the barrier flag),
+ * then submit more flush requests.
+ */
+ struct bio *barrier;
+ atomic_t flush_pending;
+ struct work_struct barrier_work;
};
@@ -353,7 +377,7 @@ struct md_sysfs_entry {
ssize_t (*show)(mddev_t *, char *);
ssize_t (*store)(mddev_t *, const char *, size_t);
};
-
+extern struct attribute_group md_bitmap_group;
static inline char * mdname (mddev_t * mddev)
{
@@ -431,6 +455,7 @@ extern void md_done_sync(mddev_t *mddev, int blocks, int ok);
extern void md_error(mddev_t *mddev, mdk_rdev_t *rdev);
extern int mddev_congested(mddev_t *mddev, int bits);
+extern void md_barrier_request(mddev_t *mddev, struct bio *bio);
extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
sector_t sector, int size, struct page *page);
extern void md_super_wait(mddev_t *mddev);
@@ -443,6 +468,8 @@ extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
extern void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors);
extern int md_check_no_bitmap(mddev_t *mddev);
extern int md_integrity_register(mddev_t *mddev);
-void md_integrity_add_rdev(mdk_rdev_t *rdev, 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);
#endif /* _MD_MD_H */
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index ee7646f974a0..32a662fc55c9 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -145,7 +145,7 @@ static int multipath_make_request (struct request_queue *q, struct bio * bio)
int cpu;
if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
- bio_endio(bio, -EOPNOTSUPP);
+ md_barrier_request(mddev, bio);
return 0;
}
@@ -581,6 +581,7 @@ static void __exit multipath_exit (void)
module_init(multipath_init);
module_exit(multipath_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("simple multi-path personality for MD");
MODULE_ALIAS("md-personality-7"); /* MULTIPATH */
MODULE_ALIAS("md-multipath");
MODULE_ALIAS("md-level--4");
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index d3a4ce06015a..77605cdceaf1 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -453,7 +453,7 @@ static int raid0_make_request(struct request_queue *q, struct bio *bio)
int cpu;
if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
- bio_endio(bio, -EOPNOTSUPP);
+ md_barrier_request(mddev, bio);
return 0;
}
@@ -567,6 +567,7 @@ static void raid0_exit (void)
module_init(raid0_init);
module_exit(raid0_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAID0 (striping) personality for MD");
MODULE_ALIAS("md-personality-2"); /* RAID0 */
MODULE_ALIAS("md-raid0");
MODULE_ALIAS("md-level-0");
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index e07ce2e033a9..859bd3ffe435 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -677,6 +677,7 @@ static void raise_barrier(conf_t *conf)
static void lower_barrier(conf_t *conf)
{
unsigned long flags;
+ BUG_ON(conf->barrier <= 0);
spin_lock_irqsave(&conf->resync_lock, flags);
conf->barrier--;
spin_unlock_irqrestore(&conf->resync_lock, flags);
@@ -801,6 +802,25 @@ static int make_request(struct request_queue *q, struct bio * bio)
md_write_start(mddev, bio); /* wait on superblock update early */
+ if (bio_data_dir(bio) == WRITE &&
+ bio->bi_sector + bio->bi_size/512 > mddev->suspend_lo &&
+ bio->bi_sector < mddev->suspend_hi) {
+ /* As the suspend_* range is controlled by
+ * userspace, we want an interruptible
+ * wait.
+ */
+ DEFINE_WAIT(w);
+ for (;;) {
+ flush_signals(current);
+ prepare_to_wait(&conf->wait_barrier,
+ &w, TASK_INTERRUPTIBLE);
+ if (bio->bi_sector + bio->bi_size/512 <= mddev->suspend_lo ||
+ bio->bi_sector >= mddev->suspend_hi)
+ break;
+ schedule();
+ }
+ finish_wait(&conf->wait_barrier, &w);
+ }
if (unlikely(!mddev->barriers_work &&
bio_rw_flagged(bio, BIO_RW_BARRIER))) {
if (rw == WRITE)
@@ -923,7 +943,8 @@ static int make_request(struct request_queue *q, struct bio * bio)
/* do behind I/O ? */
if (bitmap &&
- atomic_read(&bitmap->behind_writes) < bitmap->max_write_behind &&
+ (atomic_read(&bitmap->behind_writes)
+ < mddev->bitmap_info.max_write_behind) &&
(behind_pages = alloc_behind_pages(bio)) != NULL)
set_bit(R1BIO_BehindIO, &r1_bio->state);
@@ -1941,74 +1962,48 @@ static sector_t raid1_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return mddev->dev_sectors;
}
-static int run(mddev_t *mddev)
+static conf_t *setup_conf(mddev_t *mddev)
{
conf_t *conf;
- int i, j, disk_idx;
+ int i;
mirror_info_t *disk;
mdk_rdev_t *rdev;
+ int err = -ENOMEM;
- if (mddev->level != 1) {
- printk("raid1: %s: raid level not set to mirroring (%d)\n",
- mdname(mddev), mddev->level);
- goto out;
- }
- if (mddev->reshape_position != MaxSector) {
- printk("raid1: %s: reshape_position set but not supported\n",
- mdname(mddev));
- goto out;
- }
- /*
- * copy the already verified devices into our private RAID1
- * bookkeeping area. [whatever we allocate in run(),
- * should be freed in stop()]
- */
conf = kzalloc(sizeof(conf_t), GFP_KERNEL);
- mddev->private = conf;
if (!conf)
- goto out_no_mem;
+ goto abort;
conf->mirrors = kzalloc(sizeof(struct mirror_info)*mddev->raid_disks,
GFP_KERNEL);
if (!conf->mirrors)
- goto out_no_mem;
+ goto abort;
conf->tmppage = alloc_page(GFP_KERNEL);
if (!conf->tmppage)
- goto out_no_mem;
+ goto abort;
- conf->poolinfo = kmalloc(sizeof(*conf->poolinfo), GFP_KERNEL);
+ conf->poolinfo = kzalloc(sizeof(*conf->poolinfo), GFP_KERNEL);
if (!conf->poolinfo)
- goto out_no_mem;
- conf->poolinfo->mddev = NULL;
+ goto abort;
conf->poolinfo->raid_disks = mddev->raid_disks;
conf->r1bio_pool = mempool_create(NR_RAID1_BIOS, r1bio_pool_alloc,
r1bio_pool_free,
conf->poolinfo);
if (!conf->r1bio_pool)
- goto out_no_mem;
+ goto abort;
+
conf->poolinfo->mddev = mddev;
spin_lock_init(&conf->device_lock);
- mddev->queue->queue_lock = &conf->device_lock;
-
list_for_each_entry(rdev, &mddev->disks, same_set) {
- disk_idx = rdev->raid_disk;
+ int disk_idx = rdev->raid_disk;
if (disk_idx >= mddev->raid_disks
|| disk_idx < 0)
continue;
disk = conf->mirrors + disk_idx;
disk->rdev = rdev;
- disk_stack_limits(mddev->gendisk, rdev->bdev,
- rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must never risk
- * violating it, so limit ->max_sector to one PAGE, as
- * a one page request is never in violation.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
- queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
- blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->head_position = 0;
}
@@ -2022,8 +2017,7 @@ static int run(mddev_t *mddev)
bio_list_init(&conf->pending_bio_list);
bio_list_init(&conf->flushing_bio_list);
-
- mddev->degraded = 0;
+ conf->last_used = -1;
for (i = 0; i < conf->raid_disks; i++) {
disk = conf->mirrors + i;
@@ -2031,38 +2025,97 @@ static int run(mddev_t *mddev)
if (!disk->rdev ||
!test_bit(In_sync, &disk->rdev->flags)) {
disk->head_position = 0;
- mddev->degraded++;
if (disk->rdev)
conf->fullsync = 1;
- }
+ } else if (conf->last_used < 0)
+ /*
+ * The first working device is used as a
+ * starting point to read balancing.
+ */
+ conf->last_used = i;
}
- if (mddev->degraded == conf->raid_disks) {
+
+ err = -EIO;
+ if (conf->last_used < 0) {
printk(KERN_ERR "raid1: no operational mirrors for %s\n",
- mdname(mddev));
- goto out_free_conf;
+ mdname(mddev));
+ goto abort;
}
- if (conf->raid_disks - mddev->degraded == 1)
- mddev->recovery_cp = MaxSector;
+ err = -ENOMEM;
+ conf->thread = md_register_thread(raid1d, mddev, NULL);
+ if (!conf->thread) {
+ printk(KERN_ERR
+ "raid1: couldn't allocate thread for %s\n",
+ mdname(mddev));
+ goto abort;
+ }
+
+ return conf;
+
+ abort:
+ if (conf) {
+ if (conf->r1bio_pool)
+ mempool_destroy(conf->r1bio_pool);
+ kfree(conf->mirrors);
+ safe_put_page(conf->tmppage);
+ kfree(conf->poolinfo);
+ kfree(conf);
+ }
+ return ERR_PTR(err);
+}
+static int run(mddev_t *mddev)
+{
+ conf_t *conf;
+ int i;
+ mdk_rdev_t *rdev;
+
+ if (mddev->level != 1) {
+ printk("raid1: %s: raid level not set to mirroring (%d)\n",
+ mdname(mddev), mddev->level);
+ return -EIO;
+ }
+ if (mddev->reshape_position != MaxSector) {
+ printk("raid1: %s: reshape_position set but not supported\n",
+ mdname(mddev));
+ return -EIO;
+ }
/*
- * find the first working one and use it as a starting point
- * to read balancing.
+ * copy the already verified devices into our private RAID1
+ * bookkeeping area. [whatever we allocate in run(),
+ * should be freed in stop()]
*/
- for (j = 0; j < conf->raid_disks &&
- (!conf->mirrors[j].rdev ||
- !test_bit(In_sync, &conf->mirrors[j].rdev->flags)) ; j++)
- /* nothing */;
- conf->last_used = j;
+ if (mddev->private == NULL)
+ conf = setup_conf(mddev);
+ else
+ conf = mddev->private;
+ if (IS_ERR(conf))
+ return PTR_ERR(conf);
- mddev->thread = md_register_thread(raid1d, mddev, NULL);
- if (!mddev->thread) {
- printk(KERN_ERR
- "raid1: couldn't allocate thread for %s\n",
- mdname(mddev));
- goto out_free_conf;
+ mddev->queue->queue_lock = &conf->device_lock;
+ list_for_each_entry(rdev, &mddev->disks, same_set) {
+ disk_stack_limits(mddev->gendisk, rdev->bdev,
+ rdev->data_offset << 9);
+ /* as we don't honour merge_bvec_fn, we must never risk
+ * violating it, so limit ->max_sector to one PAGE, as
+ * a one page request is never in violation.
+ */
+ if (rdev->bdev->bd_disk->queue->merge_bvec_fn &&
+ queue_max_sectors(mddev->queue) > (PAGE_SIZE>>9))
+ blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
}
+ mddev->degraded = 0;
+ for (i=0; i < conf->raid_disks; i++)
+ if (conf->mirrors[i].rdev == NULL ||
+ !test_bit(In_sync, &conf->mirrors[i].rdev->flags) ||
+ test_bit(Faulty, &conf->mirrors[i].rdev->flags))
+ mddev->degraded++;
+
+ if (conf->raid_disks - mddev->degraded == 1)
+ mddev->recovery_cp = MaxSector;
+
if (mddev->recovery_cp != MaxSector)
printk(KERN_NOTICE "raid1: %s is not clean"
" -- starting background reconstruction\n",
@@ -2071,9 +2124,14 @@ static int run(mddev_t *mddev)
"raid1: raid set %s active with %d out of %d mirrors\n",
mdname(mddev), mddev->raid_disks - mddev->degraded,
mddev->raid_disks);
+
/*
* Ok, everything is just fine now
*/
+ mddev->thread = conf->thread;
+ conf->thread = NULL;
+ mddev->private = conf;
+
md_set_array_sectors(mddev, raid1_size(mddev, 0, 0));
mddev->queue->unplug_fn = raid1_unplug;
@@ -2081,23 +2139,6 @@ static int run(mddev_t *mddev)
mddev->queue->backing_dev_info.congested_data = mddev;
md_integrity_register(mddev);
return 0;
-
-out_no_mem:
- printk(KERN_ERR "raid1: couldn't allocate memory for %s\n",
- mdname(mddev));
-
-out_free_conf:
- if (conf) {
- if (conf->r1bio_pool)
- mempool_destroy(conf->r1bio_pool);
- kfree(conf->mirrors);
- safe_put_page(conf->tmppage);
- kfree(conf->poolinfo);
- kfree(conf);
- mddev->private = NULL;
- }
-out:
- return -EIO;
}
static int stop(mddev_t *mddev)
@@ -2271,6 +2312,9 @@ static void raid1_quiesce(mddev_t *mddev, int state)
conf_t *conf = mddev->private;
switch(state) {
+ case 2: /* wake for suspend */
+ wake_up(&conf->wait_barrier);
+ break;
case 1:
raise_barrier(conf);
break;
@@ -2280,6 +2324,23 @@ static void raid1_quiesce(mddev_t *mddev, int state)
}
}
+static void *raid1_takeover(mddev_t *mddev)
+{
+ /* raid1 can take over:
+ * raid5 with 2 devices, any layout or chunk size
+ */
+ if (mddev->level == 5 && mddev->raid_disks == 2) {
+ conf_t *conf;
+ mddev->new_level = 1;
+ mddev->new_layout = 0;
+ mddev->new_chunk_sectors = 0;
+ conf = setup_conf(mddev);
+ if (!IS_ERR(conf))
+ conf->barrier = 1;
+ return conf;
+ }
+ return ERR_PTR(-EINVAL);
+}
static struct mdk_personality raid1_personality =
{
@@ -2299,6 +2360,7 @@ static struct mdk_personality raid1_personality =
.size = raid1_size,
.check_reshape = raid1_reshape,
.quiesce = raid1_quiesce,
+ .takeover = raid1_takeover,
};
static int __init raid_init(void)
@@ -2314,6 +2376,7 @@ static void raid_exit(void)
module_init(raid_init);
module_exit(raid_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAID1 (mirroring) personality for MD");
MODULE_ALIAS("md-personality-3"); /* RAID1 */
MODULE_ALIAS("md-raid1");
MODULE_ALIAS("md-level-1");
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index e87b84deff68..5f2d443ae28a 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -59,6 +59,11 @@ struct r1_private_data_s {
mempool_t *r1bio_pool;
mempool_t *r1buf_pool;
+
+ /* When taking over an array from a different personality, we store
+ * the new thread here until we fully activate the array.
+ */
+ struct mdk_thread_s *thread;
};
typedef struct r1_private_data_s conf_t;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index c2cb7b87b440..d119b7b75e71 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -804,7 +804,7 @@ static int make_request(struct request_queue *q, struct bio * bio)
mdk_rdev_t *blocked_rdev;
if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
- bio_endio(bio, -EOPNOTSUPP);
+ md_barrier_request(mddev, bio);
return 0;
}
@@ -1432,6 +1432,43 @@ static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio)
/*
+ * Used by fix_read_error() to decay the per rdev read_errors.
+ * We halve the read error count for every hour that has elapsed
+ * since the last recorded read error.
+ *
+ */
+static void check_decay_read_errors(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+ struct timespec cur_time_mon;
+ unsigned long hours_since_last;
+ unsigned int read_errors = atomic_read(&rdev->read_errors);
+
+ ktime_get_ts(&cur_time_mon);
+
+ if (rdev->last_read_error.tv_sec == 0 &&
+ rdev->last_read_error.tv_nsec == 0) {
+ /* first time we've seen a read error */
+ rdev->last_read_error = cur_time_mon;
+ return;
+ }
+
+ hours_since_last = (cur_time_mon.tv_sec -
+ rdev->last_read_error.tv_sec) / 3600;
+
+ rdev->last_read_error = cur_time_mon;
+
+ /*
+ * if hours_since_last is > the number of bits in read_errors
+ * just set read errors to 0. We do this to avoid
+ * overflowing the shift of read_errors by hours_since_last.
+ */
+ if (hours_since_last >= 8 * sizeof(read_errors))
+ atomic_set(&rdev->read_errors, 0);
+ else
+ atomic_set(&rdev->read_errors, read_errors >> hours_since_last);
+}
+
+/*
* This is a kernel thread which:
*
* 1. Retries failed read operations on working mirrors.
@@ -1444,6 +1481,43 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
int sect = 0; /* Offset from r10_bio->sector */
int sectors = r10_bio->sectors;
mdk_rdev_t*rdev;
+ int max_read_errors = atomic_read(&mddev->max_corr_read_errors);
+
+ rcu_read_lock();
+ {
+ int d = r10_bio->devs[r10_bio->read_slot].devnum;
+ char b[BDEVNAME_SIZE];
+ int cur_read_error_count = 0;
+
+ rdev = rcu_dereference(conf->mirrors[d].rdev);
+ bdevname(rdev->bdev, b);
+
+ if (test_bit(Faulty, &rdev->flags)) {
+ rcu_read_unlock();
+ /* drive has already been failed, just ignore any
+ more fix_read_error() attempts */
+ return;
+ }
+
+ check_decay_read_errors(mddev, rdev);
+ atomic_inc(&rdev->read_errors);
+ cur_read_error_count = atomic_read(&rdev->read_errors);
+ if (cur_read_error_count > max_read_errors) {
+ rcu_read_unlock();
+ printk(KERN_NOTICE
+ "raid10: %s: Raid device exceeded "
+ "read_error threshold "
+ "[cur %d:max %d]\n",
+ b, cur_read_error_count, max_read_errors);
+ printk(KERN_NOTICE
+ "raid10: %s: Failing raid "
+ "device\n", b);
+ md_error(mddev, conf->mirrors[d].rdev);
+ return;
+ }
+ }
+ rcu_read_unlock();
+
while(sectors) {
int s = sectors;
int sl = r10_bio->read_slot;
@@ -1488,6 +1562,7 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
/* write it back and re-read */
rcu_read_lock();
while (sl != r10_bio->read_slot) {
+ char b[BDEVNAME_SIZE];
int d;
if (sl==0)
sl = conf->copies;
@@ -1503,9 +1578,21 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
r10_bio->devs[sl].addr +
sect + rdev->data_offset,
s<<9, conf->tmppage, WRITE)
- == 0)
+ == 0) {
/* Well, this device is dead */
+ printk(KERN_NOTICE
+ "raid10:%s: read correction "
+ "write failed"
+ " (%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)(sect+
+ rdev->data_offset),
+ bdevname(rdev->bdev, b));
+ printk(KERN_NOTICE "raid10:%s: failing "
+ "drive\n",
+ bdevname(rdev->bdev, b));
md_error(mddev, rdev);
+ }
rdev_dec_pending(rdev, mddev);
rcu_read_lock();
}
@@ -1526,10 +1613,22 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
if (sync_page_io(rdev->bdev,
r10_bio->devs[sl].addr +
sect + rdev->data_offset,
- s<<9, conf->tmppage, READ) == 0)
+ s<<9, conf->tmppage,
+ READ) == 0) {
/* Well, this device is dead */
+ printk(KERN_NOTICE
+ "raid10:%s: unable to read back "
+ "corrected sectors"
+ " (%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)(sect+
+ rdev->data_offset),
+ bdevname(rdev->bdev, b));
+ printk(KERN_NOTICE "raid10:%s: failing drive\n",
+ bdevname(rdev->bdev, b));
+
md_error(mddev, rdev);
- else
+ } else {
printk(KERN_INFO
"raid10:%s: read error corrected"
" (%d sectors at %llu on %s)\n",
@@ -1537,6 +1636,7 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
(unsigned long long)(sect+
rdev->data_offset),
bdevname(rdev->bdev, b));
+ }
rdev_dec_pending(rdev, mddev);
rcu_read_lock();
@@ -2275,13 +2375,6 @@ static void raid10_quiesce(mddev_t *mddev, int state)
lower_barrier(conf);
break;
}
- if (mddev->thread) {
- if (mddev->bitmap)
- mddev->thread->timeout = mddev->bitmap->daemon_sleep * HZ;
- else
- mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
- md_wakeup_thread(mddev->thread);
- }
}
static struct mdk_personality raid10_personality =
@@ -2315,6 +2408,7 @@ static void raid_exit(void)
module_init(raid_init);
module_exit(raid_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAID10 (striped mirror) personality for MD");
MODULE_ALIAS("md-personality-9"); /* RAID10 */
MODULE_ALIAS("md-raid10");
MODULE_ALIAS("md-level-10");
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index d29215d966da..e84204eb12df 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2947,6 +2947,7 @@ static void handle_stripe5(struct stripe_head *sh)
struct r5dev *dev;
mdk_rdev_t *blocked_rdev = NULL;
int prexor;
+ int dec_preread_active = 0;
memset(&s, 0, sizeof(s));
pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d "
@@ -3096,12 +3097,8 @@ static void handle_stripe5(struct stripe_head *sh)
set_bit(STRIPE_INSYNC, &sh->state);
}
}
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
- atomic_dec(&conf->preread_active_stripes);
- if (atomic_read(&conf->preread_active_stripes) <
- IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
- }
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ dec_preread_active = 1;
}
/* Now to consider new write requests and what else, if anything
@@ -3208,6 +3205,16 @@ static void handle_stripe5(struct stripe_head *sh)
ops_run_io(sh, &s);
+ if (dec_preread_active) {
+ /* We delay this until after ops_run_io so that if make_request
+ * is waiting on a barrier, it won't continue until the writes
+ * have actually been submitted.
+ */
+ atomic_dec(&conf->preread_active_stripes);
+ if (atomic_read(&conf->preread_active_stripes) <
+ IO_THRESHOLD)
+ md_wakeup_thread(conf->mddev->thread);
+ }
return_io(return_bi);
}
@@ -3221,6 +3228,7 @@ static void handle_stripe6(struct stripe_head *sh)
struct r6_state r6s;
struct r5dev *dev, *pdev, *qdev;
mdk_rdev_t *blocked_rdev = NULL;
+ int dec_preread_active = 0;
pr_debug("handling stripe %llu, state=%#lx cnt=%d, "
"pd_idx=%d, qd_idx=%d\n, check:%d, reconstruct:%d\n",
@@ -3358,7 +3366,6 @@ static void handle_stripe6(struct stripe_head *sh)
* completed
*/
if (sh->reconstruct_state == reconstruct_state_drain_result) {
- int qd_idx = sh->qd_idx;
sh->reconstruct_state = reconstruct_state_idle;
/* All the 'written' buffers and the parity blocks are ready to
@@ -3380,12 +3387,8 @@ static void handle_stripe6(struct stripe_head *sh)
set_bit(STRIPE_INSYNC, &sh->state);
}
}
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
- atomic_dec(&conf->preread_active_stripes);
- if (atomic_read(&conf->preread_active_stripes) <
- IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
- }
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ dec_preread_active = 1;
}
/* Now to consider new write requests and what else, if anything
@@ -3494,6 +3497,18 @@ static void handle_stripe6(struct stripe_head *sh)
ops_run_io(sh, &s);
+
+ if (dec_preread_active) {
+ /* We delay this until after ops_run_io so that if make_request
+ * is waiting on a barrier, it won't continue until the writes
+ * have actually been submitted.
+ */
+ atomic_dec(&conf->preread_active_stripes);
+ if (atomic_read(&conf->preread_active_stripes) <
+ IO_THRESHOLD)
+ md_wakeup_thread(conf->mddev->thread);
+ }
+
return_io(return_bi);
}
@@ -3741,7 +3756,7 @@ static int chunk_aligned_read(struct request_queue *q, struct bio * raid_bio)
{
mddev_t *mddev = q->queuedata;
raid5_conf_t *conf = mddev->private;
- unsigned int dd_idx;
+ int dd_idx;
struct bio* align_bi;
mdk_rdev_t *rdev;
@@ -3866,7 +3881,13 @@ static int make_request(struct request_queue *q, struct bio * bi)
int cpu, remaining;
if (unlikely(bio_rw_flagged(bi, BIO_RW_BARRIER))) {
- bio_endio(bi, -EOPNOTSUPP);
+ /* Drain all pending writes. We only really need
+ * to ensure they have been submitted, but this is
+ * easier.
+ */
+ mddev->pers->quiesce(mddev, 1);
+ mddev->pers->quiesce(mddev, 0);
+ md_barrier_request(mddev, bi);
return 0;
}
@@ -3990,6 +4011,9 @@ static int make_request(struct request_queue *q, struct bio * bi)
finish_wait(&conf->wait_for_overlap, &w);
set_bit(STRIPE_HANDLE, &sh->state);
clear_bit(STRIPE_DELAYED, &sh->state);
+ if (mddev->barrier &&
+ !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ atomic_inc(&conf->preread_active_stripes);
release_stripe(sh);
} else {
/* cannot get stripe for read-ahead, just give-up */
@@ -4009,6 +4033,14 @@ static int make_request(struct request_queue *q, struct bio * bi)
bio_endio(bi, 0);
}
+
+ if (mddev->barrier) {
+ /* We need to wait for the stripes to all be handled.
+ * So: wait for preread_active_stripes to drop to 0.
+ */
+ wait_event(mddev->thread->wqueue,
+ atomic_read(&conf->preread_active_stripes) == 0);
+ }
return 0;
}
@@ -5860,6 +5892,7 @@ static void raid5_exit(void)
module_init(raid5_init);
module_exit(raid5_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAID4/5/6 (striping with parity) personality for MD");
MODULE_ALIAS("md-personality-4"); /* RAID5 */
MODULE_ALIAS("md-raid5");
MODULE_ALIAS("md-raid4");
diff --git a/drivers/md/raid6algos.c b/drivers/md/raid6algos.c
index 866215ac7f25..bffc61bff5ab 100644
--- a/drivers/md/raid6algos.c
+++ b/drivers/md/raid6algos.c
@@ -31,25 +31,6 @@ EXPORT_SYMBOL(raid6_empty_zero_page);
struct raid6_calls raid6_call;
EXPORT_SYMBOL_GPL(raid6_call);
-/* Various routine sets */
-extern const struct raid6_calls raid6_intx1;
-extern const struct raid6_calls raid6_intx2;
-extern const struct raid6_calls raid6_intx4;
-extern const struct raid6_calls raid6_intx8;
-extern const struct raid6_calls raid6_intx16;
-extern const struct raid6_calls raid6_intx32;
-extern const struct raid6_calls raid6_mmxx1;
-extern const struct raid6_calls raid6_mmxx2;
-extern const struct raid6_calls raid6_sse1x1;
-extern const struct raid6_calls raid6_sse1x2;
-extern const struct raid6_calls raid6_sse2x1;
-extern const struct raid6_calls raid6_sse2x2;
-extern const struct raid6_calls raid6_sse2x4;
-extern const struct raid6_calls raid6_altivec1;
-extern const struct raid6_calls raid6_altivec2;
-extern const struct raid6_calls raid6_altivec4;
-extern const struct raid6_calls raid6_altivec8;
-
const struct raid6_calls * const raid6_algos[] = {
&raid6_intx1,
&raid6_intx2,
@@ -169,3 +150,4 @@ static void raid6_exit(void)
subsys_initcall(raid6_select_algo);
module_exit(raid6_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
new file mode 100644
index 000000000000..4dde7d180a32
--- /dev/null
+++ b/drivers/media/IR/Kconfig
@@ -0,0 +1,9 @@
+config IR_CORE
+ tristate
+ depends on INPUT
+ default INPUT
+
+config VIDEO_IR
+ tristate
+ depends on IR_CORE
+ default IR_CORE
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
new file mode 100644
index 000000000000..df5ddb4bbbf7
--- /dev/null
+++ b/drivers/media/IR/Makefile
@@ -0,0 +1,5 @@
+ir-common-objs := ir-functions.o ir-keymaps.o
+ir-core-objs := ir-keytable.o
+
+obj-$(CONFIG_IR_CORE) += ir-core.o
+obj-$(CONFIG_VIDEO_IR) += ir-common.o
diff --git a/drivers/media/common/ir-functions.c b/drivers/media/IR/ir-functions.c
index e616f624ceaa..776a136616d6 100644
--- a/drivers/media/common/ir-functions.c
+++ b/drivers/media/IR/ir-functions.c
@@ -34,9 +34,6 @@ static int repeat = 1;
module_param(repeat, int, 0444);
MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)");
-int media_ir_debug; /* media_ir_debug level (0,1,2) */
-module_param_named(debug, media_ir_debug, int, 0644);
-
/* -------------------------------------------------------------------------- */
static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
@@ -55,25 +52,10 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
/* -------------------------------------------------------------------------- */
int ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
- int ir_type, struct ir_scancode_table *ir_codes)
+ int ir_type)
{
ir->ir_type = ir_type;
- ir->keytable.size = ir_roundup_tablesize(ir_codes->size);
- ir->keytable.scan = kzalloc(ir->keytable.size *
- sizeof(struct ir_scancode), GFP_KERNEL);
- if (!ir->keytable.scan)
- return -ENOMEM;
-
- IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n",
- ir->keytable.size,
- ir->keytable.size * sizeof(ir->keytable.scan));
-
- ir_copy_table(&ir->keytable, ir_codes);
- ir_set_keycode_table(dev, &ir->keytable);
-
- clear_bit(0, dev->keybit);
- set_bit(EV_KEY, dev->evbit);
if (repeat)
set_bit(EV_REP, dev->evbit);
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/IR/ir-keymaps.c
index 328c973a0838..9bbe6b1e9871 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/IR/ir-keymaps.c
@@ -1847,76 +1847,6 @@ struct ir_scancode_table ir_codes_hauppauge_new_table = {
};
EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table);
-/*
- * Hauppauge:the newer, gray remotes (seems there are multiple
- * slightly different versions), shipped with cx88+ivtv cards.
- *
- * This table contains the complete RC5 code, instead of just the data part
- */
-static struct ir_scancode ir_codes_rc5_hauppauge_new[] = {
- /* Keys 0 to 9 */
- { 0x1e00, KEY_0 },
- { 0x1e01, KEY_1 },
- { 0x1e02, KEY_2 },
- { 0x1e03, KEY_3 },
- { 0x1e04, KEY_4 },
- { 0x1e05, KEY_5 },
- { 0x1e06, KEY_6 },
- { 0x1e07, KEY_7 },
- { 0x1e08, KEY_8 },
- { 0x1e09, KEY_9 },
-
- { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */
- { 0x1e0b, KEY_RED }, /* red button */
- { 0x1e0c, KEY_RADIO },
- { 0x1e0d, KEY_MENU },
- { 0x1e0e, KEY_SUBTITLE }, /* also the # key */
- { 0x1e0f, KEY_MUTE },
- { 0x1e10, KEY_VOLUMEUP },
- { 0x1e11, KEY_VOLUMEDOWN },
- { 0x1e12, KEY_PREVIOUS }, /* previous channel */
- { 0x1e14, KEY_UP },
- { 0x1e15, KEY_DOWN },
- { 0x1e16, KEY_LEFT },
- { 0x1e17, KEY_RIGHT },
- { 0x1e18, KEY_VIDEO }, /* Videos */
- { 0x1e19, KEY_AUDIO }, /* Music */
- /* 0x1e1a: Pictures - presume this means
- "Multimedia Home Platform" -
- no "PICTURES" key in input.h
- */
- { 0x1e1a, KEY_MHP },
-
- { 0x1e1b, KEY_EPG }, /* Guide */
- { 0x1e1c, KEY_TV },
- { 0x1e1e, KEY_NEXTSONG }, /* skip >| */
- { 0x1e1f, KEY_EXIT }, /* back/exit */
- { 0x1e20, KEY_CHANNELUP }, /* channel / program + */
- { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */
- { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */
- { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */
- { 0x1e25, KEY_ENTER }, /* OK */
- { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */
- { 0x1e29, KEY_BLUE }, /* blue key */
- { 0x1e2e, KEY_GREEN }, /* green button */
- { 0x1e30, KEY_PAUSE }, /* pause */
- { 0x1e32, KEY_REWIND }, /* backward << */
- { 0x1e34, KEY_FASTFORWARD }, /* forward >> */
- { 0x1e35, KEY_PLAY },
- { 0x1e36, KEY_STOP },
- { 0x1e37, KEY_RECORD }, /* recording */
- { 0x1e38, KEY_YELLOW }, /* yellow key */
- { 0x1e3b, KEY_SELECT }, /* top right button */
- { 0x1e3c, KEY_ZOOM }, /* full */
- { 0x1e3d, KEY_POWER }, /* system power (green button) */
-};
-
-struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = {
- .scan = ir_codes_rc5_hauppauge_new,
- .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new),
-};
-EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table);
-
static struct ir_scancode ir_codes_npgtech[] = {
{ 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */
{ 0x2a, KEY_FRONT },
@@ -3314,3 +3244,152 @@ struct ir_scancode_table ir_codes_gadmei_rm008z_table = {
};
EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table);
+/*************************************************************
+ * COMPLETE SCANCODE TABLES
+ * Instead of just a partial scancode, the tables bellow
+ * contains the complete scancode and the receiver protocol
+ *************************************************************/
+
+/*
+ * Hauppauge:the newer, gray remotes (seems there are multiple
+ * slightly different versions), shipped with cx88+ivtv cards.
+ *
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+static struct ir_scancode ir_codes_rc5_hauppauge_new[] = {
+ /* Keys 0 to 9 */
+ { 0x1e00, KEY_0 },
+ { 0x1e01, KEY_1 },
+ { 0x1e02, KEY_2 },
+ { 0x1e03, KEY_3 },
+ { 0x1e04, KEY_4 },
+ { 0x1e05, KEY_5 },
+ { 0x1e06, KEY_6 },
+ { 0x1e07, KEY_7 },
+ { 0x1e08, KEY_8 },
+ { 0x1e09, KEY_9 },
+
+ { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */
+ { 0x1e0b, KEY_RED }, /* red button */
+ { 0x1e0c, KEY_RADIO },
+ { 0x1e0d, KEY_MENU },
+ { 0x1e0e, KEY_SUBTITLE }, /* also the # key */
+ { 0x1e0f, KEY_MUTE },
+ { 0x1e10, KEY_VOLUMEUP },
+ { 0x1e11, KEY_VOLUMEDOWN },
+ { 0x1e12, KEY_PREVIOUS }, /* previous channel */
+ { 0x1e14, KEY_UP },
+ { 0x1e15, KEY_DOWN },
+ { 0x1e16, KEY_LEFT },
+ { 0x1e17, KEY_RIGHT },
+ { 0x1e18, KEY_VIDEO }, /* Videos */
+ { 0x1e19, KEY_AUDIO }, /* Music */
+ /* 0x1e1a: Pictures - presume this means
+ "Multimedia Home Platform" -
+ no "PICTURES" key in input.h
+ */
+ { 0x1e1a, KEY_MHP },
+
+ { 0x1e1b, KEY_EPG }, /* Guide */
+ { 0x1e1c, KEY_TV },
+ { 0x1e1e, KEY_NEXTSONG }, /* skip >| */
+ { 0x1e1f, KEY_EXIT }, /* back/exit */
+ { 0x1e20, KEY_CHANNELUP }, /* channel / program + */
+ { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */
+ { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */
+ { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */
+ { 0x1e25, KEY_ENTER }, /* OK */
+ { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */
+ { 0x1e29, KEY_BLUE }, /* blue key */
+ { 0x1e2e, KEY_GREEN }, /* green button */
+ { 0x1e30, KEY_PAUSE }, /* pause */
+ { 0x1e32, KEY_REWIND }, /* backward << */
+ { 0x1e34, KEY_FASTFORWARD }, /* forward >> */
+ { 0x1e35, KEY_PLAY },
+ { 0x1e36, KEY_STOP },
+ { 0x1e37, KEY_RECORD }, /* recording */
+ { 0x1e38, KEY_YELLOW }, /* yellow key */
+ { 0x1e3b, KEY_SELECT }, /* top right button */
+ { 0x1e3c, KEY_ZOOM }, /* full */
+ { 0x1e3d, KEY_POWER }, /* system power (green button) */
+};
+
+struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = {
+ .scan = ir_codes_rc5_hauppauge_new,
+ .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new),
+ .ir_type = IR_TYPE_RC5,
+};
+EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table);
+
+/* Terratec Cinergy Hybrid T USB XS FM
+ Mauro Carvalho Chehab <mchehab@redhat.com>
+ */
+static struct ir_scancode ir_codes_nec_terratec_cinergy_xs[] = {
+ { 0x1441, KEY_HOME},
+ { 0x1401, KEY_POWER2},
+
+ { 0x1442, KEY_MENU}, /* DVD menu */
+ { 0x1443, KEY_SUBTITLE},
+ { 0x1444, KEY_TEXT}, /* Teletext */
+ { 0x1445, KEY_DELETE},
+
+ { 0x1402, KEY_1},
+ { 0x1403, KEY_2},
+ { 0x1404, KEY_3},
+ { 0x1405, KEY_4},
+ { 0x1406, KEY_5},
+ { 0x1407, KEY_6},
+ { 0x1408, KEY_7},
+ { 0x1409, KEY_8},
+ { 0x140a, KEY_9},
+ { 0x140c, KEY_0},
+
+ { 0x140b, KEY_TUNER}, /* AV */
+ { 0x140d, KEY_MODE}, /* A.B */
+
+ { 0x1446, KEY_TV},
+ { 0x1447, KEY_DVD},
+ { 0x1449, KEY_VIDEO},
+ { 0x144a, KEY_RADIO}, /* Music */
+ { 0x144b, KEY_CAMERA}, /* PIC */
+
+ { 0x1410, KEY_UP},
+ { 0x1411, KEY_LEFT},
+ { 0x1412, KEY_OK},
+ { 0x1413, KEY_RIGHT},
+ { 0x1414, KEY_DOWN},
+
+ { 0x140f, KEY_EPG},
+ { 0x1416, KEY_INFO},
+ { 0x144d, KEY_BACKSPACE},
+
+ { 0x141c, KEY_VOLUMEUP},
+ { 0x141e, KEY_VOLUMEDOWN},
+
+ { 0x144c, KEY_PLAY},
+ { 0x141d, KEY_MUTE},
+
+ { 0x141b, KEY_CHANNELUP},
+ { 0x141f, KEY_CHANNELDOWN},
+
+ { 0x1417, KEY_RED},
+ { 0x1418, KEY_GREEN},
+ { 0x1419, KEY_YELLOW},
+ { 0x141a, KEY_BLUE},
+
+ { 0x1458, KEY_RECORD},
+ { 0x1448, KEY_STOP},
+ { 0x1440, KEY_PAUSE},
+
+ { 0x1454, KEY_LAST},
+ { 0x144e, KEY_REWIND},
+ { 0x144f, KEY_FASTFORWARD},
+ { 0x145c, KEY_NEXT},
+};
+struct ir_scancode_table ir_codes_nec_terratec_cinergy_xs_table = {
+ .scan = ir_codes_nec_terratec_cinergy_xs,
+ .size = ARRAY_SIZE(ir_codes_nec_terratec_cinergy_xs),
+ .ir_type = IR_TYPE_NEC,
+};
+EXPORT_SYMBOL_GPL(ir_codes_nec_terratec_cinergy_xs_table);
+
diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/IR/ir-keytable.c
index 26ce5bc2fdd5..bff7a5356037 100644
--- a/drivers/media/common/ir-keytable.c
+++ b/drivers/media/IR/ir-keytable.c
@@ -1,10 +1,19 @@
/* ir-register.c - handle IR scancode->keycode tables
*
* Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
-#include <linux/usb/input.h>
+#include <linux/usb/input.h>
#include <media/ir-common.h>
#define IR_TAB_MIN_SIZE 32
@@ -72,6 +81,7 @@ int ir_roundup_tablesize(int n_elems)
return n_elems;
}
+EXPORT_SYMBOL_GPL(ir_roundup_tablesize);
/**
* ir_copy_table() - copies a keytable, discarding the unused entries
@@ -100,6 +110,7 @@ int ir_copy_table(struct ir_scancode_table *destin,
return 0;
}
+EXPORT_SYMBOL_GPL(ir_copy_table);
/**
* ir_getkeycode() - get a keycode at the evdev scancode ->keycode table
@@ -114,7 +125,8 @@ static int ir_getkeycode(struct input_dev *dev,
int scancode, int *keycode)
{
int elem;
- struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_input_dev *ir_dev = input_get_drvdata(dev);
+ struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
elem = ir_seek_table(rc_tab, scancode);
if (elem >= 0) {
@@ -136,7 +148,6 @@ static int ir_getkeycode(struct input_dev *dev,
return 0;
}
-
/**
* ir_is_resize_needed() - Check if the table needs rezise
* @table: keycode table that may need to resize
@@ -286,7 +297,8 @@ static int ir_setkeycode(struct input_dev *dev,
int scancode, int keycode)
{
int rc = 0;
- struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_input_dev *ir_dev = input_get_drvdata(dev);
+ struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
struct ir_scancode *keymap = rc_tab->scan;
unsigned long flags;
@@ -360,7 +372,8 @@ static int ir_setkeycode(struct input_dev *dev,
*/
u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
{
- struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_input_dev *ir_dev = input_get_drvdata(dev);
+ struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
struct ir_scancode *keymap = rc_tab->scan;
int elem;
@@ -378,9 +391,10 @@ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
/* Reports userspace that an unknown keycode were got */
return KEY_RESERVED;
}
+EXPORT_SYMBOL_GPL(ir_g_keycode_from_table);
/**
- * ir_set_keycode_table() - sets the IR keycode table and add the handlers
+ * ir_input_register() - sets the IR keycode table and add the handlers
* for keymap table get/set
* @input_dev: the struct input_dev descriptor of the device
* @rc_tab: the struct ir_scancode_table table of scancode/keymap
@@ -389,17 +403,34 @@ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
* an IR.
* It should be called before registering the IR device.
*/
-int ir_set_keycode_table(struct input_dev *input_dev,
- struct ir_scancode_table *rc_tab)
+int ir_input_register(struct input_dev *input_dev,
+ struct ir_scancode_table *rc_tab)
{
- struct ir_scancode *keymap = rc_tab->scan;
- int i;
-
- spin_lock_init(&rc_tab->lock);
+ struct ir_input_dev *ir_dev;
+ struct ir_scancode *keymap = rc_tab->scan;
+ int i, rc;
if (rc_tab->scan == NULL || !rc_tab->size)
return -EINVAL;
+ ir_dev = kzalloc(sizeof(*ir_dev), GFP_KERNEL);
+ if (!ir_dev)
+ return -ENOMEM;
+
+ spin_lock_init(&rc_tab->lock);
+
+ ir_dev->rc_tab.size = ir_roundup_tablesize(rc_tab->size);
+ ir_dev->rc_tab.scan = kzalloc(ir_dev->rc_tab.size *
+ sizeof(struct ir_scancode), GFP_KERNEL);
+ if (!ir_dev->rc_tab.scan)
+ return -ENOMEM;
+
+ IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n",
+ ir_dev->rc_tab.size,
+ ir_dev->rc_tab.size * sizeof(ir_dev->rc_tab.scan));
+
+ ir_copy_table(&ir_dev->rc_tab, rc_tab);
+
/* set the bits for the keys */
IR_dprintk(1, "key map size: %d\n", rc_tab->size);
for (i = 0; i < rc_tab->size; i++) {
@@ -407,23 +438,48 @@ int ir_set_keycode_table(struct input_dev *input_dev,
i, keymap[i].keycode);
set_bit(keymap[i].keycode, input_dev->keybit);
}
+ clear_bit(0, input_dev->keybit);
+
+ set_bit(EV_KEY, input_dev->evbit);
input_dev->getkeycode = ir_getkeycode;
input_dev->setkeycode = ir_setkeycode;
- input_set_drvdata(input_dev, rc_tab);
+ input_set_drvdata(input_dev, ir_dev);
- return 0;
+ rc = input_register_device(input_dev);
+ if (rc < 0) {
+ kfree(rc_tab->scan);
+ kfree(ir_dev);
+ input_set_drvdata(input_dev, NULL);
+ }
+
+ return rc;
}
+EXPORT_SYMBOL_GPL(ir_input_register);
-void ir_input_free(struct input_dev *dev)
+void ir_input_unregister(struct input_dev *dev)
{
- struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_input_dev *ir_dev = input_get_drvdata(dev);
+ struct ir_scancode_table *rc_tab;
+
+ if (!ir_dev)
+ return;
IR_dprintk(1, "Freed keycode table\n");
+ rc_tab = &ir_dev->rc_tab;
rc_tab->size = 0;
kfree(rc_tab->scan);
rc_tab->scan = NULL;
+
+ kfree(ir_dev);
+ input_unregister_device(dev);
}
-EXPORT_SYMBOL_GPL(ir_input_free);
+EXPORT_SYMBOL_GPL(ir_input_unregister);
+
+int ir_core_debug; /* ir_debug level (0,1,2) */
+EXPORT_SYMBOL_GPL(ir_core_debug);
+module_param_named(debug, ir_core_debug, int, 0644);
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index ba69beeb0e21..a28541b2b1a2 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -99,6 +99,7 @@ config VIDEO_MEDIA
comment "Multimedia drivers"
source "drivers/media/common/Kconfig"
+source "drivers/media/IR/Kconfig"
#
# Tuner drivers for DVB and V4L
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 09a829d8a7e7..499b0810d019 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -2,7 +2,7 @@
# Makefile for the kernel multimedia device drivers.
#
-obj-y += common/ video/
+obj-y += common/ IR/ video/
obj-$(CONFIG_VIDEO_DEV) += radio/
obj-$(CONFIG_DVB_CORE) += dvb/
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index 169b337b7c9d..e3ec9639321b 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,8 +1,6 @@
saa7146-objs := saa7146_i2c.o saa7146_core.o
saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
-ir-common-objs := ir-functions.o ir-keymaps.o ir-keytable.o
obj-y += tuners/
obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
-obj-$(CONFIG_VIDEO_IR) += ir-common.o
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 620f655fa9c5..7364b9642d00 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -1,7 +1,5 @@
#include <media/saa7146_vv.h>
-#define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1)
-
/****************************************************************************/
/* resource management functions, shamelessly stolen from saa7134 driver */
@@ -194,43 +192,24 @@ void saa7146_buffer_timeout(unsigned long data)
static int fops_open(struct file *file)
{
- unsigned int minor = video_devdata(file)->minor;
- struct saa7146_dev *h = NULL, *dev = NULL;
- struct list_head *list;
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = video_drvdata(file);
struct saa7146_fh *fh = NULL;
int result = 0;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ enum v4l2_buf_type type;
- DEB_EE(("file:%p, minor:%d\n", file, minor));
+ DEB_EE(("file:%p, dev:%s\n", file, video_device_node_name(vdev)));
if (mutex_lock_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
- list_for_each(list,&saa7146_devices) {
- h = list_entry(list, struct saa7146_dev, item);
- if( NULL == h->vv_data ) {
- DEB_D(("device %p has not registered video devices.\n",h));
- continue;
- }
- DEB_D(("trying: %p @ major %d,%d\n",h,h->vv_data->video_minor,h->vv_data->vbi_minor));
-
- if (h->vv_data->video_minor == minor) {
- dev = h;
- }
- if (h->vv_data->vbi_minor == minor) {
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- dev = h;
- }
- }
- if (NULL == dev) {
- DEB_S(("no such video device.\n"));
- result = -ENODEV;
- goto out;
- }
-
DEB_D(("using: %p\n",dev));
+ type = vdev->vfl_type == VFL_TYPE_GRABBER
+ ? V4L2_BUF_TYPE_VIDEO_CAPTURE
+ : V4L2_BUF_TYPE_VBI_CAPTURE;
+
/* check if an extension is registered */
if( NULL == dev->ext ) {
DEB_S(("no extension registered for this device.\n"));
@@ -474,9 +453,6 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
configuration data) */
dev->ext_vv_data = ext_vv;
- vv->video_minor = -1;
- vv->vbi_minor = -1;
-
vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
if( NULL == vv->d_clipping.cpu_addr ) {
ERR(("out of memory. aborting.\n"));
@@ -515,7 +491,6 @@ EXPORT_SYMBOL_GPL(saa7146_vv_release);
int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
char *name, int type)
{
- struct saa7146_vv *vv = dev->vv_data;
struct video_device *vfd;
int err;
int i;
@@ -543,15 +518,8 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
return err;
}
- if( VFL_TYPE_GRABBER == type ) {
- vv->video_minor = vfd->minor;
- INFO(("%s: registered device video%d [v4l2]\n",
- dev->name, vfd->num));
- } else {
- vv->vbi_minor = vfd->minor;
- INFO(("%s: registered device vbi%d [v4l2]\n",
- dev->name, vfd->num));
- }
+ INFO(("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd)));
*vid = vfd;
return 0;
@@ -560,16 +528,8 @@ EXPORT_SYMBOL_GPL(saa7146_register_device);
int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
{
- struct saa7146_vv *vv = dev->vv_data;
-
DEB_EE(("dev:%p\n",dev));
- if ((*vid)->vfl_type == VFL_TYPE_GRABBER) {
- vv->video_minor = -1;
- } else {
- vv->vbi_minor = -1;
- }
-
video_unregister_device(*vid);
*vid = NULL;
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index 53e3f2a7d31a..f0f483ac8b89 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -589,7 +589,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
"pci-%s/ir0", pci_name(dm1105->pdev));
- err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type);
if (err < 0) {
input_free_device(input_dev);
return err;
@@ -611,20 +611,14 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
INIT_WORK(&dm1105->ir.work, dm1105_emit_key);
- err = input_register_device(input_dev);
- if (err) {
- ir_input_free(input_dev);
- input_free_device(input_dev);
- return err;
- }
+ err = ir_input_register(input_dev, ir_codes);
- return 0;
+ return err;
}
void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
{
- ir_input_free(dm1105->ir.input_dev);
- input_unregister_device(dm1105->ir.input_dev);
+ ir_input_unregister(dm1105->ir.input_dev);
}
static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 2dee1bf73577..1b249897c9fb 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -265,9 +265,13 @@ config DVB_USB_DW2102
select DVB_TDA10021 if !DVB_FE_CUSTOMISE
select DVB_MT312 if !DVB_FE_CUSTOMISE
select DVB_ZL10039 if !DVB_FE_CUSTOMISE
+ select DVB_DS3000 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ select DVB_STV6110 if !DVB_FE_CUSTOMISE
+ select DVB_STV0900 if !DVB_FE_CUSTOMISE
help
- Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers
- and the TeVii S650, S630.
+ Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0
+ receivers.
config DVB_USB_CINERGY_T2
tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h
index 8b544fe79b0d..495a90577c5f 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/dvb/dvb-usb/dib0700.h
@@ -20,20 +20,22 @@ extern int dvb_usb_dib0700_debug;
#define deb_fwdata(args...) dprintk(dvb_usb_dib0700_debug,0x04,args)
#define deb_data(args...) dprintk(dvb_usb_dib0700_debug,0x08,args)
-#define REQUEST_I2C_READ 0x2
-#define REQUEST_I2C_WRITE 0x3
-#define REQUEST_POLL_RC 0x4 /* deprecated in firmware v1.20 */
-#define REQUEST_JUMPRAM 0x8
-#define REQUEST_SET_CLOCK 0xB
-#define REQUEST_SET_GPIO 0xC
-#define REQUEST_ENABLE_VIDEO 0xF
+#define REQUEST_SET_USB_XFER_LEN 0x0 /* valid only for firmware version */
+ /* higher than 1.21 */
+#define REQUEST_I2C_READ 0x2
+#define REQUEST_I2C_WRITE 0x3
+#define REQUEST_POLL_RC 0x4 /* deprecated in firmware v1.20 */
+#define REQUEST_JUMPRAM 0x8
+#define REQUEST_SET_CLOCK 0xB
+#define REQUEST_SET_GPIO 0xC
+#define REQUEST_ENABLE_VIDEO 0xF
// 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog)
// 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " )
-#define REQUEST_SET_RC 0x11
-#define REQUEST_NEW_I2C_READ 0x12
-#define REQUEST_NEW_I2C_WRITE 0x13
-#define REQUEST_GET_VERSION 0x15
+#define REQUEST_SET_RC 0x11
+#define REQUEST_NEW_I2C_READ 0x12
+#define REQUEST_NEW_I2C_WRITE 0x13
+#define REQUEST_GET_VERSION 0x15
struct dib0700_state {
u8 channel_state;
@@ -44,6 +46,8 @@ struct dib0700_state {
u8 is_dib7000pc;
u8 fw_use_new_i2c_api;
u8 disable_streaming_master_mode;
+ u32 fw_version;
+ u32 nb_packet_buffer_size;
};
extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index db7f7f79a66c..0d3c9a9a33be 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -17,6 +17,14 @@ int dvb_usb_dib0700_ir_proto = 1;
module_param(dvb_usb_dib0700_ir_proto, int, 0644);
MODULE_PARM_DESC(dvb_usb_dib0700_ir_proto, "set ir protocol (0=NEC, 1=RC5 (default), 2=RC6).");
+static int nb_packet_buffer_size = 21;
+module_param(nb_packet_buffer_size, int, 0644);
+MODULE_PARM_DESC(nb_packet_buffer_size,
+ "Set the dib0700 driver data buffer size. This parameter "
+ "corresponds to the number of TS packets. The actual size of "
+ "the data buffer corresponds to this parameter "
+ "multiplied by 188 (default: 21)");
+
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
@@ -28,10 +36,14 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
REQUEST_GET_VERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
b, sizeof(b), USB_CTRL_GET_TIMEOUT);
- *hwversion = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
- *romversion = (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | b[7];
- *ramversion = (b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11];
- *fwtype = (b[12] << 24) | (b[13] << 16) | (b[14] << 8) | b[15];
+ if (hwversion != NULL)
+ *hwversion = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+ if (romversion != NULL)
+ *romversion = (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | b[7];
+ if (ramversion != NULL)
+ *ramversion = (b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11];
+ if (fwtype != NULL)
+ *fwtype = (b[12] << 24) | (b[13] << 16) | (b[14] << 8) | b[15];
return ret;
}
@@ -97,6 +109,27 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_
return dib0700_ctrl_wr(d,buf,3);
}
+static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
+{
+ struct dib0700_state *st = d->priv;
+ u8 b[3];
+ int ret;
+
+ if (st->fw_version >= 0x10201) {
+ b[0] = REQUEST_SET_USB_XFER_LEN;
+ b[1] = (nb_ts_packets >> 8)&0xff;
+ b[2] = nb_ts_packets & 0xff;
+
+ deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
+
+ ret = dib0700_ctrl_wr(d, b, 3);
+ } else {
+ deb_info("this firmware does not allow to change the USB xfer len\n");
+ ret = -EIO;
+ }
+ return ret;
+}
+
/*
* I2C master xfer function (supported in 1.20 firmware)
*/
@@ -328,7 +361,9 @@ static int dib0700_jumpram(struct usb_device *udev, u32 address)
int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
{
struct hexline hx;
- int pos = 0, ret, act_len;
+ int pos = 0, ret, act_len, i, adap_num;
+ u8 b[16];
+ u32 fw_version;
u8 buf[260];
@@ -364,6 +399,34 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw
} else
ret = -EIO;
+ /* the number of ts packet has to be at least 1 */
+ if (nb_packet_buffer_size < 1)
+ nb_packet_buffer_size = 1;
+
+ /* get the fimware version */
+ usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REQUEST_GET_VERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ b, sizeof(b), USB_CTRL_GET_TIMEOUT);
+ fw_version = (b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11];
+
+ /* set the buffer size - DVB-USB is allocating URB buffers
+ * only after the firwmare download was successful */
+ for (i = 0; i < dib0700_device_count; i++) {
+ for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
+ adap_num++) {
+ if (fw_version >= 0x10201)
+ dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
+ else {
+ /* for fw version older than 1.20.1,
+ * the buffersize has to be n times 512 */
+ dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
+ if (dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize < 512)
+ dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 512;
+ }
+ }
+ }
+
return ret;
}
@@ -371,6 +434,18 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct dib0700_state *st = adap->dev->priv;
u8 b[4];
+ int ret;
+
+ if ((onoff != 0) && (st->fw_version >= 0x10201)) {
+ /* for firmware later than 1.20.1,
+ * the USB xfer length can be set */
+ ret = dib0700_set_usb_xfer_len(adap->dev,
+ st->nb_packet_buffer_size);
+ if (ret < 0) {
+ deb_info("can not set the USB xfer len\n");
+ return ret;
+ }
+ }
b[0] = REQUEST_ENABLE_VIDEO;
b[1] = (onoff << 4) | 0x00; /* this bit gives a kind of command, rather than enabling something or not */
@@ -415,9 +490,21 @@ static int dib0700_probe(struct usb_interface *intf,
for (i = 0; i < dib0700_device_count; i++)
if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE,
- &dev, adapter_nr) == 0)
- {
+ &dev, adapter_nr) == 0) {
+ struct dib0700_state *st = dev->priv;
+ u32 hwversion, romversion, fw_version, fwtype;
+
+ dib0700_get_version(dev, &hwversion, &romversion,
+ &fw_version, &fwtype);
+
+ deb_info("Firmware version: %x, %d, 0x%x, %d\n",
+ hwversion, romversion, fw_version, fwtype);
+
+ st->fw_version = fw_version;
+ st->nb_packet_buffer_size = (u32)nb_packet_buffer_size;
+
dib0700_rc_setup(dev);
+
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 684146f98eb7..44972d01bbd0 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -18,6 +18,7 @@
#include "xc5000.h"
#include "s5h1411.h"
#include "dib0070.h"
+#include "dib0090.h"
#include "lgdt3305.h"
#include "mxl5007t.h"
@@ -130,93 +131,95 @@ static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
/* MT226x */
static struct dibx000_agc_config stk7700d_7000p_mt2266_agc_config[2] = {
{
- BAND_UHF, // band_caps
+ BAND_UHF,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=1, P_agc_inv_pwm2=1,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
- (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup
-
- 1130, // inv_gain
- 21, // time_stabiliz
-
- 0, // alpha_level
- 118, // thlock
-
- 0, // wbd_inv
- 3530, // wbd_ref
- 1, // wbd_sel
- 0, // wbd_alpha
-
- 65535, // agc1_max
- 33770, // agc1_min
- 65535, // agc2_max
- 23592, // agc2_min
-
- 0, // agc1_pt1
- 62, // agc1_pt2
- 255, // agc1_pt3
- 64, // agc1_slope1
- 64, // agc1_slope2
- 132, // agc2_pt1
- 192, // agc2_pt2
- 80, // agc2_slope1
- 80, // agc2_slope2
-
- 17, // alpha_mant
- 27, // alpha_exp
- 23, // beta_mant
- 51, // beta_exp
-
- 1, // perform_agc_softsplit
+ (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+
+ 1130,
+ 21,
+
+ 0,
+ 118,
+
+ 0,
+ 3530,
+ 1,
+ 0,
+
+ 65535,
+ 33770,
+ 65535,
+ 23592,
+
+ 0,
+ 62,
+ 255,
+ 64,
+ 64,
+ 132,
+ 192,
+ 80,
+ 80,
+
+ 17,
+ 27,
+ 23,
+ 51,
+
+ 1,
}, {
- BAND_VHF | BAND_LBAND, // band_caps
+ BAND_VHF | BAND_LBAND,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=1, P_agc_inv_pwm2=1,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
- (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
-
- 2372, // inv_gain
- 21, // time_stabiliz
-
- 0, // alpha_level
- 118, // thlock
-
- 0, // wbd_inv
- 3530, // wbd_ref
- 1, // wbd_sel
- 0, // wbd_alpha
-
- 65535, // agc1_max
- 0, // agc1_min
- 65535, // agc2_max
- 23592, // agc2_min
-
- 0, // agc1_pt1
- 128, // agc1_pt2
- 128, // agc1_pt3
- 128, // agc1_slope1
- 0, // agc1_slope2
- 128, // agc2_pt1
- 253, // agc2_pt2
- 81, // agc2_slope1
- 0, // agc2_slope2
-
- 17, // alpha_mant
- 27, // alpha_exp
- 23, // beta_mant
- 51, // beta_exp
-
- 1, // perform_agc_softsplit
+ (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0),
+
+ 2372,
+ 21,
+
+ 0,
+ 118,
+
+ 0,
+ 3530,
+ 1,
+ 0,
+
+ 65535,
+ 0,
+ 65535,
+ 23592,
+
+ 0,
+ 128,
+ 128,
+ 128,
+ 0,
+ 128,
+ 253,
+ 81,
+ 0,
+
+ 17,
+ 27,
+ 23,
+ 51,
+
+ 1,
}
};
static struct dibx000_bandwidth_config stk7700d_mt2266_pll_config = {
- 60000, 30000, // internal, sampling
- 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
- 0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
- (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k
- 0, // ifreq
- 20452225, // timf
+ 60000, 30000,
+ 1, 8, 3, 1, 0,
+ 0, 0, 1, 1, 2,
+ (3 << 14) | (1 << 12) | (524 << 0),
+ 0,
+ 20452225,
};
static struct dib7000p_config stk7700d_dib7000p_mt2266_config[] = {
@@ -605,17 +608,17 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
}
break;
default:
- if (actlen != sizeof(buf)) {
- /* We didn't get back the 6 byte message we expected */
- err("Unexpected RC response size [%d]", actlen);
- return -1;
- }
+ if (actlen != sizeof(buf)) {
+ /* We didn't get back the 6 byte message we expected */
+ err("Unexpected RC response size [%d]", actlen);
+ return -1;
+ }
- poll_reply.report_id = buf[0];
- poll_reply.data_state = buf[1];
+ poll_reply.report_id = buf[0];
+ poll_reply.data_state = buf[1];
poll_reply.system = (buf[2] << 8) | buf[3];
- poll_reply.data = buf[4];
- poll_reply.not_data = buf[5];
+ poll_reply.data = buf[4];
+ poll_reply.not_data = buf[5];
break;
}
@@ -632,7 +635,7 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
/* Find the key in the map */
for (i = 0; i < d->props.rc_key_map_size; i++) {
if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) &&
- rc5_data(&keymap[i]) == poll_reply.data) {
+ rc5_data(&keymap[i]) == poll_reply.data) {
*event = keymap[i].event;
found = 1;
break;
@@ -641,8 +644,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
if (found == 0) {
err("Unknown remote controller key: %04x %02x %02x",
- poll_reply.system,
- poll_reply.data, poll_reply.not_data);
+ poll_reply.system,
+ poll_reply.data, poll_reply.not_data);
d->last_event = 0;
return 0;
}
@@ -933,47 +936,48 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = {
- BAND_UHF | BAND_VHF, // band_caps
+ BAND_UHF | BAND_VHF,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
-
- 712, // inv_gain
- 41, // time_stabiliz
-
- 0, // alpha_level
- 118, // thlock
-
- 0, // wbd_inv
- 4095, // wbd_ref
- 0, // wbd_sel
- 0, // wbd_alpha
-
- 42598, // agc1_max
- 17694, // agc1_min
- 45875, // agc2_max
- 2621, // agc2_min
- 0, // agc1_pt1
- 76, // agc1_pt2
- 139, // agc1_pt3
- 52, // agc1_slope1
- 59, // agc1_slope2
- 107, // agc2_pt1
- 172, // agc2_pt2
- 57, // agc2_slope1
- 70, // agc2_slope2
-
- 21, // alpha_mant
- 25, // alpha_exp
- 28, // beta_mant
- 48, // beta_exp
-
- 1, // perform_agc_softsplit
- { 0, // split_min
- 107, // split_max
- 51800, // global_split_min
- 24700 // global_split_max
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0),
+
+ 712,
+ 41,
+
+ 0,
+ 118,
+
+ 0,
+ 4095,
+ 0,
+ 0,
+
+ 42598,
+ 17694,
+ 45875,
+ 2621,
+ 0,
+ 76,
+ 139,
+ 52,
+ 59,
+ 107,
+ 172,
+ 57,
+ 70,
+
+ 21,
+ 25,
+ 28,
+ 48,
+
+ 1,
+ { 0,
+ 107,
+ 51800,
+ 24700
},
};
@@ -982,54 +986,55 @@ static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = {
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0),
- 712, // inv_gain
- 41, // time_stabiliz
+ 712,
+ 41,
- 0, // alpha_level
- 118, // thlock
+ 0,
+ 118,
- 0, // wbd_inv
- 4095, // wbd_ref
- 0, // wbd_sel
- 0, // wbd_alpha
+ 0,
+ 4095,
+ 0,
+ 0,
- 42598, // agc1_max
- 16384, // agc1_min
- 42598, // agc2_max
- 0, // agc2_min
+ 42598,
+ 16384,
+ 42598,
+ 0,
- 0, // agc1_pt1
- 137, // agc1_pt2
- 255, // agc1_pt3
+ 0,
+ 137,
+ 255,
- 0, // agc1_slope1
- 255, // agc1_slope2
+ 0,
+ 255,
- 0, // agc2_pt1
- 0, // agc2_pt2
+ 0,
+ 0,
- 0, // agc2_slope1
- 41, // agc2_slope2
+ 0,
+ 41,
- 15, // alpha_mant
- 25, // alpha_exp
+ 15,
+ 25,
- 28, // beta_mant
- 48, // beta_exp
+ 28,
+ 48,
- 0, // perform_agc_softsplit
+ 0,
};
static struct dibx000_bandwidth_config stk7700p_pll_config = {
- 60000, 30000, // internal, sampling
- 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
- 0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
- (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k
- 60258167, // ifreq
- 20452225, // timf
- 30000000, // xtal
+ 60000, 30000,
+ 1, 8, 3, 1, 0,
+ 0, 0, 1, 1, 0,
+ (3 << 14) | (1 << 12) | (524 << 0),
+ 60258167,
+ 20452225,
+ 30000000,
};
static struct dib7000m_config stk7700p_dib7000m_config = {
@@ -1115,41 +1120,42 @@ static struct dibx000_agc_config dib7070_agc_config = {
BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), // setup
-
- 600, // inv_gain
- 10, // time_stabiliz
-
- 0, // alpha_level
- 118, // thlock
-
- 0, // wbd_inv
- 3530, // wbd_ref
- 1, // wbd_sel
- 5, // wbd_alpha
-
- 65535, // agc1_max
- 0, // agc1_min
-
- 65535, // agc2_max
- 0, // agc2_min
-
- 0, // agc1_pt1
- 40, // agc1_pt2
- 183, // agc1_pt3
- 206, // agc1_slope1
- 255, // agc1_slope2
- 72, // agc2_pt1
- 152, // agc2_pt2
- 88, // agc2_slope1
- 90, // agc2_slope2
-
- 17, // alpha_mant
- 27, // alpha_exp
- 23, // beta_mant
- 51, // beta_exp
-
- 0, // perform_agc_softsplit
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+
+ 600,
+ 10,
+
+ 0,
+ 118,
+
+ 0,
+ 3530,
+ 1,
+ 5,
+
+ 65535,
+ 0,
+
+ 65535,
+ 0,
+
+ 0,
+ 40,
+ 183,
+ 206,
+ 255,
+ 72,
+ 152,
+ 88,
+ 90,
+
+ 17,
+ 27,
+ 23,
+ 51,
+
+ 0,
};
static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff)
@@ -1276,13 +1282,13 @@ static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
}
static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
- 60000, 15000, // internal, sampling
- 1, 20, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
- 0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
- (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k
- (0 << 25) | 0, // ifreq = 0.000000 MHz
- 20452225, // timf
- 12000000, // xtal_hz
+ 60000, 15000,
+ 1, 20, 3, 1, 0,
+ 0, 0, 1, 1, 2,
+ (3 << 14) | (1 << 12) | (524 << 0),
+ (0 << 25) | 0,
+ 20452225,
+ 12000000,
};
static struct dib7000p_config dib7070p_dib7000p_config = {
@@ -1476,12 +1482,12 @@ static struct dib8000_config dib807x_dib8000_config[2] = {
}
};
-static int dib807x_tuner_reset(struct dvb_frontend *fe, int onoff)
+static int dib80xx_tuner_reset(struct dvb_frontend *fe, int onoff)
{
return dib8000_set_gpio(fe, 5, 0, !onoff);
}
-static int dib807x_tuner_sleep(struct dvb_frontend *fe, int onoff)
+static int dib80xx_tuner_sleep(struct dvb_frontend *fe, int onoff)
{
return dib8000_set_gpio(fe, 0, 0, onoff);
}
@@ -1494,8 +1500,8 @@ static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = {
static struct dib0070_config dib807x_dib0070_config[2] = {
{
.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
- .reset = dib807x_tuner_reset,
- .sleep = dib807x_tuner_sleep,
+ .reset = dib80xx_tuner_reset,
+ .sleep = dib80xx_tuner_sleep,
.clock_khz = 12000,
.clock_pad_drive = 4,
.vga_filter = 1,
@@ -1508,8 +1514,8 @@ static struct dib0070_config dib807x_dib0070_config[2] = {
.freq_offset_khz_vhf = -100,
}, {
.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
- .reset = dib807x_tuner_reset,
- .sleep = dib807x_tuner_sleep,
+ .reset = dib80xx_tuner_reset,
+ .sleep = dib80xx_tuner_sleep,
.clock_khz = 12000,
.clock_pad_drive = 2,
.vga_filter = 1,
@@ -1566,12 +1572,14 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
-static int stk807x_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
+static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index,
+ u16 pid, int onoff)
{
return dib8000_pid_filter(adapter->fe, index, pid, onoff);
}
-static int stk807x_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
+static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter,
+ int onoff)
{
return dib8000_pid_filter_ctrl(adapter->fe, onoff);
}
@@ -1624,7 +1632,7 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap)
dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
/* initialize IC 0 */
- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x80);
+ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80);
adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
@@ -1635,7 +1643,7 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap)
static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
{
/* initialize IC 1 */
- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x82);
+ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82);
adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82,
&dib807x_dib8000_config[1]);
@@ -1643,6 +1651,245 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
return adap->fe == NULL ? -ENODEV : 0;
}
+/* STK8096GP */
+struct dibx000_agc_config dib8090_agc_config[2] = {
+ {
+ BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1,
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
+ * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+
+ 787,
+ 10,
+
+ 0,
+ 118,
+
+ 0,
+ 3530,
+ 1,
+ 5,
+
+ 65535,
+ 0,
+
+ 65535,
+ 0,
+
+ 0,
+ 32,
+ 114,
+ 143,
+ 144,
+ 114,
+ 227,
+ 116,
+ 117,
+
+ 28,
+ 26,
+ 31,
+ 51,
+
+ 0,
+ },
+ {
+ BAND_CBAND,
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1,
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
+ * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */
+ (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8)
+ | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+
+ 787,
+ 10,
+
+ 0,
+ 118,
+
+ 0,
+ 3530,
+ 1,
+ 5,
+
+ 0,
+ 0,
+
+ 65535,
+ 0,
+
+ 0,
+ 32,
+ 114,
+ 143,
+ 144,
+ 114,
+ 227,
+ 116,
+ 117,
+
+ 28,
+ 26,
+ 31,
+ 51,
+
+ 0,
+ }
+};
+
+static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = {
+ 54000, 13500,
+ 1, 18, 3, 1, 0,
+ 0, 0, 1, 1, 2,
+ (3 << 14) | (1 << 12) | (599 << 0),
+ (0 << 25) | 0,
+ 20199727,
+ 12000000,
+};
+
+static int dib8090_get_adc_power(struct dvb_frontend *fe)
+{
+ return dib8000_get_adc_power(fe, 1);
+}
+
+static struct dib8000_config dib809x_dib8000_config = {
+ .output_mpeg2_in_188_bytes = 1,
+
+ .agc_config_count = 2,
+ .agc = dib8090_agc_config,
+ .agc_control = dib0090_dcc_freq,
+ .pll = &dib8090_pll_config_12mhz,
+ .tuner_is_baseband = 1,
+
+ .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB8000_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS,
+
+ .hostbus_diversity = 1,
+ .div_cfg = 0x31,
+ .output_mode = OUTMODE_MPEG2_FIFO,
+ .drives = 0x2d98,
+ .diversity_delay = 144,
+ .refclksel = 3,
+};
+
+static struct dib0090_config dib809x_dib0090_config = {
+ .io.pll_bypass = 1,
+ .io.pll_range = 1,
+ .io.pll_prediv = 1,
+ .io.pll_loopdiv = 20,
+ .io.adc_clock_ratio = 8,
+ .io.pll_int_loop_filt = 0,
+ .io.clock_khz = 12000,
+ .reset = dib80xx_tuner_reset,
+ .sleep = dib80xx_tuner_sleep,
+ .clkouttobamse = 1,
+ .analog_output = 1,
+ .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS,
+ .wbd_vhf_offset = 100,
+ .wbd_cband_offset = 450,
+ .use_pwm_agc = 1,
+ .clkoutdrive = 1,
+ .get_adc_power = dib8090_get_adc_power,
+ .freq_offset_khz_uhf = 0,
+ .freq_offset_khz_vhf = -143,
+};
+
+static int dib8096_set_param_override(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dib0700_adapter_state *state = adap->priv;
+ u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
+ u16 offset;
+ int ret = 0;
+ enum frontend_tune_state tune_state = CT_SHUTDOWN;
+ u16 ltgain, rf_gain_limit;
+
+ ret = state->set_param_save(fe, fep);
+ if (ret < 0)
+ return ret;
+
+ switch (band) {
+ case BAND_VHF:
+ offset = 100;
+ break;
+ case BAND_UHF:
+ offset = 550;
+ break;
+ default:
+ offset = 0;
+ break;
+ }
+ offset += (dib0090_get_wbd_offset(fe) * 8 * 18 / 33 + 1) / 2;
+ dib8000_set_wbd_ref(fe, offset);
+
+
+ if (band == BAND_CBAND) {
+ deb_info("tuning in CBAND - soft-AGC startup\n");
+ /* TODO specific wbd target for dib0090 - needed for startup ? */
+ dib0090_set_tune_state(fe, CT_AGC_START);
+ do {
+ ret = dib0090_gain_control(fe);
+ msleep(ret);
+ tune_state = dib0090_get_tune_state(fe);
+ if (tune_state == CT_AGC_STEP_0)
+ dib8000_set_gpio(fe, 6, 0, 1);
+ else if (tune_state == CT_AGC_STEP_1) {
+ dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, &ltgain);
+ if (rf_gain_limit == 0)
+ dib8000_set_gpio(fe, 6, 0, 0);
+ }
+ } while (tune_state < CT_AGC_STOP);
+ dib0090_pwm_gain_reset(fe);
+ dib8000_pwm_agc_reset(fe);
+ dib8000_set_tune_state(fe, CT_DEMOD_START);
+ } else {
+ deb_info("not tuning in CBAND - standard AGC startup\n");
+ dib0090_pwm_gain_reset(fe);
+ }
+
+ return 0;
+}
+
+static int dib809x_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+
+ if (dvb_attach(dib0090_register, adap->fe, tun_i2c, &dib809x_dib0090_config) == NULL)
+ return -ENODEV;
+
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib8096_set_param_override;
+ return 0;
+}
+
+static int stk809x_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+
+ dib0700_ctrl_clock(adap->dev, 72, 1);
+
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80);
+
+ adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
/* STK7070PD */
static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
@@ -1929,14 +2176,17 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) },
/* 55 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) },
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) },
- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) },
- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) },
+ { USB_DEVICE(USB_VID_PCTV, USB_PID_PINNACLE_PCTV73ESE) },
+ { USB_DEVICE(USB_VID_PCTV, USB_PID_PINNACLE_PCTV282E) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) },
/* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) },
{ USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) },
{ USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) },
+/* 65 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) },
+ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096GP) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -2238,11 +2488,11 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ NULL },
},
{ "Pinnacle PCTV 73e SE",
- { &dib0700_usb_id_table[57], NULL },
+ { &dib0700_usb_id_table[57], &dib0700_usb_id_table[65], NULL },
{ NULL },
},
{ "Pinnacle PCTV 282e",
- { &dib0700_usb_id_table[58], NULL },
+ { &dib0700_usb_id_table[58], &dib0700_usb_id_table[66], NULL },
{ NULL },
},
},
@@ -2471,8 +2721,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
- .pid_filter = stk807x_pid_filter,
- .pid_filter_ctrl = stk807x_pid_filter_ctrl,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
.frontend_attach = stk807x_frontend_attach,
.tuner_attach = dib807x_tuner_attach,
@@ -2510,8 +2760,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
- .pid_filter = stk807x_pid_filter,
- .pid_filter_ctrl = stk807x_pid_filter_ctrl,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
.frontend_attach = stk807xpvr_frontend_attach0,
.tuner_attach = dib807x_tuner_attach,
@@ -2523,8 +2773,8 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
- .pid_filter = stk807x_pid_filter,
- .pid_filter_ctrl = stk807x_pid_filter_ctrl,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
.frontend_attach = stk807xpvr_frontend_attach1,
.tuner_attach = dib807x_tuner_attach,
@@ -2547,6 +2797,37 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_key_map = dib0700_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk80xx_pid_filter,
+ .pid_filter_ctrl = stk80xx_pid_filter_ctrl,
+ .frontend_attach = stk809x_frontend_attach,
+ .tuner_attach = dib809x_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv =
+ sizeof(struct dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DiBcom STK8096GP reference design",
+ { &dib0700_usb_id_table[67], NULL },
+ { NULL },
+ },
+ },
+
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
},
};
diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c
index da34979b5337..9143b5631e88 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-common.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-common.c
@@ -142,8 +142,13 @@ static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num
} else if ((msg[i].flags & I2C_M_RD) == 0) {
if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
break;
- } else
- break;
+ } else if (msg[i].addr != 0x50) {
+ /* 0x50 is the address of the eeprom - we need to protect it
+ * from dibusb's bad i2c implementation: reads without
+ * writing the offset before are forbidden */
+ if (dibusb_i2c_msg(d, msg[i].addr, NULL, 0, msg[i].buf, msg[i].len) < 0)
+ break;
+ }
}
mutex_unlock(&d->i2c_mutex);
@@ -243,6 +248,12 @@ static struct dib3000mc_config mod3000p_dib3000p_config = {
int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap)
{
+ if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON &&
+ adap->dev->udev->descriptor.idProduct ==
+ USB_PID_LITEON_DVB_T_WARM) {
+ msleep(1000);
+ }
+
if ((adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000P_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL ||
(adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000MC_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL) {
if (adap->priv != NULL) {
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index f1602d4ace6d..bc3581d58ced 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -47,6 +47,7 @@
#define USB_VID_MSI_2 0x1462
#define USB_VID_OPERA1 0x695c
#define USB_VID_PINNACLE 0x2304
+#define USB_VID_PCTV 0x2013
#define USB_VID_PIXELVIEW 0x1554
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
@@ -101,6 +102,7 @@
#define USB_PID_DIBCOM_STK7070PD 0x1ebe
#define USB_PID_DIBCOM_STK807XP 0x1f90
#define USB_PID_DIBCOM_STK807XPVR 0x1f98
+#define USB_PID_DIBCOM_STK8096GP 0x1fa0
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
#define USB_PID_DIBCOM_STK7770P 0x1e80
#define USB_PID_DPOSH_M9206_COLD 0x9206
@@ -211,6 +213,7 @@
#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
#define USB_PID_PINNACLE_PCTV73A 0x0243
#define USB_PID_PINNACLE_PCTV73ESE 0x0245
+#define USB_PID_PINNACLE_PCTV74E 0x0246
#define USB_PID_PINNACLE_PCTV282E 0x0248
#define USB_PID_PIXELVIEW_SBTVD 0x5010
#define USB_PID_PCTV_200E 0x020e
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 5bb9479d154e..64132c0cf80d 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -20,6 +20,11 @@
#include "tda1002x.h"
#include "mt312.h"
#include "zl10039.h"
+#include "ds3000.h"
+#include "stv0900.h"
+#include "stv6110.h"
+#include "stb6100.h"
+#include "stb6100_proc.h"
#ifndef USB_PID_DW2102
#define USB_PID_DW2102 0x2102
@@ -37,12 +42,20 @@
#define USB_PID_CINERGY_S 0x0064
#endif
+#ifndef USB_PID_TEVII_S630
+#define USB_PID_TEVII_S630 0xd630
+#endif
+
#ifndef USB_PID_TEVII_S650
#define USB_PID_TEVII_S650 0xd650
#endif
-#ifndef USB_PID_TEVII_S630
-#define USB_PID_TEVII_S630 0xd630
+#ifndef USB_PID_TEVII_S660
+#define USB_PID_TEVII_S660 0xd660
+#endif
+
+#ifndef USB_PID_PROF_1100
+#define USB_PID_PROF_1100 0xb012
#endif
#define DW210X_READ_MSG 0
@@ -55,6 +68,10 @@
#define DW2102_VOLTAGE_CTRL (0x1800)
#define DW2102_RC_QUERY (0x1a00)
+#define err_str "did not find the firmware file. (%s) " \
+ "Please see linux/Documentation/dvb/ for more details " \
+ "on firmware-problems."
+
struct dvb_usb_rc_keys_table {
struct dvb_usb_rc_key *rc_keys;
int rc_keys_size;
@@ -71,6 +88,12 @@ static int ir_keymap;
module_param_named(keymap, ir_keymap, int, 0644);
MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ...");
+/* demod probe */
+static int demod_probe = 1;
+module_param_named(demod, demod_probe, int, 0644);
+MODULE_PARM_DESC(demod, "demod to probe (1=cx24116 2=stv0903+stv6110 "
+ "4=stv0903+stb6100(or-able)).");
+
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
@@ -183,7 +206,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
switch (num) {
case 2:
/* read si2109 register by number */
- buf6[0] = 0xd0;
+ buf6[0] = msg[0].addr << 1;
buf6[1] = msg[0].len;
buf6[2] = msg[0].buf[0];
ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
@@ -198,7 +221,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
switch (msg[0].addr) {
case 0x68:
/* write to si2109 register */
- buf6[0] = 0xd0;
+ buf6[0] = msg[0].addr << 1;
buf6[1] = msg[0].len;
memcpy(buf6 + 2, msg[0].buf, msg[0].len);
ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
@@ -239,7 +262,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
/* read */
/* first write first register number */
u8 ibuf[msg[1].len + 2], obuf[3];
- obuf[0] = 0xd0;
+ obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
obuf[2] = msg[0].buf[0];
ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
@@ -256,7 +279,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
case 0x68: {
/* write to register */
u8 obuf[msg[0].len + 2];
- obuf[0] = 0xd0;
+ obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
@@ -266,7 +289,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
case 0x61: {
/* write to tuner */
u8 obuf[msg[0].len + 2];
- obuf[0] = 0xc2;
+ obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
@@ -301,78 +324,78 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int ret = 0;
- int len, i;
+ int len, i, j;
if (!d)
return -ENODEV;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
- switch (num) {
- case 2: {
- /* read */
- /* first write first register number */
- u8 ibuf[msg[1].len + 2], obuf[3];
- obuf[0] = 0xaa;
- obuf[1] = msg[0].len;
- obuf[2] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
- /* second read registers */
- ret = dw210x_op_rw(d->udev, 0xc3, 0xab , 0,
- ibuf, msg[1].len + 2, DW210X_READ_MSG);
- memcpy(msg[1].buf, ibuf + 2, msg[1].len);
-
- break;
- }
- case 1:
- switch (msg[0].addr) {
- case 0x55: {
- if (msg[0].buf[0] == 0xf7) {
- /* firmware */
- /* Write in small blocks */
- u8 obuf[19];
- obuf[0] = 0xaa;
- obuf[1] = 0x11;
- obuf[2] = 0xf7;
- len = msg[0].len - 1;
- i = 1;
- do {
- memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len));
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG);
- i += 16;
- len -= 16;
- } while (len > 0);
- } else {
- /* write to register */
- u8 obuf[msg[0].len + 2];
- obuf[0] = 0xaa;
- obuf[1] = msg[0].len;
- memcpy(obuf + 2, msg[0].buf, msg[0].len);
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
- }
- break;
- }
+ for (j = 0; j < num; j++) {
+ switch (msg[j].addr) {
case(DW2102_RC_QUERY): {
u8 ibuf[2];
ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
ibuf, 2, DW210X_READ_MSG);
- memcpy(msg[0].buf, ibuf , 2);
+ memcpy(msg[j].buf, ibuf , 2);
break;
}
case(DW2102_VOLTAGE_CTRL): {
u8 obuf[2];
obuf[0] = 0x30;
- obuf[1] = msg[0].buf[0];
+ obuf[1] = msg[j].buf[0];
ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
}
+ /*case 0x55: cx24116
+ case 0x6a: stv0903
+ case 0x68: ds3000, stv0903
+ case 0x60: ts2020, stv6110, stb6100 */
+ default: {
+ if (msg[j].flags == I2C_M_RD) {
+ /* read registers */
+ u8 ibuf[msg[j].len + 2];
+ ret = dw210x_op_rw(d->udev, 0xc3,
+ (msg[j].addr << 1) + 1, 0,
+ ibuf, msg[j].len + 2,
+ DW210X_READ_MSG);
+ memcpy(msg[j].buf, ibuf + 2, msg[j].len);
+ mdelay(10);
+ } else if (((msg[j].buf[0] == 0xb0) &&
+ (msg[j].addr == 0x68)) ||
+ ((msg[j].buf[0] == 0xf7) &&
+ (msg[j].addr == 0x55))) {
+ /* write firmware */
+ u8 obuf[19];
+ obuf[0] = msg[j].addr << 1;
+ obuf[1] = (msg[j].len > 15 ? 17 : msg[j].len);
+ obuf[2] = msg[j].buf[0];
+ len = msg[j].len - 1;
+ i = 1;
+ do {
+ memcpy(obuf + 3, msg[j].buf + i,
+ (len > 16 ? 16 : len));
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, (len > 16 ? 16 : len) + 3,
+ DW210X_WRITE_MSG);
+ i += 16;
+ len -= 16;
+ } while (len > 0);
+ } else {
+ /* write registers */
+ u8 obuf[msg[j].len + 2];
+ obuf[0] = msg[j].addr << 1;
+ obuf[1] = msg[j].len;
+ memcpy(obuf + 2, msg[j].buf, msg[j].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[j].len + 2,
+ DW210X_WRITE_MSG);
+ }
+ break;
+ }
}
- break;
}
mutex_unlock(&d->i2c_mutex);
@@ -442,63 +465,85 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
return num;
}
-static int s630_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int ret = 0;
+ int len, i, j;
if (!d)
return -ENODEV;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
- switch (num) {
- case 2: { /* read */
- u8 ibuf[msg[1].len], obuf[3];
- obuf[0] = msg[1].len;
- obuf[1] = (msg[0].addr << 1);
- obuf[2] = msg[0].buf[0];
-
- ret = dw210x_op_rw(d->udev, 0x90, 0, 0,
- obuf, 3, DW210X_WRITE_MSG);
- msleep(5);
- ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
- ibuf, msg[1].len, DW210X_READ_MSG);
- memcpy(msg[1].buf, ibuf, msg[1].len);
- break;
- }
- case 1:
- switch (msg[0].addr) {
- case 0x60:
- case 0x0e: {
- /* write to zl10313, zl10039 register, */
- u8 obuf[msg[0].len + 2];
- obuf[0] = msg[0].len + 1;
- obuf[1] = (msg[0].addr << 1);
- memcpy(obuf + 2, msg[0].buf, msg[0].len);
- ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
- obuf, msg[0].len + 2, DW210X_WRITE_MSG);
- break;
- }
+ for (j = 0; j < num; j++) {
+ switch (msg[j].addr) {
case (DW2102_RC_QUERY): {
u8 ibuf[4];
ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
ibuf, 4, DW210X_READ_MSG);
- msg[0].buf[0] = ibuf[3];
+ memcpy(msg[j].buf, ibuf + 1, 2);
break;
}
case (DW2102_VOLTAGE_CTRL): {
u8 obuf[2];
- obuf[0] = 0x03;
- obuf[1] = msg[0].buf[0];
+ obuf[0] = 3;
+ obuf[1] = msg[j].buf[0];
ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
}
+ /*case 0x55: cx24116
+ case 0x6a: stv0903
+ case 0x68: ds3000, stv0903
+ case 0x60: ts2020, stv6110, stb6100
+ case 0xa0: eeprom */
+ default: {
+ if (msg[j].flags == I2C_M_RD) {
+ /* read registers */
+ u8 ibuf[msg[j].len];
+ ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
+ ibuf, msg[j].len,
+ DW210X_READ_MSG);
+ memcpy(msg[j].buf, ibuf, msg[j].len);
+ break;
+ } else if ((msg[j].buf[0] == 0xb0) &&
+ (msg[j].addr == 0x68)) {
+ /* write firmware */
+ u8 obuf[19];
+ obuf[0] = (msg[j].len > 16 ?
+ 18 : msg[j].len + 1);
+ obuf[1] = msg[j].addr << 1;
+ obuf[2] = msg[j].buf[0];
+ len = msg[j].len - 1;
+ i = 1;
+ do {
+ memcpy(obuf + 3, msg[j].buf + i,
+ (len > 16 ? 16 : len));
+ ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
+ obuf, (len > 16 ? 16 : len) + 3,
+ DW210X_WRITE_MSG);
+ i += 16;
+ len -= 16;
+ } while (len > 0);
+ } else {
+ /* write registers */
+ u8 obuf[msg[j].len + 2];
+ obuf[0] = msg[j].len + 1;
+ obuf[1] = (msg[j].addr << 1);
+ memcpy(obuf + 2, msg[j].buf, msg[j].len);
+ ret = dw210x_op_rw(d->udev,
+ (num > 1 ? 0x90 : 0x80), 0, 0,
+ obuf, msg[j].len + 2,
+ DW210X_WRITE_MSG);
+ break;
+ }
+ break;
+ }
}
- break;
+ msleep(3);
}
mutex_unlock(&d->i2c_mutex);
@@ -535,8 +580,8 @@ static struct i2c_algorithm dw3101_i2c_algo = {
.functionality = dw210x_i2c_func,
};
-static struct i2c_algorithm s630_i2c_algo = {
- .master_xfer = s630_i2c_transfer,
+static struct i2c_algorithm s6x0_i2c_algo = {
+ .master_xfer = s6x0_i2c_transfer,
.functionality = dw210x_i2c_func,
};
@@ -564,25 +609,34 @@ static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
return 0;
};
-static int s630_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
{
int i, ret;
- u8 buf[3], eeprom[256], eepromline[16];
+ u8 ibuf[] = { 0 }, obuf[] = { 0 };
+ u8 eeprom[256], eepromline[16];
+ struct i2c_msg msg[] = {
+ {
+ .addr = 0xa0 >> 1,
+ .flags = 0,
+ .buf = obuf,
+ .len = 1,
+ }, {
+ .addr = 0xa0 >> 1,
+ .flags = I2C_M_RD,
+ .buf = ibuf,
+ .len = 1,
+ }
+ };
for (i = 0; i < 256; i++) {
- buf[0] = 1;
- buf[1] = 0xa0;
- buf[2] = i;
- ret = dw210x_op_rw(d->udev, 0x90, 0, 0,
- buf, 3, DW210X_WRITE_MSG);
- ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
- buf, 1, DW210X_READ_MSG);
- if (ret < 0) {
+ obuf[0] = i;
+ ret = s6x0_i2c_transfer(&d->i2c_adap, msg, 2);
+ if (ret != 2) {
err("read eeprom failed.");
return -1;
} else {
- eepromline[i % 16] = buf[0];
- eeprom[i] = buf[0];
+ eepromline[i % 16] = ibuf[0];
+ eeprom[i] = ibuf[0];
}
if ((i % 16) == 15) {
@@ -644,19 +698,104 @@ static struct mt312_config zl313_config = {
.demod_address = 0x0e,
};
+static struct ds3000_config dw2104_ds3000_config = {
+ .demod_address = 0x68,
+};
+
+static struct stv0900_config dw2104a_stv0900_config = {
+ .demod_address = 0x6a,
+ .demod_mode = 0,
+ .xtal = 27000000,
+ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+ .diseqc_mode = 2,/* 2/3 PWM */
+ .tun1_maddress = 0,/* 0x60 */
+ .tun1_adc = 0,/* 2 Vpp */
+ .path1_mode = 3,
+};
+
+static struct stb6100_config dw2104a_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
+
+static struct stv0900_config dw2104_stv0900_config = {
+ .demod_address = 0x68,
+ .demod_mode = 0,
+ .xtal = 8000000,
+ .clkmode = 3,
+ .diseqc_mode = 2,
+ .tun1_maddress = 0,
+ .tun1_adc = 1,/* 1 Vpp */
+ .path1_mode = 3,
+};
+
+static struct stv6110_config dw2104_stv6110_config = {
+ .i2c_address = 0x60,
+ .mclk = 16000000,
+ .clk_div = 1,
+};
+
static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
{
- if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config,
- &d->dev->i2c_adap)) != NULL) {
+ struct dvb_tuner_ops *tuner_ops = NULL;
+
+ if (demod_probe & 4) {
+ d->fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config,
+ &d->dev->i2c_adap, 0);
+ if (d->fe != NULL) {
+ if (dvb_attach(stb6100_attach, d->fe,
+ &dw2104a_stb6100_config,
+ &d->dev->i2c_adap)) {
+ tuner_ops = &d->fe->ops.tuner_ops;
+ tuner_ops->set_frequency = stb6100_set_freq;
+ tuner_ops->get_frequency = stb6100_get_freq;
+ tuner_ops->set_bandwidth = stb6100_set_bandw;
+ tuner_ops->get_bandwidth = stb6100_get_bandw;
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached STV0900+STB6100!\n");
+ return 0;
+ }
+ }
+ }
+
+ if (demod_probe & 2) {
+ d->fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config,
+ &d->dev->i2c_adap, 0);
+ if (d->fe != NULL) {
+ if (dvb_attach(stv6110_attach, d->fe,
+ &dw2104_stv6110_config,
+ &d->dev->i2c_adap)) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached STV0900+STV6110A!\n");
+ return 0;
+ }
+ }
+ }
+
+ if (demod_probe & 1) {
+ d->fe = dvb_attach(cx24116_attach, &dw2104_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached cx24116!\n");
+ return 0;
+ }
+ }
+
+ d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
d->fe->ops.set_voltage = dw210x_set_voltage;
- info("Attached cx24116!\n");
+ info("Attached DS3000!\n");
return 0;
}
+
return -EIO;
}
static struct dvb_usb_device_properties dw2102_properties;
static struct dvb_usb_device_properties dw2104_properties;
+static struct dvb_usb_device_properties s6x0_properties;
static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
{
@@ -670,14 +809,17 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
return 0;
}
}
+
if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) {
- /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
d->fe = dvb_attach(stv0288_attach, &earda_config,
&d->dev->i2c_adap);
if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw210x_set_voltage;
- info("Attached stv0288!\n");
- return 0;
+ if (dvb_attach(stb6000_attach, d->fe, 0x61,
+ &d->dev->i2c_adap)) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached stv0288!\n");
+ return 0;
+ }
}
}
@@ -705,15 +847,38 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
return -EIO;
}
-static int s630_frontend_attach(struct dvb_usb_adapter *d)
+static int s6x0_frontend_attach(struct dvb_usb_adapter *d)
{
d->fe = dvb_attach(mt312_attach, &zl313_config,
- &d->dev->i2c_adap);
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ if (dvb_attach(zl10039_attach, d->fe, 0x60,
+ &d->dev->i2c_adap)) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached zl100313+zl10039!\n");
+ return 0;
+ }
+ }
+
+ d->fe = dvb_attach(stv0288_attach, &earda_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ if (dvb_attach(stb6000_attach, d->fe, 0x61,
+ &d->dev->i2c_adap)) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached stv0288+stb6000!\n");
+ return 0;
+ }
+ }
+
+ d->fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config,
+ &d->dev->i2c_adap);
if (d->fe != NULL) {
d->fe->ops.set_voltage = dw210x_set_voltage;
- info("Attached zl10313!\n");
+ info("Attached ds3000+ds2020!\n");
return 0;
}
+
return -EIO;
}
@@ -724,14 +889,6 @@ static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
-static int dw2102_earda_tuner_attach(struct dvb_usb_adapter *adap)
-{
- dvb_attach(stb6000_attach, adap->fe, 0x61,
- &adap->dev->i2c_adap);
-
- return 0;
-}
-
static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -740,14 +897,6 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
-static int s630_zl10039_tuner_attach(struct dvb_usb_adapter *adap)
-{
- dvb_attach(zl10039_attach, adap->fe, 0x60,
- &adap->dev->i2c_adap);
-
- return 0;
-}
-
static struct dvb_usb_rc_key dw210x_rc_keys[] = {
{ 0xf80a, KEY_Q }, /*power*/
{ 0xf80c, KEY_M }, /*mute*/
@@ -922,6 +1071,8 @@ static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)},
{USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)},
{USB_DEVICE(0x9022, USB_PID_TEVII_S630)},
+ {USB_DEVICE(0x3011, USB_PID_PROF_1100)},
+ {USB_DEVICE(0x9022, USB_PID_TEVII_S660)},
{ }
};
@@ -935,15 +1086,13 @@ static int dw2102_load_firmware(struct usb_device *dev,
u8 reset;
u8 reset16[] = {0, 0, 0, 0, 0, 0, 0};
const struct firmware *fw;
- const char *filename = "dvb-usb-dw2101.fw";
+ const char *fw_2101 = "dvb-usb-dw2101.fw";
switch (dev->descriptor.idProduct) {
case 0x2101:
- ret = request_firmware(&fw, filename, &dev->dev);
+ ret = request_firmware(&fw, fw_2101, &dev->dev);
if (ret != 0) {
- err("did not find the firmware file. (%s) "
- "Please see linux/Documentation/dvb/ for more details "
- "on firmware-problems.", filename);
+ err(err_str, fw_2101);
return ret;
}
break;
@@ -983,6 +1132,11 @@ static int dw2102_load_firmware(struct usb_device *dev,
}
/* init registers */
switch (dev->descriptor.idProduct) {
+ case USB_PID_PROF_1100:
+ s6x0_properties.rc_key_map = tbs_rc_keys;
+ s6x0_properties.rc_key_map_size =
+ ARRAY_SIZE(tbs_rc_keys);
+ break;
case USB_PID_TEVII_S650:
dw2104_properties.rc_key_map = tevii_rc_keys;
dw2104_properties.rc_key_map_size =
@@ -1021,7 +1175,6 @@ static int dw2102_load_firmware(struct usb_device *dev,
DW210X_READ_MSG);
if (reset16[2] == 0x11) {
dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo;
- dw2102_properties.adapter->tuner_attach = &dw2102_earda_tuner_attach;
break;
}
}
@@ -1184,13 +1337,13 @@ static struct dvb_usb_device_properties dw3101_properties = {
}
};
-static struct dvb_usb_device_properties s630_properties = {
+static struct dvb_usb_device_properties s6x0_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-s630.fw",
.no_reconnect = 1,
- .i2c_algo = &s630_i2c_algo,
+ .i2c_algo = &s6x0_i2c_algo,
.rc_key_map = tevii_rc_keys,
.rc_key_map_size = ARRAY_SIZE(tevii_rc_keys),
.rc_interval = 150,
@@ -1199,12 +1352,12 @@ static struct dvb_usb_device_properties s630_properties = {
.generic_bulk_ctrl_endpoint = 0x81,
.num_adapters = 1,
.download_firmware = dw2102_load_firmware,
- .read_mac_address = s630_read_mac_address,
+ .read_mac_address = s6x0_read_mac_address,
.adapter = {
{
- .frontend_attach = s630_frontend_attach,
+ .frontend_attach = s6x0_frontend_attach,
.streaming_ctrl = NULL,
- .tuner_attach = s630_zl10039_tuner_attach,
+ .tuner_attach = NULL,
.stream = {
.type = USB_BULK,
.count = 8,
@@ -1217,12 +1370,20 @@ static struct dvb_usb_device_properties s630_properties = {
},
}
},
- .num_device_descs = 1,
+ .num_device_descs = 3,
.devices = {
{"TeVii S630 USB",
{&dw2102_table[6], NULL},
{NULL},
},
+ {"Prof 1100 USB ",
+ {&dw2102_table[7], NULL},
+ {NULL},
+ },
+ {"TeVii S660 USB",
+ {&dw2102_table[8], NULL},
+ {NULL},
+ },
}
};
@@ -1235,10 +1396,10 @@ static int dw2102_probe(struct usb_interface *intf,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &dw3101_properties,
THIS_MODULE, NULL, adapter_nr) ||
- 0 == dvb_usb_device_init(intf, &s630_properties,
- THIS_MODULE, NULL, adapter_nr)) {
+ 0 == dvb_usb_device_init(intf, &s6x0_properties,
+ THIS_MODULE, NULL, adapter_nr))
return 0;
- }
+
return -ENODEV;
}
@@ -1269,6 +1430,7 @@ module_exit(dw2102_module_exit);
MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
" DVB-C 3101 USB2.0,"
- " TeVii S600, S630, S650 USB2.0 devices");
+ " TeVii S600, S630, S650, S660 USB2.0,"
+ " Prof 1100 USB2.0 devices");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c
index 9cbbe42ca44b..ebb7b9fd115b 100644
--- a/drivers/media/dvb/dvb-usb/friio-fe.c
+++ b/drivers/media/dvb/dvb-usb/friio-fe.c
@@ -134,11 +134,13 @@ static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq)
deb_fe("%s: freq=%d, step=%d\n", __func__, freq,
state->frontend.ops.info.frequency_stepsize);
/* freq -> oscilator frequency conversion. */
- /* freq: 473,000,000 + n*6,000,000 (no 1/7MHz shift to center freq) */
- /* add 400[1/7 MHZ] = 57.142857MHz. 57MHz for the IF, */
- /* 1/7MHz for center freq shift */
+ /* freq: 473,000,000 + n*6,000,000 [+ 142857 (center freq. shift)] */
f = freq / state->frontend.ops.info.frequency_stepsize;
- f += 400;
+ /* add 399[1/7 MHZ] = 57MHz for the IF */
+ f += 399;
+ /* add center frequency shift if necessary */
+ if (f % 7 == 0)
+ f++;
pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */
pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1;
pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F;
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
index 20eadf9318e0..7a7f1b2b681c 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
@@ -146,8 +146,8 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
switch (c->delivery_system) {
case SYS_DVBS:
- /* Only QPSK is supported for DVB-S */
- if (c->modulation != QPSK) {
+ /* Allow QPSK and 8PSK (even for DVB-S) */
+ if (c->modulation != QPSK && c->modulation != PSK_8) {
deb_fe("%s: unsupported modulation selected (%d)\n",
__func__, c->modulation);
return -EOPNOTSUPP;
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 58aac018f109..a3b8b697349b 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -526,6 +526,15 @@ config DVB_TUNER_DIB0070
This device is only used inside a SiP called together with a
demodulator for now.
+config DVB_TUNER_DIB0090
+ tristate "DiBcom DiB0090 silicon base-band tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon baseband tuner DiB0090 from DiBcom.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
comment "SEC control devices for DVB-S"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 823482535d11..47575cc7b699 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
obj-$(CONFIG_DVB_TDA8261) += tda8261.o
obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
+obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index 2dc2723b724a..24268ef2753d 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -62,7 +62,7 @@ struct au8522_register_config {
The values are as follows from left to right
0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13"
*/
-struct au8522_register_config filter_coef[] = {
+static const struct au8522_register_config filter_coef[] = {
{AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} },
{AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} },
{AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} },
@@ -104,7 +104,7 @@ struct au8522_register_config filter_coef[] = {
0="SIF" 1="ATVRF/ATVRF13"
Note: the "ATVRF/ATVRF13" mode has never been tested
*/
-struct au8522_register_config lpfilter_coef[] = {
+static const struct au8522_register_config lpfilter_coef[] = {
{0x060b, {0x21, 0x0b} },
{0x060c, {0xad, 0xad} },
{0x060d, {0x70, 0xf0} },
diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c
index 2be17b93e0bd..0d12763603b4 100644
--- a/drivers/media/dvb/frontends/dib0070.c
+++ b/drivers/media/dvb/frontends/dib0070.c
@@ -49,21 +49,6 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
#define DIB0070_P1G 0x03
#define DIB0070S_P1A 0x02
-enum frontend_tune_state {
- CT_TUNER_START = 10,
- CT_TUNER_STEP_0,
- CT_TUNER_STEP_1,
- CT_TUNER_STEP_2,
- CT_TUNER_STEP_3,
- CT_TUNER_STEP_4,
- CT_TUNER_STEP_5,
- CT_TUNER_STEP_6,
- CT_TUNER_STEP_7,
- CT_TUNER_STOP,
-};
-
-#define FE_CALLBACK_TIME_NEVER 0xffffffff
-
struct dib0070_state {
struct i2c_adapter *i2c;
struct dvb_frontend *fe;
@@ -71,10 +56,10 @@ struct dib0070_state {
u16 wbd_ff_offset;
u8 revision;
- enum frontend_tune_state tune_state;
- u32 current_rf;
+ enum frontend_tune_state tune_state;
+ u32 current_rf;
- /* for the captrim binary search */
+ /* for the captrim binary search */
s8 step;
u16 adc_diff;
@@ -85,7 +70,7 @@ struct dib0070_state {
const struct dib0070_tuning *current_tune_table_index;
const struct dib0070_lna_match *lna_match;
- u8 wbd_gain_current;
+ u8 wbd_gain_current;
u16 wbd_offset_3_3[2];
};
@@ -93,8 +78,8 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
{
u8 b[2];
struct i2c_msg msg[2] = {
- {.addr = state->cfg->i2c_address,.flags = 0,.buf = &reg,.len = 1},
- {.addr = state->cfg->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2},
+ { .addr = state->cfg->i2c_address, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2 },
};
if (i2c_transfer(state->i2c, msg, 2) != 2) {
printk(KERN_WARNING "DiB0070 I2C read failed\n");
@@ -106,7 +91,7 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
{
u8 b[3] = { reg, val >> 8, val & 0xff };
- struct i2c_msg msg = {.addr = state->cfg->i2c_address,.flags = 0,.buf = b,.len = 3 };
+ struct i2c_msg msg = { .addr = state->cfg->i2c_address, .flags = 0, .buf = b, .len = 3 };
if (i2c_transfer(state->i2c, &msg, 1) != 1) {
printk(KERN_WARNING "DiB0070 I2C write failed\n");
return -EREMOTEIO;
@@ -124,30 +109,30 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
{
- struct dib0070_state *state = fe->tuner_priv;
- u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
-
- if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 7000)
- tmp |= (0 << 14);
- else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 6000)
- tmp |= (1 << 14);
- else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 5000)
- tmp |= (2 << 14);
- else
- tmp |= (3 << 14);
-
- dib0070_write_reg(state, 0x02, tmp);
-
- /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */
- if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
- u16 value = dib0070_read_reg(state, 0x17);
-
- dib0070_write_reg(state, 0x17, value & 0xfffc);
- tmp = dib0070_read_reg(state, 0x01) & 0x01ff;
- dib0070_write_reg(state, 0x01, tmp | (60 << 9));
-
- dib0070_write_reg(state, 0x17, value);
- }
+ struct dib0070_state *state = fe->tuner_priv;
+ u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
+
+ if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000)
+ tmp |= (0 << 14);
+ else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000)
+ tmp |= (1 << 14);
+ else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000)
+ tmp |= (2 << 14);
+ else
+ tmp |= (3 << 14);
+
+ dib0070_write_reg(state, 0x02, tmp);
+
+ /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */
+ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
+ u16 value = dib0070_read_reg(state, 0x17);
+
+ dib0070_write_reg(state, 0x17, value & 0xfffc);
+ tmp = dib0070_read_reg(state, 0x01) & 0x01ff;
+ dib0070_write_reg(state, 0x01, tmp | (60 << 9));
+
+ dib0070_write_reg(state, 0x17, value);
+ }
return 0;
}
@@ -160,14 +145,14 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state
if (*tune_state == CT_TUNER_STEP_0) {
dib0070_write_reg(state, 0x0f, 0xed10);
- dib0070_write_reg(state, 0x17, 0x0034);
+ dib0070_write_reg(state, 0x17, 0x0034);
dib0070_write_reg(state, 0x18, 0x0032);
state->step = state->captrim = state->fcaptrim = 64;
state->adc_diff = 3000;
ret = 20;
- *tune_state = CT_TUNER_STEP_1;
+ *tune_state = CT_TUNER_STEP_1;
} else if (*tune_state == CT_TUNER_STEP_1) {
state->step /= 2;
dib0070_write_reg(state, 0x14, state->lo4 | state->captrim);
@@ -178,7 +163,7 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state
adc = dib0070_read_reg(state, 0x19);
- dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc * (u32) 1800 / (u32) 1024);
+ dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc*(u32)1800/(u32)1024);
if (adc >= 400) {
adc -= 400;
@@ -193,6 +178,8 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state
state->adc_diff = adc;
state->fcaptrim = state->captrim;
+
+
}
state->captrim += (step_sign * state->step);
@@ -213,7 +200,7 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state
static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)
{
struct dib0070_state *state = fe->tuner_priv;
- u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);
+ u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);
dprintk("CTRL_LO5: 0x%x", lo5);
return dib0070_write_reg(state, 0x15, lo5);
}
@@ -227,99 +214,99 @@ void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
dib0070_write_reg(state, 0x1a, 0x0000);
} else {
dib0070_write_reg(state, 0x1b, 0x4112);
- if (state->cfg->vga_filter != 0) {
- dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
- dprintk("vga filter register is set to %x", state->cfg->vga_filter);
- } else
- dib0070_write_reg(state, 0x1a, 0x0009);
+ if (state->cfg->vga_filter != 0) {
+ dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
+ dprintk("vga filter register is set to %x", state->cfg->vga_filter);
+ } else
+ dib0070_write_reg(state, 0x1a, 0x0009);
}
}
EXPORT_SYMBOL(dib0070_ctrl_agc_filter);
struct dib0070_tuning {
- u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
- u8 switch_trim;
- u8 vco_band;
- u8 hfdiv;
- u8 vco_multi;
- u8 presc;
- u8 wbdmux;
- u16 tuner_enable;
+ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u8 switch_trim;
+ u8 vco_band;
+ u8 hfdiv;
+ u8 vco_multi;
+ u8 presc;
+ u8 wbdmux;
+ u16 tuner_enable;
};
struct dib0070_lna_match {
- u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
- u8 lna_band;
+ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u8 lna_band;
};
static const struct dib0070_tuning dib0070s_tuning_table[] = {
- {570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800}, /* UHF */
- {700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800},
- {863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800},
- {1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400}, /* LBAND */
- {1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400},
- {2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400},
- {0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000}, /* SBAND */
+ { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */
+ { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 },
+ { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 },
+ { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */
+ { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
+ { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
+ { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */
};
static const struct dib0070_tuning dib0070_tuning_table[] = {
- {115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000}, /* FM below 92MHz cannot be tuned */
- {179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000}, /* VHF */
- {189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000},
- {250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000},
- {569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800}, /* UHF */
- {699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800},
- {863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800},
- {0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400}, /* LBAND or everything higher than UHF */
+ { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */
+ { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */
+ { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 },
+ { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 },
+ { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */
+ { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 },
+ { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 },
+ { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */
};
static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
- {180000, 0}, /* VHF */
- {188000, 1},
- {196400, 2},
- {250000, 3},
- {550000, 0}, /* UHF */
- {590000, 1},
- {666000, 3},
- {864000, 5},
- {1500000, 0}, /* LBAND or everything higher than UHF */
- {1600000, 1},
- {2000000, 3},
- {0xffffffff, 7},
+ { 180000, 0 }, /* VHF */
+ { 188000, 1 },
+ { 196400, 2 },
+ { 250000, 3 },
+ { 550000, 0 }, /* UHF */
+ { 590000, 1 },
+ { 666000, 3 },
+ { 864000, 5 },
+ { 1500000, 0 }, /* LBAND or everything higher than UHF */
+ { 1600000, 1 },
+ { 2000000, 3 },
+ { 0xffffffff, 7 },
};
static const struct dib0070_lna_match dib0070_lna[] = {
- {180000, 0}, /* VHF */
- {188000, 1},
- {196400, 2},
- {250000, 3},
- {550000, 2}, /* UHF */
- {650000, 3},
- {750000, 5},
- {850000, 6},
- {864000, 7},
- {1500000, 0}, /* LBAND or everything higher than UHF */
- {1600000, 1},
- {2000000, 3},
- {0xffffffff, 7},
+ { 180000, 0 }, /* VHF */
+ { 188000, 1 },
+ { 196400, 2 },
+ { 250000, 3 },
+ { 550000, 2 }, /* UHF */
+ { 650000, 3 },
+ { 750000, 5 },
+ { 850000, 6 },
+ { 864000, 7 },
+ { 1500000, 0 }, /* LBAND or everything higher than UHF */
+ { 1600000, 1 },
+ { 2000000, 3 },
+ { 0xffffffff, 7 },
};
-#define LPF 100 // define for the loop filter 100kHz by default 16-07-06
+#define LPF 100
static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
{
- struct dib0070_state *state = fe->tuner_priv;
+ struct dib0070_state *state = fe->tuner_priv;
- const struct dib0070_tuning *tune;
- const struct dib0070_lna_match *lna_match;
+ const struct dib0070_tuning *tune;
+ const struct dib0070_lna_match *lna_match;
- enum frontend_tune_state *tune_state = &state->tune_state;
- int ret = 10; /* 1ms is the default delay most of the time */
+ enum frontend_tune_state *tune_state = &state->tune_state;
+ int ret = 10; /* 1ms is the default delay most of the time */
- u8 band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000);
- u32 freq = fe->dtv_property_cache.frequency / 1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);
+ u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000);
+ u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);
#ifdef CONFIG_SYS_ISDBT
- if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
+ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)
&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
@@ -328,172 +315,180 @@ static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_par
&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))
freq += 850;
#endif
+ if (state->current_rf != freq) {
+
+ switch (state->revision) {
+ case DIB0070S_P1A:
+ tune = dib0070s_tuning_table;
+ lna_match = dib0070_lna;
+ break;
+ default:
+ tune = dib0070_tuning_table;
+ if (state->cfg->flip_chip)
+ lna_match = dib0070_lna_flip_chip;
+ else
+ lna_match = dib0070_lna;
+ break;
+ }
+ while (freq > tune->max_freq) /* find the right one */
+ tune++;
+ while (freq > lna_match->max_freq) /* find the right one */
+ lna_match++;
+
+ state->current_tune_table_index = tune;
+ state->lna_match = lna_match;
+ }
+
+ if (*tune_state == CT_TUNER_START) {
+ dprintk("Tuning for Band: %hd (%d kHz)", band, freq);
if (state->current_rf != freq) {
+ u8 REFDIV;
+ u32 FBDiv, Rest, FREF, VCOF_kHz;
+ u8 Den;
+
+ state->current_rf = freq;
+ state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
+
+
+ dib0070_write_reg(state, 0x17, 0x30);
+
+
+ VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
+
+ switch (band) {
+ case BAND_VHF:
+ REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
+ break;
+ case BAND_FM:
+ REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
+ break;
+ default:
+ REFDIV = (u8) (state->cfg->clock_khz / 10000);
+ break;
+ }
+ FREF = state->cfg->clock_khz / REFDIV;
+
+
switch (state->revision) {
case DIB0070S_P1A:
- tune = dib0070s_tuning_table;
- lna_match = dib0070_lna;
+ FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
+ Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
break;
+
+ case DIB0070_P1G:
+ case DIB0070_P1F:
default:
- tune = dib0070_tuning_table;
- if (state->cfg->flip_chip)
- lna_match = dib0070_lna_flip_chip;
- else
- lna_match = dib0070_lna;
+ FBDiv = (freq / (FREF / 2));
+ Rest = 2 * freq - FBDiv * FREF;
break;
}
- while (freq > tune->max_freq) /* find the right one */
- tune++;
- while (freq > lna_match->max_freq) /* find the right one */
- lna_match++;
- state->current_tune_table_index = tune;
- state->lna_match = lna_match;
- }
+ if (Rest < LPF)
+ Rest = 0;
+ else if (Rest < 2 * LPF)
+ Rest = 2 * LPF;
+ else if (Rest > (FREF - LPF)) {
+ Rest = 0;
+ FBDiv += 1;
+ } else if (Rest > (FREF - 2 * LPF))
+ Rest = FREF - 2 * LPF;
+ Rest = (Rest * 6528) / (FREF / 10);
+
+ Den = 1;
+ if (Rest > 0) {
+ state->lo4 |= (1 << 14) | (1 << 12);
+ Den = 255;
+ }
+
- if (*tune_state == CT_TUNER_START) {
- dprintk("Tuning for Band: %hd (%d kHz)", band, freq);
- if (state->current_rf != freq) {
- u8 REFDIV;
- u32 FBDiv, Rest, FREF, VCOF_kHz;
- u8 Den;
-
- state->current_rf = freq;
- state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
-
- dib0070_write_reg(state, 0x17, 0x30);
-
- VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
-
- switch (band) {
- case BAND_VHF:
- REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
- break;
- case BAND_FM:
- REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
- break;
- default:
- REFDIV = (u8) (state->cfg->clock_khz / 10000);
- break;
- }
- FREF = state->cfg->clock_khz / REFDIV;
-
- switch (state->revision) {
- case DIB0070S_P1A:
- FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
- Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
- break;
-
- case DIB0070_P1G:
- case DIB0070_P1F:
- default:
- FBDiv = (freq / (FREF / 2));
- Rest = 2 * freq - FBDiv * FREF;
- break;
- }
-
- if (Rest < LPF)
- Rest = 0;
- else if (Rest < 2 * LPF)
- Rest = 2 * LPF;
- else if (Rest > (FREF - LPF)) {
- Rest = 0;
- FBDiv += 1;
- } else if (Rest > (FREF - 2 * LPF))
- Rest = FREF - 2 * LPF;
- Rest = (Rest * 6528) / (FREF / 10);
-
- Den = 1;
- if (Rest > 0) {
- state->lo4 |= (1 << 14) | (1 << 12);
- Den = 255;
- }
-
- dib0070_write_reg(state, 0x11, (u16) FBDiv);
- dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
- dib0070_write_reg(state, 0x13, (u16) Rest);
-
- if (state->revision == DIB0070S_P1A) {
-
- if (band == BAND_SBAND) {
- dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
- dib0070_write_reg(state, 0x1d, 0xFFFF);
- } else
- dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
- }
-
- dib0070_write_reg(state, 0x20,
- 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
-
- dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF);
- dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest);
- dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1);
- dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv);
- dprintk("VCO = %hd", state->current_tune_table_index->vco_band);
- dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq);
-
- *tune_state = CT_TUNER_STEP_0;
- } else { /* we are already tuned to this frequency - the configuration is correct */
- ret = 50; /* wakeup time */
- *tune_state = CT_TUNER_STEP_5;
+ dib0070_write_reg(state, 0x11, (u16)FBDiv);
+ dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
+ dib0070_write_reg(state, 0x13, (u16) Rest);
+
+ if (state->revision == DIB0070S_P1A) {
+
+ if (band == BAND_SBAND) {
+ dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
+ dib0070_write_reg(state, 0x1d, 0xFFFF);
+ } else
+ dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
}
- } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
- ret = dib0070_captrim(state, tune_state);
+ dib0070_write_reg(state, 0x20,
+ 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
- } else if (*tune_state == CT_TUNER_STEP_4) {
- const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
- if (tmp != NULL) {
- while (freq / 1000 > tmp->freq) /* find the right one */
- tmp++;
- dib0070_write_reg(state, 0x0f,
- (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (state->
- current_tune_table_index->
- wbdmux << 0));
- state->wbd_gain_current = tmp->wbd_gain_val;
- } else {
+ dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF);
+ dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest);
+ dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1);
+ dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv);
+ dprintk("VCO = %hd", state->current_tune_table_index->vco_band);
+ dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq);
+
+ *tune_state = CT_TUNER_STEP_0;
+ } else { /* we are already tuned to this frequency - the configuration is correct */
+ ret = 50; /* wakeup time */
+ *tune_state = CT_TUNER_STEP_5;
+ }
+ } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
+
+ ret = dib0070_captrim(state, tune_state);
+
+ } else if (*tune_state == CT_TUNER_STEP_4) {
+ const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
+ if (tmp != NULL) {
+ while (freq/1000 > tmp->freq) /* find the right one */
+ tmp++;
+ dib0070_write_reg(state, 0x0f,
+ (0 << 15) | (1 << 14) | (3 << 12)
+ | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7)
+ | (state->current_tune_table_index->wbdmux << 0));
+ state->wbd_gain_current = tmp->wbd_gain_val;
+ } else {
dib0070_write_reg(state, 0x0f,
(0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index->
wbdmux << 0));
- state->wbd_gain_current = 6;
- }
+ state->wbd_gain_current = 6;
+ }
- dib0070_write_reg(state, 0x06, 0x3fff);
+ dib0070_write_reg(state, 0x06, 0x3fff);
dib0070_write_reg(state, 0x07,
(state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));
- dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
- dib0070_write_reg(state, 0x0d, 0x0d80);
+ dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
+ dib0070_write_reg(state, 0x0d, 0x0d80);
- dib0070_write_reg(state, 0x18, 0x07ff);
- dib0070_write_reg(state, 0x17, 0x0033);
- *tune_state = CT_TUNER_STEP_5;
- } else if (*tune_state == CT_TUNER_STEP_5) {
- dib0070_set_bandwidth(fe, ch);
- *tune_state = CT_TUNER_STOP;
- } else {
- ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */
- }
- return ret;
+ dib0070_write_reg(state, 0x18, 0x07ff);
+ dib0070_write_reg(state, 0x17, 0x0033);
+
+
+ *tune_state = CT_TUNER_STEP_5;
+ } else if (*tune_state == CT_TUNER_STEP_5) {
+ dib0070_set_bandwidth(fe, ch);
+ *tune_state = CT_TUNER_STOP;
+ } else {
+ ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */
+ }
+ return ret;
}
+
static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
{
- struct dib0070_state *state = fe->tuner_priv;
- uint32_t ret;
+ struct dib0070_state *state = fe->tuner_priv;
+ uint32_t ret;
- state->tune_state = CT_TUNER_START;
+ state->tune_state = CT_TUNER_START;
- do {
- ret = dib0070_tune_digital(fe, p);
- if (ret != FE_CALLBACK_TIME_NEVER)
- msleep(ret / 10);
- else
- break;
- } while (state->tune_state != CT_TUNER_STOP);
+ do {
+ ret = dib0070_tune_digital(fe, p);
+ if (ret != FE_CALLBACK_TIME_NEVER)
+ msleep(ret/10);
+ else
+ break;
+ } while (state->tune_state != CT_TUNER_STOP);
- return 0;
+ return 0;
}
static int dib0070_wakeup(struct dvb_frontend *fe)
@@ -512,92 +507,113 @@ static int dib0070_sleep(struct dvb_frontend *fe)
return 0;
}
-static const u16 dib0070_p1f_defaults[] = {
+u8 dib0070_get_rf_output(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ return (dib0070_read_reg(state, 0x07) >> 11) & 0x3;
+}
+EXPORT_SYMBOL(dib0070_get_rf_output);
+
+int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff;
+ if (no > 3)
+ no = 3;
+ if (no < 1)
+ no = 1;
+ return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11));
+}
+EXPORT_SYMBOL(dib0070_set_rf_output);
+
+static const u16 dib0070_p1f_defaults[] =
+
+{
7, 0x02,
- 0x0008,
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0002,
- 0x0100,
+ 0x0008,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0002,
+ 0x0100,
3, 0x0d,
- 0x0d80,
- 0x0001,
- 0x0000,
+ 0x0d80,
+ 0x0001,
+ 0x0000,
4, 0x11,
- 0x0000,
- 0x0103,
- 0x0000,
- 0x0000,
+ 0x0000,
+ 0x0103,
+ 0x0000,
+ 0x0000,
3, 0x16,
- 0x0004 | 0x0040,
- 0x0030,
- 0x07ff,
+ 0x0004 | 0x0040,
+ 0x0030,
+ 0x07ff,
6, 0x1b,
- 0x4112,
- 0xff00,
- 0xc07f,
- 0x0000,
- 0x0180,
- 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
+ 0x4112,
+ 0xff00,
+ 0xc07f,
+ 0x0000,
+ 0x0180,
+ 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
0,
};
static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
{
- u16 tuner_en = dib0070_read_reg(state, 0x20);
- u16 offset;
-
- dib0070_write_reg(state, 0x18, 0x07ff);
- dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
- dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
- msleep(9);
- offset = dib0070_read_reg(state, 0x19);
- dib0070_write_reg(state, 0x20, tuner_en);
- return offset;
+ u16 tuner_en = dib0070_read_reg(state, 0x20);
+ u16 offset;
+
+ dib0070_write_reg(state, 0x18, 0x07ff);
+ dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
+ dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
+ msleep(9);
+ offset = dib0070_read_reg(state, 0x19);
+ dib0070_write_reg(state, 0x20, tuner_en);
+ return offset;
}
static void dib0070_wbd_offset_calibration(struct dib0070_state *state)
{
- u8 gain;
- for (gain = 6; gain < 8; gain++) {
- state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
- dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain - 6]);
- }
+ u8 gain;
+ for (gain = 6; gain < 8; gain++) {
+ state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
+ dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]);
+ }
}
u16 dib0070_wbd_offset(struct dvb_frontend *fe)
{
- struct dib0070_state *state = fe->tuner_priv;
- const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
- u32 freq = fe->dtv_property_cache.frequency / 1000;
-
- if (tmp != NULL) {
- while (freq / 1000 > tmp->freq) /* find the right one */
- tmp++;
- state->wbd_gain_current = tmp->wbd_gain_val;
+ struct dib0070_state *state = fe->tuner_priv;
+ const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
+ u32 freq = fe->dtv_property_cache.frequency/1000;
+
+ if (tmp != NULL) {
+ while (freq/1000 > tmp->freq) /* find the right one */
+ tmp++;
+ state->wbd_gain_current = tmp->wbd_gain_val;
} else
- state->wbd_gain_current = 6;
+ state->wbd_gain_current = 6;
- return state->wbd_offset_3_3[state->wbd_gain_current - 6];
+ return state->wbd_offset_3_3[state->wbd_gain_current - 6];
}
-
EXPORT_SYMBOL(dib0070_wbd_offset);
#define pgm_read_word(w) (*w)
static int dib0070_reset(struct dvb_frontend *fe)
{
- struct dib0070_state *state = fe->tuner_priv;
+ struct dib0070_state *state = fe->tuner_priv;
u16 l, r, *n;
HARD_RESET(state);
+
#ifndef FORCE_SBAND_TUNER
if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)
state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;
@@ -605,7 +621,7 @@ static int dib0070_reset(struct dvb_frontend *fe)
#else
#warning forcing SBAND
#endif
- state->revision = DIB0070S_P1A;
+ state->revision = DIB0070S_P1A;
/* P1F or not */
dprintk("Revision: %x", state->revision);
@@ -620,7 +636,7 @@ static int dib0070_reset(struct dvb_frontend *fe)
while (l) {
r = pgm_read_word(n++);
do {
- dib0070_write_reg(state, (u8) r, pgm_read_word(n++));
+ dib0070_write_reg(state, (u8)r, pgm_read_word(n++));
r++;
} while (--l);
l = pgm_read_word(n++);
@@ -633,6 +649,7 @@ static int dib0070_reset(struct dvb_frontend *fe)
else
r = 2;
+
r |= state->cfg->osc_buffer_state << 3;
dib0070_write_reg(state, 0x10, r);
@@ -643,16 +660,24 @@ static int dib0070_reset(struct dvb_frontend *fe)
dib0070_write_reg(state, 0x02, r | (1 << 5));
}
- if (state->revision == DIB0070S_P1A)
- dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
- else
+ if (state->revision == DIB0070S_P1A)
+ dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
+ else
dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter);
dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
- dib0070_wbd_offset_calibration(state);
+ dib0070_wbd_offset_calibration(state);
- return 0;
+ return 0;
+}
+
+static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+
+ *frequency = 1000 * state->current_rf;
+ return 0;
}
static int dib0070_release(struct dvb_frontend *fe)
@@ -664,18 +689,18 @@ static int dib0070_release(struct dvb_frontend *fe)
static const struct dvb_tuner_ops dib0070_ops = {
.info = {
- .name = "DiBcom DiB0070",
- .frequency_min = 45000000,
- .frequency_max = 860000000,
- .frequency_step = 1000,
- },
- .release = dib0070_release,
-
- .init = dib0070_wakeup,
- .sleep = dib0070_sleep,
- .set_params = dib0070_tune,
-
-// .get_frequency = dib0070_get_frequency,
+ .name = "DiBcom DiB0070",
+ .frequency_min = 45000000,
+ .frequency_max = 860000000,
+ .frequency_step = 1000,
+ },
+ .release = dib0070_release,
+
+ .init = dib0070_wakeup,
+ .sleep = dib0070_sleep,
+ .set_params = dib0070_tune,
+
+ .get_frequency = dib0070_get_frequency,
// .get_bandwidth = dib0070_get_bandwidth
};
@@ -687,7 +712,7 @@ struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter
state->cfg = cfg;
state->i2c = i2c;
- state->fe = fe;
+ state->fe = fe;
fe->tuner_priv = state;
if (dib0070_reset(fe) != 0)
@@ -699,12 +724,11 @@ struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter
fe->tuner_priv = state;
return fe;
- free_mem:
+free_mem:
kfree(state);
fe->tuner_priv = NULL;
return NULL;
}
-
EXPORT_SYMBOL(dib0070_attach);
MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h
index eec9e52ffa75..45c31fae3967 100644
--- a/drivers/media/dvb/frontends/dib0070.h
+++ b/drivers/media/dvb/frontends/dib0070.h
@@ -52,6 +52,8 @@ struct dib0070_config {
extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg);
extern u16 dib0070_wbd_offset(struct dvb_frontend *);
extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open);
+extern u8 dib0070_get_rf_output(struct dvb_frontend *fe);
+extern int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no);
#else
static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
{
@@ -62,7 +64,7 @@ static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struc
static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -ENODEV;
+ return 0;
}
static inline void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
new file mode 100644
index 000000000000..614552709a6f
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -0,0 +1,1522 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner.
+ *
+ * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 code is more or less generated from another driver, please
+ * excuse some codingstyle oddities.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib0090.h"
+#include "dibx000_common.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { \
+ if (debug) { \
+ printk(KERN_DEBUG "DiB0090: "); \
+ printk(args); \
+ printk("\n"); \
+ } \
+} while (0)
+
+#define CONFIG_SYS_ISDBT
+#define CONFIG_BAND_CBAND
+#define CONFIG_BAND_VHF
+#define CONFIG_BAND_UHF
+#define CONFIG_DIB0090_USE_PWM_AGC
+
+#define EN_LNA0 0x8000
+#define EN_LNA1 0x4000
+#define EN_LNA2 0x2000
+#define EN_LNA3 0x1000
+#define EN_MIX0 0x0800
+#define EN_MIX1 0x0400
+#define EN_MIX2 0x0200
+#define EN_MIX3 0x0100
+#define EN_IQADC 0x0040
+#define EN_PLL 0x0020
+#define EN_TX 0x0010
+#define EN_BB 0x0008
+#define EN_LO 0x0004
+#define EN_BIAS 0x0001
+
+#define EN_IQANA 0x0002
+#define EN_DIGCLK 0x0080 /* not in the 0x24 reg, only in 0x1b */
+#define EN_CRYSTAL 0x0002
+
+#define EN_UHF 0x22E9
+#define EN_VHF 0x44E9
+#define EN_LBD 0x11E9
+#define EN_SBD 0x44E9
+#define EN_CAB 0x88E9
+
+#define pgm_read_word(w) (*w)
+
+struct dc_calibration;
+
+struct dib0090_tuning {
+ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u8 switch_trim;
+ u8 lna_tune;
+ u8 lna_bias;
+ u16 v2i;
+ u16 mix;
+ u16 load;
+ u16 tuner_enable;
+};
+
+struct dib0090_pll {
+ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u8 vco_band;
+ u8 hfdiv_code;
+ u8 hfdiv;
+ u8 topresc;
+};
+
+struct dib0090_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe;
+ const struct dib0090_config *config;
+
+ u8 current_band;
+ u16 revision;
+ enum frontend_tune_state tune_state;
+ u32 current_rf;
+
+ u16 wbd_offset;
+ s16 wbd_target; /* in dB */
+
+ s16 rf_gain_limit; /* take-over-point: where to split between bb and rf gain */
+ s16 current_gain; /* keeps the currently programmed gain */
+ u8 agc_step; /* new binary search */
+
+ u16 gain[2]; /* for channel monitoring */
+
+ const u16 *rf_ramp;
+ const u16 *bb_ramp;
+
+ /* for the software AGC ramps */
+ u16 bb_1_def;
+ u16 rf_lt_def;
+ u16 gain_reg[4];
+
+ /* for the captrim/dc-offset search */
+ s8 step;
+ s16 adc_diff;
+ s16 min_adc_diff;
+
+ s8 captrim;
+ s8 fcaptrim;
+
+ const struct dc_calibration *dc;
+ u16 bb6, bb7;
+
+ const struct dib0090_tuning *current_tune_table_index;
+ const struct dib0090_pll *current_pll_table_index;
+
+ u8 tuner_is_tuned;
+ u8 agc_freeze;
+
+ u8 reset;
+};
+
+static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
+{
+ u8 b[2];
+ struct i2c_msg msg[2] = {
+ {.addr = state->config->i2c_address, .flags = 0, .buf = &reg, .len = 1},
+ {.addr = state->config->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2},
+ };
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "DiB0090 I2C read failed\n");
+ return 0;
+ }
+ return (b[0] << 8) | b[1];
+}
+
+static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
+{
+ u8 b[3] = { reg & 0xff, val >> 8, val & 0xff };
+ struct i2c_msg msg = {.addr = state->config->i2c_address, .flags = 0, .buf = b, .len = 3 };
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "DiB0090 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0)
+#define ADC_TARGET -220
+#define GAIN_ALPHA 5
+#define WBD_ALPHA 6
+#define LPF 100
+static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b, u8 c)
+{
+ do {
+ dib0090_write_reg(state, r++, *b++);
+ } while (--c);
+}
+
+static u16 dib0090_identify(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ u16 v;
+
+ v = dib0090_read_reg(state, 0x1a);
+
+#ifdef FIRMWARE_FIREFLY
+ /* pll is not locked locked */
+ if (!(v & 0x800))
+ dprintk("FE%d : Identification : pll is not yet locked", fe->id);
+#endif
+
+ /* without PLL lock info */
+ v &= 0x3ff;
+ dprintk("P/V: %04x:", v);
+
+ if ((v >> 8) & 0xf)
+ dprintk("FE%d : Product ID = 0x%x : KROSUS", fe->id, (v >> 8) & 0xf);
+ else
+ return 0xff;
+
+ v &= 0xff;
+ if (((v >> 5) & 0x7) == 0x1)
+ dprintk("FE%d : MP001 : 9090/8096", fe->id);
+ else if (((v >> 5) & 0x7) == 0x4)
+ dprintk("FE%d : MP005 : Single Sband", fe->id);
+ else if (((v >> 5) & 0x7) == 0x6)
+ dprintk("FE%d : MP008 : diversity VHF-UHF-LBAND", fe->id);
+ else if (((v >> 5) & 0x7) == 0x7)
+ dprintk("FE%d : MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND", fe->id);
+ else
+ return 0xff;
+
+ /* revision only */
+ if ((v & 0x1f) == 0x3)
+ dprintk("FE%d : P1-D/E/F detected", fe->id);
+ else if ((v & 0x1f) == 0x1)
+ dprintk("FE%d : P1C detected", fe->id);
+ else if ((v & 0x1f) == 0x0) {
+#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
+ dprintk("FE%d : P1-A/B detected: using previous driver - support will be removed soon", fe->id);
+ dib0090_p1b_register(fe);
+#else
+ dprintk("FE%d : P1-A/B detected: driver is deactivated - not available", fe->id);
+ return 0xff;
+#endif
+ }
+
+ return v;
+}
+
+static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+
+ HARD_RESET(state);
+
+ dib0090_write_reg(state, 0x24, EN_PLL);
+ dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */
+
+ /* adcClkOutRatio=8->7, release reset */
+ dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0);
+ if (cfg->clkoutdrive != 0)
+ dib0090_write_reg(state, 0x23,
+ (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (cfg->clkoutdrive << 5) | (cfg->
+ clkouttobamse
+ << 4) | (0
+ <<
+ 2)
+ | (0));
+ else
+ dib0090_write_reg(state, 0x23,
+ (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (7 << 5) | (cfg->
+ clkouttobamse << 4) | (0
+ <<
+ 2)
+ | (0));
+
+ /* enable pll, de-activate reset, ratio: 2/1 = 60MHz */
+ dib0090_write_reg(state, 0x21,
+ (cfg->io.pll_bypass << 15) | (1 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv));
+
+}
+
+static int dib0090_wakeup(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ if (state->config->sleep)
+ state->config->sleep(fe, 0);
+ return 0;
+}
+
+static int dib0090_sleep(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ if (state->config->sleep)
+ state->config->sleep(fe, 1);
+ return 0;
+}
+
+extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ if (fast)
+ dib0090_write_reg(state, 0x04, 0);
+ else
+ dib0090_write_reg(state, 0x04, 1);
+}
+EXPORT_SYMBOL(dib0090_dcc_freq);
+
+static const u16 rf_ramp_pwm_cband[] = {
+ 0, /* max RF gain in 10th of dB */
+ 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
+ 0, /* ramp_max = maximum X used on the ramp */
+ (0 << 10) | 0, /* 0x2c, LNA 1 = 0dB */
+ (0 << 10) | 0, /* 0x2d, LNA 1 */
+ (0 << 10) | 0, /* 0x2e, LNA 2 = 0dB */
+ (0 << 10) | 0, /* 0x2f, LNA 2 */
+ (0 << 10) | 0, /* 0x30, LNA 3 = 0dB */
+ (0 << 10) | 0, /* 0x31, LNA 3 */
+ (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */
+ (0 << 10) | 0, /* GAIN_4_2, LNA 4 */
+};
+
+static const u16 rf_ramp_vhf[] = {
+ 412, /* max RF gain in 10th of dB */
+ 132, 307, 127, /* LNA1, 13.2dB */
+ 105, 412, 255, /* LNA2, 10.5dB */
+ 50, 50, 127, /* LNA3, 5dB */
+ 125, 175, 127, /* LNA4, 12.5dB */
+ 0, 0, 127, /* CBAND, 0dB */
+};
+
+static const u16 rf_ramp_uhf[] = {
+ 412, /* max RF gain in 10th of dB */
+ 132, 307, 127, /* LNA1 : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */
+ 105, 412, 255, /* LNA2 : 10.5 dB */
+ 50, 50, 127, /* LNA3 : 5.0 dB */
+ 125, 175, 127, /* LNA4 : 12.5 dB */
+ 0, 0, 127, /* CBAND : 0.0 dB */
+};
+
+static const u16 rf_ramp_cband[] = {
+ 332, /* max RF gain in 10th of dB */
+ 132, 252, 127, /* LNA1, dB */
+ 80, 332, 255, /* LNA2, dB */
+ 0, 0, 127, /* LNA3, dB */
+ 0, 0, 127, /* LNA4, dB */
+ 120, 120, 127, /* LT1 CBAND */
+};
+
+static const u16 rf_ramp_pwm_vhf[] = {
+ 404, /* max RF gain in 10th of dB */
+ 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
+ 1011, /* ramp_max = maximum X used on the ramp */
+ (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */
+ (0 << 10) | 756, /* 0x2d, LNA 1 */
+ (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */
+ (0 << 10) | 1011, /* 0x2f, LNA 2 */
+ (16 << 10) | 290, /* 0x30, LNA 3 = 5dB */
+ (0 << 10) | 417, /* 0x31, LNA 3 */
+ (7 << 10) | 0, /* GAIN_4_1, LNA 4 = 12.5dB */
+ (0 << 10) | 290, /* GAIN_4_2, LNA 4 */
+};
+
+static const u16 rf_ramp_pwm_uhf[] = {
+ 404, /* max RF gain in 10th of dB */
+ 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
+ 1011, /* ramp_max = maximum X used on the ramp */
+ (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */
+ (0 << 10) | 756, /* 0x2d, LNA 1 */
+ (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */
+ (0 << 10) | 1011, /* 0x2f, LNA 2 */
+ (16 << 10) | 0, /* 0x30, LNA 3 = 5dB */
+ (0 << 10) | 127, /* 0x31, LNA 3 */
+ (7 << 10) | 127, /* GAIN_4_1, LNA 4 = 12.5dB */
+ (0 << 10) | 417, /* GAIN_4_2, LNA 4 */
+};
+
+static const u16 bb_ramp_boost[] = {
+ 550, /* max BB gain in 10th of dB */
+ 260, 260, 26, /* BB1, 26dB */
+ 290, 550, 29, /* BB2, 29dB */
+};
+
+static const u16 bb_ramp_pwm_normal[] = {
+ 500, /* max RF gain in 10th of dB */
+ 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */
+ 400,
+ (2 << 9) | 0, /* 0x35 = 21dB */
+ (0 << 9) | 168, /* 0x36 */
+ (2 << 9) | 168, /* 0x37 = 29dB */
+ (0 << 9) | 400, /* 0x38 */
+};
+
+struct slope {
+ int16_t range;
+ int16_t slope;
+};
+static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val)
+{
+ u8 i;
+ u16 rest;
+ u16 ret = 0;
+ for (i = 0; i < num; i++) {
+ if (val > slopes[i].range)
+ rest = slopes[i].range;
+ else
+ rest = val;
+ ret += (rest * slopes[i].slope) / slopes[i].range;
+ val -= rest;
+ }
+ return ret;
+}
+
+static const struct slope dib0090_wbd_slopes[3] = {
+ {66, 120}, /* -64,-52: offset - 65 */
+ {600, 170}, /* -52,-35: 65 - 665 */
+ {170, 250}, /* -45,-10: 665 - 835 */
+};
+
+static s16 dib0090_wbd_to_db(struct dib0090_state *state, u16 wbd)
+{
+ wbd &= 0x3ff;
+ if (wbd < state->wbd_offset)
+ wbd = 0;
+ else
+ wbd -= state->wbd_offset;
+ /* -64dB is the floor */
+ return -640 + (s16) slopes_to_scale(dib0090_wbd_slopes, ARRAY_SIZE(dib0090_wbd_slopes), wbd);
+}
+
+static void dib0090_wbd_target(struct dib0090_state *state, u32 rf)
+{
+ u16 offset = 250;
+
+ /* TODO : DAB digital N+/-1 interferer perfs : offset = 10 */
+
+ if (state->current_band == BAND_VHF)
+ offset = 650;
+#ifndef FIRMWARE_FIREFLY
+ if (state->current_band == BAND_VHF)
+ offset = state->config->wbd_vhf_offset;
+ if (state->current_band == BAND_CBAND)
+ offset = state->config->wbd_cband_offset;
+#endif
+
+ state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + offset);
+ dprintk("wbd-target: %d dB", (u32) state->wbd_target);
+}
+
+static const int gain_reg_addr[4] = {
+ 0x08, 0x0a, 0x0f, 0x01
+};
+
+static void dib0090_gain_apply(struct dib0090_state *state, s16 gain_delta, s16 top_delta, u8 force)
+{
+ u16 rf, bb, ref;
+ u16 i, v, gain_reg[4] = { 0 }, gain;
+ const u16 *g;
+
+ if (top_delta < -511)
+ top_delta = -511;
+ if (top_delta > 511)
+ top_delta = 511;
+
+ if (force) {
+ top_delta *= (1 << WBD_ALPHA);
+ gain_delta *= (1 << GAIN_ALPHA);
+ }
+
+ if (top_delta >= ((s16) (state->rf_ramp[0] << WBD_ALPHA) - state->rf_gain_limit)) /* overflow */
+ state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA;
+ else
+ state->rf_gain_limit += top_delta;
+
+ if (state->rf_gain_limit < 0) /*underflow */
+ state->rf_gain_limit = 0;
+
+ /* use gain as a temporary variable and correct current_gain */
+ gain = ((state->rf_gain_limit >> WBD_ALPHA) + state->bb_ramp[0]) << GAIN_ALPHA;
+ if (gain_delta >= ((s16) gain - state->current_gain)) /* overflow */
+ state->current_gain = gain;
+ else
+ state->current_gain += gain_delta;
+ /* cannot be less than 0 (only if gain_delta is less than 0 we can have current_gain < 0) */
+ if (state->current_gain < 0)
+ state->current_gain = 0;
+
+ /* now split total gain to rf and bb gain */
+ gain = state->current_gain >> GAIN_ALPHA;
+
+ /* requested gain is bigger than rf gain limit - ACI/WBD adjustment */
+ if (gain > (state->rf_gain_limit >> WBD_ALPHA)) {
+ rf = state->rf_gain_limit >> WBD_ALPHA;
+ bb = gain - rf;
+ if (bb > state->bb_ramp[0])
+ bb = state->bb_ramp[0];
+ } else { /* high signal level -> all gains put on RF */
+ rf = gain;
+ bb = 0;
+ }
+
+ state->gain[0] = rf;
+ state->gain[1] = bb;
+
+ /* software ramp */
+ /* Start with RF gains */
+ g = state->rf_ramp + 1; /* point on RF LNA1 max gain */
+ ref = rf;
+ for (i = 0; i < 7; i++) { /* Go over all amplifiers => 5RF amps + 2 BB amps = 7 amps */
+ if (g[0] == 0 || ref < (g[1] - g[0])) /* if total gain of the current amp is null or this amp is not concerned because it starts to work from an higher gain value */
+ v = 0; /* force the gain to write for the current amp to be null */
+ else if (ref >= g[1]) /* Gain to set is higher than the high working point of this amp */
+ v = g[2]; /* force this amp to be full gain */
+ else /* compute the value to set to this amp because we are somewhere in his range */
+ v = ((ref - (g[1] - g[0])) * g[2]) / g[0];
+
+ if (i == 0) /* LNA 1 reg mapping */
+ gain_reg[0] = v;
+ else if (i == 1) /* LNA 2 reg mapping */
+ gain_reg[0] |= v << 7;
+ else if (i == 2) /* LNA 3 reg mapping */
+ gain_reg[1] = v;
+ else if (i == 3) /* LNA 4 reg mapping */
+ gain_reg[1] |= v << 7;
+ else if (i == 4) /* CBAND LNA reg mapping */
+ gain_reg[2] = v | state->rf_lt_def;
+ else if (i == 5) /* BB gain 1 reg mapping */
+ gain_reg[3] = v << 3;
+ else if (i == 6) /* BB gain 2 reg mapping */
+ gain_reg[3] |= v << 8;
+
+ g += 3; /* go to next gain bloc */
+
+ /* When RF is finished, start with BB */
+ if (i == 4) {
+ g = state->bb_ramp + 1; /* point on BB gain 1 max gain */
+ ref = bb;
+ }
+ }
+ gain_reg[3] |= state->bb_1_def;
+ gain_reg[3] |= ((bb % 10) * 100) / 125;
+
+#ifdef DEBUG_AGC
+ dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x", rf, bb, rf + bb,
+ gain_reg[0], gain_reg[1], gain_reg[2], gain_reg[3]);
+#endif
+
+ /* Write the amplifier regs */
+ for (i = 0; i < 4; i++) {
+ v = gain_reg[i];
+ if (force || state->gain_reg[i] != v) {
+ state->gain_reg[i] = v;
+ dib0090_write_reg(state, gain_reg_addr[i], v);
+ }
+ }
+}
+
+static void dib0090_set_boost(struct dib0090_state *state, int onoff)
+{
+ state->bb_1_def &= 0xdfff;
+ state->bb_1_def |= onoff << 13;
+}
+
+static void dib0090_set_rframp(struct dib0090_state *state, const u16 * cfg)
+{
+ state->rf_ramp = cfg;
+}
+
+static void dib0090_set_rframp_pwm(struct dib0090_state *state, const u16 * cfg)
+{
+ state->rf_ramp = cfg;
+
+ dib0090_write_reg(state, 0x2a, 0xffff);
+
+ dprintk("total RF gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x2a));
+
+ dib0090_write_regs(state, 0x2c, cfg + 3, 6);
+ dib0090_write_regs(state, 0x3e, cfg + 9, 2);
+}
+
+static void dib0090_set_bbramp(struct dib0090_state *state, const u16 * cfg)
+{
+ state->bb_ramp = cfg;
+ dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */
+}
+
+static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg)
+{
+ state->bb_ramp = cfg;
+
+ dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */
+
+ dib0090_write_reg(state, 0x33, 0xffff);
+ dprintk("total BB gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x33));
+ dib0090_write_regs(state, 0x35, cfg + 3, 4);
+}
+
+void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ /* reset the AGC */
+
+ if (state->config->use_pwm_agc) {
+#ifdef CONFIG_BAND_SBAND
+ if (state->current_band == BAND_SBAND) {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost);
+ } else
+#endif
+#ifdef CONFIG_BAND_CBAND
+ if (state->current_band == BAND_CBAND) {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ } else
+#endif
+#ifdef CONFIG_BAND_VHF
+ if (state->current_band == BAND_VHF) {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ } else
+#endif
+ {
+ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf);
+ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+ }
+
+ if (state->rf_ramp[0] != 0)
+ dib0090_write_reg(state, 0x32, (3 << 11));
+ else
+ dib0090_write_reg(state, 0x32, (0 << 11));
+
+ dib0090_write_reg(state, 0x39, (1 << 10));
+ }
+}
+EXPORT_SYMBOL(dib0090_pwm_gain_reset);
+
+int dib0090_gain_control(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ enum frontend_tune_state *tune_state = &state->tune_state;
+ int ret = 10;
+
+ u16 wbd_val = 0;
+ u8 apply_gain_immediatly = 1;
+ s16 wbd_error = 0, adc_error = 0;
+
+ if (*tune_state == CT_AGC_START) {
+ state->agc_freeze = 0;
+ dib0090_write_reg(state, 0x04, 0x0);
+
+#ifdef CONFIG_BAND_SBAND
+ if (state->current_band == BAND_SBAND) {
+ dib0090_set_rframp(state, rf_ramp_sband);
+ dib0090_set_bbramp(state, bb_ramp_boost);
+ } else
+#endif
+#ifdef CONFIG_BAND_VHF
+ if (state->current_band == BAND_VHF) {
+ dib0090_set_rframp(state, rf_ramp_vhf);
+ dib0090_set_bbramp(state, bb_ramp_boost);
+ } else
+#endif
+#ifdef CONFIG_BAND_CBAND
+ if (state->current_band == BAND_CBAND) {
+ dib0090_set_rframp(state, rf_ramp_cband);
+ dib0090_set_bbramp(state, bb_ramp_boost);
+ } else
+#endif
+ {
+ dib0090_set_rframp(state, rf_ramp_uhf);
+ dib0090_set_bbramp(state, bb_ramp_boost);
+ }
+
+ dib0090_write_reg(state, 0x32, 0);
+ dib0090_write_reg(state, 0x39, 0);
+
+ dib0090_wbd_target(state, state->current_rf);
+
+ state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA;
+ state->current_gain = ((state->rf_ramp[0] + state->bb_ramp[0]) / 2) << GAIN_ALPHA;
+
+ *tune_state = CT_AGC_STEP_0;
+ } else if (!state->agc_freeze) {
+ s16 wbd;
+
+ int adc;
+ wbd_val = dib0090_read_reg(state, 0x1d);
+
+ /* read and calc the wbd power */
+ wbd = dib0090_wbd_to_db(state, wbd_val);
+ wbd_error = state->wbd_target - wbd;
+
+ if (*tune_state == CT_AGC_STEP_0) {
+ if (wbd_error < 0 && state->rf_gain_limit > 0) {
+#ifdef CONFIG_BAND_CBAND
+ /* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */
+ u8 ltg2 = (state->rf_lt_def >> 10) & 0x7;
+ if (state->current_band == BAND_CBAND && ltg2) {
+ ltg2 >>= 1;
+ state->rf_lt_def &= ltg2 << 10; /* reduce in 3 steps from 7 to 0 */
+ }
+#endif
+ } else {
+ state->agc_step = 0;
+ *tune_state = CT_AGC_STEP_1;
+ }
+ } else {
+ /* calc the adc power */
+ adc = state->config->get_adc_power(fe);
+ adc = (adc * ((s32) 355774) + (((s32) 1) << 20)) >> 21; /* included in [0:-700] */
+
+ adc_error = (s16) (((s32) ADC_TARGET) - adc);
+#ifdef CONFIG_STANDARD_DAB
+ if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB)
+ adc_error += 130;
+#endif
+#ifdef CONFIG_STANDARD_DVBT
+ if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT &&
+ (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16))
+ adc_error += 60;
+#endif
+#ifdef CONFIG_SYS_ISDBT
+ if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count >
+ 0)
+ &&
+ ((state->fe->dtv_property_cache.layer[0].modulation ==
+ QAM_64)
+ || (state->fe->dtv_property_cache.layer[0].
+ modulation == QAM_16)))
+ ||
+ ((state->fe->dtv_property_cache.layer[1].segment_count >
+ 0)
+ &&
+ ((state->fe->dtv_property_cache.layer[1].modulation ==
+ QAM_64)
+ || (state->fe->dtv_property_cache.layer[1].
+ modulation == QAM_16)))
+ ||
+ ((state->fe->dtv_property_cache.layer[2].segment_count >
+ 0)
+ &&
+ ((state->fe->dtv_property_cache.layer[2].modulation ==
+ QAM_64)
+ || (state->fe->dtv_property_cache.layer[2].
+ modulation == QAM_16)))
+ )
+ )
+ adc_error += 60;
+#endif
+
+ if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */
+ if (ABS(adc_error) < 50 || state->agc_step++ > 5) {
+
+#ifdef CONFIG_STANDARD_DAB
+ if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) {
+ dib0090_write_reg(state, 0x02, (1 << 15) | (15 << 11) | (31 << 6) | (63)); /* cap value = 63 : narrow BB filter : Fc = 1.8MHz */
+ dib0090_write_reg(state, 0x04, 0x0);
+ } else
+#endif
+ {
+ dib0090_write_reg(state, 0x02, (1 << 15) | (3 << 11) | (6 << 6) | (32));
+ dib0090_write_reg(state, 0x04, 0x01); /*0 = 1KHz ; 1 = 150Hz ; 2 = 50Hz ; 3 = 50KHz ; 4 = servo fast */
+ }
+
+ *tune_state = CT_AGC_STOP;
+ }
+ } else {
+ /* everything higher than or equal to CT_AGC_STOP means tracking */
+ ret = 100; /* 10ms interval */
+ apply_gain_immediatly = 0;
+ }
+ }
+#ifdef DEBUG_AGC
+ dprintk
+ ("FE: %d, tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm",
+ (u32) fe->id, (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val,
+ (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA));
+#endif
+ }
+
+ /* apply gain */
+ if (!state->agc_freeze)
+ dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly);
+ return ret;
+}
+EXPORT_SYMBOL(dib0090_gain_control);
+
+void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ if (rf)
+ *rf = state->gain[0];
+ if (bb)
+ *bb = state->gain[1];
+ if (rf_gain_limit)
+ *rf_gain_limit = state->rf_gain_limit;
+ if (rflt)
+ *rflt = (state->rf_lt_def >> 10) & 0x7;
+}
+EXPORT_SYMBOL(dib0090_get_current_gain);
+
+u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner)
+{
+ struct dib0090_state *st = tuner->tuner_priv;
+ return st->wbd_offset;
+}
+EXPORT_SYMBOL(dib0090_get_wbd_offset);
+
+static const u16 dib0090_defaults[] = {
+
+ 25, 0x01,
+ 0x0000,
+ 0x99a0,
+ 0x6008,
+ 0x0000,
+ 0x8acb,
+ 0x0000,
+ 0x0405,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0xb802,
+ 0x0300,
+ 0x2d12,
+ 0xbac0,
+ 0x7c00,
+ 0xdbb9,
+ 0x0954,
+ 0x0743,
+ 0x8000,
+ 0x0001,
+ 0x0040,
+ 0x0100,
+ 0x0000,
+ 0xe910,
+ 0x149e,
+
+ 1, 0x1c,
+ 0xff2d,
+
+ 1, 0x39,
+ 0x0000,
+
+ 1, 0x1b,
+ EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL,
+ 2, 0x1e,
+ 0x07FF,
+ 0x0007,
+
+ 1, 0x24,
+ EN_UHF | EN_CRYSTAL,
+
+ 2, 0x3c,
+ 0x3ff,
+ 0x111,
+ 0
+};
+
+static int dib0090_reset(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ u16 l, r, *n;
+
+ dib0090_reset_digital(fe, state->config);
+ state->revision = dib0090_identify(fe);
+
+ /* Revision definition */
+ if (state->revision == 0xff)
+ return -EINVAL;
+#ifdef EFUSE
+ else if ((state->revision & 0x1f) >= 3) /* Update the efuse : Only available for KROSUS > P1C */
+ dib0090_set_EFUSE(state);
+#endif
+
+#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
+ if (!(state->revision & 0x1)) /* it is P1B - reset is already done */
+ return 0;
+#endif
+
+ /* Upload the default values */
+ n = (u16 *) dib0090_defaults;
+ l = pgm_read_word(n++);
+ while (l) {
+ r = pgm_read_word(n++);
+ do {
+ /* DEBUG_TUNER */
+ /* dprintk("%d, %d, %d", l, r, pgm_read_word(n)); */
+ dib0090_write_reg(state, r, pgm_read_word(n++));
+ r++;
+ } while (--l);
+ l = pgm_read_word(n++);
+ }
+
+ /* Congigure in function of the crystal */
+ if (state->config->io.clock_khz >= 24000)
+ l = 1;
+ else
+ l = 2;
+ dib0090_write_reg(state, 0x14, l);
+ dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1);
+
+ state->reset = 3; /* enable iq-offset-calibration and wbd-calibration when tuning next time */
+
+ return 0;
+}
+
+#define steps(u) (((u) > 15) ? ((u)-16) : (u))
+#define INTERN_WAIT 10
+static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_state *tune_state)
+{
+ int ret = INTERN_WAIT * 10;
+
+ switch (*tune_state) {
+ case CT_TUNER_STEP_2:
+ /* Turns to positive */
+ dib0090_write_reg(state, 0x1f, 0x7);
+ *tune_state = CT_TUNER_STEP_3;
+ break;
+
+ case CT_TUNER_STEP_3:
+ state->adc_diff = dib0090_read_reg(state, 0x1d);
+
+ /* Turns to negative */
+ dib0090_write_reg(state, 0x1f, 0x4);
+ *tune_state = CT_TUNER_STEP_4;
+ break;
+
+ case CT_TUNER_STEP_4:
+ state->adc_diff -= dib0090_read_reg(state, 0x1d);
+ *tune_state = CT_TUNER_STEP_5;
+ ret = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+struct dc_calibration {
+ uint8_t addr;
+ uint8_t offset;
+ uint8_t pga:1;
+ uint16_t bb1;
+ uint8_t i:1;
+};
+
+static const struct dc_calibration dc_table[] = {
+ /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */
+ {0x06, 5, 1, (1 << 13) | (0 << 8) | (26 << 3), 1},
+ {0x07, 11, 1, (1 << 13) | (0 << 8) | (26 << 3), 0},
+ /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */
+ {0x06, 0, 0, (1 << 13) | (29 << 8) | (26 << 3), 1},
+ {0x06, 10, 0, (1 << 13) | (29 << 8) | (26 << 3), 0},
+ {0},
+};
+
+static void dib0090_set_trim(struct dib0090_state *state)
+{
+ u16 *val;
+
+ if (state->dc->addr == 0x07)
+ val = &state->bb7;
+ else
+ val = &state->bb6;
+
+ *val &= ~(0x1f << state->dc->offset);
+ *val |= state->step << state->dc->offset;
+
+ dib0090_write_reg(state, state->dc->addr, *val);
+}
+
+static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state)
+{
+ int ret = 0;
+
+ switch (*tune_state) {
+
+ case CT_TUNER_START:
+ /* init */
+ dprintk("Internal DC calibration");
+
+ /* the LNA is off */
+ dib0090_write_reg(state, 0x24, 0x02ed);
+
+ /* force vcm2 = 0.8V */
+ state->bb6 = 0;
+ state->bb7 = 0x040d;
+
+ state->dc = dc_table;
+
+ *tune_state = CT_TUNER_STEP_0;
+
+ /* fall through */
+
+ case CT_TUNER_STEP_0:
+ dib0090_write_reg(state, 0x01, state->dc->bb1);
+ dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7));
+
+ state->step = 0;
+
+ state->min_adc_diff = 1023;
+
+ *tune_state = CT_TUNER_STEP_1;
+ ret = 50;
+ break;
+
+ case CT_TUNER_STEP_1:
+ dib0090_set_trim(state);
+
+ *tune_state = CT_TUNER_STEP_2;
+ break;
+
+ case CT_TUNER_STEP_2:
+ case CT_TUNER_STEP_3:
+ case CT_TUNER_STEP_4:
+ ret = dib0090_get_offset(state, tune_state);
+ break;
+
+ case CT_TUNER_STEP_5: /* found an offset */
+ dprintk("FE%d: IQC read=%d, current=%x", state->fe->id, (u32) state->adc_diff, state->step);
+
+ /* first turn for this frequency */
+ if (state->step == 0) {
+ if (state->dc->pga && state->adc_diff < 0)
+ state->step = 0x10;
+ if (state->dc->pga == 0 && state->adc_diff > 0)
+ state->step = 0x10;
+ }
+
+ state->adc_diff = ABS(state->adc_diff);
+
+ if (state->adc_diff < state->min_adc_diff && steps(state->step) < 15) { /* stop search when the delta to 0 is increasing */
+ state->step++;
+ state->min_adc_diff = state->adc_diff;
+ *tune_state = CT_TUNER_STEP_1;
+ } else {
+
+ /* the minimum was what we have seen in the step before */
+ state->step--;
+ dib0090_set_trim(state);
+
+ dprintk("FE%d: BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->fe->id, state->dc->addr, state->adc_diff,
+ state->step);
+
+ state->dc++;
+ if (state->dc->addr == 0) /* done */
+ *tune_state = CT_TUNER_STEP_6;
+ else
+ *tune_state = CT_TUNER_STEP_0;
+
+ }
+ break;
+
+ case CT_TUNER_STEP_6:
+ dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008);
+ dib0090_write_reg(state, 0x1f, 0x7);
+ *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */
+ state->reset &= ~0x1;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state)
+{
+ switch (*tune_state) {
+ case CT_TUNER_START:
+ /* WBD-mode=log, Bias=2, Gain=6, Testmode=1, en=1, WBDMUX=1 */
+ dib0090_write_reg(state, 0x10, 0xdb09 | (1 << 10));
+ dib0090_write_reg(state, 0x24, EN_UHF & 0x0fff);
+
+ *tune_state = CT_TUNER_STEP_0;
+ return 90; /* wait for the WBDMUX to switch and for the ADC to sample */
+ case CT_TUNER_STEP_0:
+ state->wbd_offset = dib0090_read_reg(state, 0x1d);
+ dprintk("WBD calibration offset = %d", state->wbd_offset);
+
+ *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */
+ state->reset &= ~0x2;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void dib0090_set_bandwidth(struct dib0090_state *state)
+{
+ u16 tmp;
+
+ if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 5000)
+ tmp = (3 << 14);
+ else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 6000)
+ tmp = (2 << 14);
+ else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 7000)
+ tmp = (1 << 14);
+ else
+ tmp = (0 << 14);
+
+ state->bb_1_def &= 0x3fff;
+ state->bb_1_def |= tmp;
+
+ dib0090_write_reg(state, 0x01, state->bb_1_def); /* be sure that we have the right bb-filter */
+}
+
+static const struct dib0090_pll dib0090_pll_table[] = {
+#ifdef CONFIG_BAND_CBAND
+ {56000, 0, 9, 48, 6},
+ {70000, 1, 9, 48, 6},
+ {87000, 0, 8, 32, 4},
+ {105000, 1, 8, 32, 4},
+ {115000, 0, 7, 24, 6},
+ {140000, 1, 7, 24, 6},
+ {170000, 0, 6, 16, 4},
+#endif
+#ifdef CONFIG_BAND_VHF
+ {200000, 1, 6, 16, 4},
+ {230000, 0, 5, 12, 6},
+ {280000, 1, 5, 12, 6},
+ {340000, 0, 4, 8, 4},
+ {380000, 1, 4, 8, 4},
+ {450000, 0, 3, 6, 6},
+#endif
+#ifdef CONFIG_BAND_UHF
+ {580000, 1, 3, 6, 6},
+ {700000, 0, 2, 4, 4},
+ {860000, 1, 2, 4, 4},
+#endif
+#ifdef CONFIG_BAND_LBAND
+ {1800000, 1, 0, 2, 4},
+#endif
+#ifdef CONFIG_BAND_SBAND
+ {2900000, 0, 14, 1, 4},
+#endif
+};
+
+static const struct dib0090_tuning dib0090_tuning_table_fm_vhf_on_cband[] = {
+
+#ifdef CONFIG_BAND_CBAND
+ {184000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
+ {227000, 4, 3, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
+ {380000, 4, 7, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
+#endif
+#ifdef CONFIG_BAND_UHF
+ {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+#endif
+#ifdef CONFIG_BAND_LBAND
+ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+#endif
+#ifdef CONFIG_BAND_SBAND
+ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD},
+ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD},
+#endif
+};
+
+static const struct dib0090_tuning dib0090_tuning_table[] = {
+
+#ifdef CONFIG_BAND_CBAND
+ {170000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
+#endif
+#ifdef CONFIG_BAND_VHF
+ {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
+ {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
+ {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
+#endif
+#ifdef CONFIG_BAND_UHF
+ {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
+#endif
+#ifdef CONFIG_BAND_LBAND
+ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
+#endif
+#ifdef CONFIG_BAND_SBAND
+ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD},
+ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD},
+#endif
+};
+
+#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */
+static int dib0090_tune(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ const struct dib0090_tuning *tune = state->current_tune_table_index;
+ const struct dib0090_pll *pll = state->current_pll_table_index;
+ enum frontend_tune_state *tune_state = &state->tune_state;
+
+ u32 rf;
+ u16 lo4 = 0xe900, lo5, lo6, Den;
+ u32 FBDiv, Rest, FREF, VCOF_kHz = 0;
+ u16 tmp, adc;
+ int8_t step_sign;
+ int ret = 10; /* 1ms is the default delay most of the time */
+ u8 c, i;
+
+ state->current_band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000);
+ rf = fe->dtv_property_cache.frequency / 1000 + (state->current_band ==
+ BAND_UHF ? state->config->freq_offset_khz_uhf : state->config->freq_offset_khz_vhf);
+ /* in any case we first need to do a reset if needed */
+ if (state->reset & 0x1)
+ return dib0090_dc_offset_calibration(state, tune_state);
+ else if (state->reset & 0x2)
+ return dib0090_wbd_calibration(state, tune_state);
+
+ /************************* VCO ***************************/
+ /* Default values for FG */
+ /* from these are needed : */
+ /* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv */
+
+#ifdef CONFIG_SYS_ISDBT
+ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
+ rf += 850;
+#endif
+
+ if (state->current_rf != rf) {
+ state->tuner_is_tuned = 0;
+
+ tune = dib0090_tuning_table;
+
+ tmp = (state->revision >> 5) & 0x7;
+ if (tmp == 0x4 || tmp == 0x7) {
+ /* CBAND tuner version for VHF */
+ if (state->current_band == BAND_FM || state->current_band == BAND_VHF) {
+ /* Force CBAND */
+ state->current_band = BAND_CBAND;
+ tune = dib0090_tuning_table_fm_vhf_on_cband;
+ }
+ }
+
+ pll = dib0090_pll_table;
+ /* Look for the interval */
+ while (rf > tune->max_freq)
+ tune++;
+ while (rf > pll->max_freq)
+ pll++;
+ state->current_tune_table_index = tune;
+ state->current_pll_table_index = pll;
+ }
+
+ if (*tune_state == CT_TUNER_START) {
+
+ if (state->tuner_is_tuned == 0)
+ state->current_rf = 0;
+
+ if (state->current_rf != rf) {
+
+ dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim));
+
+ /* external loop filter, otherwise:
+ * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4;
+ * lo6 = 0x0e34 */
+ if (pll->vco_band)
+ lo5 = 0x049e;
+ else if (state->config->analog_output)
+ lo5 = 0x041d;
+ else
+ lo5 = 0x041c;
+
+ lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */
+
+ if (!state->config->io.pll_int_loop_filt)
+ lo6 = 0xff28;
+ else
+ lo6 = (state->config->io.pll_int_loop_filt << 3);
+
+ VCOF_kHz = (pll->hfdiv * rf) * 2;
+
+ FREF = state->config->io.clock_khz;
+
+ FBDiv = (VCOF_kHz / pll->topresc / FREF);
+ Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF;
+
+ if (Rest < LPF)
+ Rest = 0;
+ else if (Rest < 2 * LPF)
+ Rest = 2 * LPF;
+ else if (Rest > (FREF - LPF)) {
+ Rest = 0;
+ FBDiv += 1;
+ } else if (Rest > (FREF - 2 * LPF))
+ Rest = FREF - 2 * LPF;
+ Rest = (Rest * 6528) / (FREF / 10);
+
+ Den = 1;
+
+ dprintk(" ***** ******* Rest value = %d", Rest);
+
+ if (Rest > 0) {
+ if (state->config->analog_output)
+ lo6 |= (1 << 2) | 2;
+ else
+ lo6 |= (1 << 2) | 1;
+ Den = 255;
+ }
+#ifdef CONFIG_BAND_SBAND
+ if (state->current_band == BAND_SBAND)
+ lo6 &= 0xfffb;
+#endif
+
+ dib0090_write_reg(state, 0x15, (u16) FBDiv);
+
+ dib0090_write_reg(state, 0x16, (Den << 8) | 1);
+
+ dib0090_write_reg(state, 0x17, (u16) Rest);
+
+ dib0090_write_reg(state, 0x19, lo5);
+
+ dib0090_write_reg(state, 0x1c, lo6);
+
+ lo6 = tune->tuner_enable;
+ if (state->config->analog_output)
+ lo6 = (lo6 & 0xff9f) | 0x2;
+
+ dib0090_write_reg(state, 0x24, lo6 | EN_LO
+#ifdef CONFIG_DIB0090_USE_PWM_AGC
+ | state->config->use_pwm_agc * EN_CRYSTAL
+#endif
+ );
+
+ state->current_rf = rf;
+
+ /* prepare a complete captrim */
+ state->step = state->captrim = state->fcaptrim = 64;
+
+ } else { /* we are already tuned to this frequency - the configuration is correct */
+
+ /* do a minimal captrim even if the frequency has not changed */
+ state->step = 4;
+ state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f;
+ }
+ state->adc_diff = 3000;
+
+ dib0090_write_reg(state, 0x10, 0x2B1);
+
+ dib0090_write_reg(state, 0x1e, 0x0032);
+
+ ret = 20;
+ *tune_state = CT_TUNER_STEP_1;
+ } else if (*tune_state == CT_TUNER_STEP_0) {
+ /* nothing */
+ } else if (*tune_state == CT_TUNER_STEP_1) {
+ state->step /= 2;
+ dib0090_write_reg(state, 0x18, lo4 | state->captrim);
+ *tune_state = CT_TUNER_STEP_2;
+ } else if (*tune_state == CT_TUNER_STEP_2) {
+
+ adc = dib0090_read_reg(state, 0x1d);
+ dprintk("FE %d CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) fe->id, (u32) state->captrim, (u32) adc,
+ (u32) (adc) * (u32) 1800 / (u32) 1024);
+
+ if (adc >= 400) {
+ adc -= 400;
+ step_sign = -1;
+ } else {
+ adc = 400 - adc;
+ step_sign = 1;
+ }
+
+ if (adc < state->adc_diff) {
+ dprintk("FE %d CAPTRIM=%d is closer to target (%d/%d)", (u32) fe->id, (u32) state->captrim, (u32) adc, (u32) state->adc_diff);
+ state->adc_diff = adc;
+ state->fcaptrim = state->captrim;
+
+ }
+
+ state->captrim += step_sign * state->step;
+ if (state->step >= 1)
+ *tune_state = CT_TUNER_STEP_1;
+ else
+ *tune_state = CT_TUNER_STEP_3;
+
+ ret = 15;
+ } else if (*tune_state == CT_TUNER_STEP_3) {
+ /*write the final cptrim config */
+ dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim);
+
+#ifdef CONFIG_TUNER_DIB0090_CAPTRIM_MEMORY
+ state->memory[state->memory_index].cap = state->fcaptrim;
+#endif
+
+ *tune_state = CT_TUNER_STEP_4;
+ } else if (*tune_state == CT_TUNER_STEP_4) {
+ dib0090_write_reg(state, 0x1e, 0x07ff);
+
+ dprintk("FE %d Final Captrim: %d", (u32) fe->id, (u32) state->fcaptrim);
+ dprintk("FE %d HFDIV code: %d", (u32) fe->id, (u32) pll->hfdiv_code);
+ dprintk("FE %d VCO = %d", (u32) fe->id, (u32) pll->vco_band);
+ dprintk("FE %d VCOF in kHz: %d ((%d*%d) << 1))", (u32) fe->id, (u32) ((pll->hfdiv * rf) * 2), (u32) pll->hfdiv, (u32) rf);
+ dprintk("FE %d REFDIV: %d, FREF: %d", (u32) fe->id, (u32) 1, (u32) state->config->io.clock_khz);
+ dprintk("FE %d FBDIV: %d, Rest: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17));
+ dprintk("FE %d Num: %d, Den: %d, SD: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x17),
+ (u32) (dib0090_read_reg(state, 0x16) >> 8), (u32) dib0090_read_reg(state, 0x1c) & 0x3);
+
+ c = 4;
+ i = 3;
+#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND)
+ if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) {
+ c = 2;
+ i = 2;
+ }
+#endif
+ dib0090_write_reg(state, 0x10, (c << 13) | (i << 11) | (WBD
+#ifdef CONFIG_DIB0090_USE_PWM_AGC
+ | (state->config->use_pwm_agc << 1)
+#endif
+ ));
+ dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | (tune->lna_bias << 0));
+ dib0090_write_reg(state, 0x0c, tune->v2i);
+ dib0090_write_reg(state, 0x0d, tune->mix);
+ dib0090_write_reg(state, 0x0e, tune->load);
+
+ *tune_state = CT_TUNER_STEP_5;
+ } else if (*tune_state == CT_TUNER_STEP_5) {
+
+ /* initialize the lt gain register */
+ state->rf_lt_def = 0x7c00;
+ dib0090_write_reg(state, 0x0f, state->rf_lt_def);
+
+ dib0090_set_bandwidth(state);
+ state->tuner_is_tuned = 1;
+ *tune_state = CT_TUNER_STOP;
+ } else
+ ret = FE_CALLBACK_TIME_NEVER;
+ return ret;
+}
+
+static int dib0090_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+
+ return state->tune_state;
+}
+EXPORT_SYMBOL(dib0090_get_tune_state);
+
+int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+
+ state->tune_state = tune_state;
+ return 0;
+}
+EXPORT_SYMBOL(dib0090_set_tune_state);
+
+static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+
+ *frequency = 1000 * state->current_rf;
+ return 0;
+}
+
+static int dib0090_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct dib0090_state *state = fe->tuner_priv;
+ uint32_t ret;
+
+ state->tune_state = CT_TUNER_START;
+
+ do {
+ ret = dib0090_tune(fe);
+ if (ret != FE_CALLBACK_TIME_NEVER)
+ msleep(ret / 10);
+ else
+ break;
+ } while (state->tune_state != CT_TUNER_STOP);
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops dib0090_ops = {
+ .info = {
+ .name = "DiBcom DiB0090",
+ .frequency_min = 45000000,
+ .frequency_max = 860000000,
+ .frequency_step = 1000,
+ },
+ .release = dib0090_release,
+
+ .init = dib0090_wakeup,
+ .sleep = dib0090_sleep,
+ .set_params = dib0090_set_params,
+ .get_frequency = dib0090_get_frequency,
+};
+
+struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config)
+{
+ struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ st->config = config;
+ st->i2c = i2c;
+ st->fe = fe;
+ fe->tuner_priv = st;
+
+ if (dib0090_reset(fe) != 0)
+ goto free_mem;
+
+ printk(KERN_INFO "DiB0090: successfully identified\n");
+ memcpy(&fe->ops.tuner_ops, &dib0090_ops, sizeof(struct dvb_tuner_ops));
+
+ return fe;
+ free_mem:
+ kfree(st);
+ fe->tuner_priv = NULL;
+ return NULL;
+}
+EXPORT_SYMBOL(dib0090_register);
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Olivier Grenie <olivier.grenie@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib0090.h b/drivers/media/dvb/frontends/dib0090.h
new file mode 100644
index 000000000000..aa7711e88776
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib0090.h
@@ -0,0 +1,108 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner.
+ *
+ * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+#ifndef DIB0090_H
+#define DIB0090_H
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+#define DEFAULT_DIB0090_I2C_ADDRESS 0x60
+
+struct dib0090_io_config {
+ u32 clock_khz;
+
+ u8 pll_bypass:1;
+ u8 pll_range:1;
+ u8 pll_prediv:6;
+ u8 pll_loopdiv:6;
+
+ u8 adc_clock_ratio; /* valid is 8, 7 ,6 */
+ u16 pll_int_loop_filt;
+};
+
+struct dib0090_config {
+ struct dib0090_io_config io;
+ int (*reset) (struct dvb_frontend *, int);
+ int (*sleep) (struct dvb_frontend *, int);
+
+ /* offset in kHz */
+ int freq_offset_khz_uhf;
+ int freq_offset_khz_vhf;
+
+ int (*get_adc_power) (struct dvb_frontend *);
+
+ u8 clkouttobamse:1; /* activate or deactivate clock output */
+ u8 analog_output;
+
+ u8 i2c_address;
+ /* add drives and other things if necessary */
+ u16 wbd_vhf_offset;
+ u16 wbd_cband_offset;
+ u8 use_pwm_agc;
+ u8 clkoutdrive;
+};
+
+#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE))
+extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
+extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast);
+extern void dib0090_pwm_gain_reset(struct dvb_frontend *fe);
+extern u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner);
+extern int dib0090_gain_control(struct dvb_frontend *fe);
+extern enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe);
+extern int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state);
+extern void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt);
+#else
+static inline struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0090_config *config)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline int dib0090_gain_control(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return CT_DONE;
+}
+
+static inline int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index 898400d331a3..6f6fa29d9ea4 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -28,18 +28,6 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0)
-enum frontend_tune_state {
- CT_AGC_START = 20,
- CT_AGC_STEP_0,
- CT_AGC_STEP_1,
- CT_AGC_STEP_2,
- CT_AGC_STEP_3,
- CT_AGC_STEP_4,
- CT_AGC_STOP,
-
- CT_DEMOD_START = 30,
-};
-
#define FE_STATUS_TUNE_FAILED 0
struct i2c_device {
@@ -133,104 +121,104 @@ static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
return dib8000_i2c_write16(&state->i2c, reg, val);
}
-const int16_t coeff_2k_sb_1seg_dqpsk[8] = {
+static const int16_t coeff_2k_sb_1seg_dqpsk[8] = {
(769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c,
(920 << 5) | 0x09
};
-const int16_t coeff_2k_sb_1seg[8] = {
+static const int16_t coeff_2k_sb_1seg[8] = {
(692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f
};
-const int16_t coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = {
+static const int16_t coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = {
(832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11,
(-931 << 5) | 0x0f
};
-const int16_t coeff_2k_sb_3seg_0dqpsk[8] = {
+static const int16_t coeff_2k_sb_3seg_0dqpsk[8] = {
(622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e,
(982 << 5) | 0x0c
};
-const int16_t coeff_2k_sb_3seg_1dqpsk[8] = {
+static const int16_t coeff_2k_sb_3seg_1dqpsk[8] = {
(699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12,
(-720 << 5) | 0x0d
};
-const int16_t coeff_2k_sb_3seg[8] = {
+static const int16_t coeff_2k_sb_3seg[8] = {
(664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e,
(-610 << 5) | 0x0a
};
-const int16_t coeff_4k_sb_1seg_dqpsk[8] = {
+static const int16_t coeff_4k_sb_1seg_dqpsk[8] = {
(-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f,
(-922 << 5) | 0x0d
};
-const int16_t coeff_4k_sb_1seg[8] = {
+static const int16_t coeff_4k_sb_1seg[8] = {
(638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d,
(-655 << 5) | 0x0a
};
-const int16_t coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = {
+static const int16_t coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = {
(-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14,
(-958 << 5) | 0x13
};
-const int16_t coeff_4k_sb_3seg_0dqpsk[8] = {
+static const int16_t coeff_4k_sb_3seg_0dqpsk[8] = {
(-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12,
(-568 << 5) | 0x0f
};
-const int16_t coeff_4k_sb_3seg_1dqpsk[8] = {
+static const int16_t coeff_4k_sb_3seg_1dqpsk[8] = {
(-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14,
(-848 << 5) | 0x13
};
-const int16_t coeff_4k_sb_3seg[8] = {
+static const int16_t coeff_4k_sb_3seg[8] = {
(612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12,
(-869 << 5) | 0x13
};
-const int16_t coeff_8k_sb_1seg_dqpsk[8] = {
+static const int16_t coeff_8k_sb_1seg_dqpsk[8] = {
(-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13,
(-598 << 5) | 0x10
};
-const int16_t coeff_8k_sb_1seg[8] = {
+static const int16_t coeff_8k_sb_1seg[8] = {
(673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f,
(585 << 5) | 0x0f
};
-const int16_t coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = {
+static const int16_t coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = {
(863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18,
(0 << 5) | 0x14
};
-const int16_t coeff_8k_sb_3seg_0dqpsk[8] = {
+static const int16_t coeff_8k_sb_3seg_0dqpsk[8] = {
(-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15,
(-877 << 5) | 0x15
};
-const int16_t coeff_8k_sb_3seg_1dqpsk[8] = {
+static const int16_t coeff_8k_sb_3seg_1dqpsk[8] = {
(-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18,
(-921 << 5) | 0x14
};
-const int16_t coeff_8k_sb_3seg[8] = {
+static const int16_t coeff_8k_sb_3seg[8] = {
(514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15,
(690 << 5) | 0x14
};
-const int16_t ana_fe_coeff_3seg[24] = {
+static const int16_t ana_fe_coeff_3seg[24] = {
81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017
};
-const int16_t ana_fe_coeff_1seg[24] = {
+static const int16_t ana_fe_coeff_1seg[24] = {
249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003
};
-const int16_t ana_fe_coeff_13seg[24] = {
+static const int16_t ana_fe_coeff_13seg[24] = {
396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1
};
@@ -852,6 +840,14 @@ static int dib8000_set_agc_config(struct dib8000_state *state, u8 band)
return 0;
}
+void dib8000_pwm_agc_reset(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ dib8000_set_adc_state(state, DIBX000_ADC_ON);
+ dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000)));
+}
+EXPORT_SYMBOL(dib8000_pwm_agc_reset);
+
static int dib8000_agc_soft_split(struct dib8000_state *state)
{
u16 agc, split_offset;
@@ -939,6 +935,32 @@ static int dib8000_agc_startup(struct dvb_frontend *fe)
}
+static const int32_t lut_1000ln_mant[] =
+{
+ 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600
+};
+
+int32_t dib8000_get_adc_power(struct dvb_frontend *fe, uint8_t mode)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ uint32_t ix = 0, tmp_val = 0, exp = 0, mant = 0;
+ int32_t val;
+
+ val = dib8000_read32(state, 384);
+ /* mode = 1 : ln_agcpower calc using mant-exp conversion and mantis look up table */
+ if (mode) {
+ tmp_val = val;
+ while (tmp_val >>= 1)
+ exp++;
+ mant = (val * 1000 / (1<<exp));
+ ix = (uint8_t)((mant-1000)/100); /* index of the LUT */
+ val = (lut_1000ln_mant[ix] + 693*(exp-20) - 6908); /* 1000 * ln(adcpower_real) ; 693 = 1000ln(2) ; 6908 = 1000*ln(1000) ; 20 comes from adc_real = adc_pow_int / 2**20 */
+ val = (val*256)/1000;
+ }
+ return val;
+}
+EXPORT_SYMBOL(dib8000_get_adc_power);
+
static void dib8000_update_timf(struct dib8000_state *state)
{
u32 timf = state->timf = dib8000_read32(state, 435);
@@ -1401,10 +1423,9 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
}
break;
}
- }
- if (state->fe.dtv_property_cache.isdbt_sb_mode == 1)
for (i = 0; i < 8; i++)
dib8000_write_word(state, 343 + i, ncoeff[i]);
+ }
// P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5
dib8000_write_word(state, 351,
@@ -1854,6 +1875,24 @@ static int dib8000_sleep(struct dvb_frontend *fe)
}
}
+enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ return state->tune_state;
+}
+EXPORT_SYMBOL(dib8000_get_tune_state);
+
+int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ state->tune_state = tune_state;
+ return 0;
+}
+EXPORT_SYMBOL(dib8000_set_tune_state);
+
+
+
+
static int dib8000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dib8000_state *state = fe->demodulator_priv;
@@ -2043,29 +2082,31 @@ static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
*stat = 0;
- if ((lock >> 14) & 1) // AGC
+ if ((lock >> 13) & 1)
*stat |= FE_HAS_SIGNAL;
- if ((lock >> 8) & 1) // Equal
+ if ((lock >> 8) & 1) /* Equal */
*stat |= FE_HAS_CARRIER;
- if ((lock >> 3) & 1) // TMCC_SYNC
+ if (((lock >> 1) & 0xf) == 0xf) /* TMCC_SYNC */
*stat |= FE_HAS_SYNC;
- if ((lock >> 5) & 7) // FEC MPEG
+ if (((lock >> 12) & 1) && ((lock >> 5) & 7)) /* FEC MPEG */
*stat |= FE_HAS_LOCK;
- lock = dib8000_read_word(state, 554); // Viterbi Layer A
- if (lock & 0x01)
- *stat |= FE_HAS_VITERBI;
+ if ((lock >> 12) & 1) {
+ lock = dib8000_read_word(state, 554); /* Viterbi Layer A */
+ if (lock & 0x01)
+ *stat |= FE_HAS_VITERBI;
- lock = dib8000_read_word(state, 555); // Viterbi Layer B
- if (lock & 0x01)
- *stat |= FE_HAS_VITERBI;
+ lock = dib8000_read_word(state, 555); /* Viterbi Layer B */
+ if (lock & 0x01)
+ *stat |= FE_HAS_VITERBI;
- lock = dib8000_read_word(state, 556); // Viterbi Layer C
- if (lock & 0x01)
- *stat |= FE_HAS_VITERBI;
+ lock = dib8000_read_word(state, 556); /* Viterbi Layer C */
+ if (lock & 0x01)
+ *stat |= FE_HAS_VITERBI;
+ }
return 0;
}
diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h
index 8c89482b738a..d99619ae983c 100644
--- a/drivers/media/dvb/frontends/dib8000.h
+++ b/drivers/media/dvb/frontends/dib8000.h
@@ -46,6 +46,10 @@ extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value);
extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff);
extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff);
+extern int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state);
+extern enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe);
+extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe);
+extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode);
#else
static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg)
{
@@ -59,35 +63,53 @@ static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe
return NULL;
}
-int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr)
+static inline int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+static inline int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+static inline int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+static inline int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+static inline int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
+static inline int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+static inline enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return CT_SHUTDOWN,
+}
+static inline void dib8000_pwm_agc_reset(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
#endif
#endif
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
index 4efca30d2127..e6f3d73db9d3 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -6,7 +6,7 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); } } while (0)
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); printk("\n"); } } while (0)
static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
{
@@ -25,7 +25,7 @@ static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst,
enum dibx000_i2c_interface intf)
{
if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) {
- dprintk("selecting interface: %d\n", intf);
+ dprintk("selecting interface: %d", intf);
mst->selected_interface = intf;
return dibx000_write_word(mst, mst->base_reg + 4, intf);
}
@@ -171,9 +171,18 @@ void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
{
i2c_del_adapter(&mst->gated_tuner_i2c_adap);
}
-
EXPORT_SYMBOL(dibx000_exit_i2c_master);
+
+u32 systime()
+{
+ struct timespec t;
+
+ t = current_kernel_time();
+ return (t.tv_sec * 10000) + (t.tv_nsec / 100000);
+}
+EXPORT_SYMBOL(systime);
+
MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
MODULE_DESCRIPTION("Common function the DiBcom demodulator family");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index 5be10eca07c0..4f5d141a308d 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -36,13 +36,17 @@ extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master
extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst);
+extern u32 systime(void);
+
#define BAND_LBAND 0x01
#define BAND_UHF 0x02
#define BAND_VHF 0x04
#define BAND_SBAND 0x08
-#define BAND_FM 0x10
+#define BAND_FM 0x10
+#define BAND_CBAND 0x20
-#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 115000 ? BAND_FM : \
+#define BAND_OF_FREQUENCY(freq_kHz) ((freq_kHz) <= 170000 ? BAND_CBAND : \
+ (freq_kHz) <= 115000 ? BAND_FM : \
(freq_kHz) <= 250000 ? BAND_VHF : \
(freq_kHz) <= 863000 ? BAND_UHF : \
(freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND )
@@ -149,4 +153,67 @@ enum dibx000_adc_states {
#define OUTMODE_MPEG2_FIFO 5
#define OUTMODE_ANALOG_ADC 6
+enum frontend_tune_state {
+ CT_TUNER_START = 10,
+ CT_TUNER_STEP_0,
+ CT_TUNER_STEP_1,
+ CT_TUNER_STEP_2,
+ CT_TUNER_STEP_3,
+ CT_TUNER_STEP_4,
+ CT_TUNER_STEP_5,
+ CT_TUNER_STEP_6,
+ CT_TUNER_STEP_7,
+ CT_TUNER_STOP,
+
+ CT_AGC_START = 20,
+ CT_AGC_STEP_0,
+ CT_AGC_STEP_1,
+ CT_AGC_STEP_2,
+ CT_AGC_STEP_3,
+ CT_AGC_STEP_4,
+ CT_AGC_STOP,
+
+ CT_DEMOD_START = 30,
+ CT_DEMOD_STEP_1,
+ CT_DEMOD_STEP_2,
+ CT_DEMOD_STEP_3,
+ CT_DEMOD_STEP_4,
+ CT_DEMOD_STEP_5,
+ CT_DEMOD_STEP_6,
+ CT_DEMOD_STEP_7,
+ CT_DEMOD_STEP_8,
+ CT_DEMOD_STEP_9,
+ CT_DEMOD_STEP_10,
+ CT_DEMOD_SEARCH_NEXT = 41,
+ CT_DEMOD_STEP_LOCKED,
+ CT_DEMOD_STOP,
+
+ CT_DONE = 100,
+ CT_SHUTDOWN,
+
+};
+
+struct dvb_frontend_parametersContext {
+#define CHANNEL_STATUS_PARAMETERS_UNKNOWN 0x01
+#define CHANNEL_STATUS_PARAMETERS_SET 0x02
+ u8 status;
+ u32 tune_time_estimation[2];
+ s32 tps_available;
+ u16 tps[9];
+};
+
+#define FE_STATUS_TUNE_FAILED 0
+#define FE_STATUS_TUNE_TIMED_OUT -1
+#define FE_STATUS_TUNE_TIME_TOO_SHORT -2
+#define FE_STATUS_TUNE_PENDING -3
+#define FE_STATUS_STD_SUCCESS -4
+#define FE_STATUS_FFT_SUCCESS -5
+#define FE_STATUS_DEMOD_SUCCESS -6
+#define FE_STATUS_LOCKED -7
+#define FE_STATUS_DATA_LOCKED -8
+
+#define FE_CALLBACK_TIME_NEVER 0xffffffff
+
+#define ABS(x) ((x < 0) ? (-x) : (x))
+
#endif
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c
index eabcadc425d5..dee53960e7e8 100644
--- a/drivers/media/dvb/frontends/lgs8gxx.c
+++ b/drivers/media/dvb/frontends/lgs8gxx.c
@@ -199,7 +199,7 @@ static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
val = freq;
if (freq != 0) {
- val *= (u64)1 << 32;
+ val <<= 32;
if (if_clk != 0)
do_div(val, if_clk);
v32 = val & 0xFFFFFFFF;
@@ -246,7 +246,7 @@ static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
val = v32;
val *= priv->config->if_clk_freq;
- val /= (u64)1 << 32;
+ val >>= 32;
dprintk("AFC = %u kHz\n", (u32)val);
return 0;
}
diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c
index 71f607fe8fc7..b181bf023ada 100644
--- a/drivers/media/dvb/frontends/lnbp21.c
+++ b/drivers/media/dvb/frontends/lnbp21.c
@@ -1,7 +1,7 @@
/*
* lnbp21.c - driver for lnb supply and control ic lnbp21
*
- * Copyright (C) 2006 Oliver Endriss
+ * Copyright (C) 2006, 2009 Oliver Endriss <o.endriss@gmx.de>
* Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
*
* This program is free software; you can redistribute it and/or
@@ -91,6 +91,31 @@ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO;
}
+static int lnbp21_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t tone)
+{
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
+ struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0,
+ .buf = &lnbp21->config,
+ .len = sizeof(lnbp21->config) };
+
+ switch (tone) {
+ case SEC_TONE_OFF:
+ lnbp21->config &= ~LNBP21_TEN;
+ break;
+ case SEC_TONE_ON:
+ lnbp21->config |= LNBP21_TEN;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ lnbp21->config |= lnbp21->override_or;
+ lnbp21->config &= lnbp21->override_and;
+
+ return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
static void lnbp21_release(struct dvb_frontend *fe)
{
/* LNBP power off */
@@ -133,6 +158,7 @@ static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe,
/* override frontend ops */
fe->ops.set_voltage = lnbp21_set_voltage;
fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage;
+ fe->ops.set_tone = lnbp21_set_tone;
printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr);
return fe;
diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c
index df49ea0983bc..8762c86044a5 100644
--- a/drivers/media/dvb/frontends/stv0900_core.c
+++ b/drivers/media/dvb/frontends/stv0900_core.c
@@ -1451,6 +1451,8 @@ static int stv0900_status(struct stv0900_internal *intp,
{
enum fe_stv0900_search_state demod_state;
int locked = FALSE;
+ u8 tsbitrate0_val, tsbitrate1_val;
+ s32 bitrate;
demod_state = stv0900_get_bits(intp, HEADER_MODE);
switch (demod_state) {
@@ -1473,6 +1475,17 @@ static int stv0900_status(struct stv0900_internal *intp,
dprintk("%s: locked = %d\n", __func__, locked);
+ if (stvdebug) {
+ /* Print TS bitrate */
+ tsbitrate0_val = stv0900_read_reg(intp, TSBITRATE0);
+ tsbitrate1_val = stv0900_read_reg(intp, TSBITRATE1);
+ /* Formula Bit rate = Mclk * px_tsfifo_bitrate / 16384 */
+ bitrate = (stv0900_get_mclk_freq(intp, intp->quartz)/1000000)
+ * (tsbitrate1_val << 8 | tsbitrate0_val);
+ bitrate /= 16384;
+ dprintk("TS bitrate = %d Mbit/sec \n", bitrate);
+ };
+
return locked;
}
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 48edd542242e..1573466a5c74 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -3597,7 +3597,8 @@ static int stv090x_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_ma
reg = STV090x_READ_DEMOD(state, DISTXCTL);
- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 2);
+ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD,
+ (state->config->diseqc_envelope_mode) ? 4 : 2);
STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
goto err;
@@ -3649,10 +3650,10 @@ static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t
reg = STV090x_READ_DEMOD(state, DISTXCTL);
if (burst == SEC_MINI_A) {
- mode = 3;
+ mode = (state->config->diseqc_envelope_mode) ? 5 : 3;
value = 0x00;
} else {
- mode = 2;
+ mode = (state->config->diseqc_envelope_mode) ? 4 : 2;
value = 0xFF;
}
diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h
index e968c98bb70f..b133807663ea 100644
--- a/drivers/media/dvb/frontends/stv090x.h
+++ b/drivers/media/dvb/frontends/stv090x.h
@@ -75,6 +75,8 @@ struct stv090x_config {
enum stv090x_i2crpt repeater_level;
+ bool diseqc_envelope_mode;
+
int (*tuner_init) (struct dvb_frontend *fe);
int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode);
int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency);
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
index 266033ae2784..68bf9fbd8fed 100644
--- a/drivers/media/dvb/siano/smsdvb.c
+++ b/drivers/media/dvb/siano/smsdvb.c
@@ -662,7 +662,7 @@ adapter_error:
return rc;
}
-int smsdvb_module_init(void)
+static int __init smsdvb_module_init(void)
{
int rc;
@@ -676,7 +676,7 @@ int smsdvb_module_init(void)
return rc;
}
-void smsdvb_module_exit(void)
+static void __exit smsdvb_module_exit(void)
{
smscore_unregister_hotplug(smsdvb_hotplug);
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
index 24206cbda264..195244a3e69b 100644
--- a/drivers/media/dvb/siano/smssdio.c
+++ b/drivers/media/dvb/siano/smssdio.c
@@ -48,7 +48,7 @@
#define SMSSDIO_INT 0x04
#define SMSSDIO_BLOCK_SIZE 128
-static const struct sdio_device_id smssdio_ids[] = {
+static const struct sdio_device_id smssdio_ids[] __devinitconst = {
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
.driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
@@ -222,7 +222,7 @@ static void smssdio_interrupt(struct sdio_func *func)
smscore_onresponse(smsdev->coredev, cb);
}
-static int smssdio_probe(struct sdio_func *func,
+static int __devinit smssdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
int ret;
@@ -338,7 +338,7 @@ static struct sdio_driver smssdio_driver = {
/* Module functions */
/*******************************************************************/
-int smssdio_module_init(void)
+static int __init smssdio_module_init(void)
{
int ret = 0;
@@ -350,7 +350,7 @@ int smssdio_module_init(void)
return ret;
}
-void smssdio_module_exit(void)
+static void __exit smssdio_module_exit(void)
{
sdio_unregister_driver(&smssdio_driver);
}
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
index 8f88a586b0dd..5eac27287d9c 100644
--- a/drivers/media/dvb/siano/smsusb.c
+++ b/drivers/media/dvb/siano/smsusb.c
@@ -390,7 +390,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id)
return rc;
}
-static int smsusb_probe(struct usb_interface *intf,
+static int __devinit smsusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
@@ -484,7 +484,7 @@ static int smsusb_resume(struct usb_interface *intf)
return 0;
}
-struct usb_device_id smsusb_id_table[] = {
+static const struct usb_device_id smsusb_id_table[] __devinitconst = {
{ USB_DEVICE(0x187f, 0x0010),
.driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
{ USB_DEVICE(0x187f, 0x0100),
@@ -533,8 +533,18 @@ struct usb_device_id smsusb_id_table[] = {
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0xb910),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xb980),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xb990),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0xc000),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc010),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc080),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc090),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ } /* Terminating entry */
};
@@ -550,7 +560,7 @@ static struct usb_driver smsusb_driver = {
.resume = smsusb_resume,
};
-int smsusb_module_init(void)
+static int __init smsusb_module_init(void)
{
int rc = usb_register(&smsusb_driver);
if (rc)
@@ -561,7 +571,7 @@ int smsusb_module_init(void)
return rc;
}
-void smsusb_module_exit(void)
+static void __exit smsusb_module_exit(void)
{
/* Regular USB Cleanup */
usb_deregister(&smsusb_driver);
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 7d193ebc0aea..9782e0593733 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -190,12 +190,13 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
struct saa7146_dev *saa = budget_ci->budget.dev;
struct input_dev *input_dev = budget_ci->ir.dev;
int error;
+ struct ir_scancode_table *ir_codes;
+
budget_ci->ir.dev = input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
- error = -ENOMEM;
- goto out1;
+ return -ENOMEM;
}
snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name),
@@ -217,6 +218,11 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
}
input_dev->dev.parent = &saa->pci->dev;
+ if (rc5_device < 0)
+ budget_ci->ir.rc5_device = IR_DEVICE_ANY;
+ else
+ budget_ci->ir.rc5_device = rc5_device;
+
/* Select keymap and address */
switch (budget_ci->budget.dev->pci->subsystem_device) {
case 0x100c:
@@ -224,53 +230,34 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
case 0x1011:
case 0x1012:
/* The hauppauge keymap is a superset of these remotes */
- error = ir_input_init(input_dev, &budget_ci->ir.state,
- IR_TYPE_RC5, &ir_codes_hauppauge_new_table);
- if (error < 0)
- goto out2;
+ ir_codes = &ir_codes_hauppauge_new_table;
if (rc5_device < 0)
budget_ci->ir.rc5_device = 0x1f;
- else
- budget_ci->ir.rc5_device = rc5_device;
break;
case 0x1010:
case 0x1017:
case 0x101a:
/* for the Technotrend 1500 bundled remote */
- error = ir_input_init(input_dev, &budget_ci->ir.state,
- IR_TYPE_RC5, &ir_codes_tt_1500_table);
- if (error < 0)
- goto out2;
-
- if (rc5_device < 0)
- budget_ci->ir.rc5_device = IR_DEVICE_ANY;
- else
- budget_ci->ir.rc5_device = rc5_device;
+ ir_codes = &ir_codes_tt_1500_table;
break;
default:
/* unknown remote */
- error = ir_input_init(input_dev, &budget_ci->ir.state,
- IR_TYPE_RC5, &ir_codes_budget_ci_old_table);
- if (error < 0)
- goto out2;
-
- if (rc5_device < 0)
- budget_ci->ir.rc5_device = IR_DEVICE_ANY;
- else
- budget_ci->ir.rc5_device = rc5_device;
+ ir_codes = &ir_codes_budget_ci_old_table;
break;
}
+ ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5);
+
/* initialise the key-up timeout handler */
init_timer(&budget_ci->ir.timer_keyup);
budget_ci->ir.timer_keyup.function = msp430_ir_keyup;
budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir;
budget_ci->ir.last_raw = 0xffff; /* An impossible value */
- error = input_register_device(input_dev);
+ error = ir_input_register(input_dev, ir_codes);
if (error) {
printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
- goto out2;
+ return error;
}
/* note: these must be after input_register_device */
@@ -284,12 +271,6 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
return 0;
-
-out2:
- ir_input_free(input_dev);
- input_free_device(input_dev);
-out1:
- return error;
}
static void msp430_ir_deinit(struct budget_ci *budget_ci)
@@ -304,8 +285,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
del_timer_sync(&dev->timer);
ir_input_nokey(dev, &budget_ci->ir.state);
- ir_input_free(dev);
- input_unregister_device(dev);
+ ir_input_unregister(dev);
}
static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 4c2b8a246772..3f40f375981b 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -215,13 +215,10 @@ config RADIO_MIROPCM20
module will be called radio-miropcm20.
config RADIO_SF16FMI
- tristate "SF16FMI Radio"
+ tristate "SF16-FMI/SF16-FMP Radio"
depends on ISA && VIDEO_V4L2
---help---
- Choose Y here if you have one of these FM radio cards. If you
- compile the driver into the kernel and your card is not PnP one, you
- have to add "sf16fm=<io>" to the kernel command line (I/O address is
- 0x284 or 0x384).
+ Choose Y here if you have one of these FM radio cards.
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 35edee009ba8..5bf4985daede 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -268,6 +268,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct rtrack *rt = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
rt_setfreq(rt, f->frequency);
return 0;
}
@@ -277,6 +279,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct rtrack *rt = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = rt->curfreq;
return 0;
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index 8daf809eb01a..c22311393624 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -254,6 +254,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct aztech *az = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
az_setfreq(az, f->frequency);
return 0;
}
@@ -263,6 +265,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct aztech *az = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = az->curfreq;
return 0;
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
index c6cf11661868..000f4d34087c 100644
--- a/drivers/media/radio/radio-gemtek-pci.c
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -240,6 +240,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct gemtek_pci *card = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
if (f->frequency < GEMTEK_PCI_RANGE_LOW ||
f->frequency > GEMTEK_PCI_RANGE_HIGH)
return -EINVAL;
@@ -253,6 +255,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct gemtek_pci *card = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = card->current_frequency;
return 0;
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
index 64d737c35acf..f8213b7c8ddc 100644
--- a/drivers/media/radio/radio-maestro.c
+++ b/drivers/media/radio/radio-maestro.c
@@ -200,6 +200,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct maestro *dev = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
return -EINVAL;
mutex_lock(&dev->lock);
@@ -213,6 +215,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct maestro *dev = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
mutex_lock(&dev->lock);
f->frequency = BITS2FREQ(radio_bits_get(dev));
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index 3da51fe8fb93..44b4dbedb322 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -262,6 +262,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct maxiradio *dev = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
f->frequency / 16000,
@@ -285,6 +287,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct maxiradio *dev = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = dev->freq;
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index 949f60513d9e..02a9cefc9a00 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -374,6 +374,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct amradio_device *radio = file->private_data;
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
return amradio_setfreq(radio, f->frequency);
}
@@ -383,6 +385,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct amradio_device *radio = file->private_data;
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 9cb193fa6e33..a79296aac9a9 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -167,6 +167,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct rtrack2 *rt = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
rt_setfreq(rt, f->frequency);
return 0;
}
@@ -176,6 +178,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct rtrack2 *rt = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = rt->curfreq;
return 0;
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 49c4aab95dab..985359d18aa5 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -1,4 +1,4 @@
-/* SF16FMI radio driver for Linux radio support
+/* SF16-FMI and SF16-FMP radio driver for Linux radio support
* heavily based on rtrack driver...
* (c) 1997 M. Kirkwood
* (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
@@ -11,7 +11,7 @@
*
* Frequency control is done digitally -- ie out(port,encodefreq(95.8));
* No volume control - only mute/unmute - you have to use line volume
- * control on SB-part of SF16FMI
+ * control on SB-part of SF16-FMI/SF16-FMP
*
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
@@ -30,14 +30,14 @@
#include <media/v4l2-ioctl.h>
MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
-MODULE_DESCRIPTION("A driver for the SF16MI radio.");
+MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio.");
MODULE_LICENSE("GPL");
static int io = -1;
static int radio_nr = -1;
module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
+MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)");
module_param(radio_nr, int, 0);
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
@@ -47,13 +47,14 @@ struct fmi
struct v4l2_device v4l2_dev;
struct video_device vdev;
int io;
- int curvol; /* 1 or 0 */
+ bool mute;
unsigned long curfreq; /* freq in kHz */
struct mutex lock;
};
static struct fmi fmi_card;
static struct pnp_dev *dev;
+bool pnp_attached;
/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
/* It is only useful to give freq in interval of 800 (=0.05Mhz),
@@ -105,7 +106,7 @@ static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
outbits(8, 0xC0, fmi->io);
msleep(143); /* was schedule_timeout(HZ/7) */
mutex_unlock(&fmi->lock);
- if (fmi->curvol)
+ if (!fmi->mute)
fmi_unmute(fmi);
return 0;
}
@@ -116,7 +117,7 @@ static inline int fmi_getsigstr(struct fmi *fmi)
int res;
mutex_lock(&fmi->lock);
- val = fmi->curvol ? 0x08 : 0x00; /* unmute/mute */
+ val = fmi->mute ? 0x00 : 0x08; /* mute/unmute */
outb(val, fmi->io);
outb(val | 0x10, fmi->io);
msleep(143); /* was schedule_timeout(HZ/7) */
@@ -168,6 +169,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct fmi *fmi = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
if (f->frequency < RSF16_MINFREQ ||
f->frequency > RSF16_MAXFREQ)
return -EINVAL;
@@ -182,6 +185,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct fmi *fmi = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = fmi->curfreq;
return 0;
@@ -204,7 +209,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- ctrl->value = fmi->curvol;
+ ctrl->value = fmi->mute;
return 0;
}
return -EINVAL;
@@ -221,7 +226,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
fmi_mute(fmi);
else
fmi_unmute(fmi);
- fmi->curvol = ctrl->value;
+ fmi->mute = ctrl->value;
return 0;
}
return -EINVAL;
@@ -316,26 +321,54 @@ static int __init fmi_init(void)
{
struct fmi *fmi = &fmi_card;
struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
- int res;
+ int res, i;
+ int probe_ports[] = { 0, 0x284, 0x384 };
+
+ if (io < 0) {
+ for (i = 0; i < ARRAY_SIZE(probe_ports); i++) {
+ io = probe_ports[i];
+ if (io == 0) {
+ io = isapnp_fmi_probe();
+ if (io < 0)
+ continue;
+ pnp_attached = 1;
+ }
+ if (!request_region(io, 2, "radio-sf16fmi")) {
+ if (pnp_attached)
+ pnp_device_detach(dev);
+ io = -1;
+ continue;
+ }
+ if (pnp_attached ||
+ ((inb(io) & 0xf9) == 0xf9 && (inb(io) & 0x4) == 0))
+ break;
+ release_region(io, 2);
+ io = -1;
+ }
+ } else {
+ if (!request_region(io, 2, "radio-sf16fmi")) {
+ printk(KERN_ERR "radio-sf16fmi: port %#x already in use\n", io);
+ return -EBUSY;
+ }
+ if (inb(io) == 0xff) {
+ printk(KERN_ERR "radio-sf16fmi: card not present at %#x\n", io);
+ release_region(io, 2);
+ return -ENODEV;
+ }
+ }
+ if (io < 0) {
+ printk(KERN_ERR "radio-sf16fmi: no cards found\n");
+ return -ENODEV;
+ }
- if (io < 0)
- io = isapnp_fmi_probe();
strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name));
fmi->io = io;
- if (fmi->io < 0) {
- v4l2_err(v4l2_dev, "No PnP card found.\n");
- return fmi->io;
- }
- if (!request_region(io, 2, "radio-sf16fmi")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", fmi->io);
- pnp_device_detach(dev);
- return -EBUSY;
- }
res = v4l2_device_register(NULL, v4l2_dev);
if (res < 0) {
release_region(fmi->io, 2);
- pnp_device_detach(dev);
+ if (pnp_attached)
+ pnp_device_detach(dev);
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
return res;
}
@@ -352,7 +385,8 @@ static int __init fmi_init(void)
if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
v4l2_device_unregister(v4l2_dev);
release_region(fmi->io, 2);
- pnp_device_detach(dev);
+ if (pnp_attached)
+ pnp_device_detach(dev);
return -EINVAL;
}
@@ -369,7 +403,7 @@ static void __exit fmi_exit(void)
video_unregister_device(&fmi->vdev);
v4l2_device_unregister(&fmi->v4l2_dev);
release_region(fmi->io, 2);
- if (dev)
+ if (dev && pnp_attached)
pnp_device_detach(dev);
}
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index a11414f648d4..52c7bbb32b8b 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -251,6 +251,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct fmr2 *fmr2 = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
if (f->frequency < RSF16_MINFREQ ||
f->frequency > RSF16_MAXFREQ)
return -EINVAL;
@@ -272,6 +274,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct fmr2 *fmr2 = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = fmr2->curfreq;
return 0;
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 3cd76dddb6aa..8e718bfcdad3 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -314,7 +314,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
if (v->index > 0)
return -EINVAL;
- memset(v, 0, sizeof(v));
+ memset(v, 0, sizeof(*v));
strcpy(v->name, "FM");
v->type = V4L2_TUNER_RADIO;
tea5764_i2c_read(radio);
@@ -349,7 +349,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct tea5764_device *radio = video_drvdata(file);
- if (f->tuner != 0)
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
if (f->frequency == 0) {
/* We special case this as a power down control. */
@@ -370,8 +370,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct tea5764_device *radio = video_drvdata(file);
struct tea5764_regs *r = &radio->regs;
+ if (f->tuner != 0)
+ return -EINVAL;
tea5764_i2c_read(radio);
- memset(f, 0, sizeof(f));
+ memset(f, 0, sizeof(*f));
f->type = V4L2_TUNER_RADIO;
if (r->tnctrl & TEA5764_TNCTRL_PUPD0)
f->frequency = (tea5764_get_freq(radio) * 2) / 125;
@@ -458,12 +460,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
static int tea5764_open(struct file *file)
{
/* Currently we support only one device */
- int minor = video_devdata(file)->minor;
struct tea5764_device *radio = video_drvdata(file);
- if (radio->videodev->minor != minor)
- return -ENODEV;
-
mutex_lock(&radio->mutex);
/* Only exclusive access */
if (radio->users) {
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index 699db9acaaf7..fc1c860fd438 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -240,6 +240,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct terratec *tt = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
tt_setfreq(tt, f->frequency);
return 0;
}
@@ -249,6 +251,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct terratec *tt = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = tt->curfreq;
return 0;
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 6f9ecc359356..9d6dcf8af5b0 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -239,6 +239,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct trust *tr = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
tr_setfreq(tr, f->frequency);
return 0;
}
@@ -248,6 +250,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct trust *tr = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = tr->curfreq;
return 0;
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 3a98f1399495..03439282dfce 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -207,6 +207,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct typhoon *dev = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = dev->curfreq;
return 0;
@@ -217,6 +219,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct typhoon *dev = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
dev->curfreq = f->frequency;
typhoon_setfreq(dev, dev->curfreq);
return 0;
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index 80e98b6422fe..f31eab99c943 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -266,6 +266,8 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct zoltrix *zol = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
if (zol_setfreq(zol, f->frequency) != 0)
return -EINVAL;
return 0;
@@ -276,6 +278,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct zoltrix *zol = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = zol->curfreq;
return 0;
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index f33315f2c543..4da0f150c6e2 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -426,6 +426,104 @@ int si470x_rds_on(struct si470x_device *radio)
/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_read - read RDS data
+ */
+static ssize_t si470x_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+ unsigned int block_count = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
+
+ /* block if no new data available */
+ while (radio->wr_index == radio->rd_index) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
+ if (wait_event_interruptible(radio->read_queue,
+ radio->wr_index != radio->rd_index) < 0) {
+ retval = -EINTR;
+ goto done;
+ }
+ }
+
+ /* calculate block count from byte count */
+ count /= 3;
+
+ /* copy RDS block out of internal buffer and to user buffer */
+ mutex_lock(&radio->lock);
+ while (block_count < count) {
+ if (radio->rd_index == radio->wr_index)
+ break;
+
+ /* always transfer rds complete blocks */
+ if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
+ /* retval = -EFAULT; */
+ break;
+
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+
+ /* increment counters */
+ block_count++;
+ buf += 3;
+ retval += 3;
+ }
+ mutex_unlock(&radio->lock);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_fops_poll - poll RDS data
+ */
+static unsigned int si470x_fops_poll(struct file *file,
+ struct poll_table_struct *pts)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
+
+ poll_wait(file, &radio->read_queue, pts);
+
+ if (radio->rd_index != radio->wr_index)
+ retval = POLLIN | POLLRDNORM;
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+static const struct v4l2_file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .read = si470x_fops_read,
+ .poll = si470x_fops_poll,
+ .ioctl = video_ioctl2,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
* Video4Linux Interface
**************************************************************************/
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 2d53b6a9409b..5466015346a1 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -22,22 +22,17 @@
*/
-/*
- * ToDo:
- * - RDS support
- */
-
-
/* driver definitions */
#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0)
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 1)
#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.0"
+#define DRIVER_VERSION "1.0.1"
/* kernel includes */
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include "radio-si470x.h"
@@ -62,6 +57,20 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0444);
MODULE_PARM_DESC(radio_nr, "Radio Nr");
+/* RDS buffer blocks */
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0444);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+/* RDS maximum block errors */
+static unsigned short max_rds_errors = 1;
+/* 0 means 0 errors requiring correction */
+/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
+/* 2 means 3-5 errors requiring correction */
+/* 3 means 6+ errors or errors in checkword, correction not possible */
+module_param(max_rds_errors, ushort, 0644);
+MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
+
/**************************************************************************
@@ -173,7 +182,7 @@ int si470x_disconnect_check(struct si470x_device *radio)
/*
* si470x_fops_open - file open
*/
-static int si470x_fops_open(struct file *file)
+int si470x_fops_open(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
int retval = 0;
@@ -181,12 +190,21 @@ static int si470x_fops_open(struct file *file)
mutex_lock(&radio->lock);
radio->users++;
- if (radio->users == 1)
+ if (radio->users == 1) {
/* start radio */
retval = si470x_start(radio);
+ if (retval < 0)
+ goto done;
+
+ /* enable RDS interrupt */
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN;
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
+ radio->registers[SYSCONFIG1] |= 0x1 << 2;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ }
+done:
mutex_unlock(&radio->lock);
-
return retval;
}
@@ -194,7 +212,7 @@ static int si470x_fops_open(struct file *file)
/*
* si470x_fops_release - file release
*/
-static int si470x_fops_release(struct file *file)
+int si470x_fops_release(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
int retval = 0;
@@ -215,17 +233,6 @@ static int si470x_fops_release(struct file *file)
}
-/*
- * si470x_fops - file operations interface
- */
-const struct v4l2_file_operations si470x_fops = {
- .owner = THIS_MODULE,
- .ioctl = video_ioctl2,
- .open = si470x_fops_open,
- .release = si470x_fops_release,
-};
-
-
/**************************************************************************
* Video4Linux Interface
@@ -253,6 +260,105 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
**************************************************************************/
/*
+ * si470x_i2c_interrupt_work - rds processing function
+ */
+static void si470x_i2c_interrupt_work(struct work_struct *work)
+{
+ struct si470x_device *radio = container_of(work,
+ struct si470x_device, radio_work);
+ unsigned char regnr;
+ unsigned char blocknum;
+ unsigned short bler; /* rds block errors */
+ unsigned short rds;
+ unsigned char tmpbuf[3];
+ int retval = 0;
+
+ /* safety checks */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ return;
+
+ /* Update RDS registers */
+ for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) {
+ retval = si470x_get_register(radio, STATUSRSSI + regnr);
+ if (retval < 0)
+ return;
+ }
+
+ /* get rds blocks */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0)
+ /* No RDS group ready, better luck next time */
+ return;
+
+ for (blocknum = 0; blocknum < 4; blocknum++) {
+ switch (blocknum) {
+ default:
+ bler = (radio->registers[STATUSRSSI] &
+ STATUSRSSI_BLERA) >> 9;
+ rds = radio->registers[RDSA];
+ break;
+ case 1:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERB) >> 14;
+ rds = radio->registers[RDSB];
+ break;
+ case 2:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERC) >> 12;
+ rds = radio->registers[RDSC];
+ break;
+ case 3:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERD) >> 10;
+ rds = radio->registers[RDSD];
+ break;
+ };
+
+ /* Fill the V4L2 RDS buffer */
+ put_unaligned_le16(rds, &tmpbuf);
+ tmpbuf[2] = blocknum; /* offset name */
+ tmpbuf[2] |= blocknum << 3; /* received offset */
+ if (bler > max_rds_errors)
+ tmpbuf[2] |= 0x80; /* uncorrectable errors */
+ else if (bler > 0)
+ tmpbuf[2] |= 0x40; /* corrected error(s) */
+
+ /* copy RDS block to internal buffer */
+ memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
+ radio->wr_index += 3;
+
+ /* wrap write pointer */
+ if (radio->wr_index >= radio->buf_size)
+ radio->wr_index = 0;
+
+ /* check for overflow */
+ if (radio->wr_index == radio->rd_index) {
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+ }
+ }
+
+ if (radio->wr_index != radio->rd_index)
+ wake_up_interruptible(&radio->read_queue);
+}
+
+
+/*
+ * si470x_i2c_interrupt - interrupt handler
+ */
+static irqreturn_t si470x_i2c_interrupt(int irq, void *dev_id)
+{
+ struct si470x_device *radio = dev_id;
+
+ if (!work_pending(&radio->radio_work))
+ schedule_work(&radio->radio_work);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
* si470x_i2c_probe - probe for the device
*/
static int __devinit si470x_i2c_probe(struct i2c_client *client,
@@ -268,6 +374,8 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
retval = -ENOMEM;
goto err_initial;
}
+
+ INIT_WORK(&radio->radio_work, si470x_i2c_interrupt_work);
radio->users = 0;
radio->client = client;
mutex_init(&radio->lock);
@@ -319,6 +427,26 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
/* set initial frequency */
si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+ /* rds buffer allocation */
+ radio->buf_size = rds_buf * 3;
+ radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ if (!radio->buffer) {
+ retval = -EIO;
+ goto err_video;
+ }
+
+ /* rds buffer configuration */
+ radio->wr_index = 0;
+ radio->rd_index = 0;
+ init_waitqueue_head(&radio->read_queue);
+
+ retval = request_irq(client->irq, si470x_i2c_interrupt,
+ IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
+ if (retval) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_rds;
+ }
+
/* register video device */
retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
radio_nr);
@@ -330,6 +458,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
return 0;
err_all:
+ free_irq(client->irq, radio);
+err_rds:
+ kfree(radio->buffer);
err_video:
video_device_release(radio->videodev);
err_radio:
@@ -346,6 +477,8 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client)
{
struct si470x_device *radio = i2c_get_clientdata(client);
+ free_irq(client->irq, radio);
+ cancel_work_sync(&radio->radio_work);
video_unregister_device(radio->videodev);
kfree(radio);
i2c_set_clientdata(client, NULL);
@@ -354,6 +487,44 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client)
}
+#ifdef CONFIG_PM
+/*
+ * si470x_i2c_suspend - suspend the device
+ */
+static int si470x_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct si470x_device *radio = i2c_get_clientdata(client);
+
+ /* power down */
+ radio->registers[POWERCFG] |= POWERCFG_DISABLE;
+ if (si470x_set_register(radio, POWERCFG) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+
+/*
+ * si470x_i2c_resume - resume the device
+ */
+static int si470x_i2c_resume(struct i2c_client *client)
+{
+ struct si470x_device *radio = i2c_get_clientdata(client);
+
+ /* power up : need 110ms */
+ radio->registers[POWERCFG] |= POWERCFG_ENABLE;
+ if (si470x_set_register(radio, POWERCFG) < 0)
+ return -EIO;
+ msleep(110);
+
+ return 0;
+}
+#else
+#define si470x_i2c_suspend NULL
+#define si470x_i2c_resume NULL
+#endif
+
+
/*
* si470x_i2c_driver - i2c driver interface
*/
@@ -364,6 +535,8 @@ static struct i2c_driver si470x_i2c_driver = {
},
.probe = si470x_i2c_probe,
.remove = __devexit_p(si470x_i2c_remove),
+ .suspend = si470x_i2c_suspend,
+ .resume = si470x_i2c_resume,
.id_table = si470x_i2c_id,
};
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index f2d0e1ddb301..a96e1b9dd646 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -509,89 +509,9 @@ resubmit:
**************************************************************************/
/*
- * si470x_fops_read - read RDS data
- */
-static ssize_t si470x_fops_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
- unsigned int block_count = 0;
-
- /* switch on rds reception */
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
- si470x_rds_on(radio);
-
- /* block if no new data available */
- while (radio->wr_index == radio->rd_index) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EWOULDBLOCK;
- goto done;
- }
- if (wait_event_interruptible(radio->read_queue,
- radio->wr_index != radio->rd_index) < 0) {
- retval = -EINTR;
- goto done;
- }
- }
-
- /* calculate block count from byte count */
- count /= 3;
-
- /* copy RDS block out of internal buffer and to user buffer */
- mutex_lock(&radio->lock);
- while (block_count < count) {
- if (radio->rd_index == radio->wr_index)
- break;
-
- /* always transfer rds complete blocks */
- if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
- /* retval = -EFAULT; */
- break;
-
- /* increment and wrap read pointer */
- radio->rd_index += 3;
- if (radio->rd_index >= radio->buf_size)
- radio->rd_index = 0;
-
- /* increment counters */
- block_count++;
- buf += 3;
- retval += 3;
- }
- mutex_unlock(&radio->lock);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_fops_poll - poll RDS data
- */
-static unsigned int si470x_fops_poll(struct file *file,
- struct poll_table_struct *pts)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* switch on rds reception */
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
- si470x_rds_on(radio);
-
- poll_wait(file, &radio->read_queue, pts);
-
- if (radio->rd_index != radio->wr_index)
- retval = POLLIN | POLLRDNORM;
-
- return retval;
-}
-
-
-/*
* si470x_fops_open - file open
*/
-static int si470x_fops_open(struct file *file)
+int si470x_fops_open(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
int retval;
@@ -645,7 +565,7 @@ done:
/*
* si470x_fops_release - file release
*/
-static int si470x_fops_release(struct file *file)
+int si470x_fops_release(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
int retval = 0;
@@ -688,19 +608,6 @@ done:
}
-/*
- * si470x_fops - file operations interface
- */
-const struct v4l2_file_operations si470x_fops = {
- .owner = THIS_MODULE,
- .read = si470x_fops_read,
- .poll = si470x_fops_poll,
- .ioctl = video_ioctl2,
- .open = si470x_fops_open,
- .release = si470x_fops_release,
-};
-
-
/**************************************************************************
* Video4Linux Interface
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index d0af194d194c..3cd0a29cd6e7 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -29,6 +29,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/input.h>
@@ -181,6 +182,7 @@ struct si470x_device {
#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
struct i2c_client *client;
+ struct work_struct radio_work;
#endif
};
@@ -212,7 +214,6 @@ struct si470x_device {
/**************************************************************************
* Common Functions
**************************************************************************/
-extern const struct v4l2_file_operations si470x_fops;
extern struct video_device si470x_viddev_template;
int si470x_get_register(struct si470x_device *radio, int regnr);
int si470x_set_register(struct si470x_device *radio, int regnr);
@@ -221,5 +222,7 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
int si470x_start(struct si470x_device *radio);
int si470x_stop(struct si470x_device *radio);
int si470x_rds_on(struct si470x_device *radio);
+int si470x_fops_open(struct file *file);
+int si470x_fops_release(struct file *file);
int si470x_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *capability);
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 9dc74c93bf24..2f83be766d9f 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -37,10 +37,6 @@ config VIDEO_BTCX
depends on PCI
tristate
-config VIDEO_IR
- tristate
- depends on INPUT
-
config VIDEO_TVEEPROM
tristate
depends on I2C
@@ -840,6 +836,12 @@ config SOC_CAMERA_MT9T031
help
This driver supports MT9T031 cameras from Micron.
+config SOC_CAMERA_MT9T112
+ tristate "mt9t112 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9T112 cameras from Aptina.
+
config SOC_CAMERA_MT9V022
tristate "mt9v022 support"
depends on SOC_CAMERA && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 7a2dcc34111c..2af68ee84122 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
+obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
@@ -149,7 +150,7 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
-obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
+obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
# soc-camera host drivers have to be linked after camera drivers
obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index d137bac84511..a356d6bd3131 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -767,7 +767,6 @@ static struct video_device ar_template = {
.name = "Colour AR VGA",
.fops = &ar_fops,
.release = ar_release,
- .minor = -1,
};
#define ALIGN4(x) ((((int)(x)) & 0x3) == 0)
@@ -860,8 +859,8 @@ static int __init ar_init(void)
goto out_dev;
}
- printk("video%d: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
- ar->vdev->num, M32R_IRQ_INT3, freq);
+ printk("%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
+ video_device_node_name(ar->vdev), M32R_IRQ_INT3, freq);
return 0;
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index 1485aee18d58..dc67bc40f36f 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -40,7 +40,6 @@
#include "au0828.h"
#include "au0828-reg.h"
-static LIST_HEAD(au0828_devlist);
static DEFINE_MUTEX(au0828_sysfs_lock);
#define AU0828_VERSION_CODE KERNEL_VERSION(0, 0, 1)
@@ -693,10 +692,8 @@ void au0828_analog_unregister(struct au0828_dev *dev)
dprintk(1, "au0828_release_resources called\n");
mutex_lock(&au0828_sysfs_lock);
- if (dev->vdev) {
- list_del(&dev->au0828list);
+ if (dev->vdev)
video_unregister_device(dev->vdev);
- }
if (dev->vbi_dev)
video_unregister_device(dev->vbi_dev);
@@ -737,29 +734,15 @@ static void res_free(struct au0828_fh *fh)
static int au0828_v4l2_open(struct file *filp)
{
- int minor = video_devdata(filp)->minor;
int ret = 0;
- struct au0828_dev *h, *dev = NULL;
+ struct au0828_dev *dev = video_drvdata(filp);
struct au0828_fh *fh;
- int type = 0;
- struct list_head *list;
-
- list_for_each(list, &au0828_devlist) {
- h = list_entry(list, struct au0828_dev, au0828list);
- if (h->vdev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
+ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
#ifdef VBI_IS_WORKING
- if (h->vbi_dev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
+ if (video_devdata(filp)->vfl_type == VFL_TYPE_GRABBER)
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
#endif
- }
-
- if (NULL == dev)
- return -ENODEV;
fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
if (NULL == fh) {
@@ -1587,7 +1570,6 @@ static const struct video_device au0828_video_template = {
.fops = &au0828_v4l_fops,
.release = video_device_release,
.ioctl_ops = &video_ioctl_ops,
- .minor = -1,
.tvnorms = V4L2_STD_NTSC_M,
.current_norm = V4L2_STD_NTSC_M,
};
@@ -1676,25 +1658,23 @@ int au0828_analog_register(struct au0828_dev *dev,
strcpy(dev->vbi_dev->name, "au0828a vbi");
#endif
- list_add_tail(&dev->au0828list, &au0828_devlist);
-
/* Register the v4l2 device */
+ video_set_drvdata(dev->vdev, dev);
retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
if (retval != 0) {
dprintk(1, "unable to register video device (error = %d).\n",
retval);
- list_del(&dev->au0828list);
video_device_release(dev->vdev);
return -ENODEV;
}
#ifdef VBI_IS_WORKING
/* Register the vbi device */
+ video_set_drvdata(dev->vbi_dev, dev);
retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
if (retval != 0) {
dprintk(1, "unable to register vbi device (error = %d).\n",
retval);
- list_del(&dev->au0828list);
video_device_release(dev->vbi_dev);
video_device_release(dev->vdev);
return -ENODEV;
diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h
index b977915efbd0..207f32dec6a6 100644
--- a/drivers/media/video/au0828/au0828.h
+++ b/drivers/media/video/au0828/au0828.h
@@ -192,7 +192,6 @@ struct au0828_dev {
struct au0828_dvb dvb;
/* Analog */
- struct list_head au0828list;
struct v4l2_device v4l2_dev;
int users;
unsigned int stream_on:1; /* Locks streams */
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index a6724019c66f..3182a406bdd1 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -3206,24 +3206,24 @@ err:
static int bttv_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
+ struct video_device *vdev = video_devdata(file);
struct bttv *btv = video_drvdata(file);
struct bttv_fh *fh;
enum v4l2_buf_type type = 0;
- dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
+ dprintk(KERN_DEBUG "bttv: open dev=%s\n", video_device_node_name(vdev));
- lock_kernel();
- if (btv->video_dev->minor == minor) {
+ if (vdev->vfl_type == VFL_TYPE_GRABBER) {
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- } else if (btv->vbi_dev->minor == minor) {
+ } else if (vdev->vfl_type == VFL_TYPE_VBI) {
type = V4L2_BUF_TYPE_VBI_CAPTURE;
} else {
WARN_ON(1);
- unlock_kernel();
return -ENODEV;
}
+ lock_kernel();
+
dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
btv->c.nr,v4l2_type_names[type]);
@@ -3397,7 +3397,6 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
static struct video_device bttv_video_template = {
.fops = &bttv_fops,
- .minor = -1,
.ioctl_ops = &bttv_ioctl_ops,
.tvnorms = BTTV_NORMS,
.current_norm = V4L2_STD_PAL,
@@ -3408,18 +3407,13 @@ static struct video_device bttv_video_template = {
static int radio_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
+ struct video_device *vdev = video_devdata(file);
struct bttv *btv = video_drvdata(file);
struct bttv_fh *fh;
- dprintk("bttv: open minor=%d\n",minor);
+ dprintk("bttv: open dev=%s\n", video_device_node_name(vdev));
lock_kernel();
- WARN_ON(btv->radio_dev && btv->radio_dev->minor != minor);
- if (!btv->radio_dev || btv->radio_dev->minor != minor) {
- unlock_kernel();
- return -ENODEV;
- }
dprintk("bttv%d: open called (radio)\n",btv->c.nr);
@@ -3640,7 +3634,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
static struct video_device radio_template = {
.fops = &radio_fops,
- .minor = -1,
.ioctl_ops = &radio_ioctl_ops,
};
@@ -4208,21 +4201,21 @@ static struct video_device *vdev_init(struct bttv *btv,
static void bttv_unregister_video(struct bttv *btv)
{
if (btv->video_dev) {
- if (-1 != btv->video_dev->minor)
+ if (video_is_registered(btv->video_dev))
video_unregister_device(btv->video_dev);
else
video_device_release(btv->video_dev);
btv->video_dev = NULL;
}
if (btv->vbi_dev) {
- if (-1 != btv->vbi_dev->minor)
+ if (video_is_registered(btv->vbi_dev))
video_unregister_device(btv->vbi_dev);
else
video_device_release(btv->vbi_dev);
btv->vbi_dev = NULL;
}
if (btv->radio_dev) {
- if (-1 != btv->radio_dev->minor)
+ if (video_is_registered(btv->radio_dev))
video_unregister_device(btv->radio_dev);
else
video_device_release(btv->radio_dev);
@@ -4244,8 +4237,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
video_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device video%d\n",
- btv->c.nr, btv->video_dev->num);
+ printk(KERN_INFO "bttv%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->video_dev));
if (device_create_file(&btv->video_dev->dev,
&dev_attr_card)<0) {
printk(KERN_ERR "bttv%d: device_create_file 'card' "
@@ -4261,8 +4254,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
vbi_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device vbi%d\n",
- btv->c.nr, btv->vbi_dev->num);
+ printk(KERN_INFO "bttv%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->vbi_dev));
if (!btv->has_radio)
return 0;
@@ -4273,8 +4266,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
radio_nr[btv->c.nr]) < 0)
goto err;
- printk(KERN_INFO "bttv%d: registered device radio%d\n",
- btv->c.nr, btv->radio_dev->num);
+ printk(KERN_INFO "bttv%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->radio_dev));
/* all done */
return 0;
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index beda363418b0..63aa31a041e8 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -40,7 +40,7 @@ static int i2c_debug;
static int i2c_hw;
static int i2c_scan;
module_param(i2c_debug, int, 0644);
-MODULE_PARM_DESC(i2c_hw,"configure i2c debug level");
+MODULE_PARM_DESC(i2c_debug, "configure i2c debug level");
module_param(i2c_hw, int, 0444);
MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, "
"instead of software bitbang");
@@ -400,7 +400,7 @@ int __devinit init_bttv_i2c(struct bttv *btv)
That's why we probe 0x1a (~0x34) first. CB
*/
const unsigned short addr_list[] = {
- 0x1a, 0x18, 0x4b, 0x64, 0x30,
+ 0x1a, 0x18, 0x4b, 0x64, 0x30, 0x71,
I2C_CLIENT_END
};
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index 84a957e52c4b..277a092e1214 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -368,7 +368,7 @@ int bttv_input_init(struct bttv *btv)
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
pci_name(btv->c.pci));
- err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type);
if (err < 0)
goto err_out_free;
@@ -389,7 +389,7 @@ int bttv_input_init(struct bttv *btv)
bttv_ir_start(btv, ir);
/* all done */
- err = input_register_device(btv->remote->dev);
+ err = ir_input_register(btv->remote->dev, ir_codes);
if (err)
goto err_out_stop;
@@ -403,8 +403,6 @@ int bttv_input_init(struct bttv *btv)
bttv_ir_stop(btv);
btv->remote = NULL;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return err;
}
@@ -415,8 +413,7 @@ void bttv_input_fini(struct bttv *btv)
return;
bttv_ir_stop(btv);
- ir_input_free(btv->remote->dev);
- input_unregister_device(btv->remote->dev);
+ ir_input_unregister(btv->remote->dev);
kfree(btv->remote);
btv->remote = NULL;
}
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index 85cf1778827a..e2cbebab959b 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -809,8 +809,8 @@ static int init_cqcam(struct parport *port)
return -ENODEV;
}
- printk(KERN_INFO "video%d: Colour QuickCam found on %s\n",
- qcam->vdev.num, qcam->pport->name);
+ printk(KERN_INFO "%s: Colour QuickCam found on %s\n",
+ video_device_node_name(&qcam->vdev), qcam->pport->name);
qcams[num_cams++] = qcam;
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 10230cb3d210..7bb9c1ec7819 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -1723,7 +1723,6 @@ static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = {
static struct video_device cafe_v4l_template = {
.name = "cafe",
- .minor = -1, /* Get one dynamically */
.tvnorms = V4L2_STD_NTSC_M,
.current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
index 2377313c041a..551ddf216a4b 100644
--- a/drivers/media/video/cpia.c
+++ b/drivers/media/video/cpia.c
@@ -32,6 +32,7 @@
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
@@ -244,72 +245,67 @@ static void rvfree(void *mem, unsigned long size)
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *cpia_proc_root=NULL;
-static int cpia_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int cpia_proc_show(struct seq_file *m, void *v)
{
- char *out = page;
- int len, tmp;
- struct cam_data *cam = data;
+ struct cam_data *cam = m->private;
+ int tmp;
char tmpstr[29];
- /* IMPORTANT: This output MUST be kept under PAGE_SIZE
- * or we need to get more sophisticated. */
-
- out += sprintf(out, "read-only\n-----------------------\n");
- out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
+ seq_printf(m, "read-only\n-----------------------\n");
+ seq_printf(m, "V4L Driver version: %d.%d.%d\n",
CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
- out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
+ seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n",
cam->params.version.firmwareVersion,
cam->params.version.firmwareRevision,
cam->params.version.vcVersion,
cam->params.version.vcRevision);
- out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
+ seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n",
cam->params.pnpID.vendor, cam->params.pnpID.product,
cam->params.pnpID.deviceRevision);
- out += sprintf(out, "VP-Version: %d.%d %04x\n",
+ seq_printf(m, "VP-Version: %d.%d %04x\n",
cam->params.vpVersion.vpVersion,
cam->params.vpVersion.vpRevision,
cam->params.vpVersion.cameraHeadID);
- out += sprintf(out, "system_state: %#04x\n",
+ seq_printf(m, "system_state: %#04x\n",
cam->params.status.systemState);
- out += sprintf(out, "grab_state: %#04x\n",
+ seq_printf(m, "grab_state: %#04x\n",
cam->params.status.grabState);
- out += sprintf(out, "stream_state: %#04x\n",
+ seq_printf(m, "stream_state: %#04x\n",
cam->params.status.streamState);
- out += sprintf(out, "fatal_error: %#04x\n",
+ seq_printf(m, "fatal_error: %#04x\n",
cam->params.status.fatalError);
- out += sprintf(out, "cmd_error: %#04x\n",
+ seq_printf(m, "cmd_error: %#04x\n",
cam->params.status.cmdError);
- out += sprintf(out, "debug_flags: %#04x\n",
+ seq_printf(m, "debug_flags: %#04x\n",
cam->params.status.debugFlags);
- out += sprintf(out, "vp_status: %#04x\n",
+ seq_printf(m, "vp_status: %#04x\n",
cam->params.status.vpStatus);
- out += sprintf(out, "error_code: %#04x\n",
+ seq_printf(m, "error_code: %#04x\n",
cam->params.status.errorCode);
/* QX3 specific entries */
if (cam->params.qx3.qx3_detected) {
- out += sprintf(out, "button: %4d\n",
+ seq_printf(m, "button: %4d\n",
cam->params.qx3.button);
- out += sprintf(out, "cradled: %4d\n",
+ seq_printf(m, "cradled: %4d\n",
cam->params.qx3.cradled);
}
- out += sprintf(out, "video_size: %s\n",
+ seq_printf(m, "video_size: %s\n",
cam->params.format.videoSize == VIDEOSIZE_CIF ?
"CIF " : "QCIF");
- out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
+ seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n",
cam->params.roi.colStart*8,
cam->params.roi.rowStart*4,
cam->params.roi.colEnd*8,
cam->params.roi.rowEnd*4);
- out += sprintf(out, "actual_fps: %3d\n", cam->fps);
- out += sprintf(out, "transfer_rate: %4dkB/s\n",
+ seq_printf(m, "actual_fps: %3d\n", cam->fps);
+ seq_printf(m, "transfer_rate: %4dkB/s\n",
cam->transfer_rate);
- out += sprintf(out, "\nread-write\n");
- out += sprintf(out, "----------------------- current min"
+ seq_printf(m, "\nread-write\n");
+ seq_printf(m, "----------------------- current min"
" max default comment\n");
- out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
+ seq_printf(m, "brightness: %8d %8d %8d %8d\n",
cam->params.colourParams.brightness, 0, 100, 50);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
@@ -318,26 +314,26 @@ static int cpia_read_proc(char *page, char **start, off_t off,
else
tmp = 96;
- out += sprintf(out, "contrast: %8d %8d %8d %8d"
+ seq_printf(m, "contrast: %8d %8d %8d %8d"
" steps of 8\n",
cam->params.colourParams.contrast, 0, tmp, 48);
- out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
+ seq_printf(m, "saturation: %8d %8d %8d %8d\n",
cam->params.colourParams.saturation, 0, 100, 50);
tmp = (25000+5000*cam->params.sensorFps.baserate)/
(1<<cam->params.sensorFps.divisor);
- out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
+ seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n",
tmp/1000, tmp%1000, 3, 30, 15);
- out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
+ seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n",
2*cam->params.streamStartLine, 0,
cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
- out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
+ seq_printf(m, "sub_sample: %8s %8s %8s %8s\n",
cam->params.format.subSample == SUBSAMPLE_420 ?
"420" : "422", "420", "422", "422");
- out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
+ seq_printf(m, "yuv_order: %8s %8s %8s %8s\n",
cam->params.format.yuvOrder == YUVORDER_YUYV ?
"YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
- out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
+ seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n",
cam->params.ecpTiming ? "slow" : "normal", "slow",
"normal", "normal");
@@ -346,13 +342,13 @@ static int cpia_read_proc(char *page, char **start, off_t off,
} else {
sprintf(tmpstr, "manual");
}
- out += sprintf(out, "color_balance_mode: %8s %8s %8s"
+ seq_printf(m, "color_balance_mode: %8s %8s %8s"
" %8s\n", tmpstr, "manual", "auto", "auto");
- out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
+ seq_printf(m, "red_gain: %8d %8d %8d %8d\n",
cam->params.colourBalance.redGain, 0, 212, 32);
- out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
+ seq_printf(m, "green_gain: %8d %8d %8d %8d\n",
cam->params.colourBalance.greenGain, 0, 212, 6);
- out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
+ seq_printf(m, "blue_gain: %8d %8d %8d %8d\n",
cam->params.colourBalance.blueGain, 0, 212, 92);
if (cam->params.version.firmwareVersion == 1 &&
@@ -363,10 +359,10 @@ static int cpia_read_proc(char *page, char **start, off_t off,
sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
if (cam->params.exposure.gainMode == 0)
- out += sprintf(out, "max_gain: unknown %28s"
+ seq_printf(m, "max_gain: unknown %28s"
" powers of 2\n", tmpstr);
else
- out += sprintf(out, "max_gain: %8d %28s"
+ seq_printf(m, "max_gain: %8d %28s"
" 1,2,4 or 8 \n",
1<<(cam->params.exposure.gainMode-1), tmpstr);
@@ -382,12 +378,12 @@ static int cpia_read_proc(char *page, char **start, off_t off,
sprintf(tmpstr, "unknown");
break;
}
- out += sprintf(out, "exposure_mode: %8s %8s %8s"
+ seq_printf(m, "exposure_mode: %8s %8s %8s"
" %8s\n", tmpstr, "manual", "auto", "auto");
- out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
+ seq_printf(m, "centre_weight: %8s %8s %8s %8s\n",
(2-cam->params.exposure.centreWeight) ? "on" : "off",
"off", "on", "on");
- out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
+ seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
1<<cam->params.exposure.gain, 1, 1);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
@@ -396,7 +392,7 @@ static int cpia_read_proc(char *page, char **start, off_t off,
else
tmp = 510;
- out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
+ seq_printf(m, "fine_exp: %8d %8d %8d %8d\n",
cam->params.exposure.fineExp*2, 0, tmp, 0);
if (cam->params.version.firmwareVersion == 1 &&
cam->params.version.firmwareRevision == 2)
@@ -405,127 +401,122 @@ static int cpia_read_proc(char *page, char **start, off_t off,
else
tmp = MAX_EXP;
- out += sprintf(out, "coarse_exp: %8d %8d %8d"
+ seq_printf(m, "coarse_exp: %8d %8d %8d"
" %8d\n", cam->params.exposure.coarseExpLo+
256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
- out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
+ seq_printf(m, "red_comp: %8d %8d %8d %8d\n",
cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
- out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
+ seq_printf(m, "green1_comp: %8d %8d %8d %8d\n",
cam->params.exposure.green1Comp, COMP_GREEN1, 255,
COMP_GREEN1);
- out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
+ seq_printf(m, "green2_comp: %8d %8d %8d %8d\n",
cam->params.exposure.green2Comp, COMP_GREEN2, 255,
COMP_GREEN2);
- out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
+ seq_printf(m, "blue_comp: %8d %8d %8d %8d\n",
cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
- out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
+ seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain1, 0, 0xff, 0x1c);
- out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
+ seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain2, 0, 0xff, 0x1a);
- out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
+ seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain4, 0, 0xff, 0x2d);
- out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
+ seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
cam->params.apcor.gain8, 0, 0xff, 0x2a);
- out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
+ seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain1, 0, 255, 24);
- out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
+ seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain2, 0, 255, 28);
- out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
+ seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain4, 0, 255, 30);
- out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
+ seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n",
cam->params.vlOffset.gain8, 0, 255, 30);
- out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
+ seq_printf(m, "flicker_control: %8s %8s %8s %8s\n",
cam->params.flickerControl.flickerMode ? "on" : "off",
"off", "on", "off");
- out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
+ seq_printf(m, "mains_frequency: %8d %8d %8d %8d"
" only 50/60\n",
cam->mainsFreq ? 60 : 50, 50, 60, 50);
if(cam->params.flickerControl.allowableOverExposure < 0)
- out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
+ seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n",
-cam->params.flickerControl.allowableOverExposure,
255);
else
- out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
+ seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n",
cam->params.flickerControl.allowableOverExposure,
255);
- out += sprintf(out, "compression_mode: ");
+ seq_printf(m, "compression_mode: ");
switch(cam->params.compression.mode) {
case CPIA_COMPRESSION_NONE:
- out += sprintf(out, "%8s", "none");
+ seq_printf(m, "%8s", "none");
break;
case CPIA_COMPRESSION_AUTO:
- out += sprintf(out, "%8s", "auto");
+ seq_printf(m, "%8s", "auto");
break;
case CPIA_COMPRESSION_MANUAL:
- out += sprintf(out, "%8s", "manual");
+ seq_printf(m, "%8s", "manual");
break;
default:
- out += sprintf(out, "%8s", "unknown");
+ seq_printf(m, "%8s", "unknown");
break;
}
- out += sprintf(out, " none,auto,manual auto\n");
- out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
+ seq_printf(m, " none,auto,manual auto\n");
+ seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n",
cam->params.compression.decimation ==
DECIMATION_ENAB ? "on":"off", "off", "on",
"off");
- out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
+ seq_printf(m, "compression_target: %9s %9s %9s %9s\n",
cam->params.compressionTarget.frTargeting ==
CPIA_COMPRESSION_TARGET_FRAMERATE ?
"framerate":"quality",
"framerate", "quality", "quality");
- out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
+ seq_printf(m, "target_framerate: %8d %8d %8d %8d\n",
cam->params.compressionTarget.targetFR, 1, 30, 15);
- out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
+ seq_printf(m, "target_quality: %8d %8d %8d %8d\n",
cam->params.compressionTarget.targetQ, 1, 64, 5);
- out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
+ seq_printf(m, "y_threshold: %8d %8d %8d %8d\n",
cam->params.yuvThreshold.yThreshold, 0, 31, 6);
- out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
+ seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n",
cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
- out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
+ seq_printf(m, "hysteresis: %8d %8d %8d %8d\n",
cam->params.compressionParams.hysteresis, 0, 255, 3);
- out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
+ seq_printf(m, "threshold_max: %8d %8d %8d %8d\n",
cam->params.compressionParams.threshMax, 0, 255, 11);
- out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
+ seq_printf(m, "small_step: %8d %8d %8d %8d\n",
cam->params.compressionParams.smallStep, 0, 255, 1);
- out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
+ seq_printf(m, "large_step: %8d %8d %8d %8d\n",
cam->params.compressionParams.largeStep, 0, 255, 3);
- out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
+ seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n",
cam->params.compressionParams.decimationHysteresis,
0, 255, 2);
- out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
+ seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
cam->params.compressionParams.frDiffStepThresh,
0, 255, 5);
- out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
+ seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n",
cam->params.compressionParams.qDiffStepThresh,
0, 255, 3);
- out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
+ seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n",
cam->params.compressionParams.decimationThreshMod,
0, 255, 2);
/* QX3 specific entries */
if (cam->params.qx3.qx3_detected) {
- out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
+ seq_printf(m, "toplight: %8s %8s %8s %8s\n",
cam->params.qx3.toplight ? "on" : "off",
"off", "on", "off");
- out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
+ seq_printf(m, "bottomlight: %8s %8s %8s %8s\n",
cam->params.qx3.bottomlight ? "on" : "off",
"off", "on", "off");
}
- len = out - page;
- len -= off;
- if (len < count) {
- *eof = 1;
- if (len <= 0) return 0;
- } else
- len = count;
-
- *start = page + off;
- return len;
+ return 0;
}
+static int cpia_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cpia_proc_show, PDE(inode)->data);
+}
-static int match(char *checkstr, char **buffer, unsigned long *count,
+static int match(char *checkstr, char **buffer, size_t *count,
int *find_colon, int *err)
{
int ret, colon_found = 1;
@@ -551,7 +542,7 @@ static int match(char *checkstr, char **buffer, unsigned long *count,
return ret;
}
-static unsigned long int value(char **buffer, unsigned long *count, int *err)
+static unsigned long int value(char **buffer, size_t *count, int *err)
{
char *p;
unsigned long int ret;
@@ -565,10 +556,10 @@ static unsigned long int value(char **buffer, unsigned long *count, int *err)
return ret;
}
-static int cpia_write_proc(struct file *file, const char __user *buf,
- unsigned long count, void *data)
+static ssize_t cpia_proc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
{
- struct cam_data *cam = data;
+ struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data;
struct cam_params new_params;
char *page, *buffer;
int retval, find_colon;
@@ -582,7 +573,7 @@ static int cpia_write_proc(struct file *file, const char __user *buf,
* from the comx driver
*/
if (count > PAGE_SIZE) {
- printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE);
return -ENOSPC;
}
@@ -1340,23 +1331,28 @@ out:
return retval;
}
+static const struct file_operations cpia_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cpia_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cpia_proc_write,
+};
+
static void create_proc_cpia_cam(struct cam_data *cam)
{
- char name[5 + 1 + 10 + 1];
struct proc_dir_entry *ent;
if (!cpia_proc_root || !cam)
return;
- snprintf(name, sizeof(name), "video%d", cam->vdev.num);
-
- ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
+ ent = proc_create_data(video_device_node_name(&cam->vdev),
+ S_IRUGO|S_IWUSR, cpia_proc_root,
+ &cpia_proc_fops, cam);
if (!ent)
return;
- ent->data = cam;
- ent->read_proc = cpia_read_proc;
- ent->write_proc = cpia_write_proc;
/*
size of the proc entry is 3736 bytes for the standard webcam;
the extra features of the QX3 microscope add 189 bytes.
@@ -1368,13 +1364,10 @@ static void create_proc_cpia_cam(struct cam_data *cam)
static void destroy_proc_cpia_cam(struct cam_data *cam)
{
- char name[5 + 1 + 10 + 1];
-
if (!cam || !cam->proc_entry)
return;
- snprintf(name, sizeof(name), "video%d", cam->vdev.num);
- remove_proc_entry(name, cpia_proc_root);
+ remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root);
cam->proc_entry = NULL;
}
@@ -3999,7 +3992,7 @@ void cpia_unregister_camera(struct cam_data *cam)
}
#ifdef CONFIG_PROC_FS
- DBG("destroying /proc/cpia/video%d\n", cam->vdev.num);
+ DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev));
destroy_proc_cpia_cam(cam);
#endif
if (!cam->open_count) {
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 0b4a8f309cfa..6f91415eb7b4 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -38,17 +38,12 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/videodev.h>
+#include <linux/stringify.h>
#include <media/v4l2-ioctl.h>
#include "cpia2.h"
#include "cpia2dev.h"
-
-//#define _CPIA2_DEBUG_
-
-#define MAKE_STRING_1(x) #x
-#define MAKE_STRING(x) MAKE_STRING_1(x)
-
static int video_nr = -1;
module_param(video_nr, int, 0);
MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)");
@@ -60,26 +55,26 @@ MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)
static int num_buffers = 3;
module_param(num_buffers, int, 0);
MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-"
- MAKE_STRING(VIDEO_MAX_FRAME) ", default 3)");
+ __stringify(VIDEO_MAX_FRAME) ", default 3)");
static int alternate = DEFAULT_ALT;
module_param(alternate, int, 0);
-MODULE_PARM_DESC(alternate, "USB Alternate (" MAKE_STRING(USBIF_ISO_1) "-"
- MAKE_STRING(USBIF_ISO_6) ", default "
- MAKE_STRING(DEFAULT_ALT) ")");
+MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-"
+ __stringify(USBIF_ISO_6) ", default "
+ __stringify(DEFAULT_ALT) ")");
static int flicker_freq = 60;
module_param(flicker_freq, int, 0);
-MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" MAKE_STRING(50) "or"
- MAKE_STRING(60) ", default "
- MAKE_STRING(60) ")");
+MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" __stringify(50) "or"
+ __stringify(60) ", default "
+ __stringify(60) ")");
static int flicker_mode = NEVER_FLICKER;
module_param(flicker_mode, int, 0);
MODULE_PARM_DESC(flicker_mode,
- "Flicker supression (" MAKE_STRING(NEVER_FLICKER) "or"
- MAKE_STRING(ANTI_FLICKER_ON) ", default "
- MAKE_STRING(NEVER_FLICKER) ")");
+ "Flicker supression (" __stringify(NEVER_FLICKER) "or"
+ __stringify(ANTI_FLICKER_ON) ", default "
+ __stringify(NEVER_FLICKER) ")");
MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
@@ -1926,7 +1921,6 @@ static const struct v4l2_file_operations fops_template = {
static struct video_device cpia2_template = {
/* I could not find any place for the old .initialize initializer?? */
.name= "CPiA2 Camera",
- .minor= -1,
.fops= &fops_template,
.release= video_device_release,
};
@@ -1967,9 +1961,9 @@ void cpia2_unregister_camera(struct camera_data *cam)
if (!cam->open_count) {
video_unregister_device(cam->vdev);
} else {
- LOG("/dev/video%d removed while open, "
- "deferring video_unregister_device\n",
- cam->vdev->num);
+ LOG("%s removed while open, deferring "
+ "video_unregister_device\n",
+ video_device_node_name(cam->vdev));
}
}
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 4e278db31cc9..c0885c69fd89 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -758,8 +758,8 @@ int cx18_v4l2_open(struct file *filp)
mutex_lock(&cx->serialize_lock);
if (cx18_init_on_first_open(cx)) {
- CX18_ERR("Failed to initialize on minor %d\n",
- video_dev->minor);
+ CX18_ERR("Failed to initialize on %s\n",
+ video_device_node_name(video_dev));
mutex_unlock(&cx->serialize_lock);
return -ENXIO;
}
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index c398651dd74c..987a9308d938 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -219,6 +219,7 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
int vfl_type = cx18_stream_info[type].vfl_type;
+ const char *name;
int num, ret;
/* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
@@ -258,31 +259,30 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
s->video_dev = NULL;
return ret;
}
- num = s->video_dev->num;
+
+ name = video_device_node_name(s->video_dev);
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device video%d for %s "
- "(%d x %d.%02d kB)\n",
- num, s->name, cx->stream_buffers[type],
+ CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
+ name, s->name, cx->stream_buffers[type],
cx->stream_buf_size[type] / 1024,
(cx->stream_buf_size[type] * 100 / 1024) % 100);
break;
case VFL_TYPE_RADIO:
- CX18_INFO("Registered device radio%d for %s\n",
- num, s->name);
+ CX18_INFO("Registered device %s for %s\n", name, s->name);
break;
case VFL_TYPE_VBI:
if (cx->stream_buffers[type])
- CX18_INFO("Registered device vbi%d for %s "
+ CX18_INFO("Registered device %s for %s "
"(%d x %d bytes)\n",
- num, s->name, cx->stream_buffers[type],
+ name, s->name, cx->stream_buffers[type],
cx->stream_buf_size[type]);
else
- CX18_INFO("Registered device vbi%d for %s\n",
- num, s->name);
+ CX18_INFO("Registered device %s for %s\n",
+ name, s->name);
break;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index 319c459459e0..a54908235009 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -68,19 +68,19 @@ struct cx231xx_board cx231xx_boards[] = {
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}
},
},
@@ -107,19 +107,19 @@ struct cx231xx_board cx231xx_boards[] = {
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}
},
},
@@ -147,19 +147,19 @@ struct cx231xx_board cx231xx_boards[] = {
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
- .gpio = 0,
+ .gpio = NULL,
}
},
},
@@ -856,8 +856,9 @@ static void cx231xx_usb_disconnect(struct usb_interface *interface)
if (dev->users) {
cx231xx_warn
- ("device /dev/video%d is open! Deregistration and memory "
- "deallocation are deferred on close.\n", dev->vdev->num);
+ ("device %s is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ video_device_node_name(dev->vdev));
dev->state |= DEV_MISCONFIGURED;
cx231xx_uninit_isoc(dev);
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
index 0d333e679f70..4a60dfbc347d 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -66,32 +66,6 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
static LIST_HEAD(cx231xx_devlist);
static DEFINE_MUTEX(cx231xx_devlist_mutex);
-struct cx231xx *cx231xx_get_device(int minor,
- enum v4l2_buf_type *fh_type, int *has_radio)
-{
- struct cx231xx *h, *dev = NULL;
-
- *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- *has_radio = 0;
-
- mutex_lock(&cx231xx_devlist_mutex);
- list_for_each_entry(h, &cx231xx_devlist, devlist) {
- if (h->vdev->minor == minor)
- dev = h;
- if (h->vbi_dev->minor == minor) {
- dev = h;
- *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev && h->radio_dev->minor == minor) {
- dev = h;
- *has_radio = 1;
- }
- }
- mutex_unlock(&cx231xx_devlist_mutex);
-
- return dev;
-}
-
/*
* cx231xx_realease_resources()
* unregisters the v4l2,i2c and usb devices
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
index cd135f01b9c1..15826f98b688 100644
--- a/drivers/media/video/cx231xx/cx231xx-input.c
+++ b/drivers/media/video/cx231xx/cx231xx-input.c
@@ -197,8 +197,7 @@ int cx231xx_ir_init(struct cx231xx *dev)
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
- err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER,
- dev->board.ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER);
if (err < 0)
goto err_out_free;
@@ -217,7 +216,7 @@ int cx231xx_ir_init(struct cx231xx *dev)
cx231xx_ir_start(ir);
/* all done */
- err = input_register_device(ir->input);
+ err = ir_input_register(ir->input, dev->board.ir_codes);
if (err)
goto err_out_stop;
@@ -226,8 +225,6 @@ err_out_stop:
cx231xx_ir_stop(ir);
dev->ir = NULL;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return err;
}
@@ -241,8 +238,7 @@ int cx231xx_ir_fini(struct cx231xx *dev)
return 0;
cx231xx_ir_stop(ir);
- ir_input_free(ir->input);
- input_unregister_device(ir->input);
+ ir_input_unregister(ir->input);
kfree(ir);
/* done */
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index d095aa0d6d19..d4f546f11d74 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -1916,20 +1916,29 @@ static int radio_queryctrl(struct file *file, void *priv,
*/
static int cx231xx_v4l2_open(struct file *filp)
{
- int minor = video_devdata(filp)->minor;
int errCode = 0, radio = 0;
- struct cx231xx *dev = NULL;
+ struct video_device *vdev = video_devdata(filp);
+ struct cx231xx *dev = video_drvdata(filp);
struct cx231xx_fh *fh;
enum v4l2_buf_type fh_type = 0;
- dev = cx231xx_get_device(minor, &fh_type, &radio);
- if (NULL == dev)
- return -ENODEV;
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
mutex_lock(&dev->lock);
- cx231xx_videodbg("open minor=%d type=%s users=%d\n",
- minor, v4l2_type_names[fh_type], dev->users);
+ cx231xx_videodbg("open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type],
+ dev->users);
#if 0
errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
@@ -2020,25 +2029,25 @@ void cx231xx_release_analog_resources(struct cx231xx *dev)
/*FIXME: I2C IR should be disconnected */
if (dev->radio_dev) {
- if (-1 != dev->radio_dev->minor)
+ if (video_is_registered(dev->radio_dev))
video_unregister_device(dev->radio_dev);
else
video_device_release(dev->radio_dev);
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
- cx231xx_info("V4L2 device /dev/vbi%d deregistered\n",
- dev->vbi_dev->num);
- if (-1 != dev->vbi_dev->minor)
+ cx231xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
+ if (video_is_registered(dev->vbi_dev))
video_unregister_device(dev->vbi_dev);
else
video_device_release(dev->vbi_dev);
dev->vbi_dev = NULL;
}
if (dev->vdev) {
- cx231xx_info("V4L2 device /dev/video%d deregistered\n",
- dev->vdev->num);
- if (-1 != dev->vdev->minor)
+ cx231xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
+ if (video_is_registered(dev->vdev))
video_unregister_device(dev->vdev);
else
video_device_release(dev->vdev);
@@ -2268,7 +2277,6 @@ static const struct video_device cx231xx_video_template = {
.fops = &cx231xx_v4l_fops,
.release = video_device_release,
.ioctl_ops = &video_ioctl_ops,
- .minor = -1,
.tvnorms = V4L2_STD_ALL,
.current_norm = V4L2_STD_PAL,
};
@@ -2303,7 +2311,6 @@ static struct video_device cx231xx_radio_template = {
.name = "cx231xx-radio",
.fops = &radio_fops,
.ioctl_ops = &radio_ioctl_ops,
- .minor = -1,
};
/******************************** usb interface ******************************/
@@ -2319,13 +2326,13 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
return NULL;
*vfd = *template;
- vfd->minor = -1;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
vfd->debug = video_debug;
snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
+ video_set_drvdata(vfd, dev);
return vfd;
}
@@ -2374,8 +2381,8 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
return ret;
}
- cx231xx_info("%s/0: registered device video%d [v4l2]\n",
- dev->name, dev->vdev->num);
+ cx231xx_info("%s/0: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->vdev));
/* Initialize VBI template */
memcpy(&cx231xx_vbi_template, &cx231xx_video_template,
@@ -2393,8 +2400,8 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
return ret;
}
- cx231xx_info("%s/0: registered device vbi%d\n",
- dev->name, dev->vbi_dev->num);
+ cx231xx_info("%s/0: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
@@ -2409,12 +2416,13 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
cx231xx_errdev("can't register radio device\n");
return ret;
}
- cx231xx_info("Registered radio device as /dev/radio%d\n",
- dev->radio_dev->num);
+ cx231xx_info("Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
}
- cx231xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
- dev->vdev->num, dev->vbi_dev->num);
+ cx231xx_info("V4L2 device registered as %s and %s\n",
+ video_device_node_name(dev->vdev),
+ video_device_node_name(dev->vbi_dev));
return 0;
}
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
index 64e2ddd3c401..17d4d1a800ce 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -689,8 +689,6 @@ void cx231xx_release_analog_resources(struct cx231xx *dev);
int cx231xx_register_analog_devices(struct cx231xx *dev);
void cx231xx_remove_from_devlist(struct cx231xx *dev);
void cx231xx_add_into_devlist(struct cx231xx *dev);
-struct cx231xx *cx231xx_get_device(int minor,
- enum v4l2_buf_type *fh_type, int *has_radio);
void cx231xx_init_extension(struct cx231xx *dev);
void cx231xx_close_extension(struct cx231xx *dev);
diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c
index c04222ffb286..d4a9d2c5947c 100644
--- a/drivers/media/video/cx23885/cimax2.c
+++ b/drivers/media/video/cx23885/cimax2.c
@@ -53,6 +53,8 @@
#define NETUP_CI_CTL 0x04
#define NETUP_CI_RD 1
+#define NETUP_IRQ_DETAM 0x1
+#define NETUP_IRQ_IRQAM 0x4
static unsigned int ci_dbg;
module_param(ci_dbg, int, 0644);
@@ -73,6 +75,9 @@ struct netup_ci_state {
int status;
struct work_struct work;
void *priv;
+ u8 current_irq_mode;
+ int current_ci_flag;
+ unsigned long next_status_checked_time;
};
@@ -169,24 +174,26 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
if (0 != slot)
return -EINVAL;
- ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
- 0, &store, 1);
- if (ret != 0)
- return ret;
+ if (state->current_ci_flag != flag) {
+ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &store, 1);
+ if (ret != 0)
+ return ret;
- store &= ~0x0c;
- store |= flag;
+ store &= ~0x0c;
+ store |= flag;
- ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
- 0, &store, 1);
- if (ret != 0)
- return ret;
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &store, 1);
+ if (ret != 0)
+ return ret;
+ };
+ state->current_ci_flag = flag;
mutex_lock(&dev->gpio_lock);
/* write addr */
cx_write(MC417_OEN, NETUP_EN_ALL);
- msleep(2);
cx_write(MC417_RWD, NETUP_CTRL_OFF |
NETUP_ADLO | (0xff & addr));
cx_clear(MC417_RWD, NETUP_ADLO);
@@ -196,7 +203,6 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
if (read) { /* data in */
cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA);
- msleep(2);
} else /* data out */
cx_write(MC417_RWD, NETUP_CTRL_OFF | data);
@@ -213,8 +219,8 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
if (mem < 0)
return -EREMOTEIO;
- ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
- (read) ? "read" : "write", addr,
+ ci_dbg_print("%s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x\n", __func__,
+ (read) ? "read" : "write", state->ci_i2c_addr, addr,
(flag == NETUP_CI_CTL) ? "ctl" : "mem",
(read) ? mem : data);
@@ -283,14 +289,39 @@ int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
return 0;
}
+int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode)
+{
+ struct netup_ci_state *state = en50221->data;
+ int ret;
+
+ if (irq_mode == state->current_irq_mode)
+ return 0;
+
+ ci_dbg_print("%s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n",
+ __func__, state->ci_i2c_addr, irq_mode);
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0x1b, &irq_mode, 1);
+
+ if (ret != 0)
+ return ret;
+
+ state->current_irq_mode = irq_mode;
+
+ return 0;
+}
+
int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
{
struct netup_ci_state *state = en50221->data;
- u8 buf = 0x60;
+ u8 buf;
if (0 != slot)
return -EINVAL;
+ netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf, 1);
+ buf |= 0x60;
+
return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
0, &buf, 1);
}
@@ -303,21 +334,35 @@ static void netup_read_ci_status(struct work_struct *work)
u8 buf[33];
int ret;
- ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
- 0, &buf[0], 33);
+ /* CAM module IRQ processing. fast operation */
+ dvb_ca_en50221_frda_irq(&state->ca, 0);
- if (ret != 0)
- return;
+ /* CAM module INSERT/REMOVE processing. slow operation because of i2c
+ * transfers */
+ if (time_after(jiffies, state->next_status_checked_time)
+ || !state->status) {
+ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf[0], 33);
+
+ state->next_status_checked_time = jiffies
+ + msecs_to_jiffies(1000);
+
+ if (ret != 0)
+ return;
- ci_dbg_print("%s: Slot Status Addr=[0x%04x], Reg=[0x%02x], data=%02x, "
- "TS config = %02x\n", __func__, state->ci_i2c_addr, 0, buf[0],
- buf[32]);
+ ci_dbg_print("%s: Slot Status Addr=[0x%04x], "
+ "Reg=[0x%02x], data=%02x, "
+ "TS config = %02x\n", __func__,
+ state->ci_i2c_addr, 0, buf[0],
+ buf[0]);
- if (buf[0] & 1)
- state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
- DVB_CA_EN50221_POLL_CAM_READY;
- else
- state->status = 0;
+
+ if (buf[0] & 1)
+ state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ else
+ state->status = 0;
+ };
}
/* CI irq handler */
@@ -347,6 +392,9 @@ int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open
if (0 != slot)
return -EINVAL;
+ netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | NETUP_IRQ_IRQAM)
+ : NETUP_IRQ_DETAM);
+
return state->status;
}
@@ -381,8 +429,8 @@ int netup_ci_init(struct cx23885_tsport *port)
0x01, /* power on (use it like store place) */
0x00, /* RFU */
0x00, /* int status read only */
- 0x01, /* all int unmasked */
- 0x04, /* int config */
+ NETUP_IRQ_IRQAM | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */
+ 0x05, /* EXTINT=active-high, INT=push-pull */
0x00, /* USCG1 */
0x04, /* ack active low */
0x00, /* LOCK = 0 */
@@ -422,6 +470,7 @@ int netup_ci_init(struct cx23885_tsport *port)
state->ca.poll_slot_status = netup_poll_ci_slot_status;
state->ca.data = state;
state->priv = port;
+ state->current_irq_mode = NETUP_IRQ_IRQAM | NETUP_IRQ_DETAM;
ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
0, &cimax_init[0], 34);
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 0eed852c61e9..88c0d2481118 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -1568,28 +1568,11 @@ static int vidioc_queryctrl(struct file *file, void *priv,
static int mpeg_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx23885_dev *h, *dev = NULL;
- struct list_head *list;
+ struct cx23885_dev *dev = video_drvdata(file);
struct cx23885_fh *fh;
dprintk(2, "%s()\n", __func__);
- lock_kernel();
- list_for_each(list, &cx23885_devlist) {
- h = list_entry(list, struct cx23885_dev, devlist);
- if (h->v4l_device &&
- h->v4l_device->minor == minor) {
- dev = h;
- break;
- }
- }
-
- if (dev == NULL) {
- unlock_kernel();
- return -ENODEV;
- }
-
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
@@ -1597,6 +1580,8 @@ static int mpeg_open(struct file *file)
return -ENOMEM;
}
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
@@ -1736,7 +1721,6 @@ static struct video_device cx23885_mpeg_template = {
.name = "cx23885",
.fops = &mpeg_fops,
.ioctl_ops = &mpeg_ioctl_ops,
- .minor = -1,
.tvnorms = CX23885_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};
@@ -1746,7 +1730,7 @@ void cx23885_417_unregister(struct cx23885_dev *dev)
dprintk(1, "%s()\n", __func__);
if (dev->v4l_device) {
- if (-1 != dev->v4l_device->minor)
+ if (video_is_registered(dev->v4l_device))
video_unregister_device(dev->v4l_device);
else
video_device_release(dev->v4l_device);
@@ -1803,6 +1787,7 @@ int cx23885_417_register(struct cx23885_dev *dev)
/* Allocate and initialize V4L video device */
dev->v4l_device = cx23885_video_dev_alloc(tsport,
dev->pci, &cx23885_mpeg_template, "mpeg");
+ video_set_drvdata(dev->v4l_device, dev);
err = video_register_device(dev->v4l_device,
VFL_TYPE_GRABBER, -1);
if (err < 0) {
@@ -1810,8 +1795,8 @@ int cx23885_417_register(struct cx23885_dev *dev)
return err;
}
- printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
- dev->name, dev->v4l_device->num);
+ printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+ dev->name, video_device_node_name(dev->v4l_device));
return 0;
}
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 04b12d27bc13..0dde57e96d30 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -55,9 +55,6 @@ MODULE_PARM_DESC(card, "card type");
static unsigned int cx23885_devcount;
-static DEFINE_MUTEX(devlist);
-LIST_HEAD(cx23885_devlist);
-
#define NO_SYNC_LINE (-1U)
/* FIXME, these allocations will change when
@@ -785,10 +782,6 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->nr = cx23885_devcount++;
sprintf(dev->name, "cx23885[%d]", dev->nr);
- mutex_lock(&devlist);
- list_add_tail(&dev->devlist, &cx23885_devlist);
- mutex_unlock(&devlist);
-
/* Configure the internal memory */
if (dev->pci->device == 0x8880) {
/* Could be 887 or 888, assume a default */
@@ -2008,10 +2001,6 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
/* unregister stuff */
free_irq(pci_dev->irq, dev);
- mutex_lock(&devlist);
- list_del(&dev->devlist);
- mutex_unlock(&devlist);
-
cx23885_dev_unregister(dev);
v4l2_device_unregister(v4l2_dev);
kfree(dev);
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index 469e083dd5f8..768eec92ccf9 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -377,7 +377,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
cx23885_boards[dev->board].name);
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci));
- ret = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ ret = ir_input_init(input_dev, &ir->ir, ir_type);
if (ret < 0)
goto err_out_free;
@@ -397,7 +397,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
dev->ir_input = ir;
cx23885_input_ir_start(dev);
- ret = input_register_device(ir->dev);
+ ret = ir_input_register(ir->dev, ir_codes);
if (ret)
goto err_out_stop;
@@ -407,8 +407,6 @@ err_out_stop:
cx23885_input_ir_stop(dev);
dev->ir_input = NULL;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return ret;
}
@@ -420,8 +418,7 @@ void cx23885_input_fini(struct cx23885_dev *dev)
if (dev->ir_input == NULL)
return;
- ir_input_free(dev->ir_input->dev);
- input_unregister_device(dev->ir_input->dev);
+ ir_input_unregister(dev->ir_input->dev);
kfree(dev->ir_input);
dev->ir_input = NULL;
}
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 8b372b4f0de2..8934d61cf660 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -318,11 +318,11 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
if (NULL == vfd)
return NULL;
*vfd = *template;
- vfd->minor = -1;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
dev->name, type, cx23885_boards[dev->board].name);
+ video_set_drvdata(vfd, dev);
return vfd;
}
@@ -716,46 +716,34 @@ static int get_resource(struct cx23885_fh *fh)
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx23885_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx23885_dev *dev = video_drvdata(file);
struct cx23885_fh *fh;
- struct list_head *list;
enum v4l2_buf_type type = 0;
int radio = 0;
- lock_kernel();
- list_for_each(list, &cx23885_devlist) {
- h = list_entry(list, struct cx23885_dev, devlist);
- if (h->video_dev &&
- h->video_dev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- if (h->vbi_dev &&
- h->vbi_dev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev &&
- h->radio_dev->minor == minor) {
- radio = 1;
- dev = h;
- }
- }
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
}
- dprintk(1, "open minor=%d radio=%d type=%s\n",
- minor, radio, v4l2_type_names[type]);
+ dprintk(1, "open dev=%s radio=%d type=%s\n",
+ video_device_node_name(vdev), radio, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->radio = radio;
@@ -1441,7 +1429,6 @@ static struct video_device cx23885_vbi_template;
static struct video_device cx23885_video_template = {
.name = "cx23885-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX23885_NORMS,
.current_norm = V4L2_STD_NTSC_M,
@@ -1461,7 +1448,7 @@ void cx23885_video_unregister(struct cx23885_dev *dev)
cx_clear(PCI_INT_MSK, 1);
if (dev->video_dev) {
- if (-1 != dev->video_dev->minor)
+ if (video_is_registered(dev->video_dev))
video_unregister_device(dev->video_dev);
else
video_device_release(dev->video_dev);
@@ -1532,8 +1519,8 @@ int cx23885_video_register(struct cx23885_dev *dev)
dev->name);
goto fail_unreg;
}
- printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n",
- dev->name, dev->video_dev->num);
+ printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->video_dev));
/* initial device configuration */
mutex_lock(&dev->lock);
cx23885_set_tvnorm(dev, dev->tvnorm);
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index fa744764dc8b..08b3f6b136a0 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -303,7 +303,6 @@ struct cx23885_tsport {
};
struct cx23885_dev {
- struct list_head devlist;
atomic_t refcount;
struct v4l2_device v4l2_dev;
@@ -399,8 +398,6 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
-extern struct list_head cx23885_devlist;
-
#define SRAM_CH01 0 /* Video A */
#define SRAM_CH02 1 /* VBI A */
#define SRAM_CH03 2 /* Video B */
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index fbdc1cde56a6..6fe30e6c4262 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -1048,21 +1048,15 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
static int mpeg_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx8802_dev *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx8802_dev *dev = video_drvdata(file);
struct cx8802_fh *fh;
struct cx8802_driver *drv = NULL;
int err;
- lock_kernel();
- dev = cx8802_get_device(minor);
-
dprintk( 1, "%s\n", __func__);
- if (dev == NULL) {
- unlock_kernel();
- return -ENODEV;
- }
+ lock_kernel();
/* Make sure we can acquire the hardware */
drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
@@ -1081,7 +1075,7 @@ static int mpeg_open(struct file *file)
unlock_kernel();
return -EINVAL;
}
- dprintk(1,"open minor=%d\n",minor);
+ dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
@@ -1129,10 +1123,6 @@ static int mpeg_release(struct file *file)
kfree(fh);
/* Make sure we release the hardware */
- dev = cx8802_get_device(video_devdata(file)->minor);
- if (dev == NULL)
- return -ENODEV;
-
drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
if (drv)
drv->request_release(drv);
@@ -1220,7 +1210,6 @@ static struct video_device cx8802_mpeg_template = {
.name = "cx8802",
.fops = &mpeg_fops,
.ioctl_ops = &mpeg_ioctl_ops,
- .minor = -1,
.tvnorms = CX88_NORMS,
.current_norm = V4L2_STD_NTSC_M,
};
@@ -1276,7 +1265,7 @@ static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
static void blackbird_unregister_video(struct cx8802_dev *dev)
{
if (dev->mpeg_dev) {
- if (-1 != dev->mpeg_dev->minor)
+ if (video_is_registered(dev->mpeg_dev))
video_unregister_device(dev->mpeg_dev);
else
video_device_release(dev->mpeg_dev);
@@ -1290,14 +1279,15 @@ static int blackbird_register_video(struct cx8802_dev *dev)
dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
&cx8802_mpeg_template,"mpeg");
+ video_set_drvdata(dev->mpeg_dev, dev);
err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
if (err < 0) {
printk(KERN_INFO "%s/2: can't register mpeg device\n",
dev->core->name);
return err;
}
- printk(KERN_INFO "%s/2: registered device video%d [mpeg]\n",
- dev->core->name, dev->mpeg_dev->num);
+ printk(KERN_INFO "%s/2: registered device %s [mpeg]\n",
+ dev->core->name, video_device_node_name(dev->mpeg_dev));
return 0;
}
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index 92b8cdf9fb81..f9fda18b410c 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -360,7 +360,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
- err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type);
if (err < 0)
goto err_out_free;
@@ -383,7 +383,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
cx88_ir_start(core, ir);
/* all done */
- err = input_register_device(ir->input);
+ err = ir_input_register(ir->input, ir_codes);
if (err)
goto err_out_stop;
@@ -393,8 +393,6 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
cx88_ir_stop(core, ir);
core->ir = NULL;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return err;
}
@@ -408,8 +406,7 @@ int cx88_ir_fini(struct cx88_core *core)
return 0;
cx88_ir_stop(core, ir);
- ir_input_free(ir->input);
- input_unregister_device(ir->input);
+ ir_input_unregister(ir->input);
kfree(ir);
/* done */
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index de9ff0fc741f..bb5104893411 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -580,21 +580,6 @@ static int cx8802_resume_common(struct pci_dev *pci_dev)
return 0;
}
-#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \
- defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE)
-struct cx8802_dev *cx8802_get_device(int minor)
-{
- struct cx8802_dev *dev;
-
- list_for_each_entry(dev, &cx8802_devlist, devlist)
- if (dev->mpeg_dev && dev->mpeg_dev->minor == minor)
- return dev;
-
- return NULL;
-}
-EXPORT_SYMBOL(cx8802_get_device);
-#endif
-
struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
{
struct cx8802_driver *d;
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index d7e8fcee559c..48c450f4a85a 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -75,10 +75,6 @@ MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
#define dprintk(level,fmt, arg...) if (video_debug >= level) \
printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
-/* ------------------------------------------------------------------ */
-
-static LIST_HEAD(cx8800_devlist);
-
/* ------------------------------------------------------------------- */
/* static data */
@@ -753,38 +749,31 @@ static int get_ressource(struct cx8800_fh *fh)
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx8800_dev *h,*dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_dev *dev = video_drvdata(file);
struct cx88_core *core;
struct cx8800_fh *fh;
enum v4l2_buf_type type = 0;
int radio = 0;
- lock_kernel();
- list_for_each_entry(h, &cx8800_devlist, devlist) {
- if (h->video_dev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- if (h->vbi_dev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev &&
- h->radio_dev->minor == minor) {
- radio = 1;
- dev = h;
- }
- }
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
}
+ lock_kernel();
+
core = dev->core;
- dprintk(1,"open minor=%d radio=%d type=%s\n",
- minor,radio,v4l2_type_names[type]);
+ dprintk(1, "open dev=%s radio=%d type=%s\n",
+ video_device_node_name(vdev), radio, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
@@ -1733,7 +1722,6 @@ static struct video_device cx8800_vbi_template;
static struct video_device cx8800_video_template = {
.name = "cx8800-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX88_NORMS,
.current_norm = V4L2_STD_NTSC_M,
@@ -1769,7 +1757,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
static struct video_device cx8800_radio_template = {
.name = "cx8800-radio",
.fops = &radio_fops,
- .minor = -1,
.ioctl_ops = &radio_ioctl_ops,
};
@@ -1778,21 +1765,21 @@ static struct video_device cx8800_radio_template = {
static void cx8800_unregister_video(struct cx8800_dev *dev)
{
if (dev->radio_dev) {
- if (-1 != dev->radio_dev->minor)
+ if (video_is_registered(dev->radio_dev))
video_unregister_device(dev->radio_dev);
else
video_device_release(dev->radio_dev);
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
- if (-1 != dev->vbi_dev->minor)
+ if (video_is_registered(dev->vbi_dev))
video_unregister_device(dev->vbi_dev);
else
video_device_release(dev->vbi_dev);
dev->vbi_dev = NULL;
}
if (dev->video_dev) {
- if (-1 != dev->video_dev->minor)
+ if (video_is_registered(dev->video_dev))
video_unregister_device(dev->video_dev);
else
video_device_release(dev->video_dev);
@@ -1909,6 +1896,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
/* register v4l devices */
dev->video_dev = cx88_vdev_init(core,dev->pci,
&cx8800_video_template,"video");
+ video_set_drvdata(dev->video_dev, dev);
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
video_nr[core->nr]);
if (err < 0) {
@@ -1916,10 +1904,11 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
core->name);
goto fail_unreg;
}
- printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n",
- core->name, dev->video_dev->num);
+ printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+ core->name, video_device_node_name(dev->video_dev));
dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
+ video_set_drvdata(dev->vbi_dev, dev);
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[core->nr]);
if (err < 0) {
@@ -1927,12 +1916,13 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
core->name);
goto fail_unreg;
}
- printk(KERN_INFO "%s/0: registered device vbi%d\n",
- core->name, dev->vbi_dev->num);
+ printk(KERN_INFO "%s/0: registered device %s\n",
+ core->name, video_device_node_name(dev->vbi_dev));
if (core->board.radio.type == CX88_RADIO) {
dev->radio_dev = cx88_vdev_init(core,dev->pci,
&cx8800_radio_template,"radio");
+ video_set_drvdata(dev->radio_dev, dev);
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
radio_nr[core->nr]);
if (err < 0) {
@@ -1940,12 +1930,11 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
core->name);
goto fail_unreg;
}
- printk(KERN_INFO "%s/0: registered device radio%d\n",
- core->name, dev->radio_dev->num);
+ printk(KERN_INFO "%s/0: registered device %s\n",
+ core->name, video_device_node_name(dev->radio_dev));
}
/* everything worked */
- list_add_tail(&dev->devlist,&cx8800_devlist);
pci_set_drvdata(pci_dev,dev);
/* initial device configuration */
@@ -2001,7 +1990,6 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
/* free memory */
btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
- list_del(&dev->devlist);
cx88_core_put(core,dev->pci);
kfree(dev);
}
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index e1c521710103..b1499bf604ea 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -423,7 +423,6 @@ struct cx8800_suspend_state {
struct cx8800_dev {
struct cx88_core *core;
- struct list_head devlist;
spinlock_t slock;
/* various device info */
@@ -670,7 +669,6 @@ int cx88_audio_thread(void *data);
int cx8802_register_driver(struct cx8802_driver *drv);
int cx8802_unregister_driver(struct cx8802_driver *drv);
-struct cx8802_dev *cx8802_get_device(int minor);
struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
index 12a1b3d7132d..de22bc9faf21 100644
--- a/drivers/media/video/davinci/vpfe_capture.c
+++ b/drivers/media/video/davinci/vpfe_capture.c
@@ -70,7 +70,6 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
-#include <linux/version.h>
#include <media/v4l2-common.h>
#include <linux/io.h>
#include <media/davinci/vpfe_capture.h>
@@ -1967,7 +1966,6 @@ static __init int vpfe_probe(struct platform_device *pdev)
vfd->release = video_device_release;
vfd->fops = &vpfe_fops;
vfd->ioctl_ops = &vpfe_ioctl_ops;
- vfd->minor = -1;
vfd->tvnorms = 0;
vfd->current_norm = V4L2_STD_PAL;
vfd->v4l2_dev = &vpfe_dev->v4l2_dev;
@@ -2071,7 +2069,7 @@ probe_out_video_unregister:
probe_out_v4l2_unregister:
v4l2_device_unregister(&vpfe_dev->v4l2_dev);
probe_out_video_release:
- if (vpfe_dev->video_dev->minor == -1)
+ if (!video_is_registered(vpfe_dev->video_dev))
video_device_release(vpfe_dev->video_dev);
probe_out_release_irq:
free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
@@ -2091,7 +2089,7 @@ probe_free_dev_mem:
/*
* vpfe_remove : It un-register device from V4L2 driver
*/
-static int vpfe_remove(struct platform_device *pdev)
+static int __devexit vpfe_remove(struct platform_device *pdev)
{
struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
struct resource *res;
@@ -2127,7 +2125,7 @@ vpfe_resume(struct device *dev)
return -1;
}
-static struct dev_pm_ops vpfe_dev_pm_ops = {
+static const struct dev_pm_ops vpfe_dev_pm_ops = {
.suspend = vpfe_suspend,
.resume = vpfe_resume,
};
diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c
index 3b8eac31ecae..1f532e31cd49 100644
--- a/drivers/media/video/davinci/vpif.c
+++ b/drivers/media/video/davinci/vpif.c
@@ -266,7 +266,7 @@ fail:
return status;
}
-static int vpif_remove(struct platform_device *pdev)
+static int __devexit vpif_remove(struct platform_device *pdev)
{
iounmap(vpif_base);
release_mem_region(res->start, res_len);
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
index d947ee5e4eb4..78130721f578 100644
--- a/drivers/media/video/davinci/vpif_capture.c
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -2107,7 +2107,7 @@ vpif_resume(struct device *dev)
return -1;
}
-static struct dev_pm_ops vpif_dev_pm_ops = {
+static const struct dev_pm_ops vpif_dev_pm_ops = {
.suspend = vpif_suspend,
.resume = vpif_resume,
};
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index d14cfb200ed0..dfddef7228dd 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -1347,7 +1347,6 @@ static const struct v4l2_file_operations vpif_fops = {
static struct video_device vpif_video_template = {
.name = "vpif",
.fops = &vpif_fops,
- .minor = -1,
.ioctl_ops = &vpif_ioctl_ops,
.tvnorms = DM646X_V4L2_STD,
.current_norm = V4L2_STD_625_50,
diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c
index 453236bd7559..7ee72ecd3d81 100644
--- a/drivers/media/video/davinci/vpss.c
+++ b/drivers/media/video/davinci/vpss.c
@@ -268,7 +268,7 @@ fail1:
return status;
}
-static int vpss_remove(struct platform_device *pdev)
+static int __devexit vpss_remove(struct platform_device *pdev)
{
iounmap(oper_cfg.vpss_bl_regs_base);
release_mem_region(oper_cfg.r1->start, oper_cfg.len1);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 82da205047be..25100001ffff 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -2285,7 +2285,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)";
break;
case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
- dev->init_data.ir_codes = &ir_codes_hauppauge_new_table;
+ dev->init_data.ir_codes = &ir_codes_rc5_hauppauge_new_table;
dev->init_data.get_key = em28xx_get_key_em_haup;
dev->init_data.name = "i2c IR (EM2840 Hauppauge)";
break;
@@ -2653,7 +2653,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
INIT_LIST_HEAD(&dev->vbiq.active);
INIT_LIST_HEAD(&dev->vbiq.queued);
-
if (dev->board.has_msp34xx) {
/* Send a reset to other chips via gpio */
errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
@@ -2923,9 +2922,9 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
if (dev->users) {
em28xx_warn
- ("device /dev/video%d is open! Deregistration and memory "
+ ("device %s is open! Deregistration and memory "
"deallocation are deferred on close.\n",
- dev->vdev->num);
+ video_device_node_name(dev->vdev));
dev->state |= DEV_MISCONFIGURED;
em28xx_uninit_isoc(dev);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 3f86d36dff2b..b311d4514bdf 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -216,7 +216,7 @@ int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
* sets only some bits (specified by bitmask) of a register, by first reading
* the actual value
*/
-static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
u8 bitmask)
{
int oldval;
@@ -1136,34 +1136,6 @@ void em28xx_wake_i2c(struct em28xx *dev)
static LIST_HEAD(em28xx_devlist);
static DEFINE_MUTEX(em28xx_devlist_mutex);
-struct em28xx *em28xx_get_device(int minor,
- enum v4l2_buf_type *fh_type,
- int *has_radio)
-{
- struct em28xx *h, *dev = NULL;
-
- *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- *has_radio = 0;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(h, &em28xx_devlist, devlist) {
- if (h->vdev->minor == minor)
- dev = h;
- if (h->vbi_dev && h->vbi_dev->minor == minor) {
- dev = h;
- *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev &&
- h->radio_dev->minor == minor) {
- dev = h;
- *has_radio = 1;
- }
- }
- mutex_unlock(&em28xx_devlist_mutex);
-
- return dev;
-}
-
/*
* em28xx_realease_resources()
* unregisters the v4l2,i2c and usb devices
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index d96ec7c09dca..af0d935c29be 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -112,10 +112,13 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
- unsigned char code;
+ u16 code;
+ int size;
/* poll IR chip */
- if (2 != i2c_master_recv(ir->c, buf, 2))
+ size = i2c_master_recv(ir->c, buf, sizeof(buf));
+
+ if (size != 2)
return -EIO;
/* Does eliminate repeated parity code */
@@ -124,16 +127,30 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
ir->old = buf[1];
- /* Rearranges bits to the right order */
- code = ((buf[0]&0x01)<<5) | /* 0010 0000 */
- ((buf[0]&0x02)<<3) | /* 0001 0000 */
- ((buf[0]&0x04)<<1) | /* 0000 1000 */
- ((buf[0]&0x08)>>1) | /* 0000 0100 */
- ((buf[0]&0x10)>>3) | /* 0000 0010 */
- ((buf[0]&0x20)>>5); /* 0000 0001 */
-
- i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
- code, buf[0]);
+ /*
+ * Rearranges bits to the right order.
+ * The bit order were determined experimentally by using
+ * The original Hauppauge Grey IR and another RC5 that uses addr=0x08
+ * The RC5 code has 14 bits, but we've experimentally determined
+ * the meaning for only 11 bits.
+ * So, the code translation is not complete. Yet, it is enough to
+ * work with the provided RC5 IR.
+ */
+ code =
+ ((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */
+ ((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */
+ ((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */
+ ((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */
+ ((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */
+ ((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */
+ ((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */
+ ((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */
+ ((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */
+ ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */
+ ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */
+
+ i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n",
+ code, buf[1], buf[0]);
/* return key */
*ir_key = code;
@@ -337,19 +354,28 @@ int em28xx_ir_init(struct em28xx *dev)
goto err_out_free;
ir->input = input_dev;
+ ir_config = EM2874_IR_RC5;
+
+ /* Adjust xclk based o IR table for RC5/NEC tables */
+ if (dev->board.ir_codes->ir_type == IR_TYPE_RC5) {
+ dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
+ ir->full_code = 1;
+ } else if (dev->board.ir_codes->ir_type == IR_TYPE_NEC) {
+ dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
+ ir_config = EM2874_IR_NEC;
+ ir->full_code = 1;
+ }
+ em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
+ EM28XX_XCLK_IR_RC5_MODE);
/* Setup the proper handler based on the chip */
switch (dev->chip_id) {
case CHIP_ID_EM2860:
case CHIP_ID_EM2883:
- if (dev->model == EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950)
- ir->full_code = 1;
ir->get_key = default_polling_getkey;
break;
case CHIP_ID_EM2874:
ir->get_key = em2874_polling_getkey;
- /* For now we only support RC5, so enable it */
- ir_config = EM2874_IR_RC5;
em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
break;
default:
@@ -367,8 +393,7 @@ int em28xx_ir_init(struct em28xx *dev)
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
- err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER,
- dev->board.ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER);
if (err < 0)
goto err_out_free;
@@ -387,7 +412,7 @@ int em28xx_ir_init(struct em28xx *dev)
em28xx_ir_start(ir);
/* all done */
- err = input_register_device(ir->input);
+ err = ir_input_register(ir->input, dev->board.ir_codes);
if (err)
goto err_out_stop;
@@ -396,8 +421,6 @@ int em28xx_ir_init(struct em28xx *dev)
em28xx_ir_stop(ir);
dev->ir = NULL;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return err;
}
@@ -411,8 +434,7 @@ int em28xx_ir_fini(struct em28xx *dev)
return 0;
em28xx_ir_stop(ir);
- ir_input_free(ir->input);
- input_unregister_device(ir->input);
+ ir_input_unregister(ir->input);
kfree(ir);
/* done */
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 7ad65370f274..849b18c94037 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -2081,22 +2081,30 @@ static int radio_queryctrl(struct file *file, void *priv,
*/
static int em28xx_v4l2_open(struct file *filp)
{
- int minor = video_devdata(filp)->minor;
- int errCode = 0, radio;
- struct em28xx *dev;
- enum v4l2_buf_type fh_type;
+ int errCode = 0, radio = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct em28xx *dev = video_drvdata(filp);
+ enum v4l2_buf_type fh_type = 0;
struct em28xx_fh *fh;
enum v4l2_field field;
- dev = em28xx_get_device(minor, &fh_type, &radio);
-
- if (NULL == dev)
- return -ENODEV;
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
mutex_lock(&dev->lock);
- em28xx_videodbg("open minor=%d type=%s users=%d\n",
- minor, v4l2_type_names[fh_type], dev->users);
+ em28xx_videodbg("open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type],
+ dev->users);
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
@@ -2160,25 +2168,25 @@ void em28xx_release_analog_resources(struct em28xx *dev)
/*FIXME: I2C IR should be disconnected */
if (dev->radio_dev) {
- if (-1 != dev->radio_dev->minor)
+ if (video_is_registered(dev->radio_dev))
video_unregister_device(dev->radio_dev);
else
video_device_release(dev->radio_dev);
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
- em28xx_info("V4L2 device /dev/vbi%d deregistered\n",
- dev->vbi_dev->num);
- if (-1 != dev->vbi_dev->minor)
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
+ if (video_is_registered(dev->vbi_dev))
video_unregister_device(dev->vbi_dev);
else
video_device_release(dev->vbi_dev);
dev->vbi_dev = NULL;
}
if (dev->vdev) {
- em28xx_info("V4L2 device /dev/video%d deregistered\n",
- dev->vdev->num);
- if (-1 != dev->vdev->minor)
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
+ if (video_is_registered(dev->vdev))
video_unregister_device(dev->vdev);
else
video_device_release(dev->vdev);
@@ -2397,8 +2405,6 @@ static const struct video_device em28xx_video_template = {
.release = video_device_release,
.ioctl_ops = &video_ioctl_ops,
- .minor = -1,
-
.tvnorms = V4L2_STD_ALL,
.current_norm = V4L2_STD_PAL,
};
@@ -2433,7 +2439,6 @@ static struct video_device em28xx_radio_template = {
.name = "em28xx-radio",
.fops = &radio_fops,
.ioctl_ops = &radio_ioctl_ops,
- .minor = -1,
};
/******************************** usb interface ******************************/
@@ -2451,7 +2456,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
return NULL;
*vfd = *template;
- vfd->minor = -1;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
vfd->debug = video_debug;
@@ -2459,6 +2463,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
snprintf(vfd->name, sizeof(vfd->name), "%s %s",
dev->name, type_name);
+ video_set_drvdata(vfd, dev);
return vfd;
}
@@ -2540,16 +2545,16 @@ int em28xx_register_analog_devices(struct em28xx *dev)
em28xx_errdev("can't register radio device\n");
return ret;
}
- em28xx_info("Registered radio device as /dev/radio%d\n",
- dev->radio_dev->num);
+ em28xx_info("Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
}
- em28xx_info("V4L2 video device registered as /dev/video%d\n",
- dev->vdev->num);
+ em28xx_info("V4L2 video device registered as %s\n",
+ video_device_node_name(dev->vdev));
if (dev->vbi_dev)
- em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n",
- dev->vbi_dev->num);
+ em28xx_info("V4L2 VBI device registered as %s\n",
+ video_device_node_name(dev->vbi_dev));
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 441df644ddbe..80d9b4fa1b97 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -643,6 +643,8 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int len);
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
+int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+ u8 bitmask);
int em28xx_read_ac97(struct em28xx *dev, u8 reg);
int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
@@ -666,9 +668,6 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
void em28xx_wake_i2c(struct em28xx *dev);
void em28xx_remove_from_devlist(struct em28xx *dev);
void em28xx_add_into_devlist(struct em28xx *dev);
-struct em28xx *em28xx_get_device(int minor,
- enum v4l2_buf_type *fh_type,
- int *has_radio);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
void em28xx_init_extension(struct em28xx *dev);
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index 88987a57cf7b..e6c23d509862 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -587,8 +587,8 @@ static int et61x251_stream_interrupt(struct et61x251_device* cam)
else if (cam->stream != STREAM_OFF) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "URB timeout reached. The camera is misconfigured. To "
- "use it, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use it, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -1195,7 +1195,8 @@ static void et61x251_release_resources(struct kref *kref)
cam = container_of(kref, struct et61x251_device, kref);
- DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->num);
+ DBG(2, "V4L2 device %s deregistered",
+ video_device_node_name(cam->v4ldev));
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
usb_put_dev(cam->usbdev);
@@ -1236,8 +1237,8 @@ static int et61x251_open(struct file *filp)
}
if (cam->users) {
- DBG(2, "Device /dev/video%d is already in use",
- cam->v4ldev->num);
+ DBG(2, "Device %s is already in use",
+ video_device_node_name(cam->v4ldev));
DBG(3, "Simultaneous opens are not supported");
if ((filp->f_flags & O_NONBLOCK) ||
(filp->f_flags & O_NDELAY)) {
@@ -1280,7 +1281,8 @@ static int et61x251_open(struct file *filp)
cam->frame_count = 0;
et61x251_empty_framequeues(cam);
- DBG(3, "Video device /dev/video%d is open", cam->v4ldev->num);
+ DBG(3, "Video device %s is open",
+ video_device_node_name(cam->v4ldev));
out:
mutex_unlock(&cam->open_mutex);
@@ -1304,7 +1306,8 @@ static int et61x251_release(struct file *filp)
cam->users--;
wake_up_interruptible_nr(&cam->wait_open, 1);
- DBG(3, "Video device /dev/video%d closed", cam->v4ldev->num);
+ DBG(3, "Video device %s closed",
+ video_device_node_name(cam->v4ldev));
kref_put(&cam->kref, et61x251_release_resources);
@@ -1846,8 +1849,8 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -1859,8 +1862,8 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -ENOMEM;
}
@@ -2069,8 +2072,8 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -2081,8 +2084,8 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -ENOMEM;
}
@@ -2130,7 +2133,7 @@ et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg)
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
"problems. To use the camera, close and open "
- "/dev/video%d again.", cam->v4ldev->num);
+ "%s again.", video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -2584,7 +2587,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera");
cam->v4ldev->fops = &et61x251_fops;
- cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
cam->v4ldev->parent = &udev->dev;
video_set_drvdata(cam->v4ldev, cam);
@@ -2603,7 +2605,8 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
- DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->num);
+ DBG(2, "V4L2 device registered as %s",
+ video_device_node_name(cam->v4ldev));
cam->module_param.force_munmap = force_munmap[dev_nr];
cam->module_param.frame_timeout = frame_timeout[dev_nr];
@@ -2654,9 +2657,9 @@ static void et61x251_usb_disconnect(struct usb_interface* intf)
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
if (cam->users) {
- DBG(2, "Device /dev/video%d is open! Deregistration and "
- "memory deallocation are deferred.",
- cam->v4ldev->num);
+ DBG(2, "Device %s is open! Deregistration and memory "
+ "deallocation are deferred.",
+ video_device_node_name(cam->v4ldev));
cam->state |= DEV_MISCONFIGURED;
et61x251_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index 2f0b8d621e00..c98b5d69c438 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -1046,14 +1046,14 @@ static struct sd_desc sd_desc = {
};
/* -- module initialisation -- */
-static __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] __devinitconst = {
{USB_DEVICE(0x0572, 0x0041)},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
+static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 9de86419ae1e..fdf4c0ec5e7a 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -864,7 +864,7 @@ static struct sd_desc sd_desc = {
};
/* -- module initialisation -- */
-static __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] __devinitconst = {
{USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106},
#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE
{USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX},
@@ -875,7 +875,7 @@ static __devinitdata struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
+static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c
index 1355e526ee84..c276a7debdec 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi1320.c
+++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c
@@ -345,7 +345,7 @@ static int mi1320_configure_alt(struct gspca_dev *gspca_dev)
return 0;
}
-int mi1320_camera_settings(struct gspca_dev *gspca_dev)
+static int mi1320_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c
index 80cb3f1b36f7..7c31b4f2abea 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi2020.c
+++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c
@@ -769,7 +769,7 @@ static int mi2020_configure_alt(struct gspca_dev *gspca_dev)
return 0;
}
-int mi2020_camera_settings(struct gspca_dev *gspca_dev)
+static int mi2020_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index a695e0ae13c2..4878c8f66543 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -40,7 +40,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
static void sd_callback(struct gspca_dev *gspca_dev);
static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
- s32 vendor_id, s32 product_id);
+ u16 vendor_id, u16 product_id);
/*============================ driver options ==============================*/
@@ -326,11 +326,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
- s32 vendor_id, product_id;
+ u16 vendor_id, product_id;
/* Get USB VendorID and ProductID */
- vendor_id = le16_to_cpu(id->idVendor);
- product_id = le16_to_cpu(id->idProduct);
+ vendor_id = id->idVendor;
+ product_id = id->idProduct;
sd->nbRightUp = 1;
sd->nbIm = -1;
@@ -534,8 +534,8 @@ static int sd_probe(struct usb_interface *intf,
gspca_dev = usb_get_intfdata(intf);
PDEBUG(D_PROBE,
- "Camera is now controlling video device /dev/video%d",
- gspca_dev->vdev.minor);
+ "Camera is now controlling video device %s",
+ video_device_node_name(&gspca_dev->vdev));
}
return ret;
@@ -673,7 +673,7 @@ void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len)
}
static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
- s32 vendor_id, s32 product_id)
+ u16 vendor_id, u16 product_id)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 probe, nb26, nb96, nOV, ntry;
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 4076f8e5a6fc..e930a67d526b 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -304,7 +304,6 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
j = gspca_dev->fr_queue[i];
gspca_dev->cur_frame = &gspca_dev->frame[j];
}
- return;
}
EXPORT_SYMBOL(gspca_frame_add);
@@ -321,7 +320,7 @@ static int gspca_is_compressed(__u32 format)
return 0;
}
-static void *rvmalloc(unsigned long size)
+static void *rvmalloc(long size)
{
void *mem;
unsigned long adr;
@@ -329,7 +328,7 @@ static void *rvmalloc(unsigned long size)
mem = vmalloc_32(size);
if (mem != NULL) {
adr = (unsigned long) mem;
- while ((long) size > 0) {
+ while (size > 0) {
SetPageReserved(vmalloc_to_page((void *) adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
@@ -768,6 +767,7 @@ static int vidioc_g_register(struct file *file, void *priv,
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->get_register(gspca_dev, reg);
else
@@ -791,6 +791,7 @@ static int vidioc_s_register(struct file *file, void *priv,
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->set_register(gspca_dev, reg);
else
@@ -812,6 +813,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv,
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
else
@@ -983,11 +985,40 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
return -EINVAL;
}
+static int vidioc_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+ __u32 i;
+
+ if (gspca_dev->cam.mode_framerates == NULL ||
+ gspca_dev->cam.mode_framerates[mode].nrates == 0)
+ return -EINVAL;
+
+ if (fival->pixel_format !=
+ gspca_dev->cam.cam_mode[mode].pixelformat)
+ return -EINVAL;
+
+ for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
+ if (fival->index == i) {
+ fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator =
+ gspca_dev->cam.mode_framerates[mode].rates[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
static void gspca_release(struct video_device *vfd)
{
struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
- PDEBUG(D_PROBE, "/dev/video%d released", gspca_dev->vdev.num);
+ PDEBUG(D_PROBE, "%s released",
+ video_device_node_name(&gspca_dev->vdev));
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
@@ -1053,6 +1084,7 @@ static int dev_close(struct file *file)
if (gspca_dev->capt_file == file) {
if (gspca_dev->streaming) {
mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
gspca_stream_off(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
}
@@ -1143,12 +1175,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
continue;
ctrls = &gspca_dev->sd_desc->ctrls[i];
}
+ if (ctrls == NULL)
+ return -EINVAL;
} else {
ctrls = get_ctrl(gspca_dev, id);
+ if (ctrls == NULL)
+ return -EINVAL;
i = ctrls - gspca_dev->sd_desc->ctrls;
}
- if (ctrls == NULL)
- return -EINVAL;
memcpy(q_ctrl, ctrls, sizeof *q_ctrl);
if (gspca_dev->ctrl_inac & (1 << i))
q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
@@ -1172,6 +1206,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = ctrls->set(gspca_dev, ctrl->value);
else
@@ -1193,6 +1228,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = ctrls->get(gspca_dev, &ctrl->value);
else
@@ -1307,6 +1343,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
/* stop streaming */
if (gspca_dev->streaming) {
mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
gspca_stream_off(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
}
@@ -1398,6 +1435,7 @@ static int vidioc_streamoff(struct file *file, void *priv,
ret = -ERESTARTSYS;
goto out;
}
+ gspca_dev->usb_err = 0;
gspca_stream_off(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
@@ -1423,6 +1461,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
else
@@ -1441,6 +1480,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
else
@@ -1461,6 +1501,7 @@ static int vidioc_g_parm(struct file *filp, void *priv,
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->get_streamparm(gspca_dev,
parm);
@@ -1490,6 +1531,7 @@ static int vidioc_s_parm(struct file *filp, void *priv,
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
ret = gspca_dev->sd_desc->set_streamparm(gspca_dev,
parm);
@@ -1613,7 +1655,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma)
size -= PAGE_SIZE;
}
- vma->vm_ops = (struct vm_operations_struct *) &gspca_vm_ops;
+ vma->vm_ops = &gspca_vm_ops;
vma->vm_private_data = frame;
gspca_vm_open(vma);
ret = 0;
@@ -1661,6 +1703,7 @@ static int frame_wait(struct gspca_dev *gspca_dev,
if (gspca_dev->sd_desc->dq_callback) {
mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
if (gspca_dev->present)
gspca_dev->sd_desc->dq_callback(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
@@ -1973,6 +2016,7 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
@@ -1988,7 +2032,6 @@ static struct video_device gspca_template = {
.fops = &dev_fops,
.ioctl_ops = &dev_ioctl_ops,
.release = gspca_release,
- .minor = -1,
};
/*
@@ -2049,9 +2092,6 @@ int gspca_dev_probe(struct usb_interface *intf,
ret = sd_desc->init(gspca_dev);
if (ret < 0)
goto out;
- ret = gspca_set_alt0(gspca_dev);
- if (ret < 0)
- goto out;
gspca_set_default_mode(gspca_dev);
mutex_init(&gspca_dev->usb_lock);
@@ -2073,7 +2113,7 @@ int gspca_dev_probe(struct usb_interface *intf,
}
usb_set_intfdata(intf, gspca_dev);
- PDEBUG(D_PROBE, "/dev/video%d created", gspca_dev->vdev.num);
+ PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
return 0;
out:
kfree(gspca_dev->usb_buf);
@@ -2092,7 +2132,8 @@ void gspca_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
- PDEBUG(D_PROBE, "/dev/video%d disconnect", gspca_dev->vdev.num);
+ PDEBUG(D_PROBE, "%s disconnect",
+ video_device_node_name(&gspca_dev->vdev));
mutex_lock(&gspca_dev->usb_lock);
gspca_dev->present = 0;
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 181617355ec3..59c7941da999 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -45,11 +45,20 @@ extern int gspca_debug;
/* image transfers */
#define MAX_NURBS 4 /* max number of URBs */
+
+/* used to list framerates supported by a camera mode (resolution) */
+struct framerates {
+ int *rates;
+ int nrates;
+};
+
/* device information - set at probe time */
struct cam {
int bulk_size; /* buffer size when image transfer by bulk */
const struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
+ const struct framerates *mode_framerates; /* must have size nmode,
+ * just like cam_mode */
__u8 bulk_nurbs; /* number of URBs in bulk mode
* - cannot be > MAX_NURBS
* - when 0 and bulk_size != 0 means
@@ -171,6 +180,7 @@ struct gspca_dev {
struct mutex usb_lock; /* usb exchange protection */
struct mutex read_lock; /* read protection */
struct mutex queue_lock; /* ISOC queue protection */
+ int usb_err; /* USB error - protected by usb_lock */
#ifdef CONFIG_PM
char frozen; /* suspend - resume */
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index 844fc1d886d1..4294c75e3b11 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -81,7 +81,7 @@ int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data)
return (err < 0) ? err : 0;
}
-int m5602_wait_for_i2c(struct sd *sd)
+static int m5602_wait_for_i2c(struct sd *sd)
{
int err;
u8 data;
@@ -388,7 +388,7 @@ static int m5602_probe(struct usb_interface *intf,
THIS_MODULE);
}
-void m5602_disconnect(struct usb_interface *intf)
+static void m5602_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
struct sd *sd = (struct sd *) gspca_dev;
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index c2739d6605a1..923cdd5f7a6b 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -439,7 +439,7 @@ int ov9650_start(struct sd *sd)
err = m5602_write_bridge(sd, res_init_ov9650[i][1],
res_init_ov9650[i][2]);
else if (res_init_ov9650[i][0] == SENSOR) {
- u8 data = res_init_ov9650[i][2];
+ data = res_init_ov9650[i][2];
err = m5602_write_sensor(sd,
res_init_ov9650[i][1], &data, 1);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index a27afeb6f39b..aa2f3c7e2cb5 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -525,7 +525,10 @@ static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
return err;
- data = (data & 0xfe) | !val;
+ if (val)
+ data &= 0xfe;
+ else
+ data |= 0x01;
err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
return err;
}
@@ -570,7 +573,10 @@ static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
return err;
- data = (data & 0xfe) | !val;
+ if (val)
+ data &= 0xfe;
+ else
+ data |= 0x01;
err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
return err;
}
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index 126d968dd9e0..9154870e07d2 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -67,7 +67,7 @@ MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
MODULE_LICENSE("GPL");
/* global parameters */
-int force_sensor_type = -1;
+static int force_sensor_type = -1;
module_param(force_sensor_type, int, 0644);
MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index ad9ec339981d..b4f965731244 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -1982,7 +1982,7 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n)
{
int ret;
- *((u32 *)sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
+ *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
ret = usb_control_msg(sd->gspca_dev.dev,
usb_sndctrlpipe(sd->gspca_dev.dev, 0),
@@ -2021,9 +2021,9 @@ static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value)
if (rc < 0)
return rc;
- do
+ do {
rc = reg_r(sd, R511_I2C_CTL);
- while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
if (rc < 0)
return rc;
@@ -2055,9 +2055,9 @@ static int ov511_i2c_r(struct sd *sd, __u8 reg)
if (rc < 0)
return rc;
- do
+ do {
rc = reg_r(sd, R511_I2C_CTL);
- while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
if (rc < 0)
return rc;
@@ -2081,9 +2081,9 @@ static int ov511_i2c_r(struct sd *sd, __u8 reg)
if (rc < 0)
return rc;
- do
+ do {
rc = reg_r(sd, R511_I2C_CTL);
- while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
if (rc < 0)
return rc;
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index 74acceea8094..de0b66c4b56e 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -90,6 +90,9 @@ struct sd {
unsigned char autogain;
__u8 hflip;
__u8 vflip;
+ u8 flags;
+#define FL_HFLIP 0x01 /* mirrored by default */
+#define FL_VFLIP 0x02 /* vertical flipped by default */
u8 sof_read;
u8 autogain_ignore_frames;
@@ -552,6 +555,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->autogain = AUTOGAIN_DEF;
sd->hflip = HFLIP_DEF;
sd->vflip = VFLIP_DEF;
+ sd->flags = id->driver_info;
return 0;
}
@@ -708,10 +712,17 @@ static int sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int ret;
- __u8 data;
+ u8 data, hflip, vflip;
+
+ hflip = sd->hflip;
+ if (sd->flags & FL_HFLIP)
+ hflip = !hflip;
+ vflip = sd->vflip;
+ if (sd->flags & FL_VFLIP)
+ vflip = !vflip;
ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- data = (sd->hflip ? 0x08 : 0x00) | (sd->vflip ? 0x04 : 0x00);
+ data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
if (0 <= ret)
ret = reg_w(gspca_dev, 0x21, data);
/* load registers to sensor (Bit 0, auto clear) */
@@ -1218,15 +1229,15 @@ static struct sd_desc sd_desc = {
};
/* -- module initialisation -- */
-static __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] __devinitconst = {
{USB_DEVICE(0x06f8, 0x3009)},
{USB_DEVICE(0x093a, 0x2620)},
{USB_DEVICE(0x093a, 0x2621)},
- {USB_DEVICE(0x093a, 0x2622)},
- {USB_DEVICE(0x093a, 0x2624)},
+ {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
{USB_DEVICE(0x093a, 0x2626)},
{USB_DEVICE(0x093a, 0x2628)},
- {USB_DEVICE(0x093a, 0x2629)},
+ {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
{USB_DEVICE(0x093a, 0x262a)},
{USB_DEVICE(0x093a, 0x262c)},
{}
@@ -1234,7 +1245,7 @@ static __devinitdata struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
+static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index e5697a6345e8..42cfcdfd8f4f 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -863,7 +863,7 @@ static struct sd_desc sd_desc = {
};
/* -- module initialisation -- */
-static __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] __devinitconst = {
{USB_DEVICE(0x093a, 0x2600)},
{USB_DEVICE(0x093a, 0x2601)},
{USB_DEVICE(0x093a, 0x2603)},
@@ -875,7 +875,7 @@ static __devinitdata struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
+static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index b1944a7cbb0f..4cff8035614f 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -1158,7 +1158,7 @@ static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
return i2c_w(gspca_dev, row);
}
-int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
@@ -1183,7 +1183,7 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
return 0;
}
-int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
@@ -1476,8 +1476,9 @@ static int sn9c20x_input_init(struct gspca_dev *gspca_dev)
if (input_register_device(sd->input_dev))
return -EINVAL;
- sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%d",
- gspca_dev->vdev.minor);
+ sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%s-%s",
+ gspca_dev->dev->bus->bus_name,
+ gspca_dev->dev->devpath);
if (IS_ERR(sd->input_task))
return -EINVAL;
@@ -2174,8 +2175,7 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
}
#define HW_WIN(mode, hstart, vstart) \
-((const u8 []){hstart & 0xff, hstart >> 8, \
-vstart & 0xff, vstart >> 8, \
+((const u8 []){hstart, 0, vstart, 0, \
(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 5be95bc65138..ddff2b5ee5c2 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -1226,7 +1226,7 @@ static const struct sd_desc sd_desc = {
.driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge
-static __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] __devinitconst = {
{USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */
{USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
@@ -1257,7 +1257,7 @@ static __devinitdata struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
+static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index ab28cc23e415..39257e4e074f 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -685,7 +685,7 @@ static struct sd_desc sd_desc = {
};
/* -- module initialisation -- */
-static __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] __devinitconst = {
{USB_DEVICE(0x06e1, 0xa190)},
/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
{USB_DEVICE(0x0733, 0x0430)}, */
@@ -696,7 +696,7 @@ static __devinitdata struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
+static int __devinit sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 8e23320d7ab7..2e2935532d99 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -126,12 +126,14 @@ static const struct v4l2_pix_format vga_mode[] = {
};
/* -- read a register -- */
-static int reg_r(struct gspca_dev *gspca_dev,
+static u8 reg_r(struct gspca_dev *gspca_dev,
__u16 index)
{
struct usb_device *dev = gspca_dev->dev;
int ret;
+ if (gspca_dev->usb_err < 0)
+ return 0;
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -141,18 +143,21 @@ static int reg_r(struct gspca_dev *gspca_dev,
500);
if (ret < 0) {
PDEBUG(D_ERR, "reg_r err %d", ret);
- return ret;
+ gspca_dev->usb_err = ret;
+ return 0;
}
return gspca_dev->usb_buf[0];
}
/* -- write a register -- */
-static int reg_w(struct gspca_dev *gspca_dev,
+static void reg_w(struct gspca_dev *gspca_dev,
__u16 index, __u16 value)
{
struct usb_device *dev = gspca_dev->dev;
int ret;
+ if (gspca_dev->usb_err < 0)
+ return;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -161,13 +166,14 @@ static int reg_w(struct gspca_dev *gspca_dev,
NULL,
0,
500);
- if (ret < 0)
+ if (ret < 0) {
PDEBUG(D_ERR, "reg_w err %d", ret);
- return ret;
+ gspca_dev->usb_err = ret;
+ }
}
/* -- get a bulk value (4 bytes) -- */
-static int rcv_val(struct gspca_dev *gspca_dev,
+static void rcv_val(struct gspca_dev *gspca_dev,
int ads)
{
struct usb_device *dev = gspca_dev->dev;
@@ -182,17 +188,22 @@ static int rcv_val(struct gspca_dev *gspca_dev,
reg_w(gspca_dev, 0x63a, 0);
reg_w(gspca_dev, 0x63b, 0);
reg_w(gspca_dev, 0x630, 5);
+ if (gspca_dev->usb_err < 0)
+ return;
ret = usb_bulk_msg(dev,
usb_rcvbulkpipe(dev, 0x05),
gspca_dev->usb_buf,
4, /* length */
&alen,
500); /* timeout in milliseconds */
- return ret;
+ if (ret < 0) {
+ PDEBUG(D_ERR, "rcv_val err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
}
/* -- send a bulk value -- */
-static int snd_val(struct gspca_dev *gspca_dev,
+static void snd_val(struct gspca_dev *gspca_dev,
int ads,
unsigned int val)
{
@@ -201,16 +212,9 @@ static int snd_val(struct gspca_dev *gspca_dev,
__u8 seq = 0;
if (ads == 0x003f08) {
- ret = reg_r(gspca_dev, 0x0704);
- if (ret < 0)
- goto ko;
- ret = reg_r(gspca_dev, 0x0705);
- if (ret < 0)
- goto ko;
- seq = ret; /* keep the sequence number */
- ret = reg_r(gspca_dev, 0x0650);
- if (ret < 0)
- goto ko;
+ reg_r(gspca_dev, 0x0704);
+ seq = reg_r(gspca_dev, 0x0705);
+ reg_r(gspca_dev, 0x0650);
reg_w(gspca_dev, 0x654, seq);
} else {
reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff);
@@ -223,6 +227,8 @@ static int snd_val(struct gspca_dev *gspca_dev,
reg_w(gspca_dev, 0x65a, 0);
reg_w(gspca_dev, 0x65b, 0);
reg_w(gspca_dev, 0x650, 5);
+ if (gspca_dev->usb_err < 0)
+ return;
gspca_dev->usb_buf[0] = val >> 24;
gspca_dev->usb_buf[1] = val >> 16;
gspca_dev->usb_buf[2] = val >> 8;
@@ -233,24 +239,23 @@ static int snd_val(struct gspca_dev *gspca_dev,
4,
&alen,
500); /* timeout in milliseconds */
- if (ret < 0)
- goto ko;
- if (ads == 0x003f08) {
- seq += 4;
- seq &= 0x3f;
- reg_w(gspca_dev, 0x705, seq);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "snd_val err %d", ret);
+ gspca_dev->usb_err = ret;
+ } else {
+ if (ads == 0x003f08) {
+ seq += 4;
+ seq &= 0x3f;
+ reg_w(gspca_dev, 0x705, seq);
+ }
}
- return ret;
-ko:
- PDEBUG(D_ERR, "snd_val err %d", ret);
- return ret;
}
/* set a camera parameter */
-static int set_par(struct gspca_dev *gspca_dev,
+static void set_par(struct gspca_dev *gspca_dev,
int parval)
{
- return snd_val(gspca_dev, 0x003f08, parval);
+ snd_val(gspca_dev, 0x003f08, parval);
}
static void setbrightness(struct gspca_dev *gspca_dev)
@@ -311,18 +316,18 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- int ret;
+ u8 ret;
/* check if the device responds */
usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
ret = reg_r(gspca_dev, 0x0740);
- if (ret < 0)
- return ret;
- if (ret != 0xff) {
- PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
- return -1;
+ if (gspca_dev->usb_err >= 0) {
+ if (ret != 0xff) {
+ PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
+ gspca_dev->usb_err = -EIO;
+ }
}
- return 0;
+ return gspca_dev->usb_err;
}
/* -- start the camera -- */
@@ -357,15 +362,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
if (ret < 0) {
PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed",
gspca_dev->iface, gspca_dev->alt);
+ gspca_dev->usb_err = ret;
goto out;
}
- ret = reg_r(gspca_dev, 0x0630);
- if (ret < 0)
- goto out;
+ reg_r(gspca_dev, 0x0630);
rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */
- ret = reg_r(gspca_dev, 0x0650);
- if (ret < 0)
- goto out;
+ reg_r(gspca_dev, 0x0650);
snd_val(gspca_dev, 0x000020, 0xffffffff);
reg_w(gspca_dev, 0x0620, 0);
reg_w(gspca_dev, 0x0630, 0);
@@ -384,11 +386,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* start the video flow */
set_par(gspca_dev, 0x01000000);
set_par(gspca_dev, 0x01000000);
- PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
- return 0;
+ if (gspca_dev->usb_err >= 0)
+ PDEBUG(D_STREAM, "camera started alt: 0x%02x",
+ gspca_dev->alt);
out:
- PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret);
- return ret;
+ return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -456,7 +458,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -474,7 +476,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)
@@ -492,7 +494,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
sd->colors = val;
if (gspca_dev->streaming)
setcolors(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
@@ -510,7 +512,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
sd->lightfreq = val;
if (gspca_dev->streaming)
setfreq(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
@@ -552,7 +554,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,
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index 72bf3b4f0a31..716df6b15fc5 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -460,13 +460,17 @@ static void reg_r(struct gspca_dev *gspca_dev,
u16 index,
u16 len)
{
+ int ret;
+
#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
err("reg_r: buffer overflow");
return;
}
#endif
- usb_control_msg(gspca_dev->dev,
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
req,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -474,6 +478,10 @@ static void reg_r(struct gspca_dev *gspca_dev,
index,
len ? gspca_dev->usb_buf : NULL, len,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_r err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
}
/* write one byte */
@@ -483,40 +491,55 @@ static void reg_w_1(struct gspca_dev *gspca_dev,
u16 index,
u16 byte)
{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
gspca_dev->usb_buf[0] = byte;
- usb_control_msg(gspca_dev->dev,
+ ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index,
gspca_dev->usb_buf, 1,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w_1 err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
}
/* write req / index / value */
-static int reg_w_riv(struct usb_device *dev,
+static void reg_w_riv(struct gspca_dev *gspca_dev,
u8 req, u16 index, u16 value)
{
+ struct usb_device *dev = gspca_dev->dev;
int ret;
+ if (gspca_dev->usb_err < 0)
+ return;
ret = usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
- PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
- req, index, value, ret);
- if (ret < 0)
- PDEBUG(D_ERR, "reg write: error %d", ret);
- return ret;
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w_riv err %d", ret);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x",
+ req, index, value);
}
/* read 1 byte */
-static int reg_r_1(struct gspca_dev *gspca_dev,
+static u8 reg_r_1(struct gspca_dev *gspca_dev,
u16 value) /* wValue */
{
int ret;
+ if (gspca_dev->usb_err < 0)
+ return 0;
ret = usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
0x20, /* request */
@@ -527,19 +550,22 @@ static int reg_r_1(struct gspca_dev *gspca_dev,
500); /* timeout */
if (ret < 0) {
PDEBUG(D_ERR, "reg_r_1 err %d", ret);
+ gspca_dev->usb_err = ret;
return 0;
}
return gspca_dev->usb_buf[0];
}
-/* read 1 or 2 bytes - returns < 0 if error */
-static int reg_r_12(struct gspca_dev *gspca_dev,
+/* read 1 or 2 bytes */
+static u16 reg_r_12(struct gspca_dev *gspca_dev,
u8 req, /* bRequest */
u16 index, /* wIndex */
u16 length) /* wLength (1 or 2 only) */
{
int ret;
+ if (gspca_dev->usb_err < 0)
+ return 0;
gspca_dev->usb_buf[1] = 0;
ret = usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
@@ -550,62 +576,44 @@ static int reg_r_12(struct gspca_dev *gspca_dev,
gspca_dev->usb_buf, length,
500);
if (ret < 0) {
- PDEBUG(D_ERR, "reg_read err %d", ret);
- return -1;
+ PDEBUG(D_ERR, "reg_r_12 err %d", ret);
+ gspca_dev->usb_err = ret;
+ return 0;
}
return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
}
-static int write_vector(struct gspca_dev *gspca_dev,
+static void write_vector(struct gspca_dev *gspca_dev,
const struct cmd *data, int ncmds)
{
- struct usb_device *dev = gspca_dev->dev;
- int ret;
-
while (--ncmds >= 0) {
- ret = reg_w_riv(dev, data->req, data->idx, data->val);
- if (ret < 0) {
- PDEBUG(D_ERR,
- "Register write failed for 0x%02x, 0x%04x, 0x%04x",
- data->req, data->val, data->idx);
- return ret;
- }
+ reg_w_riv(gspca_dev, data->req, data->idx, data->val);
data++;
}
- return 0;
}
-static int spca50x_setup_qtable(struct gspca_dev *gspca_dev,
- const u8 qtable[2][64])
+static void setup_qtable(struct gspca_dev *gspca_dev,
+ const u8 qtable[2][64])
{
- struct usb_device *dev = gspca_dev->dev;
- int i, err;
+ int i;
/* loop over y components */
- for (i = 0; i < 64; i++) {
- err = reg_w_riv(dev, 0x00, 0x2800 + i, qtable[0][i]);
- if (err < 0)
- return err;
- }
+ for (i = 0; i < 64; i++)
+ reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
/* loop over c components */
- for (i = 0; i < 64; i++) {
- err = reg_w_riv(dev, 0x00, 0x2840 + i, qtable[1][i]);
- if (err < 0)
- return err;
- }
- return 0;
+ for (i = 0; i < 64; i++)
+ reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]);
}
static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
u8 req, u16 idx, u16 val)
{
- struct usb_device *dev = gspca_dev->dev;
- int notdone;
+ u16 notdone;
- reg_w_riv(dev, req, idx, val);
+ reg_w_riv(gspca_dev, req, idx, val);
notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
- reg_w_riv(dev, req, idx, val);
+ reg_w_riv(gspca_dev, req, idx, val);
PDEBUG(D_FRAM, "before wait 0x%04x", notdone);
@@ -616,23 +624,22 @@ static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
u8 req,
- u16 idx, u16 val, u8 stat, u8 count)
+ u16 idx, u16 val, u16 endcode, u8 count)
{
- struct usb_device *dev = gspca_dev->dev;
- int status;
- u8 endcode;
+ u16 status;
- reg_w_riv(dev, req, idx, val);
+ reg_w_riv(gspca_dev, req, idx, val);
status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
- endcode = stat;
- PDEBUG(D_FRAM, "Status 0x%x Need 0x%04x", status, stat);
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_FRAM, "Status 0x%04x Need 0x%04x", status, endcode);
if (!count)
return;
count = 200;
while (--count > 0) {
msleep(10);
/* gsmart mini2 write a each wait setting 1 ms is enough */
-/* reg_w_riv(dev, req, idx, val); */
+/* reg_w_riv(gspca_dev, req, idx, val); */
status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
if (status == endcode) {
PDEBUG(D_FRAM, "status 0x%04x after wait %d",
@@ -642,7 +649,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
}
}
-static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
+static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
{
int count = 10;
@@ -652,7 +659,6 @@ static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
break;
msleep(10);
}
- return gspca_dev->usb_buf[0];
}
static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
@@ -686,28 +692,26 @@ static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
u8 Size;
- int rc;
Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
switch (sd->bridge) {
case BRIDGE_SPCA533:
- reg_w_riv(dev, 0x31, 0, 0);
+ reg_w_riv(gspca_dev, 0x31, 0, 0);
spca504B_WaitCmdStatus(gspca_dev);
- rc = spca504B_PollingDataReady(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
spca50x_GetFirmware(gspca_dev);
reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */
reg_r(gspca_dev, 0x24, 8, 1);
reg_w_1(gspca_dev, 0x25, 0, 4, Size);
reg_r(gspca_dev, 0x25, 4, 1); /* size */
- rc = spca504B_PollingDataReady(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
/* Init the cam width height with some values get on init ? */
- reg_w_riv(dev, 0x31, 0, 0x04);
+ reg_w_riv(gspca_dev, 0x31, 0, 0x04);
spca504B_WaitCmdStatus(gspca_dev);
- rc = spca504B_PollingDataReady(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
break;
default:
/* case BRIDGE_SPCA504B: */
@@ -716,7 +720,7 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
reg_r(gspca_dev, 0x25, 4, 1); /* size */
reg_w_1(gspca_dev, 0x27, 0, 0, 6);
reg_r(gspca_dev, 0x27, 0, 1); /* type */
- rc = spca504B_PollingDataReady(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
break;
case BRIDGE_SPCA504:
Size += 3;
@@ -733,8 +737,8 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
break;
case BRIDGE_SPCA504C:
/* capture mode */
- reg_w_riv(dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
- reg_w_riv(dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
+ reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
+ reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
break;
}
}
@@ -762,37 +766,33 @@ static void spca504B_setQtable(struct gspca_dev *gspca_dev)
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
u16 reg;
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
- reg_w_riv(dev, 0x00, reg, sd->brightness);
+ reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
u16 reg;
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
- reg_w_riv(dev, 0x00, reg, sd->contrast);
+ reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
u16 reg;
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
- reg_w_riv(dev, 0x00, reg, sd->colors);
+ reg_w_riv(gspca_dev, 0x00, reg, sd->colors);
}
static void init_ctl_reg(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
int pollreg = 1;
setbrightness(gspca_dev);
@@ -807,14 +807,14 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev)
default:
/* case BRIDGE_SPCA533: */
/* case BRIDGE_SPCA504B: */
- reg_w_riv(dev, 0, 0x00, 0x21ad); /* hue */
- reg_w_riv(dev, 0, 0x01, 0x21ac); /* sat/hue */
- reg_w_riv(dev, 0, 0x00, 0x21a3); /* gamma */
+ reg_w_riv(gspca_dev, 0, 0x00, 0x21ad); /* hue */
+ reg_w_riv(gspca_dev, 0, 0x01, 0x21ac); /* sat/hue */
+ reg_w_riv(gspca_dev, 0, 0x00, 0x21a3); /* gamma */
break;
case BRIDGE_SPCA536:
- reg_w_riv(dev, 0, 0x40, 0x20f5);
- reg_w_riv(dev, 0, 0x01, 0x20f4);
- reg_w_riv(dev, 0, 0x00, 0x2089);
+ reg_w_riv(gspca_dev, 0, 0x40, 0x20f5);
+ reg_w_riv(gspca_dev, 0, 0x01, 0x20f4);
+ reg_w_riv(gspca_dev, 0, 0x00, 0x2089);
break;
}
if (pollreg)
@@ -881,18 +881,17 @@ static int sd_config(struct gspca_dev *gspca_dev,
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
- int i, err_code;
+ int i;
u8 info[6];
switch (sd->bridge) {
case BRIDGE_SPCA504B:
- reg_w_riv(dev, 0x1d, 0x00, 0);
- reg_w_riv(dev, 0, 0x01, 0x2306);
- reg_w_riv(dev, 0, 0x00, 0x0d04);
- reg_w_riv(dev, 0, 0x00, 0x2000);
- reg_w_riv(dev, 0, 0x13, 0x2301);
- reg_w_riv(dev, 0, 0x00, 0x2306);
+ reg_w_riv(gspca_dev, 0x1d, 0x00, 0);
+ reg_w_riv(gspca_dev, 0, 0x01, 0x2306);
+ reg_w_riv(gspca_dev, 0, 0x00, 0x0d04);
+ reg_w_riv(gspca_dev, 0, 0x00, 0x2000);
+ reg_w_riv(gspca_dev, 0, 0x13, 0x2301);
+ reg_w_riv(gspca_dev, 0, 0x00, 0x2306);
/* fall thru */
case BRIDGE_SPCA533:
spca504B_PollingDataReady(gspca_dev);
@@ -904,13 +903,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w_1(gspca_dev, 0x24, 0, 0, 0);
reg_r(gspca_dev, 0x24, 0, 1);
spca504B_PollingDataReady(gspca_dev);
- reg_w_riv(dev, 0x34, 0, 0);
+ reg_w_riv(gspca_dev, 0x34, 0, 0);
spca504B_WaitCmdStatus(gspca_dev);
break;
case BRIDGE_SPCA504C: /* pccam600 */
PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)");
- reg_w_riv(dev, 0xe0, 0x0000, 0x0000);
- reg_w_riv(dev, 0xe0, 0x0000, 0x0001); /* reset */
+ reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000);
+ reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001); /* reset */
spca504_wait_status(gspca_dev);
if (sd->subtype == LogitechClickSmart420)
write_vector(gspca_dev,
@@ -919,12 +918,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
else
write_vector(gspca_dev, spca504_pccam600_open_data,
ARRAY_SIZE(spca504_pccam600_open_data));
- err_code = spca50x_setup_qtable(gspca_dev,
- qtable_creative_pccam);
- if (err_code < 0) {
- PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed");
- return err_code;
- }
+ setup_qtable(gspca_dev, qtable_creative_pccam);
break;
default:
/* case BRIDGE_SPCA504: */
@@ -958,29 +952,24 @@ static int sd_init(struct gspca_dev *gspca_dev)
6, 0, 0x86, 1); */
/* spca504A_acknowledged_command (gspca_dev, 0x24,
0, 0, 0x9D, 1); */
- reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */
- reg_w_riv(dev, 0x00, 0x2310, 0x05);
+ reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
+ /* L92 sno1t.txt */
+ reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
spca504A_acknowledged_command(gspca_dev, 0x01,
0x0f, 0, 0xff, 0);
}
/* setup qtable */
- reg_w_riv(dev, 0, 0x2000, 0);
- reg_w_riv(dev, 0, 0x2883, 1);
- err_code = spca50x_setup_qtable(gspca_dev,
- qtable_spca504_default);
- if (err_code < 0) {
- PDEBUG(D_ERR, "spca50x_setup_qtable failed");
- return err_code;
- }
+ reg_w_riv(gspca_dev, 0, 0x2000, 0);
+ reg_w_riv(gspca_dev, 0, 0x2883, 1);
+ setup_qtable(gspca_dev, qtable_spca504_default);
break;
}
- return 0;
+ 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 enable;
int i;
u8 info[6];
@@ -1005,13 +994,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
case MegapixV4:
case LogitechClickSmart820:
case MegaImageVI:
- reg_w_riv(dev, 0xf0, 0, 0);
+ reg_w_riv(gspca_dev, 0xf0, 0, 0);
spca504B_WaitCmdStatus(gspca_dev);
reg_r(gspca_dev, 0xf0, 4, 0);
spca504B_WaitCmdStatus(gspca_dev);
break;
default:
- reg_w_riv(dev, 0x31, 0, 0x04);
+ reg_w_riv(gspca_dev, 0x31, 0, 0x04);
spca504B_WaitCmdStatus(gspca_dev);
spca504B_PollingDataReady(gspca_dev);
break;
@@ -1048,8 +1037,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
}
spca504B_SetSizeType(gspca_dev);
- reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */
- reg_w_riv(dev, 0x00, 0x2310, 0x05);
+ reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
+ /* L92 sno1t.txt */
+ reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
break;
case BRIDGE_SPCA504C:
if (sd->subtype == LogitechClickSmart420) {
@@ -1061,36 +1051,37 @@ static int sd_start(struct gspca_dev *gspca_dev)
ARRAY_SIZE(spca504_pccam600_init_data));
}
enable = (sd->autogain ? 0x04 : 0x01);
- reg_w_riv(dev, 0x0c, 0x0000, enable); /* auto exposure */
- reg_w_riv(dev, 0xb0, 0x0000, enable); /* auto whiteness */
+ reg_w_riv(gspca_dev, 0x0c, 0x0000, enable);
+ /* auto exposure */
+ reg_w_riv(gspca_dev, 0xb0, 0x0000, enable);
+ /* auto whiteness */
/* set default exposure compensation and whiteness balance */
- reg_w_riv(dev, 0x30, 0x0001, 800); /* ~ 20 fps */
- reg_w_riv(dev, 0x30, 0x0002, 1600);
+ reg_w_riv(gspca_dev, 0x30, 0x0001, 800); /* ~ 20 fps */
+ reg_w_riv(gspca_dev, 0x30, 0x0002, 1600);
spca504B_SetSizeType(gspca_dev);
break;
}
init_ctl_reg(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
switch (sd->bridge) {
default:
/* case BRIDGE_SPCA533: */
/* case BRIDGE_SPCA536: */
/* case BRIDGE_SPCA504B: */
- reg_w_riv(dev, 0x31, 0, 0);
+ reg_w_riv(gspca_dev, 0x31, 0, 0);
spca504B_WaitCmdStatus(gspca_dev);
spca504B_PollingDataReady(gspca_dev);
break;
case BRIDGE_SPCA504:
case BRIDGE_SPCA504C:
- reg_w_riv(dev, 0x00, 0x2000, 0x0000);
+ reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000);
if (sd->subtype == AiptekMiniPenCam13) {
/* spca504a aiptek */
@@ -1102,7 +1093,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
0x0f, 0x00, 0xff, 1);
} else {
spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
- reg_w_riv(dev, 0x01, 0x000f, 0x0000);
+ reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000);
}
break;
}
@@ -1216,7 +1207,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
sd->brightness = val;
if (gspca_dev->streaming)
setbrightness(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1234,7 +1225,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)
@@ -1252,7 +1243,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
sd->colors = val;
if (gspca_dev->streaming)
setcolors(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
@@ -1292,7 +1283,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,
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 69e5dc4fc9de..1a800fc1c00e 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -5345,9 +5345,6 @@ static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
- {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
-
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -5364,27 +5361,27 @@ static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
{0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x95, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{}
};
static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
- {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -5400,13 +5397,15 @@ static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x95, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{}
};
static const struct usb_action tas5130cxx_50HZ[] = {
@@ -6424,11 +6423,11 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
if (retword != 0)
return 0x0e; /* PAS202BCB */
- start_2wr_probe(dev, 0x02); /* ?? */
+ start_2wr_probe(dev, 0x02); /* TAS5130C */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
- return 0x02; /* ?? */
+ return 0x02; /* TAS5130C */
ov_check:
reg_r(gspca_dev, 0x0010); /* ?? */
reg_r(gspca_dev, 0x0010);
@@ -6505,6 +6504,8 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
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;
@@ -6515,7 +6516,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
}
}
- reg_w(dev, 0x01, 0x0000); /* check ?? */
+ reg_w(dev, 0x01, 0x0000); /* check PB0330 */
reg_w(dev, 0x01, 0x0001);
reg_w(dev, 0xdd, 0x008b);
reg_w(dev, 0x0a, 0x0010);
@@ -6524,7 +6525,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
retword = i2c_read(gspca_dev, 0x00);
if (retword != 0) {
PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
- return 0x0a; /* ?? */
+ return 0x0a; /* PB0330 */
}
reg_w(dev, 0x01, 0x0000);
@@ -6673,6 +6674,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
PDEBUG(D_PROBE, "Find Sensor HV7131B");
sd->sensor = SENSOR_HV7131B;
break;
+ case 0x02:
+ PDEBUG(D_PROBE, "Sensor TAS5130C");
+ sd->sensor = SENSOR_TAS5130CXX;
+ break;
case 0x04:
PDEBUG(D_PROBE, "Find Sensor CS2102");
sd->sensor = SENSOR_CS2102;
@@ -6866,11 +6871,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_GC0305:
case SENSOR_OV7620:
case SENSOR_PO2030:
+ case SENSOR_TAS5130CXX:
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)
+ break;
reg_w(dev, 0x0d, 0x003a);
reg_w(dev, 0x02, 0x003b);
reg_w(dev, 0x00, 0x0038);
@@ -6887,6 +6895,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
case SENSOR_PAS202B:
case SENSOR_GC0305:
+ case SENSOR_TAS5130CXX:
reg_r(gspca_dev, 0x0008);
/* fall thru */
case SENSOR_PO2030:
@@ -6928,6 +6937,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(dev, 0x40, 0x0117);
break;
case SENSOR_GC0305:
+ case SENSOR_TAS5130CXX:
reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
reg_w(dev, 0x15, 0x01ae);
/* fall thru */
@@ -7220,7 +7230,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
{USB_DEVICE(0x0ac8, 0x301b)},
{USB_DEVICE(0x0ac8, 0x303b)},
- {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250},
+ {USB_DEVICE(0x0ac8, 0x305b)},
{USB_DEVICE(0x0ac8, 0x307b)},
{USB_DEVICE(0x10fd, 0x0128)},
{USB_DEVICE(0x10fd, 0x804d)},
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index 1c9bc94c905c..51f393d03a46 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -145,7 +145,7 @@ static int device_authorization(struct hdpvr_device *dev)
#ifdef HDPVR_DEBUG
else {
hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf,
- sizeof(print_buf), 0);
+ 5*buf_size+1, 0);
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
"Status request returned, len %d: %s\n",
ret, print_buf);
@@ -168,13 +168,13 @@ static int device_authorization(struct hdpvr_device *dev)
response = dev->usbc_buf+38;
#ifdef HDPVR_DEBUG
- hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0);
+ hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0);
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n",
print_buf);
#endif
challenge(response);
#ifdef HDPVR_DEBUG
- hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0);
+ hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0);
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n",
print_buf);
#endif
@@ -376,8 +376,8 @@ static int hdpvr_probe(struct usb_interface *interface,
usb_set_intfdata(interface, dev);
/* let the user know what node this device is now attached to */
- v4l2_info(&dev->v4l2_dev, "device now attached to /dev/video%d\n",
- dev->video_dev->minor);
+ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n",
+ video_device_node_name(dev->video_dev));
return 0;
error:
@@ -391,13 +391,10 @@ error:
static void hdpvr_disconnect(struct usb_interface *interface)
{
struct hdpvr_device *dev;
- int minor;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
- minor = dev->video_dev->minor;
-
/* prevent more I/O from starting and stop any ongoing */
mutex_lock(&dev->io_mutex);
dev->status = STATUS_DISCONNECTED;
@@ -425,7 +422,8 @@ static void hdpvr_disconnect(struct usb_interface *interface)
atomic_dec(&dev_nr);
- v4l2_info(&dev->v4l2_dev, "device /dev/video%d disconnected\n", minor);
+ v4l2_info(&dev->v4l2_dev, "device %s disconnected\n",
+ video_device_node_name(dev->video_dev));
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev->usbc_buf);
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index b5439cabb381..fdd782039e9d 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -523,7 +523,7 @@ static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
mutex_lock(&dev->io_mutex);
- if (video_is_unregistered(dev->video_dev)) {
+ if (!video_is_registered(dev->video_dev)) {
mutex_unlock(&dev->io_mutex);
return -EIO;
}
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 64360d26b32d..b86e35386cee 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -353,6 +353,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir_type = IR_TYPE_RC5;
ir_codes = &ir_codes_fusionhdtv_mce_table;
break;
+ case 0x0b:
case 0x47:
case 0x71:
if (adap->id == I2C_HW_B_CX2388x ||
@@ -422,7 +423,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Make sure we are all setup before going on */
if (!name || !ir->get_key || !ir_type || !ir_codes) {
- dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n",
+ dprintk(1, ": Unsupported device at address 0x%02x\n",
addr);
err = -ENODEV;
goto err_out_free;
@@ -437,7 +438,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_name(&client->dev));
/* init + register input device */
- err = ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type);
if (err < 0)
goto err_out_free;
@@ -445,7 +446,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
input_dev->name = ir->name;
input_dev->phys = ir->phys;
- err = input_register_device(ir->input);
+ err = ir_input_register(ir->input, ir->ir_codes);
if (err)
goto err_out_free;
@@ -459,8 +460,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
return 0;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return err;
}
@@ -473,8 +472,7 @@ static int ir_remove(struct i2c_client *client)
cancel_delayed_work_sync(&ir->work);
/* unregister device */
- ir_input_free(ir->input);
- input_unregister_device(ir->input);
+ ir_input_unregister(ir->input);
/* free memory */
kfree(ir);
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index e707ef3086b2..babcabd73c08 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -985,8 +985,8 @@ int ivtv_v4l2_open(struct file *filp)
mutex_lock(&itv->serialize_lock);
if (ivtv_init_on_first_open(itv)) {
- IVTV_ERR("Failed to initialize on minor %d\n",
- vdev->minor);
+ IVTV_ERR("Failed to initialize on device %s\n",
+ video_device_node_name(vdev));
mutex_unlock(&itv->serialize_lock);
return -ENXIO;
}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 67699e3f2aaa..e12c6022373e 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -245,6 +245,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int vfl_type = ivtv_stream_info[type].vfl_type;
+ const char *name;
int num;
if (s->vdev == NULL)
@@ -268,24 +269,24 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
s->vdev = NULL;
return -ENOMEM;
}
- num = s->vdev->num;
+ name = video_device_node_name(s->vdev);
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- IVTV_INFO("Registered device video%d for %s (%d kB)\n",
- num, s->name, itv->options.kilobytes[type]);
+ IVTV_INFO("Registered device %s for %s (%d kB)\n",
+ name, s->name, itv->options.kilobytes[type]);
break;
case VFL_TYPE_RADIO:
- IVTV_INFO("Registered device radio%d for %s\n",
- num, s->name);
+ IVTV_INFO("Registered device %s for %s\n",
+ name, s->name);
break;
case VFL_TYPE_VBI:
if (itv->options.kilobytes[type])
- IVTV_INFO("Registered device vbi%d for %s (%d kB)\n",
- num, s->name, itv->options.kilobytes[type]);
+ IVTV_INFO("Registered device %s for %s (%d kB)\n",
+ name, s->name, itv->options.kilobytes[type]);
else
- IVTV_INFO("Registered device vbi%d for %s\n",
- num, s->name);
+ IVTV_INFO("Registered device %s for %s\n",
+ name, s->name);
break;
}
return 0;
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index 01e1eefcf1eb..6ffa64cd1c6d 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -1681,7 +1681,6 @@ static struct video_device meye_template = {
.fops = &meye_fops,
.ioctl_ops = &meye_ioctl_ops,
.release = video_device_release,
- .minor = -1,
};
#ifdef CONFIG_PM
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 45388d2ce2fd..b62c0bd3f8ea 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -17,9 +17,11 @@
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
-/* mt9m001 i2c address 0x5d
+/*
+ * mt9m001 i2c address 0x5d
* The platform has to define ctruct i2c_board_info objects and link to them
- * from struct soc_camera_link */
+ * from struct soc_camera_link
+ */
/* mt9m001 selected register addresses */
#define MT9M001_CHIP_VERSION 0x00
@@ -46,42 +48,50 @@
#define MT9M001_COLUMN_SKIP 20
#define MT9M001_ROW_SKIP 12
-static const struct soc_camera_data_format mt9m001_colour_formats[] = {
- /* Order important: first natively supported,
- * second supported with a GPIO extender */
- {
- .name = "Bayer (sRGB) 10 bit",
- .depth = 10,
- .fourcc = V4L2_PIX_FMT_SBGGR16,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .name = "Bayer (sRGB) 8 bit",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }
+/* MT9M001 has only one fixed colorspace per pixelcode */
+struct mt9m001_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9m001_datafmt *mt9m001_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9m001_datafmt mt9m001_colour_fmts[] = {
+ /*
+ * Order important: first natively supported,
+ * second supported with a GPIO extender
+ */
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
-static const struct soc_camera_data_format mt9m001_monochrome_formats[] = {
+static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
/* Order important - see above */
- {
- .name = "Monochrome 10 bit",
- .depth = 10,
- .fourcc = V4L2_PIX_FMT_Y16,
- }, {
- .name = "Monochrome 8 bit",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_GREY,
- },
+ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG},
};
struct mt9m001 {
struct v4l2_subdev subdev;
struct v4l2_rect rect; /* Sensor window */
- __u32 fourcc;
+ const struct mt9m001_datafmt *fmt;
+ const struct mt9m001_datafmt *fmts;
+ int num_fmts;
int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
unsigned int gain;
unsigned int exposure;
+ unsigned short y_skip_top; /* Lines to skip at the top */
unsigned char autoexposure;
};
@@ -204,8 +214,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
const u16 hblank = 9, vblank = 25;
unsigned int total_h;
- if (mt9m001->fourcc == V4L2_PIX_FMT_SBGGR8 ||
- mt9m001->fourcc == V4L2_PIX_FMT_SBGGR16)
+ if (mt9m001->fmts == mt9m001_colour_fmts)
/*
* Bayer format - even number of rows for simplicity,
* but let the user play with the top row.
@@ -222,15 +231,17 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
soc_camera_limit_side(&rect.top, &rect.height,
MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
- total_h = rect.height + icd->y_skip_top + vblank;
+ total_h = rect.height + mt9m001->y_skip_top + vblank;
/* Blanking and start values - default... */
ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
if (!ret)
ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
- /* The caller provides a supported format, as verified per
- * call to icd->try_fmt() */
+ /*
+ * The caller provides a supported format, as verified per
+ * call to icd->try_fmt()
+ */
if (!ret)
ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
if (!ret)
@@ -239,7 +250,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
if (!ret)
ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
- rect.height + icd->y_skip_top - 1);
+ rect.height + mt9m001->y_skip_top - 1);
if (!ret && mt9m001->autoexposure) {
ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
if (!ret) {
@@ -283,32 +294,32 @@ static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int mt9m001_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9m001_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- pix->width = mt9m001->rect.width;
- pix->height = mt9m001->rect.height;
- pix->pixelformat = mt9m001->fourcc;
- pix->field = V4L2_FIELD_NONE;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->width = mt9m001->rect.width;
+ mf->height = mt9m001->rect.height;
+ mf->code = mt9m001->fmt->code;
+ mf->colorspace = mt9m001->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
return 0;
}
-static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9m001_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_crop a = {
.c = {
.left = mt9m001->rect.left,
.top = mt9m001->rect.top,
- .width = pix->width,
- .height = pix->height,
+ .width = mf->width,
+ .height = mf->height,
},
};
int ret;
@@ -316,28 +327,39 @@ static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
/* No support for scaling so far, just crop. TODO: use skipping */
ret = mt9m001_s_crop(sd, &a);
if (!ret) {
- pix->width = mt9m001->rect.width;
- pix->height = mt9m001->rect.height;
- mt9m001->fourcc = pix->pixelformat;
+ mf->width = mt9m001->rect.width;
+ mf->height = mt9m001->rect.height;
+ mt9m001->fmt = mt9m001_find_datafmt(mf->code,
+ mt9m001->fmts, mt9m001->num_fmts);
+ mf->colorspace = mt9m001->fmt->colorspace;
}
return ret;
}
-static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9m001_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ const struct mt9m001_datafmt *fmt;
- v4l_bound_align_image(&pix->width, MT9M001_MIN_WIDTH,
+ v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH,
MT9M001_MAX_WIDTH, 1,
- &pix->height, MT9M001_MIN_HEIGHT + icd->y_skip_top,
- MT9M001_MAX_HEIGHT + icd->y_skip_top, 0, 0);
+ &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top,
+ MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0);
+
+ if (mt9m001->fmts == mt9m001_colour_fmts)
+ mf->height = ALIGN(mf->height - 1, 2);
- if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
- pix->pixelformat == V4L2_PIX_FMT_SBGGR16)
- pix->height = ALIGN(pix->height - 1, 2);
+ fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts,
+ mt9m001->num_fmts);
+ if (!fmt) {
+ fmt = mt9m001->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->colorspace = fmt->colorspace;
return 0;
}
@@ -552,7 +574,7 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
if (ctrl->value) {
const u16 vblank = 25;
unsigned int total_h = mt9m001->rect.height +
- icd->y_skip_top + vblank;
+ mt9m001->y_skip_top + vblank;
if (reg_write(client, MT9M001_SHUTTER_WIDTH,
total_h) < 0)
return -EIO;
@@ -568,8 +590,10 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return 0;
}
-/* Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one */
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
static int mt9m001_video_probe(struct soc_camera_device *icd,
struct i2c_client *client)
{
@@ -579,8 +603,10 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
unsigned long flags;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant. */
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
if (!icd->dev.parent ||
to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
return -ENODEV;
@@ -597,11 +623,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
case 0x8411:
case 0x8421:
mt9m001->model = V4L2_IDENT_MT9M001C12ST;
- icd->formats = mt9m001_colour_formats;
+ mt9m001->fmts = mt9m001_colour_fmts;
break;
case 0x8431:
mt9m001->model = V4L2_IDENT_MT9M001C12STM;
- icd->formats = mt9m001_monochrome_formats;
+ mt9m001->fmts = mt9m001_monochrome_fmts;
break;
default:
dev_err(&client->dev,
@@ -609,7 +635,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
return -ENODEV;
}
- icd->num_formats = 0;
+ mt9m001->num_fmts = 0;
/*
* This is a 10bit sensor, so by default we only allow 10bit.
@@ -622,14 +648,14 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
flags = SOCAM_DATAWIDTH_10;
if (flags & SOCAM_DATAWIDTH_10)
- icd->num_formats++;
+ mt9m001->num_fmts++;
else
- icd->formats++;
+ mt9m001->fmts++;
if (flags & SOCAM_DATAWIDTH_8)
- icd->num_formats++;
+ mt9m001->num_fmts++;
- mt9m001->fourcc = icd->formats->fourcc;
+ mt9m001->fmt = &mt9m001->fmts[0];
dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
data == 0x8431 ? "C12STM" : "C12ST");
@@ -655,6 +681,16 @@ static void mt9m001_video_remove(struct soc_camera_device *icd)
icl->free_bus(icl);
}
+static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ *lines = mt9m001->y_skip_top;
+
+ return 0;
+}
+
static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
.g_ctrl = mt9m001_g_ctrl,
.s_ctrl = mt9m001_s_ctrl,
@@ -665,19 +701,38 @@ static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
#endif
};
+static int mt9m001_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ if ((unsigned int)index >= mt9m001->num_fmts)
+ return -EINVAL;
+
+ *code = mt9m001->fmts[index].code;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.s_stream = mt9m001_s_stream,
- .s_fmt = mt9m001_s_fmt,
- .g_fmt = mt9m001_g_fmt,
- .try_fmt = mt9m001_try_fmt,
+ .s_mbus_fmt = mt9m001_s_fmt,
+ .g_mbus_fmt = mt9m001_g_fmt,
+ .try_mbus_fmt = mt9m001_try_fmt,
.s_crop = mt9m001_s_crop,
.g_crop = mt9m001_g_crop,
.cropcap = mt9m001_cropcap,
+ .enum_mbus_fmt = mt9m001_enum_fmt,
+};
+
+static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9m001_g_skip_top_lines,
};
static struct v4l2_subdev_ops mt9m001_subdev_ops = {
.core = &mt9m001_subdev_core_ops,
.video = &mt9m001_subdev_video_ops,
+ .sensor = &mt9m001_subdev_sensor_ops,
};
static int mt9m001_probe(struct i2c_client *client,
@@ -714,15 +769,17 @@ static int mt9m001_probe(struct i2c_client *client,
/* Second stage probe - when a capture adapter is there */
icd->ops = &mt9m001_ops;
- icd->y_skip_top = 0;
+ mt9m001->y_skip_top = 0;
mt9m001->rect.left = MT9M001_COLUMN_SKIP;
mt9m001->rect.top = MT9M001_ROW_SKIP;
mt9m001->rect.width = MT9M001_MAX_WIDTH;
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
- /* Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width */
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
mt9m001->autoexposure = 1;
ret = mt9m001_video_probe(icd, client);
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index 90da699601ea..d35f536f9fc3 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -123,23 +123,34 @@
#define MT9M111_MAX_HEIGHT 1024
#define MT9M111_MAX_WIDTH 1280
-#define COL_FMT(_name, _depth, _fourcc, _colorspace) \
- { .name = _name, .depth = _depth, .fourcc = _fourcc, \
- .colorspace = _colorspace }
-#define RGB_FMT(_name, _depth, _fourcc) \
- COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB)
-#define JPG_FMT(_name, _depth, _fourcc) \
- COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_JPEG)
-
-static const struct soc_camera_data_format mt9m111_colour_formats[] = {
- JPG_FMT("CbYCrY 16 bit", 16, V4L2_PIX_FMT_UYVY),
- JPG_FMT("CrYCbY 16 bit", 16, V4L2_PIX_FMT_VYUY),
- JPG_FMT("YCbYCr 16 bit", 16, V4L2_PIX_FMT_YUYV),
- JPG_FMT("YCrYCb 16 bit", 16, V4L2_PIX_FMT_YVYU),
- RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565),
- RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555),
- RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16),
- RGB_FMT("Bayer (sRGB) 8 bit", 8, V4L2_PIX_FMT_SBGGR8),
+/* MT9M111 has only one fixed colorspace per pixelcode */
+struct mt9m111_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9m111_datafmt *mt9m111_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9m111_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
+ {V4L2_MBUS_FMT_YUYV8_2X8_LE, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8_LE, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YUYV8_2X8_BE, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8_BE, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
};
enum mt9m111_context {
@@ -152,7 +163,7 @@ struct mt9m111 {
int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
enum mt9m111_context context;
struct v4l2_rect rect;
- u32 pixfmt;
+ const struct mt9m111_datafmt *fmt;
unsigned int gain;
unsigned char autoexposure;
unsigned char datawidth;
@@ -258,8 +269,8 @@ static int mt9m111_setup_rect(struct i2c_client *client,
int width = rect->width;
int height = rect->height;
- if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 ||
- mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)
+ if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE)
is_raw_format = 1;
else
is_raw_format = 0;
@@ -307,7 +318,8 @@ static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt)
static int mt9m111_setfmt_bayer8(struct i2c_client *client)
{
- return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER);
+ return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_RGB);
}
static int mt9m111_setfmt_bayer10(struct i2c_client *client)
@@ -401,8 +413,8 @@ static int mt9m111_make_rect(struct i2c_client *client,
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
- if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 ||
- mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) {
+ if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
/* Bayer format - even size lengths */
rect->width = ALIGN(rect->width, 2);
rect->height = ALIGN(rect->height, 2);
@@ -460,120 +472,139 @@ static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int mt9m111_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9m111_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9m111 *mt9m111 = to_mt9m111(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- pix->width = mt9m111->rect.width;
- pix->height = mt9m111->rect.height;
- pix->pixelformat = mt9m111->pixfmt;
- pix->field = V4L2_FIELD_NONE;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->width = mt9m111->rect.width;
+ mf->height = mt9m111->rect.height;
+ mf->code = mt9m111->fmt->code;
+ mf->field = V4L2_FIELD_NONE;
return 0;
}
-static int mt9m111_set_pixfmt(struct i2c_client *client, u32 pixfmt)
+static int mt9m111_set_pixfmt(struct i2c_client *client,
+ enum v4l2_mbus_pixelcode code)
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
- switch (pixfmt) {
- case V4L2_PIX_FMT_SBGGR8:
+ switch (code) {
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
ret = mt9m111_setfmt_bayer8(client);
break;
- case V4L2_PIX_FMT_SBGGR16:
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
ret = mt9m111_setfmt_bayer10(client);
break;
- case V4L2_PIX_FMT_RGB555:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
ret = mt9m111_setfmt_rgb555(client);
break;
- case V4L2_PIX_FMT_RGB565:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
ret = mt9m111_setfmt_rgb565(client);
break;
- case V4L2_PIX_FMT_UYVY:
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
mt9m111->swap_yuv_y_chromas = 0;
mt9m111->swap_yuv_cb_cr = 0;
ret = mt9m111_setfmt_yuv(client);
break;
- case V4L2_PIX_FMT_VYUY:
+ case V4L2_MBUS_FMT_YVYU8_2X8_BE:
mt9m111->swap_yuv_y_chromas = 0;
mt9m111->swap_yuv_cb_cr = 1;
ret = mt9m111_setfmt_yuv(client);
break;
- case V4L2_PIX_FMT_YUYV:
+ case V4L2_MBUS_FMT_YUYV8_2X8_LE:
mt9m111->swap_yuv_y_chromas = 1;
mt9m111->swap_yuv_cb_cr = 0;
ret = mt9m111_setfmt_yuv(client);
break;
- case V4L2_PIX_FMT_YVYU:
+ case V4L2_MBUS_FMT_YVYU8_2X8_LE:
mt9m111->swap_yuv_y_chromas = 1;
mt9m111->swap_yuv_cb_cr = 1;
ret = mt9m111_setfmt_yuv(client);
break;
default:
dev_err(&client->dev, "Pixel format not handled : %x\n",
- pixfmt);
+ code);
ret = -EINVAL;
}
- if (!ret)
- mt9m111->pixfmt = pixfmt;
-
return ret;
}
-static int mt9m111_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9m111_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
+ const struct mt9m111_datafmt *fmt;
struct mt9m111 *mt9m111 = to_mt9m111(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_rect rect = {
.left = mt9m111->rect.left,
.top = mt9m111->rect.top,
- .width = pix->width,
- .height = pix->height,
+ .width = mf->width,
+ .height = mf->height,
};
int ret;
+ fmt = mt9m111_find_datafmt(mf->code, mt9m111_colour_fmts,
+ ARRAY_SIZE(mt9m111_colour_fmts));
+ if (!fmt)
+ return -EINVAL;
+
dev_dbg(&client->dev,
- "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", __func__,
- pix->pixelformat, rect.left, rect.top, rect.width, rect.height);
+ "%s code=%x left=%d, top=%d, width=%d, height=%d\n", __func__,
+ mf->code, rect.left, rect.top, rect.width, rect.height);
ret = mt9m111_make_rect(client, &rect);
if (!ret)
- ret = mt9m111_set_pixfmt(client, pix->pixelformat);
- if (!ret)
- mt9m111->rect = rect;
+ ret = mt9m111_set_pixfmt(client, mf->code);
+ if (!ret) {
+ mt9m111->rect = rect;
+ mt9m111->fmt = fmt;
+ mf->colorspace = fmt->colorspace;
+ }
+
return ret;
}
-static int mt9m111_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9m111_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
- struct v4l2_pix_format *pix = &f->fmt.pix;
- bool bayer = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
- pix->pixelformat == V4L2_PIX_FMT_SBGGR16;
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+ const struct mt9m111_datafmt *fmt;
+ bool bayer = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
+
+ fmt = mt9m111_find_datafmt(mf->code, mt9m111_colour_fmts,
+ ARRAY_SIZE(mt9m111_colour_fmts));
+ if (!fmt) {
+ fmt = mt9m111->fmt;
+ mf->code = fmt->code;
+ }
/*
* With Bayer format enforce even side lengths, but let the user play
* with the starting pixel
*/
- if (pix->height > MT9M111_MAX_HEIGHT)
- pix->height = MT9M111_MAX_HEIGHT;
- else if (pix->height < 2)
- pix->height = 2;
+ if (mf->height > MT9M111_MAX_HEIGHT)
+ mf->height = MT9M111_MAX_HEIGHT;
+ else if (mf->height < 2)
+ mf->height = 2;
else if (bayer)
- pix->height = ALIGN(pix->height, 2);
+ mf->height = ALIGN(mf->height, 2);
- if (pix->width > MT9M111_MAX_WIDTH)
- pix->width = MT9M111_MAX_WIDTH;
- else if (pix->width < 2)
- pix->width = 2;
+ if (mf->width > MT9M111_MAX_WIDTH)
+ mf->width = MT9M111_MAX_WIDTH;
+ else if (mf->width < 2)
+ mf->width = 2;
else if (bayer)
- pix->width = ALIGN(pix->width, 2);
+ mf->width = ALIGN(mf->width, 2);
+
+ mf->colorspace = fmt->colorspace;
return 0;
}
@@ -863,7 +894,7 @@ static int mt9m111_restore_state(struct i2c_client *client)
struct mt9m111 *mt9m111 = to_mt9m111(client);
mt9m111_set_context(client, mt9m111->context);
- mt9m111_set_pixfmt(client, mt9m111->pixfmt);
+ mt9m111_set_pixfmt(client, mt9m111->fmt->code);
mt9m111_setup_rect(client, &mt9m111->rect);
mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
@@ -952,9 +983,6 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
goto ei2c;
}
- icd->formats = mt9m111_colour_formats;
- icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
-
dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
ei2c:
@@ -971,13 +999,24 @@ static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
#endif
};
+static int mt9m111_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if ((unsigned int)index >= ARRAY_SIZE(mt9m111_colour_fmts))
+ return -EINVAL;
+
+ *code = mt9m111_colour_fmts[index].code;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
- .s_fmt = mt9m111_s_fmt,
- .g_fmt = mt9m111_g_fmt,
- .try_fmt = mt9m111_try_fmt,
+ .s_mbus_fmt = mt9m111_s_fmt,
+ .g_mbus_fmt = mt9m111_g_fmt,
+ .try_mbus_fmt = mt9m111_try_fmt,
.s_crop = mt9m111_s_crop,
.g_crop = mt9m111_g_crop,
.cropcap = mt9m111_cropcap,
+ .enum_mbus_fmt = mt9m111_enum_fmt,
};
static struct v4l2_subdev_ops mt9m111_subdev_ops = {
@@ -1019,12 +1058,12 @@ static int mt9m111_probe(struct i2c_client *client,
/* Second stage probe - when a capture adapter is there */
icd->ops = &mt9m111_ops;
- icd->y_skip_top = 0;
mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
mt9m111->rect.width = MT9M111_MAX_WIDTH;
mt9m111->rect.height = MT9M111_MAX_HEIGHT;
+ mt9m111->fmt = &mt9m111_colour_fmts[0];
ret = mt9m111_video_probe(icd, client);
if (ret) {
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index 6966f644977e..a9061bff79b2 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -17,9 +17,11 @@
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
-/* mt9t031 i2c address 0x5d
+/*
+ * mt9t031 i2c address 0x5d
* The platform has to define i2c_board_info and link to it from
- * struct soc_camera_link */
+ * struct soc_camera_link
+ */
/* mt9t031 selected register addresses */
#define MT9T031_CHIP_VERSION 0x00
@@ -58,15 +60,6 @@
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
SOCAM_MASTER | SOCAM_DATAWIDTH_10)
-static const struct soc_camera_data_format mt9t031_colour_formats[] = {
- {
- .name = "Bayer (sRGB) 10 bit",
- .depth = 10,
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }
-};
-
struct mt9t031 {
struct v4l2_subdev subdev;
struct v4l2_rect rect; /* Sensor window */
@@ -74,6 +67,7 @@ struct mt9t031 {
u16 xskip;
u16 yskip;
unsigned int gain;
+ unsigned short y_skip_top; /* Lines to skip at the top */
unsigned int exposure;
unsigned char autoexposure;
};
@@ -207,6 +201,71 @@ static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
}
+enum {
+ MT9T031_CTRL_VFLIP,
+ MT9T031_CTRL_HFLIP,
+ MT9T031_CTRL_GAIN,
+ MT9T031_CTRL_EXPOSURE,
+ MT9T031_CTRL_EXPOSURE_AUTO,
+};
+
+static const struct v4l2_queryctrl mt9t031_controls[] = {
+ [MT9T031_CTRL_VFLIP] = {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ [MT9T031_CTRL_HFLIP] = {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Horizontally",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ [MT9T031_CTRL_GAIN] = {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },
+ [MT9T031_CTRL_EXPOSURE] = {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 1,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 255,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },
+ [MT9T031_CTRL_EXPOSURE_AUTO] = {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ }
+};
+
+static struct soc_camera_ops mt9t031_ops = {
+ .set_bus_param = mt9t031_set_bus_param,
+ .query_bus_param = mt9t031_query_bus_param,
+ .controls = mt9t031_controls,
+ .num_controls = ARRAY_SIZE(mt9t031_controls),
+};
+
/* target must be _even_ */
static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
{
@@ -226,10 +285,9 @@ static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
}
/* rect is the sensor rectangle, the caller guarantees parameter validity */
-static int mt9t031_set_params(struct soc_camera_device *icd,
+static int mt9t031_set_params(struct i2c_client *client,
struct v4l2_rect *rect, u16 xskip, u16 yskip)
{
- struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
struct mt9t031 *mt9t031 = to_mt9t031(client);
int ret;
u16 xbin, ybin;
@@ -291,8 +349,10 @@ static int mt9t031_set_params(struct soc_camera_device *icd,
dev_dbg(&client->dev, "new physical left %u, top %u\n",
rect->left, rect->top);
- /* The caller provides a supported format, as guaranteed by
- * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */
+ /*
+ * The caller provides a supported format, as guaranteed by
+ * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap()
+ */
if (ret >= 0)
ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
if (ret >= 0)
@@ -301,15 +361,14 @@ static int mt9t031_set_params(struct soc_camera_device *icd,
ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1);
if (ret >= 0)
ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
- rect->height + icd->y_skip_top - 1);
+ rect->height + mt9t031->y_skip_top - 1);
if (ret >= 0 && mt9t031->autoexposure) {
- unsigned int total_h = rect->height + icd->y_skip_top + vblank;
+ unsigned int total_h = rect->height + mt9t031->y_skip_top + vblank;
ret = set_shutter(client, total_h);
if (ret >= 0) {
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
const struct v4l2_queryctrl *qctrl =
- soc_camera_find_qctrl(icd->ops,
- V4L2_CID_EXPOSURE);
+ &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
(qctrl->maximum - qctrl->minimum)) /
shutter_max + qctrl->minimum;
@@ -334,7 +393,6 @@ static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
struct v4l2_rect rect = a->c;
struct i2c_client *client = sd->priv;
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct soc_camera_device *icd = client->dev.platform_data;
rect.width = ALIGN(rect.width, 2);
rect.height = ALIGN(rect.height, 2);
@@ -345,7 +403,7 @@ static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
soc_camera_limit_side(&rect.top, &rect.height,
MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
- return mt9t031_set_params(icd, &rect, mt9t031->xskip, mt9t031->yskip);
+ return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip);
}
static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
@@ -373,27 +431,26 @@ static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int mt9t031_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9t031_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- pix->width = mt9t031->rect.width / mt9t031->xskip;
- pix->height = mt9t031->rect.height / mt9t031->yskip;
- pix->pixelformat = V4L2_PIX_FMT_SGRBG10;
- pix->field = V4L2_FIELD_NONE;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->width = mt9t031->rect.width / mt9t031->xskip;
+ mf->height = mt9t031->rect.height / mt9t031->yskip;
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->field = V4L2_FIELD_NONE;
return 0;
}
-static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9t031_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct soc_camera_device *icd = client->dev.platform_data;
- struct v4l2_pix_format *pix = &f->fmt.pix;
u16 xskip, yskip;
struct v4l2_rect rect = mt9t031->rect;
@@ -401,24 +458,29 @@ static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
* try_fmt has put width and height within limits.
* S_FMT: use binning and skipping for scaling
*/
- xskip = mt9t031_skip(&rect.width, pix->width, MT9T031_MAX_WIDTH);
- yskip = mt9t031_skip(&rect.height, pix->height, MT9T031_MAX_HEIGHT);
+ xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
+ yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
+
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
/* mt9t031_set_params() doesn't change width and height */
- return mt9t031_set_params(icd, &rect, xskip, yskip);
+ return mt9t031_set_params(client, &rect, xskip, yskip);
}
/*
* If a user window larger than sensor window is requested, we'll increase the
* sensor window.
*/
-static int mt9t031_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9t031_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
v4l_bound_align_image(
- &pix->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
- &pix->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
+ &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
+ &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
+
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
@@ -479,59 +541,6 @@ static int mt9t031_s_register(struct v4l2_subdev *sd,
}
#endif
-static const struct v4l2_queryctrl mt9t031_controls[] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 255,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }
-};
-
-static struct soc_camera_ops mt9t031_ops = {
- .set_bus_param = mt9t031_set_bus_param,
- .query_bus_param = mt9t031_query_bus_param,
- .controls = mt9t031_controls,
- .num_controls = ARRAY_SIZE(mt9t031_controls),
-};
-
static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct i2c_client *client = sd->priv;
@@ -568,15 +577,9 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct i2c_client *client = sd->priv;
struct mt9t031 *mt9t031 = to_mt9t031(client);
- struct soc_camera_device *icd = client->dev.platform_data;
const struct v4l2_queryctrl *qctrl;
int data;
- qctrl = soc_camera_find_qctrl(&mt9t031_ops, ctrl->id);
-
- if (!qctrl)
- return -EINVAL;
-
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->value)
@@ -595,6 +598,7 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return -EIO;
break;
case V4L2_CID_GAIN:
+ qctrl = &mt9t031_controls[MT9T031_CTRL_GAIN];
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
return -EINVAL;
/* See Datasheet Table 7, Gain settings. */
@@ -634,6 +638,7 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
mt9t031->gain = ctrl->value;
break;
case V4L2_CID_EXPOSURE:
+ qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
/* mt9t031 has maximum == default */
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
return -EINVAL;
@@ -657,11 +662,11 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
const u16 vblank = MT9T031_VERTICAL_BLANK;
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
unsigned int total_h = mt9t031->rect.height +
- icd->y_skip_top + vblank;
+ mt9t031->y_skip_top + vblank;
if (set_shutter(client, total_h) < 0)
return -EIO;
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
+ qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
(qctrl->maximum - qctrl->minimum)) /
shutter_max + qctrl->minimum;
@@ -669,15 +674,18 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
} else
mt9t031->autoexposure = 0;
break;
+ default:
+ return -EINVAL;
}
return 0;
}
-/* Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one */
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
static int mt9t031_video_probe(struct i2c_client *client)
{
- struct soc_camera_device *icd = client->dev.platform_data;
struct mt9t031 *mt9t031 = to_mt9t031(client);
s32 data;
int ret;
@@ -692,8 +700,6 @@ static int mt9t031_video_probe(struct i2c_client *client)
switch (data) {
case 0x1621:
mt9t031->model = V4L2_IDENT_MT9T031;
- icd->formats = mt9t031_colour_formats;
- icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats);
break;
default:
dev_err(&client->dev,
@@ -714,6 +720,16 @@ static int mt9t031_video_probe(struct i2c_client *client)
return ret;
}
+static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ *lines = mt9t031->y_skip_top;
+
+ return 0;
+}
+
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
.g_ctrl = mt9t031_g_ctrl,
.s_ctrl = mt9t031_s_ctrl,
@@ -724,19 +740,35 @@ static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
#endif
};
+static int mt9t031_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
.s_stream = mt9t031_s_stream,
- .s_fmt = mt9t031_s_fmt,
- .g_fmt = mt9t031_g_fmt,
- .try_fmt = mt9t031_try_fmt,
+ .s_mbus_fmt = mt9t031_s_fmt,
+ .g_mbus_fmt = mt9t031_g_fmt,
+ .try_mbus_fmt = mt9t031_try_fmt,
.s_crop = mt9t031_s_crop,
.g_crop = mt9t031_g_crop,
.cropcap = mt9t031_cropcap,
+ .enum_mbus_fmt = mt9t031_enum_fmt,
+};
+
+static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9t031_g_skip_top_lines,
};
static struct v4l2_subdev_ops mt9t031_subdev_ops = {
.core = &mt9t031_subdev_core_ops,
.video = &mt9t031_subdev_video_ops,
+ .sensor = &mt9t031_subdev_sensor_ops,
};
static int mt9t031_probe(struct i2c_client *client,
@@ -745,18 +777,16 @@ static int mt9t031_probe(struct i2c_client *client,
struct mt9t031 *mt9t031;
struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl;
int ret;
- if (!icd) {
- dev_err(&client->dev, "MT9T031: missing soc-camera data!\n");
- return -EINVAL;
- }
+ if (icd) {
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
+ }
- icl = to_soc_camera_link(icd);
- if (!icl) {
- dev_err(&client->dev, "MT9T031 driver needs platform data\n");
- return -EINVAL;
+ icd->ops = &mt9t031_ops;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -771,17 +801,16 @@ static int mt9t031_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
- /* Second stage probe - when a capture adapter is there */
- icd->ops = &mt9t031_ops;
- icd->y_skip_top = 0;
-
+ mt9t031->y_skip_top = 0;
mt9t031->rect.left = MT9T031_COLUMN_SKIP;
mt9t031->rect.top = MT9T031_ROW_SKIP;
mt9t031->rect.width = MT9T031_MAX_WIDTH;
mt9t031->rect.height = MT9T031_MAX_HEIGHT;
- /* Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width */
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
mt9t031->autoexposure = 1;
mt9t031->xskip = 1;
@@ -794,7 +823,8 @@ static int mt9t031_probe(struct i2c_client *client,
mt9t031_disable(client);
if (ret) {
- icd->ops = NULL;
+ if (icd)
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
kfree(mt9t031);
}
@@ -807,7 +837,8 @@ static int mt9t031_remove(struct i2c_client *client)
struct mt9t031 *mt9t031 = to_mt9t031(client);
struct soc_camera_device *icd = client->dev.platform_data;
- icd->ops = NULL;
+ if (icd)
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
client->driver = NULL;
kfree(mt9t031);
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
new file mode 100644
index 000000000000..fc4dd6045720
--- /dev/null
+++ b/drivers/media/video/mt9t112.c
@@ -0,0 +1,1177 @@
+/*
+ * mt9t112 Camera Driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver, mt9m111 driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9t112.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+
+/* you can check PLL/clock info */
+/* #define EXT_CLOCK 24000000 */
+
+/************************************************************************
+
+
+ macro
+
+
+************************************************************************/
+/*
+ * frame size
+ */
+#define MAX_WIDTH 2048
+#define MAX_HEIGHT 1536
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+
+/*
+ * macro of read/write
+ */
+#define ECHECKER(ret, x) \
+ do { \
+ (ret) = (x); \
+ if ((ret) < 0) \
+ return (ret); \
+ } while (0)
+
+#define mt9t112_reg_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_reg_write(client, a, b))
+#define mt9t112_mcu_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
+
+#define mt9t112_reg_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
+#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
+
+#define mt9t112_reg_read(ret, client, a) \
+ ECHECKER(ret, __mt9t112_reg_read(client, a))
+
+/*
+ * Logical address
+ */
+#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
+#define VAR(id, offset) _VAR(id, offset, 0x0000)
+#define VAR8(id, offset) _VAR(id, offset, 0x8000)
+
+/************************************************************************
+
+
+ struct
+
+
+************************************************************************/
+struct mt9t112_frame_size {
+ u16 width;
+ u16 height;
+};
+
+struct mt9t112_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 fmt;
+ u16 order;
+};
+
+struct mt9t112_priv {
+ struct v4l2_subdev subdev;
+ struct mt9t112_camera_info *info;
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ struct mt9t112_frame_size frame;
+ const struct mt9t112_format *format;
+ int model;
+ u32 flags;
+/* for flags */
+#define INIT_DONE (1<<0)
+};
+
+/************************************************************************
+
+
+ supported format
+
+
+************************************************************************/
+
+static const struct mt9t112_format mt9t112_cfmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 1,
+ }, {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 2,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 3,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 8,
+ .order = 2,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 4,
+ .order = 2,
+ },
+};
+
+/************************************************************************
+
+
+ general function
+
+
+************************************************************************/
+static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client),
+ struct mt9t112_priv,
+ subdev);
+}
+
+static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ int ret;
+
+ command = swab16(command);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *)&command;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = buf;
+
+ /*
+ * if return value of this function is < 0,
+ * it mean error.
+ * else, under 16bit is valid data.
+ */
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ memcpy(&ret, buf, 2);
+ return swab16(ret);
+}
+
+static int __mt9t112_reg_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ struct i2c_msg msg;
+ u8 buf[4];
+ int ret;
+
+ command = swab16(command);
+ data = swab16(data);
+
+ memcpy(buf + 0, &command, 2);
+ memcpy(buf + 2, &data, 2);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 4;
+ msg.buf = buf;
+
+ /*
+ * i2c_transfer return message length,
+ * but this function should return 0 if correct case
+ */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret >= 0)
+ ret = 0;
+
+ return ret;
+}
+
+static int __mt9t112_reg_mask_set(const struct i2c_client *client,
+ u16 command,
+ u16 mask,
+ u16 set)
+{
+ int val = __mt9t112_reg_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_reg_write(client, command, val);
+}
+
+/* mcu access */
+static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_read(client, 0x0990);
+}
+
+static int __mt9t112_mcu_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_write(client, 0x0990, data);
+}
+
+static int __mt9t112_mcu_mask_set(const struct i2c_client *client,
+ u16 command,
+ u16 mask,
+ u16 set)
+{
+ int val = __mt9t112_mcu_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_mcu_write(client, command, val);
+}
+
+static int mt9t112_reset(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001);
+ msleep(1);
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000);
+
+ return ret;
+}
+
+#ifndef EXT_CLOCK
+#define CLOCK_INFO(a, b)
+#else
+#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b)
+static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
+{
+ int m, n, p1, p2, p3, p4, p5, p6, p7;
+ u32 vco, clk;
+ char *enable;
+
+ ext /= 1000; /* kbyte order */
+
+ mt9t112_reg_read(n, client, 0x0012);
+ p1 = n & 0x000f;
+ n = n >> 4;
+ p2 = n & 0x000f;
+ n = n >> 4;
+ p3 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002a);
+ p4 = n & 0x000f;
+ n = n >> 4;
+ p5 = n & 0x000f;
+ n = n >> 4;
+ p6 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002c);
+ p7 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x0010);
+ m = n & 0x00ff;
+ n = (n >> 8) & 0x003f;
+
+ enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
+ dev_info(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
+
+ vco = 2 * m * ext / (n+1);
+ enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
+ dev_info(&client->dev, "VCO : %10u K %s\n", vco, enable);
+
+ clk = vco / (p1+1) / (p2+1);
+ enable = (96000 < clk) ? "X" : "";
+ dev_info(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p3+1);
+ enable = (768000 < clk) ? "X" : "";
+ dev_info(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p6+1);
+ enable = (96000 < clk) ? "X" : "";
+ dev_info(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p5+1);
+ enable = (54000 < clk) ? "X" : "";
+ dev_info(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p4+1);
+ enable = (70000 < clk) ? "X" : "";
+ dev_info(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p7+1);
+ dev_info(&client->dev, "External sensor : %10u K\n", clk);
+
+ clk = ext / (n+1);
+ enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
+ dev_info(&client->dev, "PFD : %10u K %s\n", clk, enable);
+
+ return 0;
+}
+#endif
+
+static void mt9t112_frame_check(u32 *width, u32 *height)
+{
+ if (*width > MAX_WIDTH)
+ *width = MAX_WIDTH;
+
+ if (*height > MAX_HEIGHT)
+ *height = MAX_HEIGHT;
+}
+
+static int mt9t112_set_a_frame_size(const struct i2c_client *client,
+ u16 width,
+ u16 height)
+{
+ int ret;
+ u16 wstart = (MAX_WIDTH - width) / 2;
+ u16 hstart = (MAX_HEIGHT - height) / 2;
+
+ /* (Context A) Image Width/Height */
+ mt9t112_mcu_write(ret, client, VAR(26, 0), width);
+ mt9t112_mcu_write(ret, client, VAR(26, 2), height);
+
+ /* (Context A) Output Width/Height */
+ mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width);
+ mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height);
+
+ /* (Context A) Start Row/Column */
+ mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart);
+
+ /* (Context A) End Row/Column */
+ mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ return ret;
+}
+
+static int mt9t112_set_pll_dividers(const struct i2c_client *client,
+ u8 m, u8 n,
+ u8 p1, u8 p2, u8 p3,
+ u8 p4, u8 p5, u8 p6,
+ u8 p7)
+{
+ int ret;
+ u16 val;
+
+ /* N/M */
+ val = (n << 8) |
+ (m << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val);
+
+ /* P1/P2/P3 */
+ val = ((p3 & 0x0F) << 8) |
+ ((p2 & 0x0F) << 4) |
+ ((p1 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val);
+
+ /* P4/P5/P6 */
+ val = (0x7 << 12) |
+ ((p6 & 0x0F) << 8) |
+ ((p5 & 0x0F) << 4) |
+ ((p4 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val);
+
+ /* P7 */
+ val = (0x1 << 12) |
+ ((p7 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val);
+
+ return ret;
+}
+
+static int mt9t112_init_pll(const struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int data, i, ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001);
+
+ /* PLL control: BYPASS PLL = 8517 */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2145);
+
+ /* Replace these registers when new timing parameters are generated */
+ mt9t112_set_pll_dividers(client,
+ priv->info->divider.m,
+ priv->info->divider.n,
+ priv->info->divider.p1,
+ priv->info->divider.p2,
+ priv->info->divider.p3,
+ priv->info->divider.p4,
+ priv->info->divider.p5,
+ priv->info->divider.p6,
+ priv->info->divider.p7);
+
+ /*
+ * TEST_BYPASS on
+ * PLL_ENABLE on
+ * SEL_LOCK_DET on
+ * TEST_BYPASS off
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2525);
+ mt9t112_reg_write(ret, client, 0x0014, 0x2527);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3427);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3027);
+
+ mdelay(10);
+
+ /*
+ * PLL_BYPASS off
+ * Reference clock count
+ * I2C Master Clock Divider
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x3046);
+ mt9t112_reg_write(ret, client, 0x0022, 0x0190);
+ mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
+
+ /* External sensor clock is PLL bypass */
+ mt9t112_reg_write(ret, client, 0x002E, 0x0500);
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002);
+ mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004);
+
+ /* MCU disabled */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004);
+
+ /* out of standby */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0);
+
+ mdelay(50);
+
+ /*
+ * Standby Workaround
+ * Disable Secondary I2C Pads
+ */
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+
+ /* poll to verify out of standby. Must Poll this bit */
+ for (i = 0; i < 100; i++) {
+ mt9t112_reg_read(data, client, 0x0018);
+ if (0x4000 & data)
+ break;
+
+ mdelay(10);
+ }
+
+ return ret;
+}
+
+static int mt9t112_init_setting(const struct i2c_client *client)
+{
+
+ int ret;
+
+ /* Adaptive Output Clock (A) */
+ mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000);
+
+ /* Read Mode (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024);
+
+ /* Fine Correction (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC);
+
+ /* Fine IT Min (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1);
+
+ /* Fine IT Max Margin (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF);
+
+ /* Base Frame Lines (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D);
+
+ /* Min Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a);
+
+ /* Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0);
+
+ /* Adaptive Output Clock (B) */
+ mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000);
+
+ /* Row Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004);
+
+ /* Column Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004);
+
+ /* Row End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B);
+
+ /* Column End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B);
+
+ /* Fine Correction (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C);
+
+ /* Fine IT Min (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1);
+
+ /* Fine IT Max Margin (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF);
+
+ /* Base Frame Lines (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668);
+
+ /* Min Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0);
+
+ /* Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
+
+ /*
+ * Flicker Dectection registers
+ * This section should be replaced whenever new Timing file is generated
+ * All the following registers need to be replaced
+ * Following registers are generated from Register Wizard but user can
+ * modify them. For detail see auto flicker detection tuning
+ */
+
+ /* FD_FDPERIOD_SELECT */
+ mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01);
+
+ /* PRI_B_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003);
+
+ /* PRI_A_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003);
+
+ /*
+ * AFD range detection tuning registers
+ */
+
+ /* search_f1_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25);
+
+ /* search_f2_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28);
+
+ /* search_f1_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C);
+
+ /* search_f2_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F);
+
+ /* period_50Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA);
+
+ /* secret register by aptina */
+ /* period_50Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00);
+
+ /* period_60Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B);
+
+ /* secret register by aptina */
+ /* period_60Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00);
+
+ /* period_50Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82);
+
+ /* secret register by aptina */
+ /* period_50Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00);
+
+ /* period_60Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D);
+
+ /* secret register by aptina */
+ /* period_60Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00);
+
+ /* FD Mode */
+ mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10);
+
+ /* Stat_min */
+ mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02);
+
+ /* Stat_max */
+ mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03);
+
+ /* Min_amplitude */
+ mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A);
+
+ /* RX FIFO Watermark (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014);
+
+ /* RX FIFO Watermark (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014);
+
+ /* MCLK: 16MHz
+ * PCLK: 73MHz
+ * CorePixCLK: 36.5 MHz
+ */
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108);
+
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_setting(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F);
+ mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_reg_write(ret, client, 0x0614, 0x0000);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+ mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02);
+ mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002);
+ mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001);
+ mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025);
+ mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193);
+ mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_trigger(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01);
+
+ return ret;
+}
+
+static int mt9t112_init_camera(const struct i2c_client *client)
+{
+ int ret;
+
+ ECHECKER(ret, mt9t112_reset(client));
+
+ ECHECKER(ret, mt9t112_init_pll(client));
+
+ ECHECKER(ret, mt9t112_init_setting(client));
+
+ ECHECKER(ret, mt9t112_auto_focus_setting(client));
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0);
+
+ /* Analog setting B */
+ mt9t112_reg_write(ret, client, 0x3084, 0x2409);
+ mt9t112_reg_write(ret, client, 0x3092, 0x0A49);
+ mt9t112_reg_write(ret, client, 0x3094, 0x4949);
+ mt9t112_reg_write(ret, client, 0x3096, 0x4950);
+
+ /*
+ * Disable adaptive clock
+ * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ */
+ mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E);
+ mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E);
+
+ /* Configure STatus in Status_before_length Format and enable header */
+ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+ mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4);
+
+ /* Enable JPEG in context B */
+ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+ mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01);
+
+ /* Disable Dac_TXLO */
+ mt9t112_reg_write(ret, client, 0x316C, 0x350F);
+
+ /* Set max slew rates */
+ mt9t112_reg_write(ret, client, 0x1E, 0x777);
+
+ return ret;
+}
+
+/************************************************************************
+
+
+ soc_camera_ops
+
+
+************************************************************************/
+static int mt9t112_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long mt9t112_query_bus_param(struct soc_camera_device *icd)
+{
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ unsigned long flags = SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH;
+
+ flags |= (priv->info->flags & MT9T112_FLAG_PCLK_RISING_EDGE) ?
+ SOCAM_PCLK_SAMPLE_RISING : SOCAM_PCLK_SAMPLE_FALLING;
+
+ if (priv->info->flags & MT9T112_FLAG_DATAWIDTH_8)
+ flags |= SOCAM_DATAWIDTH_8;
+ else
+ flags |= SOCAM_DATAWIDTH_10;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static struct soc_camera_ops mt9t112_ops = {
+ .set_bus_param = mt9t112_set_bus_param,
+ .query_bus_param = mt9t112_query_bus_param,
+};
+
+/************************************************************************
+
+
+ v4l2_subdev_core_ops
+
+
+************************************************************************/
+static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t112_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = sd->priv;
+ int ret;
+
+ reg->size = 2;
+ mt9t112_reg_read(ret, client, reg->reg);
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int mt9t112_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = sd->priv;
+ int ret;
+
+ mt9t112_reg_write(ret, client, reg->reg, reg->val);
+
+ return ret;
+}
+#endif
+
+static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
+ .g_chip_ident = mt9t112_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t112_g_register,
+ .s_register = mt9t112_s_register,
+#endif
+};
+
+
+/************************************************************************
+
+
+ v4l2_subdev_video_ops
+
+
+************************************************************************/
+static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int ret = 0;
+
+ if (!enable) {
+ /* FIXME
+ *
+ * If user selected large output size,
+ * and used it long time,
+ * mt9t112 camera will be very warm.
+ *
+ * But current driver can not stop mt9t112 camera.
+ * So, set small size here to solve this problem.
+ */
+ mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT);
+ return ret;
+ }
+
+ if (!(priv->flags & INIT_DONE)) {
+ u16 param = (MT9T112_FLAG_PCLK_RISING_EDGE &
+ priv->info->flags) ? 0x0001 : 0x0000;
+
+ ECHECKER(ret, mt9t112_init_camera(client));
+
+ /* Invert PCLK (Data sampled on falling edge of pixclk) */
+ mt9t112_reg_write(ret, client, 0x3C20, param);
+
+ mdelay(5);
+
+ priv->flags |= INIT_DONE;
+ }
+
+ mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt);
+ mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_set_a_frame_size(client,
+ priv->frame.width,
+ priv->frame.height);
+
+ ECHECKER(ret, mt9t112_auto_focus_trigger(client));
+
+ dev_dbg(&client->dev, "format : %d\n", priv->format->code);
+ dev_dbg(&client->dev, "size : %d x %d\n",
+ priv->frame.width,
+ priv->frame.height);
+
+ CLOCK_INFO(client, EXT_CLOCK);
+
+ return ret;
+}
+
+static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
+ enum v4l2_mbus_pixelcode code)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int i;
+
+ priv->format = NULL;
+
+ /*
+ * frame size check
+ */
+ mt9t112_frame_check(&width, &height);
+
+ /*
+ * get color format
+ */
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts))
+ return -EINVAL;
+
+ priv->frame.width = (u16)width;
+ priv->frame.height = (u16)height;
+
+ priv->format = mt9t112_cfmts + i;
+
+ return 0;
+}
+
+static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = VGA_WIDTH;
+ a->bounds.height = VGA_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = VGA_WIDTH;
+ a->c.height = VGA_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = sd->priv;
+ struct v4l2_rect *rect = &a->c;
+
+ return mt9t112_set_params(client, rect->width, rect->height,
+ V4L2_MBUS_FMT_YUYV8_2X8_BE);
+}
+
+static int mt9t112_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (!priv->format) {
+ int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT,
+ V4L2_MBUS_FMT_YUYV8_2X8_BE);
+ if (ret < 0)
+ return ret;
+ }
+
+ mf->width = priv->frame.width;
+ mf->height = priv->frame.height;
+ /* TODO: set colorspace */
+ mf->code = priv->format->code;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = sd->priv;
+
+ /* TODO: set colorspace */
+ return mt9t112_set_params(client, mf->width, mf->height, mf->code);
+}
+
+static int mt9t112_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ mt9t112_frame_check(&mf->width, &mf->height);
+
+ /* TODO: set colorspace */
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if ((unsigned int)index >= ARRAY_SIZE(mt9t112_cfmts))
+ return -EINVAL;
+
+ *code = mt9t112_cfmts[index].code;
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
+ .s_stream = mt9t112_s_stream,
+ .g_mbus_fmt = mt9t112_g_fmt,
+ .s_mbus_fmt = mt9t112_s_fmt,
+ .try_mbus_fmt = mt9t112_try_fmt,
+ .cropcap = mt9t112_cropcap,
+ .g_crop = mt9t112_g_crop,
+ .s_crop = mt9t112_s_crop,
+ .enum_mbus_fmt = mt9t112_enum_fmt,
+};
+
+/************************************************************************
+
+
+ i2c driver
+
+
+************************************************************************/
+static struct v4l2_subdev_ops mt9t112_subdev_ops = {
+ .core = &mt9t112_subdev_core_ops,
+ .video = &mt9t112_subdev_video_ops,
+};
+
+static int mt9t112_camera_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ const char *devname;
+ int chipid;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /*
+ * check and show chip ID
+ */
+ mt9t112_reg_read(chipid, client, 0x0000);
+
+ switch (chipid) {
+ case 0x2680:
+ devname = "mt9t111";
+ priv->model = V4L2_IDENT_MT9T111;
+ break;
+ case 0x2682:
+ devname = "mt9t112";
+ priv->model = V4L2_IDENT_MT9T112;
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %04x\n", chipid);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid);
+
+ return 0;
+}
+
+static int mt9t112_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t112_priv *priv;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl;
+ int ret;
+
+ if (!icd) {
+ dev_err(&client->dev, "mt9t112: missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
+ if (!icl || !icl->priv)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = icl->priv;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
+
+ icd->ops = &mt9t112_ops;
+
+ ret = mt9t112_camera_probe(icd, client);
+ if (ret) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int mt9t112_remove(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
+
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t112_id[] = {
+ { "mt9t112", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t112_id);
+
+static struct i2c_driver mt9t112_i2c_driver = {
+ .driver = {
+ .name = "mt9t112",
+ },
+ .probe = mt9t112_probe,
+ .remove = mt9t112_remove,
+ .id_table = mt9t112_id,
+};
+
+/************************************************************************
+
+
+ module function
+
+
+************************************************************************/
+static int __init mt9t112_module_init(void)
+{
+ return i2c_add_driver(&mt9t112_i2c_driver);
+}
+
+static void __exit mt9t112_module_exit(void)
+{
+ i2c_del_driver(&mt9t112_i2c_driver);
+}
+
+module_init(mt9t112_module_init);
+module_exit(mt9t112_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 995607f9d3ba..91df7ec91fb6 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -18,9 +18,11 @@
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
-/* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
+/*
+ * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
* The platform has to define ctruct i2c_board_info objects and link to them
- * from struct soc_camera_link */
+ * from struct soc_camera_link
+ */
static char *sensor_type;
module_param(sensor_type, charp, S_IRUGO);
@@ -62,41 +64,49 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
#define MT9V022_COLUMN_SKIP 1
#define MT9V022_ROW_SKIP 4
-static const struct soc_camera_data_format mt9v022_colour_formats[] = {
- /* Order important: first natively supported,
- * second supported with a GPIO extender */
- {
- .name = "Bayer (sRGB) 10 bit",
- .depth = 10,
- .fourcc = V4L2_PIX_FMT_SBGGR16,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .name = "Bayer (sRGB) 8 bit",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }
+/* MT9V022 has only one fixed colorspace per pixelcode */
+struct mt9v022_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9v022_datafmt *mt9v022_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9v022_datafmt mt9v022_colour_fmts[] = {
+ /*
+ * Order important: first natively supported,
+ * second supported with a GPIO extender
+ */
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
-static const struct soc_camera_data_format mt9v022_monochrome_formats[] = {
+static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
/* Order important - see above */
- {
- .name = "Monochrome 10 bit",
- .depth = 10,
- .fourcc = V4L2_PIX_FMT_Y16,
- }, {
- .name = "Monochrome 8 bit",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_GREY,
- },
+ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG},
};
struct mt9v022 {
struct v4l2_subdev subdev;
struct v4l2_rect rect; /* Sensor window */
- __u32 fourcc;
+ const struct mt9v022_datafmt *fmt;
+ const struct mt9v022_datafmt *fmts;
+ int num_fmts;
int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
u16 chip_control;
+ unsigned short y_skip_top; /* Lines to skip at the top */
};
static struct mt9v022 *to_mt9v022(const struct i2c_client *client)
@@ -143,9 +153,11 @@ static int mt9v022_init(struct i2c_client *client)
struct mt9v022 *mt9v022 = to_mt9v022(client);
int ret;
- /* Almost the default mode: master, parallel, simultaneous, and an
+ /*
+ * Almost the default mode: master, parallel, simultaneous, and an
* undocumented bit 0x200, which is present in table 7, but not in 8,
- * plus snapshot mode to disable scan for now */
+ * plus snapshot mode to disable scan for now
+ */
mt9v022->chip_control |= 0x10;
ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
if (!ret)
@@ -265,12 +277,10 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
struct i2c_client *client = sd->priv;
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct v4l2_rect rect = a->c;
- struct soc_camera_device *icd = client->dev.platform_data;
int ret;
/* Bayer format - even size lengths */
- if (mt9v022->fourcc == V4L2_PIX_FMT_SBGGR8 ||
- mt9v022->fourcc == V4L2_PIX_FMT_SBGGR16) {
+ if (mt9v022->fmts == mt9v022_colour_fmts) {
rect.width = ALIGN(rect.width, 2);
rect.height = ALIGN(rect.height, 2);
/* Let the user play with the starting pixel */
@@ -287,10 +297,10 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
if (ret >= 0) {
if (ret & 1) /* Autoexposure */
ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH,
- rect.height + icd->y_skip_top + 43);
+ rect.height + mt9v022->y_skip_top + 43);
else
ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
- rect.height + icd->y_skip_top + 43);
+ rect.height + mt9v022->y_skip_top + 43);
}
/* Setup frame format: defaults apart from width and height */
if (!ret)
@@ -298,8 +308,10 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
if (!ret)
ret = reg_write(client, MT9V022_ROW_START, rect.top);
if (!ret)
- /* Default 94, Phytec driver says:
- * "width + horizontal blank >= 660" */
+ /*
+ * Default 94, Phytec driver says:
+ * "width + horizontal blank >= 660"
+ */
ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING,
rect.width > 660 - 43 ? 43 :
660 - rect.width);
@@ -309,7 +321,7 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
if (!ret)
ret = reg_write(client, MT9V022_WINDOW_HEIGHT,
- rect.height + icd->y_skip_top);
+ rect.height + mt9v022->y_skip_top);
if (ret < 0)
return ret;
@@ -346,46 +358,48 @@ static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int mt9v022_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9v022_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- pix->width = mt9v022->rect.width;
- pix->height = mt9v022->rect.height;
- pix->pixelformat = mt9v022->fourcc;
- pix->field = V4L2_FIELD_NONE;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->width = mt9v022->rect.width;
+ mf->height = mt9v022->rect.height;
+ mf->code = mt9v022->fmt->code;
+ mf->colorspace = mt9v022->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
return 0;
}
-static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9v022_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_crop a = {
.c = {
.left = mt9v022->rect.left,
.top = mt9v022->rect.top,
- .width = pix->width,
- .height = pix->height,
+ .width = mf->width,
+ .height = mf->height,
},
};
int ret;
- /* The caller provides a supported format, as verified per call to
- * icd->try_fmt(), datawidth is from our supported format list */
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_GREY:
- case V4L2_PIX_FMT_Y16:
+ /*
+ * The caller provides a supported format, as verified per call to
+ * icd->try_fmt(), datawidth is from our supported format list
+ */
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_GREY8_1X8:
+ case V4L2_MBUS_FMT_Y10_1X10:
if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
return -EINVAL;
break;
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SBGGR16:
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
return -EINVAL;
break;
@@ -399,26 +413,38 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
/* No support for scaling on this camera, just crop. */
ret = mt9v022_s_crop(sd, &a);
if (!ret) {
- pix->width = mt9v022->rect.width;
- pix->height = mt9v022->rect.height;
- mt9v022->fourcc = pix->pixelformat;
+ mf->width = mt9v022->rect.width;
+ mf->height = mt9v022->rect.height;
+ mt9v022->fmt = mt9v022_find_datafmt(mf->code,
+ mt9v022->fmts, mt9v022->num_fmts);
+ mf->colorspace = mt9v022->fmt->colorspace;
}
return ret;
}
-static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int mt9v022_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
- struct soc_camera_device *icd = client->dev.platform_data;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int align = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
- pix->pixelformat == V4L2_PIX_FMT_SBGGR16;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ const struct mt9v022_datafmt *fmt;
+ int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_1X10;
- v4l_bound_align_image(&pix->width, MT9V022_MIN_WIDTH,
+ v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH,
MT9V022_MAX_WIDTH, align,
- &pix->height, MT9V022_MIN_HEIGHT + icd->y_skip_top,
- MT9V022_MAX_HEIGHT + icd->y_skip_top, align, 0);
+ &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top,
+ MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0);
+
+ fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts,
+ mt9v022->num_fmts);
+ if (!fmt) {
+ fmt = mt9v022->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->colorspace = fmt->colorspace;
return 0;
}
@@ -635,8 +661,10 @@ static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
48 + range / 2) / range + 16;
if (gain >= 32)
gain &= ~1;
- /* The user wants to set gain manually, hope, she
- * knows, what she's doing... Switch AGC off. */
+ /*
+ * The user wants to set gain manually, hope, she
+ * knows, what she's doing... Switch AGC off.
+ */
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
return -EIO;
@@ -655,8 +683,10 @@ static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
unsigned long range = qctrl->maximum - qctrl->minimum;
unsigned long shutter = ((ctrl->value - qctrl->minimum) *
479 + range / 2) / range + 1;
- /* The user wants to set shutter width manually, hope,
- * she knows, what she's doing... Switch AEC off. */
+ /*
+ * The user wants to set shutter width manually, hope,
+ * she knows, what she's doing... Switch AEC off.
+ */
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
return -EIO;
@@ -689,8 +719,10 @@ static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return 0;
}
-/* Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one */
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
static int mt9v022_video_probe(struct soc_camera_device *icd,
struct i2c_client *client)
{
@@ -733,17 +765,17 @@ static int mt9v022_video_probe(struct soc_camera_device *icd,
!strcmp("color", sensor_type))) {
ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
- icd->formats = mt9v022_colour_formats;
+ mt9v022->fmts = mt9v022_colour_fmts;
} else {
ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
- icd->formats = mt9v022_monochrome_formats;
+ mt9v022->fmts = mt9v022_monochrome_fmts;
}
if (ret < 0)
goto ei2c;
- icd->num_formats = 0;
+ mt9v022->num_fmts = 0;
/*
* This is a 10bit sensor, so by default we only allow 10bit.
@@ -756,14 +788,14 @@ static int mt9v022_video_probe(struct soc_camera_device *icd,
flags = SOCAM_DATAWIDTH_10;
if (flags & SOCAM_DATAWIDTH_10)
- icd->num_formats++;
+ mt9v022->num_fmts++;
else
- icd->formats++;
+ mt9v022->fmts++;
if (flags & SOCAM_DATAWIDTH_8)
- icd->num_formats++;
+ mt9v022->num_fmts++;
- mt9v022->fourcc = icd->formats->fourcc;
+ mt9v022->fmt = &mt9v022->fmts[0];
dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
@@ -787,6 +819,16 @@ static void mt9v022_video_remove(struct soc_camera_device *icd)
icl->free_bus(icl);
}
+static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ *lines = mt9v022->y_skip_top;
+
+ return 0;
+}
+
static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
.g_ctrl = mt9v022_g_ctrl,
.s_ctrl = mt9v022_s_ctrl,
@@ -797,19 +839,38 @@ static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
#endif
};
+static int mt9v022_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if ((unsigned int)index >= mt9v022->num_fmts)
+ return -EINVAL;
+
+ *code = mt9v022->fmts[index].code;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.s_stream = mt9v022_s_stream,
- .s_fmt = mt9v022_s_fmt,
- .g_fmt = mt9v022_g_fmt,
- .try_fmt = mt9v022_try_fmt,
+ .s_mbus_fmt = mt9v022_s_fmt,
+ .g_mbus_fmt = mt9v022_g_fmt,
+ .try_mbus_fmt = mt9v022_try_fmt,
.s_crop = mt9v022_s_crop,
.g_crop = mt9v022_g_crop,
.cropcap = mt9v022_cropcap,
+ .enum_mbus_fmt = mt9v022_enum_fmt,
+};
+
+static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9v022_g_skip_top_lines,
};
static struct v4l2_subdev_ops mt9v022_subdev_ops = {
.core = &mt9v022_subdev_core_ops,
.video = &mt9v022_subdev_video_ops,
+ .sensor = &mt9v022_subdev_sensor_ops,
};
static int mt9v022_probe(struct i2c_client *client,
@@ -851,8 +912,7 @@ static int mt9v022_probe(struct i2c_client *client,
* MT9V022 _really_ corrupts the first read out line.
* TODO: verify on i.MX31
*/
- icd->y_skip_top = 1;
-
+ mt9v022->y_skip_top = 1;
mt9v022->rect.left = MT9V022_COLUMN_SKIP;
mt9v022->rect.top = MT9V022_ROW_SKIP;
mt9v022->rect.width = MT9V022_MAX_WIDTH;
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 72802291e812..2ba14fb5b031 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -37,6 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-dma-contig.h>
+#include <media/soc_mediabus.h>
#include <asm/dma.h>
#include <asm/fiq.h>
@@ -94,14 +95,16 @@
/* buffer for one video frame */
struct mx1_buffer {
/* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
- const struct soc_camera_data_format *fmt;
- int inwork;
+ struct videobuf_buffer vb;
+ enum v4l2_mbus_pixelcode code;
+ int inwork;
};
-/* i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor
+/*
+ * i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor
* Interface. If anyone ever builds hardware to enable more than
- * one camera, they will have to modify this driver too */
+ * one camera, they will have to modify this driver too
+ */
struct mx1_camera_dev {
struct soc_camera_host soc_host;
struct soc_camera_device *icd;
@@ -126,9 +129,13 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
- *size = icd->user_width * icd->user_height *
- ((icd->current_fmt->depth + 7) >> 3);
+ *size = bytes_per_line * icd->user_height;
if (!*count)
*count = 32;
@@ -151,8 +158,10 @@ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf)
dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
- /* This waits until this buffer is out of danger, i.e., until it is no
- * longer in STATE_QUEUED or STATE_ACTIVE */
+ /*
+ * This waits until this buffer is out of danger, i.e., until it is no
+ * longer in STATE_QUEUED or STATE_ACTIVE
+ */
videobuf_waiton(vb, 0, 0);
videobuf_dma_contig_free(vq, vb);
@@ -165,6 +174,11 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
struct soc_camera_device *icd = vq->priv_data;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
int ret;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
@@ -174,22 +188,24 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
BUG_ON(NULL == icd->current_fmt);
- /* I think, in buf_prepare you only have to protect global data,
- * the actual buffer is yours */
+ /*
+ * I think, in buf_prepare you only have to protect global data,
+ * the actual buffer is yours
+ */
buf->inwork = 1;
- if (buf->fmt != icd->current_fmt ||
+ if (buf->code != icd->current_fmt->code ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
- buf->fmt = icd->current_fmt;
+ buf->code = icd->current_fmt->code;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
- vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3);
+ vb->size = bytes_per_line * vb->height;
if (0 != vb->baddr && vb->bsize < vb->size) {
ret = -EINVAL;
goto out;
@@ -381,8 +397,10 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
lcdclk = clk_get_rate(pcdev->clk);
- /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here
- * they get a nice Oops */
+ /*
+ * We verify platform_mclk_10khz != 0, so if anyone breaks it, here
+ * they get a nice Oops
+ */
div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
dev_dbg(pcdev->icd->dev.parent,
@@ -420,8 +438,10 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
clk_disable(pcdev->clk);
}
-/* The following two functions absolutely depend on the fact, that
- * there can be only one camera on i.MX1/i.MXL camera sensor interface */
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on i.MX1/i.MXL camera sensor interface
+ */
static int mx1_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -487,12 +507,10 @@ static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
/* MX1 supports only 8bit buswidth */
common_flags = soc_camera_bus_param_compatible(camera_flags,
- CSI_BUS_FLAGS);
+ CSI_BUS_FLAGS);
if (!common_flags)
return -EINVAL;
- icd->buswidth = 8;
-
/* Make choises, based on platform choice */
if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
@@ -545,7 +563,8 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
- int ret;
+ struct v4l2_mbus_framefmt mf;
+ int ret, buswidth;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
@@ -554,12 +573,33 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd,
return -EINVAL;
}
- ret = v4l2_subdev_call(sd, video, s_fmt, f);
- if (!ret) {
- icd->buswidth = xlate->buswidth;
- icd->current_fmt = xlate->host_fmt;
+ buswidth = xlate->host_fmt->bits_per_sample;
+ if (buswidth > 8) {
+ dev_warn(icd->dev.parent,
+ "bits-per-sample %d for format %x unsupported\n",
+ buswidth, pix->pixelformat);
+ return -EINVAL;
}
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
return ret;
}
@@ -567,10 +607,36 @@ static int mx1_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
/* TODO: limit to mx1 hardware capabilities */
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->dev.parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
/* limit to sensor capabilities */
- return v4l2_subdev_call(sd, video, try_fmt, f);
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ return 0;
}
static int mx1_camera_reqbufs(struct soc_camera_file *icf,
@@ -578,10 +644,12 @@ static int mx1_camera_reqbufs(struct soc_camera_file *icf,
{
int i;
- /* This is for locking debugging only. I removed spinlocks and now I
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
* check whether .prepare is ever called on a linked buffer, or whether
* a dma IRQ can occur for an in-work or unlinked buffer. Until now
- * it hadn't triggered */
+ * it hadn't triggered
+ */
for (i = 0; i < p->count; i++) {
struct mx1_buffer *buf = container_of(icf->vb_vidq.bufs[i],
struct mx1_buffer, vb);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index 7db82bdf6f31..bd297f567dc7 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -23,6 +23,7 @@
#include <media/v4l2-dev.h>
#include <media/videobuf-dma-contig.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <mach/ipu.h>
#include <mach/mx3_camera.h>
@@ -63,7 +64,7 @@
struct mx3_camera_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
- const struct soc_camera_data_format *fmt;
+ enum v4l2_mbus_pixelcode code;
/* One descriptot per scatterlist (per frame) */
struct dma_async_tx_descriptor *txd;
@@ -118,8 +119,6 @@ struct dma_chan_request {
enum ipu_channel id;
};
-static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt);
-
static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
{
return __raw_readl(mx3->base + reg);
@@ -211,17 +210,16 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- /*
- * bits-per-pixel (depth) as specified in camera's pixel format does
- * not necessarily match what the camera interface writes to RAM, but
- * it should be good enough for now.
- */
- unsigned int bpp = DIV_ROUND_UP(icd->current_fmt->depth, 8);
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
- *size = icd->user_width * icd->user_height * bpp;
+ *size = bytes_per_line * icd->user_height;
if (!*count)
*count = 32;
@@ -241,21 +239,26 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq,
struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf =
container_of(vb, struct mx3_camera_buffer, vb);
- /* current_fmt _must_ always be set */
- size_t new_size = icd->user_width * icd->user_height *
- ((icd->current_fmt->depth + 7) >> 3);
+ size_t new_size;
int ret;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ new_size = bytes_per_line * icd->user_height;
/*
* I think, in buf_prepare you only have to protect global data,
* the actual buffer is yours
*/
- if (buf->fmt != icd->current_fmt ||
+ if (buf->code != icd->current_fmt->code ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
- buf->fmt = icd->current_fmt;
+ buf->code = icd->current_fmt->code;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
@@ -348,13 +351,13 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct dma_async_tx_descriptor *txd = buf->txd;
struct idmac_channel *ichan = to_idmac_chan(txd->chan);
struct idmac_video_param *video = &ichan->params.video;
- const struct soc_camera_data_format *data_fmt = icd->current_fmt;
dma_cookie_t cookie;
+ u32 fourcc = icd->current_fmt->host_fmt->fourcc;
BUG_ON(!irqs_disabled());
/* This is the configuration of one sg-element */
- video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc);
+ video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc);
video->out_width = icd->user_width;
video->out_height = icd->user_height;
video->out_stride = icd->user_width;
@@ -564,30 +567,37 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
SOCAM_DATA_ACTIVE_HIGH |
SOCAM_DATA_ACTIVE_LOW;
- /* If requested data width is supported by the platform, use it or any
- * possible lower value - i.MX31 is smart enough to schift bits */
+ /*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to schift bits
+ */
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
+ SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
+ SOCAM_DATAWIDTH_4;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ *flags |= SOCAM_DATAWIDTH_4;
+
switch (buswidth) {
case 15:
- if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15))
+ if (!(*flags & SOCAM_DATAWIDTH_15))
return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
- SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
break;
case 10:
- if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10))
+ if (!(*flags & SOCAM_DATAWIDTH_10))
return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
- SOCAM_DATAWIDTH_4;
break;
case 8:
- if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8))
+ if (!(*flags & SOCAM_DATAWIDTH_8))
return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
break;
case 4:
- if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4))
+ if (!(*flags & SOCAM_DATAWIDTH_4))
return -EINVAL;
- *flags |= SOCAM_DATAWIDTH_4;
break;
default:
dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
@@ -636,91 +646,92 @@ static bool chan_filter(struct dma_chan *chan, void *arg)
pdata->dma_dev == chan->device->dev;
}
-static const struct soc_camera_data_format mx3_camera_formats[] = {
+static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
{
- .name = "Bayer (sRGB) 8 bit",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .colorspace = V4L2_COLORSPACE_SRGB,
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer BGGR (sRGB) 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
}, {
- .name = "Monochrome 8 bit",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_GREY,
- .colorspace = V4L2_COLORSPACE_JPEG,
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Monochrome 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
},
};
-static bool buswidth_supported(struct soc_camera_host *ici, int depth)
+/* This will be corrected as we get more formats */
+static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
{
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- switch (depth) {
- case 4:
- return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4);
- case 8:
- return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8);
- case 10:
- return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10);
- case 15:
- return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15);
- }
- return false;
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
}
static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx,
struct soc_camera_format_xlate *xlate)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int formats = 0, buswidth, ret;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->dev.parent;
+ int formats = 0, ret;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
- buswidth = icd->formats[idx].depth;
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
- if (!buswidth_supported(ici, buswidth))
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(icd->dev.parent,
+ "Invalid format code #%d: %d\n", idx, code);
return 0;
+ }
- ret = mx3_camera_try_bus_param(icd, buswidth);
+ /* This also checks support for the requested bits-per-sample */
+ ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
return 0;
- switch (icd->formats[idx].fourcc) {
- case V4L2_PIX_FMT_SGRBG10:
+ switch (code) {
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
formats++;
if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[0];
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = buswidth;
+ xlate->host_fmt = &mx3_camera_formats[0];
+ xlate->code = code;
xlate++;
- dev_dbg(icd->dev.parent,
- "Providing format %s using %s\n",
- mx3_camera_formats[0].name,
- icd->formats[idx].name);
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ mx3_camera_formats[0].name, code);
}
- goto passthrough;
- case V4L2_PIX_FMT_Y16:
+ break;
+ case V4L2_MBUS_FMT_Y10_1X10:
formats++;
if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[1];
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = buswidth;
+ xlate->host_fmt = &mx3_camera_formats[1];
+ xlate->code = code;
xlate++;
- dev_dbg(icd->dev.parent,
- "Providing format %s using %s\n",
- mx3_camera_formats[0].name,
- icd->formats[idx].name);
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ mx3_camera_formats[1].name, code);
}
+ break;
default:
-passthrough:
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = icd->formats + idx;
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = buswidth;
- xlate++;
- dev_dbg(icd->dev.parent,
- "Providing format %s in pass-through mode\n",
- icd->formats[idx].name);
- }
+ if (!mx3_camera_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %x in pass-through mode\n",
+ xlate->host_fmt->fourcc);
}
return formats;
@@ -804,8 +815,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
- struct v4l2_pix_format *pix = &f.fmt.pix;
+ struct v4l2_mbus_framefmt mf;
int ret;
soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
@@ -816,19 +826,19 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
return ret;
/* The capture device might have changed its output */
- ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
if (ret < 0)
return ret;
- if (pix->width & 7) {
+ if (mf.width & 7) {
/* Ouch! We can only handle 8-byte aligned width... */
- stride_align(&pix->width);
- ret = v4l2_subdev_call(sd, video, s_fmt, &f);
+ stride_align(&mf.width);
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
if (ret < 0)
return ret;
}
- if (pix->width != icd->user_width || pix->height != icd->user_height) {
+ if (mf.width != icd->user_width || mf.height != icd->user_height) {
/*
* We now know pixel formats and can decide upon DMA-channel(s)
* So far only direct camera-to-memory is supported
@@ -839,14 +849,14 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
return ret;
}
- configure_geometry(mx3_cam, pix->width, pix->height);
+ configure_geometry(mx3_cam, mf.width, mf.height);
}
dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
- pix->width, pix->height);
+ mf.width, mf.height);
- icd->user_width = pix->width;
- icd->user_height = pix->height;
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
return ret;
}
@@ -859,6 +869,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
@@ -883,11 +894,24 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
configure_geometry(mx3_cam, pix->width, pix->height);
- ret = v4l2_subdev_call(sd, video, s_fmt, f);
- if (!ret) {
- icd->buswidth = xlate->buswidth;
- icd->current_fmt = xlate->host_fmt;
- }
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height);
@@ -900,8 +924,8 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
- enum v4l2_field field;
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
@@ -916,23 +940,37 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
if (pix->width > 4096)
pix->width = 4096;
- pix->bytesperline = pix->width *
- DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
pix->sizeimage = pix->height * pix->bytesperline;
- /* camera has to see its format, but the user the original one */
- pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- ret = v4l2_subdev_call(sd, video, try_fmt, f);
- pix->pixelformat = xlate->host_fmt->fourcc;
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
- field = pix->field;
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
- if (field == V4L2_FIELD_ANY) {
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
pix->field = V4L2_FIELD_NONE;
- } else if (field != V4L2_FIELD_NONE) {
- dev_err(icd->dev.parent, "Field type %d unsupported.\n", field);
- return -EINVAL;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+ mf.field);
+ ret = -EINVAL;
}
return ret;
@@ -968,18 +1006,26 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
struct mx3_camera_dev *mx3_cam = ici->priv;
unsigned long bus_flags, camera_flags, common_flags;
u32 dw, sens_conf;
- int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags);
+ const struct soc_mbus_pixelfmt *fmt;
+ int buswidth;
+ int ret;
const struct soc_camera_format_xlate *xlate;
struct device *dev = icd->dev.parent;
+ fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
dev_warn(dev, "Format %x not found\n", pixfmt);
return -EINVAL;
}
- dev_dbg(dev, "requested bus width %d bit: %d\n",
- icd->buswidth, ret);
+ dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
if (ret < 0)
return ret;
@@ -1027,8 +1073,10 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
}
- /* Make the camera work in widest common mode, we'll take care of
- * the rest */
+ /*
+ * Make the camera work in widest common mode, we'll take care of
+ * the rest
+ */
if (common_flags & SOCAM_DATAWIDTH_15)
common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
SOCAM_DATAWIDTH_15;
@@ -1078,7 +1126,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
/* Just do what we're asked to do */
- switch (xlate->host_fmt->depth) {
+ switch (xlate->host_fmt->bits_per_sample) {
case 4:
dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
break;
@@ -1152,8 +1200,10 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 |
MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 |
MX3_CAMERA_DATAWIDTH_15))) {
- /* Platform hasn't set available data widths. This is bad.
- * Warn and use a default. */
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
"data widths, using default 8 bit\n");
mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
index 5fc4ac0d88f0..7400eacb4d64 100644
--- a/drivers/media/video/omap24xxcam.c
+++ b/drivers/media/video/omap24xxcam.c
@@ -1450,12 +1450,11 @@ static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
static int omap24xxcam_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
struct omap24xxcam_device *cam = omap24xxcam.priv;
struct omap24xxcam_fh *fh;
struct v4l2_format format;
- if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+ if (!cam || !cam->vfd)
return -ENODEV;
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
@@ -1660,7 +1659,6 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s)
strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
vfd->fops = &omap24xxcam_fops;
- vfd->minor = -1;
vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
omap24xxcam_hwinit(cam);
@@ -1671,14 +1669,14 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s)
if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
dev_err(cam->dev, "could not register V4L device\n");
- vfd->minor = -1;
rval = -EBUSY;
goto err;
}
omap24xxcam_poweron_reset(cam);
- dev_info(cam->dev, "registered device video%d\n", vfd->minor);
+ dev_info(cam->dev, "registered device %s\n",
+ video_device_node_name(vfd));
return 0;
@@ -1695,7 +1693,7 @@ static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
omap24xxcam_sensor_exit(cam);
if (cam->vfd) {
- if (cam->vfd->minor == -1) {
+ if (!video_is_registered(cam->vfd)) {
/*
* The device was never registered, so release the
* video_device struct directly.
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
index 0bc2cf573c76..e0bce8dc74bf 100644
--- a/drivers/media/video/ov511.c
+++ b/drivers/media/video/ov511.c
@@ -4674,7 +4674,6 @@ static struct video_device vdev_template = {
.name = "OV511 USB Camera",
.fops = &ov511_fops,
.release = video_device_release,
- .minor = -1,
};
/****************************************************************************
@@ -5867,8 +5866,8 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id)
ov511_devused |= 1 << nr;
ov->nr = nr;
- dev_info(&intf->dev, "Device at %s registered to minor %d\n",
- ov->usb_path, ov->vdev->minor);
+ dev_info(&intf->dev, "Device at %s registered to %s\n",
+ ov->usb_path, video_device_node_name(ov->vdev));
usb_set_intfdata(intf, ov);
if (ov_create_sysfs(ov->vdev)) {
@@ -5878,13 +5877,13 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto error;
}
- mutex_lock(&ov->lock);
+ mutex_unlock(&ov->lock);
return 0;
error:
if (ov->vdev) {
- if (-1 == ov->vdev->minor)
+ if (!video_is_registered(ov->vdev))
video_device_release(ov->vdev);
else
video_unregister_device(ov->vdev);
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 205229333466..3a45e945a528 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -24,6 +24,7 @@
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/ov772x.h>
/*
@@ -382,7 +383,8 @@ struct regval_list {
};
struct ov772x_color_format {
- const struct soc_camera_data_format *format;
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
u8 dsp3;
u8 com3;
u8 com7;
@@ -399,7 +401,7 @@ struct ov772x_win_size {
struct ov772x_priv {
struct v4l2_subdev subdev;
struct ov772x_camera_info *info;
- const struct ov772x_color_format *fmt;
+ const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
int model;
unsigned short flag_vflip:1;
@@ -434,93 +436,57 @@ static const struct regval_list ov772x_vga_regs[] = {
};
/*
- * supported format list
- */
-
-#define SETFOURCC(type) .name = (#type), .fourcc = (V4L2_PIX_FMT_ ## type)
-static const struct soc_camera_data_format ov772x_fmt_lists[] = {
- {
- SETFOURCC(YUYV),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- {
- SETFOURCC(YVYU),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- {
- SETFOURCC(UYVY),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- {
- SETFOURCC(RGB555),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
- {
- SETFOURCC(RGB555X),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
- {
- SETFOURCC(RGB565),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
- {
- SETFOURCC(RGB565X),
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
-};
-
-/*
- * color format list
+ * supported color format list
*/
static const struct ov772x_color_format ov772x_cfmts[] = {
{
- .format = &ov772x_fmt_lists[0],
- .dsp3 = 0x0,
- .com3 = SWAP_YUV,
- .com7 = OFMT_YUV,
+ .code = V4L2_MBUS_FMT_YUYV8_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = 0x0,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
},
{
- .format = &ov772x_fmt_lists[1],
- .dsp3 = UV_ON,
- .com3 = SWAP_YUV,
- .com7 = OFMT_YUV,
+ .code = V4L2_MBUS_FMT_YVYU8_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = UV_ON,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
},
{
- .format = &ov772x_fmt_lists[2],
- .dsp3 = 0x0,
- .com3 = 0x0,
- .com7 = OFMT_YUV,
+ .code = V4L2_MBUS_FMT_YUYV8_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = 0x0,
+ .com3 = 0x0,
+ .com7 = OFMT_YUV,
},
{
- .format = &ov772x_fmt_lists[3],
- .dsp3 = 0x0,
- .com3 = SWAP_RGB,
- .com7 = FMT_RGB555 | OFMT_RGB,
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB555 | OFMT_RGB,
},
{
- .format = &ov772x_fmt_lists[4],
- .dsp3 = 0x0,
- .com3 = 0x0,
- .com7 = FMT_RGB555 | OFMT_RGB,
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = 0x0,
+ .com7 = FMT_RGB555 | OFMT_RGB,
},
{
- .format = &ov772x_fmt_lists[5],
- .dsp3 = 0x0,
- .com3 = SWAP_RGB,
- .com7 = FMT_RGB565 | OFMT_RGB,
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB565 | OFMT_RGB,
},
{
- .format = &ov772x_fmt_lists[6],
- .dsp3 = 0x0,
- .com3 = 0x0,
- .com7 = FMT_RGB565 | OFMT_RGB,
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = 0x0,
+ .com7 = FMT_RGB565 | OFMT_RGB,
},
};
@@ -642,15 +608,15 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
- if (!priv->win || !priv->fmt) {
+ if (!priv->win || !priv->cfmt) {
dev_err(&client->dev, "norm or win select error\n");
return -EPERM;
}
ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
- dev_dbg(&client->dev, "format %s, win %s\n",
- priv->fmt->format->name, priv->win->name);
+ dev_dbg(&client->dev, "format %d, win %s\n",
+ priv->cfmt->code, priv->win->name);
return 0;
}
@@ -806,8 +772,8 @@ static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
return win;
}
-static int ov772x_set_params(struct i2c_client *client,
- u32 *width, u32 *height, u32 pixfmt)
+static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
+ enum v4l2_mbus_pixelcode code)
{
struct ov772x_priv *priv = to_ov772x(client);
int ret = -EINVAL;
@@ -817,14 +783,14 @@ static int ov772x_set_params(struct i2c_client *client,
/*
* select format
*/
- priv->fmt = NULL;
+ priv->cfmt = NULL;
for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
- if (pixfmt == ov772x_cfmts[i].format->fourcc) {
- priv->fmt = ov772x_cfmts + i;
+ if (code == ov772x_cfmts[i].code) {
+ priv->cfmt = ov772x_cfmts + i;
break;
}
}
- if (!priv->fmt)
+ if (!priv->cfmt)
goto ov772x_set_fmt_error;
/*
@@ -894,7 +860,7 @@ static int ov772x_set_params(struct i2c_client *client,
/*
* set DSP_CTRL3
*/
- val = priv->fmt->dsp3;
+ val = priv->cfmt->dsp3;
if (val) {
ret = ov772x_mask_set(client,
DSP_CTRL3, UV_MASK, val);
@@ -905,7 +871,7 @@ static int ov772x_set_params(struct i2c_client *client,
/*
* set COM3
*/
- val = priv->fmt->com3;
+ val = priv->cfmt->com3;
if (priv->info->flags & OV772X_FLAG_VFLIP)
val |= VFLIP_IMG;
if (priv->info->flags & OV772X_FLAG_HFLIP)
@@ -923,9 +889,9 @@ static int ov772x_set_params(struct i2c_client *client,
/*
* set COM7
*/
- val = priv->win->com7_bit | priv->fmt->com7;
+ val = priv->win->com7_bit | priv->cfmt->com7;
ret = ov772x_mask_set(client,
- COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK),
+ COM7, SLCT_MASK | FMT_MASK | OFMT_MASK,
val);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -951,7 +917,7 @@ ov772x_set_fmt_error:
ov772x_reset(client);
priv->win = NULL;
- priv->fmt = NULL;
+ priv->cfmt = NULL;
return ret;
}
@@ -981,54 +947,79 @@ static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int ov772x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int ov772x_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct ov772x_priv *priv = to_ov772x(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- if (!priv->win || !priv->fmt) {
+ if (!priv->win || !priv->cfmt) {
u32 width = VGA_WIDTH, height = VGA_HEIGHT;
int ret = ov772x_set_params(client, &width, &height,
- V4L2_PIX_FMT_YUYV);
+ V4L2_MBUS_FMT_YUYV8_2X8_LE);
if (ret < 0)
return ret;
}
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- pix->width = priv->win->width;
- pix->height = priv->win->height;
- pix->pixelformat = priv->fmt->format->fourcc;
- pix->colorspace = priv->fmt->format->colorspace;
- pix->field = V4L2_FIELD_NONE;
+ mf->width = priv->win->width;
+ mf->height = priv->win->height;
+ mf->code = priv->cfmt->code;
+ mf->colorspace = priv->cfmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
return 0;
}
-static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int ov772x_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct ov772x_priv *priv = to_ov772x(client);
+ int ret = ov772x_set_params(client, &mf->width, &mf->height,
+ mf->code);
+
+ if (!ret)
+ mf->colorspace = priv->cfmt->colorspace;
- return ov772x_set_params(client, &pix->width, &pix->height,
- pix->pixelformat);
+ return ret;
}
static int ov772x_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_format *f)
+ struct v4l2_mbus_framefmt *mf)
{
- struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct i2c_client *client = sd->priv;
+ struct ov772x_priv *priv = to_ov772x(client);
const struct ov772x_win_size *win;
+ int i;
/*
* select suitable win
*/
- win = ov772x_select_win(pix->width, pix->height);
+ win = ov772x_select_win(mf->width, mf->height);
+
+ mf->width = win->width;
+ mf->height = win->height;
+ mf->field = V4L2_FIELD_NONE;
- pix->width = win->width;
- pix->height = win->height;
- pix->field = V4L2_FIELD_NONE;
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++)
+ if (mf->code == ov772x_cfmts[i].code)
+ break;
+
+ if (i == ARRAY_SIZE(ov772x_cfmts)) {
+ /* Unsupported format requested. Propose either */
+ if (priv->cfmt) {
+ /* the current one or */
+ mf->colorspace = priv->cfmt->colorspace;
+ mf->code = priv->cfmt->code;
+ } else {
+ /* the default one */
+ mf->colorspace = ov772x_cfmts[0].colorspace;
+ mf->code = ov772x_cfmts[0].code;
+ }
+ } else {
+ /* Also return the colorspace */
+ mf->colorspace = ov772x_cfmts[i].colorspace;
+ }
return 0;
}
@@ -1057,9 +1048,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
return -ENODEV;
}
- icd->formats = ov772x_fmt_lists;
- icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists);
-
/*
* check and show product ID and manufacturer ID
*/
@@ -1109,13 +1097,24 @@ static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
#endif
};
+static int ov772x_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if ((unsigned int)index >= ARRAY_SIZE(ov772x_cfmts))
+ return -EINVAL;
+
+ *code = ov772x_cfmts[index].code;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.s_stream = ov772x_s_stream,
- .g_fmt = ov772x_g_fmt,
- .s_fmt = ov772x_s_fmt,
- .try_fmt = ov772x_try_fmt,
+ .g_mbus_fmt = ov772x_g_fmt,
+ .s_mbus_fmt = ov772x_s_fmt,
+ .try_mbus_fmt = ov772x_try_fmt,
.cropcap = ov772x_cropcap,
.g_crop = ov772x_g_crop,
+ .enum_mbus_fmt = ov772x_enum_fmt,
};
static struct v4l2_subdev_ops ov772x_subdev_ops = {
@@ -1143,10 +1142,10 @@ static int ov772x_probe(struct i2c_client *client,
}
icl = to_soc_camera_link(icd);
- if (!icl)
+ if (!icl || !icl->priv)
return -EINVAL;
- info = container_of(icl, struct ov772x_camera_info, link);
+ info = icl->priv;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&adapter->dev,
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index c81ae2192887..47bf60ceb7a2 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -154,19 +154,10 @@ static const struct ov9640_reg ov9640_regs_rgb[] = {
{ OV9640_MTXS, 0x65 },
};
-/*
- * TODO: this sensor also supports RGB555 and RGB565 formats, but support for
- * them has not yet been sufficiently tested and so it is not included with
- * this version of the driver. To test and debug these formats add two entries
- * to the below array, see ov722x.c for an example.
- */
-static const struct soc_camera_data_format ov9640_fmt_lists[] = {
- {
- .name = "UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
+static enum v4l2_mbus_pixelcode ov9640_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8_BE,
+ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ V4L2_MBUS_FMT_RGB565_2X8_LE,
};
static const struct v4l2_queryctrl ov9640_controls[] = {
@@ -434,20 +425,22 @@ static void ov9640_res_roundup(u32 *width, u32 *height)
}
/* Prepare necessary register changes depending on color encoding */
-static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt)
+static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
+ struct ov9640_reg_alt *alt)
{
- switch (pixfmt) {
- case V4L2_PIX_FMT_UYVY:
+ switch (code) {
+ default:
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
alt->com12 = OV9640_COM12_YUV_AVG;
alt->com13 = OV9640_COM13_Y_DELAY_EN |
OV9640_COM13_YUV_DLY(0x01);
break;
- case V4L2_PIX_FMT_RGB555:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
alt->com7 = OV9640_COM7_RGB;
alt->com13 = OV9640_COM13_RGB_AVG;
alt->com15 = OV9640_COM15_RGB_555;
break;
- case V4L2_PIX_FMT_RGB565:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
alt->com7 = OV9640_COM7_RGB;
alt->com13 = OV9640_COM13_RGB_AVG;
alt->com15 = OV9640_COM15_RGB_565;
@@ -456,8 +449,8 @@ static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt)
}
/* Setup registers according to resolution and color encoding */
-static int ov9640_write_regs(struct i2c_client *client,
- u32 width, u32 pixfmt, struct ov9640_reg_alt *alts)
+static int ov9640_write_regs(struct i2c_client *client, u32 width,
+ enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts)
{
const struct ov9640_reg *ov9640_regs, *matrix_regs;
int ov9640_regs_len, matrix_regs_len;
@@ -500,7 +493,7 @@ static int ov9640_write_regs(struct i2c_client *client,
}
/* select color matrix configuration for given color encoding */
- if (pixfmt == V4L2_PIX_FMT_UYVY) {
+ if (code == V4L2_MBUS_FMT_YUYV8_2X8_BE) {
matrix_regs = ov9640_regs_yuv;
matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
} else {
@@ -562,15 +555,17 @@ static int ov9640_prog_dflt(struct i2c_client *client)
}
/* set the format we will capture in */
-static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int ov9640_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
struct ov9640_reg_alt alts = {0};
+ enum v4l2_colorspace cspace;
+ enum v4l2_mbus_pixelcode code = mf->code;
int ret;
- ov9640_res_roundup(&pix->width, &pix->height);
- ov9640_alter_regs(pix->pixelformat, &alts);
+ ov9640_res_roundup(&mf->width, &mf->height);
+ ov9640_alter_regs(mf->code, &alts);
ov9640_reset(client);
@@ -578,19 +573,57 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
if (ret)
return ret;
- return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts);
+ switch (code) {
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ cspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ code = V4L2_MBUS_FMT_YUYV8_2X8_BE;
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
+ cspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ ret = ov9640_write_regs(client, mf->width, code, &alts);
+ if (!ret) {
+ mf->code = code;
+ mf->colorspace = cspace;
+ }
+
+ return ret;
}
-static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int ov9640_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
- struct v4l2_pix_format *pix = &f->fmt.pix;
+ ov9640_res_roundup(&mf->width, &mf->height);
- ov9640_res_roundup(&pix->width, &pix->height);
- pix->field = V4L2_FIELD_NONE;
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE;
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
return 0;
}
+static int ov9640_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if ((unsigned int)index >= ARRAY_SIZE(ov9640_codes))
+ return -EINVAL;
+
+ *code = ov9640_codes[index];
+ return 0;
+}
+
static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
a->c.left = 0;
@@ -637,9 +670,6 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
goto err;
}
- icd->formats = ov9640_fmt_lists;
- icd->num_formats = ARRAY_SIZE(ov9640_fmt_lists);
-
/*
* check and show product ID and manufacturer ID
*/
@@ -702,11 +732,12 @@ static struct v4l2_subdev_core_ops ov9640_core_ops = {
};
static struct v4l2_subdev_video_ops ov9640_video_ops = {
- .s_stream = ov9640_s_stream,
- .s_fmt = ov9640_s_fmt,
- .try_fmt = ov9640_try_fmt,
- .cropcap = ov9640_cropcap,
- .g_crop = ov9640_g_crop,
+ .s_stream = ov9640_s_stream,
+ .s_mbus_fmt = ov9640_s_fmt,
+ .try_mbus_fmt = ov9640_try_fmt,
+ .enum_mbus_fmt = ov9640_enum_fmt,
+ .cropcap = ov9640_cropcap,
+ .g_crop = ov9640_g_crop,
};
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index 73ec970ca5ca..11a2c26399b5 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -31,7 +31,7 @@
#include <linux/init.h>
#include <linux/version.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/videodev2.h>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 6aa48e0ae731..cc8ddb2d2382 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -151,17 +151,6 @@ static struct v4l2_format pvr_format [] = {
};
-static const char *get_v4l_name(int v4l_type)
-{
- switch (v4l_type) {
- case VFL_TYPE_GRABBER: return "video";
- case VFL_TYPE_RADIO: return "radio";
- case VFL_TYPE_VBI: return "vbi";
- default: return "?";
- }
-}
-
-
/*
* pvr_ioctl()
*
@@ -891,10 +880,8 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
{
- int num = dip->devbase.num;
struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
enum pvr2_config cfg = dip->config;
- int v4l_type = dip->v4l_type;
pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
@@ -906,8 +893,8 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
are gone. */
video_unregister_device(&dip->devbase);
- printk(KERN_INFO "pvrusb2: unregistered device %s%u [%s]\n",
- get_v4l_name(v4l_type), num,
+ printk(KERN_INFO "pvrusb2: unregistered device %s [%s]\n",
+ video_device_node_name(&dip->devbase),
pvr2_config_get_name(cfg));
}
@@ -1317,8 +1304,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
": Failed to register pvrusb2 v4l device\n");
}
- printk(KERN_INFO "pvrusb2: registered device %s%u [%s]\n",
- get_v4l_name(dip->v4l_type), dip->devbase.num,
+ printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
+ video_device_node_name(&dip->devbase),
pvr2_config_get_name(dip->config));
pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 89b620f6db7b..aea7e224cef6 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -169,7 +169,6 @@ static struct video_device pwc_template = {
.name = "Philips Webcam", /* Filled in later */
.release = video_device_release,
.fops = &pwc_fops,
- .minor = -1,
};
/***************************************************************************/
@@ -1807,7 +1806,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
goto err_video_release;
}
- PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num);
+ PWC_INFO("Registered as %s.\n", video_device_node_name(pdev->vdev));
/* occupy slot */
if (hint < MAX_DEV_HINTS)
@@ -1948,7 +1947,9 @@ MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif,
MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
+#ifdef CONFIG_USB_PWC_DEBUG
MODULE_PARM_DESC(trace, "For debugging purposes");
+#endif
MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 51b683c63b70..294f860ce2b0 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -32,6 +32,7 @@
#include <media/v4l2-dev.h>
#include <media/videobuf-dma-sg.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <linux/videodev2.h>
@@ -183,23 +184,21 @@ struct pxa_cam_dma {
/* buffer for one video frame */
struct pxa_buffer {
/* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
- const struct soc_camera_data_format *fmt;
-
+ struct videobuf_buffer vb;
+ enum v4l2_mbus_pixelcode code;
/* our descriptor lists for Y, U and V channels */
- struct pxa_cam_dma dmas[3];
-
- int inwork;
-
- enum pxa_camera_active_dma active_dma;
+ struct pxa_cam_dma dmas[3];
+ int inwork;
+ enum pxa_camera_active_dma active_dma;
};
struct pxa_camera_dev {
struct soc_camera_host soc_host;
- /* PXA27x is only supposed to handle one camera on its Quick Capture
+ /*
+ * PXA27x is only supposed to handle one camera on its Quick Capture
* interface. If anyone ever builds hardware to enable more than
- * one camera, they will have to modify this driver too */
+ * one camera, they will have to modify this driver too
+ */
struct soc_camera_device *icd;
struct clk *clk;
@@ -241,11 +240,15 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
- *size = roundup(icd->user_width * icd->user_height *
- ((icd->current_fmt->depth + 7) >> 3), 8);
+ *size = bytes_per_line * icd->user_height;
if (0 == *count)
*count = 32;
@@ -267,8 +270,10 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
&buf->vb, buf->vb.baddr, buf->vb.bsize);
- /* This waits until this buffer is out of danger, i.e., until it is no
- * longer in STATE_QUEUED or STATE_ACTIVE */
+ /*
+ * This waits until this buffer is out of danger, i.e., until it is no
+ * longer in STATE_QUEUED or STATE_ACTIVE
+ */
videobuf_waiton(&buf->vb, 0, 0);
videobuf_dma_unmap(vq, dma);
videobuf_dma_free(dma);
@@ -429,6 +434,11 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
int ret;
int size_y, size_u = 0, size_v = 0;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
@@ -437,29 +447,33 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
WARN_ON(!list_empty(&vb->queue));
#ifdef DEBUG
- /* This can be useful if you want to see if we actually fill
- * the buffer with something */
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
memset((void *)vb->baddr, 0xaa, vb->bsize);
#endif
BUG_ON(NULL == icd->current_fmt);
- /* I think, in buf_prepare you only have to protect global data,
- * the actual buffer is yours */
+ /*
+ * I think, in buf_prepare you only have to protect global data,
+ * the actual buffer is yours
+ */
buf->inwork = 1;
- if (buf->fmt != icd->current_fmt ||
+ if (buf->code != icd->current_fmt->code ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
- buf->fmt = icd->current_fmt;
+ buf->code = icd->current_fmt->code;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
- vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3);
+ vb->size = bytes_per_line * vb->height;
if (0 != vb->baddr && vb->bsize < vb->size) {
ret = -EINVAL;
goto out;
@@ -834,8 +848,10 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
- /* We must pass NULL as dev pointer, then all pci_* dma operations
- * transform to normal dma_* ones. */
+ /*
+ * We must pass NULL as dev pointer, then all pci_* dma operations
+ * transform to normal dma_* ones.
+ */
videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
sizeof(struct pxa_buffer), icd);
@@ -1051,11 +1067,18 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
unsigned long dw, bpp;
- u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0;
+ u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0, y_skip_top;
+ int ret = v4l2_subdev_call(sd, sensor, g_skip_top_lines, &y_skip_top);
+
+ if (ret < 0)
+ y_skip_top = 0;
- /* Datawidth is now guaranteed to be equal to one of the three values.
- * We fix bit-per-pixel equal to data-width... */
+ /*
+ * Datawidth is now guaranteed to be equal to one of the three values.
+ * We fix bit-per-pixel equal to data-width...
+ */
switch (flags & SOCAM_DATAWIDTH_MASK) {
case SOCAM_DATAWIDTH_10:
dw = 4;
@@ -1066,8 +1089,10 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
bpp = 0x20;
break;
default:
- /* Actually it can only be 8 now,
- * default is just to silence compiler warnings */
+ /*
+ * Actually it can only be 8 now,
+ * default is just to silence compiler warnings
+ */
case SOCAM_DATAWIDTH_8:
dw = 2;
bpp = 0;
@@ -1118,7 +1143,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
cicr2 = 0;
cicr3 = CICR3_LPF_VAL(icd->user_height - 1) |
- CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
+ CICR3_BFW_VAL(min((u32)255, y_skip_top));
cicr4 |= pcdev->mclk_divisor;
__raw_writel(cicr1, pcdev->base + CICR1);
@@ -1138,9 +1163,15 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
unsigned long bus_flags, camera_flags, common_flags;
- int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
+ const struct soc_mbus_pixelfmt *fmt;
+ int ret;
struct pxa_cam *cam = icd->host_priv;
+ fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ ret = test_platform_param(pcdev, fmt->bits_per_sample, &bus_flags);
if (ret < 0)
return ret;
@@ -1204,59 +1235,49 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
}
-static const struct soc_camera_data_format pxa_camera_formats[] = {
+static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
{
- .name = "Planar YUV422 16 bit",
- .depth = 16,
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .colorspace = V4L2_COLORSPACE_JPEG,
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .name = "Planar YUV422 16 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
},
};
-static bool buswidth_supported(struct soc_camera_device *icd, int depth)
+/* This will be corrected as we get more formats */
+static bool pxa_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct pxa_camera_dev *pcdev = ici->priv;
-
- switch (depth) {
- case 8:
- return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8);
- case 9:
- return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9);
- case 10:
- return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10);
- }
- return false;
-}
-
-static int required_buswidth(const struct soc_camera_data_format *fmt)
-{
- switch (fmt->fourcc) {
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB555:
- return 8;
- default:
- return fmt->depth;
- }
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
}
static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
struct soc_camera_format_xlate *xlate)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->dev.parent;
- int formats = 0, buswidth, ret;
+ int formats = 0, ret;
struct pxa_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
- buswidth = required_buswidth(icd->formats + idx);
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
- if (!buswidth_supported(icd, buswidth))
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(dev, "Invalid format code #%d: %d\n", idx, code);
return 0;
+ }
- ret = pxa_camera_try_bus_param(icd, buswidth);
+ /* This also checks support for the requested bits-per-sample */
+ ret = pxa_camera_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
return 0;
@@ -1270,45 +1291,40 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
cam = icd->host_priv;
}
- switch (icd->formats[idx].fourcc) {
- case V4L2_PIX_FMT_UYVY:
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
formats++;
if (xlate) {
- xlate->host_fmt = &pxa_camera_formats[0];
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = buswidth;
+ xlate->host_fmt = &pxa_camera_formats[0];
+ xlate->code = code;
xlate++;
- dev_dbg(dev, "Providing format %s using %s\n",
- pxa_camera_formats[0].name,
- icd->formats[idx].name);
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ pxa_camera_formats[0].name, code);
}
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB555:
- formats++;
- if (xlate) {
- xlate->host_fmt = icd->formats + idx;
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = buswidth;
- xlate++;
+ case V4L2_MBUS_FMT_YVYU8_2X8_BE:
+ case V4L2_MBUS_FMT_YUYV8_2X8_LE:
+ case V4L2_MBUS_FMT_YVYU8_2X8_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ if (xlate)
dev_dbg(dev, "Providing format %s packed\n",
- icd->formats[idx].name);
- }
+ fmt->name);
break;
default:
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = icd->formats + idx;
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = icd->formats[idx].depth;
- xlate++;
+ if (!pxa_camera_packing_supported(fmt))
+ return 0;
+ if (xlate)
dev_dbg(dev,
"Providing format %s in pass-through mode\n",
- icd->formats[idx].name);
- }
+ fmt->name);
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
}
return formats;
@@ -1320,11 +1336,11 @@ static void pxa_camera_put_formats(struct soc_camera_device *icd)
icd->host_priv = NULL;
}
-static int pxa_camera_check_frame(struct v4l2_pix_format *pix)
+static int pxa_camera_check_frame(u32 width, u32 height)
{
/* limit to pxa hardware capabilities */
- return pix->height < 32 || pix->height > 2048 || pix->width < 48 ||
- pix->width > 2048 || (pix->width & 0x01);
+ return height < 32 || height > 2048 || width < 48 || width > 2048 ||
+ (width & 0x01);
}
static int pxa_camera_set_crop(struct soc_camera_device *icd,
@@ -1339,9 +1355,9 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
.master_clock = pcdev->mclk,
.pixel_clock_max = pcdev->ciclk / 4,
};
- struct v4l2_format f;
- struct v4l2_pix_format *pix = &f.fmt.pix, pix_tmp;
+ struct v4l2_mbus_framefmt mf;
struct pxa_cam *cam = icd->host_priv;
+ u32 fourcc = icd->current_fmt->host_fmt->fourcc;
int ret;
/* If PCLK is used to latch data from the sensor, check sense */
@@ -1358,27 +1374,23 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
return ret;
}
- f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
if (ret < 0)
return ret;
- pix_tmp = *pix;
- if (pxa_camera_check_frame(pix)) {
+ if (pxa_camera_check_frame(mf.width, mf.height)) {
/*
* Camera cropping produced a frame beyond our capabilities.
* FIXME: just extract a subframe, that we can process.
*/
- v4l_bound_align_image(&pix->width, 48, 2048, 1,
- &pix->height, 32, 2048, 0,
- icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P ?
- 4 : 0);
- ret = v4l2_subdev_call(sd, video, s_fmt, &f);
+ v4l_bound_align_image(&mf.width, 48, 2048, 1,
+ &mf.height, 32, 2048, 0,
+ fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
if (ret < 0)
return ret;
- if (pxa_camera_check_frame(pix)) {
+ if (pxa_camera_check_frame(mf.width, mf.height)) {
dev_warn(icd->dev.parent,
"Inconsistent state. Use S_FMT to repair\n");
return -EINVAL;
@@ -1395,10 +1407,10 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
recalculate_fifo_timeout(pcdev, sense.pixel_clock);
}
- icd->user_width = pix->width;
- icd->user_height = pix->height;
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
- pxa_camera_setup_cicr(icd, cam->flags, icd->current_fmt->fourcc);
+ pxa_camera_setup_cicr(icd, cam->flags, fourcc);
return ret;
}
@@ -1410,14 +1422,13 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
struct pxa_camera_dev *pcdev = ici->priv;
struct device *dev = icd->dev.parent;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_data_format *cam_fmt = NULL;
const struct soc_camera_format_xlate *xlate = NULL;
struct soc_camera_sense sense = {
.master_clock = pcdev->mclk,
.pixel_clock_max = pcdev->ciclk / 4,
};
struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_format cam_f = *f;
+ struct v4l2_mbus_framefmt mf;
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
@@ -1426,26 +1437,31 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
return -EINVAL;
}
- cam_fmt = xlate->cam_fmt;
-
/* If PCLK is used to latch data from the sensor, check sense */
if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ /* The caller holds a mutex. */
icd->sense = &sense;
- cam_f.fmt.pix.pixelformat = cam_fmt->fourcc;
- ret = v4l2_subdev_call(sd, video, s_fmt, &cam_f);
- cam_f.fmt.pix.pixelformat = pix->pixelformat;
- *pix = cam_f.fmt.pix;
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
icd->sense = NULL;
if (ret < 0) {
dev_warn(dev, "Failed to configure for format %x\n",
pix->pixelformat);
- } else if (pxa_camera_check_frame(pix)) {
+ } else if (pxa_camera_check_frame(mf.width, mf.height)) {
dev_warn(dev,
"Camera driver produced an unsupported frame %dx%d\n",
- pix->width, pix->height);
+ mf.width, mf.height);
ret = -EINVAL;
} else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
if (sense.pixel_clock > sense.pixel_clock_max) {
@@ -1457,10 +1473,14 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
recalculate_fifo_timeout(pcdev, sense.pixel_clock);
}
- if (!ret) {
- icd->buswidth = xlate->buswidth;
- icd->current_fmt = xlate->host_fmt;
- }
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
return ret;
}
@@ -1468,17 +1488,16 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
static int pxa_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
- enum v4l2_field field;
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(ici->v4l2_dev.dev, "Format %x not found\n", pixfmt);
+ dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
@@ -1492,22 +1511,36 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
&pix->height, 32, 2048, 0,
pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
- pix->bytesperline = pix->width *
- DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
pix->sizeimage = pix->height * pix->bytesperline;
- /* camera has to see its format, but the user the original one */
- pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- ret = v4l2_subdev_call(sd, video, try_fmt, f);
- pix->pixelformat = pixfmt;
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
- field = pix->field;
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
- if (field == V4L2_FIELD_ANY) {
- pix->field = V4L2_FIELD_NONE;
- } else if (field != V4L2_FIELD_NONE) {
- dev_err(icd->dev.parent, "Field type %d unsupported.\n", field);
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
+
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
+ case V4L2_FIELD_NONE:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ default:
+ /* TODO: support interlaced at least in pass-through mode */
+ dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+ mf.field);
return -EINVAL;
}
@@ -1519,10 +1552,12 @@ static int pxa_camera_reqbufs(struct soc_camera_file *icf,
{
int i;
- /* This is for locking debugging only. I removed spinlocks and now I
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
* check whether .prepare is ever called on a linked buffer, or whether
* a dma IRQ can occur for an in-work or unlinked buffer. Until now
- * it hadn't triggered */
+ * it hadn't triggered
+ */
for (i = 0; i < p->count; i++) {
struct pxa_buffer *buf = container_of(icf->vb_vidq.bufs[i],
struct pxa_buffer, vb);
@@ -1657,8 +1692,10 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
pcdev->platform_flags = pcdev->pdata->flags;
if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 |
PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) {
- /* Platform hasn't set available data widths. This is bad.
- * Warn and use a default. */
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
"data widths, using default 10 bit\n");
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 373f2a30a677..7e42989ce0e4 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -13,9 +13,11 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <media/rj54n1cb0c.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
#define RJ54N1_DEV_CODE 0x0400
#define RJ54N1_DEV_CODE2 0x0401
@@ -38,6 +40,7 @@
#define RJ54N1_H_OBEN_OFS 0x0413
#define RJ54N1_V_OBEN_OFS 0x0414
#define RJ54N1_RESIZE_CONTROL 0x0415
+#define RJ54N1_STILL_CONTROL 0x0417
#define RJ54N1_INC_USE_SEL_H 0x0425
#define RJ54N1_INC_USE_SEL_L 0x0426
#define RJ54N1_MIRROR_STILL_MODE 0x0427
@@ -49,10 +52,21 @@
#define RJ54N1_RA_SEL_UL 0x0530
#define RJ54N1_BYTE_SWAP 0x0531
#define RJ54N1_OUT_SIGPO 0x053b
+#define RJ54N1_WB_SEL_WEIGHT_I 0x054e
+#define RJ54N1_BIT8_WB 0x0569
+#define RJ54N1_HCAPS_WB 0x056a
+#define RJ54N1_VCAPS_WB 0x056b
+#define RJ54N1_HCAPE_WB 0x056c
+#define RJ54N1_VCAPE_WB 0x056d
+#define RJ54N1_EXPOSURE_CONTROL 0x058c
#define RJ54N1_FRAME_LENGTH_S_H 0x0595
#define RJ54N1_FRAME_LENGTH_S_L 0x0596
#define RJ54N1_FRAME_LENGTH_P_H 0x0597
#define RJ54N1_FRAME_LENGTH_P_L 0x0598
+#define RJ54N1_PEAK_H 0x05b7
+#define RJ54N1_PEAK_50 0x05b8
+#define RJ54N1_PEAK_60 0x05b9
+#define RJ54N1_PEAK_DIFF 0x05ba
#define RJ54N1_IOC 0x05ef
#define RJ54N1_TG_BYPASS 0x0700
#define RJ54N1_PLL_L 0x0701
@@ -68,6 +82,7 @@
#define RJ54N1_OCLK_SEL_EN 0x0713
#define RJ54N1_CLK_RST 0x0717
#define RJ54N1_RESET_STANDBY 0x0718
+#define RJ54N1_FWFLG 0x07fe
#define E_EXCLK (1 << 7)
#define SOFT_STDBY (1 << 4)
@@ -78,29 +93,53 @@
#define RESIZE_HOLD_SEL (1 << 2)
#define RESIZE_GO (1 << 1)
+/*
+ * When cropping, the camera automatically centers the cropped region, there
+ * doesn't seem to be a way to specify an explicit location of the rectangle.
+ */
#define RJ54N1_COLUMN_SKIP 0
#define RJ54N1_ROW_SKIP 0
#define RJ54N1_MAX_WIDTH 1600
#define RJ54N1_MAX_HEIGHT 1200
+#define PLL_L 2
+#define PLL_N 0x31
+
/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
-static const struct soc_camera_data_format rj54n1_colour_formats[] = {
- {
- .name = "YUYV",
- .depth = 16,
- .fourcc = V4L2_PIX_FMT_YUYV,
- .colorspace = V4L2_COLORSPACE_JPEG,
- }, {
- .name = "RGB565",
- .depth = 16,
- .fourcc = V4L2_PIX_FMT_RGB565,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }
+/* RJ54N1CB0C has only one fixed colorspace per pixelcode */
+struct rj54n1_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct rj54n1_datafmt *rj54n1_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct rj54n1_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct rj54n1_datafmt rj54n1_colour_fmts[] = {
+ {V4L2_MBUS_FMT_YUYV8_2X8_LE, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8_LE, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
};
struct rj54n1_clock_div {
- u8 ratio_tg;
+ u8 ratio_tg; /* can be 0 or an odd number */
u8 ratio_t;
u8 ratio_r;
u8 ratio_op;
@@ -109,12 +148,14 @@ struct rj54n1_clock_div {
struct rj54n1 {
struct v4l2_subdev subdev;
+ struct rj54n1_clock_div clk_div;
+ const struct rj54n1_datafmt *fmt;
struct v4l2_rect rect; /* Sensor window */
+ unsigned int tgclk_mhz;
+ bool auto_wb;
unsigned short width; /* Output window */
unsigned short height;
unsigned short resize; /* Sensor * 1024 / resize = Output */
- struct rj54n1_clock_div clk_div;
- u32 fourcc;
unsigned short scale;
u8 bank;
};
@@ -171,7 +212,7 @@ const static struct rj54n1_reg_val bank_7[] = {
{0x714, 0xff},
{0x715, 0xff},
{0x716, 0x1f},
- {0x7FE, 0x02},
+ {0x7FE, 2},
};
const static struct rj54n1_reg_val bank_8[] = {
@@ -359,7 +400,7 @@ const static struct rj54n1_reg_val bank_8[] = {
{0x8BB, 0x00},
{0x8BC, 0xFF},
{0x8BD, 0x00},
- {0x8FE, 0x02},
+ {0x8FE, 2},
};
const static struct rj54n1_reg_val bank_10[] = {
@@ -440,12 +481,24 @@ static int reg_write_multiple(struct i2c_client *client,
return 0;
}
-static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
+static int rj54n1_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
{
- /* TODO: start / stop streaming */
+ if ((unsigned int)index >= ARRAY_SIZE(rj54n1_colour_fmts))
+ return -EINVAL;
+
+ *code = rj54n1_colour_fmts[index].code;
return 0;
}
+static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = sd->priv;
+
+ /* Switch between preview and still shot modes */
+ return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
+}
+
static int rj54n1_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
@@ -502,6 +555,44 @@ static int rj54n1_commit(struct i2c_client *client)
return ret;
}
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
+ u32 *out_w, u32 *out_h);
+
+static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct v4l2_rect *rect = &a->c;
+ unsigned int dummy, output_w, output_h,
+ input_w = rect->width, input_h = rect->height;
+ int ret;
+
+ /* arbitrary minimum width and height, edges unimportant */
+ soc_camera_limit_side(&dummy, &input_w,
+ RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH);
+
+ soc_camera_limit_side(&dummy, &input_h,
+ RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT);
+
+ output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize;
+ output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize;
+
+ dev_dbg(&client->dev, "Scaling for %ux%u : %u = %ux%u\n",
+ input_w, input_h, rj54n1->resize, output_w, output_h);
+
+ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+ if (ret < 0)
+ return ret;
+
+ rj54n1->width = output_w;
+ rj54n1->height = output_h;
+ rj54n1->resize = ret;
+ rj54n1->rect.width = input_w;
+ rj54n1->rect.height = input_h;
+
+ return 0;
+}
+
static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = sd->priv;
@@ -527,16 +618,17 @@ static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int rj54n1_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int rj54n1_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- pix->pixelformat = rj54n1->fourcc;
- pix->field = V4L2_FIELD_NONE;
- pix->width = rj54n1->width;
- pix->height = rj54n1->height;
+ mf->code = rj54n1->fmt->code;
+ mf->colorspace = rj54n1->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mf->width = rj54n1->width;
+ mf->height = rj54n1->height;
return 0;
}
@@ -550,11 +642,44 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
u32 *out_w, u32 *out_h)
{
struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
output_w = *out_w, output_h = *out_h;
- u16 inc_sel;
+ u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom;
+ unsigned int peak, peak_50, peak_60;
int ret;
+ /*
+ * We have a problem with crops, where the window is larger than 512x384
+ * and output window is larger than a half of the input one. In this
+ * case we have to either reduce the input window to equal or below
+ * 512x384 or the output window to equal or below 1/2 of the input.
+ */
+ if (output_w > max(512U, input_w / 2)) {
+ if (2 * output_w > RJ54N1_MAX_WIDTH) {
+ input_w = RJ54N1_MAX_WIDTH;
+ output_w = RJ54N1_MAX_WIDTH / 2;
+ } else {
+ input_w = output_w * 2;
+ }
+
+ dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n",
+ input_w, output_w);
+ }
+
+ if (output_h > max(384U, input_h / 2)) {
+ if (2 * output_h > RJ54N1_MAX_HEIGHT) {
+ input_h = RJ54N1_MAX_HEIGHT;
+ output_h = RJ54N1_MAX_HEIGHT / 2;
+ } else {
+ input_h = output_h * 2;
+ }
+
+ dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n",
+ input_h, output_h);
+ }
+
+ /* Idea: use the read mode for snapshots, handle separate geometries */
ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
RJ54N1_Y_OUTPUT_SIZE_S_L,
RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
@@ -566,17 +691,27 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
if (ret < 0)
return ret;
- if (output_w > input_w || output_h > input_h) {
+ if (output_w > input_w && output_h > input_h) {
input_w = output_w;
input_h = output_h;
resize = 1024;
} else {
unsigned int resize_x, resize_y;
- resize_x = input_w * 1024 / output_w;
- resize_y = input_h * 1024 / output_h;
-
- resize = min(resize_x, resize_y);
+ resize_x = (input_w * 1024 + output_w / 2) / output_w;
+ resize_y = (input_h * 1024 + output_h / 2) / output_h;
+
+ /* We want max(resize_x, resize_y), check if it still fits */
+ if (resize_x > resize_y &&
+ (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT)
+ resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) /
+ output_h;
+ else if (resize_y > resize_x &&
+ (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH)
+ resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) /
+ output_w;
+ else
+ resize = max(resize_x, resize_y);
/* Prohibited value ranges */
switch (resize) {
@@ -589,12 +724,9 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
case 8160 ... 8191:
resize = 8159;
break;
- case 16320 ... 16383:
+ case 16320 ... 16384:
resize = 16319;
}
-
- input_w = output_w * resize / 1024;
- input_h = output_h * resize / 1024;
}
/* Set scaling */
@@ -607,9 +739,18 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
/*
* Configure a skipping bitmask. The sensor will select a skipping value
- * among set bits automatically.
+ * among set bits automatically. This is very unclear in the datasheet
+ * too. I was told, in this register one enables all skipping values,
+ * that are required for a specific resize, and the camera selects
+ * automatically, which ones to use. But it is unclear how to identify,
+ * which cropping values are needed. Secondly, why don't we just set all
+ * bits and let the camera choose? Would it increase processing time and
+ * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to
+ * improve the image quality or stability for larger frames (see comment
+ * above), but I didn't check the framerate.
*/
skip = min(resize / 1024, (unsigned)15);
+
inc_sel = 1 << skip;
if (inc_sel <= 2)
@@ -621,6 +762,43 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
if (!ret)
ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
+ if (!rj54n1->auto_wb) {
+ /* Auto white balance window */
+ wb_left = output_w / 16;
+ wb_right = (3 * output_w / 4 - 3) / 4;
+ wb_top = output_h / 16;
+ wb_bottom = (3 * output_h / 4 - 3) / 4;
+ wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) |
+ ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1);
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom);
+ }
+
+ /* Antiflicker */
+ peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz /
+ 10000;
+ peak_50 = peak / 6;
+ peak_60 = peak / 5;
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_H,
+ ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8));
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_50, peak_50);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_60, peak_60);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150);
+
/* Start resizing */
if (!ret)
ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
@@ -629,8 +807,6 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
if (ret < 0)
return ret;
- dev_dbg(&client->dev, "resize %u, skip %u\n", resize, skip);
-
/* Constant taken from manufacturer's example */
msleep(230);
@@ -638,11 +814,14 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
if (ret < 0)
return ret;
- *in_w = input_w;
- *in_h = input_h;
+ *in_w = (output_w * resize + 512) / 1024;
+ *in_h = (output_h * resize + 512) / 1024;
*out_w = output_w;
*out_h = output_h;
+ dev_dbg(&client->dev, "Scaled for %ux%u : %u = %ux%u, skip %u\n",
+ *in_w, *in_h, resize, output_w, output_h, skip);
+
return resize;
}
@@ -653,14 +832,14 @@ static int rj54n1_set_clock(struct i2c_client *client)
/* Enable external clock */
ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
- /* Leave stand-by */
+ /* Leave stand-by. Note: use this when implementing suspend / resume */
if (!ret)
ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
if (!ret)
- ret = reg_write(client, RJ54N1_PLL_L, 2);
+ ret = reg_write(client, RJ54N1_PLL_L, PLL_L);
if (!ret)
- ret = reg_write(client, RJ54N1_PLL_N, 0x31);
+ ret = reg_write(client, RJ54N1_PLL_N, PLL_N);
/* TGCLK dividers */
if (!ret)
@@ -719,6 +898,7 @@ static int rj54n1_set_clock(struct i2c_client *client)
"Resetting RJ54N1CB0C clock failed: %d!\n", ret);
return -EIO;
}
+
/* Start the PLL */
ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
@@ -731,6 +911,7 @@ static int rj54n1_set_clock(struct i2c_client *client)
static int rj54n1_reg_init(struct i2c_client *client)
{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
int ret = rj54n1_set_clock(client);
if (!ret)
@@ -753,14 +934,26 @@ static int rj54n1_reg_init(struct i2c_client *client)
if (!ret)
ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
- /* Mirror the image back: default is upside down and left-to-right... */
+ /*
+ * Mirror the image back: default is upside down and left-to-right...
+ * Set manual preview / still shot switching
+ */
if (!ret)
- ret = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 3, 3);
+ ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27);
if (!ret)
ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
+
+ /* Auto exposure area */
if (!ret)
+ ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80);
+ /* Check current auto WB config */
+ if (!ret)
+ ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I);
+ if (ret >= 0) {
+ rj54n1->auto_wb = ret & 0x80;
ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
+ }
if (!ret)
ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
@@ -777,8 +970,9 @@ static int rj54n1_reg_init(struct i2c_client *client)
ret = reg_write(client, RJ54N1_RESET_STANDBY,
E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
+ /* Start register update? Same register as 0x?FE in many bank_* sets */
if (!ret)
- ret = reg_write(client, 0x7fe, 2);
+ ret = reg_write(client, RJ54N1_FWFLG, 2);
/* Constant taken from manufacturer's example */
msleep(700);
@@ -786,27 +980,44 @@ static int rj54n1_reg_init(struct i2c_client *client)
return ret;
}
-/* FIXME: streaming output only up to 800x600 is functional */
-static int rj54n1_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int rj54n1_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
- struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ const struct rj54n1_datafmt *fmt;
+ int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE;
+
+ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
+ __func__, mf->code, mf->width, mf->height);
+
+ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
+ ARRAY_SIZE(rj54n1_colour_fmts));
+ if (!fmt) {
+ fmt = rj54n1->fmt;
+ mf->code = fmt->code;
+ }
- pix->field = V4L2_FIELD_NONE;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = fmt->colorspace;
- if (pix->width > 800)
- pix->width = 800;
- if (pix->height > 600)
- pix->height = 600;
+ v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align,
+ &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0);
return 0;
}
-static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int rj54n1_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- unsigned int output_w, output_h,
+ const struct rj54n1_datafmt *fmt;
+ unsigned int output_w, output_h, max_w, max_h,
input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
int ret;
@@ -814,14 +1025,13 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
* The host driver can call us without .try_fmt(), so, we have to take
* care ourseleves
*/
- ret = rj54n1_try_fmt(sd, f);
+ rj54n1_try_fmt(sd, mf);
/*
* Verify if the sensor has just been powered on. TODO: replace this
* with proper PM, when a suitable API is available.
*/
- if (!ret)
- ret = reg_read(client, RJ54N1_RESET_STANDBY);
+ ret = reg_read(client, RJ54N1_RESET_STANDBY);
if (ret < 0)
return ret;
@@ -831,50 +1041,105 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
return ret;
}
+ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
+ __func__, mf->code, mf->width, mf->height);
+
/* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_YUYV:
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 0);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
break;
- case V4L2_PIX_FMT_RGB565:
+ case V4L2_MBUS_FMT_YVYU8_2X8_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 5);
break;
default:
ret = -EINVAL;
}
+ /* Special case: a raw mode with 10 bits of data per clock tick */
+ if (!ret)
+ ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
+ (mf->code == V4L2_MBUS_FMT_SBGGR10_1X10) << 1, 2);
+
if (ret < 0)
return ret;
- /* Supported scales 1:1 - 1:16 */
- if (pix->width < input_w / 16)
- pix->width = input_w / 16;
- if (pix->height < input_h / 16)
- pix->height = input_h / 16;
+ /* Supported scales 1:1 >= scale > 1:16 */
+ max_w = mf->width * (16 * 1024 - 1) / 1024;
+ if (input_w > max_w)
+ input_w = max_w;
+ max_h = mf->height * (16 * 1024 - 1) / 1024;
+ if (input_h > max_h)
+ input_h = max_h;
- output_w = pix->width;
- output_h = pix->height;
+ output_w = mf->width;
+ output_h = mf->height;
ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
if (ret < 0)
return ret;
- rj54n1->fourcc = pix->pixelformat;
+ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
+ ARRAY_SIZE(rj54n1_colour_fmts));
+
+ rj54n1->fmt = fmt;
rj54n1->resize = ret;
rj54n1->rect.width = input_w;
rj54n1->rect.height = input_h;
rj54n1->width = output_w;
rj54n1->height = output_h;
- pix->width = output_w;
- pix->height = output_h;
- pix->field = V4L2_FIELD_NONE;
+ mf->width = output_w;
+ mf->height = output_h;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = fmt->colorspace;
- return ret;
+ return 0;
}
static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
@@ -963,6 +1228,14 @@ static const struct v4l2_queryctrl rj54n1_controls[] = {
.step = 1,
.default_value = 66,
.flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
},
};
@@ -976,6 +1249,7 @@ static struct soc_camera_ops rj54n1_ops = {
static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
int data;
switch (ctrl->id) {
@@ -998,6 +1272,9 @@ static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
ctrl->value = data / 2;
break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ctrl->value = rj54n1->auto_wb;
+ break;
}
return 0;
@@ -1007,6 +1284,7 @@ static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
int data;
struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
const struct v4l2_queryctrl *qctrl;
qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
@@ -1037,6 +1315,13 @@ static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
return -EIO;
break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ /* Auto WB area - whole image */
+ if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->value << 7,
+ 0x80) < 0)
+ return -EIO;
+ rj54n1->auto_wb = ctrl->value;
+ break;
}
return 0;
@@ -1054,10 +1339,12 @@ static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
.s_stream = rj54n1_s_stream,
- .s_fmt = rj54n1_s_fmt,
- .g_fmt = rj54n1_g_fmt,
- .try_fmt = rj54n1_try_fmt,
+ .s_mbus_fmt = rj54n1_s_fmt,
+ .g_mbus_fmt = rj54n1_g_fmt,
+ .try_mbus_fmt = rj54n1_try_fmt,
+ .enum_mbus_fmt = rj54n1_enum_fmt,
.g_crop = rj54n1_g_crop,
+ .s_crop = rj54n1_s_crop,
.cropcap = rj54n1_cropcap,
};
@@ -1066,21 +1353,13 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = {
.video = &rj54n1_subdev_video_ops,
};
-static int rj54n1_pin_config(struct i2c_client *client)
-{
- /*
- * Experimentally found out IOCTRL wired to 0. TODO: add to platform
- * data: 0 or 1 << 7.
- */
- return reg_write(client, RJ54N1_IOC, 0);
-}
-
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
static int rj54n1_video_probe(struct soc_camera_device *icd,
- struct i2c_client *client)
+ struct i2c_client *client,
+ struct rj54n1_pdata *priv)
{
int data1, data2;
int ret;
@@ -1101,7 +1380,8 @@ static int rj54n1_video_probe(struct soc_camera_device *icd,
goto ei2c;
}
- ret = rj54n1_pin_config(client);
+ /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */
+ ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7);
if (ret < 0)
goto ei2c;
@@ -1119,6 +1399,7 @@ static int rj54n1_probe(struct i2c_client *client,
struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct soc_camera_link *icl;
+ struct rj54n1_pdata *rj54n1_priv;
int ret;
if (!icd) {
@@ -1127,11 +1408,13 @@ static int rj54n1_probe(struct i2c_client *client,
}
icl = to_soc_camera_link(icd);
- if (!icl) {
+ if (!icl || !icl->priv) {
dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
return -EINVAL;
}
+ rj54n1_priv = icl->priv;
+
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
@@ -1153,10 +1436,12 @@ static int rj54n1_probe(struct i2c_client *client,
rj54n1->rect.height = RJ54N1_MAX_HEIGHT;
rj54n1->width = RJ54N1_MAX_WIDTH;
rj54n1->height = RJ54N1_MAX_HEIGHT;
- rj54n1->fourcc = V4L2_PIX_FMT_YUYV;
+ rj54n1->fmt = &rj54n1_colour_fmts[0];
rj54n1->resize = 1024;
+ rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
+ (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
- ret = rj54n1_video_probe(icd, client);
+ ret = rj54n1_video_probe(icd, client, rj54n1_priv);
if (ret < 0) {
icd->ops = NULL;
i2c_set_clientdata(client, NULL);
@@ -1164,9 +1449,6 @@ static int rj54n1_probe(struct i2c_client *client,
return ret;
}
- icd->formats = rj54n1_colour_formats;
- icd->num_formats = ARRAY_SIZE(rj54n1_colour_formats);
-
return ret;
}
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 41765f3c7c28..fb742f1ae711 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -233,7 +233,6 @@ struct s2255_dev {
struct s2255_dmaqueue vidq[MAX_CHANNELS];
struct video_device *vdev[MAX_CHANNELS];
- struct list_head s2255_devlist;
struct timer_list timer;
struct s2255_fw *fw_data;
struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS];
@@ -313,8 +312,6 @@ struct s2255_fh {
/* Channels on box are in reverse order */
static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
-static LIST_HEAD(s2255_devlist);
-
static int debug;
static int *s2255_debug = &debug;
@@ -1533,32 +1530,24 @@ static int vidioc_s_parm(struct file *file, void *priv,
}
static int s2255_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct s2255_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct s2255_dev *dev = video_drvdata(file);
struct s2255_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int i = 0;
int cur_channel = -1;
int state;
- dprintk(1, "s2255: open called (minor=%d)\n", minor);
+
+ dprintk(1, "s2255: open called (dev=%s)\n",
+ video_device_node_name(vdev));
lock_kernel();
- list_for_each(list, &s2255_devlist) {
- h = list_entry(list, struct s2255_dev, s2255_devlist);
- for (i = 0; i < MAX_CHANNELS; i++) {
- if (h->vdev[i]->minor == minor) {
- cur_channel = i;
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
- }
- if ((NULL == dev) || (cur_channel == -1)) {
- unlock_kernel();
- printk(KERN_INFO "s2255: openv4l no dev\n");
- return -ENODEV;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (dev->vdev[i] == vdev) {
+ cur_channel = i;
+ break;
+ }
}
if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING) {
@@ -1662,8 +1651,9 @@ static int s2255_open(struct file *file)
for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
qctl_regs[i] = s2255_qctrl[i].default_value;
- dprintk(1, "s2255drv: open minor=%d type=%s users=%d\n",
- minor, v4l2_type_names[type], dev->users[cur_channel]);
+ dprintk(1, "s2255drv: open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[type],
+ dev->users[cur_channel]);
dprintk(2, "s2255drv: open: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n",
(unsigned long)fh, (unsigned long)dev,
(unsigned long)&dev->vidq[cur_channel]);
@@ -1699,7 +1689,6 @@ static unsigned int s2255_poll(struct file *file,
static void s2255_destroy(struct kref *kref)
{
struct s2255_dev *dev = to_s2255_dev(kref);
- struct list_head *list;
int i;
if (!dev) {
printk(KERN_ERR "s2255drv: kref problem\n");
@@ -1733,10 +1722,6 @@ static void s2255_destroy(struct kref *kref)
usb_put_dev(dev->udev);
dprintk(1, "%s", __func__);
- while (!list_empty(&s2255_devlist)) {
- list = s2255_devlist.next;
- list_del(list);
- }
mutex_unlock(&dev->open_lock);
kfree(dev);
}
@@ -1745,7 +1730,8 @@ static int s2255_close(struct file *file)
{
struct s2255_fh *fh = file->private_data;
struct s2255_dev *dev = fh->dev;
- int minor = video_devdata(file)->minor;
+ struct video_device *vdev = video_devdata(file);
+
if (!dev)
return -ENODEV;
@@ -1765,8 +1751,8 @@ static int s2255_close(struct file *file)
mutex_unlock(&dev->open_lock);
kref_put(&dev->kref, s2255_destroy);
- dprintk(1, "s2255: close called (minor=%d, users=%d)\n",
- minor, dev->users[fh->channel]);
+ dprintk(1, "s2255: close called (dev=%s, users=%d)\n",
+ video_device_node_name(vdev), dev->users[fh->channel]);
kfree(fh);
return 0;
}
@@ -1830,7 +1816,6 @@ static struct video_device template = {
.name = "s2255v",
.fops = &s2255_fops_v4l,
.ioctl_ops = &s2255_ioctl_ops,
- .minor = -1,
.release = video_device_release,
.tvnorms = S2255_NORMS,
.current_norm = V4L2_STD_NTSC_M,
@@ -1843,7 +1828,6 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
int cur_nr = video_nr;
/* initialize all video 4 linux */
- list_add_tail(&dev->s2255_devlist, &s2255_devlist);
/* register 4 video devices */
for (i = 0; i < MAX_CHANNELS; i++) {
INIT_LIST_HEAD(&dev->vidq[i].active);
@@ -1853,6 +1837,7 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
dev->vdev[i] = video_device_alloc();
memcpy(dev->vdev[i], &template, sizeof(struct video_device));
dev->vdev[i]->parent = &dev->interface->dev;
+ video_set_drvdata(dev->vdev[i], dev);
if (video_nr == -1)
ret = video_register_device(dev->vdev[i],
VFL_TYPE_GRABBER,
@@ -1880,7 +1865,7 @@ static void s2255_exit_v4l(struct s2255_dev *dev)
int i;
for (i = 0; i < MAX_CHANNELS; i++) {
- if (-1 != dev->vdev[i]->minor) {
+ if (video_is_registered(dev->vdev[i])) {
video_unregister_device(dev->vdev[i]);
printk(KERN_INFO "s2255 unregistered\n");
} else {
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
index b624a4c01fdc..5ab6a0f901c0 100644
--- a/drivers/media/video/saa5246a.c
+++ b/drivers/media/video/saa5246a.c
@@ -1036,7 +1036,6 @@ static struct video_device saa_template =
.name = "saa5246a",
.fops = &saa_fops,
.release = video_device_release,
- .minor = -1,
};
static int saa5246a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 7e40d6d99dd0..03f572708b85 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -7211,9 +7211,31 @@ int saa7134_board_init2(struct saa7134_dev *dev)
}
case SAA7134_BOARD_FLYDVB_TRIO:
{
+ u8 temp = 0;
+ int rc;
u8 data[] = { 0x3c, 0x33, 0x62};
struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+ /*
+ * send weak up message to pic16C505 chip
+ * @ LifeView FlyDVB Trio
+ */
+ msg.buf = &temp;
+ msg.addr = 0x0b;
+ msg.len = 1;
+ if (1 != i2c_transfer(&dev->i2c_adap, &msg, 1)) {
+ printk(KERN_WARNING "%s: send wake up byte to pic16C505"
+ "(IR chip) failed\n", dev->name);
+ } else {
+ msg.flags = I2C_M_RD;
+ rc = i2c_transfer(&dev->i2c_adap, &msg, 1);
+ printk(KERN_INFO "%s: probe IR chip @ i2c 0x%02x: %s\n",
+ dev->name, msg.addr,
+ (1 == rc) ? "yes" : "no");
+ if (rc == 1)
+ dev->has_remote = SAA7134_REMOTE_I2C;
+ }
break;
}
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 0ba7f5af0fc3..9f85e917f9f3 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -797,27 +797,28 @@ static struct video_device *vdev_init(struct saa7134_dev *dev,
vfd->debug = video_debug;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
dev->name, type, saa7134_boards[dev->board].name);
+ video_set_drvdata(vfd, dev);
return vfd;
}
static void saa7134_unregister_video(struct saa7134_dev *dev)
{
if (dev->video_dev) {
- if (-1 != dev->video_dev->minor)
+ if (video_is_registered(dev->video_dev))
video_unregister_device(dev->video_dev);
else
video_device_release(dev->video_dev);
dev->video_dev = NULL;
}
if (dev->vbi_dev) {
- if (-1 != dev->vbi_dev->minor)
+ if (video_is_registered(dev->vbi_dev))
video_unregister_device(dev->vbi_dev);
else
video_device_release(dev->vbi_dev);
dev->vbi_dev = NULL;
}
if (dev->radio_dev) {
- if (-1 != dev->radio_dev->minor)
+ if (video_is_registered(dev->radio_dev))
video_unregister_device(dev->radio_dev);
else
video_device_release(dev->radio_dev);
@@ -1046,8 +1047,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
dev->name);
goto fail4;
}
- printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
- dev->name, dev->video_dev->num);
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->video_dev));
dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
@@ -1055,8 +1056,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
vbi_nr[dev->nr]);
if (err < 0)
goto fail4;
- printk(KERN_INFO "%s: registered device vbi%d\n",
- dev->name, dev->vbi_dev->num);
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
if (card_has_radio(dev)) {
dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
@@ -1064,8 +1065,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
radio_nr[dev->nr]);
if (err < 0)
goto fail4;
- printk(KERN_INFO "%s: registered device radio%d\n",
- dev->name, dev->radio_dev->num);
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->radio_dev));
}
/* everything worked */
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 296788c3bf0e..7dfecfc6017c 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -86,19 +86,11 @@ static int ts_init_encoder(struct saa7134_dev* dev)
static int ts_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct saa7134_dev *dev;
+ struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
int err;
- lock_kernel();
- list_for_each_entry(dev, &saa7134_devlist, devlist)
- if (dev->empress_dev && dev->empress_dev->minor == minor)
- goto found;
- unlock_kernel();
- return -ENODEV;
- found:
-
- dprintk("open minor=%d\n",minor);
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
err = -EBUSY;
if (!mutex_trylock(&dev->empress_tsq.vb_lock))
goto done;
@@ -489,7 +481,6 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = {
static struct video_device saa7134_empress_template = {
.name = "saa7134-empress",
.fops = &ts_fops,
- .minor = -1,
.ioctl_ops = &ts_ioctl_ops,
.tvnorms = SAA7134_NORMS,
@@ -531,6 +522,7 @@ static int empress_init(struct saa7134_dev *dev)
INIT_WORK(&dev->empress_workqueue, empress_signal_update);
+ video_set_drvdata(dev->empress_dev, dev);
err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
empress_nr[dev->nr]);
if (err < 0) {
@@ -540,8 +532,8 @@ static int empress_init(struct saa7134_dev *dev)
dev->empress_dev = NULL;
return err;
}
- printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
- dev->name, dev->empress_dev->num);
+ printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+ dev->name, video_device_node_name(dev->empress_dev));
videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops,
&dev->pci->dev, &dev->slock,
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 744918b1cd47..f8e985989ca0 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -127,6 +127,61 @@ static int build_key(struct saa7134_dev *dev)
/* --------------------- Chip specific I2C key builders ----------------- */
+static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ int gpio;
+ int attempt = 0;
+ unsigned char b;
+
+ /* We need this to access GPI Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+
+ if (dev == NULL) {
+ dprintk("get_key_flydvb_trio: "
+ "gir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIGPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ if (0x40000 & ~gpio)
+ return 0; /* No button press */
+
+ /* No button press - only before first key pressed */
+ if (b == 0xFF)
+ return 0;
+
+ /* poll IR chip */
+ /* weak up the IR chip */
+ b = 0;
+
+ while (1 != i2c_master_send(ir->c, &b, 1)) {
+ if ((attempt++) < 10) {
+ /*
+ * wait a bit for next attempt -
+ * I don't know how make it better
+ */
+ msleep(10);
+ continue;
+ }
+ i2cdprintk("send wake up byte to pic16C505 (IR chip)"
+ "failed %dx\n", attempt);
+ return -EIO;
+ }
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
u32 *ir_raw)
{
@@ -622,6 +677,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keyup = 0x020000;
polling = 50; /* ms */
break;
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
@@ -652,7 +708,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
pci_name(dev->pci));
- err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type);
if (err < 0)
goto err_out_free;
@@ -672,7 +728,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
dev->remote = ir;
saa7134_ir_start(dev, ir);
- err = input_register_device(ir->dev);
+ err = ir_input_register(ir->dev, ir_codes);
if (err)
goto err_out_stop;
@@ -686,8 +742,6 @@ int saa7134_input_init1(struct saa7134_dev *dev)
saa7134_ir_stop(dev);
dev->remote = NULL;
err_out_free:
- ir_input_free(input_dev);
- input_free_device(input_dev);
kfree(ir);
return err;
}
@@ -698,8 +752,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
return;
saa7134_ir_stop(dev);
- ir_input_free(dev->remote->dev);
- input_unregister_device(dev->remote->dev);
+ ir_input_unregister(dev->remote->dev);
kfree(dev->remote);
dev->remote = NULL;
}
@@ -788,6 +841,12 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
info.addr = 0x40;
break;
+ case SAA7134_BOARD_FLYDVB_TRIO:
+ dev->init_data.name = "FlyDVB Trio";
+ dev->init_data.get_key = get_key_flydvb_trio;
+ dev->init_data.ir_codes = &ir_codes_flydvb_table;
+ info.addr = 0x0b;
+ break;
default:
dprintk("No I2C IR support for board %x\n", dev->board);
return;
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 35f8daa3a359..cb732640ac4a 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -1326,33 +1326,26 @@ static int saa7134_resource(struct saa7134_fh *fh)
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct saa7134_dev *dev;
+ struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_fh *fh;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ enum v4l2_buf_type type = 0;
int radio = 0;
- mutex_lock(&saa7134_devlist_lock);
- list_for_each_entry(dev, &saa7134_devlist, devlist) {
- if (dev->video_dev && (dev->video_dev->minor == minor))
- goto found;
- if (dev->radio_dev && (dev->radio_dev->minor == minor)) {
- radio = 1;
- goto found;
- }
- if (dev->vbi_dev && (dev->vbi_dev->minor == minor)) {
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- goto found;
- }
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
}
- mutex_unlock(&saa7134_devlist_lock);
- return -ENODEV;
-
-found:
- mutex_unlock(&saa7134_devlist_lock);
- dprintk("open minor=%d radio=%d type=%s\n",minor,radio,
- v4l2_type_names[type]);
+ dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev),
+ radio, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
@@ -2502,7 +2495,6 @@ struct video_device saa7134_video_template = {
.name = "saa7134-video",
.fops = &video_fops,
.ioctl_ops = &video_ioctl_ops,
- .minor = -1,
.tvnorms = SAA7134_NORMS,
.current_norm = V4L2_STD_PAL,
};
@@ -2511,7 +2503,6 @@ struct video_device saa7134_radio_template = {
.name = "saa7134-radio",
.fops = &radio_fops,
.ioctl_ops = &radio_ioctl_ops,
- .minor = -1,
};
int saa7134_video_init1(struct saa7134_dev *dev)
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
index 85ffc2cba039..41d0166c0f95 100644
--- a/drivers/media/video/se401.c
+++ b/drivers/media/video/se401.c
@@ -1428,8 +1428,8 @@ static int se401_probe(struct usb_interface *intf,
err("video_register_device failed");
return -EIO;
}
- dev_info(&intf->dev, "registered new video device: video%d\n",
- se401->vdev.num);
+ dev_info(&intf->dev, "registered new video device: %s\n",
+ video_device_node_name(&se401->vdev));
usb_set_intfdata(intf, se401);
return 0;
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index a4f3472d4db8..d69363f0d8c9 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -38,6 +38,8 @@
#include <media/soc_camera.h>
#include <media/sh_mobile_ceu.h>
#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-mediabus.h>
+#include <media/soc_mediabus.h>
/* register offsets for sh7722 / sh7723 */
@@ -85,7 +87,7 @@
/* per video frame buffer */
struct sh_mobile_ceu_buffer {
struct videobuf_buffer vb; /* v4l buffer must be first */
- const struct soc_camera_data_format *fmt;
+ enum v4l2_mbus_pixelcode code;
};
struct sh_mobile_ceu_dev {
@@ -105,7 +107,8 @@ struct sh_mobile_ceu_dev {
u32 cflcr;
- unsigned int is_interlaced:1;
+ enum v4l2_field field;
+
unsigned int image_mode:1;
unsigned int is_16bit:1;
};
@@ -114,8 +117,8 @@ struct sh_mobile_ceu_cam {
struct v4l2_rect ceu_rect;
unsigned int cam_width;
unsigned int cam_height;
- const struct soc_camera_data_format *extra_fmt;
- const struct soc_camera_data_format *camera_fmt;
+ const struct soc_mbus_pixelfmt *extra_fmt;
+ enum v4l2_mbus_pixelcode code;
};
static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
@@ -197,16 +200,19 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq,
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
- *size = PAGE_ALIGN(icd->user_width * icd->user_height *
- bytes_per_pixel);
+ *size = bytes_per_line * icd->user_height;
if (0 == *count)
*count = 2;
if (pcdev->video_limit) {
- while (*size * *count > pcdev->video_limit)
+ while (PAGE_ALIGN(*size) * *count > pcdev->video_limit)
(*count)--;
}
@@ -249,10 +255,13 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
{
struct soc_camera_device *icd = pcdev->icd;
dma_addr_t phys_addr_top, phys_addr_bottom;
+ unsigned long top1, top2;
+ unsigned long bottom1, bottom2;
u32 status;
int ret = 0;
- /* The hardware is _very_ picky about this sequence. Especially
+ /*
+ * The hardware is _very_ picky about this sequence. Especially
* the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
* several not-so-well documented interrupt sources in CETCR.
*/
@@ -276,25 +285,36 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
if (!pcdev->active)
return ret;
+ if (V4L2_FIELD_INTERLACED_BT == pcdev->field) {
+ top1 = CDBYR;
+ top2 = CDBCR;
+ bottom1 = CDAYR;
+ bottom2 = CDACR;
+ } else {
+ top1 = CDAYR;
+ top2 = CDACR;
+ bottom1 = CDBYR;
+ bottom2 = CDBCR;
+ }
+
phys_addr_top = videobuf_to_dma_contig(pcdev->active);
- ceu_write(pcdev, CDAYR, phys_addr_top);
- if (pcdev->is_interlaced) {
+ ceu_write(pcdev, top1, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
phys_addr_bottom = phys_addr_top + icd->user_width;
- ceu_write(pcdev, CDBYR, phys_addr_bottom);
+ ceu_write(pcdev, bottom1, phys_addr_bottom);
}
- switch (icd->current_fmt->fourcc) {
+ switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
phys_addr_top += icd->user_width *
icd->user_height;
- ceu_write(pcdev, CDACR, phys_addr_top);
- if (pcdev->is_interlaced) {
- phys_addr_bottom = phys_addr_top +
- icd->user_width;
- ceu_write(pcdev, CDBCR, phys_addr_bottom);
+ ceu_write(pcdev, top2, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ phys_addr_bottom = phys_addr_top + icd->user_width;
+ ceu_write(pcdev, bottom2, phys_addr_bottom);
}
}
@@ -310,8 +330,13 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
{
struct soc_camera_device *icd = vq->priv_data;
struct sh_mobile_ceu_buffer *buf;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
int ret;
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
buf = container_of(vb, struct sh_mobile_ceu_buffer, vb);
dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
@@ -321,25 +346,27 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
WARN_ON(!list_empty(&vb->queue));
#ifdef DEBUG
- /* This can be useful if you want to see if we actually fill
- * the buffer with something */
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
memset((void *)vb->baddr, 0xaa, vb->bsize);
#endif
BUG_ON(NULL == icd->current_fmt);
- if (buf->fmt != icd->current_fmt ||
+ if (buf->code != icd->current_fmt->code ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
- buf->fmt = icd->current_fmt;
+ buf->code = icd->current_fmt->code;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
- vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3);
+ vb->size = vb->height * bytes_per_line;
if (0 != vb->baddr && vb->bsize < vb->size) {
ret = -EINVAL;
goto out;
@@ -456,6 +483,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ int ret;
if (pcdev->icd)
return -EBUSY;
@@ -466,9 +494,11 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
pm_runtime_get_sync(ici->v4l2_dev.dev);
- pcdev->icd = icd;
+ ret = sh_mobile_ceu_soft_reset(pcdev);
+ if (!ret)
+ pcdev->icd = icd;
- return sh_mobile_ceu_soft_reset(pcdev);
+ return ret;
}
/* Called with .video_lock held */
@@ -558,24 +588,35 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd,
in_width *= 2;
left_offset *= 2;
}
- width = cdwdr_width = out_width;
+ width = out_width;
+ cdwdr_width = out_width;
} else {
- unsigned int w_factor = (icd->current_fmt->depth + 7) >> 3;
+ int bytes_per_line = soc_mbus_bytes_per_line(out_width,
+ icd->current_fmt->host_fmt);
+ unsigned int w_factor;
- width = out_width * w_factor / 2;
+ width = out_width;
- if (!pcdev->is_16bit)
- w_factor *= 2;
+ switch (icd->current_fmt->host_fmt->packing) {
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ w_factor = 2;
+ break;
+ default:
+ w_factor = 1;
+ }
- in_width = rect->width * w_factor / 2;
- left_offset = left_offset * w_factor / 2;
+ in_width = rect->width * w_factor;
+ left_offset = left_offset * w_factor;
- cdwdr_width = width * 2;
+ if (bytes_per_line < 0)
+ cdwdr_width = out_width;
+ else
+ cdwdr_width = bytes_per_line;
}
height = out_height;
in_height = rect->height;
- if (pcdev->is_interlaced) {
+ if (V4L2_FIELD_NONE != pcdev->field) {
height /= 2;
in_height /= 2;
top_offset /= 2;
@@ -646,6 +687,23 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
if (!common_flags)
return -EINVAL;
+ /* Make choises, based on platform preferences */
+ if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+ common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+ common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ }
+
ret = icd->ops->set_bus_param(icd, common_flags);
if (ret < 0)
return ret;
@@ -667,24 +725,24 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
value = 0x00000010; /* data fetch by default */
yuv_lineskip = 0;
- switch (icd->current_fmt->fourcc) {
+ switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
/* fall-through */
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
- switch (cam->camera_fmt->fourcc) {
- case V4L2_PIX_FMT_UYVY:
+ switch (cam->code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
break;
- case V4L2_PIX_FMT_VYUY:
+ case V4L2_MBUS_FMT_YVYU8_2X8_BE:
value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
break;
- case V4L2_PIX_FMT_YUYV:
+ case V4L2_MBUS_FMT_YUYV8_2X8_LE:
value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
break;
- case V4L2_PIX_FMT_YVYU:
+ case V4L2_MBUS_FMT_YVYU8_2X8_LE:
value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
break;
default:
@@ -692,8 +750,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
}
}
- if (icd->current_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
- icd->current_fmt->fourcc == V4L2_PIX_FMT_NV61)
+ if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
+ icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
@@ -702,14 +760,27 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
ceu_write(pcdev, CAMCR, value);
ceu_write(pcdev, CAPCR, 0x00300000);
- ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0);
+
+ switch (pcdev->field) {
+ case V4L2_FIELD_INTERLACED_TB:
+ value = 0x101;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ value = 0x102;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ ceu_write(pcdev, CAIFR, value);
sh_mobile_ceu_set_rect(icd, icd->user_width, icd->user_height);
mdelay(1);
ceu_write(pcdev, CFLCR, pcdev->cflcr);
- /* A few words about byte order (observed in Big Endian mode)
+ /*
+ * A few words about byte order (observed in Big Endian mode)
*
* In data fetch mode bytes are received in chunks of 8 bytes.
* D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
@@ -739,7 +810,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
return 0;
}
-static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd)
+static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
@@ -748,48 +820,75 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd)
camera_flags = icd->ops->query_bus_param(icd);
common_flags = soc_camera_bus_param_compatible(camera_flags,
make_bus_param(pcdev));
- if (!common_flags)
+ if (!common_flags || buswidth > 16 ||
+ (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
return -EINVAL;
return 0;
}
-static const struct soc_camera_data_format sh_mobile_ceu_formats[] = {
- {
- .name = "NV12",
- .depth = 12,
- .fourcc = V4L2_PIX_FMT_NV12,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- {
- .name = "NV21",
- .depth = 12,
- .fourcc = V4L2_PIX_FMT_NV21,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- {
- .name = "NV16",
- .depth = 16,
- .fourcc = V4L2_PIX_FMT_NV16,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
+static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
{
- .name = "NV61",
- .depth = 16,
- .fourcc = V4L2_PIX_FMT_NV61,
- .colorspace = V4L2_COLORSPACE_JPEG,
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .name = "NV12",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .name = "NV21",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .name = "NV16",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .name = "NV61",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
},
};
+/* This will be corrected as we get more formats */
+static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
struct soc_camera_format_xlate *xlate)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->dev.parent;
int ret, k, n;
int formats = 0;
struct sh_mobile_ceu_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
- ret = sh_mobile_ceu_try_bus_param(icd);
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(icd->dev.parent,
+ "Invalid format code #%d: %d\n", idx, code);
+ return -EINVAL;
+ }
+
+ ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
return 0;
@@ -807,13 +906,13 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
if (!idx)
cam->extra_fmt = NULL;
- switch (icd->formats[idx].fourcc) {
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8_BE:
+ case V4L2_MBUS_FMT_YVYU8_2X8_BE:
+ case V4L2_MBUS_FMT_YUYV8_2X8_LE:
+ case V4L2_MBUS_FMT_YVYU8_2X8_LE:
if (cam->extra_fmt)
- goto add_single_format;
+ break;
/*
* Our case is simple so far: for any of the above four camera
@@ -824,32 +923,31 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
* the host_priv pointer and check whether the format you're
* going to add now is already there.
*/
- cam->extra_fmt = (void *)sh_mobile_ceu_formats;
+ cam->extra_fmt = sh_mobile_ceu_formats;
n = ARRAY_SIZE(sh_mobile_ceu_formats);
formats += n;
for (k = 0; xlate && k < n; k++) {
- xlate->host_fmt = &sh_mobile_ceu_formats[k];
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = icd->formats[idx].depth;
+ xlate->host_fmt = &sh_mobile_ceu_formats[k];
+ xlate->code = code;
xlate++;
- dev_dbg(dev, "Providing format %s using %s\n",
- sh_mobile_ceu_formats[k].name,
- icd->formats[idx].name);
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ sh_mobile_ceu_formats[k].name, code);
}
+ break;
default:
-add_single_format:
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = icd->formats + idx;
- xlate->cam_fmt = icd->formats + idx;
- xlate->buswidth = icd->formats[idx].depth;
- xlate++;
- dev_dbg(dev,
- "Providing format %s in pass-through mode\n",
- icd->formats[idx].name);
- }
+ if (!sh_mobile_ceu_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s in pass-through mode\n",
+ xlate->host_fmt->name);
}
return formats;
@@ -1029,17 +1127,15 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop,
static int get_camera_scales(struct v4l2_subdev *sd, struct v4l2_rect *rect,
unsigned int *scale_h, unsigned int *scale_v)
{
- struct v4l2_format f;
+ struct v4l2_mbus_framefmt mf;
int ret;
- f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
if (ret < 0)
return ret;
- *scale_h = calc_generic_scale(rect->width, f.fmt.pix.width);
- *scale_v = calc_generic_scale(rect->height, f.fmt.pix.height);
+ *scale_h = calc_generic_scale(rect->width, mf.width);
+ *scale_v = calc_generic_scale(rect->height, mf.height);
return 0;
}
@@ -1054,32 +1150,29 @@ static int get_camera_subwin(struct soc_camera_device *icd,
if (!ceu_rect->width) {
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->dev.parent;
- struct v4l2_format f;
- struct v4l2_pix_format *pix = &f.fmt.pix;
+ struct v4l2_mbus_framefmt mf;
int ret;
/* First time */
- f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
if (ret < 0)
return ret;
- dev_geo(dev, "camera fmt %ux%u\n", pix->width, pix->height);
+ dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height);
- if (pix->width > 2560) {
+ if (mf.width > 2560) {
ceu_rect->width = 2560;
- ceu_rect->left = (pix->width - 2560) / 2;
+ ceu_rect->left = (mf.width - 2560) / 2;
} else {
- ceu_rect->width = pix->width;
+ ceu_rect->width = mf.width;
ceu_rect->left = 0;
}
- if (pix->height > 1920) {
+ if (mf.height > 1920) {
ceu_rect->height = 1920;
- ceu_rect->top = (pix->height - 1920) / 2;
+ ceu_rect->top = (mf.height - 1920) / 2;
} else {
- ceu_rect->height = pix->height;
+ ceu_rect->height = mf.height;
ceu_rect->top = 0;
}
@@ -1096,13 +1189,12 @@ static int get_camera_subwin(struct soc_camera_device *icd,
return 0;
}
-static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_format *f,
- bool ceu_can_scale)
+static int client_s_fmt(struct soc_camera_device *icd,
+ struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->dev.parent;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- unsigned int width = pix->width, height = pix->height, tmp_w, tmp_h;
+ unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
unsigned int max_width, max_height;
struct v4l2_cropcap cap;
int ret;
@@ -1116,29 +1208,29 @@ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_format *f,
max_width = min(cap.bounds.width, 2560);
max_height = min(cap.bounds.height, 1920);
- ret = v4l2_subdev_call(sd, video, s_fmt, f);
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf);
if (ret < 0)
return ret;
- dev_geo(dev, "camera scaled to %ux%u\n", pix->width, pix->height);
+ dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
- if ((width == pix->width && height == pix->height) || !ceu_can_scale)
+ if ((width == mf->width && height == mf->height) || !ceu_can_scale)
return 0;
/* Camera set a format, but geometry is not precise, try to improve */
- tmp_w = pix->width;
- tmp_h = pix->height;
+ tmp_w = mf->width;
+ tmp_h = mf->height;
/* width <= max_width && height <= max_height - guaranteed by try_fmt */
while ((width > tmp_w || height > tmp_h) &&
tmp_w < max_width && tmp_h < max_height) {
tmp_w = min(2 * tmp_w, max_width);
tmp_h = min(2 * tmp_h, max_height);
- pix->width = tmp_w;
- pix->height = tmp_h;
- ret = v4l2_subdev_call(sd, video, s_fmt, f);
+ mf->width = tmp_w;
+ mf->height = tmp_h;
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf);
dev_geo(dev, "Camera scaled to %ux%u\n",
- pix->width, pix->height);
+ mf->width, mf->height);
if (ret < 0) {
/* This shouldn't happen */
dev_err(dev, "Client failed to set format: %d\n", ret);
@@ -1156,27 +1248,26 @@ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_format *f,
*/
static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect,
struct v4l2_rect *sub_rect, struct v4l2_rect *ceu_rect,
- struct v4l2_format *f, bool ceu_can_scale)
+ struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct device *dev = icd->dev.parent;
- struct v4l2_format f_tmp = *f;
- struct v4l2_pix_format *pix_tmp = &f_tmp.fmt.pix;
+ struct v4l2_mbus_framefmt mf_tmp = *mf;
unsigned int scale_h, scale_v;
int ret;
/* 5. Apply iterative camera S_FMT for camera user window. */
- ret = client_s_fmt(icd, &f_tmp, ceu_can_scale);
+ ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale);
if (ret < 0)
return ret;
dev_geo(dev, "5: camera scaled to %ux%u\n",
- pix_tmp->width, pix_tmp->height);
+ mf_tmp.width, mf_tmp.height);
/* 6. Retrieve camera output window (g_fmt) */
- /* unneeded - it is already in "f_tmp" */
+ /* unneeded - it is already in "mf_tmp" */
/* 7. Calculate new camera scales. */
ret = get_camera_scales(sd, rect, &scale_h, &scale_v);
@@ -1185,10 +1276,11 @@ static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect,
dev_geo(dev, "7: camera scales %u:%u\n", scale_h, scale_v);
- cam->cam_width = pix_tmp->width;
- cam->cam_height = pix_tmp->height;
- f->fmt.pix.width = pix_tmp->width;
- f->fmt.pix.height = pix_tmp->height;
+ cam->cam_width = mf_tmp.width;
+ cam->cam_height = mf_tmp.height;
+ mf->width = mf_tmp.width;
+ mf->height = mf_tmp.height;
+ mf->colorspace = mf_tmp.colorspace;
/*
* 8. Calculate new CEU crop - apply camera scales to previously
@@ -1252,8 +1344,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
struct v4l2_rect *cam_rect = &cam_crop.c, *ceu_rect = &cam->ceu_rect;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->dev.parent;
- struct v4l2_format f;
- struct v4l2_pix_format *pix = &f.fmt.pix;
+ struct v4l2_mbus_framefmt mf;
unsigned int scale_comb_h, scale_comb_v, scale_ceu_h, scale_ceu_v,
out_width, out_height;
u32 capsr, cflcr;
@@ -1302,26 +1393,25 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
* 5. Using actual input window and calculated combined scales calculate
* camera target output window.
*/
- pix->width = scale_down(cam_rect->width, scale_comb_h);
- pix->height = scale_down(cam_rect->height, scale_comb_v);
+ mf.width = scale_down(cam_rect->width, scale_comb_h);
+ mf.height = scale_down(cam_rect->height, scale_comb_v);
- dev_geo(dev, "5: camera target %ux%u\n", pix->width, pix->height);
+ dev_geo(dev, "5: camera target %ux%u\n", mf.width, mf.height);
/* 6. - 9. */
- pix->pixelformat = cam->camera_fmt->fourcc;
- pix->colorspace = cam->camera_fmt->colorspace;
+ mf.code = cam->code;
+ mf.field = pcdev->field;
capsr = capture_save_reset(pcdev);
dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
/* Make relative to camera rectangle */
- rect->left -= cam_rect->left;
- rect->top -= cam_rect->top;
+ rect->left -= cam_rect->left;
+ rect->top -= cam_rect->top;
- f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = client_scale(icd, cam_rect, rect, ceu_rect, &f,
- pcdev->image_mode && !pcdev->is_interlaced);
+ ret = client_scale(icd, cam_rect, rect, ceu_rect, &mf,
+ pcdev->image_mode &&
+ V4L2_FIELD_NONE == pcdev->field);
dev_geo(dev, "6-9: %d\n", ret);
@@ -1368,8 +1458,7 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_format cam_f = *f;
- struct v4l2_pix_format *cam_pix = &cam_f.fmt.pix;
+ struct v4l2_mbus_framefmt mf;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->dev.parent;
__u32 pixfmt = pix->pixelformat;
@@ -1379,18 +1468,20 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
unsigned int scale_cam_h, scale_cam_v;
u16 scale_v, scale_h;
int ret;
- bool is_interlaced, image_mode;
+ bool image_mode;
+ enum v4l2_field field;
switch (pix->field) {
- case V4L2_FIELD_INTERLACED:
- is_interlaced = true;
- break;
- case V4L2_FIELD_ANY:
default:
pix->field = V4L2_FIELD_NONE;
/* fall-through */
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_NONE:
- is_interlaced = false;
+ field = pix->field;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ field = V4L2_FIELD_INTERLACED_TB;
break;
}
@@ -1438,9 +1529,11 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
* 4. Calculate camera output window by applying combined scales to real
* input window.
*/
- cam_pix->width = scale_down(cam_rect->width, scale_h);
- cam_pix->height = scale_down(cam_rect->height, scale_v);
- cam_pix->pixelformat = xlate->cam_fmt->fourcc;
+ mf.width = scale_down(cam_rect->width, scale_h);
+ mf.height = scale_down(cam_rect->height, scale_v);
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
switch (pixfmt) {
case V4L2_PIX_FMT_NV12:
@@ -1453,51 +1546,61 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
image_mode = false;
}
- dev_geo(dev, "4: camera output %ux%u\n",
- cam_pix->width, cam_pix->height);
+ dev_geo(dev, "4: camera output %ux%u\n", mf.width, mf.height);
/* 5. - 9. */
- ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &cam_f,
- image_mode && !is_interlaced);
+ ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &mf,
+ image_mode && V4L2_FIELD_NONE == field);
dev_geo(dev, "5-9: client scale %d\n", ret);
/* Done with the camera. Now see if we can improve the result */
dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n",
- ret, cam_pix->width, cam_pix->height, pix->width, pix->height);
+ ret, mf.width, mf.height, pix->width, pix->height);
if (ret < 0)
return ret;
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
/* 10. Use CEU scaling to scale to the requested user window. */
/* We cannot scale up */
- if (pix->width > cam_pix->width)
- pix->width = cam_pix->width;
+ if (pix->width > mf.width)
+ pix->width = mf.width;
if (pix->width > ceu_rect.width)
pix->width = ceu_rect.width;
- if (pix->height > cam_pix->height)
- pix->height = cam_pix->height;
+ if (pix->height > mf.height)
+ pix->height = mf.height;
if (pix->height > ceu_rect.height)
pix->height = ceu_rect.height;
- /* Let's rock: scale pix->{width x height} down to width x height */
- scale_h = calc_scale(ceu_rect.width, &pix->width);
- scale_v = calc_scale(ceu_rect.height, &pix->height);
+ pix->colorspace = mf.colorspace;
+
+ if (image_mode) {
+ /* Scale pix->{width x height} down to width x height */
+ scale_h = calc_scale(ceu_rect.width, &pix->width);
+ scale_v = calc_scale(ceu_rect.height, &pix->height);
+
+ pcdev->cflcr = scale_h | (scale_v << 16);
+ } else {
+ pix->width = ceu_rect.width;
+ pix->height = ceu_rect.height;
+ scale_h = scale_v = 0;
+ pcdev->cflcr = 0;
+ }
dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n",
ceu_rect.width, scale_h, pix->width,
ceu_rect.height, scale_v, pix->height);
- pcdev->cflcr = scale_h | (scale_v << 16);
+ cam->code = xlate->code;
+ cam->ceu_rect = ceu_rect;
+ icd->current_fmt = xlate;
- icd->buswidth = xlate->buswidth;
- icd->current_fmt = xlate->host_fmt;
- cam->camera_fmt = xlate->cam_fmt;
- cam->ceu_rect = ceu_rect;
-
- pcdev->is_interlaced = is_interlaced;
+ pcdev->field = field;
pcdev->image_mode = image_mode;
return 0;
@@ -1509,6 +1612,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
int width, height;
int ret;
@@ -1527,18 +1631,27 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
width = pix->width;
height = pix->height;
- pix->bytesperline = pix->width *
- DIV_ROUND_UP(xlate->host_fmt->depth, 8);
- pix->sizeimage = pix->height * pix->bytesperline;
-
- pix->pixelformat = xlate->cam_fmt->fourcc;
+ pix->bytesperline = soc_mbus_bytes_per_line(width, xlate->host_fmt);
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
+ pix->sizeimage = height * pix->bytesperline;
/* limit to sensor capabilities */
- ret = v4l2_subdev_call(sd, video, try_fmt, f);
- pix->pixelformat = pixfmt;
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.code = xlate->code;
+ mf.colorspace = pix->colorspace;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
if (ret < 0)
return ret;
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
switch (pixfmt) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
@@ -1547,21 +1660,25 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
/* FIXME: check against rect_max after converting soc-camera */
/* We can scale precisely, need a bigger image from camera */
if (pix->width < width || pix->height < height) {
- int tmp_w = pix->width, tmp_h = pix->height;
- pix->width = 2560;
- pix->height = 1920;
- ret = v4l2_subdev_call(sd, video, try_fmt, f);
+ /*
+ * We presume, the sensor behaves sanely, i.e., if
+ * requested a bigger rectangle, it will not return a
+ * smaller one.
+ */
+ mf.width = 2560;
+ mf.height = 1920;
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
if (ret < 0) {
/* Shouldn't actually happen... */
dev_err(icd->dev.parent,
- "FIXME: try_fmt() returned %d\n", ret);
- pix->width = tmp_w;
- pix->height = tmp_h;
+ "FIXME: client try_fmt() = %d\n", ret);
+ return ret;
}
}
- if (pix->width > width)
+ /* We will scale exactly */
+ if (mf.width > width)
pix->width = width;
- if (pix->height > height)
+ if (mf.height > height)
pix->height = height;
}
@@ -1573,10 +1690,12 @@ static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf,
{
int i;
- /* This is for locking debugging only. I removed spinlocks and now I
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
* check whether .prepare is ever called on a linked buffer, or whether
* a dma IRQ can occur for an in-work or unlinked buffer. Until now
- * it hadn't triggered */
+ * it hadn't triggered
+ */
for (i = 0; i < p->count; i++) {
struct sh_mobile_ceu_buffer *buf;
@@ -1624,8 +1743,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
&sh_mobile_ceu_videobuf_ops,
icd->dev.parent, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
- pcdev->is_interlaced ?
- V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE,
+ pcdev->field,
sizeof(struct sh_mobile_ceu_buffer),
icd);
}
@@ -1654,7 +1772,7 @@ static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
switch (ctrl->id) {
case V4L2_CID_SHARPNESS:
- switch (icd->current_fmt->fourcc) {
+ switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
@@ -1825,7 +1943,7 @@ static int sh_mobile_ceu_runtime_nop(struct device *dev)
return 0;
}
-static struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
+static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
.runtime_suspend = sh_mobile_ceu_runtime_nop,
.runtime_resume = sh_mobile_ceu_runtime_nop,
};
@@ -1836,7 +1954,7 @@ static struct platform_driver sh_mobile_ceu_driver = {
.pm = &sh_mobile_ceu_dev_pm_ops,
},
.probe = sh_mobile_ceu_probe,
- .remove = __exit_p(sh_mobile_ceu_remove),
+ .remove = __devexit_p(sh_mobile_ceu_remove),
};
static int __init sh_mobile_ceu_init(void)
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index 4a7711c3e745..cbf8087b286f 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -1007,8 +1007,8 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
else if (cam->stream != STREAM_OFF) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "URB timeout reached. The camera is misconfigured. "
- "To use it, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "To use it, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -1734,7 +1734,8 @@ static void sn9c102_release_resources(struct kref *kref)
cam = container_of(kref, struct sn9c102_device, kref);
- DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->num);
+ DBG(2, "V4L2 device %s deregistered",
+ video_device_node_name(cam->v4ldev));
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
usb_put_dev(cam->usbdev);
@@ -1791,8 +1792,8 @@ static int sn9c102_open(struct file *filp)
}
if (cam->users) {
- DBG(2, "Device /dev/video%d is already in use",
- cam->v4ldev->num);
+ DBG(2, "Device %s is already in use",
+ video_device_node_name(cam->v4ldev));
DBG(3, "Simultaneous opens are not supported");
/*
open() must follow the open flags and should block
@@ -1845,7 +1846,7 @@ static int sn9c102_open(struct file *filp)
cam->frame_count = 0;
sn9c102_empty_framequeues(cam);
- DBG(3, "Video device /dev/video%d is open", cam->v4ldev->num);
+ DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev));
out:
mutex_unlock(&cam->open_mutex);
@@ -1870,7 +1871,7 @@ static int sn9c102_release(struct file *filp)
cam->users--;
wake_up_interruptible_nr(&cam->wait_open, 1);
- DBG(3, "Video device /dev/video%d closed", cam->v4ldev->num);
+ DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev));
kref_put(&cam->kref, sn9c102_release_resources);
@@ -2433,8 +2434,8 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -2446,8 +2447,8 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -ENOMEM;
}
@@ -2690,8 +2691,8 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -2702,8 +2703,8 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -ENOMEM;
}
@@ -2748,9 +2749,9 @@ sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
err += sn9c102_set_compression(cam, &jc);
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
- DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
- "problems. To use the camera, close and open "
- "/dev/video%d again.", cam->v4ldev->num);
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. "
+ "To use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -3328,7 +3329,6 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
strcpy(cam->v4ldev->name, "SN9C1xx PC Camera");
cam->v4ldev->fops = &sn9c102_fops;
- cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
cam->v4ldev->parent = &udev->dev;
@@ -3346,7 +3346,8 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
- DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->num);
+ DBG(2, "V4L2 device registered as %s",
+ video_device_node_name(cam->v4ldev));
video_set_drvdata(cam->v4ldev, cam);
cam->module_param.force_munmap = force_munmap[dev_nr];
@@ -3398,9 +3399,9 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf)
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
if (cam->users) {
- DBG(2, "Device /dev/video%d is open! Deregistration and "
- "memory deallocation are deferred.",
- cam->v4ldev->num);
+ DBG(2, "Device %s is open! Deregistration and memory "
+ "deallocation are deferred.",
+ video_device_node_name(cam->v4ldev));
cam->state |= DEV_MISCONFIGURED;
sn9c102_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 95fdeb23c2c1..6b3fbcca7747 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -31,6 +31,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-core.h>
+#include <media/soc_mediabus.h>
/* Default to VGA resolution */
#define DEFAULT_WIDTH 640
@@ -40,18 +41,6 @@ static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
-const struct soc_camera_data_format *soc_camera_format_by_fourcc(
- struct soc_camera_device *icd, unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < icd->num_formats; i++)
- if (icd->formats[i].fourcc == fourcc)
- return icd->formats + i;
- return NULL;
-}
-EXPORT_SYMBOL(soc_camera_format_by_fourcc);
-
const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
struct soc_camera_device *icd, unsigned int fourcc)
{
@@ -207,21 +196,26 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
/* Always entered with .video_lock held */
static int soc_camera_init_user_formats(struct soc_camera_device *icd)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int i, fmts = 0, ret;
+ int i, fmts = 0, raw_fmts = 0, ret;
+ enum v4l2_mbus_pixelcode code;
+
+ while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, raw_fmts, &code))
+ raw_fmts++;
if (!ici->ops->get_formats)
/*
* Fallback mode - the host will have to serve all
* sensor-provided formats one-to-one to the user
*/
- fmts = icd->num_formats;
+ fmts = raw_fmts;
else
/*
* First pass - only count formats this host-sensor
* configuration can provide
*/
- for (i = 0; i < icd->num_formats; i++) {
+ for (i = 0; i < raw_fmts; i++) {
ret = ici->ops->get_formats(icd, i, NULL);
if (ret < 0)
return ret;
@@ -242,11 +236,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
/* Second pass - actually fill data formats */
fmts = 0;
- for (i = 0; i < icd->num_formats; i++)
+ for (i = 0; i < raw_fmts; i++)
if (!ici->ops->get_formats) {
- icd->user_formats[i].host_fmt = icd->formats + i;
- icd->user_formats[i].cam_fmt = icd->formats + i;
- icd->user_formats[i].buswidth = icd->formats[i].depth;
+ v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &code);
+ icd->user_formats[i].host_fmt =
+ soc_mbus_get_fmtdesc(code);
+ icd->user_formats[i].code = code;
} else {
ret = ici->ops->get_formats(icd, i,
&icd->user_formats[fmts]);
@@ -255,7 +250,7 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
fmts += ret;
}
- icd->current_fmt = icd->user_formats[0].host_fmt;
+ icd->current_fmt = &icd->user_formats[0];
return 0;
@@ -281,7 +276,7 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd)
#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
((x) >> 24) & 0xff
-/* Called with .vb_lock held */
+/* Called with .vb_lock held, or from the first open(2), see comment there */
static int soc_camera_set_fmt(struct soc_camera_file *icf,
struct v4l2_format *f)
{
@@ -302,7 +297,7 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
if (ret < 0) {
return ret;
} else if (!icd->current_fmt ||
- icd->current_fmt->fourcc != pix->pixelformat) {
+ icd->current_fmt->host_fmt->fourcc != pix->pixelformat) {
dev_err(&icd->dev,
"Host driver hasn't set up current format correctly!\n");
return -EINVAL;
@@ -310,6 +305,7 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
icd->user_width = pix->width;
icd->user_height = pix->height;
+ icd->colorspace = pix->colorspace;
icf->vb_vidq.field =
icd->field = pix->field;
@@ -369,8 +365,9 @@ static int soc_camera_open(struct file *file)
.width = icd->user_width,
.height = icd->user_height,
.field = icd->field,
- .pixelformat = icd->current_fmt->fourcc,
- .colorspace = icd->current_fmt->colorspace,
+ .colorspace = icd->colorspace,
+ .pixelformat =
+ icd->current_fmt->host_fmt->fourcc,
},
};
@@ -390,7 +387,12 @@ static int soc_camera_open(struct file *file)
goto eiciadd;
}
- /* Try to configure with default parameters */
+ /*
+ * Try to configure with default parameters. Notice: this is the
+ * very first open, so, we cannot race against other calls,
+ * apart from someone else calling open() simultaneously, but
+ * .video_lock is protecting us against it.
+ */
ret = soc_camera_set_fmt(icf, &f);
if (ret < 0)
goto esfmt;
@@ -534,7 +536,7 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- const struct soc_camera_data_format *format;
+ const struct soc_mbus_pixelfmt *format;
WARN_ON(priv != file->private_data);
@@ -543,7 +545,8 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
format = icd->user_formats[f->index].host_fmt;
- strlcpy(f->description, format->name, sizeof(f->description));
+ if (format->name)
+ strlcpy(f->description, format->name, sizeof(f->description));
f->pixelformat = format->fourcc;
return 0;
}
@@ -560,12 +563,15 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
pix->width = icd->user_width;
pix->height = icd->user_height;
pix->field = icf->vb_vidq.field;
- pix->pixelformat = icd->current_fmt->fourcc;
- pix->bytesperline = pix->width *
- DIV_ROUND_UP(icd->current_fmt->depth, 8);
+ pix->pixelformat = icd->current_fmt->host_fmt->fourcc;
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ icd->current_fmt->host_fmt);
+ pix->colorspace = icd->colorspace;
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
pix->sizeimage = pix->height * pix->bytesperline;
dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n",
- icd->current_fmt->fourcc);
+ icd->current_fmt->host_fmt->fourcc);
return 0;
}
@@ -621,8 +627,10 @@ static int soc_camera_streamoff(struct file *file, void *priv,
mutex_lock(&icd->video_lock);
- /* This calls buf_release from host driver's videobuf_queue_ops for all
- * remaining buffers. When the last buffer is freed, stop capture */
+ /*
+ * This calls buf_release from host driver's videobuf_queue_ops for all
+ * remaining buffers. When the last buffer is freed, stop capture
+ */
videobuf_streamoff(&icf->vb_vidq);
v4l2_subdev_call(sd, video, s_stream, 0);
@@ -892,7 +900,7 @@ static int soc_camera_probe(struct device *dev)
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct device *control = NULL;
struct v4l2_subdev *sd;
- struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
+ struct v4l2_mbus_framefmt mf;
int ret;
dev_info(dev, "Probing %s\n", dev_name(dev));
@@ -963,9 +971,11 @@ static int soc_camera_probe(struct device *dev)
/* Try to improve our guess of a reasonable window format */
sd = soc_camera_to_subdev(icd);
- if (!v4l2_subdev_call(sd, video, g_fmt, &f)) {
- icd->user_width = f.fmt.pix.width;
- icd->user_height = f.fmt.pix.height;
+ if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+ icd->colorspace = mf.colorspace;
+ icd->field = mf.field;
}
/* Do we have to sysfs_remove_link() before device_unregister()? */
@@ -1004,8 +1014,10 @@ epower:
return ret;
}
-/* This is called on device_unregister, which only means we have to disconnect
- * from the host, but not remove ourselves from the device list */
+/*
+ * This is called on device_unregister, which only means we have to disconnect
+ * from the host, but not remove ourselves from the device list
+ */
static int soc_camera_remove(struct device *dev)
{
struct soc_camera_device *icd = to_soc_camera_dev(dev);
@@ -1205,8 +1217,10 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
}
if (num < 0)
- /* ok, we have 256 cameras on this host...
- * man, stay reasonable... */
+ /*
+ * ok, we have 256 cameras on this host...
+ * man, stay reasonable...
+ */
return -ENOMEM;
icd->devnum = num;
@@ -1268,7 +1282,6 @@ static int video_dev_create(struct soc_camera_device *icd)
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
- vdev->minor = -1;
vdev->tvnorms = V4L2_STD_UNKNOWN;
icd->vdev = vdev;
@@ -1291,8 +1304,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
!icd->ops->set_bus_param)
return -EINVAL;
- ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER,
- icd->vdev->minor);
+ ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
return ret;
@@ -1335,9 +1347,11 @@ escdevreg:
return ret;
}
-/* Only called on rmmod for each platform device, since they are not
+/*
+ * Only called on rmmod for each platform device, since they are not
* hot-pluggable. Now we know, that all our users - hosts and devices have
- * been unloaded already */
+ * been unloaded already
+ */
static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
{
struct soc_camera_device *icd = platform_get_drvdata(pdev);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index b6a575ce5da2..10b003a8be83 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -22,7 +22,6 @@
struct soc_camera_platform_priv {
struct v4l2_subdev subdev;
- struct soc_camera_data_format format;
};
static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
@@ -58,36 +57,36 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
}
static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_format *f)
+ struct v4l2_mbus_framefmt *mf)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- pix->width = p->format.width;
- pix->height = p->format.height;
+ mf->width = p->format.width;
+ mf->height = p->format.height;
+ mf->code = p->format.code;
+ mf->colorspace = p->format.colorspace;
+
return 0;
}
-static void soc_camera_platform_video_probe(struct soc_camera_device *icd,
- struct platform_device *pdev)
+static struct v4l2_subdev_core_ops platform_subdev_core_ops;
+
+static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
{
- struct soc_camera_platform_priv *priv = get_priv(pdev);
- struct soc_camera_platform_info *p = pdev->dev.platform_data;
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- priv->format.name = p->format_name;
- priv->format.depth = p->format_depth;
- priv->format.fourcc = p->format.pixelformat;
- priv->format.colorspace = p->format.colorspace;
+ if (index)
+ return -EINVAL;
- icd->formats = &priv->format;
- icd->num_formats = 1;
+ *code = p->format.code;
+ return 0;
}
-static struct v4l2_subdev_core_ops platform_subdev_core_ops;
-
static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
.s_stream = soc_camera_platform_s_stream,
- .try_fmt = soc_camera_platform_try_fmt,
+ .try_mbus_fmt = soc_camera_platform_try_fmt,
+ .enum_mbus_fmt = soc_camera_platform_enum_fmt,
};
static struct v4l2_subdev_ops platform_subdev_ops = {
@@ -128,13 +127,10 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
/* Set the control device reference */
dev_set_drvdata(&icd->dev, &pdev->dev);
- icd->y_skip_top = 0;
- icd->ops = &soc_camera_platform_ops;
+ icd->ops = &soc_camera_platform_ops;
ici = to_soc_camera_host(icd->dev.parent);
- soc_camera_platform_video_probe(icd, pdev);
-
v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, p);
strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
new file mode 100644
index 000000000000..f8d5c87dc2aa
--- /dev/null
+++ b/drivers/media/video/soc_mediabus.c
@@ -0,0 +1,157 @@
+/*
+ * soc-camera media bus helper routines
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/soc_mediabus.h>
+
+#define MBUS_IDX(f) (V4L2_MBUS_FMT_ ## f - V4L2_MBUS_FMT_FIXED - 1)
+
+static const struct soc_mbus_pixelfmt mbus_fmt[] = {
+ [MBUS_IDX(YUYV8_2X8_LE)] = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(YVYU8_2X8_LE)] = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(YUYV8_2X8_BE)] = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(YVYU8_2X8_BE)] = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(RGB555_2X8_PADHI_LE)] = {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(RGB555_2X8_PADHI_BE)] = {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(RGB565_2X8_LE)] = {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(RGB565_2X8_BE)] = {
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(SBGGR8_1X8)] = {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer 8 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(SBGGR10_1X10)] = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(GREY8_1X8)] = {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Grey",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(Y10_1X10)] = {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ .name = "Grey 10bit",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(SBGGR10_2X8_PADHI_LE)] = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(SBGGR10_2X8_PADLO_LE)] = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .order = SOC_MBUS_ORDER_LE,
+ }, [MBUS_IDX(SBGGR10_2X8_PADHI_BE)] = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ }, [MBUS_IDX(SBGGR10_2X8_PADLO_BE)] = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+};
+
+s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
+{
+ switch (mf->packing) {
+ case SOC_MBUS_PACKING_NONE:
+ return width * mf->bits_per_sample / 8;
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ case SOC_MBUS_PACKING_2X8_PADLO:
+ case SOC_MBUS_PACKING_EXTEND16:
+ return width * 2;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(soc_mbus_bytes_per_line);
+
+const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
+ enum v4l2_mbus_pixelcode code)
+{
+ if ((unsigned int)(code - V4L2_MBUS_FMT_FIXED) > ARRAY_SIZE(mbus_fmt))
+ return NULL;
+ return mbus_fmt + code - V4L2_MBUS_FMT_FIXED - 1;
+}
+EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
+
+static int __init soc_mbus_init(void)
+{
+ return 0;
+}
+
+static void __exit soc_mbus_exit(void)
+{
+}
+
+module_init(soc_mbus_init);
+module_exit(soc_mbus_exit);
+
+MODULE_DESCRIPTION("soc-camera media bus interface");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index 6b41865f42bd..f07a0f6b71c4 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -1307,7 +1307,6 @@ static void stk_v4l_dev_release(struct video_device *vd)
static struct video_device stk_v4l_data = {
.name = "stkwebcam",
- .minor = -1,
.tvnorms = V4L2_STD_UNKNOWN,
.current_norm = V4L2_STD_UNKNOWN,
.fops = &v4l_stk_fops,
@@ -1327,8 +1326,8 @@ static int stk_register_video_device(struct stk_camera *dev)
if (err)
STK_ERROR("v4l registration failed\n");
else
- STK_INFO("Syntek USB2.0 Camera is now controlling video device"
- " /dev/video%d\n", dev->vdev.num);
+ STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n",
+ video_device_node_name(&dev->vdev));
return err;
}
@@ -1418,8 +1417,8 @@ static void stk_camera_disconnect(struct usb_interface *interface)
wake_up_interruptible(&dev->wait_frame);
stk_remove_sysfs_files(&dev->vdev);
- STK_INFO("Syntek USB2.0 Camera release resources "
- "video device /dev/video%d\n", dev->vdev.num);
+ STK_INFO("Syntek USB2.0 Camera release resources device %s\n",
+ video_device_node_name(&dev->vdev));
video_unregister_device(&dev->vdev);
}
diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c
index eaada39c76fd..a057824e7ebc 100644
--- a/drivers/media/video/stradis.c
+++ b/drivers/media/video/stradis.c
@@ -1921,7 +1921,6 @@ static const struct v4l2_file_operations saa_fops = {
static struct video_device saa_template = {
.name = "SAA7146A",
.fops = &saa_fops,
- .minor = -1,
.release = video_device_release_empty,
};
@@ -1972,7 +1971,6 @@ static int __devinit configure_saa7146(struct pci_dev *pdev, int num)
saa->id = pdev->device;
saa->irq = pdev->irq;
- saa->video_dev.minor = -1;
saa->saa7146_adr = pci_resource_start(pdev, 0);
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision);
@@ -2134,7 +2132,7 @@ static void stradis_release_saa(struct pci_dev *pdev)
free_irq(saa->irq, saa);
if (saa->saa7146_mem)
iounmap(saa->saa7146_mem);
- if (saa->video_dev.minor != -1)
+ if (video_is_registered(&saa->video_dev))
video_unregister_device(&saa->video_dev);
}
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
index 6a91714125d2..5938ad8702ef 100644
--- a/drivers/media/video/stv680.c
+++ b/drivers/media/video/stv680.c
@@ -1405,7 +1405,6 @@ static struct video_device stv680_template = {
.name = "STV0680 USB camera",
.fops = &stv680_fops,
.release = video_device_release,
- .minor = -1,
};
static int stv680_probe (struct usb_interface *intf, const struct usb_device_id *id)
@@ -1467,8 +1466,8 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id
retval = -EIO;
goto error_vdev;
}
- PDEBUG(0, "STV(i): registered new video device: video%d",
- stv680->vdev->num);
+ PDEBUG(0, "STV(i): registered new video device: %s",
+ video_device_node_name(stv680->vdev));
usb_set_intfdata (intf, stv680);
retval = stv680_create_sysfs_files(stv680->vdev);
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 269ab044072a..5b801a6e1eea 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -29,7 +29,7 @@
#include <media/tw9910.h>
#define GET_ID(val) ((val & 0xF8) >> 3)
-#define GET_ReV(val) (val & 0x07)
+#define GET_REV(val) (val & 0x07)
/*
* register offset
@@ -117,7 +117,7 @@
#define LCTL24 0x68
#define LCTL25 0x69
#define LCTL26 0x6A
-#define HSGEGIN 0x6B
+#define HSBEGIN 0x6B
#define HSEND 0x6C
#define OVSDLY 0x6D
#define OVSEND 0x6E
@@ -152,7 +152,10 @@
/* 1 : non-auto */
#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
/* 0 : Vertical out ctrl by HACTIVE and DVALID */
-#define OEN 0x04 /* Output Enable together with TRI_SEL. */
+#define OEN_TRI_SEL_MASK 0x07
+#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
+#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
+#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
/* OUTCTR1 */
#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
@@ -178,11 +181,18 @@
* but all register content remain unchanged.
* This bit is self-resetting.
*/
+#define ACNTL1_PDN_MASK 0x0e
+#define CLK_PDN 0x08 /* system clock power down */
+#define Y_PDN 0x04 /* Luma ADC power down */
+#define C_PDN 0x02 /* Chroma ADC power down */
+
+/* ACNTL2 */
+#define ACNTL2_PDN_MASK 0x40
+#define PLL_PDN 0x40 /* PLL power down */
/* VBICNTL */
-/* RTSEL : control the real time signal
-* output from the MPOUT pin
-*/
+
+/* RTSEL : control the real time signal output from the MPOUT pin */
#define RTSEL_MASK 0x07
#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
@@ -226,28 +236,7 @@ struct tw9910_priv {
struct v4l2_subdev subdev;
struct tw9910_video_info *info;
const struct tw9910_scale_ctrl *scale;
-};
-
-/*
- * register settings
- */
-
-#define ENDMARKER { 0xff, 0xff }
-
-static const struct regval_list tw9910_default_regs[] =
-{
- { OPFORM, 0x00 },
- { OUTCTR1, VSP_LO | VSSL_VVALID | HSP_HI | HSSL_HSYNC },
- ENDMARKER,
-};
-
-static const struct soc_camera_data_format tw9910_color_fmt[] = {
- {
- .name = "VYUY",
- .fourcc = V4L2_PIX_FMT_VYUY,
- .depth = 16,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- }
+ u32 revision;
};
static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
@@ -340,13 +329,6 @@ static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
},
};
-static const struct tw9910_cropping_ctrl tw9910_cropping_ctrl = {
- .vdelay = 0x0012,
- .vactive = 0x00F0,
- .hdelay = 0x0010,
- .hactive = 0x02D0,
-};
-
static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
.start = 0x0260,
.end = 0x0300,
@@ -361,6 +343,19 @@ static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
subdev);
}
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
static int tw9910_set_scale(struct i2c_client *client,
const struct tw9910_scale_ctrl *scale)
{
@@ -383,47 +378,14 @@ static int tw9910_set_scale(struct i2c_client *client,
return ret;
}
-static int tw9910_set_cropping(struct i2c_client *client,
- const struct tw9910_cropping_ctrl *cropping)
-{
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, CROP_HI,
- (cropping->vdelay & 0x0300) >> 2 |
- (cropping->vactive & 0x0300) >> 4 |
- (cropping->hdelay & 0x0300) >> 6 |
- (cropping->hactive & 0x0300) >> 8);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
- cropping->vdelay & 0x00FF);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
- cropping->vactive & 0x00FF);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, HDELAY_LO,
- cropping->hdelay & 0x00FF);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, HACTIVE_LO,
- cropping->hactive & 0x00FF);
-
- return ret;
-}
-
static int tw9910_set_hsync(struct i2c_client *client,
const struct tw9910_hsync_ctrl *hsync)
{
+ struct tw9910_priv *priv = to_tw9910(client);
int ret;
/* bit 10 - 3 */
- ret = i2c_smbus_write_byte_data(client, HSGEGIN,
+ ret = i2c_smbus_write_byte_data(client, HSBEGIN,
(hsync->start & 0x07F8) >> 3);
if (ret < 0)
return ret;
@@ -434,50 +396,41 @@ static int tw9910_set_hsync(struct i2c_client *client,
if (ret < 0)
return ret;
+ /* So far only revisions 0 and 1 have been seen */
/* bit 2 - 0 */
- ret = i2c_smbus_read_byte_data(client, HSLOWCTL);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, HSLOWCTL,
- (ret & 0x88) |
- (hsync->start & 0x0007) << 4 |
- (hsync->end & 0x0007));
+ if (1 == priv->revision)
+ ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
+ (hsync->start & 0x0007) << 4 |
+ (hsync->end & 0x0007));
return ret;
}
-static int tw9910_write_array(struct i2c_client *client,
- const struct regval_list *vals)
+static void tw9910_reset(struct i2c_client *client)
{
- while (vals->reg_num != 0xff) {
- int ret = i2c_smbus_write_byte_data(client,
- vals->reg_num,
- vals->value);
- if (ret < 0)
- return ret;
- vals++;
- }
- return 0;
+ tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
+ msleep(1);
}
-static int tw9910_mask_set(struct i2c_client *client, u8 command,
- u8 mask, u8 set)
+static int tw9910_power(struct i2c_client *client, int enable)
{
- s32 val = i2c_smbus_read_byte_data(client, command);
- if (val < 0)
- return val;
+ int ret;
+ u8 acntl1;
+ u8 acntl2;
- val &= ~mask;
- val |= set & mask;
+ if (enable) {
+ acntl1 = 0;
+ acntl2 = 0;
+ } else {
+ acntl1 = CLK_PDN | Y_PDN | C_PDN;
+ acntl2 = PLL_PDN;
+ }
- return i2c_smbus_write_byte_data(client, command, val);
-}
+ ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
+ if (ret < 0)
+ return ret;
-static void tw9910_reset(struct i2c_client *client)
-{
- i2c_smbus_write_byte_data(client, ACNTL1, SRESET);
- msleep(1);
+ return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
}
static const struct tw9910_scale_ctrl*
@@ -518,27 +471,62 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = sd->priv;
struct tw9910_priv *priv = to_tw9910(client);
+ u8 val;
+ int ret;
- if (!enable)
- return 0;
+ if (!enable) {
+ switch (priv->revision) {
+ case 0:
+ val = OEN_TRI_SEL_ALL_OFF_r0;
+ break;
+ case 1:
+ val = OEN_TRI_SEL_ALL_OFF_r1;
+ break;
+ default:
+ dev_err(&client->dev, "un-supported revision\n");
+ return -EINVAL;
+ }
+ } else {
+ val = OEN_TRI_SEL_ALL_ON;
- if (!priv->scale) {
- dev_err(&client->dev, "norm select error\n");
- return -EPERM;
+ if (!priv->scale) {
+ dev_err(&client->dev, "norm select error\n");
+ return -EPERM;
+ }
+
+ dev_dbg(&client->dev, "%s %dx%d\n",
+ priv->scale->name,
+ priv->scale->width,
+ priv->scale->height);
}
- dev_dbg(&client->dev, "%s %dx%d\n",
- priv->scale->name,
- priv->scale->width,
- priv->scale->height);
+ ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
+ if (ret < 0)
+ return ret;
- return 0;
+ return tw9910_power(client, enable);
}
static int tw9910_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
- return 0;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct i2c_client *client = sd->priv;
+ u8 val = VSSL_VVALID | HSSL_DVALID;
+
+ /*
+ * set OUTCTR1
+ *
+ * We use VVALID and DVALID signals to control VSYNC and HSYNC
+ * outputs, in this mode their polarity is inverted.
+ */
+ if (flags & SOCAM_HSYNC_ACTIVE_LOW)
+ val |= HSP_HI;
+
+ if (flags & SOCAM_VSYNC_ACTIVE_LOW)
+ val |= VSP_HI;
+
+ return i2c_smbus_write_byte_data(client, OUTCTR1, val);
}
static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
@@ -548,6 +536,7 @@ static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_VSYNC_ACTIVE_LOW | SOCAM_HSYNC_ACTIVE_LOW |
SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
return soc_camera_apply_sensor_flags(icl, flags);
@@ -576,8 +565,11 @@ static int tw9910_enum_input(struct soc_camera_device *icd,
static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
{
+ struct i2c_client *client = sd->priv;
+ struct tw9910_priv *priv = to_tw9910(client);
+
id->ident = V4L2_IDENT_TW9910;
- id->revision = 0;
+ id->revision = priv->revision;
return 0;
}
@@ -596,7 +588,8 @@ static int tw9910_g_register(struct v4l2_subdev *sd,
if (ret < 0)
return ret;
- /* ret = int
+ /*
+ * ret = int
* reg->val = __u64
*/
reg->val = (__u64)ret;
@@ -637,9 +630,6 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
* reset hardware
*/
tw9910_reset(client);
- ret = tw9910_write_array(client, tw9910_default_regs);
- if (ret < 0)
- goto tw9910_set_fmt_error;
/*
* set bus width
@@ -688,13 +678,6 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
goto tw9910_set_fmt_error;
/*
- * set cropping
- */
- ret = tw9910_set_cropping(client, &tw9910_cropping_ctrl);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
* set hsync
*/
ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl);
@@ -762,11 +745,11 @@ static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
return 0;
}
-static int tw9910_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int tw9910_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct tw9910_priv *priv = to_tw9910(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
if (!priv->scale) {
int ret;
@@ -783,74 +766,76 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
return ret;
}
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- pix->width = priv->scale->width;
- pix->height = priv->scale->height;
- pix->pixelformat = V4L2_PIX_FMT_VYUY;
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- pix->field = V4L2_FIELD_INTERLACED;
+ mf->width = priv->scale->width;
+ mf->height = priv->scale->height;
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_INTERLACED_BT;
return 0;
}
-static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int tw9910_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct tw9910_priv *priv = to_tw9910(client);
- struct v4l2_pix_format *pix = &f->fmt.pix;
/* See tw9910_s_crop() - no proper cropping support */
struct v4l2_crop a = {
.c = {
.left = 0,
.top = 0,
- .width = pix->width,
- .height = pix->height,
+ .width = mf->width,
+ .height = mf->height,
},
};
- int i, ret;
+ int ret;
+
+ WARN_ON(mf->field != V4L2_FIELD_ANY &&
+ mf->field != V4L2_FIELD_INTERLACED_BT);
/*
* check color format
*/
- for (i = 0; i < ARRAY_SIZE(tw9910_color_fmt); i++)
- if (pix->pixelformat == tw9910_color_fmt[i].fourcc)
- break;
-
- if (i == ARRAY_SIZE(tw9910_color_fmt))
+ if (mf->code != V4L2_MBUS_FMT_YUYV8_2X8_BE)
return -EINVAL;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
ret = tw9910_s_crop(sd, &a);
if (!ret) {
- pix->width = priv->scale->width;
- pix->height = priv->scale->height;
+ mf->width = priv->scale->width;
+ mf->height = priv->scale->height;
}
return ret;
}
-static int tw9910_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int tw9910_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = sd->priv;
struct soc_camera_device *icd = client->dev.platform_data;
- struct v4l2_pix_format *pix = &f->fmt.pix;
const struct tw9910_scale_ctrl *scale;
- if (V4L2_FIELD_ANY == pix->field) {
- pix->field = V4L2_FIELD_INTERLACED;
- } else if (V4L2_FIELD_INTERLACED != pix->field) {
- dev_err(&client->dev, "Field type invalid.\n");
+ if (V4L2_FIELD_ANY == mf->field) {
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+ } else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
+ dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
return -EINVAL;
}
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
/*
* select suitable norm
*/
- scale = tw9910_select_norm(icd, pix->width, pix->height);
+ scale = tw9910_select_norm(icd, mf->width, mf->height);
if (!scale)
return -EINVAL;
- pix->width = scale->width;
- pix->height = scale->height;
+ mf->width = scale->width;
+ mf->height = scale->height;
return 0;
}
@@ -859,7 +844,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
- s32 val;
+ s32 id;
/*
* We must have a parent by now. And it cannot be a wrong one.
@@ -878,23 +863,24 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
return -ENODEV;
}
- icd->formats = tw9910_color_fmt;
- icd->num_formats = ARRAY_SIZE(tw9910_color_fmt);
-
/*
* check and show Product ID
+ * So far only revisions 0 and 1 have been seen
*/
- val = i2c_smbus_read_byte_data(client, ID);
+ id = i2c_smbus_read_byte_data(client, ID);
+ priv->revision = GET_REV(id);
+ id = GET_ID(id);
- if (0x0B != GET_ID(val) ||
- 0x00 != GET_ReV(val)) {
+ if (0x0B != id ||
+ 0x01 < priv->revision) {
dev_err(&client->dev,
- "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val));
+ "Product ID error %x:%x\n",
+ id, priv->revision);
return -ENODEV;
}
dev_info(&client->dev,
- "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val));
+ "tw9910 Product ID %0x:%0x\n", id, priv->revision);
icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL;
icd->vdev->current_norm = V4L2_STD_NTSC;
@@ -917,14 +903,25 @@ static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
#endif
};
+static int tw9910_enum_fmt(struct v4l2_subdev *sd, int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV8_2X8_BE;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.s_stream = tw9910_s_stream,
- .g_fmt = tw9910_g_fmt,
- .s_fmt = tw9910_s_fmt,
- .try_fmt = tw9910_try_fmt,
+ .g_mbus_fmt = tw9910_g_fmt,
+ .s_mbus_fmt = tw9910_s_fmt,
+ .try_mbus_fmt = tw9910_try_fmt,
.cropcap = tw9910_cropcap,
.g_crop = tw9910_g_crop,
.s_crop = tw9910_s_crop,
+ .enum_mbus_fmt = tw9910_enum_fmt,
};
static struct v4l2_subdev_ops tw9910_subdev_ops = {
@@ -954,10 +951,10 @@ static int tw9910_probe(struct i2c_client *client,
}
icl = to_soc_camera_link(icd);
- if (!icl)
+ if (!icl || !icl->priv)
return -EINVAL;
- info = container_of(icl, struct tw9910_video_info, link);
+ info = icl->priv;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev,
@@ -975,7 +972,7 @@ static int tw9910_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
icd->ops = &tw9910_ops;
- icd->iface = info->link.bus_id;
+ icd->iface = icl->bus_id;
ret = tw9910_video_probe(icd, client);
if (ret) {
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index dea8b321fb4a..5ac37c6c4313 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -1053,9 +1053,9 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd)
"%s: video_register_device() successful\n", __func__);
}
- dev_info(&uvd->dev->dev, "%s on /dev/video%d: canvas=%s videosize=%s\n",
+ dev_info(&uvd->dev->dev, "%s on %s: canvas=%s videosize=%s\n",
(uvd->handle != NULL) ? uvd->handle->drvName : "???",
- uvd->vdev.num, tmp2, tmp1);
+ video_device_node_name(&uvd->vdev), tmp2, tmp1);
usb_get_dev(uvd->dev);
return 0;
diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
index 45fce39ec9ad..6030410c6677 100644
--- a/drivers/media/video/usbvideo/vicam.c
+++ b/drivers/media/video/usbvideo/vicam.c
@@ -796,7 +796,6 @@ static const struct v4l2_file_operations vicam_fops = {
static struct video_device vicam_template = {
.name = "ViCam-based USB Camera",
.fops = &vicam_fops,
- .minor = -1,
.release = video_device_release_empty,
};
@@ -873,8 +872,8 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
return -EIO;
}
- printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",
- cam->vdev.num);
+ printk(KERN_INFO "ViCam webcam driver now controlling device %s\n",
+ video_device_node_name(&cam->vdev));
usb_set_intfdata (intf, cam);
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
index c19f51dba2ee..0613922997e0 100644
--- a/drivers/media/video/usbvision/usbvision-i2c.c
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -215,8 +215,8 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision)
memcpy(&usbvision->i2c_adap, &i2c_adap_template,
sizeof(struct i2c_adapter));
- sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name),
- " #%d", usbvision->vdev->num);
+ sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name,
+ usbvision->dev->bus->busnum, usbvision->dev->devpath);
PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name);
usbvision->i2c_adap.dev.parent = &usbvision->dev->dev;
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index c07b0ac452ab..1054546db908 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -1328,7 +1328,6 @@ static struct video_device usbvision_video_template = {
.ioctl_ops = &usbvision_ioctl_ops,
.name = "usbvision-video",
.release = video_device_release,
- .minor = -1,
.tvnorms = USBVISION_NORMS,
.current_norm = V4L2_STD_PAL
};
@@ -1362,7 +1361,6 @@ static struct video_device usbvision_radio_template = {
.fops = &usbvision_radio_fops,
.name = "usbvision-radio",
.release = video_device_release,
- .minor = -1,
.ioctl_ops = &usbvision_radio_ioctl_ops,
.tvnorms = USBVISION_NORMS,
@@ -1382,7 +1380,6 @@ static struct video_device usbvision_vbi_template=
.fops = &usbvision_vbi_fops,
.release = video_device_release,
.name = "usbvision-vbi",
- .minor = -1,
};
@@ -1404,7 +1401,6 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
return NULL;
}
*vdev = *vdev_template;
-// vdev->minor = -1;
vdev->v4l2_dev = &usbvision->v4l2_dev;
snprintf(vdev->name, sizeof(vdev->name), "%s", name);
video_set_drvdata(vdev, usbvision);
@@ -1416,9 +1412,9 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision)
{
// vbi Device:
if (usbvision->vbi) {
- PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]",
- usbvision->vbi->num);
- if (usbvision->vbi->minor != -1) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->vbi));
+ if (video_is_registered(usbvision->vbi)) {
video_unregister_device(usbvision->vbi);
} else {
video_device_release(usbvision->vbi);
@@ -1428,9 +1424,9 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision)
// Radio Device:
if (usbvision->rdev) {
- PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]",
- usbvision->rdev->num);
- if (usbvision->rdev->minor != -1) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->rdev));
+ if (video_is_registered(usbvision->rdev)) {
video_unregister_device(usbvision->rdev);
} else {
video_device_release(usbvision->rdev);
@@ -1440,9 +1436,9 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision)
// Video Device:
if (usbvision->vdev) {
- PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]",
- usbvision->vdev->num);
- if (usbvision->vdev->minor != -1) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->vdev));
+ if (video_is_registered(usbvision->vdev)) {
video_unregister_device(usbvision->vdev);
} else {
video_device_release(usbvision->vdev);
@@ -1466,8 +1462,8 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
video_nr)<0) {
goto err_exit;
}
- printk(KERN_INFO "USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]\n",
- usbvision->nr, usbvision->vdev->num);
+ printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n",
+ usbvision->nr, video_device_node_name(usbvision->vdev));
// Radio Device:
if (usbvision_device_data[usbvision->DevModel].Radio) {
@@ -1483,8 +1479,8 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
radio_nr)<0) {
goto err_exit;
}
- printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]\n",
- usbvision->nr, usbvision->rdev->num);
+ printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n",
+ usbvision->nr, video_device_node_name(usbvision->rdev));
}
// vbi Device:
if (usbvision_device_data[usbvision->DevModel].vbi) {
@@ -1499,8 +1495,8 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
vbi_nr)<0) {
goto err_exit;
}
- printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)\n",
- usbvision->nr, usbvision->vbi->num);
+ printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device %s [v4l2] (Not Working Yet!)\n",
+ usbvision->nr, video_device_node_name(usbvision->vbi));
}
// all done
return 0;
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index c31bc50113bc..391cccca7ffc 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -1651,7 +1651,6 @@ static int uvc_register_video(struct uvc_device *dev,
* get another one.
*/
vdev->parent = &dev->intf->dev;
- vdev->minor = -1;
vdev->fops = &uvc_fops;
vdev->release = uvc_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 05139a4f14f6..9a9802830d41 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -145,7 +145,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non "
"compliance - GET_MIN/MAX(PROBE) incorrectly "
"supported. Enabling workaround.\n");
- memset(ctrl, 0, sizeof ctrl);
+ memset(ctrl, 0, sizeof *ctrl);
ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
ret = 0;
goto out;
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index e8e5affbabce..36b5cb86fb57 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -1024,3 +1024,50 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
}
}
EXPORT_SYMBOL_GPL(v4l_bound_align_image);
+
+/**
+ * v4l_fill_dv_preset_info - fill description of a digital video preset
+ * @preset - preset value
+ * @info - pointer to struct v4l2_dv_enum_preset
+ *
+ * drivers can use this helper function to fill description of dv preset
+ * in info.
+ */
+int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info)
+{
+ static const struct v4l2_dv_preset_info {
+ u16 width;
+ u16 height;
+ const char *name;
+ } dv_presets[] = {
+ { 0, 0, "Invalid" }, /* V4L2_DV_INVALID */
+ { 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */
+ { 720, 576, "576p@50" }, /* V4L2_DV_576P50 */
+ { 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */
+ { 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */
+ { 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */
+ { 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */
+ { 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */
+ { 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */
+ { 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */
+ { 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */
+ { 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */
+ { 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */
+ { 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */
+ { 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */
+ { 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */
+ { 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */
+ { 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */
+ { 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */
+ };
+
+ if (info == NULL || preset >= ARRAY_SIZE(dv_presets))
+ return -EINVAL;
+
+ info->preset = preset;
+ info->width = dv_presets[preset].width;
+ info->height = dv_presets[preset].height;
+ strlcpy(info->name, dv_presets[preset].name, sizeof(info->name));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 997975d5e024..c4150bd26337 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -1077,6 +1077,12 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_DBG_G_REGISTER:
case VIDIOC_DBG_G_CHIP_IDENT:
case VIDIOC_S_HW_FREQ_SEEK:
+ case VIDIOC_ENUM_DV_PRESETS:
+ case VIDIOC_S_DV_PRESET:
+ case VIDIOC_G_DV_PRESET:
+ case VIDIOC_QUERY_DV_PRESET:
+ case VIDIOC_S_DV_TIMINGS:
+ case VIDIOC_G_DV_TIMINGS:
ret = do_video_ioctl(file, cmd, arg);
break;
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 500cbe9891ac..709069916068 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -189,7 +189,7 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf,
if (!vdev->fops->read)
return -EINVAL;
- if (video_is_unregistered(vdev))
+ if (!video_is_registered(vdev))
return -EIO;
return vdev->fops->read(filp, buf, sz, off);
}
@@ -201,7 +201,7 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf,
if (!vdev->fops->write)
return -EINVAL;
- if (video_is_unregistered(vdev))
+ if (!video_is_registered(vdev))
return -EIO;
return vdev->fops->write(filp, buf, sz, off);
}
@@ -210,7 +210,7 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
{
struct video_device *vdev = video_devdata(filp);
- if (!vdev->fops->poll || video_is_unregistered(vdev))
+ if (!vdev->fops->poll || !video_is_registered(vdev))
return DEFAULT_POLLMASK;
return vdev->fops->poll(filp, poll);
}
@@ -250,7 +250,7 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp,
if (!vdev->fops->get_unmapped_area)
return -ENOSYS;
- if (video_is_unregistered(vdev))
+ if (!video_is_registered(vdev))
return -ENODEV;
return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
}
@@ -260,8 +260,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
struct video_device *vdev = video_devdata(filp);
- if (!vdev->fops->mmap ||
- video_is_unregistered(vdev))
+ if (!vdev->fops->mmap || !video_is_registered(vdev))
return -ENODEV;
return vdev->fops->mmap(filp, vm);
}
@@ -277,7 +276,7 @@ static int v4l2_open(struct inode *inode, struct file *filp)
vdev = video_devdata(filp);
/* return ENODEV if the video device has been removed
already or if it is not registered anymore. */
- if (vdev == NULL || video_is_unregistered(vdev)) {
+ if (vdev == NULL || !video_is_registered(vdev)) {
mutex_unlock(&videodev_lock);
return -ENODEV;
}
@@ -551,10 +550,11 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
- printk(KERN_WARNING "%s: requested %s%d, got %s%d\n",
- __func__, name_base, nr, name_base, vdev->num);
+ printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
+ name_base, nr, video_device_node_name(vdev));
/* Part 5: Activate this minor. The char device can now be used. */
+ set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
@@ -593,11 +593,11 @@ EXPORT_SYMBOL(video_register_device_no_warn);
void video_unregister_device(struct video_device *vdev)
{
/* Check if vdev was ever registered at all */
- if (!vdev || vdev->minor < 0)
+ if (!vdev || !video_is_registered(vdev))
return;
mutex_lock(&videodev_lock);
- set_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
+ clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_unlock(&videodev_lock);
device_unregister(&vdev->dev);
}
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 30cc3347ae52..4b11257c3184 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -284,6 +284,12 @@ static const char *v4l2_ioctls[] = {
[_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT",
[_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK",
#endif
+ [_IOC_NR(VIDIOC_ENUM_DV_PRESETS)] = "VIDIOC_ENUM_DV_PRESETS",
+ [_IOC_NR(VIDIOC_S_DV_PRESET)] = "VIDIOC_S_DV_PRESET",
+ [_IOC_NR(VIDIOC_G_DV_PRESET)] = "VIDIOC_G_DV_PRESET",
+ [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET",
+ [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS",
+ [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS",
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -1135,6 +1141,19 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_input *p = arg;
+ /*
+ * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific input, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_IN_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_IN_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
+
if (!ops->vidioc_enum_input)
break;
@@ -1179,6 +1198,19 @@ static long __video_do_ioctl(struct file *file,
if (!ops->vidioc_enum_output)
break;
+ /*
+ * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific output, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_OUT_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
+
ret = ops->vidioc_enum_output(file, fh, p);
if (!ret)
dbgarg(cmd, "index=%d, name=%s, type=%d, "
@@ -1794,6 +1826,121 @@ static long __video_do_ioctl(struct file *file,
}
break;
}
+ case VIDIOC_ENUM_DV_PRESETS:
+ {
+ struct v4l2_dv_enum_preset *p = arg;
+
+ if (!ops->vidioc_enum_dv_presets)
+ break;
+
+ ret = ops->vidioc_enum_dv_presets(file, fh, p);
+ if (!ret)
+ dbgarg(cmd,
+ "index=%d, preset=%d, name=%s, width=%d,"
+ " height=%d ",
+ p->index, p->preset, p->name, p->width,
+ p->height);
+ break;
+ }
+ case VIDIOC_S_DV_PRESET:
+ {
+ struct v4l2_dv_preset *p = arg;
+
+ if (!ops->vidioc_s_dv_preset)
+ break;
+
+ dbgarg(cmd, "preset=%d\n", p->preset);
+ ret = ops->vidioc_s_dv_preset(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_DV_PRESET:
+ {
+ struct v4l2_dv_preset *p = arg;
+
+ if (!ops->vidioc_g_dv_preset)
+ break;
+
+ ret = ops->vidioc_g_dv_preset(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "preset=%d\n", p->preset);
+ break;
+ }
+ case VIDIOC_QUERY_DV_PRESET:
+ {
+ struct v4l2_dv_preset *p = arg;
+
+ if (!ops->vidioc_query_dv_preset)
+ break;
+
+ ret = ops->vidioc_query_dv_preset(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "preset=%d\n", p->preset);
+ break;
+ }
+ case VIDIOC_S_DV_TIMINGS:
+ {
+ struct v4l2_dv_timings *p = arg;
+
+ if (!ops->vidioc_s_dv_timings)
+ break;
+
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld,"
+ " width=%d, height=%d, polarities=%x,"
+ " hfrontporch=%d, hsync=%d, hbackporch=%d,"
+ " vfrontporch=%d, vsync=%d, vbackporch=%d,"
+ " il_vfrontporch=%d, il_vsync=%d,"
+ " il_vbackporch=%d\n",
+ p->bt.interlaced, p->bt.pixelclock,
+ p->bt.width, p->bt.height, p->bt.polarities,
+ p->bt.hfrontporch, p->bt.hsync,
+ p->bt.hbackporch, p->bt.vfrontporch,
+ p->bt.vsync, p->bt.vbackporch,
+ p->bt.il_vfrontporch, p->bt.il_vsync,
+ p->bt.il_vbackporch);
+ ret = ops->vidioc_s_dv_timings(file, fh, p);
+ break;
+ default:
+ dbgarg2("Unknown type %d!\n", p->type);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_DV_TIMINGS:
+ {
+ struct v4l2_dv_timings *p = arg;
+
+ if (!ops->vidioc_g_dv_timings)
+ break;
+
+ ret = ops->vidioc_g_dv_timings(file, fh, p);
+ if (!ret) {
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ dbgarg2("bt-656/1120:interlaced=%d,"
+ " pixelclock=%lld,"
+ " width=%d, height=%d, polarities=%x,"
+ " hfrontporch=%d, hsync=%d,"
+ " hbackporch=%d, vfrontporch=%d,"
+ " vsync=%d, vbackporch=%d,"
+ " il_vfrontporch=%d, il_vsync=%d,"
+ " il_vbackporch=%d\n",
+ p->bt.interlaced, p->bt.pixelclock,
+ p->bt.width, p->bt.height,
+ p->bt.polarities, p->bt.hfrontporch,
+ p->bt.hsync, p->bt.hbackporch,
+ p->bt.vfrontporch, p->bt.vsync,
+ p->bt.vbackporch, p->bt.il_vfrontporch,
+ p->bt.il_vsync, p->bt.il_vbackporch);
+ break;
+ default:
+ dbgarg2("Unknown type %d!\n", p->type);
+ break;
+ }
+ }
+ break;
+ }
default:
{
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index d25f28461da1..22c01097e8a8 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -141,9 +141,11 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
struct vm_area_struct *vma;
unsigned long prev_pfn, this_pfn;
unsigned long pages_done, user_address;
+ unsigned int offset;
int ret;
- mem->size = PAGE_ALIGN(vb->size);
+ offset = vb->baddr & ~PAGE_MASK;
+ mem->size = PAGE_ALIGN(vb->size + offset);
mem->is_userptr = 0;
ret = -EINVAL;
@@ -166,7 +168,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
break;
if (pages_done == 0)
- mem->dma_handle = this_pfn << PAGE_SHIFT;
+ mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
else if (this_pfn != (prev_pfn + 1))
ret = -EFAULT;
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index b034a81d2b1c..a15d1e7cbed8 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -4068,7 +4068,6 @@ static struct video_device vdev_template = {
.fops = &vino_fops,
.ioctl_ops = &vino_ioctl_ops,
.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
- .minor = -1,
};
static void vino_module_cleanup(int stage)
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 7705fc6baf00..37632a064966 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -1148,7 +1148,8 @@ static int vivi_open(struct file *file)
return -EBUSY;
}
- dprintk(dev, 1, "open /dev/video%d type=%s users=%d\n", dev->vfd->num,
+ dprintk(dev, 1, "open %s type=%s users=%d\n",
+ video_device_node_name(dev->vfd),
v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
/* allocate + initialize per filehandle data */
@@ -1221,8 +1222,7 @@ static int vivi_close(struct file *file)
struct vivi_fh *fh = file->private_data;
struct vivi_dev *dev = fh->dev;
struct vivi_dmaqueue *vidq = &dev->vidq;
-
- int minor = video_devdata(file)->minor;
+ struct video_device *vdev = video_devdata(file);
vivi_stop_thread(vidq);
videobuf_stop(&fh->vb_vidq);
@@ -1234,8 +1234,8 @@ static int vivi_close(struct file *file)
dev->users--;
mutex_unlock(&dev->mutex);
- dprintk(dev, 1, "close called (minor=%d, users=%d)\n",
- minor, dev->users);
+ dprintk(dev, 1, "close called (dev=%s, users=%d)\n",
+ video_device_node_name(vdev), dev->users);
return 0;
}
@@ -1296,7 +1296,6 @@ static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
- .minor = -1,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
@@ -1317,8 +1316,8 @@ static int vivi_release(void)
list_del(list);
dev = list_entry(list, struct vivi_dev, vivi_devlist);
- v4l2_info(&dev->v4l2_dev, "unregistering /dev/video%d\n",
- dev->vfd->num);
+ v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+ video_device_node_name(dev->vfd));
video_unregister_device(dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
@@ -1372,15 +1371,12 @@ static int __init vivi_create_instance(int inst)
/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
- snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
- vivi_template.name, vfd->num);
-
if (video_nr >= 0)
video_nr++;
dev->vfd = vfd;
- v4l2_info(&dev->v4l2_dev, "V4L2 device registered as /dev/video%d\n",
- vfd->num);
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(vfd));
return 0;
rel_vdev:
diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c
index 37fcdc447db5..d807eea91757 100644
--- a/drivers/media/video/w9968cf.c
+++ b/drivers/media/video/w9968cf.c
@@ -2323,9 +2323,9 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam)
error:
cam->sensor_initialized = 0;
cam->sensor = CC_UNKNOWN;
- DBG(1, "Image sensor initialization failed for %s (/dev/video%d). "
+ DBG(1, "Image sensor initialization failed for %s (%s). "
"Try to detach and attach this device again",
- symbolic(camlist, cam->id), cam->v4ldev->num)
+ symbolic(camlist, cam->id), video_device_node_name(cam->v4ldev))
return err;
}
@@ -2571,7 +2571,8 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
{
mutex_lock(&w9968cf_devlist_mutex);
- DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->num)
+ DBG(2, "V4L device deregistered: %s",
+ video_device_node_name(cam->v4ldev))
video_unregister_device(cam->v4ldev);
list_del(&cam->v4llist);
@@ -2605,17 +2606,19 @@ static int w9968cf_open(struct file *filp)
if (cam->sensor == CC_UNKNOWN) {
DBG(2, "No supported image sensor has been detected by the "
- "'ovcamchip' module for the %s (/dev/video%d). Make "
- "sure it is loaded *before* (re)connecting the camera.",
- symbolic(camlist, cam->id), cam->v4ldev->num)
+ "'ovcamchip' module for the %s (%s). Make sure "
+ "it is loaded *before* (re)connecting the camera.",
+ symbolic(camlist, cam->id),
+ video_device_node_name(cam->v4ldev))
mutex_unlock(&cam->dev_mutex);
up_read(&w9968cf_disconnect);
return -ENODEV;
}
if (cam->users) {
- DBG(2, "%s (/dev/video%d) has been already occupied by '%s'",
- symbolic(camlist, cam->id), cam->v4ldev->num, cam->command)
+ DBG(2, "%s (%s) has been already occupied by '%s'",
+ symbolic(camlist, cam->id),
+ video_device_node_name(cam->v4ldev), cam->command)
if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
mutex_unlock(&cam->dev_mutex);
up_read(&w9968cf_disconnect);
@@ -2636,8 +2639,8 @@ static int w9968cf_open(struct file *filp)
mutex_lock(&cam->dev_mutex);
}
- DBG(5, "Opening '%s', /dev/video%d ...",
- symbolic(camlist, cam->id), cam->v4ldev->num)
+ DBG(5, "Opening '%s', %s ...",
+ symbolic(camlist, cam->id), video_device_node_name(cam->v4ldev))
cam->streaming = 0;
cam->misconfigured = 0;
@@ -2874,8 +2877,7 @@ static long w9968cf_v4l_ioctl(struct file *filp,
.minwidth = cam->minwidth,
.minheight = cam->minheight,
};
- sprintf(cap.name, "W996[87]CF USB Camera #%d",
- cam->v4ldev->num);
+ sprintf(cap.name, "W996[87]CF USB Camera");
cap.maxwidth = (cam->upscaling && w9968cf_vpp)
? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
: cam->maxwidth;
@@ -3485,7 +3487,6 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
strcpy(cam->v4ldev->name, symbolic(camlist, mod_id));
cam->v4ldev->fops = &w9968cf_fops;
- cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam);
cam->v4ldev->v4l2_dev = &cam->v4l2_dev;
@@ -3501,7 +3502,8 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
- DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev->num)
+ DBG(2, "V4L device registered as %s",
+ video_device_node_name(cam->v4ldev))
/* Set some basic constants */
w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
@@ -3557,10 +3559,10 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
wake_up_interruptible_all(&cam->open);
if (cam->users) {
- DBG(2, "The device is open (/dev/video%d)! "
+ DBG(2, "The device is open (%s)! "
"Process name: %s. Deregistration and memory "
"deallocation are deferred on close.",
- cam->v4ldev->num, cam->command)
+ video_device_node_name(cam->v4ldev), cam->command)
cam->misconfigured = 1;
w9968cf_stop_transfer(cam);
wake_up_interruptible(&cam->wait_queue);
diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c
index 312a71336fd0..e44e4b5f3e50 100644
--- a/drivers/media/video/zc0301/zc0301_core.c
+++ b/drivers/media/video/zc0301/zc0301_core.c
@@ -538,8 +538,8 @@ static int zc0301_stream_interrupt(struct zc0301_device* cam)
else if (cam->stream != STREAM_OFF) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "URB timeout reached. The camera is misconfigured. To "
- "use it, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use it, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -640,7 +640,8 @@ static void zc0301_release_resources(struct kref *kref)
{
struct zc0301_device *cam = container_of(kref, struct zc0301_device,
kref);
- DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->num);
+ DBG(2, "V4L2 device %s deregistered",
+ video_device_node_name(cam->v4ldev));
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
usb_put_dev(cam->usbdev);
@@ -679,7 +680,8 @@ static int zc0301_open(struct file *filp)
}
if (cam->users) {
- DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->num);
+ DBG(2, "Device %s is busy...",
+ video_device_node_name(cam->v4ldev));
DBG(3, "Simultaneous opens are not supported");
if ((filp->f_flags & O_NONBLOCK) ||
(filp->f_flags & O_NDELAY)) {
@@ -722,7 +724,8 @@ static int zc0301_open(struct file *filp)
cam->frame_count = 0;
zc0301_empty_framequeues(cam);
- DBG(3, "Video device /dev/video%d is open", cam->v4ldev->num);
+ DBG(3, "Video device %s is open",
+ video_device_node_name(cam->v4ldev));
out:
mutex_unlock(&cam->open_mutex);
@@ -746,7 +749,8 @@ static int zc0301_release(struct file *filp)
cam->users--;
wake_up_interruptible_nr(&cam->wait_open, 1);
- DBG(3, "Video device /dev/video%d closed", cam->v4ldev->num);
+ DBG(3, "Video device %s closed",
+ video_device_node_name(cam->v4ldev));
kref_put(&cam->kref, zc0301_release_resources);
@@ -1276,8 +1280,8 @@ zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg)
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -1289,8 +1293,8 @@ zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg)
nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -ENOMEM;
}
@@ -1471,8 +1475,8 @@ zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd,
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -1483,8 +1487,8 @@ zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd,
nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
- "use the camera, close and open /dev/video%d again.",
- cam->v4ldev->num);
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -ENOMEM;
}
@@ -1530,8 +1534,8 @@ zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg)
if (err) { /* atomic, no rollback in ioctl() */
cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
- "problems. To use the camera, close and open "
- "/dev/video%d again.", cam->v4ldev->num);
+ "problems. To use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
return -EIO;
}
@@ -1984,7 +1988,6 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
strcpy(cam->v4ldev->name, "ZC0301[P] PC Camera");
cam->v4ldev->fops = &zc0301_fops;
- cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
cam->v4ldev->parent = &udev->dev;
video_set_drvdata(cam->v4ldev, cam);
@@ -2003,7 +2006,8 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
- DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->num);
+ DBG(2, "V4L2 device registered as %s",
+ video_device_node_name(cam->v4ldev));
cam->module_param.force_munmap = force_munmap[dev_nr];
cam->module_param.frame_timeout = frame_timeout[dev_nr];
@@ -2040,9 +2044,9 @@ static void zc0301_usb_disconnect(struct usb_interface* intf)
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
if (cam->users) {
- DBG(2, "Device /dev/video%d is open! Deregistration and "
+ DBG(2, "Device %s is open! Deregistration and "
"memory deallocation are deferred.",
- cam->v4ldev->num);
+ video_device_node_name(cam->v4ldev));
cam->state |= DEV_MISCONFIGURED;
zc0301_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index e9f72ca458f1..2ddffed019ee 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -3387,6 +3387,5 @@ struct video_device zoran_template __devinitdata = {
.ioctl_ops = &zoran_ioctl_ops,
.release = &zoran_vdev_release,
.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
- .minor = -1
};
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index 2ef110b5221b..f0eae83e3d89 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -1455,7 +1455,6 @@ static struct video_device zr364xx_template = {
.fops = &zr364xx_fops,
.ioctl_ops = &zr364xx_ioctl_ops,
.release = video_device_release,
- .minor = -1,
};
@@ -1635,8 +1634,8 @@ static int zr364xx_probe(struct usb_interface *intf,
spin_lock_init(&cam->slock);
- dev_info(&udev->dev, DRIVER_DESC " controlling video device %d\n",
- cam->vdev->num);
+ dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n",
+ video_device_node_name(cam->vdev));
return 0;
}
diff --git a/drivers/mfd/88pm8607.c b/drivers/mfd/88pm8607.c
new file mode 100644
index 000000000000..7e3f65907993
--- /dev/null
+++ b/drivers/mfd/88pm8607.c
@@ -0,0 +1,302 @@
+/*
+ * Base driver for Marvell 88PM8607
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm8607.h>
+
+
+#define PM8607_REG_RESOURCE(_start, _end) \
+{ \
+ .start = PM8607_##_start, \
+ .end = PM8607_##_end, \
+ .flags = IORESOURCE_IO, \
+}
+
+static struct resource pm8607_regulator_resources[] = {
+ PM8607_REG_RESOURCE(BUCK1, BUCK1),
+ PM8607_REG_RESOURCE(BUCK2, BUCK2),
+ PM8607_REG_RESOURCE(BUCK3, BUCK3),
+ PM8607_REG_RESOURCE(LDO1, LDO1),
+ PM8607_REG_RESOURCE(LDO2, LDO2),
+ PM8607_REG_RESOURCE(LDO3, LDO3),
+ PM8607_REG_RESOURCE(LDO4, LDO4),
+ PM8607_REG_RESOURCE(LDO5, LDO5),
+ PM8607_REG_RESOURCE(LDO6, LDO6),
+ PM8607_REG_RESOURCE(LDO7, LDO7),
+ PM8607_REG_RESOURCE(LDO8, LDO8),
+ PM8607_REG_RESOURCE(LDO9, LDO9),
+ PM8607_REG_RESOURCE(LDO10, LDO10),
+ PM8607_REG_RESOURCE(LDO12, LDO12),
+ PM8607_REG_RESOURCE(LDO14, LDO14),
+};
+
+#define PM8607_REG_DEVS(_name, _id) \
+{ \
+ .name = "88pm8607-" #_name, \
+ .num_resources = 1, \
+ .resources = &pm8607_regulator_resources[PM8607_ID_##_id], \
+}
+
+static struct mfd_cell pm8607_devs[] = {
+ PM8607_REG_DEVS(buck1, BUCK1),
+ PM8607_REG_DEVS(buck2, BUCK2),
+ PM8607_REG_DEVS(buck3, BUCK3),
+ PM8607_REG_DEVS(ldo1, LDO1),
+ PM8607_REG_DEVS(ldo2, LDO2),
+ PM8607_REG_DEVS(ldo3, LDO3),
+ PM8607_REG_DEVS(ldo4, LDO4),
+ PM8607_REG_DEVS(ldo5, LDO5),
+ PM8607_REG_DEVS(ldo6, LDO6),
+ PM8607_REG_DEVS(ldo7, LDO7),
+ PM8607_REG_DEVS(ldo8, LDO8),
+ PM8607_REG_DEVS(ldo9, LDO9),
+ PM8607_REG_DEVS(ldo10, LDO10),
+ PM8607_REG_DEVS(ldo12, LDO12),
+ PM8607_REG_DEVS(ldo14, LDO14),
+};
+
+static inline int pm8607_read_device(struct pm8607_chip *chip,
+ int reg, int bytes, void *dest)
+{
+ struct i2c_client *i2c = chip->client;
+ unsigned char data;
+ int ret;
+
+ data = (unsigned char)reg;
+ ret = i2c_master_send(i2c, &data, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(i2c, dest, bytes);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static inline int pm8607_write_device(struct pm8607_chip *chip,
+ int reg, int bytes, void *src)
+{
+ struct i2c_client *i2c = chip->client;
+ unsigned char buf[bytes + 1];
+ int ret;
+
+ buf[0] = (unsigned char)reg;
+ memcpy(&buf[1], src, bytes);
+
+ ret = i2c_master_send(i2c, buf, bytes + 1);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+int pm8607_reg_read(struct pm8607_chip *chip, int reg)
+{
+ unsigned char data;
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = chip->read(chip, reg, 1, &data);
+ mutex_unlock(&chip->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return (int)data;
+}
+EXPORT_SYMBOL(pm8607_reg_read);
+
+int pm8607_reg_write(struct pm8607_chip *chip, int reg,
+ unsigned char data)
+{
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = chip->write(chip, reg, 1, &data);
+ mutex_unlock(&chip->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(pm8607_reg_write);
+
+int pm8607_bulk_read(struct pm8607_chip *chip, int reg,
+ int count, unsigned char *buf)
+{
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = chip->read(chip, reg, count, buf);
+ mutex_unlock(&chip->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(pm8607_bulk_read);
+
+int pm8607_bulk_write(struct pm8607_chip *chip, int reg,
+ int count, unsigned char *buf)
+{
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = chip->write(chip, reg, count, buf);
+ mutex_unlock(&chip->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(pm8607_bulk_write);
+
+int pm8607_set_bits(struct pm8607_chip *chip, int reg,
+ unsigned char mask, unsigned char data)
+{
+ unsigned char value;
+ int ret;
+
+ mutex_lock(&chip->io_lock);
+ ret = chip->read(chip, reg, 1, &value);
+ if (ret < 0)
+ goto out;
+ value &= ~mask;
+ value |= data;
+ ret = chip->write(chip, reg, 1, &value);
+out:
+ mutex_unlock(&chip->io_lock);
+ return ret;
+}
+EXPORT_SYMBOL(pm8607_set_bits);
+
+
+static const struct i2c_device_id pm8607_id_table[] = {
+ { "88PM8607", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pm8607_id_table);
+
+
+static int __devinit pm8607_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pm8607_platform_data *pdata = client->dev.platform_data;
+ struct pm8607_chip *chip;
+ int i, count;
+ int ret;
+
+ chip = kzalloc(sizeof(struct pm8607_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->client = client;
+ chip->dev = &client->dev;
+ chip->read = pm8607_read_device;
+ chip->write = pm8607_write_device;
+ i2c_set_clientdata(client, chip);
+
+ mutex_init(&chip->io_lock);
+ dev_set_drvdata(chip->dev, chip);
+
+ ret = pm8607_reg_read(chip, PM8607_CHIP_ID);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+ goto out;
+ }
+ if ((ret & CHIP_ID_MASK) == CHIP_ID)
+ dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
+ ret);
+ else {
+ dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
+ "Chip ID: %02x\n", ret);
+ goto out;
+ }
+ chip->chip_id = ret;
+
+ ret = pm8607_reg_read(chip, PM8607_BUCK3);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
+ goto out;
+ }
+ if (ret & PM8607_BUCK3_DOUBLE)
+ chip->buck3_double = 1;
+
+ ret = pm8607_reg_read(chip, PM8607_MISC1);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
+ goto out;
+ }
+ if (pdata->i2c_port == PI2C_PORT)
+ ret |= PM8607_MISC1_PI2C;
+ else
+ ret &= ~PM8607_MISC1_PI2C;
+ ret = pm8607_reg_write(chip, PM8607_MISC1, ret);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to write MISC1 register: %d\n", ret);
+ goto out;
+ }
+
+
+ count = ARRAY_SIZE(pm8607_devs);
+ for (i = 0; i < count; i++) {
+ ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i],
+ 1, NULL, 0);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to add subdevs\n");
+ goto out;
+ }
+ }
+
+ return 0;
+
+out:
+ i2c_set_clientdata(client, NULL);
+ kfree(chip);
+ return ret;
+}
+
+static int __devexit pm8607_remove(struct i2c_client *client)
+{
+ struct pm8607_chip *chip = i2c_get_clientdata(client);
+
+ mfd_remove_devices(chip->dev);
+ kfree(chip);
+ return 0;
+}
+
+static struct i2c_driver pm8607_driver = {
+ .driver = {
+ .name = "88PM8607",
+ .owner = THIS_MODULE,
+ },
+ .probe = pm8607_probe,
+ .remove = __devexit_p(pm8607_remove),
+ .id_table = pm8607_id_table,
+};
+
+static int __init pm8607_init(void)
+{
+ int ret;
+ ret = i2c_add_driver(&pm8607_driver);
+ if (ret != 0)
+ pr_err("Failed to register 88PM8607 I2C driver: %d\n", ret);
+ return ret;
+}
+subsys_initcall(pm8607_init);
+
+static void __exit pm8607_exit(void)
+{
+ i2c_del_driver(&pm8607_driver);
+}
+module_exit(pm8607_exit);
+
+MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM8607");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a296e717e86e..87829789243e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -103,10 +103,10 @@ config MENELAUS
cell phones and PDAs.
config TWL4030_CORE
- bool "Texas Instruments TWL4030/TPS659x0 Support"
+ bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y && GENERIC_HARDIRQS
help
- Say yes here if you have TWL4030 family chip on your board.
+ Say yes here if you have TWL4030 / TWL6030 family chip on your board.
This core driver provides register access and IRQ handling
facilities, and registers devices for the various functions
so that function-specific drivers can bind to them.
@@ -174,6 +174,16 @@ config PMIC_DA903X
individual components like LCD backlight, voltage regulators,
LEDs and battery-charger under the corresponding menus.
+config PMIC_ADP5520
+ bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
+ depends on I2C=y
+ help
+ Say yes here to add support for Analog Devices AD5520 and ADP5501,
+ Multifunction Power Management IC. This includes
+ the I2C driver and the core APIs _only_, you have to select
+ individual components like LCD backlight, LEDs, GPIOs and Kepad
+ under the corresponding menus.
+
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
@@ -185,12 +195,12 @@ config MFD_WM8400
the functionality of the device.
config MFD_WM831X
- tristate "Support Wolfson Microelectronics WM831x PMICs"
+ bool "Support Wolfson Microelectronics WM831x/2x PMICs"
select MFD_CORE
- depends on I2C
+ depends on I2C=y
help
- Support for the Wolfson Microelecronics WM831x PMICs. This
- driver provides common support for accessing the device,
+ Support for the Wolfson Microelecronics WM831x and WM832x PMICs.
+ This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
@@ -319,6 +329,25 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
+config MFD_88PM8607
+ bool "Support Marvell 88PM8607"
+ depends on I2C=y
+ select MFD_CORE
+ help
+ This supports for Marvell 88PM8607 Power Management IC. This includes
+ the I2C driver and the core APIs _only_, you have to select
+ individual components like voltage regulators, RTC and
+ battery-charger under the corresponding menus.
+
+config AB4500_CORE
+ tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip"
+ depends on SPI
+ help
+ Select this option to enable access to AB4500 power management
+ chip. This connects to U8500 on the SSP/SPI bus and exports
+ read/write functions for the devices to get access to this chip.
+ This chip embeds various other multimedia funtionalities as well.
+
endmenu
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 11350c1d9301..ca2f2c4ff05e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -19,13 +19,14 @@ obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
obj-$(CONFIG_MFD_WM831X) += wm831x.o
wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
+wm8350-objs += wm8350-irq.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_MENELAUS) += menelaus.o
-obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.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
@@ -52,3 +53,6 @@ obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
+obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
+obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
+obj-$(CONFIG_PMIC_ADP5520) += adp5520.o \ No newline at end of file
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 613481028272..fd42a80e7bf9 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -900,9 +900,6 @@ static int __init ab3100_probe(struct i2c_client *client,
goto exit_no_testreg_client;
}
- strlcpy(ab3100->testreg_client->name, id->name,
- sizeof(ab3100->testreg_client->name));
-
err = ab3100_setup(ab3100);
if (err)
goto exit_no_setup;
diff --git a/drivers/mfd/ab4500-core.c b/drivers/mfd/ab4500-core.c
new file mode 100644
index 000000000000..1c44c19e073a
--- /dev/null
+++ b/drivers/mfd/ab4500-core.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2009 ST-Ericsson
+ *
+ * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.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.
+ *
+ * AB4500 is a companion power management chip used with U8500.
+ * On this platform, this is interfaced with SSP0 controller
+ * which is a ARM primecell pl022.
+ *
+ * At the moment the module just exports read/write features.
+ * Interrupt management to be added - TODO.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/ab4500.h>
+
+/* just required if probe fails, we need to
+ * unregister the device
+ */
+static struct spi_driver ab4500_driver;
+
+/*
+ * This funtion writes to any AB4500 registers using
+ * SPI protocol & before it writes it packs the data
+ * in the below 24 bit frame format
+ *
+ * *|------------------------------------|
+ * *| 23|22...18|17.......10|9|8|7......0|
+ * *| r/w bank adr data |
+ * * ------------------------------------
+ *
+ * This function shouldn't be called from interrupt
+ * context
+ */
+int ab4500_write(struct ab4500 *ab4500, unsigned char block,
+ unsigned long addr, unsigned char data)
+{
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ int err;
+ unsigned long spi_data =
+ block << 18 | addr << 10 | data;
+
+ mutex_lock(&ab4500->lock);
+ ab4500->tx_buf[0] = spi_data;
+ ab4500->rx_buf[0] = 0;
+
+ xfer.tx_buf = ab4500->tx_buf;
+ xfer.rx_buf = NULL;
+ xfer.len = sizeof(unsigned long);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ err = spi_sync(ab4500->spi, &msg);
+ mutex_unlock(&ab4500->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(ab4500_write);
+
+int ab4500_read(struct ab4500 *ab4500, unsigned char block,
+ unsigned long addr)
+{
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ unsigned long spi_data =
+ 1 << 23 | block << 18 | addr << 10;
+
+ mutex_lock(&ab4500->lock);
+ ab4500->tx_buf[0] = spi_data;
+ ab4500->rx_buf[0] = 0;
+
+ xfer.tx_buf = ab4500->tx_buf;
+ xfer.rx_buf = ab4500->rx_buf;
+ xfer.len = sizeof(unsigned long);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ spi_sync(ab4500->spi, &msg);
+ mutex_unlock(&ab4500->lock);
+
+ return ab4500->rx_buf[0];
+}
+EXPORT_SYMBOL(ab4500_read);
+
+/* ref: ab3100 core */
+#define AB4500_DEVICE(devname, devid) \
+static struct platform_device ab4500_##devname##_device = { \
+ .name = devid, \
+ .id = -1, \
+}
+
+/* list of childern devices of ab4500 - all are
+ * not populated here - TODO
+ */
+AB4500_DEVICE(charger, "ab4500-charger");
+AB4500_DEVICE(audio, "ab4500-audio");
+AB4500_DEVICE(usb, "ab4500-usb");
+AB4500_DEVICE(tvout, "ab4500-tvout");
+AB4500_DEVICE(sim, "ab4500-sim");
+AB4500_DEVICE(gpadc, "ab4500-gpadc");
+AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
+AB4500_DEVICE(misc, "ab4500-misc");
+
+static struct platform_device *ab4500_platform_devs[] = {
+ &ab4500_charger_device,
+ &ab4500_audio_device,
+ &ab4500_usb_device,
+ &ab4500_tvout_device,
+ &ab4500_sim_device,
+ &ab4500_gpadc_device,
+ &ab4500_clkmgt_device,
+ &ab4500_misc_device,
+};
+
+static int __init ab4500_probe(struct spi_device *spi)
+{
+ struct ab4500 *ab4500;
+ unsigned char revision;
+ int err = 0;
+ int i;
+
+ ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
+ if (!ab4500) {
+ dev_err(&spi->dev, "could not allocate AB4500\n");
+ err = -ENOMEM;
+ goto not_detect;
+ }
+
+ ab4500->spi = spi;
+ spi_set_drvdata(spi, ab4500);
+
+ mutex_init(&ab4500->lock);
+
+ /* read the revision register */
+ revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
+
+ /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
+ if (revision == 0x0 || revision == 0x10)
+ dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
+ ab4500_driver.driver.name, revision);
+ else {
+ dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
+ goto not_detect;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) {
+ ab4500_platform_devs[i]->dev.parent =
+ &spi->dev;
+ platform_set_drvdata(ab4500_platform_devs[i], ab4500);
+ }
+
+ /* register the ab4500 platform devices */
+ platform_add_devices(ab4500_platform_devs,
+ ARRAY_SIZE(ab4500_platform_devs));
+
+ return err;
+
+ not_detect:
+ spi_unregister_driver(&ab4500_driver);
+ kfree(ab4500);
+ return err;
+}
+
+static int __devexit ab4500_remove(struct spi_device *spi)
+{
+ struct ab4500 *ab4500 =
+ spi_get_drvdata(spi);
+
+ kfree(ab4500);
+
+ return 0;
+}
+
+static struct spi_driver ab4500_driver = {
+ .driver = {
+ .name = "ab4500",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab4500_probe,
+ .remove = __devexit_p(ab4500_remove)
+};
+
+static int __devinit ab4500_init(void)
+{
+ return spi_register_driver(&ab4500_driver);
+}
+
+static void __exit ab4500_exit(void)
+{
+ spi_unregister_driver(&ab4500_driver);
+}
+
+subsys_initcall(ab4500_init);
+module_exit(ab4500_exit);
+
+MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
+MODULE_DESCRIPTION("AB4500 core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
new file mode 100644
index 000000000000..b26644772d02
--- /dev/null
+++ b/drivers/mfd/adp5520.c
@@ -0,0 +1,379 @@
+/*
+ * Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs
+ * LCD Backlight: drivers/video/backlight/adp5520_bl
+ * LEDs : drivers/led/leds-adp5520
+ * GPIO : drivers/gpio/adp5520-gpio (ADP5520 only)
+ * Keys : drivers/input/keyboard/adp5520-keys (ADP5520 only)
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Derived from da903x:
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/adp5520.h>
+
+struct adp5520_chip {
+ struct i2c_client *client;
+ struct device *dev;
+ struct mutex lock;
+ struct blocking_notifier_head notifier_list;
+ int irq;
+ unsigned long id;
+};
+
+static int __adp5520_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 int __adp5520_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 int __adp5520_ack_bits(struct i2c_client *client, int reg,
+ uint8_t bit_mask)
+{
+ struct adp5520_chip *chip = i2c_get_clientdata(client);
+ uint8_t reg_val;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = __adp5520_read(client, reg, &reg_val);
+
+ if (!ret) {
+ reg_val |= bit_mask;
+ ret = __adp5520_write(client, reg, reg_val);
+ }
+
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+int adp5520_write(struct device *dev, int reg, uint8_t val)
+{
+ return __adp5520_write(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(adp5520_write);
+
+int adp5520_read(struct device *dev, int reg, uint8_t *val)
+{
+ return __adp5520_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(adp5520_read);
+
+int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = __adp5520_read(chip->client, reg, &reg_val);
+
+ if (!ret && ((reg_val & bit_mask) == 0)) {
+ reg_val |= bit_mask;
+ ret = __adp5520_write(chip->client, reg, reg_val);
+ }
+
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(adp5520_set_bits);
+
+int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = __adp5520_read(chip->client, reg, &reg_val);
+
+ if (!ret && (reg_val & bit_mask)) {
+ reg_val &= ~bit_mask;
+ ret = __adp5520_write(chip->client, reg, reg_val);
+ }
+
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(adp5520_clr_bits);
+
+int adp5520_register_notifier(struct device *dev, struct notifier_block *nb,
+ unsigned int events)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(dev);
+
+ if (chip->irq) {
+ adp5520_set_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
+ events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
+ ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
+
+ return blocking_notifier_chain_register(&chip->notifier_list,
+ nb);
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(adp5520_register_notifier);
+
+int adp5520_unregister_notifier(struct device *dev, struct notifier_block *nb,
+ unsigned int events)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(dev);
+
+ adp5520_clr_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
+ events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
+ ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
+
+ return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(adp5520_unregister_notifier);
+
+static irqreturn_t adp5520_irq_thread(int irq, void *data)
+{
+ struct adp5520_chip *chip = data;
+ unsigned int events;
+ uint8_t reg_val;
+ int ret;
+
+ ret = __adp5520_read(chip->client, ADP5520_MODE_STATUS, &reg_val);
+ if (ret)
+ goto out;
+
+ events = reg_val & (ADP5520_OVP_INT | ADP5520_CMPR_INT |
+ ADP5520_GPI_INT | ADP5520_KR_INT | ADP5520_KP_INT);
+
+ blocking_notifier_call_chain(&chip->notifier_list, events, NULL);
+ /* ACK, Sticky bits are W1C */
+ __adp5520_ack_bits(chip->client, ADP5520_MODE_STATUS, events);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
+static int adp5520_remove_subdevs(struct adp5520_chip *chip)
+{
+ return device_for_each_child(chip->dev, NULL, __remove_subdev);
+}
+
+static int __devinit adp5520_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adp5520_platform_data *pdata = client->dev.platform_data;
+ struct platform_device *pdev;
+ struct adp5520_chip *chip;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "SMBUS Word Data not Supported\n");
+ return -EIO;
+ }
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ chip->dev = &client->dev;
+ chip->irq = client->irq;
+ chip->id = id->driver_data;
+ mutex_init(&chip->lock);
+
+ if (chip->irq) {
+ BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
+
+ ret = request_threaded_irq(chip->irq, NULL, adp5520_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "adp5520", chip);
+ if (ret) {
+ dev_err(&client->dev, "failed to request irq %d\n",
+ chip->irq);
+ goto out_free_chip;
+ }
+ }
+
+ ret = adp5520_write(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
+ if (ret) {
+ dev_err(&client->dev, "failed to write\n");
+ goto out_free_irq;
+ }
+
+ if (pdata->keys) {
+ pdev = platform_device_register_data(chip->dev, "adp5520-keys",
+ chip->id, pdata->keys, sizeof(*pdata->keys));
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ goto out_remove_subdevs;
+ }
+ }
+
+ if (pdata->gpio) {
+ pdev = platform_device_register_data(chip->dev, "adp5520-gpio",
+ chip->id, pdata->gpio, sizeof(*pdata->gpio));
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ goto out_remove_subdevs;
+ }
+ }
+
+ if (pdata->leds) {
+ pdev = platform_device_register_data(chip->dev, "adp5520-led",
+ chip->id, pdata->leds, sizeof(*pdata->leds));
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ goto out_remove_subdevs;
+ }
+ }
+
+ if (pdata->backlight) {
+ pdev = platform_device_register_data(chip->dev,
+ "adp5520-backlight",
+ chip->id,
+ pdata->backlight,
+ sizeof(*pdata->backlight));
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ goto out_remove_subdevs;
+ }
+ }
+
+ return 0;
+
+out_remove_subdevs:
+ adp5520_remove_subdevs(chip);
+
+out_free_irq:
+ if (chip->irq)
+ free_irq(chip->irq, chip);
+
+out_free_chip:
+ i2c_set_clientdata(client, NULL);
+ kfree(chip);
+
+ return ret;
+}
+
+static int __devexit adp5520_remove(struct i2c_client *client)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
+
+ if (chip->irq)
+ free_irq(chip->irq, chip);
+
+ adp5520_remove_subdevs(chip);
+ adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
+ i2c_set_clientdata(client, NULL);
+ kfree(chip);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int adp5520_suspend(struct i2c_client *client,
+ pm_message_t state)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
+
+ adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
+ return 0;
+}
+
+static int adp5520_resume(struct i2c_client *client)
+{
+ struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
+
+ adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
+ return 0;
+}
+#else
+#define adp5520_suspend NULL
+#define adp5520_resume NULL
+#endif
+
+static const struct i2c_device_id adp5520_id[] = {
+ { "pmic-adp5520", ID_ADP5520 },
+ { "pmic-adp5501", ID_ADP5501 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp5520_id);
+
+static struct i2c_driver adp5520_driver = {
+ .driver = {
+ .name = "adp5520",
+ .owner = THIS_MODULE,
+ },
+ .probe = adp5520_probe,
+ .remove = __devexit_p(adp5520_remove),
+ .suspend = adp5520_suspend,
+ .resume = adp5520_resume,
+ .id_table = adp5520_id,
+};
+
+static int __init adp5520_init(void)
+{
+ return i2c_add_driver(&adp5520_driver);
+}
+module_init(adp5520_init);
+
+static void __exit adp5520_exit(void)
+{
+ i2c_del_driver(&adp5520_driver);
+}
+module_exit(adp5520_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 63a2a6632106..e22128c3e9a8 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -908,7 +908,7 @@ static int __init asic3_probe(struct platform_device *pdev)
return ret;
}
-static int asic3_remove(struct platform_device *pdev)
+static int __devexit asic3_remove(struct platform_device *pdev)
{
int ret;
struct asic3 *asic = platform_get_drvdata(pdev);
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index 876288917976..df405af968fa 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -387,7 +387,6 @@ static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
pdev = platform_device_alloc(subdev->name, subdev->id);
pdev->dev.parent = &pcap->spi->dev;
pdev->dev.platform_data = subdev->platform_data;
- platform_set_drvdata(pdev, pcap);
return platform_device_add(pdev);
}
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index e354d2912ef1..a1ade2324ea9 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -1,286 +1,549 @@
/*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This code is in parts based on wm8350-core.c and pcf50633-core.c
- *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
*
- * 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.
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
*
- * 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 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/mfd/mc13783-private.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/mc13783.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/core.h>
-#include <linux/spi/spi.h>
-#include <linux/uaccess.h>
-#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13783-private.h>
+
+#define MC13783_IRQSTAT0 0
+#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
+#define MC13783_IRQSTAT0_ADCBISDONEI (1 << 1)
+#define MC13783_IRQSTAT0_TSI (1 << 2)
+#define MC13783_IRQSTAT0_WHIGHI (1 << 3)
+#define MC13783_IRQSTAT0_WLOWI (1 << 4)
+#define MC13783_IRQSTAT0_CHGDETI (1 << 6)
+#define MC13783_IRQSTAT0_CHGOVI (1 << 7)
+#define MC13783_IRQSTAT0_CHGREVI (1 << 8)
+#define MC13783_IRQSTAT0_CHGSHORTI (1 << 9)
+#define MC13783_IRQSTAT0_CCCVI (1 << 10)
+#define MC13783_IRQSTAT0_CHGCURRI (1 << 11)
+#define MC13783_IRQSTAT0_BPONI (1 << 12)
+#define MC13783_IRQSTAT0_LOBATLI (1 << 13)
+#define MC13783_IRQSTAT0_LOBATHI (1 << 14)
+#define MC13783_IRQSTAT0_UDPI (1 << 15)
+#define MC13783_IRQSTAT0_USBI (1 << 16)
+#define MC13783_IRQSTAT0_IDI (1 << 19)
+#define MC13783_IRQSTAT0_SE1I (1 << 21)
+#define MC13783_IRQSTAT0_CKDETI (1 << 22)
+#define MC13783_IRQSTAT0_UDMI (1 << 23)
+
+#define MC13783_IRQMASK0 1
+#define MC13783_IRQMASK0_ADCDONEM MC13783_IRQSTAT0_ADCDONEI
+#define MC13783_IRQMASK0_ADCBISDONEM MC13783_IRQSTAT0_ADCBISDONEI
+#define MC13783_IRQMASK0_TSM MC13783_IRQSTAT0_TSI
+#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
+#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
+#define MC13783_IRQMASK0_CHGDETM MC13783_IRQSTAT0_CHGDETI
+#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
+#define MC13783_IRQMASK0_CHGREVM MC13783_IRQSTAT0_CHGREVI
+#define MC13783_IRQMASK0_CHGSHORTM MC13783_IRQSTAT0_CHGSHORTI
+#define MC13783_IRQMASK0_CCCVM MC13783_IRQSTAT0_CCCVI
+#define MC13783_IRQMASK0_CHGCURRM MC13783_IRQSTAT0_CHGCURRI
+#define MC13783_IRQMASK0_BPONM MC13783_IRQSTAT0_BPONI
+#define MC13783_IRQMASK0_LOBATLM MC13783_IRQSTAT0_LOBATLI
+#define MC13783_IRQMASK0_LOBATHM MC13783_IRQSTAT0_LOBATHI
+#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
+#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
+#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
+#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
+#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
+#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
+
+#define MC13783_IRQSTAT1 3
+#define MC13783_IRQSTAT1_1HZI (1 << 0)
+#define MC13783_IRQSTAT1_TODAI (1 << 1)
+#define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
+#define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
+#define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
+#define MC13783_IRQSTAT1_SYSRSTI (1 << 6)
+#define MC13783_IRQSTAT1_RTCRSTI (1 << 7)
+#define MC13783_IRQSTAT1_PCI (1 << 8)
+#define MC13783_IRQSTAT1_WARMI (1 << 9)
+#define MC13783_IRQSTAT1_MEMHLDI (1 << 10)
+#define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
+#define MC13783_IRQSTAT1_THWARNLI (1 << 12)
+#define MC13783_IRQSTAT1_THWARNHI (1 << 13)
+#define MC13783_IRQSTAT1_CLKI (1 << 14)
+#define MC13783_IRQSTAT1_SEMAFI (1 << 15)
+#define MC13783_IRQSTAT1_MC2BI (1 << 17)
+#define MC13783_IRQSTAT1_HSDETI (1 << 18)
+#define MC13783_IRQSTAT1_HSLI (1 << 19)
+#define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
+#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
+
+#define MC13783_IRQMASK1 4
+#define MC13783_IRQMASK1_1HZM MC13783_IRQSTAT1_1HZI
+#define MC13783_IRQMASK1_TODAM MC13783_IRQSTAT1_TODAI
+#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
+#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
+#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
+#define MC13783_IRQMASK1_SYSRSTM MC13783_IRQSTAT1_SYSRSTI
+#define MC13783_IRQMASK1_RTCRSTM MC13783_IRQSTAT1_RTCRSTI
+#define MC13783_IRQMASK1_PCM MC13783_IRQSTAT1_PCI
+#define MC13783_IRQMASK1_WARMM MC13783_IRQSTAT1_WARMI
+#define MC13783_IRQMASK1_MEMHLDM MC13783_IRQSTAT1_MEMHLDI
+#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
+#define MC13783_IRQMASK1_THWARNLM MC13783_IRQSTAT1_THWARNLI
+#define MC13783_IRQMASK1_THWARNHM MC13783_IRQSTAT1_THWARNHI
+#define MC13783_IRQMASK1_CLKM MC13783_IRQSTAT1_CLKI
+#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
+#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
+#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
+#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
+#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
+#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
+
+#define MC13783_ADC1 44
+#define MC13783_ADC1_ADEN (1 << 0)
+#define MC13783_ADC1_RAND (1 << 1)
+#define MC13783_ADC1_ADSEL (1 << 3)
+#define MC13783_ADC1_ASC (1 << 20)
+#define MC13783_ADC1_ADTRIGIGN (1 << 21)
+
+#define MC13783_NUMREGS 0x3f
+
+void mc13783_lock(struct mc13783 *mc13783)
+{
+ if (!mutex_trylock(&mc13783->lock)) {
+ dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
+ __func__, __builtin_return_address(0));
+
+ mutex_lock(&mc13783->lock);
+ }
+ dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+ __func__, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(mc13783_lock);
-#define MC13783_MAX_REG_NUM 0x3f
-#define MC13783_FRAME_MASK 0x00ffffff
-#define MC13783_MAX_REG_NUM 0x3f
-#define MC13783_REG_NUM_SHIFT 0x19
-#define MC13783_WRITE_BIT_SHIFT 31
+void mc13783_unlock(struct mc13783 *mc13783)
+{
+ dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+ __func__, __builtin_return_address(0));
+ mutex_unlock(&mc13783->lock);
+}
+EXPORT_SYMBOL(mc13783_unlock);
-static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+#define MC13783_REGOFFSET_SHIFT 25
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
{
- struct spi_transfer t = {
- .tx_buf = (const void *)buf,
- .rx_buf = buf,
- .len = len,
- .cs_change = 0,
- .delay_usecs = 0,
- };
+ struct spi_transfer t;
struct spi_message m;
+ int ret;
+
+ BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+ if (offset > MC13783_NUMREGS)
+ return -EINVAL;
+
+ *val = offset << MC13783_REGOFFSET_SHIFT;
+
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = val;
+ t.rx_buf = val;
+ t.len = sizeof(u32);
spi_message_init(&m);
spi_message_add_tail(&t, &m);
- if (spi_sync(spi, &m) != 0 || m.status != 0)
- return -EINVAL;
- return len - m.actual_length;
-}
-static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
-{
- unsigned int frame = 0;
- int ret = 0;
+ ret = spi_sync(mc13783->spidev, &m);
- if (reg_num > MC13783_MAX_REG_NUM)
- return -EINVAL;
+ /* error in message.status implies error return from spi_sync */
+ BUG_ON(!ret && m.status);
- frame |= reg_num << MC13783_REG_NUM_SHIFT;
+ if (ret)
+ return ret;
- ret = spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+ *val &= 0xffffff;
- *reg_val = frame & MC13783_FRAME_MASK;
+ dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
- return ret;
+ return 0;
}
+EXPORT_SYMBOL(mc13783_reg_read);
-static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
{
- unsigned int frame = 0;
+ u32 buf;
+ struct spi_transfer t;
+ struct spi_message m;
+ int ret;
+
+ BUG_ON(!mutex_is_locked(&mc13783->lock));
- if (reg_num > MC13783_MAX_REG_NUM)
+ dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+ if (offset > MC13783_NUMREGS || val > 0xffffff)
return -EINVAL;
- frame |= (1 << MC13783_WRITE_BIT_SHIFT);
- frame |= reg_num << MC13783_REG_NUM_SHIFT;
- frame |= reg_val & MC13783_FRAME_MASK;
+ buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
+
+ memset(&t, 0, sizeof(t));
- return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+ t.tx_buf = &buf;
+ t.rx_buf = &buf;
+ t.len = sizeof(u32);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(mc13783->spidev, &m);
+
+ BUG_ON(!ret && m.status);
+
+ if (ret)
+ return ret;
+
+ return 0;
}
+EXPORT_SYMBOL(mc13783_reg_write);
-int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+ u32 mask, u32 val)
{
int ret;
+ u32 valread;
- mutex_lock(&mc13783->io_lock);
- ret = mc13783_read(mc13783, reg_num, reg_val);
- mutex_unlock(&mc13783->io_lock);
+ BUG_ON(val & ~mask);
- return ret;
+ ret = mc13783_reg_read(mc13783, offset, &valread);
+ if (ret)
+ return ret;
+
+ valread = (valread & ~mask) | val;
+
+ return mc13783_reg_write(mc13783, offset, valread);
}
-EXPORT_SYMBOL_GPL(mc13783_reg_read);
+EXPORT_SYMBOL(mc13783_reg_rmw);
-int mc13783_reg_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+int mc13783_mask(struct mc13783 *mc13783, int irq)
{
int ret;
+ unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+ u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+ u32 mask;
- mutex_lock(&mc13783->io_lock);
- ret = mc13783_write(mc13783, reg_num, reg_val);
- mutex_unlock(&mc13783->io_lock);
+ if (irq < 0 || irq >= MC13783_NUM_IRQ)
+ return -EINVAL;
- return ret;
+ ret = mc13783_reg_read(mc13783, offmask, &mask);
+ if (ret)
+ return ret;
+
+ if (mask & irqbit)
+ /* already masked */
+ return 0;
+
+ return mc13783_reg_write(mc13783, offmask, mask | irqbit);
}
-EXPORT_SYMBOL_GPL(mc13783_reg_write);
+EXPORT_SYMBOL(mc13783_mask);
-/**
- * mc13783_set_bits - Bitmask write
- *
- * @mc13783: Pointer to mc13783 control structure
- * @reg: Register to access
- * @mask: Mask of bits to change
- * @val: Value to set for masked bits
- */
-int mc13783_set_bits(struct mc13783 *mc13783, int reg, u32 mask, u32 val)
+int mc13783_unmask(struct mc13783 *mc13783, int irq)
{
- u32 tmp;
int ret;
+ unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+ u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+ u32 mask;
- mutex_lock(&mc13783->io_lock);
+ if (irq < 0 || irq >= MC13783_NUM_IRQ)
+ return -EINVAL;
- ret = mc13783_read(mc13783, reg, &tmp);
- tmp = (tmp & ~mask) | val;
- if (ret == 0)
- ret = mc13783_write(mc13783, reg, tmp);
+ ret = mc13783_reg_read(mc13783, offmask, &mask);
+ if (ret)
+ return ret;
- mutex_unlock(&mc13783->io_lock);
+ if (!(mask & irqbit))
+ /* already unmasked */
+ return 0;
- return ret;
+ return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
}
-EXPORT_SYMBOL_GPL(mc13783_set_bits);
+EXPORT_SYMBOL(mc13783_unmask);
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
- void (*handler) (int, void *), void *data)
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+ irq_handler_t handler, const char *name, void *dev)
{
- if (irq < 0 || irq > MC13783_NUM_IRQ || !handler)
+ BUG_ON(!mutex_is_locked(&mc13783->lock));
+ BUG_ON(!handler);
+
+ if (irq < 0 || irq >= MC13783_NUM_IRQ)
return -EINVAL;
- if (WARN_ON(mc13783->irq_handler[irq].handler))
+ if (mc13783->irqhandler[irq])
return -EBUSY;
- mutex_lock(&mc13783->io_lock);
- mc13783->irq_handler[irq].handler = handler;
- mc13783->irq_handler[irq].data = data;
- mutex_unlock(&mc13783->io_lock);
+ mc13783->irqhandler[irq] = handler;
+ mc13783->irqdata[irq] = dev;
return 0;
}
-EXPORT_SYMBOL_GPL(mc13783_register_irq);
+EXPORT_SYMBOL(mc13783_irq_request_nounmask);
-int mc13783_free_irq(struct mc13783 *mc13783, int irq)
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+ irq_handler_t handler, const char *name, void *dev)
{
- if (irq < 0 || irq > MC13783_NUM_IRQ)
+ int ret;
+
+ ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
+ if (ret)
+ return ret;
+
+ ret = mc13783_unmask(mc13783, irq);
+ if (ret) {
+ mc13783->irqhandler[irq] = NULL;
+ mc13783->irqdata[irq] = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13783_irq_request);
+
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
+{
+ int ret;
+ BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+ if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
+ mc13783->irqdata[irq] != dev)
return -EINVAL;
- mutex_lock(&mc13783->io_lock);
- mc13783->irq_handler[irq].handler = NULL;
- mutex_unlock(&mc13783->io_lock);
+ ret = mc13783_mask(mc13783, irq);
+ if (ret)
+ return ret;
+
+ mc13783->irqhandler[irq] = NULL;
+ mc13783->irqdata[irq] = NULL;
return 0;
}
-EXPORT_SYMBOL_GPL(mc13783_free_irq);
+EXPORT_SYMBOL(mc13783_irq_free);
-static void mc13783_irq_work(struct work_struct *work)
+static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
{
- struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
- int i;
- unsigned int adc_sts;
-
- /* check if the adc has finished any completion */
- mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
- mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
- adc_sts & MC13783_INT_STAT_ADCDONEI);
-
- if (adc_sts & MC13783_INT_STAT_ADCDONEI)
- complete_all(&mc13783->adc_done);
-
- for (i = 0; i < MC13783_NUM_IRQ; i++)
- if (mc13783->irq_handler[i].handler)
- mc13783->irq_handler[i].handler(i,
- mc13783->irq_handler[i].data);
- enable_irq(mc13783->irq);
+ return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
}
-static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
+int mc13783_ackirq(struct mc13783 *mc13783, int irq)
{
- struct mc13783 *mc13783 = dev_id;
+ unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
+ unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
- disable_irq_nosync(irq);
+ BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
- schedule_work(&mc13783->work);
- return IRQ_HANDLED;
+ return mc13783_reg_write(mc13783, offstat, val);
}
+EXPORT_SYMBOL(mc13783_ackirq);
-/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
-static inline void mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+/*
+ * returns: number of handled irqs or negative error
+ * locking: holds mc13783->lock
+ */
+static int mc13783_irq_handle(struct mc13783 *mc13783,
+ unsigned int offstat, unsigned int offmask, int baseirq)
{
- unsigned int reg_adc0, reg_adc1;
+ u32 stat, mask;
+ int ret = mc13783_reg_read(mc13783, offstat, &stat);
+ int num_handled = 0;
+
+ if (ret)
+ return ret;
+
+ ret = mc13783_reg_read(mc13783, offmask, &mask);
+ if (ret)
+ return ret;
+
+ while (stat & ~mask) {
+ int irq = __ffs(stat & ~mask);
+
+ stat &= ~(1 << irq);
+
+ if (likely(mc13783->irqhandler[baseirq + irq])) {
+ irqreturn_t handled;
- reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
- | MC13783_ADC0_TSMOD0;
- reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+ handled = mc13783_irqhandler(mc13783, baseirq + irq);
+ if (handled == IRQ_HANDLED)
+ num_handled++;
+ } else {
+ dev_err(&mc13783->spidev->dev,
+ "BUG: irq %u but no handler\n",
+ baseirq + irq);
- mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
- mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+ mask |= 1 << irq;
+
+ ret = mc13783_reg_write(mc13783, offmask, mask);
+ }
+ }
+
+ return num_handled;
}
+static irqreturn_t mc13783_irq_thread(int irq, void *data)
+{
+ struct mc13783 *mc13783 = data;
+ irqreturn_t ret;
+ int handled = 0;
+
+ mc13783_lock(mc13783);
+
+ ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
+ MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
+ if (ret > 0)
+ handled = 1;
+
+ ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
+ MC13783_IRQMASK1, MC13783_IRQ_1HZ);
+ if (ret > 0)
+ handled = 1;
+
+ mc13783_unlock(mc13783);
+
+ return IRQ_RETVAL(handled);
+}
+
+#define MC13783_ADC1_CHAN0_SHIFT 5
+#define MC13783_ADC1_CHAN1_SHIFT 8
+
+struct mc13783_adcdone_data {
+ struct mc13783 *mc13783;
+ struct completion done;
+};
+
+static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
+{
+ struct mc13783_adcdone_data *adcdone_data = data;
+
+ mc13783_ackirq(adcdone_data->mc13783, irq);
+
+ complete_all(&adcdone_data->done);
+
+ return IRQ_HANDLED;
+}
+
+#define MC13783_ADC_WORKING (1 << 16)
+
int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
unsigned int channel, unsigned int *sample)
{
- unsigned int reg_adc0, reg_adc1;
- int i;
+ u32 adc0, adc1, old_adc0;
+ int i, ret;
+ struct mc13783_adcdone_data adcdone_data = {
+ .mc13783 = mc13783,
+ };
+ init_completion(&adcdone_data.done);
+
+ dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
+
+ mc13783_lock(mc13783);
+
+ if (mc13783->flags & MC13783_ADC_WORKING) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ mc13783->flags |= MC13783_ADC_WORKING;
- mutex_lock(&mc13783->adc_conv_lock);
+ mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
- /* set up auto incrementing anyway to make quick read */
- reg_adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
- /* enable the adc, ignore external triggering and set ASC to trigger
- * conversion */
- reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
- | MC13783_ADC1_ASC;
+ adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+ adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
- /* setup channel number */
if (channel > 7)
- reg_adc1 |= MC13783_ADC1_ADSEL;
+ adc1 |= MC13783_ADC1_ADSEL;
switch (mode) {
case MC13783_ADC_MODE_TS:
- /* enables touch screen reference mode and set touchscreen mode
- * to position mode */
- reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
- | MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
- reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
+ MC13783_ADC0_TSMOD1;
+ adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
break;
+
case MC13783_ADC_MODE_SINGLE_CHAN:
- reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
- reg_adc1 |= MC13783_ADC1_RAND;
+ adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+ adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+ adc1 |= MC13783_ADC1_RAND;
break;
+
case MC13783_ADC_MODE_MULT_CHAN:
- reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+ adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
break;
+
default:
+ mc13783_unlock(mc13783);
return -EINVAL;
}
- mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
- mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+ dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
+ mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
+ mc13783_handler_adcdone, __func__, &adcdone_data);
+ mc13783_ackirq(mc13783, MC13783_IRQ_ADCDONE);
- wait_for_completion_interruptible(&mc13783->adc_done);
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
- for (i = 0; i < 4; i++)
- mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
+ mc13783_unlock(mc13783);
- if (mc13783->ts_active)
- mc13783_adc_set_ts_irq_mode(mc13783);
+ ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
- mutex_unlock(&mc13783->adc_conv_lock);
+ if (!ret)
+ ret = -ETIMEDOUT;
- return 0;
+ mc13783_lock(mc13783);
+
+ mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
+
+ if (ret > 0)
+ for (i = 0; i < 4; ++i) {
+ ret = mc13783_reg_read(mc13783,
+ MC13783_REG_ADC_2, &sample[i]);
+ if (ret)
+ break;
+ }
+
+ if (mode == MC13783_ADC_MODE_TS)
+ /* restore TSMOD */
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
+
+ mc13783->flags &= ~MC13783_ADC_WORKING;
+out:
+ mc13783_unlock(mc13783);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
+ const char *name, void *pdata, size_t pdata_size)
{
- mc13783->ts_active = status;
+ struct mfd_cell cell = {
+ .name = name,
+ .platform_data = pdata,
+ .data_size = pdata_size,
+ };
+
+ return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
+{
+ return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
}
-EXPORT_SYMBOL_GPL(mc13783_adc_set_ts_status);
static int mc13783_check_revision(struct mc13783 *mc13783)
{
u32 rev_id, rev1, rev2, finid, icid;
- mc13783_read(mc13783, MC13783_REG_REVISION, &rev_id);
+ mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
rev1 = (rev_id & 0x018) >> 3;
rev2 = (rev_id & 0x007);
@@ -292,38 +555,24 @@ static int mc13783_check_revision(struct mc13783 *mc13783)
rev1 = 3;
if (rev1 == 0 || icid != 2) {
- dev_err(mc13783->dev, "No MC13783 detected.\n");
+ dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
return -ENODEV;
}
- mc13783->revision = ((rev1 * 10) + rev2);
- dev_info(mc13783->dev, "MC13783 Rev %d.%d FinVer %x detected\n", rev1,
- rev2, finid);
+ dev_info(&mc13783->spidev->dev,
+ "MC13783 Rev %d.%d FinVer %x detected\n",
+ rev1, rev2, finid);
return 0;
}
-/*
- * Register a client device. This is non-fatal since there is no need to
- * fail the entire device init due to a single platform device failing.
- */
-static void mc13783_client_dev_register(struct mc13783 *mc13783,
- const char *name)
-{
- struct mfd_cell cell = {};
-
- cell.name = name;
-
- mfd_add_devices(mc13783->dev, -1, &cell, 1, NULL, 0);
-}
-
-static int __devinit mc13783_probe(struct spi_device *spi)
+static int mc13783_probe(struct spi_device *spi)
{
struct mc13783 *mc13783;
- struct mc13783_platform_data *pdata = spi->dev.platform_data;
+ struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
int ret;
- mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+ mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
if (!mc13783)
return -ENOMEM;
@@ -332,96 +581,104 @@ static int __devinit mc13783_probe(struct spi_device *spi)
spi->bits_per_word = 32;
spi_setup(spi);
- mc13783->spi_device = spi;
- mc13783->dev = &spi->dev;
- mc13783->irq = spi->irq;
+ mc13783->spidev = spi;
+
+ mutex_init(&mc13783->lock);
+ mc13783_lock(mc13783);
+
+ ret = mc13783_check_revision(mc13783);
+ if (ret)
+ goto err_revision;
+
+ /* mask all irqs */
+ ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
+ if (ret)
+ goto err_mask;
- INIT_WORK(&mc13783->work, mc13783_irq_work);
- mutex_init(&mc13783->io_lock);
- mutex_init(&mc13783->adc_conv_lock);
- init_completion(&mc13783->adc_done);
+ ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
+ if (ret)
+ goto err_mask;
+
+ ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
+
+ if (ret) {
+err_mask:
+err_revision:
+ mutex_unlock(&mc13783->lock);
+ dev_set_drvdata(&spi->dev, NULL);
+ kfree(mc13783);
+ return ret;
+ }
+ /* This should go away (BEGIN) */
if (pdata) {
mc13783->flags = pdata->flags;
mc13783->regulators = pdata->regulators;
mc13783->num_regulators = pdata->num_regulators;
}
+ /* This should go away (END) */
- if (mc13783_check_revision(mc13783)) {
- ret = -ENODEV;
- goto err_out;
+ if (pdata->flags & MC13783_USE_ADC)
+ mc13783_add_subdevice(mc13783, "mc13783-adc");
+
+ if (pdata->flags & MC13783_USE_CODEC)
+ mc13783_add_subdevice(mc13783, "mc13783-codec");
+
+ if (pdata->flags & MC13783_USE_REGULATOR) {
+ struct mc13783_regulator_platform_data regulator_pdata = {
+ .num_regulators = pdata->num_regulators,
+ .regulators = pdata->regulators,
+ };
+
+ mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
+ &regulator_pdata, sizeof(regulator_pdata));
}
- /* clear and mask all interrupts */
- mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0, 0x00ffffff);
- mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_0, 0x00ffffff);
- mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_1, 0x00ffffff);
- mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_1, 0x00ffffff);
+ if (pdata->flags & MC13783_USE_RTC)
+ mc13783_add_subdevice(mc13783, "mc13783-rtc");
- /* unmask adcdone interrupts */
- mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
- MC13783_INT_MASK_ADCDONEM, 0);
+ if (pdata->flags & MC13783_USE_TOUCHSCREEN)
+ mc13783_add_subdevice(mc13783, "mc13783-ts");
- ret = request_irq(mc13783->irq, mc13783_interrupt,
- IRQF_DISABLED | IRQF_TRIGGER_HIGH, "mc13783",
- mc13783);
- if (ret)
- goto err_out;
-
- if (mc13783->flags & MC13783_USE_CODEC)
- mc13783_client_dev_register(mc13783, "mc13783-codec");
- if (mc13783->flags & MC13783_USE_ADC)
- mc13783_client_dev_register(mc13783, "mc13783-adc");
- if (mc13783->flags & MC13783_USE_RTC)
- mc13783_client_dev_register(mc13783, "mc13783-rtc");
- if (mc13783->flags & MC13783_USE_REGULATOR)
- mc13783_client_dev_register(mc13783, "mc13783-regulator");
- if (mc13783->flags & MC13783_USE_TOUCHSCREEN)
- mc13783_client_dev_register(mc13783, "mc13783-ts");
+ mc13783_unlock(mc13783);
return 0;
-
-err_out:
- kfree(mc13783);
- return ret;
}
static int __devexit mc13783_remove(struct spi_device *spi)
{
- struct mc13783 *mc13783;
+ struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
- mc13783 = dev_get_drvdata(&spi->dev);
-
- free_irq(mc13783->irq, mc13783);
+ free_irq(mc13783->spidev->irq, mc13783);
mfd_remove_devices(&spi->dev);
return 0;
}
-static struct spi_driver pmic_driver = {
+static struct spi_driver mc13783_driver = {
.driver = {
- .name = "mc13783",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
+ .name = "mc13783",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
},
.probe = mc13783_probe,
.remove = __devexit_p(mc13783_remove),
};
-static int __init pmic_init(void)
+static int __init mc13783_init(void)
{
- return spi_register_driver(&pmic_driver);
+ return spi_register_driver(&mc13783_driver);
}
-subsys_initcall(pmic_init);
+subsys_initcall(mc13783_init);
-static void __exit pmic_exit(void)
+static void __exit mc13783_exit(void)
{
- spi_unregister_driver(&pmic_driver);
+ spi_unregister_driver(&mc13783_driver);
}
-module_exit(pmic_exit);
-
-MODULE_DESCRIPTION("Core/Protocol driver for Freescale MC13783 PMIC");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
index 3d31e97d6a45..6d2e8466df1d 100644
--- a/drivers/mfd/pcf50633-adc.c
+++ b/drivers/mfd/pcf50633-adc.c
@@ -209,17 +209,16 @@ static void pcf50633_adc_irq(int irq, void *data)
static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
{
- struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
struct pcf50633_adc *adc;
adc = kzalloc(sizeof(*adc), GFP_KERNEL);
if (!adc)
return -ENOMEM;
- adc->pcf = pdata->pcf;
+ adc->pcf = dev_to_pcf50633(pdev->dev.parent);
platform_set_drvdata(pdev, adc);
- pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ADCRDY,
+ pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
pcf50633_adc_irq, adc);
mutex_init(&adc->queue_mutex);
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index d26d7747175e..03dcc9200707 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -290,7 +290,7 @@ out:
int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
{
- dev_info(pcf->dev, "Masking IRQ %d\n", irq);
+ dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
return __pcf50633_irq_mask_set(pcf, irq, 1);
}
@@ -298,7 +298,7 @@ EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
{
- dev_info(pcf->dev, "Unmasking IRQ %d\n", irq);
+ dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
return __pcf50633_irq_mask_set(pcf, irq, 0);
}
@@ -345,6 +345,9 @@ static void pcf50633_irq_worker(struct work_struct *work)
goto out;
}
+ /* defeat 8s death from lowsys on A5 */
+ pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04);
+
/* We immediately read the usb and adapter status. We thus make sure
* only of USBINS/USBREM IRQ handlers are called */
if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
@@ -453,7 +456,6 @@ static void
pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
struct platform_device **pdev)
{
- struct pcf50633_subdev_pdata *subdev_pdata;
int ret;
*pdev = platform_device_alloc(name, -1);
@@ -462,15 +464,6 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
return;
}
- subdev_pdata = kmalloc(sizeof(*subdev_pdata), GFP_KERNEL);
- if (!subdev_pdata) {
- dev_err(pcf->dev, "Error allocating subdev pdata\n");
- platform_device_put(*pdev);
- }
-
- subdev_pdata->pcf = pcf;
- platform_device_add_data(*pdev, subdev_pdata, sizeof(*subdev_pdata));
-
(*pdev)->dev.parent = pcf->dev;
ret = platform_device_add(*pdev);
@@ -482,13 +475,13 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
}
#ifdef CONFIG_PM
-static int pcf50633_suspend(struct device *dev, pm_message_t state)
+static int pcf50633_suspend(struct i2c_client *client, pm_message_t state)
{
struct pcf50633 *pcf;
int ret = 0, i;
u8 res[5];
- pcf = dev_get_drvdata(dev);
+ pcf = i2c_get_clientdata(client);
/* Make sure our interrupt handlers are not called
* henceforth */
@@ -523,12 +516,12 @@ out:
return ret;
}
-static int pcf50633_resume(struct device *dev)
+static int pcf50633_resume(struct i2c_client *client)
{
struct pcf50633 *pcf;
int ret;
- pcf = dev_get_drvdata(dev);
+ pcf = i2c_get_clientdata(client);
/* Write the saved mask registers */
ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
@@ -560,9 +553,14 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
{
struct pcf50633 *pcf;
struct pcf50633_platform_data *pdata = client->dev.platform_data;
- int i, ret = 0;
+ int i, ret;
int version, variant;
+ if (!client->irq) {
+ dev_err(&client->dev, "Missing IRQ\n");
+ return -ENOENT;
+ }
+
pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
if (!pcf)
return -ENOMEM;
@@ -577,6 +575,12 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pcf->irq = client->irq;
pcf->work_queue = create_singlethread_workqueue("pcf50633");
+ if (!pcf->work_queue) {
+ dev_err(&client->dev, "Failed to alloc workqueue\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
INIT_WORK(&pcf->irq_work, pcf50633_irq_worker);
version = pcf50633_reg_read(pcf, 0);
@@ -584,7 +588,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
if (version < 0 || variant < 0) {
dev_err(pcf->dev, "Unable to probe pcf50633\n");
ret = -ENODEV;
- goto err;
+ goto err_destroy_workqueue;
}
dev_info(pcf->dev, "Probed device version %d variant %d\n",
@@ -598,6 +602,14 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
+ ret = request_irq(client->irq, pcf50633_irq,
+ IRQF_TRIGGER_LOW, "pcf50633", pcf);
+
+ if (ret) {
+ dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
+ goto err_destroy_workqueue;
+ }
+
/* Create sub devices */
pcf50633_client_dev_register(pcf, "pcf50633-input",
&pcf->input_pdev);
@@ -613,31 +625,18 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
pdev = platform_device_alloc("pcf50633-regltr", i);
if (!pdev) {
- dev_err(pcf->dev, "Cannot create regulator\n");
+ dev_err(pcf->dev, "Cannot create regulator %d\n", i);
continue;
}
pdev->dev.parent = pcf->dev;
- pdev->dev.platform_data = &pdata->reg_init_data[i];
- dev_set_drvdata(&pdev->dev, pcf);
+ platform_device_add_data(pdev, &pdata->reg_init_data[i],
+ sizeof(pdata->reg_init_data[i]));
pcf->regulator_pdev[i] = pdev;
platform_device_add(pdev);
}
- if (client->irq) {
- ret = request_irq(client->irq, pcf50633_irq,
- IRQF_TRIGGER_LOW, "pcf50633", pcf);
-
- if (ret) {
- dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
- goto err;
- }
- } else {
- dev_err(pcf->dev, "No IRQ configured\n");
- goto err;
- }
-
if (enable_irq_wake(client->irq) < 0)
dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
"in this hardware revision", client->irq);
@@ -651,9 +650,12 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
return 0;
-err:
+err_destroy_workqueue:
destroy_workqueue(pcf->work_queue);
+err_free:
+ i2c_set_clientdata(client, NULL);
kfree(pcf);
+
return ret;
}
@@ -686,12 +688,12 @@ static struct i2c_device_id pcf50633_id_table[] = {
static struct i2c_driver pcf50633_driver = {
.driver = {
.name = "pcf50633",
- .suspend = pcf50633_suspend,
- .resume = pcf50633_resume,
},
.id_table = pcf50633_id_table,
.probe = pcf50633_probe,
.remove = __devexit_p(pcf50633_remove),
+ .suspend = pcf50633_suspend,
+ .resume = pcf50633_resume,
};
static int __init pcf50633_init(void)
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index acf8b9d5f575..e5955306c2fa 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -637,7 +637,7 @@ static int tps65010_probe(struct i2c_client *client,
tps, DEBUG_FOPS);
/* optionally register GPIOs */
- if (board && board->base > 0) {
+ if (board && board->base != 0) {
tps->outmask = board->outmask;
tps->chip.label = client->name;
@@ -964,6 +964,34 @@ int tps65010_config_vregs1(unsigned value)
}
EXPORT_SYMBOL(tps65010_config_vregs1);
+int tps65010_config_vdcdc2(unsigned value)
+{
+ struct i2c_client *c;
+ int status;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ c = the_tps->client;
+ mutex_lock(&the_tps->lock);
+
+ pr_debug("%s: vdcdc2 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(c, TPS_VDCDC2));
+
+ status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value);
+
+ if (status != 0)
+ printk(KERN_ERR "%s: Failed to write vdcdc2 register\n",
+ DRIVER_NAME);
+ else
+ pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(c, TPS_VDCDC2));
+
+ mutex_unlock(&the_tps->lock);
+ return status;
+}
+EXPORT_SYMBOL(tps65010_config_vdcdc2);
+
/*-------------------------------------------------------------------------*/
/* tps65013_set_low_pwr parameter:
* mode: ON or OFF
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl-core.c
index 40449cdf09db..2a7606534196 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl-core.c
@@ -1,5 +1,6 @@
/*
- * twl4030_core.c - driver for TWL4030/TPS659x0 PM and audio CODEC devices
+ * twl_core.c - driver for TWL4030/TWL5030/TWL60X0/TPS659x0 PM
+ * and audio CODEC devices
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
@@ -36,7 +37,7 @@
#include <linux/regulator/machine.h>
#include <linux/i2c.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
#include <plat/cpu.h>
@@ -55,7 +56,7 @@
* (and associated registers).
*/
-#define DRIVER_NAME "twl4030"
+#define DRIVER_NAME "twl"
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
@@ -125,7 +126,7 @@
/* Last - for index max*/
#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG
-#define TWL4030_NUM_SLAVES 4
+#define TWL_NUM_SLAVES 4
#if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
|| defined(CONFIG_INPUT_TWL4030_PWBUTTON_MODULE)
@@ -134,6 +135,13 @@
#define twl_has_pwrbutton() false
#endif
+#define SUB_CHIP_ID0 0
+#define SUB_CHIP_ID1 1
+#define SUB_CHIP_ID2 2
+#define SUB_CHIP_ID3 3
+
+#define TWL_MODULE_LAST TWL4030_MODULE_LAST
+
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
@@ -158,6 +166,10 @@
#define TWL4030_BASEADD_PWMB 0x00F1
#define TWL4030_BASEADD_KEYPAD 0x00D2
+#define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */
+#define TWL5031_BASEADD_INTERRUPTS 0x00B9 /* Different than TWL4030's
+ one */
+
/* subchip/slave 3 - POWER ID */
#define TWL4030_BASEADD_BACKUP 0x0014
#define TWL4030_BASEADD_INT 0x002E
@@ -169,6 +181,30 @@
/* Triton Core internal information (END) */
+/* subchip/slave 0 0x48 - POWER */
+#define TWL6030_BASEADD_RTC 0x0000
+#define TWL6030_BASEADD_MEM 0x0017
+#define TWL6030_BASEADD_PM_MASTER 0x001F
+#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
+#define TWL6030_BASEADD_PM_MISC 0x00E2
+#define TWL6030_BASEADD_PM_PUPD 0x00F0
+
+/* subchip/slave 1 0x49 - FEATURE */
+#define TWL6030_BASEADD_USB 0x0000
+#define TWL6030_BASEADD_GPADC_CTRL 0x002E
+#define TWL6030_BASEADD_AUX 0x0090
+#define TWL6030_BASEADD_PWM 0x00BA
+#define TWL6030_BASEADD_GASGAUGE 0x00C0
+#define TWL6030_BASEADD_PIH 0x00D0
+#define TWL6030_BASEADD_CHARGER 0x00E0
+
+/* subchip/slave 2 0x4A - DFT */
+#define TWL6030_BASEADD_DIEID 0x00C0
+
+/* subchip/slave 3 0x4B - AUDIO */
+#define TWL6030_BASEADD_AUDIO 0x0000
+#define TWL6030_BASEADD_RSV 0x0000
+
/* Few power values */
#define R_CFG_BOOT 0x05
#define R_PROTECT_KEY 0x0E
@@ -183,19 +219,29 @@
#define HFCLK_FREQ_26_MHZ (2 << 0)
#define HFCLK_FREQ_38p4_MHZ (3 << 0)
#define HIGH_PERF_SQ (1 << 3)
+#define CK32K_LOWPWR_EN (1 << 7)
/* chip-specific feature flags, for i2c_device_id.driver_data */
#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */
#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */
+#define TWL5031 BIT(2) /* twl5031 has different registers */
+#define TWL6030_CLASS BIT(3) /* TWL6030 class */
/*----------------------------------------------------------------------*/
/* is driver active, bound to a chip? */
static bool inuse;
-/* Structure for each TWL4030 Slave */
-struct twl4030_client {
+static unsigned int twl_id;
+unsigned int twl_rev(void)
+{
+ return twl_id;
+}
+EXPORT_SYMBOL(twl_rev);
+
+/* Structure for each TWL4030/TWL6030 Slave */
+struct twl_client {
struct i2c_client *client;
u8 address;
@@ -206,19 +252,20 @@ struct twl4030_client {
struct mutex xfer_lock;
};
-static struct twl4030_client twl4030_modules[TWL4030_NUM_SLAVES];
+static struct twl_client twl_modules[TWL_NUM_SLAVES];
/* mapping the module id to slave id and base address */
-struct twl4030mapping {
+struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
+struct twl_mapping *twl_map;
-static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
+static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
/*
* NOTE: don't change this table without updating the
- * <linux/i2c/twl4030.h> defines for TWL4030_MODULE_*
+ * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
@@ -240,6 +287,8 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
{ 2, TWL4030_BASEADD_PWM1 },
{ 2, TWL4030_BASEADD_PWMA },
{ 2, TWL4030_BASEADD_PWMB },
+ { 2, TWL5031_BASEADD_ACCESSORY },
+ { 2, TWL5031_BASEADD_INTERRUPTS },
{ 3, TWL4030_BASEADD_BACKUP },
{ 3, TWL4030_BASEADD_INT },
@@ -249,12 +298,46 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
{ 3, TWL4030_BASEADD_SECURED_REG },
};
+static struct twl_mapping twl6030_map[] = {
+ /*
+ * NOTE: don't change this table without updating the
+ * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
+ * so they continue to match the order in this table.
+ */
+ { SUB_CHIP_ID1, TWL6030_BASEADD_USB },
+ { SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
+
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+
+ { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
+ { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
+ { SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+ { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
+ { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
+
+ { SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
+ { SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
+};
+
/*----------------------------------------------------------------------*/
/* Exported Functions */
/**
- * twl4030_i2c_write - Writes a n bit register in TWL4030
+ * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: an array of num_bytes+1 containing data to write
* @reg: register address (just offset will do)
@@ -265,19 +348,19 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
*
* Returns the result of operation - 0 is success
*/
-int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
+int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{
int ret;
int sid;
- struct twl4030_client *twl;
+ struct twl_client *twl;
struct i2c_msg *msg;
- if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
+ if (unlikely(mod_no > TWL_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
- sid = twl4030_map[mod_no].sid;
- twl = &twl4030_modules[sid];
+ sid = twl_map[mod_no].sid;
+ twl = &twl_modules[sid];
if (unlikely(!inuse)) {
pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
@@ -294,19 +377,26 @@ int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
msg->flags = 0;
msg->buf = value;
/* over write the first byte of buffer with the register address */
- *value = twl4030_map[mod_no].base + reg;
+ *value = twl_map[mod_no].base + reg;
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
mutex_unlock(&twl->xfer_lock);
- /* i2cTransfer returns num messages.translate it pls.. */
- if (ret >= 0)
- ret = 0;
- return ret;
+ /* i2c_transfer returns number of messages transferred */
+ if (ret != 1) {
+ pr_err("%s: i2c_write failed to transfer all messages\n",
+ DRIVER_NAME);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+ } else {
+ return 0;
+ }
}
-EXPORT_SYMBOL(twl4030_i2c_write);
+EXPORT_SYMBOL(twl_i2c_write);
/**
- * twl4030_i2c_read - Reads a n bit register in TWL4030
+ * twl_i2c_read - Reads a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: an array of num_bytes containing data to be read
* @reg: register address (just offset will do)
@@ -314,20 +404,20 @@ EXPORT_SYMBOL(twl4030_i2c_write);
*
* Returns result of operation - num_bytes is success else failure.
*/
-int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
+int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
{
int ret;
u8 val;
int sid;
- struct twl4030_client *twl;
+ struct twl_client *twl;
struct i2c_msg *msg;
- if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
+ if (unlikely(mod_no > TWL_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
- sid = twl4030_map[mod_no].sid;
- twl = &twl4030_modules[sid];
+ sid = twl_map[mod_no].sid;
+ twl = &twl_modules[sid];
if (unlikely(!inuse)) {
pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
@@ -339,7 +429,7 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
msg->addr = twl->address;
msg->len = 1;
msg->flags = 0; /* Read the register value */
- val = twl4030_map[mod_no].base + reg;
+ val = twl_map[mod_no].base + reg;
msg->buf = &val;
/* [MSG2] fill the data rx buffer */
msg = &twl->xfer_msg[1];
@@ -350,45 +440,52 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2);
mutex_unlock(&twl->xfer_lock);
- /* i2cTransfer returns num messages.translate it pls.. */
- if (ret >= 0)
- ret = 0;
- return ret;
+ /* i2c_transfer returns number of messages transferred */
+ if (ret != 2) {
+ pr_err("%s: i2c_read failed to transfer all messages\n",
+ DRIVER_NAME);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+ } else {
+ return 0;
+ }
}
-EXPORT_SYMBOL(twl4030_i2c_read);
+EXPORT_SYMBOL(twl_i2c_read);
/**
- * twl4030_i2c_write_u8 - Writes a 8 bit register in TWL4030
+ * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: the value to be written 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
-int twl4030_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
+int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
{
/* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
u8 temp_buffer[2] = { 0 };
/* offset 1 contains the data */
temp_buffer[1] = value;
- return twl4030_i2c_write(mod_no, temp_buffer, reg, 1);
+ return twl_i2c_write(mod_no, temp_buffer, reg, 1);
}
-EXPORT_SYMBOL(twl4030_i2c_write_u8);
+EXPORT_SYMBOL(twl_i2c_write_u8);
/**
- * twl4030_i2c_read_u8 - Reads a 8 bit register from TWL4030
+ * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: the value read 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
-int twl4030_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
+int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
{
- return twl4030_i2c_read(mod_no, value, reg, 1);
+ return twl_i2c_read(mod_no, value, reg, 1);
}
-EXPORT_SYMBOL(twl4030_i2c_read_u8);
+EXPORT_SYMBOL(twl_i2c_read_u8);
/*----------------------------------------------------------------------*/
@@ -398,7 +495,7 @@ add_numbered_child(unsigned chip, const char *name, int num,
bool can_wakeup, int irq0, int irq1)
{
struct platform_device *pdev;
- struct twl4030_client *twl = &twl4030_modules[chip];
+ struct twl_client *twl = &twl_modules[chip];
int status;
pdev = platform_device_alloc(name, num);
@@ -456,6 +553,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
unsigned num_consumers)
{
+ unsigned sub_chip_id;
/* regulator framework demands init_data ... */
if (!pdata)
return NULL;
@@ -466,7 +564,8 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
}
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
- return add_numbered_child(3, "twl4030_reg", num,
+ sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
+ return add_numbered_child(sub_chip_id, "twl_reg", num,
pdata, sizeof(*pdata), false, 0, 0);
}
@@ -486,29 +585,32 @@ static int
add_children(struct twl4030_platform_data *pdata, unsigned long features)
{
struct device *child;
+ unsigned sub_chip_id;
- if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) {
+ if (twl_has_bci() && pdata->bci &&
+ !(features & (TPS_SUBSET | TWL5031))) {
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci),
false,
/* irq0 = CHG_PRES, irq1 = BCI */
- pdata->irq_base + 8 + 1, pdata->irq_base + 2);
+ pdata->irq_base + BCI_PRES_INTR_OFFSET,
+ pdata->irq_base + BCI_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_gpio() && pdata->gpio) {
- child = add_child(1, "twl4030_gpio",
+ child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
- false, pdata->irq_base + 0, 0);
+ false, pdata->irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_keypad() && pdata->keypad) {
- child = add_child(2, "twl4030_keypad",
+ child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
- true, pdata->irq_base + 1, 0);
+ true, pdata->irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -516,7 +618,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_madc() && pdata->madc) {
child = add_child(2, "twl4030_madc",
pdata->madc, sizeof(*pdata->madc),
- true, pdata->irq_base + 3, 0);
+ true, pdata->irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -529,14 +631,15 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
- child = add_child(3, "twl4030_rtc",
+ sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
+ child = add_child(sub_chip_id, "twl_rtc",
NULL, 0,
- true, pdata->irq_base + 8 + 3, 0);
+ true, pdata->irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
- if (twl_has_usb() && pdata->usb) {
+ if (twl_has_usb() && pdata->usb && twl_class_is_4030()) {
static struct regulator_consumer_supply usb1v5 = {
.supply = "usb1v5",
@@ -581,7 +684,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->usb, sizeof(*pdata->usb),
true,
/* irq0 = USB_PRES, irq1 = USB */
- pdata->irq_base + 8 + 2, pdata->irq_base + 4);
+ pdata->irq_base + USB_PRES_INTR_OFFSET,
+ pdata->irq_base + USB_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -615,12 +719,23 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
- if (twl_has_regulator()) {
- /*
+ /* twl4030 regulators */
+ if (twl_has_regulator() && twl_class_is_4030()) {
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
if (IS_ERR(child))
return PTR_ERR(child);
- */
+
+ child = add_regulator(TWL4030_REG_VIO, pdata->vio);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1);
if (IS_ERR(child))
@@ -636,10 +751,23 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->vaux2);
if (IS_ERR(child))
return PTR_ERR(child);
+
+ child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
}
/* maybe add LDOs that are omitted on cost-reduced parts */
- if (twl_has_regulator() && !(features & TPS_SUBSET)) {
+ if (twl_has_regulator() && !(features & TPS_SUBSET)
+ && twl_class_is_4030()) {
child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -665,6 +793,49 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
+ /* twl6030 regulators */
+ if (twl_has_regulator() && twl_class_is_6030()) {
+ child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VPP, pdata->vpp);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VANA, pdata->vana);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VDAC, pdata->vdac);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VUSB, pdata->vusb);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
return 0;
}
@@ -679,7 +850,7 @@ static inline int __init protect_pm_master(void)
{
int e = 0;
- e = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_LOCK,
+ e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_LOCK,
R_PROTECT_KEY);
return e;
}
@@ -688,14 +859,15 @@ static inline int __init unprotect_pm_master(void)
{
int e = 0;
- e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK1,
+ e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK1,
R_PROTECT_KEY);
- e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK2,
+ e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK2,
R_PROTECT_KEY);
return e;
}
-static void clocks_init(struct device *dev)
+static void clocks_init(struct device *dev,
+ struct twl4030_clock_init_data *clock)
{
int e = 0;
struct clk *osc;
@@ -709,7 +881,7 @@ static void clocks_init(struct device *dev)
osc = clk_get(dev, "osc_sys_ck");
if (IS_ERR(osc)) {
- printk(KERN_WARNING "Skipping twl4030 internal clock init and "
+ printk(KERN_WARNING "Skipping twl internal clock init and "
"using bootloader value (unknown osc rate)\n");
return;
}
@@ -723,7 +895,7 @@ static void clocks_init(struct device *dev)
*/
osc = ERR_PTR(-EIO);
- printk(KERN_WARNING "Skipping twl4030 internal clock init and "
+ printk(KERN_WARNING "Skipping twl internal clock init and "
"using bootloader value (unknown osc rate)\n");
return;
@@ -742,9 +914,12 @@ static void clocks_init(struct device *dev)
}
ctrl |= HIGH_PERF_SQ;
+ if (clock && clock->ck32k_lowpwr_enable)
+ ctrl |= CK32K_LOWPWR_EN;
+
e |= unprotect_pm_master();
/* effect->MADC+USB ck en */
- e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
+ e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
e |= protect_pm_master();
if (e < 0)
@@ -753,24 +928,31 @@ static void clocks_init(struct device *dev)
/*----------------------------------------------------------------------*/
-int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
-int twl_exit_irq(void);
+int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+int twl4030_exit_irq(void);
+int twl4030_init_chip_irq(const char *chip);
+int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+int twl6030_exit_irq(void);
-static int twl4030_remove(struct i2c_client *client)
+static int twl_remove(struct i2c_client *client)
{
unsigned i;
int status;
- status = twl_exit_irq();
+ if (twl_class_is_4030())
+ status = twl4030_exit_irq();
+ else
+ status = twl6030_exit_irq();
+
if (status < 0)
return status;
- for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
- struct twl4030_client *twl = &twl4030_modules[i];
+ for (i = 0; i < TWL_NUM_SLAVES; i++) {
+ struct twl_client *twl = &twl_modules[i];
if (twl->client && twl->client != client)
i2c_unregister_device(twl->client);
- twl4030_modules[i].client = NULL;
+ twl_modules[i].client = NULL;
}
inuse = false;
return 0;
@@ -778,7 +960,7 @@ static int twl4030_remove(struct i2c_client *client)
/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
static int __init
-twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
+twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;
unsigned i;
@@ -799,8 +981,8 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -EBUSY;
}
- for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
- struct twl4030_client *twl = &twl4030_modules[i];
+ for (i = 0; i < TWL_NUM_SLAVES; i++) {
+ struct twl_client *twl = &twl_modules[i];
twl->address = client->addr + i;
if (i == 0)
@@ -814,15 +996,20 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
status = -ENOMEM;
goto fail;
}
- strlcpy(twl->client->name, id->name,
- sizeof(twl->client->name));
}
mutex_init(&twl->xfer_lock);
}
inuse = true;
+ if ((id->driver_data) & TWL6030_CLASS) {
+ twl_id = TWL6030_CLASS_ID;
+ twl_map = &twl6030_map[0];
+ } else {
+ twl_id = TWL4030_CLASS_ID;
+ twl_map = &twl4030_map[0];
+ }
/* setup clock framework */
- clocks_init(&client->dev);
+ clocks_init(&client->dev, pdata->clock);
/* load power event scripts */
if (twl_has_power() && pdata->power)
@@ -832,7 +1019,15 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->irq
&& pdata->irq_base
&& pdata->irq_end > pdata->irq_base) {
- status = twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end);
+ if (twl_class_is_4030()) {
+ twl4030_init_chip_irq(id->name);
+ status = twl4030_init_irq(client->irq, pdata->irq_base,
+ pdata->irq_end);
+ } else {
+ status = twl6030_init_irq(client->irq, pdata->irq_base,
+ pdata->irq_end);
+ }
+
if (status < 0)
goto fail;
}
@@ -840,40 +1035,42 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
status = add_children(pdata, id->driver_data);
fail:
if (status < 0)
- twl4030_remove(client);
+ twl_remove(client);
return status;
}
-static const struct i2c_device_id twl4030_ids[] = {
+static const struct i2c_device_id twl_ids[] = {
{ "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */
{ "twl5030", 0 }, /* T2 updated */
+ { "twl5031", TWL5031 }, /* TWL5030 updated */
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
+ { "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ /* end of list */ },
};
-MODULE_DEVICE_TABLE(i2c, twl4030_ids);
+MODULE_DEVICE_TABLE(i2c, twl_ids);
/* One Client Driver , 4 Clients */
-static struct i2c_driver twl4030_driver = {
+static struct i2c_driver twl_driver = {
.driver.name = DRIVER_NAME,
- .id_table = twl4030_ids,
- .probe = twl4030_probe,
- .remove = twl4030_remove,
+ .id_table = twl_ids,
+ .probe = twl_probe,
+ .remove = twl_remove,
};
-static int __init twl4030_init(void)
+static int __init twl_init(void)
{
- return i2c_add_driver(&twl4030_driver);
+ return i2c_add_driver(&twl_driver);
}
-subsys_initcall(twl4030_init);
+subsys_initcall(twl_init);
-static void __exit twl4030_exit(void)
+static void __exit twl_exit(void)
{
- i2c_del_driver(&twl4030_driver);
+ i2c_del_driver(&twl_driver);
}
-module_exit(twl4030_exit);
+module_exit(twl_exit);
MODULE_AUTHOR("Texas Instruments, Inc.");
-MODULE_DESCRIPTION("I2C Core interface for TWL4030");
+MODULE_DESCRIPTION("I2C Core interface for TWL");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
index 77b914907d7c..700b149c1b91 100644
--- a/drivers/mfd/twl4030-codec.c
+++ b/drivers/mfd/twl4030-codec.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl4030-codec.h>
@@ -56,7 +56,7 @@ static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
u8 val;
- twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
codec->resource[id].reg);
if (enable)
@@ -64,7 +64,7 @@ static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
else
val &= ~codec->resource[id].mask;
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
val, codec->resource[id].reg);
return val;
@@ -75,7 +75,7 @@ static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
u8 val;
- twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
codec->resource[id].reg);
return val;
@@ -183,7 +183,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Invalid audio_mclk\n");
return -EINVAL;
}
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
val, TWL4030_REG_APLL_CTL);
codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index fb194fe244c1..20d29bafc9f5 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -32,7 +32,7 @@
#include <linux/irq.h>
#include <linux/kthread.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
/*
@@ -74,6 +74,8 @@ struct sih {
u8 edr_offset;
u8 bytes_edr; /* bytelen of EDR */
+ u8 irq_lines; /* number of supported irq lines */
+
/* SIR ignored -- set interrupt, for testing only */
struct irq_data {
u8 isr_offset;
@@ -82,6 +84,9 @@ struct sih {
/* + 2 bytes padding */
};
+static const struct sih *sih_modules;
+static int nr_sih_modules;
+
#define SIH_INITIALIZER(modname, nbits) \
.module = TWL4030_MODULE_ ## modname, \
.control_offset = TWL4030_ ## modname ## _SIH_CTRL, \
@@ -89,6 +94,7 @@ struct sih {
.bytes_ixr = DIV_ROUND_UP(nbits, 8), \
.edr_offset = TWL4030_ ## modname ## _EDR, \
.bytes_edr = DIV_ROUND_UP((2*(nbits)), 8), \
+ .irq_lines = 2, \
.mask = { { \
.isr_offset = TWL4030_ ## modname ## _ISR1, \
.imr_offset = TWL4030_ ## modname ## _IMR1, \
@@ -107,7 +113,8 @@ struct sih {
/* Order in this table matches order in PIH_ISR. That is,
* BIT(n) in PIH_ISR is sih_modules[n].
*/
-static const struct sih sih_modules[6] = {
+/* sih_modules_twl4030 is used both in twl4030 and twl5030 */
+static const struct sih sih_modules_twl4030[6] = {
[0] = {
.name = "gpio",
.module = TWL4030_MODULE_GPIO,
@@ -118,6 +125,7 @@ static const struct sih sih_modules[6] = {
/* Note: *all* of these IRQs default to no-trigger */
.edr_offset = REG_GPIO_EDR1,
.bytes_edr = 5,
+ .irq_lines = 2,
.mask = { {
.isr_offset = REG_GPIO_ISR1A,
.imr_offset = REG_GPIO_IMR1A,
@@ -140,6 +148,7 @@ static const struct sih sih_modules[6] = {
.edr_offset = TWL4030_INTERRUPTS_BCIEDR1,
/* Note: most of these IRQs default to no-trigger */
.bytes_edr = 3,
+ .irq_lines = 2,
.mask = { {
.isr_offset = TWL4030_INTERRUPTS_BCIISR1A,
.imr_offset = TWL4030_INTERRUPTS_BCIIMR1A,
@@ -164,6 +173,99 @@ static const struct sih sih_modules[6] = {
/* there are no SIH modules #6 or #7 ... */
};
+static const struct sih sih_modules_twl5031[8] = {
+ [0] = {
+ .name = "gpio",
+ .module = TWL4030_MODULE_GPIO,
+ .control_offset = REG_GPIO_SIH_CTRL,
+ .set_cor = true,
+ .bits = TWL4030_GPIO_MAX,
+ .bytes_ixr = 3,
+ /* Note: *all* of these IRQs default to no-trigger */
+ .edr_offset = REG_GPIO_EDR1,
+ .bytes_edr = 5,
+ .irq_lines = 2,
+ .mask = { {
+ .isr_offset = REG_GPIO_ISR1A,
+ .imr_offset = REG_GPIO_IMR1A,
+ }, {
+ .isr_offset = REG_GPIO_ISR1B,
+ .imr_offset = REG_GPIO_IMR1B,
+ }, },
+ },
+ [1] = {
+ .name = "keypad",
+ .set_cor = true,
+ SIH_INITIALIZER(KEYPAD_KEYP, 4)
+ },
+ [2] = {
+ .name = "bci",
+ .module = TWL5031_MODULE_INTERRUPTS,
+ .control_offset = TWL5031_INTERRUPTS_BCISIHCTRL,
+ .bits = 7,
+ .bytes_ixr = 1,
+ .edr_offset = TWL5031_INTERRUPTS_BCIEDR1,
+ /* Note: most of these IRQs default to no-trigger */
+ .bytes_edr = 2,
+ .irq_lines = 2,
+ .mask = { {
+ .isr_offset = TWL5031_INTERRUPTS_BCIISR1,
+ .imr_offset = TWL5031_INTERRUPTS_BCIIMR1,
+ }, {
+ .isr_offset = TWL5031_INTERRUPTS_BCIISR2,
+ .imr_offset = TWL5031_INTERRUPTS_BCIIMR2,
+ }, },
+ },
+ [3] = {
+ .name = "madc",
+ SIH_INITIALIZER(MADC, 4)
+ },
+ [4] = {
+ /* USB doesn't use the same SIH organization */
+ .name = "usb",
+ },
+ [5] = {
+ .name = "power",
+ .set_cor = true,
+ SIH_INITIALIZER(INT_PWR, 8)
+ },
+ [6] = {
+ /*
+ * ACI doesn't use the same SIH organization.
+ * For example, it supports only one interrupt line
+ */
+ .name = "aci",
+ .module = TWL5031_MODULE_ACCESSORY,
+ .bits = 9,
+ .bytes_ixr = 2,
+ .irq_lines = 1,
+ .mask = { {
+ .isr_offset = TWL5031_ACIIDR_LSB,
+ .imr_offset = TWL5031_ACIIMR_LSB,
+ }, },
+
+ },
+ [7] = {
+ /* Accessory */
+ .name = "acc",
+ .module = TWL5031_MODULE_ACCESSORY,
+ .control_offset = TWL5031_ACCSIHCTRL,
+ .bits = 2,
+ .bytes_ixr = 1,
+ .edr_offset = TWL5031_ACCEDR1,
+ /* Note: most of these IRQs default to no-trigger */
+ .bytes_edr = 1,
+ .irq_lines = 2,
+ .mask = { {
+ .isr_offset = TWL5031_ACCISR1,
+ .imr_offset = TWL5031_ACCIMR1,
+ }, {
+ .isr_offset = TWL5031_ACCISR2,
+ .imr_offset = TWL5031_ACCIMR2,
+ }, },
+ },
+};
+
#undef TWL4030_MODULE_KEYPAD_KEYP
#undef TWL4030_MODULE_INT_PWR
#undef TWL4030_INT_PWR_EDR
@@ -194,7 +296,7 @@ static int twl4030_irq_thread(void *data)
/* Wait for IRQ, then read PIH irq status (also blocking) */
wait_for_completion_interruptible(&irq_event);
- ret = twl4030_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+ ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
REG_PIH_ISR_P1);
if (ret) {
pr_warning("twl4030: I2C error %d reading PIH ISR\n",
@@ -284,13 +386,17 @@ static int twl4030_init_sih_modules(unsigned line)
/* disable all interrupts on our line */
memset(buf, 0xff, sizeof buf);
sih = sih_modules;
- for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) {
+ for (i = 0; i < nr_sih_modules; i++, sih++) {
/* skip USB -- it's funky */
if (!sih->bytes_ixr)
continue;
- status = twl4030_i2c_write(sih->module, buf,
+ /* Not all the SIH modules support multiple interrupt lines */
+ if (sih->irq_lines <= line)
+ continue;
+
+ status = twl_i2c_write(sih->module, buf,
sih->mask[line].imr_offset, sih->bytes_ixr);
if (status < 0)
pr_err("twl4030: err %d initializing %s %s\n",
@@ -304,7 +410,7 @@ static int twl4030_init_sih_modules(unsigned line)
* And for PWR_INT it's not documented...
*/
if (sih->set_cor) {
- status = twl4030_i2c_write_u8(sih->module,
+ status = twl_i2c_write_u8(sih->module,
TWL4030_SIH_CTRL_COR_MASK,
sih->control_offset);
if (status < 0)
@@ -314,7 +420,7 @@ static int twl4030_init_sih_modules(unsigned line)
}
sih = sih_modules;
- for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) {
+ for (i = 0; i < nr_sih_modules; i++, sih++) {
u8 rxbuf[4];
int j;
@@ -322,20 +428,24 @@ static int twl4030_init_sih_modules(unsigned line)
if (!sih->bytes_ixr)
continue;
+ /* Not all the SIH modules support multiple interrupt lines */
+ if (sih->irq_lines <= line)
+ continue;
+
/* Clear pending interrupt status. Either the read was
* enough, or we need to write those bits. Repeat, in
* case an IRQ is pending (PENDDIS=0) ... that's not
* uncommon with PWR_INT.PWRON.
*/
for (j = 0; j < 2; j++) {
- status = twl4030_i2c_read(sih->module, rxbuf,
+ status = twl_i2c_read(sih->module, rxbuf,
sih->mask[line].isr_offset, sih->bytes_ixr);
if (status < 0)
pr_err("twl4030: err %d initializing %s %s\n",
status, sih->name, "ISR");
if (!sih->set_cor)
- status = twl4030_i2c_write(sih->module, buf,
+ status = twl_i2c_write(sih->module, buf,
sih->mask[line].isr_offset,
sih->bytes_ixr);
/* else COR=1 means read sufficed.
@@ -404,7 +514,7 @@ static void twl4030_sih_do_mask(struct work_struct *work)
return;
/* write the whole mask ... simpler than subsetting it */
- status = twl4030_i2c_write(sih->module, imr.bytes,
+ status = twl_i2c_write(sih->module, imr.bytes,
sih->mask[irq_line].imr_offset, sih->bytes_ixr);
if (status)
pr_err("twl4030: %s, %s --> %d\n", __func__,
@@ -435,7 +545,7 @@ static void twl4030_sih_do_edge(struct work_struct *work)
* any processor on the other IRQ line, EDR registers are
* shared.
*/
- status = twl4030_i2c_read(sih->module, bytes + 1,
+ status = twl_i2c_read(sih->module, bytes + 1,
sih->edr_offset, sih->bytes_edr);
if (status) {
pr_err("twl4030: %s, %s --> %d\n", __func__,
@@ -469,7 +579,7 @@ static void twl4030_sih_do_edge(struct work_struct *work)
}
/* Write */
- status = twl4030_i2c_write(sih->module, bytes,
+ status = twl_i2c_write(sih->module, bytes,
sih->edr_offset, sih->bytes_edr);
if (status)
pr_err("twl4030: %s, %s --> %d\n", __func__,
@@ -554,7 +664,7 @@ static inline int sih_read_isr(const struct sih *sih)
/* FIXME need retry-on-error ... */
isr.word = 0;
- status = twl4030_i2c_read(sih->module, isr.bytes,
+ status = twl_i2c_read(sih->module, isr.bytes,
sih->mask[irq_line].isr_offset, sih->bytes_ixr);
return (status < 0) ? status : le32_to_cpu(isr.word);
@@ -611,7 +721,7 @@ int twl4030_sih_setup(int module)
/* only support modules with standard clear-on-read for now */
for (sih_mod = 0, sih = sih_modules;
- sih_mod < ARRAY_SIZE(sih_modules);
+ sih_mod < nr_sih_modules;
sih_mod++, sih++) {
if (sih->module == module && sih->set_cor) {
if (!WARN((irq_base + sih->bits) > NR_IRQS,
@@ -668,7 +778,7 @@ int twl4030_sih_setup(int module)
/* FIXME pass in which interrupt line we'll use ... */
#define twl_irq_line 0
-int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
+int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
static struct irq_chip twl4030_irq_chip;
@@ -728,7 +838,8 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
goto fail_rqirq;
}
- task = kthread_run(twl4030_irq_thread, (void *)irq_num, "twl4030-irq");
+ task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
+ "twl4030-irq");
if (IS_ERR(task)) {
pr_err("twl4030: could not create irq %d thread!\n", irq_num);
status = PTR_ERR(task);
@@ -747,7 +858,7 @@ fail:
return status;
}
-int twl_exit_irq(void)
+int twl4030_exit_irq(void)
{
/* FIXME undo twl_init_irq() */
if (twl4030_irq_base) {
@@ -756,3 +867,16 @@ int twl_exit_irq(void)
}
return 0;
}
+
+int twl4030_init_chip_irq(const char *chip)
+{
+ if (!strcmp(chip, "twl5031")) {
+ sih_modules = sih_modules_twl5031;
+ nr_sih_modules = ARRAY_SIZE(sih_modules_twl5031);
+ } else {
+ sih_modules = sih_modules_twl4030;
+ nr_sih_modules = ARRAY_SIZE(sih_modules_twl4030);
+ }
+
+ return 0;
+}
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index d423e0c4176b..0815292fdafc 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -26,7 +26,7 @@
#include <linux/module.h>
#include <linux/pm.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
@@ -67,19 +67,35 @@ static u8 twl4030_start_script_address = 0x2b;
#define R_KEY_1 0xC0
#define R_KEY_2 0x0C
-/* resource configuration registers */
-
-#define DEVGROUP_OFFSET 0
+/* resource configuration registers
+ <RESOURCE>_DEV_GRP at address 'n+0'
+ <RESOURCE>_TYPE at address 'n+1'
+ <RESOURCE>_REMAP at address 'n+2'
+ <RESOURCE>_DEDICATED at address 'n+3'
+*/
+#define DEV_GRP_OFFSET 0
#define TYPE_OFFSET 1
+#define REMAP_OFFSET 2
+#define DEDICATED_OFFSET 3
+
+/* Bit positions in the registers */
+
+/* <RESOURCE>_DEV_GRP */
+#define DEV_GRP_SHIFT 5
+#define DEV_GRP_MASK (7 << DEV_GRP_SHIFT)
-/* Bit positions */
-#define DEVGROUP_SHIFT 5
-#define DEVGROUP_MASK (7 << DEVGROUP_SHIFT)
+/* <RESOURCE>_TYPE */
#define TYPE_SHIFT 0
#define TYPE_MASK (7 << TYPE_SHIFT)
#define TYPE2_SHIFT 3
#define TYPE2_MASK (3 << TYPE2_SHIFT)
+/* <RESOURCE>_REMAP */
+#define SLEEP_STATE_SHIFT 0
+#define SLEEP_STATE_MASK (0xf << SLEEP_STATE_SHIFT)
+#define OFF_STATE_SHIFT 4
+#define OFF_STATE_MASK (0xf << OFF_STATE_SHIFT)
+
static u8 res_config_addrs[] = {
[RES_VAUX1] = 0x17,
[RES_VAUX2] = 0x1b,
@@ -115,11 +131,11 @@ static int __init twl4030_write_script_byte(u8 address, u8 byte)
{
int err;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
R_MEMORY_ADDRESS);
if (err)
goto out;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
R_MEMORY_DATA);
out:
return err;
@@ -176,18 +192,18 @@ static int __init twl4030_config_wakeup3_sequence(u8 address)
u8 data;
/* Set SLEEP to ACTIVE SEQ address for P3 */
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
R_SEQ_ADD_S2A3);
if (err)
goto out;
/* P3 LVL_WAKEUP should be on LEVEL */
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
R_P3_SW_EVENTS);
if (err)
goto out;
data |= LVL_WAKEUP;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
R_P3_SW_EVENTS);
out:
if (err)
@@ -201,42 +217,42 @@ static int __init twl4030_config_wakeup12_sequence(u8 address)
u8 data;
/* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
R_SEQ_ADD_S2A12);
if (err)
goto out;
/* P1/P2 LVL_WAKEUP should be on LEVEL */
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
R_P1_SW_EVENTS);
if (err)
goto out;
data |= LVL_WAKEUP;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
R_P1_SW_EVENTS);
if (err)
goto out;
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
R_P2_SW_EVENTS);
if (err)
goto out;
data |= LVL_WAKEUP;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
R_P2_SW_EVENTS);
if (err)
goto out;
if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
/* Disabling AC charger effect on sleep-active transitions */
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
R_CFG_P1_TRANSITION);
if (err)
goto out;
data &= ~(1<<1);
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
R_CFG_P1_TRANSITION);
if (err)
goto out;
@@ -254,7 +270,7 @@ static int __init twl4030_config_sleep_sequence(u8 address)
int err;
/* Set ACTIVE to SLEEP SEQ address in T2 memory*/
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
R_SEQ_ADD_A2S);
if (err)
@@ -269,41 +285,41 @@ static int __init twl4030_config_warmreset_sequence(u8 address)
u8 rd_data;
/* Set WARM RESET SEQ address for P1 */
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
R_SEQ_ADD_WARM);
if (err)
goto out;
/* P1/P2/P3 enable WARMRESET */
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
R_P1_SW_EVENTS);
if (err)
goto out;
rd_data |= ENABLE_WARMRESET;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
R_P1_SW_EVENTS);
if (err)
goto out;
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
R_P2_SW_EVENTS);
if (err)
goto out;
rd_data |= ENABLE_WARMRESET;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
R_P2_SW_EVENTS);
if (err)
goto out;
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
R_P3_SW_EVENTS);
if (err)
goto out;
rd_data |= ENABLE_WARMRESET;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
R_P3_SW_EVENTS);
out:
if (err)
@@ -317,6 +333,7 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
int err;
u8 type;
u8 grp;
+ u8 remap;
if (rconfig->resource > TOTAL_RESOURCES) {
pr_err("TWL4030 Resource %d does not exist\n",
@@ -327,19 +344,19 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
rconfig_addr = res_config_addrs[rconfig->resource];
/* Set resource group */
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
- rconfig_addr + DEVGROUP_OFFSET);
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
+ rconfig_addr + DEV_GRP_OFFSET);
if (err) {
pr_err("TWL4030 Resource %d group could not be read\n",
rconfig->resource);
return err;
}
- if (rconfig->devgroup >= 0) {
- grp &= ~DEVGROUP_MASK;
- grp |= rconfig->devgroup << DEVGROUP_SHIFT;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
- grp, rconfig_addr + DEVGROUP_OFFSET);
+ if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) {
+ grp &= ~DEV_GRP_MASK;
+ grp |= rconfig->devgroup << DEV_GRP_SHIFT;
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ grp, rconfig_addr + DEV_GRP_OFFSET);
if (err < 0) {
pr_err("TWL4030 failed to program devgroup\n");
return err;
@@ -347,7 +364,7 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
}
/* Set resource types */
- err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
rconfig_addr + TYPE_OFFSET);
if (err < 0) {
pr_err("TWL4030 Resource %d type could not be read\n",
@@ -355,23 +372,50 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
return err;
}
- if (rconfig->type >= 0) {
+ if (rconfig->type != TWL4030_RESCONFIG_UNDEF) {
type &= ~TYPE_MASK;
type |= rconfig->type << TYPE_SHIFT;
}
- if (rconfig->type2 >= 0) {
+ if (rconfig->type2 != TWL4030_RESCONFIG_UNDEF) {
type &= ~TYPE2_MASK;
type |= rconfig->type2 << TYPE2_SHIFT;
}
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
type, rconfig_addr + TYPE_OFFSET);
if (err < 0) {
pr_err("TWL4030 failed to program resource type\n");
return err;
}
+ /* Set remap states */
+ err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap,
+ rconfig_addr + REMAP_OFFSET);
+ if (err < 0) {
+ pr_err("TWL4030 Resource %d remap could not be read\n",
+ rconfig->resource);
+ return err;
+ }
+
+ if (rconfig->remap_off != TWL4030_RESCONFIG_UNDEF) {
+ remap &= ~OFF_STATE_MASK;
+ remap |= rconfig->remap_off << OFF_STATE_SHIFT;
+ }
+
+ if (rconfig->remap_sleep != TWL4030_RESCONFIG_UNDEF) {
+ remap &= ~SLEEP_STATE_MASK;
+ remap |= rconfig->remap_off << SLEEP_STATE_SHIFT;
+ }
+
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ remap,
+ rconfig_addr + REMAP_OFFSET);
+ if (err < 0) {
+ pr_err("TWL4030 failed to program remap\n");
+ return err;
+ }
+
return 0;
}
@@ -424,12 +468,12 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
struct twl4030_resconfig *resconfig;
u8 address = twl4030_start_script_address;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
R_PROTECT_KEY);
if (err)
goto unlock;
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
R_PROTECT_KEY);
if (err)
goto unlock;
@@ -452,7 +496,7 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
}
}
- err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
+ err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
return;
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
new file mode 100644
index 000000000000..10bf228ad626
--- /dev/null
+++ b/drivers/mfd/twl6030-irq.c
@@ -0,0 +1,299 @@
+/*
+ * twl6030-irq.c - TWL6030 irq support
+ *
+ * Copyright (C) 2005-2009 Texas Instruments, Inc.
+ *
+ * Modifications to defer interrupt handling to a kernel thread:
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Based on tlv320aic23.c:
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Code cleanup and modifications to IRQ handler.
+ * by syed khasim <x0khasim@ti.com>
+ *
+ * TWL6030 specific code and IRQ handling changes by
+ * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
+ * Balaji T K <balajitk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/i2c/twl.h>
+
+/*
+ * TWL6030 (unlike its predecessors, which had two level interrupt handling)
+ * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
+ * It exposes status bits saying who has raised an interrupt. There are
+ * three mask registers that corresponds to these status registers, that
+ * enables/disables these interrupts.
+ *
+ * We set up IRQs starting at a platform-specified base. An interrupt map table,
+ * specifies mapping between interrupt number and the associated module.
+ *
+ */
+
+static int twl6030_interrupt_mapping[24] = {
+ PWR_INTR_OFFSET, /* Bit 0 PWRON */
+ PWR_INTR_OFFSET, /* Bit 1 RPWRON */
+ PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */
+ RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
+ RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
+ HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
+ SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */
+ SMPSLDO_INTR_OFFSET, /* Bit 7 VMMC_SHORT */
+
+ SMPSLDO_INTR_OFFSET, /* Bit 8 VUSIM_SHORT */
+ BATDETECT_INTR_OFFSET, /* Bit 9 BAT */
+ SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */
+ MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */
+ RSV_INTR_OFFSET, /* Bit 12 Reserved */
+ MADC_INTR_OFFSET, /* Bit 13 GPADC_RT_EOC */
+ MADC_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */
+ GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */
+
+ USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
+ USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
+ USBOTG_INTR_OFFSET, /* Bit 18 ID */
+ USBOTG_INTR_OFFSET, /* Bit 19 VBUS */
+ CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
+ CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */
+ CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */
+ RSV_INTR_OFFSET, /* Bit 23 Reserved */
+};
+/*----------------------------------------------------------------------*/
+
+static unsigned twl6030_irq_base;
+
+static struct completion irq_event;
+
+/*
+ * This thread processes interrupts reported by the Primary Interrupt Handler.
+ */
+static int twl6030_irq_thread(void *data)
+{
+ long irq = (long)data;
+ static unsigned i2c_errors;
+ static const unsigned max_i2c_errors = 100;
+ int ret;
+
+ current->flags |= PF_NOFREEZE;
+
+ while (!kthread_should_stop()) {
+ int i;
+ union {
+ u8 bytes[4];
+ u32 int_sts;
+ } sts;
+
+ /* Wait for IRQ, then read PIH irq status (also blocking) */
+ wait_for_completion_interruptible(&irq_event);
+
+ /* read INT_STS_A, B and C in one shot using a burst read */
+ ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
+ REG_INT_STS_A, 3);
+ if (ret) {
+ pr_warning("twl6030: I2C error %d reading PIH ISR\n",
+ ret);
+ if (++i2c_errors >= max_i2c_errors) {
+ printk(KERN_ERR "Maximum I2C error count"
+ " exceeded. Terminating %s.\n",
+ __func__);
+ break;
+ }
+ complete(&irq_event);
+ continue;
+ }
+
+
+
+ sts.bytes[3] = 0; /* Only 24 bits are valid*/
+
+ for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
+ local_irq_disable();
+ if (sts.int_sts & 0x1) {
+ int module_irq = twl6030_irq_base +
+ twl6030_interrupt_mapping[i];
+ struct irq_desc *d = irq_to_desc(module_irq);
+
+ if (!d) {
+ pr_err("twl6030: Invalid SIH IRQ: %d\n",
+ module_irq);
+ return -EINVAL;
+ }
+
+ /* These can't be masked ... always warn
+ * if we get any surprises.
+ */
+ if (d->status & IRQ_DISABLED)
+ note_interrupt(module_irq, d,
+ IRQ_NONE);
+ else
+ d->handle_irq(module_irq, d);
+
+ }
+ local_irq_enable();
+ }
+ ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
+ REG_INT_STS_A, 3); /* clear INT_STS_A */
+ if (ret)
+ pr_warning("twl6030: I2C error in clearing PIH ISR\n");
+
+ enable_irq(irq);
+ }
+
+ return 0;
+}
+
+/*
+ * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
+ * This is a chained interrupt, so there is no desc->action method for it.
+ * Now we need to query the interrupt controller in the twl6030 to determine
+ * which module is generating the interrupt request. However, we can't do i2c
+ * transactions in interrupt context, so we must defer that work to a kernel
+ * thread. All we do here is acknowledge and mask the interrupt and wakeup
+ * the kernel thread.
+ */
+static irqreturn_t handle_twl6030_pih(int irq, void *devid)
+{
+ disable_irq_nosync(irq);
+ complete(devid);
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+static inline void activate_irq(int irq)
+{
+#ifdef CONFIG_ARM
+ /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
+ * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
+ */
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ /* same effect on other architectures */
+ set_irq_noprobe(irq);
+#endif
+}
+
+/*----------------------------------------------------------------------*/
+
+static unsigned twl6030_irq_next;
+
+/*----------------------------------------------------------------------*/
+int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
+{
+ int ret;
+ u8 unmask_value;
+ ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
+ REG_INT_STS_A + offset);
+ unmask_value &= (~(bit_mask));
+ ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
+ REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
+ return ret;
+}
+EXPORT_SYMBOL(twl6030_interrupt_unmask);
+
+int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
+{
+ int ret;
+ u8 mask_value;
+ ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
+ REG_INT_STS_A + offset);
+ mask_value |= (bit_mask);
+ ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
+ REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
+ return ret;
+}
+EXPORT_SYMBOL(twl6030_interrupt_mask);
+
+int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
+{
+
+ int status = 0;
+ int i;
+ struct task_struct *task;
+ int ret;
+ u8 mask[4];
+
+ static struct irq_chip twl6030_irq_chip;
+ mask[1] = 0xFF;
+ mask[2] = 0xFF;
+ mask[3] = 0xFF;
+ ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
+ REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */
+ ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
+ REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */
+ ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
+ REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
+
+ twl6030_irq_base = irq_base;
+
+ /* install an irq handler for each of the modules;
+ * clone dummy irq_chip since PIH can't *do* anything
+ */
+ twl6030_irq_chip = dummy_irq_chip;
+ twl6030_irq_chip.name = "twl6030";
+ twl6030_irq_chip.set_type = NULL;
+
+ for (i = irq_base; i < irq_end; i++) {
+ set_irq_chip_and_handler(i, &twl6030_irq_chip,
+ handle_simple_irq);
+ activate_irq(i);
+ }
+
+ twl6030_irq_next = i;
+ pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
+ irq_num, irq_base, twl6030_irq_next - 1);
+
+ /* install an irq handler to demultiplex the TWL6030 interrupt */
+ init_completion(&irq_event);
+ task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
+ if (IS_ERR(task)) {
+ pr_err("twl6030: could not create irq %d thread!\n", irq_num);
+ status = PTR_ERR(task);
+ goto fail_kthread;
+ }
+
+ status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
+ "TWL6030-PIH", &irq_event);
+ if (status < 0) {
+ pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
+ goto fail_irq;
+ }
+ return status;
+fail_irq:
+ free_irq(irq_num, &irq_event);
+
+fail_kthread:
+ for (i = irq_base; i < irq_end; i++)
+ set_irq_chip_and_handler(i, NULL, NULL);
+ return status;
+}
+
+int twl6030_exit_irq(void)
+{
+
+ if (twl6030_irq_base) {
+ pr_err("twl6030: can't yet clean up IRQs?\n");
+ return -ENOSYS;
+ }
+ return 0;
+}
+
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 7f27576ca046..4b2021af1d96 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -90,9 +90,10 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
enum wm831x_parent {
- WM8310 = 0,
- WM8311 = 1,
- WM8312 = 2,
+ WM8310 = 0x8310,
+ WM8311 = 0x8311,
+ WM8312 = 0x8312,
+ WM8320 = 0x8320,
};
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
@@ -478,6 +479,20 @@ static struct resource wm831x_dcdc4_resources[] = {
},
};
+static struct resource wm8320_dcdc4_buck_resources[] = {
+ {
+ .start = WM831X_DC4_CONTROL,
+ .end = WM832X_DC4_SLEEP_CONTROL,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .name = "UV",
+ .start = WM831X_IRQ_UV_DC4,
+ .end = WM831X_IRQ_UV_DC4,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct resource wm831x_gpio_resources[] = {
{
.start = WM831X_IRQ_GPIO_1,
@@ -794,6 +809,9 @@ static struct resource wm831x_wdt_resources[] = {
static struct mfd_cell wm8310_devs[] = {
{
+ .name = "wm831x-backup",
+ },
+ {
.name = "wm831x-buckv",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
@@ -947,6 +965,9 @@ static struct mfd_cell wm8310_devs[] = {
static struct mfd_cell wm8311_devs[] = {
{
+ .name = "wm831x-backup",
+ },
+ {
.name = "wm831x-buckv",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
@@ -1081,6 +1102,9 @@ static struct mfd_cell wm8311_devs[] = {
static struct mfd_cell wm8312_devs[] = {
{
+ .name = "wm831x-backup",
+ },
+ {
.name = "wm831x-buckv",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
@@ -1237,6 +1261,137 @@ static struct mfd_cell wm8312_devs[] = {
},
};
+static struct mfd_cell wm8320_devs[] = {
+ {
+ .name = "wm831x-backup",
+ },
+ {
+ .name = "wm831x-buckv",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+ .resources = wm831x_dcdc1_resources,
+ },
+ {
+ .name = "wm831x-buckv",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+ .resources = wm831x_dcdc2_resources,
+ },
+ {
+ .name = "wm831x-buckp",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+ .resources = wm831x_dcdc3_resources,
+ },
+ {
+ .name = "wm831x-buckp",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
+ .resources = wm8320_dcdc4_buck_resources,
+ },
+ {
+ .name = "wm831x-gpio",
+ .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+ .resources = wm831x_gpio_resources,
+ },
+ {
+ .name = "wm831x-hwmon",
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+ .resources = wm831x_ldo1_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+ .resources = wm831x_ldo2_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+ .resources = wm831x_ldo3_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+ .resources = wm831x_ldo4_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 5,
+ .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+ .resources = wm831x_ldo5_resources,
+ },
+ {
+ .name = "wm831x-ldo",
+ .id = 6,
+ .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+ .resources = wm831x_ldo6_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 7,
+ .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+ .resources = wm831x_ldo7_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 8,
+ .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+ .resources = wm831x_ldo8_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 9,
+ .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+ .resources = wm831x_ldo9_resources,
+ },
+ {
+ .name = "wm831x-aldo",
+ .id = 10,
+ .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+ .resources = wm831x_ldo10_resources,
+ },
+ {
+ .name = "wm831x-alive-ldo",
+ .id = 11,
+ .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+ .resources = wm831x_ldo11_resources,
+ },
+ {
+ .name = "wm831x-on",
+ .num_resources = ARRAY_SIZE(wm831x_on_resources),
+ .resources = wm831x_on_resources,
+ },
+ {
+ .name = "wm831x-rtc",
+ .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+ .resources = wm831x_rtc_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(wm831x_status1_resources),
+ .resources = wm831x_status1_resources,
+ },
+ {
+ .name = "wm831x-status",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(wm831x_status2_resources),
+ .resources = wm831x_status2_resources,
+ },
+ {
+ .name = "wm831x-watchdog",
+ .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+ .resources = wm831x_wdt_resources,
+ },
+};
+
static struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
@@ -1282,50 +1437,37 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
goto err;
}
+ /* Some engineering samples do not have the ID set, rely on
+ * the device being registered correctly.
+ */
+ if (ret == 0) {
+ dev_info(wm831x->dev, "Device is an engineering sample\n");
+ ret = id;
+ }
+
switch (ret) {
- case 0x8310:
+ case WM8310:
parent = WM8310;
- switch (rev) {
- case 0:
- dev_info(wm831x->dev, "WM8310 revision %c\n",
- 'A' + rev);
- break;
- }
+ wm831x->num_gpio = 16;
+ dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
break;
- case 0x8311:
+ case WM8311:
parent = WM8311;
- switch (rev) {
- case 0:
- dev_info(wm831x->dev, "WM8311 revision %c\n",
- 'A' + rev);
- break;
- }
+ wm831x->num_gpio = 16;
+ dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
break;
- case 0x8312:
+ case WM8312:
parent = WM8312;
- switch (rev) {
- case 0:
- dev_info(wm831x->dev, "WM8312 revision %c\n",
- 'A' + rev);
- break;
- }
+ wm831x->num_gpio = 16;
+ dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
break;
- case 0:
- /* Some engineering samples do not have the ID set,
- * rely on the device being registered correctly.
- * This will need revisiting for future devices with
- * multiple dies.
- */
- parent = id;
- switch (rev) {
- case 0:
- dev_info(wm831x->dev, "WM831%d ES revision %c\n",
- parent, 'A' + rev);
- break;
- }
+ case WM8320:
+ parent = WM8320;
+ wm831x->num_gpio = 12;
+ dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
break;
default:
@@ -1338,7 +1480,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
* current parts.
*/
if (parent != id)
- dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n",
+ dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
id);
/* Bootstrap the user key */
@@ -1371,18 +1513,24 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8310:
ret = mfd_add_devices(wm831x->dev, -1,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
- NULL, 0);
+ NULL, wm831x->irq_base);
break;
case WM8311:
ret = mfd_add_devices(wm831x->dev, -1,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
- NULL, 0);
+ NULL, wm831x->irq_base);
break;
case WM8312:
ret = mfd_add_devices(wm831x->dev, -1,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
+ NULL, wm831x->irq_base);
+ break;
+
+ case WM8320:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, 0);
break;
@@ -1399,7 +1547,8 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
if (pdata && pdata->backlight) {
/* Treat errors as non-critical */
ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
- ARRAY_SIZE(backlight_devs), NULL, 0);
+ ARRAY_SIZE(backlight_devs), NULL,
+ wm831x->irq_base);
if (ret < 0)
dev_err(wm831x->dev, "Failed to add backlight: %d\n",
ret);
@@ -1511,6 +1660,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8310", WM8310 },
{ "wm8311", WM8311 },
{ "wm8312", WM8312 },
+ { "wm8320", WM8320 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index ac056ea6b66e..301327697117 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/interrupt.h>
@@ -339,110 +340,71 @@ static inline int irq_data_to_mask_reg(struct wm831x_irq_data *irq_data)
return WM831X_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
}
-static void __wm831x_enable_irq(struct wm831x *wm831x, int irq)
+static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
+ int irq)
{
- struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
-
- wm831x->irq_masks[irq_data->reg - 1] &= ~irq_data->mask;
- wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data),
- wm831x->irq_masks[irq_data->reg - 1]);
+ return &wm831x_irqs[irq - wm831x->irq_base];
}
-void wm831x_enable_irq(struct wm831x *wm831x, int irq)
+static void wm831x_irq_lock(unsigned int irq)
{
- mutex_lock(&wm831x->irq_lock);
- __wm831x_enable_irq(wm831x, irq);
- mutex_unlock(&wm831x->irq_lock);
-}
-EXPORT_SYMBOL_GPL(wm831x_enable_irq);
+ struct wm831x *wm831x = get_irq_chip_data(irq);
-static void __wm831x_disable_irq(struct wm831x *wm831x, int irq)
-{
- struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
-
- wm831x->irq_masks[irq_data->reg - 1] |= irq_data->mask;
- wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data),
- wm831x->irq_masks[irq_data->reg - 1]);
-}
-
-void wm831x_disable_irq(struct wm831x *wm831x, int irq)
-{
mutex_lock(&wm831x->irq_lock);
- __wm831x_disable_irq(wm831x, irq);
- mutex_unlock(&wm831x->irq_lock);
}
-EXPORT_SYMBOL_GPL(wm831x_disable_irq);
-int wm831x_request_irq(struct wm831x *wm831x,
- unsigned int irq, irq_handler_t handler,
- unsigned long flags, const char *name,
- void *dev)
+static void wm831x_irq_sync_unlock(unsigned int irq)
{
- int ret = 0;
-
- if (irq < 0 || irq >= WM831X_NUM_IRQS)
- return -EINVAL;
-
- mutex_lock(&wm831x->irq_lock);
-
- if (wm831x_irqs[irq].handler) {
- dev_err(wm831x->dev, "Already have handler for IRQ %d\n", irq);
- ret = -EINVAL;
- goto out;
+ struct wm831x *wm831x = get_irq_chip_data(irq);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+ /* If there's been a change in the mask write it back
+ * to the hardware. */
+ if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
+ wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
+ wm831x_reg_write(wm831x,
+ WM831X_INTERRUPT_STATUS_1_MASK + i,
+ wm831x->irq_masks_cur[i]);
+ }
}
- wm831x_irqs[irq].handler = handler;
- wm831x_irqs[irq].handler_data = dev;
-
- __wm831x_enable_irq(wm831x, irq);
-
-out:
mutex_unlock(&wm831x->irq_lock);
-
- return ret;
}
-EXPORT_SYMBOL_GPL(wm831x_request_irq);
-void wm831x_free_irq(struct wm831x *wm831x, unsigned int irq, void *data)
+static void wm831x_irq_unmask(unsigned int irq)
{
- if (irq < 0 || irq >= WM831X_NUM_IRQS)
- return;
-
- mutex_lock(&wm831x->irq_lock);
+ struct wm831x *wm831x = get_irq_chip_data(irq);
+ struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq);
- wm831x_irqs[irq].handler = NULL;
- wm831x_irqs[irq].handler_data = NULL;
-
- __wm831x_disable_irq(wm831x, irq);
-
- mutex_unlock(&wm831x->irq_lock);
+ wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
}
-EXPORT_SYMBOL_GPL(wm831x_free_irq);
-
-static void wm831x_handle_irq(struct wm831x *wm831x, int irq, int status)
+static void wm831x_irq_mask(unsigned int irq)
{
- struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
-
- if (irq_data->handler) {
- irq_data->handler(irq, irq_data->handler_data);
- wm831x_reg_write(wm831x, irq_data_to_status_reg(irq_data),
- irq_data->mask);
- } else {
- dev_err(wm831x->dev, "Unhandled IRQ %d, masking\n", irq);
- __wm831x_disable_irq(wm831x, irq);
- }
+ struct wm831x *wm831x = get_irq_chip_data(irq);
+ struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq);
+
+ wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
}
-/* Main interrupt handling occurs in a workqueue since we need
- * interrupts enabled to interact with the chip. */
-static void wm831x_irq_worker(struct work_struct *work)
+static struct irq_chip wm831x_irq_chip = {
+ .name = "wm831x",
+ .bus_lock = wm831x_irq_lock,
+ .bus_sync_unlock = wm831x_irq_sync_unlock,
+ .mask = wm831x_irq_mask,
+ .unmask = wm831x_irq_unmask,
+};
+
+/* The processing of the primary interrupt occurs in a thread so that
+ * we can interact with the device over I2C or SPI. */
+static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
- struct wm831x *wm831x = container_of(work, struct wm831x, irq_work);
+ struct wm831x *wm831x = data;
unsigned int i;
int primary;
- int status_regs[5];
- int read[5] = { 0 };
+ int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
+ int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
@@ -452,8 +414,6 @@ static void wm831x_irq_worker(struct work_struct *work)
goto out;
}
- mutex_lock(&wm831x->irq_lock);
-
for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
int offset = wm831x_irqs[i].reg - 1;
@@ -471,41 +431,34 @@ static void wm831x_irq_worker(struct work_struct *work)
dev_err(wm831x->dev,
"Failed to read IRQ status: %d\n",
*status);
- goto out_lock;
+ goto out;
}
- /* Mask out the disabled IRQs */
- *status &= ~wm831x->irq_masks[offset];
read[offset] = 1;
}
- if (*status & wm831x_irqs[i].mask)
- wm831x_handle_irq(wm831x, i, *status);
+ /* Report it if it isn't masked, or forget the status. */
+ if ((*status & ~wm831x->irq_masks_cur[offset])
+ & wm831x_irqs[i].mask)
+ handle_nested_irq(wm831x->irq_base + i);
+ else
+ *status &= ~wm831x_irqs[i].mask;
}
-out_lock:
- mutex_unlock(&wm831x->irq_lock);
out:
- enable_irq(wm831x->irq);
-}
-
-
-static irqreturn_t wm831x_cpu_irq(int irq, void *data)
-{
- struct wm831x *wm831x = data;
-
- /* Shut the interrupt to the CPU up and schedule the actual
- * handler; we can't check that the IRQ is asserted. */
- disable_irq_nosync(irq);
-
- queue_work(wm831x->irq_wq, &wm831x->irq_work);
+ for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
+ if (status_regs[i])
+ wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
+ status_regs[i]);
+ }
return IRQ_HANDLED;
}
int wm831x_irq_init(struct wm831x *wm831x, int irq)
{
- int i, ret;
+ struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ int i, cur_irq, ret;
mutex_init(&wm831x->irq_lock);
@@ -515,41 +468,53 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
return 0;
}
-
- wm831x->irq_wq = create_singlethread_workqueue("wm831x-irq");
- if (!wm831x->irq_wq) {
- dev_err(wm831x->dev, "Failed to allocate IRQ worker\n");
- return -ESRCH;
+ if (!pdata || !pdata->irq_base) {
+ dev_err(wm831x->dev,
+ "No interrupt base specified, no interrupts\n");
+ return 0;
}
wm831x->irq = irq;
- INIT_WORK(&wm831x->irq_work, wm831x_irq_worker);
+ wm831x->irq_base = pdata->irq_base;
/* Mask the individual interrupt sources */
- for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks); i++) {
- wm831x->irq_masks[i] = 0xffff;
+ for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+ wm831x->irq_masks_cur[i] = 0xffff;
+ wm831x->irq_masks_cache[i] = 0xffff;
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
0xffff);
}
- /* Enable top level interrupts, we mask at secondary level */
- wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
+ /* Register them with genirq */
+ for (cur_irq = wm831x->irq_base;
+ cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
+ cur_irq++) {
+ set_irq_chip_data(cur_irq, wm831x);
+ set_irq_chip_and_handler(cur_irq, &wm831x_irq_chip,
+ handle_edge_irq);
+ set_irq_nested_thread(cur_irq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ set_irq_noprobe(cur_irq);
+#endif
+ }
- /* We're good to go. We set IRQF_SHARED since there's a
- * chance the driver will interoperate with another driver but
- * the need to disable the IRQ while handing via I2C/SPI means
- * that this may break and performance will be impacted. If
- * this does happen it's a hardware design issue and the only
- * other alternative would be polling.
- */
- ret = request_irq(irq, wm831x_cpu_irq, IRQF_TRIGGER_LOW | IRQF_SHARED,
- "wm831x", wm831x);
+ ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "wm831x", wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
return ret;
}
+ /* Enable top level interrupts, we mask at secondary level */
+ wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
+
return 0;
}
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index ba27c9dc1ad3..8485a7018060 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -337,733 +337,6 @@ int wm8350_reg_unlock(struct wm8350 *wm8350)
}
EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
-static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
-{
- mutex_lock(&wm8350->irq_mutex);
-
- if (wm8350->irq[irq].handler)
- wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data);
- else {
- dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
- irq);
- wm8350_mask_irq(wm8350, irq);
- }
-
- mutex_unlock(&wm8350->irq_mutex);
-}
-
-/*
- * This is a threaded IRQ handler so can access I2C/SPI. Since all
- * interrupts are clear on read the IRQ line will be reasserted and
- * the physical IRQ will be handled again if another interrupt is
- * asserted while we run - in the normal course of events this is a
- * rare occurrence so we save I2C/SPI reads.
- */
-static irqreturn_t wm8350_irq(int irq, void *data)
-{
- struct wm8350 *wm8350 = data;
- u16 level_one, status1, status2, comp;
-
- /* TODO: Use block reads to improve performance? */
- level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
- & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
- status1 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1)
- & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_1_MASK);
- status2 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_2)
- & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_2_MASK);
- comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS)
- & ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK);
-
- /* over current */
- if (level_one & WM8350_OC_INT) {
- u16 oc;
-
- oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INT_STATUS);
- oc &= ~wm8350_reg_read(wm8350,
- WM8350_OVER_CURRENT_INT_STATUS_MASK);
-
- if (oc & WM8350_OC_LS_EINT) /* limit switch */
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_OC_LS);
- }
-
- /* under voltage */
- if (level_one & WM8350_UV_INT) {
- u16 uv;
-
- uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS);
- uv &= ~wm8350_reg_read(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK);
-
- if (uv & WM8350_UV_DC1_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC1);
- if (uv & WM8350_UV_DC2_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC2);
- if (uv & WM8350_UV_DC3_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC3);
- if (uv & WM8350_UV_DC4_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC4);
- if (uv & WM8350_UV_DC5_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC5);
- if (uv & WM8350_UV_DC6_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC6);
- if (uv & WM8350_UV_LDO1_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO1);
- if (uv & WM8350_UV_LDO2_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO2);
- if (uv & WM8350_UV_LDO3_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO3);
- if (uv & WM8350_UV_LDO4_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO4);
- }
-
- /* charger, RTC */
- if (status1) {
- if (status1 & WM8350_CHG_BAT_HOT_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_BAT_HOT);
- if (status1 & WM8350_CHG_BAT_COLD_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_BAT_COLD);
- if (status1 & WM8350_CHG_BAT_FAIL_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_BAT_FAIL);
- if (status1 & WM8350_CHG_TO_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_TO);
- if (status1 & WM8350_CHG_END_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_END);
- if (status1 & WM8350_CHG_START_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_START);
- if (status1 & WM8350_CHG_FAST_RDY_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_FAST_RDY);
- if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_VBATT_LT_3P9);
- if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_VBATT_LT_3P1);
- if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CHG_VBATT_LT_2P85);
- if (status1 & WM8350_RTC_ALM_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_ALM);
- if (status1 & WM8350_RTC_SEC_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_SEC);
- if (status1 & WM8350_RTC_PER_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_PER);
- }
-
- /* current sink, system, aux adc */
- if (status2) {
- if (status2 & WM8350_CS1_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS1);
- if (status2 & WM8350_CS2_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS2);
-
- if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_SYS_HYST_COMP_FAIL);
- if (status2 & WM8350_SYS_CHIP_GT115_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_SYS_CHIP_GT115);
- if (status2 & WM8350_SYS_CHIP_GT140_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_SYS_CHIP_GT140);
- if (status2 & WM8350_SYS_WDOG_TO_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_SYS_WDOG_TO);
-
- if (status2 & WM8350_AUXADC_DATARDY_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_AUXADC_DATARDY);
- if (status2 & WM8350_AUXADC_DCOMP4_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_AUXADC_DCOMP4);
- if (status2 & WM8350_AUXADC_DCOMP3_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_AUXADC_DCOMP3);
- if (status2 & WM8350_AUXADC_DCOMP2_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_AUXADC_DCOMP2);
- if (status2 & WM8350_AUXADC_DCOMP1_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_AUXADC_DCOMP1);
-
- if (status2 & WM8350_USB_LIMIT_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_USB_LIMIT);
- }
-
- /* wake, codec, ext */
- if (comp) {
- if (comp & WM8350_WKUP_OFF_STATE_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_WKUP_OFF_STATE);
- if (comp & WM8350_WKUP_HIB_STATE_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_WKUP_HIB_STATE);
- if (comp & WM8350_WKUP_CONV_FAULT_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_WKUP_CONV_FAULT);
- if (comp & WM8350_WKUP_WDOG_RST_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_WKUP_WDOG_RST);
- if (comp & WM8350_WKUP_GP_PWR_ON_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_WKUP_GP_PWR_ON);
- if (comp & WM8350_WKUP_ONKEY_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_WKUP_ONKEY);
- if (comp & WM8350_WKUP_GP_WAKEUP_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_WKUP_GP_WAKEUP);
-
- if (comp & WM8350_CODEC_JCK_DET_L_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CODEC_JCK_DET_L);
- if (comp & WM8350_CODEC_JCK_DET_R_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CODEC_JCK_DET_R);
- if (comp & WM8350_CODEC_MICSCD_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_CODEC_MICSCD);
- if (comp & WM8350_CODEC_MICD_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_CODEC_MICD);
-
- if (comp & WM8350_EXT_USB_FB_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_USB_FB);
- if (comp & WM8350_EXT_WALL_FB_EINT)
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_EXT_WALL_FB);
- if (comp & WM8350_EXT_BAT_FB_EINT)
- wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_BAT_FB);
- }
-
- if (level_one & WM8350_GP_INT) {
- int i;
- u16 gpio;
-
- gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INT_STATUS);
- gpio &= ~wm8350_reg_read(wm8350,
- WM8350_GPIO_INT_STATUS_MASK);
-
- for (i = 0; i < 12; i++) {
- if (gpio & (1 << i))
- wm8350_irq_call_handler(wm8350,
- WM8350_IRQ_GPIO(i));
- }
- }
-
- return IRQ_HANDLED;
-}
-
-int wm8350_register_irq(struct wm8350 *wm8350, int irq,
- void (*handler) (struct wm8350 *, int, void *),
- void *data)
-{
- if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
- return -EINVAL;
-
- if (wm8350->irq[irq].handler)
- return -EBUSY;
-
- mutex_lock(&wm8350->irq_mutex);
- wm8350->irq[irq].handler = handler;
- wm8350->irq[irq].data = data;
- mutex_unlock(&wm8350->irq_mutex);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm8350_register_irq);
-
-int wm8350_free_irq(struct wm8350 *wm8350, int irq)
-{
- if (irq < 0 || irq > WM8350_NUM_IRQ)
- return -EINVAL;
-
- mutex_lock(&wm8350->irq_mutex);
- wm8350->irq[irq].handler = NULL;
- mutex_unlock(&wm8350->irq_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm8350_free_irq);
-
-int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
-{
- switch (irq) {
- case WM8350_IRQ_CHG_BAT_HOT:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_BAT_HOT_EINT);
- case WM8350_IRQ_CHG_BAT_COLD:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_BAT_COLD_EINT);
- case WM8350_IRQ_CHG_BAT_FAIL:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_BAT_FAIL_EINT);
- case WM8350_IRQ_CHG_TO:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_TO_EINT);
- case WM8350_IRQ_CHG_END:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_END_EINT);
- case WM8350_IRQ_CHG_START:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_START_EINT);
- case WM8350_IRQ_CHG_FAST_RDY:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_FAST_RDY_EINT);
- case WM8350_IRQ_RTC_PER:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_RTC_PER_EINT);
- case WM8350_IRQ_RTC_SEC:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_RTC_SEC_EINT);
- case WM8350_IRQ_RTC_ALM:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_RTC_ALM_EINT);
- case WM8350_IRQ_CHG_VBATT_LT_3P9:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_VBATT_LT_3P9_EINT);
- case WM8350_IRQ_CHG_VBATT_LT_3P1:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_VBATT_LT_3P1_EINT);
- case WM8350_IRQ_CHG_VBATT_LT_2P85:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_VBATT_LT_2P85_EINT);
- case WM8350_IRQ_CS1:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_CS1_EINT);
- case WM8350_IRQ_CS2:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_CS2_EINT);
- case WM8350_IRQ_USB_LIMIT:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_USB_LIMIT_EINT);
- case WM8350_IRQ_AUXADC_DATARDY:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DATARDY_EINT);
- case WM8350_IRQ_AUXADC_DCOMP4:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP4_EINT);
- case WM8350_IRQ_AUXADC_DCOMP3:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP3_EINT);
- case WM8350_IRQ_AUXADC_DCOMP2:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP2_EINT);
- case WM8350_IRQ_AUXADC_DCOMP1:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP1_EINT);
- case WM8350_IRQ_SYS_HYST_COMP_FAIL:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
- case WM8350_IRQ_SYS_CHIP_GT115:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_CHIP_GT115_EINT);
- case WM8350_IRQ_SYS_CHIP_GT140:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_CHIP_GT140_EINT);
- case WM8350_IRQ_SYS_WDOG_TO:
- return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_WDOG_TO_EINT);
- case WM8350_IRQ_UV_LDO4:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO4_EINT);
- case WM8350_IRQ_UV_LDO3:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO3_EINT);
- case WM8350_IRQ_UV_LDO2:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO2_EINT);
- case WM8350_IRQ_UV_LDO1:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO1_EINT);
- case WM8350_IRQ_UV_DC6:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC6_EINT);
- case WM8350_IRQ_UV_DC5:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC5_EINT);
- case WM8350_IRQ_UV_DC4:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC4_EINT);
- case WM8350_IRQ_UV_DC3:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC3_EINT);
- case WM8350_IRQ_UV_DC2:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC2_EINT);
- case WM8350_IRQ_UV_DC1:
- return wm8350_set_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC1_EINT);
- case WM8350_IRQ_OC_LS:
- return wm8350_set_bits(wm8350,
- WM8350_OVER_CURRENT_INT_STATUS_MASK,
- WM8350_IM_OC_LS_EINT);
- case WM8350_IRQ_EXT_USB_FB:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_EXT_USB_FB_EINT);
- case WM8350_IRQ_EXT_WALL_FB:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_EXT_WALL_FB_EINT);
- case WM8350_IRQ_EXT_BAT_FB:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_EXT_BAT_FB_EINT);
- case WM8350_IRQ_CODEC_JCK_DET_L:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_JCK_DET_L_EINT);
- case WM8350_IRQ_CODEC_JCK_DET_R:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_JCK_DET_R_EINT);
- case WM8350_IRQ_CODEC_MICSCD:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_MICSCD_EINT);
- case WM8350_IRQ_CODEC_MICD:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_MICD_EINT);
- case WM8350_IRQ_WKUP_OFF_STATE:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_OFF_STATE_EINT);
- case WM8350_IRQ_WKUP_HIB_STATE:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_HIB_STATE_EINT);
- case WM8350_IRQ_WKUP_CONV_FAULT:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_CONV_FAULT_EINT);
- case WM8350_IRQ_WKUP_WDOG_RST:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_OFF_STATE_EINT);
- case WM8350_IRQ_WKUP_GP_PWR_ON:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_GP_PWR_ON_EINT);
- case WM8350_IRQ_WKUP_ONKEY:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_ONKEY_EINT);
- case WM8350_IRQ_WKUP_GP_WAKEUP:
- return wm8350_set_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_GP_WAKEUP_EINT);
- case WM8350_IRQ_GPIO(0):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP0_EINT);
- case WM8350_IRQ_GPIO(1):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP1_EINT);
- case WM8350_IRQ_GPIO(2):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP2_EINT);
- case WM8350_IRQ_GPIO(3):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP3_EINT);
- case WM8350_IRQ_GPIO(4):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP4_EINT);
- case WM8350_IRQ_GPIO(5):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP5_EINT);
- case WM8350_IRQ_GPIO(6):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP6_EINT);
- case WM8350_IRQ_GPIO(7):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP7_EINT);
- case WM8350_IRQ_GPIO(8):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP8_EINT);
- case WM8350_IRQ_GPIO(9):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP9_EINT);
- case WM8350_IRQ_GPIO(10):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP10_EINT);
- case WM8350_IRQ_GPIO(11):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP11_EINT);
- case WM8350_IRQ_GPIO(12):
- return wm8350_set_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP12_EINT);
- default:
- dev_warn(wm8350->dev, "Attempting to mask unknown IRQ %d\n",
- irq);
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm8350_mask_irq);
-
-int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
-{
- switch (irq) {
- case WM8350_IRQ_CHG_BAT_HOT:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_BAT_HOT_EINT);
- case WM8350_IRQ_CHG_BAT_COLD:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_BAT_COLD_EINT);
- case WM8350_IRQ_CHG_BAT_FAIL:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_BAT_FAIL_EINT);
- case WM8350_IRQ_CHG_TO:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_TO_EINT);
- case WM8350_IRQ_CHG_END:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_END_EINT);
- case WM8350_IRQ_CHG_START:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_START_EINT);
- case WM8350_IRQ_CHG_FAST_RDY:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_FAST_RDY_EINT);
- case WM8350_IRQ_RTC_PER:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_RTC_PER_EINT);
- case WM8350_IRQ_RTC_SEC:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_RTC_SEC_EINT);
- case WM8350_IRQ_RTC_ALM:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_RTC_ALM_EINT);
- case WM8350_IRQ_CHG_VBATT_LT_3P9:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_VBATT_LT_3P9_EINT);
- case WM8350_IRQ_CHG_VBATT_LT_3P1:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_VBATT_LT_3P1_EINT);
- case WM8350_IRQ_CHG_VBATT_LT_2P85:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
- WM8350_IM_CHG_VBATT_LT_2P85_EINT);
- case WM8350_IRQ_CS1:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_CS1_EINT);
- case WM8350_IRQ_CS2:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_CS2_EINT);
- case WM8350_IRQ_USB_LIMIT:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_USB_LIMIT_EINT);
- case WM8350_IRQ_AUXADC_DATARDY:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DATARDY_EINT);
- case WM8350_IRQ_AUXADC_DCOMP4:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP4_EINT);
- case WM8350_IRQ_AUXADC_DCOMP3:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP3_EINT);
- case WM8350_IRQ_AUXADC_DCOMP2:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP2_EINT);
- case WM8350_IRQ_AUXADC_DCOMP1:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_AUXADC_DCOMP1_EINT);
- case WM8350_IRQ_SYS_HYST_COMP_FAIL:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
- case WM8350_IRQ_SYS_CHIP_GT115:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_CHIP_GT115_EINT);
- case WM8350_IRQ_SYS_CHIP_GT140:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_CHIP_GT140_EINT);
- case WM8350_IRQ_SYS_WDOG_TO:
- return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
- WM8350_IM_SYS_WDOG_TO_EINT);
- case WM8350_IRQ_UV_LDO4:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO4_EINT);
- case WM8350_IRQ_UV_LDO3:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO3_EINT);
- case WM8350_IRQ_UV_LDO2:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO2_EINT);
- case WM8350_IRQ_UV_LDO1:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_LDO1_EINT);
- case WM8350_IRQ_UV_DC6:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC6_EINT);
- case WM8350_IRQ_UV_DC5:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC5_EINT);
- case WM8350_IRQ_UV_DC4:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC4_EINT);
- case WM8350_IRQ_UV_DC3:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC3_EINT);
- case WM8350_IRQ_UV_DC2:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC2_EINT);
- case WM8350_IRQ_UV_DC1:
- return wm8350_clear_bits(wm8350,
- WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
- WM8350_IM_UV_DC1_EINT);
- case WM8350_IRQ_OC_LS:
- return wm8350_clear_bits(wm8350,
- WM8350_OVER_CURRENT_INT_STATUS_MASK,
- WM8350_IM_OC_LS_EINT);
- case WM8350_IRQ_EXT_USB_FB:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_EXT_USB_FB_EINT);
- case WM8350_IRQ_EXT_WALL_FB:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_EXT_WALL_FB_EINT);
- case WM8350_IRQ_EXT_BAT_FB:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_EXT_BAT_FB_EINT);
- case WM8350_IRQ_CODEC_JCK_DET_L:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_JCK_DET_L_EINT);
- case WM8350_IRQ_CODEC_JCK_DET_R:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_JCK_DET_R_EINT);
- case WM8350_IRQ_CODEC_MICSCD:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_MICSCD_EINT);
- case WM8350_IRQ_CODEC_MICD:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_CODEC_MICD_EINT);
- case WM8350_IRQ_WKUP_OFF_STATE:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_OFF_STATE_EINT);
- case WM8350_IRQ_WKUP_HIB_STATE:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_HIB_STATE_EINT);
- case WM8350_IRQ_WKUP_CONV_FAULT:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_CONV_FAULT_EINT);
- case WM8350_IRQ_WKUP_WDOG_RST:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_OFF_STATE_EINT);
- case WM8350_IRQ_WKUP_GP_PWR_ON:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_GP_PWR_ON_EINT);
- case WM8350_IRQ_WKUP_ONKEY:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_ONKEY_EINT);
- case WM8350_IRQ_WKUP_GP_WAKEUP:
- return wm8350_clear_bits(wm8350,
- WM8350_COMPARATOR_INT_STATUS_MASK,
- WM8350_IM_WKUP_GP_WAKEUP_EINT);
- case WM8350_IRQ_GPIO(0):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP0_EINT);
- case WM8350_IRQ_GPIO(1):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP1_EINT);
- case WM8350_IRQ_GPIO(2):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP2_EINT);
- case WM8350_IRQ_GPIO(3):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP3_EINT);
- case WM8350_IRQ_GPIO(4):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP4_EINT);
- case WM8350_IRQ_GPIO(5):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP5_EINT);
- case WM8350_IRQ_GPIO(6):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP6_EINT);
- case WM8350_IRQ_GPIO(7):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP7_EINT);
- case WM8350_IRQ_GPIO(8):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP8_EINT);
- case WM8350_IRQ_GPIO(9):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP9_EINT);
- case WM8350_IRQ_GPIO(10):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP10_EINT);
- case WM8350_IRQ_GPIO(11):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP11_EINT);
- case WM8350_IRQ_GPIO(12):
- return wm8350_clear_bits(wm8350,
- WM8350_GPIO_INT_STATUS_MASK,
- WM8350_IM_GP12_EINT);
- default:
- dev_warn(wm8350->dev, "Attempting to unmask unknown IRQ %d\n",
- irq);
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
-
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
{
u16 reg, result = 0;
@@ -1264,7 +537,7 @@ static void wm8350_client_dev_register(struct wm8350 *wm8350,
int ret;
*pdev = platform_device_alloc(name, -1);
- if (pdev == NULL) {
+ if (*pdev == NULL) {
dev_err(wm8350->dev, "Failed to allocate %s\n", name);
return;
}
@@ -1409,49 +682,18 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
return ret;
}
- wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
- wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF);
- wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF);
- wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF);
- wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF);
- wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF);
-
mutex_init(&wm8350->auxadc_mutex);
- mutex_init(&wm8350->irq_mutex);
- if (irq) {
- int flags = IRQF_ONESHOT;
-
- if (pdata && pdata->irq_high) {
- flags |= IRQF_TRIGGER_HIGH;
-
- wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
- WM8350_IRQ_POL);
- } else {
- flags |= IRQF_TRIGGER_LOW;
-
- wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
- WM8350_IRQ_POL);
- }
- ret = request_threaded_irq(irq, NULL, wm8350_irq, flags,
- "wm8350", wm8350);
- if (ret != 0) {
- dev_err(wm8350->dev, "Failed to request IRQ: %d\n",
- ret);
- goto err;
- }
- } else {
- dev_err(wm8350->dev, "No IRQ configured\n");
+ ret = wm8350_irq_init(wm8350, irq, pdata);
+ if (ret < 0)
goto err;
- }
- wm8350->chip_irq = irq;
if (pdata && pdata->init) {
ret = pdata->init(wm8350);
if (ret != 0) {
dev_err(wm8350->dev, "Platform init() failed: %d\n",
ret);
- goto err;
+ goto err_irq;
}
}
@@ -1470,6 +712,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
return 0;
+err_irq:
+ wm8350_irq_exit(wm8350);
err:
kfree(wm8350->reg_cache);
return ret;
@@ -1493,7 +737,8 @@ void wm8350_device_exit(struct wm8350 *wm8350)
platform_device_unregister(wm8350->gpio.pdev);
platform_device_unregister(wm8350->codec.pdev);
- free_irq(wm8350->chip_irq, wm8350);
+ wm8350_irq_exit(wm8350);
+
kfree(wm8350->reg_cache);
}
EXPORT_SYMBOL_GPL(wm8350_device_exit);
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
new file mode 100644
index 000000000000..c8df547c4747
--- /dev/null
+++ b/drivers/mfd/wm8350-irq.c
@@ -0,0 +1,529 @@
+/*
+ * wm8350-irq.c -- IRQ support for Wolfson WM8350
+ *
+ * Copyright 2007, 2008, 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood, Mark Brown
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/comparator.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+#define WM8350_NUM_IRQ_REGS 7
+
+#define WM8350_INT_OFFSET_1 0
+#define WM8350_INT_OFFSET_2 1
+#define WM8350_POWER_UP_INT_OFFSET 2
+#define WM8350_UNDER_VOLTAGE_INT_OFFSET 3
+#define WM8350_OVER_CURRENT_INT_OFFSET 4
+#define WM8350_GPIO_INT_OFFSET 5
+#define WM8350_COMPARATOR_INT_OFFSET 6
+
+struct wm8350_irq_data {
+ int primary;
+ int reg;
+ int mask;
+ int primary_only;
+};
+
+static struct wm8350_irq_data wm8350_irqs[] = {
+ [WM8350_IRQ_OC_LS] = {
+ .primary = WM8350_OC_INT,
+ .reg = WM8350_OVER_CURRENT_INT_OFFSET,
+ .mask = WM8350_OC_LS_EINT,
+ .primary_only = 1,
+ },
+ [WM8350_IRQ_UV_DC1] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_DC1_EINT,
+ },
+ [WM8350_IRQ_UV_DC2] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_DC2_EINT,
+ },
+ [WM8350_IRQ_UV_DC3] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_DC3_EINT,
+ },
+ [WM8350_IRQ_UV_DC4] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_DC4_EINT,
+ },
+ [WM8350_IRQ_UV_DC5] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_DC5_EINT,
+ },
+ [WM8350_IRQ_UV_DC6] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_DC6_EINT,
+ },
+ [WM8350_IRQ_UV_LDO1] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_LDO1_EINT,
+ },
+ [WM8350_IRQ_UV_LDO2] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_LDO2_EINT,
+ },
+ [WM8350_IRQ_UV_LDO3] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_LDO3_EINT,
+ },
+ [WM8350_IRQ_UV_LDO4] = {
+ .primary = WM8350_UV_INT,
+ .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+ .mask = WM8350_UV_LDO4_EINT,
+ },
+ [WM8350_IRQ_CHG_BAT_HOT] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_BAT_HOT_EINT,
+ },
+ [WM8350_IRQ_CHG_BAT_COLD] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_BAT_COLD_EINT,
+ },
+ [WM8350_IRQ_CHG_BAT_FAIL] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_BAT_FAIL_EINT,
+ },
+ [WM8350_IRQ_CHG_TO] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_TO_EINT,
+ },
+ [WM8350_IRQ_CHG_END] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_END_EINT,
+ },
+ [WM8350_IRQ_CHG_START] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_START_EINT,
+ },
+ [WM8350_IRQ_CHG_FAST_RDY] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_FAST_RDY_EINT,
+ },
+ [WM8350_IRQ_CHG_VBATT_LT_3P9] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_VBATT_LT_3P9_EINT,
+ },
+ [WM8350_IRQ_CHG_VBATT_LT_3P1] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_VBATT_LT_3P1_EINT,
+ },
+ [WM8350_IRQ_CHG_VBATT_LT_2P85] = {
+ .primary = WM8350_CHG_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_CHG_VBATT_LT_2P85_EINT,
+ },
+ [WM8350_IRQ_RTC_ALM] = {
+ .primary = WM8350_RTC_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_RTC_ALM_EINT,
+ },
+ [WM8350_IRQ_RTC_SEC] = {
+ .primary = WM8350_RTC_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_RTC_SEC_EINT,
+ },
+ [WM8350_IRQ_RTC_PER] = {
+ .primary = WM8350_RTC_INT,
+ .reg = WM8350_INT_OFFSET_1,
+ .mask = WM8350_RTC_PER_EINT,
+ },
+ [WM8350_IRQ_CS1] = {
+ .primary = WM8350_CS_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_CS1_EINT,
+ },
+ [WM8350_IRQ_CS2] = {
+ .primary = WM8350_CS_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_CS2_EINT,
+ },
+ [WM8350_IRQ_SYS_HYST_COMP_FAIL] = {
+ .primary = WM8350_SYS_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_SYS_HYST_COMP_FAIL_EINT,
+ },
+ [WM8350_IRQ_SYS_CHIP_GT115] = {
+ .primary = WM8350_SYS_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_SYS_CHIP_GT115_EINT,
+ },
+ [WM8350_IRQ_SYS_CHIP_GT140] = {
+ .primary = WM8350_SYS_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_SYS_CHIP_GT140_EINT,
+ },
+ [WM8350_IRQ_SYS_WDOG_TO] = {
+ .primary = WM8350_SYS_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_SYS_WDOG_TO_EINT,
+ },
+ [WM8350_IRQ_AUXADC_DATARDY] = {
+ .primary = WM8350_AUXADC_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_AUXADC_DATARDY_EINT,
+ },
+ [WM8350_IRQ_AUXADC_DCOMP4] = {
+ .primary = WM8350_AUXADC_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_AUXADC_DCOMP4_EINT,
+ },
+ [WM8350_IRQ_AUXADC_DCOMP3] = {
+ .primary = WM8350_AUXADC_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_AUXADC_DCOMP3_EINT,
+ },
+ [WM8350_IRQ_AUXADC_DCOMP2] = {
+ .primary = WM8350_AUXADC_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_AUXADC_DCOMP2_EINT,
+ },
+ [WM8350_IRQ_AUXADC_DCOMP1] = {
+ .primary = WM8350_AUXADC_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_AUXADC_DCOMP1_EINT,
+ },
+ [WM8350_IRQ_USB_LIMIT] = {
+ .primary = WM8350_USB_INT,
+ .reg = WM8350_INT_OFFSET_2,
+ .mask = WM8350_USB_LIMIT_EINT,
+ .primary_only = 1,
+ },
+ [WM8350_IRQ_WKUP_OFF_STATE] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_OFF_STATE_EINT,
+ },
+ [WM8350_IRQ_WKUP_HIB_STATE] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_HIB_STATE_EINT,
+ },
+ [WM8350_IRQ_WKUP_CONV_FAULT] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_CONV_FAULT_EINT,
+ },
+ [WM8350_IRQ_WKUP_WDOG_RST] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_WDOG_RST_EINT,
+ },
+ [WM8350_IRQ_WKUP_GP_PWR_ON] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_GP_PWR_ON_EINT,
+ },
+ [WM8350_IRQ_WKUP_ONKEY] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_ONKEY_EINT,
+ },
+ [WM8350_IRQ_WKUP_GP_WAKEUP] = {
+ .primary = WM8350_WKUP_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_WKUP_GP_WAKEUP_EINT,
+ },
+ [WM8350_IRQ_CODEC_JCK_DET_L] = {
+ .primary = WM8350_CODEC_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_CODEC_JCK_DET_L_EINT,
+ },
+ [WM8350_IRQ_CODEC_JCK_DET_R] = {
+ .primary = WM8350_CODEC_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_CODEC_JCK_DET_R_EINT,
+ },
+ [WM8350_IRQ_CODEC_MICSCD] = {
+ .primary = WM8350_CODEC_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_CODEC_MICSCD_EINT,
+ },
+ [WM8350_IRQ_CODEC_MICD] = {
+ .primary = WM8350_CODEC_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_CODEC_MICD_EINT,
+ },
+ [WM8350_IRQ_EXT_USB_FB] = {
+ .primary = WM8350_EXT_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_EXT_USB_FB_EINT,
+ },
+ [WM8350_IRQ_EXT_WALL_FB] = {
+ .primary = WM8350_EXT_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_EXT_WALL_FB_EINT,
+ },
+ [WM8350_IRQ_EXT_BAT_FB] = {
+ .primary = WM8350_EXT_INT,
+ .reg = WM8350_COMPARATOR_INT_OFFSET,
+ .mask = WM8350_EXT_BAT_FB_EINT,
+ },
+ [WM8350_IRQ_GPIO(0)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP0_EINT,
+ },
+ [WM8350_IRQ_GPIO(1)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP1_EINT,
+ },
+ [WM8350_IRQ_GPIO(2)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP2_EINT,
+ },
+ [WM8350_IRQ_GPIO(3)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP3_EINT,
+ },
+ [WM8350_IRQ_GPIO(4)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP4_EINT,
+ },
+ [WM8350_IRQ_GPIO(5)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP5_EINT,
+ },
+ [WM8350_IRQ_GPIO(6)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP6_EINT,
+ },
+ [WM8350_IRQ_GPIO(7)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP7_EINT,
+ },
+ [WM8350_IRQ_GPIO(8)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP8_EINT,
+ },
+ [WM8350_IRQ_GPIO(9)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP9_EINT,
+ },
+ [WM8350_IRQ_GPIO(10)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP10_EINT,
+ },
+ [WM8350_IRQ_GPIO(11)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP11_EINT,
+ },
+ [WM8350_IRQ_GPIO(12)] = {
+ .primary = WM8350_GP_INT,
+ .reg = WM8350_GPIO_INT_OFFSET,
+ .mask = WM8350_GP12_EINT,
+ },
+};
+
+static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
+{
+ mutex_lock(&wm8350->irq_mutex);
+
+ if (wm8350->irq[irq].handler)
+ wm8350->irq[irq].handler(irq, wm8350->irq[irq].data);
+ else {
+ dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
+ irq);
+ wm8350_mask_irq(wm8350, irq);
+ }
+
+ mutex_unlock(&wm8350->irq_mutex);
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI. Since all
+ * interrupts are clear on read the IRQ line will be reasserted and
+ * the physical IRQ will be handled again if another interrupt is
+ * asserted while we run - in the normal course of events this is a
+ * rare occurrence so we save I2C/SPI reads.
+ */
+static irqreturn_t wm8350_irq(int irq, void *irq_data)
+{
+ struct wm8350 *wm8350 = irq_data;
+ u16 level_one;
+ u16 sub_reg[WM8350_NUM_IRQ_REGS];
+ int read_done[WM8350_NUM_IRQ_REGS];
+ struct wm8350_irq_data *data;
+ int i;
+
+ /* TODO: Use block reads to improve performance? */
+ level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
+ & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
+
+ if (!level_one)
+ return IRQ_NONE;
+
+ memset(&read_done, 0, sizeof(read_done));
+
+ for (i = 0; i < ARRAY_SIZE(wm8350_irqs); i++) {
+ data = &wm8350_irqs[i];
+
+ if (!(level_one & data->primary))
+ continue;
+
+ if (!read_done[data->reg]) {
+ sub_reg[data->reg] =
+ wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 +
+ data->reg);
+ sub_reg[data->reg] &=
+ ~wm8350_reg_read(wm8350,
+ WM8350_INT_STATUS_1_MASK +
+ data->reg);
+ read_done[data->reg] = 1;
+ }
+
+ if (sub_reg[data->reg] & data->mask)
+ wm8350_irq_call_handler(wm8350, i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int wm8350_register_irq(struct wm8350 *wm8350, int irq,
+ irq_handler_t handler, unsigned long flags,
+ const char *name, void *data)
+{
+ if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
+ return -EINVAL;
+
+ if (wm8350->irq[irq].handler)
+ return -EBUSY;
+
+ mutex_lock(&wm8350->irq_mutex);
+ wm8350->irq[irq].handler = handler;
+ wm8350->irq[irq].data = data;
+ mutex_unlock(&wm8350->irq_mutex);
+
+ wm8350_unmask_irq(wm8350, irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_register_irq);
+
+int wm8350_free_irq(struct wm8350 *wm8350, int irq)
+{
+ if (irq < 0 || irq > WM8350_NUM_IRQ)
+ return -EINVAL;
+
+ wm8350_mask_irq(wm8350, irq);
+
+ mutex_lock(&wm8350->irq_mutex);
+ wm8350->irq[irq].handler = NULL;
+ mutex_unlock(&wm8350->irq_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_free_irq);
+
+int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
+{
+ return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK +
+ wm8350_irqs[irq].reg,
+ wm8350_irqs[irq].mask);
+}
+EXPORT_SYMBOL_GPL(wm8350_mask_irq);
+
+int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
+{
+ return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK +
+ wm8350_irqs[irq].reg,
+ wm8350_irqs[irq].mask);
+}
+EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
+
+int wm8350_irq_init(struct wm8350 *wm8350, int irq,
+ struct wm8350_platform_data *pdata)
+{
+ int ret;
+ int flags = IRQF_ONESHOT;
+
+ if (!irq) {
+ dev_err(wm8350->dev, "No IRQ configured\n");
+ return -EINVAL;
+ }
+
+ wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
+ wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF);
+ wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF);
+ wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF);
+ wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF);
+ wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF);
+
+ mutex_init(&wm8350->irq_mutex);
+ wm8350->chip_irq = irq;
+
+ if (pdata && pdata->irq_high) {
+ flags |= IRQF_TRIGGER_HIGH;
+
+ wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
+ WM8350_IRQ_POL);
+ } else {
+ flags |= IRQF_TRIGGER_LOW;
+
+ wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
+ WM8350_IRQ_POL);
+ }
+
+ ret = request_threaded_irq(irq, NULL, wm8350_irq, flags,
+ "wm8350", wm8350);
+ if (ret != 0)
+ dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret);
+
+ return ret;
+}
+
+int wm8350_irq_exit(struct wm8350 *wm8350)
+{
+ free_irq(wm8350->chip_irq, wm8350);
+ return 0;
+}
diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c
index 7ccc1eab98ab..e965139e5cd5 100644
--- a/drivers/mfd/wm8350-regmap.c
+++ b/drivers/mfd/wm8350-regmap.c
@@ -3170,14 +3170,6 @@ const u16 wm8352_mode3_defaults[] = {
};
#endif
-/* The register defaults for the config mode used must be compiled in but
- * due to the impact on kernel size it is possible to disable
- */
-#ifndef WM8350_HAVE_CONFIG_MODE
-#warning No WM8350 config modes supported - select at least one of the
-#warning MFD_WM8350_CONFIG_MODE_n options from the board driver.
-#endif
-
/*
* Access masks.
*/
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2c16ca6501d5..59f4ba1b7034 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -13,6 +13,20 @@ menuconfig MISC_DEVICES
if MISC_DEVICES
+config AD525X_DPOT
+ tristate "Analog Devices AD525x Digital Potentiometers"
+ depends on I2C && SYSFS
+ help
+ If you say yes here, you get support for the Analog Devices
+ AD5258, AD5259, AD5251, AD5252, AD5253, AD5254 and AD5255
+ digital potentiometer chips.
+
+ See Documentation/misc-devices/ad525x_dpot.txt for the
+ userspace interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called ad525x_dpot.
+
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
@@ -173,6 +187,30 @@ config SGI_XP
this feature will allow for direct communication between SSIs
based on a network adapter and DMA messaging.
+config CS5535_MFGPT
+ tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
+ depends on PCI
+ depends on X86
+ default n
+ help
+ This driver provides access to MFGPT functionality for other
+ drivers that need timers. MFGPTs are available in the CS5535 and
+ CS5536 companion chips that are found in AMD Geode and several
+ other platforms. They have a better resolution and max interval
+ than the generic PIT, and are suitable for use as high-res timers.
+ You probably don't want to enable this manually; other drivers that
+ make use of it should enable it.
+
+config CS5535_MFGPT_DEFAULT_IRQ
+ int
+ default 7
+ help
+ MFGPTs on the CS5535 require an interrupt. The selected IRQ
+ can be overridden as a module option as well as by driver that
+ use the cs5535_mfgpt_ API; however, different architectures might
+ want to use a different IRQ by default. This is here for
+ architectures to set as necessary.
+
config HP_ILO
tristate "Channel interface driver for HP iLO/iLO2 processor"
depends on PCI
@@ -256,6 +294,16 @@ config DS1682
This driver can also be built as a module. If so, the module
will be called ds1682.
+config TI_DAC7512
+ tristate "Texas Instruments DAC7512"
+ depends on SPI && SYSFS
+ help
+ If you say yes here you get support for the Texas Instruments
+ DAC7512 16-bit digital-to-analog converter.
+
+ This driver can also be built as a module. If so, the module
+ will be calles ti_dac7512.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 906a0edcea40..049ff2482f30 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
+obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
@@ -17,10 +18,12 @@ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
obj-$(CONFIG_SGI_XP) += sgi-xp/
obj-$(CONFIG_SGI_GRU) += sgi-gru/
+obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_DS1682) += ds1682.o
+obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
new file mode 100644
index 000000000000..30a59f2bacd2
--- /dev/null
+++ b/drivers/misc/ad525x_dpot.c
@@ -0,0 +1,666 @@
+/*
+ * ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers
+ * Copyright (c) 2009 Analog Devices, Inc.
+ * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
+ *
+ * DEVID #Wipers #Positions Resistor Options (kOhm)
+ * AD5258 1 64 1, 10, 50, 100
+ * AD5259 1 256 5, 10, 50, 100
+ * AD5251 2 64 1, 10, 50, 100
+ * AD5252 2 256 1, 10, 50, 100
+ * AD5255 3 512 25, 250
+ * AD5253 4 64 1, 10, 50, 100
+ * AD5254 4 256 1, 10, 50, 100
+ *
+ * See Documentation/misc-devices/ad525x_dpot.txt for more info.
+ *
+ * derived from ad5258.c
+ * Copyright (c) 2009 Cyber Switching, Inc.
+ * Author: Chris Verges <chrisv@cyberswitching.com>
+ *
+ * derived from ad5252.c
+ * Copyright (c) 2006 Michael Hennerich <hennerich@blackfin.uclinux.org>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#define DRIVER_NAME "ad525x_dpot"
+#define DRIVER_VERSION "0.1"
+
+enum dpot_devid {
+ AD5258_ID,
+ AD5259_ID,
+ AD5251_ID,
+ AD5252_ID,
+ AD5253_ID,
+ AD5254_ID,
+ AD5255_ID,
+};
+
+#define AD5258_MAX_POSITION 64
+#define AD5259_MAX_POSITION 256
+#define AD5251_MAX_POSITION 64
+#define AD5252_MAX_POSITION 256
+#define AD5253_MAX_POSITION 64
+#define AD5254_MAX_POSITION 256
+#define AD5255_MAX_POSITION 512
+
+#define AD525X_RDAC0 0
+#define AD525X_RDAC1 1
+#define AD525X_RDAC2 2
+#define AD525X_RDAC3 3
+
+#define AD525X_REG_TOL 0x18
+#define AD525X_TOL_RDAC0 (AD525X_REG_TOL | AD525X_RDAC0)
+#define AD525X_TOL_RDAC1 (AD525X_REG_TOL | AD525X_RDAC1)
+#define AD525X_TOL_RDAC2 (AD525X_REG_TOL | AD525X_RDAC2)
+#define AD525X_TOL_RDAC3 (AD525X_REG_TOL | AD525X_RDAC3)
+
+/* RDAC-to-EEPROM Interface Commands */
+#define AD525X_I2C_RDAC (0x00 << 5)
+#define AD525X_I2C_EEPROM (0x01 << 5)
+#define AD525X_I2C_CMD (0x80)
+
+#define AD525X_DEC_ALL_6DB (AD525X_I2C_CMD | (0x4 << 3))
+#define AD525X_INC_ALL_6DB (AD525X_I2C_CMD | (0x9 << 3))
+#define AD525X_DEC_ALL (AD525X_I2C_CMD | (0x6 << 3))
+#define AD525X_INC_ALL (AD525X_I2C_CMD | (0xB << 3))
+
+static s32 ad525x_read(struct i2c_client *client, u8 reg);
+static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value);
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct dpot_data {
+ struct mutex update_lock;
+ unsigned rdac_mask;
+ unsigned max_pos;
+ unsigned devid;
+};
+
+/* sysfs functions */
+
+static ssize_t sysfs_show_reg(struct device *dev,
+ struct device_attribute *attr, char *buf, u32 reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct dpot_data *data = i2c_get_clientdata(client);
+ s32 value;
+
+ mutex_lock(&data->update_lock);
+ value = ad525x_read(client, reg);
+ mutex_unlock(&data->update_lock);
+
+ if (value < 0)
+ return -EINVAL;
+ /*
+ * Let someone else deal with converting this ...
+ * the tolerance is a two-byte value where the MSB
+ * is a sign + integer value, and the LSB is a
+ * decimal value. See page 18 of the AD5258
+ * datasheet (Rev. A) for more details.
+ */
+
+ if (reg & AD525X_REG_TOL)
+ return sprintf(buf, "0x%04x\n", value & 0xFFFF);
+ else
+ return sprintf(buf, "%u\n", value & data->rdac_mask);
+}
+
+static ssize_t sysfs_set_reg(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count, u32 reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct dpot_data *data = i2c_get_clientdata(client);
+ unsigned long value;
+ int err;
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err)
+ return err;
+
+ if (value > data->rdac_mask)
+ value = data->rdac_mask;
+
+ mutex_lock(&data->update_lock);
+ ad525x_write(client, reg, value);
+ if (reg & AD525X_I2C_EEPROM)
+ msleep(26); /* Sleep while the EEPROM updates */
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t sysfs_do_cmd(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count, u32 reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct dpot_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+ ad525x_write(client, reg, 0);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac0(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC0);
+}
+
+static ssize_t set_rdac0(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_RDAC | AD525X_RDAC0);
+}
+
+static DEVICE_ATTR(rdac0, S_IWUSR | S_IRUGO, show_rdac0, set_rdac0);
+
+static ssize_t show_eeprom0(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC0);
+}
+
+static ssize_t set_eeprom0(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_EEPROM | AD525X_RDAC0);
+}
+
+static DEVICE_ATTR(eeprom0, S_IWUSR | S_IRUGO, show_eeprom0, set_eeprom0);
+
+static ssize_t show_tolerance0(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf,
+ AD525X_I2C_EEPROM | AD525X_TOL_RDAC0);
+}
+
+static DEVICE_ATTR(tolerance0, S_IRUGO, show_tolerance0, NULL);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC1);
+}
+
+static ssize_t set_rdac1(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_RDAC | AD525X_RDAC1);
+}
+
+static DEVICE_ATTR(rdac1, S_IWUSR | S_IRUGO, show_rdac1, set_rdac1);
+
+static ssize_t show_eeprom1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC1);
+}
+
+static ssize_t set_eeprom1(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_EEPROM | AD525X_RDAC1);
+}
+
+static DEVICE_ATTR(eeprom1, S_IWUSR | S_IRUGO, show_eeprom1, set_eeprom1);
+
+static ssize_t show_tolerance1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf,
+ AD525X_I2C_EEPROM | AD525X_TOL_RDAC1);
+}
+
+static DEVICE_ATTR(tolerance1, S_IRUGO, show_tolerance1, NULL);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC2);
+}
+
+static ssize_t set_rdac2(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_RDAC | AD525X_RDAC2);
+}
+
+static DEVICE_ATTR(rdac2, S_IWUSR | S_IRUGO, show_rdac2, set_rdac2);
+
+static ssize_t show_eeprom2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC2);
+}
+
+static ssize_t set_eeprom2(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_EEPROM | AD525X_RDAC2);
+}
+
+static DEVICE_ATTR(eeprom2, S_IWUSR | S_IRUGO, show_eeprom2, set_eeprom2);
+
+static ssize_t show_tolerance2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf,
+ AD525X_I2C_EEPROM | AD525X_TOL_RDAC2);
+}
+
+static DEVICE_ATTR(tolerance2, S_IRUGO, show_tolerance2, NULL);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac3(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC3);
+}
+
+static ssize_t set_rdac3(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_RDAC | AD525X_RDAC3);
+}
+
+static DEVICE_ATTR(rdac3, S_IWUSR | S_IRUGO, show_rdac3, set_rdac3);
+
+static ssize_t show_eeprom3(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC3);
+}
+
+static ssize_t set_eeprom3(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_set_reg(dev, attr, buf, count,
+ AD525X_I2C_EEPROM | AD525X_RDAC3);
+}
+
+static DEVICE_ATTR(eeprom3, S_IWUSR | S_IRUGO, show_eeprom3, set_eeprom3);
+
+static ssize_t show_tolerance3(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_show_reg(dev, attr, buf,
+ AD525X_I2C_EEPROM | AD525X_TOL_RDAC3);
+}
+
+static DEVICE_ATTR(tolerance3, S_IRUGO, show_tolerance3, NULL);
+
+static struct attribute *ad525x_attributes_wipers[4][4] = {
+ {
+ &dev_attr_rdac0.attr,
+ &dev_attr_eeprom0.attr,
+ &dev_attr_tolerance0.attr,
+ NULL
+ }, {
+ &dev_attr_rdac1.attr,
+ &dev_attr_eeprom1.attr,
+ &dev_attr_tolerance1.attr,
+ NULL
+ }, {
+ &dev_attr_rdac2.attr,
+ &dev_attr_eeprom2.attr,
+ &dev_attr_tolerance2.attr,
+ NULL
+ }, {
+ &dev_attr_rdac3.attr,
+ &dev_attr_eeprom3.attr,
+ &dev_attr_tolerance3.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group ad525x_group_wipers[] = {
+ {.attrs = ad525x_attributes_wipers[AD525X_RDAC0]},
+ {.attrs = ad525x_attributes_wipers[AD525X_RDAC1]},
+ {.attrs = ad525x_attributes_wipers[AD525X_RDAC2]},
+ {.attrs = ad525x_attributes_wipers[AD525X_RDAC3]},
+};
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t set_inc_all(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL);
+}
+
+static DEVICE_ATTR(inc_all, S_IWUSR, NULL, set_inc_all);
+
+static ssize_t set_dec_all(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL);
+}
+
+static DEVICE_ATTR(dec_all, S_IWUSR, NULL, set_dec_all);
+
+static ssize_t set_inc_all_6db(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL_6DB);
+}
+
+static DEVICE_ATTR(inc_all_6db, S_IWUSR, NULL, set_inc_all_6db);
+
+static ssize_t set_dec_all_6db(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL_6DB);
+}
+
+static DEVICE_ATTR(dec_all_6db, S_IWUSR, NULL, set_dec_all_6db);
+
+static struct attribute *ad525x_attributes_commands[] = {
+ &dev_attr_inc_all.attr,
+ &dev_attr_dec_all.attr,
+ &dev_attr_inc_all_6db.attr,
+ &dev_attr_dec_all_6db.attr,
+ NULL
+};
+
+static const struct attribute_group ad525x_group_commands = {
+ .attrs = ad525x_attributes_commands,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* i2c device functions */
+
+/**
+ * ad525x_read - return the value contained in the specified register
+ * on the AD5258 device.
+ * @client: value returned from i2c_new_device()
+ * @reg: the register to read
+ *
+ * If the tolerance register is specified, 2 bytes are returned.
+ * Otherwise, 1 byte is returned. A negative value indicates an error
+ * occurred while reading the register.
+ */
+static s32 ad525x_read(struct i2c_client *client, u8 reg)
+{
+ struct dpot_data *data = i2c_get_clientdata(client);
+
+ if ((reg & AD525X_REG_TOL) || (data->max_pos > 256))
+ return i2c_smbus_read_word_data(client, (reg & 0xF8) |
+ ((reg & 0x7) << 1));
+ else
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/**
+ * ad525x_write - store the given value in the specified register on
+ * the AD5258 device.
+ * @client: value returned from i2c_new_device()
+ * @reg: the register to write
+ * @value: the byte to store in the register
+ *
+ * For certain instructions that do not require a data byte, "NULL"
+ * should be specified for the "value" parameter. These instructions
+ * include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM.
+ *
+ * A negative return value indicates an error occurred while reading
+ * the register.
+ */
+static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ struct dpot_data *data = i2c_get_clientdata(client);
+
+ /* Only write the instruction byte for certain commands */
+ if (reg & AD525X_I2C_CMD)
+ return i2c_smbus_write_byte(client, reg);
+
+ if (data->max_pos > 256)
+ return i2c_smbus_write_word_data(client, (reg & 0xF8) |
+ ((reg & 0x7) << 1), value);
+ else
+ /* All other registers require instruction + data bytes */
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int ad525x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct dpot_data *data;
+ int err = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
+ dev_err(dev, "missing I2C functionality for this driver\n");
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ switch (id->driver_data) {
+ case AD5258_ID:
+ data->max_pos = AD5258_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ break;
+ case AD5259_ID:
+ data->max_pos = AD5259_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ break;
+ case AD5251_ID:
+ data->max_pos = AD5251_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC3]);
+ err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ case AD5252_ID:
+ data->max_pos = AD5252_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC3]);
+ err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ case AD5253_ID:
+ data->max_pos = AD5253_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC2]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC3]);
+ err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ case AD5254_ID:
+ data->max_pos = AD5254_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC2]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC3]);
+ err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ case AD5255_ID:
+ data->max_pos = AD5255_MAX_POSITION;
+ err = sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ err |= sysfs_create_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC2]);
+ err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ default:
+ err = -ENODEV;
+ goto exit_free;
+ }
+
+ if (err) {
+ dev_err(dev, "failed to register sysfs hooks\n");
+ goto exit_free;
+ }
+
+ data->devid = id->driver_data;
+ data->rdac_mask = data->max_pos - 1;
+
+ dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
+ id->name, data->max_pos);
+
+ return 0;
+
+exit_free:
+ kfree(data);
+ i2c_set_clientdata(client, NULL);
+exit:
+ dev_err(dev, "failed to create client\n");
+ return err;
+}
+
+static int __devexit ad525x_remove(struct i2c_client *client)
+{
+ struct dpot_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+
+ switch (data->devid) {
+ case AD5258_ID:
+ case AD5259_ID:
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ break;
+ case AD5251_ID:
+ case AD5252_ID:
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC3]);
+ sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ case AD5253_ID:
+ case AD5254_ID:
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC2]);
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC3]);
+ sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ case AD5255_ID:
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC0]);
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC1]);
+ sysfs_remove_group(&dev->kobj,
+ &ad525x_group_wipers[AD525X_RDAC2]);
+ sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
+ break;
+ }
+
+ i2c_set_clientdata(client, NULL);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad525x_idtable[] = {
+ {"ad5258", AD5258_ID},
+ {"ad5259", AD5259_ID},
+ {"ad5251", AD5251_ID},
+ {"ad5252", AD5252_ID},
+ {"ad5253", AD5253_ID},
+ {"ad5254", AD5254_ID},
+ {"ad5255", AD5255_ID},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad525x_idtable);
+
+static struct i2c_driver ad525x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+ .id_table = ad525x_idtable,
+ .probe = ad525x_probe,
+ .remove = __devexit_p(ad525x_remove),
+};
+
+static int __init ad525x_init(void)
+{
+ return i2c_add_driver(&ad525x_driver);
+}
+
+module_init(ad525x_init);
+
+static void __exit ad525x_exit(void)
+{
+ i2c_del_driver(&ad525x_driver);
+}
+
+module_exit(ad525x_exit);
+
+MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, "
+ "Michael Hennerich <hennerich@blackfin.uclinux.org>, ");
+MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c
new file mode 100644
index 000000000000..8110460558ff
--- /dev/null
+++ b/drivers/misc/cs5535-mfgpt.c
@@ -0,0 +1,370 @@
+/*
+ * Driver for the CS5535/CS5536 Multi-Function General Purpose Timers (MFGPT)
+ *
+ * Copyright (C) 2006, Advanced Micro Devices, Inc.
+ * Copyright (C) 2007 Andres Salomon <dilinger@debian.org>
+ * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/cs5535.h>
+
+#define DRV_NAME "cs5535-mfgpt"
+#define MFGPT_BAR 2
+
+static int mfgpt_reset_timers;
+module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644);
+MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; "
+ "required by some broken BIOSes (ie, TinyBIOS < 0.99).");
+
+struct cs5535_mfgpt_timer {
+ struct cs5535_mfgpt_chip *chip;
+ int nr;
+};
+
+static struct cs5535_mfgpt_chip {
+ DECLARE_BITMAP(avail, MFGPT_MAX_TIMERS);
+ resource_size_t base;
+
+ struct pci_dev *pdev;
+ spinlock_t lock;
+ int initialized;
+} cs5535_mfgpt_chip;
+
+int cs5535_mfgpt_toggle_event(struct cs5535_mfgpt_timer *timer, int cmp,
+ int event, int enable)
+{
+ uint32_t msr, mask, value, dummy;
+ int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
+
+ if (!timer) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ /*
+ * The register maps for these are described in sections 6.17.1.x of
+ * the AMD Geode CS5536 Companion Device Data Book.
+ */
+ switch (event) {
+ case MFGPT_EVENT_RESET:
+ /*
+ * XXX: According to the docs, we cannot reset timers above
+ * 6; that is, resets for 7 and 8 will be ignored. Is this
+ * a problem? -dilinger
+ */
+ msr = MSR_MFGPT_NR;
+ mask = 1 << (timer->nr + 24);
+ break;
+
+ case MFGPT_EVENT_NMI:
+ msr = MSR_MFGPT_NR;
+ mask = 1 << (timer->nr + shift);
+ break;
+
+ case MFGPT_EVENT_IRQ:
+ msr = MSR_MFGPT_IRQ;
+ mask = 1 << (timer->nr + shift);
+ break;
+
+ default:
+ return -EIO;
+ }
+
+ rdmsr(msr, value, dummy);
+
+ if (enable)
+ value |= mask;
+ else
+ value &= ~mask;
+
+ wrmsr(msr, value, dummy);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_toggle_event);
+
+int cs5535_mfgpt_set_irq(struct cs5535_mfgpt_timer *timer, int cmp, int *irq,
+ int enable)
+{
+ uint32_t zsel, lpc, dummy;
+ int shift;
+
+ if (!timer) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ /*
+ * Unfortunately, MFGPTs come in pairs sharing their IRQ lines. If VSA
+ * is using the same CMP of the timer's Siamese twin, the IRQ is set to
+ * 2, and we mustn't use nor change it.
+ * XXX: Likewise, 2 Linux drivers might clash if the 2nd overwrites the
+ * IRQ of the 1st. This can only happen if forcing an IRQ, calling this
+ * with *irq==0 is safe. Currently there _are_ no 2 drivers.
+ */
+ rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
+ shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer->nr % 4) * 4;
+ if (((zsel >> shift) & 0xF) == 2)
+ return -EIO;
+
+ /* Choose IRQ: if none supplied, keep IRQ already set or use default */
+ if (!*irq)
+ *irq = (zsel >> shift) & 0xF;
+ if (!*irq)
+ *irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
+
+ /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */
+ if (*irq < 1 || *irq == 2 || *irq > 15)
+ return -EIO;
+ rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy);
+ if (lpc & (1 << *irq))
+ return -EIO;
+
+ /* All chosen and checked - go for it */
+ if (cs5535_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
+ return -EIO;
+ if (enable) {
+ zsel = (zsel & ~(0xF << shift)) | (*irq << shift);
+ wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_set_irq);
+
+struct cs5535_mfgpt_timer *cs5535_mfgpt_alloc_timer(int timer_nr, int domain)
+{
+ struct cs5535_mfgpt_chip *mfgpt = &cs5535_mfgpt_chip;
+ struct cs5535_mfgpt_timer *timer = NULL;
+ unsigned long flags;
+ int max;
+
+ if (!mfgpt->initialized)
+ goto done;
+
+ /* only allocate timers from the working domain if requested */
+ if (domain == MFGPT_DOMAIN_WORKING)
+ max = 6;
+ else
+ max = MFGPT_MAX_TIMERS;
+
+ if (timer_nr >= max) {
+ /* programmer error. silly programmers! */
+ WARN_ON(1);
+ goto done;
+ }
+
+ spin_lock_irqsave(&mfgpt->lock, flags);
+ if (timer_nr < 0) {
+ unsigned long t;
+
+ /* try to find any available timer */
+ t = find_first_bit(mfgpt->avail, max);
+ /* set timer_nr to -1 if no timers available */
+ timer_nr = t < max ? (int) t : -1;
+ } else {
+ /* check if the requested timer's available */
+ if (test_bit(timer_nr, mfgpt->avail))
+ timer_nr = -1;
+ }
+
+ if (timer_nr >= 0)
+ /* if timer_nr is not -1, it's an available timer */
+ __clear_bit(timer_nr, mfgpt->avail);
+ spin_unlock_irqrestore(&mfgpt->lock, flags);
+
+ if (timer_nr < 0)
+ goto done;
+
+ timer = kmalloc(sizeof(*timer), GFP_KERNEL);
+ if (!timer) {
+ /* aw hell */
+ spin_lock_irqsave(&mfgpt->lock, flags);
+ __set_bit(timer_nr, mfgpt->avail);
+ spin_unlock_irqrestore(&mfgpt->lock, flags);
+ goto done;
+ }
+ timer->chip = mfgpt;
+ timer->nr = timer_nr;
+ dev_info(&mfgpt->pdev->dev, "registered timer %d\n", timer_nr);
+
+done:
+ return timer;
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer);
+
+/*
+ * XXX: This frees the timer memory, but never resets the actual hardware
+ * timer. The old geode_mfgpt code did this; it would be good to figure
+ * out a way to actually release the hardware timer. See comments below.
+ */
+void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer)
+{
+ kfree(timer);
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer);
+
+uint16_t cs5535_mfgpt_read(struct cs5535_mfgpt_timer *timer, uint16_t reg)
+{
+ return inw(timer->chip->base + reg + (timer->nr * 8));
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_read);
+
+void cs5535_mfgpt_write(struct cs5535_mfgpt_timer *timer, uint16_t reg,
+ uint16_t value)
+{
+ outw(value, timer->chip->base + reg + (timer->nr * 8));
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_write);
+
+/*
+ * This is a sledgehammer that resets all MFGPT timers. This is required by
+ * some broken BIOSes which leave the system in an unstable state
+ * (TinyBIOS 0.98, for example; fixed in 0.99). It's uncertain as to
+ * whether or not this secret MSR can be used to release individual timers.
+ * Jordan tells me that he and Mitch once played w/ it, but it's unclear
+ * what the results of that were (and they experienced some instability).
+ */
+static void __init reset_all_timers(void)
+{
+ uint32_t val, dummy;
+
+ /* The following undocumented bit resets the MFGPT timers */
+ val = 0xFF; dummy = 0;
+ wrmsr(MSR_MFGPT_SETUP, val, dummy);
+}
+
+/*
+ * Check whether any MFGPTs are available for the kernel to use. In most
+ * cases, firmware that uses AMD's VSA code will claim all timers during
+ * bootup; we certainly don't want to take them if they're already in use.
+ * In other cases (such as with VSAless OpenFirmware), the system firmware
+ * leaves timers available for us to use.
+ */
+static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt)
+{
+ struct cs5535_mfgpt_timer timer = { .chip = mfgpt };
+ unsigned long flags;
+ int timers = 0;
+ uint16_t val;
+ int i;
+
+ /* bios workaround */
+ if (mfgpt_reset_timers)
+ reset_all_timers();
+
+ /* just to be safe, protect this section w/ lock */
+ spin_lock_irqsave(&mfgpt->lock, flags);
+ for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
+ timer.nr = i;
+ val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP);
+ if (!(val & MFGPT_SETUP_SETUP)) {
+ __set_bit(i, mfgpt->avail);
+ timers++;
+ }
+ }
+ spin_unlock_irqrestore(&mfgpt->lock, flags);
+
+ return timers;
+}
+
+static int __init cs5535_mfgpt_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ int err, t;
+
+ /* There are two ways to get the MFGPT base address; one is by
+ * fetching it from MSR_LBAR_MFGPT, the other is by reading the
+ * PCI BAR info. The latter method is easier (especially across
+ * different architectures), so we'll stick with that for now. If
+ * it turns out to be unreliable in the face of crappy BIOSes, we
+ * can always go back to using MSRs.. */
+
+ err = pci_enable_device_io(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "can't enable device IO\n");
+ goto done;
+ }
+
+ err = pci_request_region(pdev, MFGPT_BAR, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", MFGPT_BAR);
+ goto done;
+ }
+
+ /* set up the driver-specific struct */
+ cs5535_mfgpt_chip.base = pci_resource_start(pdev, MFGPT_BAR);
+ cs5535_mfgpt_chip.pdev = pdev;
+ spin_lock_init(&cs5535_mfgpt_chip.lock);
+
+ dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", MFGPT_BAR,
+ (unsigned long long) cs5535_mfgpt_chip.base);
+
+ /* detect the available timers */
+ t = scan_timers(&cs5535_mfgpt_chip);
+ dev_info(&pdev->dev, DRV_NAME ": %d MFGPT timers available\n", t);
+ cs5535_mfgpt_chip.initialized = 1;
+ return 0;
+
+done:
+ return err;
+}
+
+static struct pci_device_id cs5535_mfgpt_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, cs5535_mfgpt_pci_tbl);
+
+/*
+ * Just like with the cs5535-gpio driver, we can't use the standard PCI driver
+ * registration stuff. It only allows only one driver to bind to each PCI
+ * device, and we want the GPIO and MFGPT drivers to be able to share a PCI
+ * device. Instead, we manually scan for the PCI device, request a single
+ * region, and keep track of the devices that we're using.
+ */
+
+static int __init cs5535_mfgpt_scan_pci(void)
+{
+ struct pci_dev *pdev;
+ int err = -ENODEV;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs5535_mfgpt_pci_tbl); i++) {
+ pdev = pci_get_device(cs5535_mfgpt_pci_tbl[i].vendor,
+ cs5535_mfgpt_pci_tbl[i].device, NULL);
+ if (pdev) {
+ err = cs5535_mfgpt_probe(pdev,
+ &cs5535_mfgpt_pci_tbl[i]);
+ if (err)
+ pci_dev_put(pdev);
+
+ /* we only support a single CS5535/6 southbridge */
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int __init cs5535_mfgpt_init(void)
+{
+ return cs5535_mfgpt_scan_pci();
+}
+
+module_init(cs5535_mfgpt_init);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
+MODULE_DESCRIPTION("CS5535/CS5536 MFGPT timer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c
index 2c27193aeaa0..f939ebc2507c 100644
--- a/drivers/misc/eeprom/eeprom.c
+++ b/drivers/misc/eeprom/eeprom.c
@@ -32,9 +32,6 @@
static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56, 0x57, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(eeprom);
-
/* Size of EEPROM in bytes */
#define EEPROM_SIZE 256
@@ -135,8 +132,7 @@ static struct bin_attribute eeprom_attr = {
};
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int eeprom_detect(struct i2c_client *client, int kind,
- struct i2c_board_info *info)
+static int eeprom_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
@@ -233,7 +229,7 @@ static struct i2c_driver eeprom_driver = {
.class = I2C_CLASS_DDC | I2C_CLASS_SPD,
.detect = eeprom_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static int __init eeprom_init(void)
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c
index 4bb7a3af9ad9..395a4ea64e9c 100644
--- a/drivers/misc/ics932s401.c
+++ b/drivers/misc/ics932s401.c
@@ -30,9 +30,6 @@
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(ics932s401);
-
/* ICS932S401 registers */
#define ICS932S401_REG_CFG2 0x01
#define ICS932S401_CFG1_SPREAD 0x01
@@ -106,12 +103,12 @@ struct ics932s401_data {
static int ics932s401_probe(struct i2c_client *client,
const struct i2c_device_id *id);
-static int ics932s401_detect(struct i2c_client *client, int kind,
+static int ics932s401_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int ics932s401_remove(struct i2c_client *client);
static const struct i2c_device_id ics932s401_id[] = {
- { "ics932s401", ics932s401 },
+ { "ics932s401", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ics932s401_id);
@@ -125,7 +122,7 @@ static struct i2c_driver ics932s401_driver = {
.remove = ics932s401_remove,
.id_table = ics932s401_id,
.detect = ics932s401_detect,
- .address_data = &addr_data,
+ .address_list = normal_i2c,
};
static struct ics932s401_data *ics932s401_update_device(struct device *dev)
@@ -413,7 +410,7 @@ static ssize_t show_spread(struct device *dev,
}
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int ics932s401_detect(struct i2c_client *client, int kind,
+static int ics932s401_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c
index 60b0b1a4fb3a..09dcb699e667 100644
--- a/drivers/misc/ioc4.c
+++ b/drivers/misc/ioc4.c
@@ -138,7 +138,7 @@ ioc4_unregister_submodule(struct ioc4_submodule *is)
* even though the following code utilizes external interrupt registers
* to perform the speed calculation.
*/
-static void
+static void __devinit
ioc4_clock_calibrate(struct ioc4_driver_data *idd)
{
union ioc4_int_out int_out;
@@ -230,7 +230,7 @@ ioc4_clock_calibrate(struct ioc4_driver_data *idd)
* on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
* If neither is present, it's a PCI-RT.
*/
-static unsigned int
+static unsigned int __devinit
ioc4_variant(struct ioc4_driver_data *idd)
{
struct pci_dev *pdev = NULL;
@@ -269,7 +269,7 @@ ioc4_variant(struct ioc4_driver_data *idd)
return IOC4_VARIANT_PCI_RT;
}
-static void
+static void __devinit
ioc4_load_modules(struct work_struct *work)
{
/* arg just has to be freed */
@@ -280,7 +280,7 @@ ioc4_load_modules(struct work_struct *work)
}
/* Adds a new instance of an IOC4 card */
-static int
+static int __devinit
ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
struct ioc4_driver_data *idd;
@@ -425,7 +425,7 @@ out:
}
/* Removes a particular instance of an IOC4 card. */
-static void
+static void __devexit
ioc4_remove(struct pci_dev *pdev)
{
struct ioc4_submodule *is;
@@ -476,7 +476,7 @@ static struct pci_driver ioc4_driver = {
.name = "IOC4",
.id_table = ioc4_id_table,
.probe = ioc4_probe,
- .remove = ioc4_remove,
+ .remove = __devexit_p(ioc4_remove),
};
MODULE_DEVICE_TABLE(pci, ioc4_id_table);
@@ -486,14 +486,14 @@ MODULE_DEVICE_TABLE(pci, ioc4_id_table);
*********************/
/* Module load */
-static int __devinit
+static int __init
ioc4_init(void)
{
return pci_register_driver(&ioc4_driver);
}
/* Module unload */
-static void __devexit
+static void __exit
ioc4_exit(void)
{
/* Ensure ioc4_load_modules() has completed before exiting */
diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h
index f93f03a9e6e9..3ad76cd18b4b 100644
--- a/drivers/misc/sgi-gru/gru.h
+++ b/drivers/misc/sgi-gru/gru.h
@@ -53,6 +53,17 @@ struct gru_chiplet_info {
int free_user_cbr;
};
+/*
+ * Statictics kept for each context.
+ */
+struct gru_gseg_statistics {
+ unsigned long fmm_tlbmiss;
+ unsigned long upm_tlbmiss;
+ unsigned long tlbdropin;
+ unsigned long context_stolen;
+ unsigned long reserved[10];
+};
+
/* Flags for GRU options on the gru_create_context() call */
/* Select one of the follow 4 options to specify how TLB misses are handled */
#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h
index 3c9c06618e6a..d95587cc794c 100644
--- a/drivers/misc/sgi-gru/gru_instructions.h
+++ b/drivers/misc/sgi-gru/gru_instructions.h
@@ -34,17 +34,17 @@ extern void gru_wait_abort_proc(void *cb);
#include <asm/intrinsics.h>
#define __flush_cache(p) ia64_fc((unsigned long)p)
/* Use volatile on IA64 to ensure ordering via st4.rel */
-#define gru_ordered_store_int(p, v) \
+#define gru_ordered_store_ulong(p, v) \
do { \
barrier(); \
- *((volatile int *)(p)) = v; /* force st.rel */ \
+ *((volatile unsigned long *)(p)) = v; /* force st.rel */ \
} while (0)
#elif defined(CONFIG_X86_64)
#define __flush_cache(p) clflush(p)
-#define gru_ordered_store_int(p, v) \
+#define gru_ordered_store_ulong(p, v) \
do { \
barrier(); \
- *(int *)p = v; \
+ *(unsigned long *)p = v; \
} while (0)
#else
#error "Unsupported architecture"
@@ -129,8 +129,13 @@ struct gru_instruction_bits {
*/
struct gru_instruction {
/* DW 0 */
- unsigned int op32; /* icmd,xtype,iaa0,ima,opc */
- unsigned int tri0;
+ union {
+ unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */
+ struct {
+ unsigned int op32;
+ unsigned int tri0;
+ };
+ };
unsigned long tri1_bufsize; /* DW 1 */
unsigned long baddr0; /* DW 2 */
unsigned long nelem; /* DW 3 */
@@ -140,7 +145,7 @@ struct gru_instruction {
unsigned long avalue; /* DW 7 */
};
-/* Some shifts and masks for the low 32 bits of a GRU command */
+/* Some shifts and masks for the low 64 bits of a GRU command */
#define GRU_CB_ICMD_SHFT 0
#define GRU_CB_ICMD_MASK 0x1
#define GRU_CB_XTYPE_SHFT 8
@@ -155,6 +160,10 @@ struct gru_instruction {
#define GRU_CB_OPC_MASK 0xff
#define GRU_CB_EXOPC_SHFT 24
#define GRU_CB_EXOPC_MASK 0xff
+#define GRU_IDEF2_SHFT 32
+#define GRU_IDEF2_MASK 0x3ffff
+#define GRU_ISTATUS_SHFT 56
+#define GRU_ISTATUS_MASK 0x3
/* GRU instruction opcodes (opc field) */
#define OP_NOP 0x00
@@ -256,6 +265,7 @@ struct gru_instruction {
#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16)
#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17)
#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18)
+#define CBE_CAUSE_FORCED_ERROR (1 << 19)
/* CBE cbrexecstatus bits */
#define CBR_EXS_ABORT_OCC_BIT 0
@@ -264,13 +274,15 @@ struct gru_instruction {
#define CBR_EXS_QUEUED_BIT 3
#define CBR_EXS_TLB_INVAL_BIT 4
#define CBR_EXS_EXCEPTION_BIT 5
+#define CBR_EXS_CB_INT_PENDING_BIT 6
#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT)
#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT)
#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT)
#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT)
-#define CBR_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT)
+#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT)
#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT)
+#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT)
/*
* Exceptions are retried for the following cases. If any OTHER bits are set
@@ -296,12 +308,14 @@ union gru_mesqhead {
/* Generate the low word of a GRU instruction */
-static inline unsigned int
-__opword(unsigned char opcode, unsigned char exopc, unsigned char xtype,
+static inline unsigned long
+__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype,
unsigned char iaa0, unsigned char iaa1,
- unsigned char ima)
+ unsigned long idef2, unsigned char ima)
{
return (1 << GRU_CB_ICMD_SHFT) |
+ ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) |
+ (idef2<< GRU_IDEF2_SHFT) |
(iaa0 << GRU_CB_IAA0_SHFT) |
(iaa1 << GRU_CB_IAA1_SHFT) |
(ima << GRU_CB_IMA_SHFT) |
@@ -319,12 +333,13 @@ static inline void gru_flush_cache(void *p)
}
/*
- * Store the lower 32 bits of the command including the "start" bit. Then
+ * Store the lower 64 bits of the command including the "start" bit. Then
* start the instruction executing.
*/
-static inline void gru_start_instruction(struct gru_instruction *ins, int op32)
+static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64)
{
- gru_ordered_store_int(ins, op32);
+ gru_ordered_store_ulong(ins, op64);
+ mb();
gru_flush_cache(ins);
}
@@ -340,6 +355,30 @@ static inline void gru_start_instruction(struct gru_instruction *ins, int op32)
* - nelem and stride are in elements
* - tri0/tri1 is in bytes for the beginning of the data segment.
*/
+static inline void gru_vload_phys(void *cb, unsigned long gpa,
+ unsigned int tri0, int iaa, unsigned long hints)
+{
+ struct gru_instruction *ins = (struct gru_instruction *)cb;
+
+ ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
+ ins->nelem = 1;
+ ins->op1_stride = 1;
+ gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0,
+ (unsigned long)tri0, CB_IMA(hints)));
+}
+
+static inline void gru_vstore_phys(void *cb, unsigned long gpa,
+ unsigned int tri0, int iaa, unsigned long hints)
+{
+ struct gru_instruction *ins = (struct gru_instruction *)cb;
+
+ ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
+ ins->nelem = 1;
+ ins->op1_stride = 1;
+ gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0,
+ (unsigned long)tri0, CB_IMA(hints)));
+}
+
static inline void gru_vload(void *cb, unsigned long mem_addr,
unsigned int tri0, unsigned char xtype, unsigned long nelem,
unsigned long stride, unsigned long hints)
@@ -348,10 +387,9 @@ static inline void gru_vload(void *cb, unsigned long mem_addr,
ins->baddr0 = (long)mem_addr;
ins->nelem = nelem;
- ins->tri0 = tri0;
ins->op1_stride = stride;
- gru_start_instruction(ins, __opword(OP_VLOAD, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0,
+ (unsigned long)tri0, CB_IMA(hints)));
}
static inline void gru_vstore(void *cb, unsigned long mem_addr,
@@ -362,10 +400,9 @@ static inline void gru_vstore(void *cb, unsigned long mem_addr,
ins->baddr0 = (long)mem_addr;
ins->nelem = nelem;
- ins->tri0 = tri0;
ins->op1_stride = stride;
- gru_start_instruction(ins, __opword(OP_VSTORE, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0,
+ tri0, CB_IMA(hints)));
}
static inline void gru_ivload(void *cb, unsigned long mem_addr,
@@ -376,10 +413,9 @@ static inline void gru_ivload(void *cb, unsigned long mem_addr,
ins->baddr0 = (long)mem_addr;
ins->nelem = nelem;
- ins->tri0 = tri0;
ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opword(OP_IVLOAD, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0,
+ tri0, CB_IMA(hints)));
}
static inline void gru_ivstore(void *cb, unsigned long mem_addr,
@@ -390,10 +426,9 @@ static inline void gru_ivstore(void *cb, unsigned long mem_addr,
ins->baddr0 = (long)mem_addr;
ins->nelem = nelem;
- ins->tri0 = tri0;
ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opword(OP_IVSTORE, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0,
+ tri0, CB_IMA(hints)));
}
static inline void gru_vset(void *cb, unsigned long mem_addr,
@@ -406,8 +441,8 @@ static inline void gru_vset(void *cb, unsigned long mem_addr,
ins->op2_value_baddr1 = value;
ins->nelem = nelem;
ins->op1_stride = stride;
- gru_start_instruction(ins, __opword(OP_VSET, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_ivset(void *cb, unsigned long mem_addr,
@@ -420,8 +455,8 @@ static inline void gru_ivset(void *cb, unsigned long mem_addr,
ins->op2_value_baddr1 = value;
ins->nelem = nelem;
ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opword(OP_IVSET, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_vflush(void *cb, unsigned long mem_addr,
@@ -433,15 +468,15 @@ static inline void gru_vflush(void *cb, unsigned long mem_addr,
ins->baddr0 = (long)mem_addr;
ins->op1_stride = stride;
ins->nelem = nelem;
- gru_start_instruction(ins, __opword(OP_VFLUSH, 0, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_nop(void *cb, int hints)
{
struct gru_instruction *ins = (void *)cb;
- gru_start_instruction(ins, __opword(OP_NOP, 0, 0, 0, 0, CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints)));
}
@@ -455,10 +490,9 @@ static inline void gru_bcopy(void *cb, const unsigned long src,
ins->baddr0 = (long)src;
ins->op2_value_baddr1 = (long)dest;
ins->nelem = nelem;
- ins->tri0 = tri0;
ins->tri1_bufsize = bufsize;
- gru_start_instruction(ins, __opword(OP_BCOPY, 0, xtype, IAA_RAM,
- IAA_RAM, CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM,
+ IAA_RAM, tri0, CB_IMA(hints)));
}
static inline void gru_bstore(void *cb, const unsigned long src,
@@ -470,9 +504,8 @@ static inline void gru_bstore(void *cb, const unsigned long src,
ins->baddr0 = (long)src;
ins->op2_value_baddr1 = (long)dest;
ins->nelem = nelem;
- ins->tri0 = tri0;
- gru_start_instruction(ins, __opword(OP_BSTORE, 0, xtype, 0, IAA_RAM,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM,
+ tri0, CB_IMA(hints)));
}
static inline void gru_gamir(void *cb, int exopc, unsigned long src,
@@ -481,8 +514,8 @@ static inline void gru_gamir(void *cb, int exopc, unsigned long src,
struct gru_instruction *ins = (void *)cb;
ins->baddr0 = (long)src;
- gru_start_instruction(ins, __opword(OP_GAMIR, exopc, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_gamirr(void *cb, int exopc, unsigned long src,
@@ -491,8 +524,8 @@ static inline void gru_gamirr(void *cb, int exopc, unsigned long src,
struct gru_instruction *ins = (void *)cb;
ins->baddr0 = (long)src;
- gru_start_instruction(ins, __opword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_gamer(void *cb, int exopc, unsigned long src,
@@ -505,8 +538,8 @@ static inline void gru_gamer(void *cb, int exopc, unsigned long src,
ins->baddr0 = (long)src;
ins->op1_stride = operand1;
ins->op2_value_baddr1 = operand2;
- gru_start_instruction(ins, __opword(OP_GAMER, exopc, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_gamerr(void *cb, int exopc, unsigned long src,
@@ -518,8 +551,8 @@ static inline void gru_gamerr(void *cb, int exopc, unsigned long src,
ins->baddr0 = (long)src;
ins->op1_stride = operand1;
ins->op2_value_baddr1 = operand2;
- gru_start_instruction(ins, __opword(OP_GAMERR, exopc, xtype, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0,
+ 0, CB_IMA(hints)));
}
static inline void gru_gamxr(void *cb, unsigned long src,
@@ -529,8 +562,8 @@ static inline void gru_gamxr(void *cb, unsigned long src,
ins->baddr0 = (long)src;
ins->nelem = 4;
- gru_start_instruction(ins, __opword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW,
- IAA_RAM, 0, CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW,
+ IAA_RAM, 0, 0, CB_IMA(hints)));
}
static inline void gru_mesq(void *cb, unsigned long queue,
@@ -541,9 +574,8 @@ static inline void gru_mesq(void *cb, unsigned long queue,
ins->baddr0 = (long)queue;
ins->nelem = nelem;
- ins->tri0 = tri0;
- gru_start_instruction(ins, __opword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0,
- CB_IMA(hints)));
+ gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0,
+ tri0, CB_IMA(hints)));
}
static inline unsigned long gru_get_amo_value(void *cb)
@@ -662,6 +694,14 @@ static inline void gru_wait_abort(void *cb)
gru_wait_abort_proc(cb);
}
+/*
+ * Get a pointer to the start of a gseg
+ * p - Any valid pointer within the gseg
+ */
+static inline void *gru_get_gseg_pointer (void *p)
+{
+ return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1));
+}
/*
* Get a pointer to a control block
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index 679e01778286..38657cdaf54d 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -40,6 +40,12 @@
#include "gru_instructions.h"
#include <asm/uv/uv_hub.h>
+/* Return codes for vtop functions */
+#define VTOP_SUCCESS 0
+#define VTOP_INVALID -1
+#define VTOP_RETRY -2
+
+
/*
* Test if a physical address is a valid GRU GSEG address
*/
@@ -90,19 +96,22 @@ static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
- struct gru_thread_state *gts = NULL;
+ struct gru_thread_state *gts = ERR_PTR(-EINVAL);
down_write(&mm->mmap_sem);
vma = gru_find_vma(vaddr);
- if (vma)
- gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
- if (gts) {
- mutex_lock(&gts->ts_ctxlock);
- downgrade_write(&mm->mmap_sem);
- } else {
- up_write(&mm->mmap_sem);
- }
+ if (!vma)
+ goto err;
+ gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
+ if (IS_ERR(gts))
+ goto err;
+ mutex_lock(&gts->ts_ctxlock);
+ downgrade_write(&mm->mmap_sem);
+ return gts;
+
+err:
+ up_write(&mm->mmap_sem);
return gts;
}
@@ -122,39 +131,15 @@ static void gru_unlock_gts(struct gru_thread_state *gts)
* is necessary to prevent the user from seeing a stale cb.istatus that will
* change as soon as the TFH restart is complete. Races may cause an
* occasional failure to clear the cb.istatus, but that is ok.
- *
- * If the cb address is not valid (should not happen, but...), nothing
- * bad will happen.. The get_user()/put_user() will fail but there
- * are no bad side-effects.
*/
-static void gru_cb_set_istatus_active(unsigned long __user *cb)
+static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk)
{
- union {
- struct gru_instruction_bits bits;
- unsigned long dw;
- } u;
-
- if (cb) {
- get_user(u.dw, cb);
- u.bits.istatus = CBS_ACTIVE;
- put_user(u.dw, cb);
+ if (cbk) {
+ cbk->istatus = CBS_ACTIVE;
}
}
/*
- * Convert a interrupt IRQ to a pointer to the GRU GTS that caused the
- * interrupt. Interrupts are always sent to a cpu on the blade that contains the
- * GRU (except for headless blades which are not currently supported). A blade
- * has N grus; a block of N consecutive IRQs is assigned to the GRUs. The IRQ
- * number uniquely identifies the GRU chiplet on the local blade that caused the
- * interrupt. Always called in interrupt context.
- */
-static inline struct gru_state *irq_to_gru(int irq)
-{
- return &gru_base[uv_numa_blade_id()]->bs_grus[irq - IRQ_GRU];
-}
-
-/*
* Read & clear a TFM
*
* The GRU has an array of fault maps. A map is private to a cpu
@@ -207,10 +192,11 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,
{
struct page *page;
- /* ZZZ Need to handle HUGE pages */
- if (is_vm_hugetlb_page(vma))
- return -EFAULT;
+#ifdef CONFIG_HUGETLB_PAGE
+ *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
+#else
*pageshift = PAGE_SHIFT;
+#endif
if (get_user_pages
(current, current->mm, vaddr, 1, write, 0, &page, NULL) <= 0)
return -EFAULT;
@@ -268,7 +254,6 @@ static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr,
return 0;
err:
- local_irq_enable();
return 1;
}
@@ -301,14 +286,69 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
paddr = paddr & ~((1UL << ps) - 1);
*gpa = uv_soc_phys_ram_to_gpa(paddr);
*pageshift = ps;
- return 0;
+ return VTOP_SUCCESS;
inval:
- return -1;
+ return VTOP_INVALID;
upm:
- return -2;
+ return VTOP_RETRY;
+}
+
+
+/*
+ * Flush a CBE from cache. The CBE is clean in the cache. Dirty the
+ * CBE cacheline so that the line will be written back to home agent.
+ * Otherwise the line may be silently dropped. This has no impact
+ * except on performance.
+ */
+static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe)
+{
+ if (unlikely(cbe)) {
+ cbe->cbrexecstatus = 0; /* make CL dirty */
+ gru_flush_cache(cbe);
+ }
}
+/*
+ * Preload the TLB with entries that may be required. Currently, preloading
+ * is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to
+ * the end of the bcopy tranfer, whichever is smaller.
+ */
+static void gru_preload_tlb(struct gru_state *gru,
+ struct gru_thread_state *gts, int atomic,
+ unsigned long fault_vaddr, int asid, int write,
+ unsigned char tlb_preload_count,
+ struct gru_tlb_fault_handle *tfh,
+ struct gru_control_block_extended *cbe)
+{
+ unsigned long vaddr = 0, gpa;
+ int ret, pageshift;
+
+ if (cbe->opccpy != OP_BCOPY)
+ return;
+
+ if (fault_vaddr == cbe->cbe_baddr0)
+ vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1;
+ else if (fault_vaddr == cbe->cbe_baddr1)
+ vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1;
+
+ fault_vaddr &= PAGE_MASK;
+ vaddr &= PAGE_MASK;
+ vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE);
+
+ while (vaddr > fault_vaddr) {
+ ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
+ if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write,
+ GRU_PAGESIZE(pageshift)))
+ return;
+ gru_dbg(grudev,
+ "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n",
+ atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh,
+ vaddr, asid, write, pageshift, gpa);
+ vaddr -= PAGE_SIZE;
+ STAT(tlb_preload_page);
+ }
+}
/*
* Drop a TLB entry into the GRU. The fault is described by info in an TFH.
@@ -320,11 +360,14 @@ upm:
* < 0 = error code
*
*/
-static int gru_try_dropin(struct gru_thread_state *gts,
+static int gru_try_dropin(struct gru_state *gru,
+ struct gru_thread_state *gts,
struct gru_tlb_fault_handle *tfh,
- unsigned long __user *cb)
+ struct gru_instruction_bits *cbk)
{
- int pageshift = 0, asid, write, ret, atomic = !cb;
+ struct gru_control_block_extended *cbe = NULL;
+ unsigned char tlb_preload_count = gts->ts_tlb_preload_count;
+ int pageshift = 0, asid, write, ret, atomic = !cbk, indexway;
unsigned long gpa = 0, vaddr = 0;
/*
@@ -335,24 +378,34 @@ static int gru_try_dropin(struct gru_thread_state *gts,
*/
/*
+ * Prefetch the CBE if doing TLB preloading
+ */
+ if (unlikely(tlb_preload_count)) {
+ cbe = gru_tfh_to_cbe(tfh);
+ prefetchw(cbe);
+ }
+
+ /*
* Error if TFH state is IDLE or FMM mode & the user issuing a UPM call.
* Might be a hardware race OR a stupid user. Ignore FMM because FMM
* is a transient state.
*/
if (tfh->status != TFHSTATUS_EXCEPTION) {
gru_flush_cache(tfh);
+ sync_core();
if (tfh->status != TFHSTATUS_EXCEPTION)
goto failnoexception;
STAT(tfh_stale_on_fault);
}
if (tfh->state == TFHSTATE_IDLE)
goto failidle;
- if (tfh->state == TFHSTATE_MISS_FMM && cb)
+ if (tfh->state == TFHSTATE_MISS_FMM && cbk)
goto failfmm;
write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0;
vaddr = tfh->missvaddr;
asid = tfh->missasid;
+ indexway = tfh->indexway;
if (asid == 0)
goto failnoasid;
@@ -366,41 +419,51 @@ static int gru_try_dropin(struct gru_thread_state *gts,
goto failactive;
ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
- if (ret == -1)
+ if (ret == VTOP_INVALID)
goto failinval;
- if (ret == -2)
+ if (ret == VTOP_RETRY)
goto failupm;
if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) {
gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift);
- if (atomic || !gru_update_cch(gts, 0)) {
+ if (atomic || !gru_update_cch(gts)) {
gts->ts_force_cch_reload = 1;
goto failupm;
}
}
- gru_cb_set_istatus_active(cb);
+
+ if (unlikely(cbe) && pageshift == PAGE_SHIFT) {
+ gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe);
+ gru_flush_cache_cbe(cbe);
+ }
+
+ gru_cb_set_istatus_active(cbk);
+ gts->ustats.tlbdropin++;
tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
GRU_PAGESIZE(pageshift));
- STAT(tlb_dropin);
gru_dbg(grudev,
- "%s: tfh 0x%p, vaddr 0x%lx, asid 0x%x, ps %d, gpa 0x%lx\n",
- ret ? "non-atomic" : "atomic", tfh, vaddr, asid,
- pageshift, gpa);
+ "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x,"
+ " rw %d, ps %d, gpa 0x%lx\n",
+ atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid,
+ indexway, write, pageshift, gpa);
+ STAT(tlb_dropin);
return 0;
failnoasid:
/* No asid (delayed unload). */
STAT(tlb_dropin_fail_no_asid);
gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- if (!cb)
+ if (!cbk)
tfh_user_polling_mode(tfh);
else
gru_flush_cache(tfh);
+ gru_flush_cache_cbe(cbe);
return -EAGAIN;
failupm:
/* Atomic failure switch CBR to UPM */
tfh_user_polling_mode(tfh);
+ gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_upm);
gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
return 1;
@@ -408,6 +471,7 @@ failupm:
failfmm:
/* FMM state on UPM call */
gru_flush_cache(tfh);
+ gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_fmm);
gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state);
return 0;
@@ -415,17 +479,20 @@ failfmm:
failnoexception:
/* TFH status did not show exception pending */
gru_flush_cache(tfh);
- if (cb)
- gru_flush_cache(cb);
+ gru_flush_cache_cbe(cbe);
+ if (cbk)
+ gru_flush_cache(cbk);
STAT(tlb_dropin_fail_no_exception);
- gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", tfh, tfh->status, tfh->state);
+ gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n",
+ tfh, tfh->status, tfh->state);
return 0;
failidle:
/* TFH state was idle - no miss pending */
gru_flush_cache(tfh);
- if (cb)
- gru_flush_cache(cb);
+ gru_flush_cache_cbe(cbe);
+ if (cbk)
+ gru_flush_cache(cbk);
STAT(tlb_dropin_fail_idle);
gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state);
return 0;
@@ -433,16 +500,18 @@ failidle:
failinval:
/* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */
tfh_exception(tfh);
+ gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_invalid);
gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
return -EFAULT;
failactive:
/* Range invalidate active. Switch to UPM iff atomic */
- if (!cb)
+ if (!cbk)
tfh_user_polling_mode(tfh);
else
gru_flush_cache(tfh);
+ gru_flush_cache_cbe(cbe);
STAT(tlb_dropin_fail_range_active);
gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n",
tfh, vaddr);
@@ -455,31 +524,41 @@ failactive:
* Note that this is the interrupt handler that is registered with linux
* interrupt handlers.
*/
-irqreturn_t gru_intr(int irq, void *dev_id)
+static irqreturn_t gru_intr(int chiplet, int blade)
{
struct gru_state *gru;
struct gru_tlb_fault_map imap, dmap;
struct gru_thread_state *gts;
struct gru_tlb_fault_handle *tfh = NULL;
+ struct completion *cmp;
int cbrnum, ctxnum;
STAT(intr);
- gru = irq_to_gru(irq);
+ gru = &gru_base[blade]->bs_grus[chiplet];
if (!gru) {
- dev_err(grudev, "GRU: invalid interrupt: cpu %d, irq %d\n",
- raw_smp_processor_id(), irq);
+ dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n",
+ raw_smp_processor_id(), chiplet);
return IRQ_NONE;
}
get_clear_fault_map(gru, &imap, &dmap);
+ gru_dbg(grudev,
+ "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n",
+ smp_processor_id(), chiplet, gru->gs_gid,
+ imap.fault_bits[0], imap.fault_bits[1],
+ dmap.fault_bits[0], dmap.fault_bits[1]);
for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) {
- complete(gru->gs_blade->bs_async_wq);
+ STAT(intr_cbr);
+ cmp = gru->gs_blade->bs_async_wq;
+ if (cmp)
+ complete(cmp);
gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n",
- gru->gs_gid, cbrnum, gru->gs_blade->bs_async_wq->done);
+ gru->gs_gid, cbrnum, cmp ? cmp->done : -1);
}
for_each_cbr_in_tfm(cbrnum, imap.fault_bits) {
+ STAT(intr_tfh);
tfh = get_tfh_by_index(gru, cbrnum);
prefetchw(tfh); /* Helps on hdw, required for emulator */
@@ -492,14 +571,20 @@ irqreturn_t gru_intr(int irq, void *dev_id)
ctxnum = tfh->ctxnum;
gts = gru->gs_gts[ctxnum];
+ /* Spurious interrupts can cause this. Ignore. */
+ if (!gts) {
+ STAT(intr_spurious);
+ continue;
+ }
+
/*
* This is running in interrupt context. Trylock the mmap_sem.
* If it fails, retry the fault in user context.
*/
+ gts->ustats.fmm_tlbmiss++;
if (!gts->ts_force_cch_reload &&
down_read_trylock(&gts->ts_mm->mmap_sem)) {
- gts->ustats.fmm_tlbdropin++;
- gru_try_dropin(gts, tfh, NULL);
+ gru_try_dropin(gru, gts, tfh, NULL);
up_read(&gts->ts_mm->mmap_sem);
} else {
tfh_user_polling_mode(tfh);
@@ -509,20 +594,43 @@ irqreturn_t gru_intr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+irqreturn_t gru0_intr(int irq, void *dev_id)
+{
+ return gru_intr(0, uv_numa_blade_id());
+}
+
+irqreturn_t gru1_intr(int irq, void *dev_id)
+{
+ return gru_intr(1, uv_numa_blade_id());
+}
+
+irqreturn_t gru_intr_mblade(int irq, void *dev_id)
+{
+ int blade;
+
+ for_each_possible_blade(blade) {
+ if (uv_blade_nr_possible_cpus(blade))
+ continue;
+ gru_intr(0, blade);
+ gru_intr(1, blade);
+ }
+ return IRQ_HANDLED;
+}
+
static int gru_user_dropin(struct gru_thread_state *gts,
struct gru_tlb_fault_handle *tfh,
- unsigned long __user *cb)
+ void *cb)
{
struct gru_mm_struct *gms = gts->ts_gms;
int ret;
- gts->ustats.upm_tlbdropin++;
+ gts->ustats.upm_tlbmiss++;
while (1) {
wait_event(gms->ms_wait_queue,
atomic_read(&gms->ms_range_active) == 0);
prefetchw(tfh); /* Helps on hdw, required for emulator */
- ret = gru_try_dropin(gts, tfh, cb);
+ ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb);
if (ret <= 0)
return ret;
STAT(call_os_wait_queue);
@@ -538,52 +646,41 @@ int gru_handle_user_call_os(unsigned long cb)
{
struct gru_tlb_fault_handle *tfh;
struct gru_thread_state *gts;
- unsigned long __user *cbp;
+ void *cbk;
int ucbnum, cbrnum, ret = -EINVAL;
STAT(call_os);
- gru_dbg(grudev, "address 0x%lx\n", cb);
/* sanity check the cb pointer */
ucbnum = get_cb_number((void *)cb);
if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB)
return -EINVAL;
- cbp = (unsigned long *)cb;
gts = gru_find_lock_gts(cb);
if (!gts)
return -EINVAL;
+ gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE)
goto exit;
- /*
- * If force_unload is set, the UPM TLB fault is phony. The task
- * has migrated to another node and the GSEG must be moved. Just
- * unload the context. The task will page fault and assign a new
- * context.
- */
- if (gts->ts_tgid_owner == current->tgid && gts->ts_blade >= 0 &&
- gts->ts_blade != uv_numa_blade_id()) {
- STAT(call_os_offnode_reference);
- gts->ts_force_unload = 1;
- }
+ gru_check_context_placement(gts);
/*
* CCH may contain stale data if ts_force_cch_reload is set.
*/
if (gts->ts_gru && gts->ts_force_cch_reload) {
gts->ts_force_cch_reload = 0;
- gru_update_cch(gts, 0);
+ gru_update_cch(gts);
}
ret = -EAGAIN;
cbrnum = thread_cbr_number(gts, ucbnum);
- if (gts->ts_force_unload) {
- gru_unload_context(gts, 1);
- } else if (gts->ts_gru) {
+ if (gts->ts_gru) {
tfh = get_tfh_by_index(gts->ts_gru, cbrnum);
- ret = gru_user_dropin(gts, tfh, cbp);
+ cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr,
+ gts->ts_ctxnum, ucbnum);
+ ret = gru_user_dropin(gts, tfh, cbk);
}
exit:
gru_unlock_gts(gts);
@@ -605,11 +702,11 @@ int gru_get_exception_detail(unsigned long arg)
if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet)))
return -EFAULT;
- gru_dbg(grudev, "address 0x%lx\n", excdet.cb);
gts = gru_find_lock_gts(excdet.cb);
if (!gts)
return -EINVAL;
+ gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
ucbnum = get_cb_number((void *)excdet.cb);
if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) {
ret = -EINVAL;
@@ -617,6 +714,7 @@ int gru_get_exception_detail(unsigned long arg)
cbrnum = thread_cbr_number(gts, ucbnum);
cbe = get_cbe_by_index(gts->ts_gru, cbrnum);
gru_flush_cache(cbe); /* CBE not coherent */
+ sync_core(); /* make sure we are have current data */
excdet.opc = cbe->opccpy;
excdet.exopc = cbe->exopccpy;
excdet.ecause = cbe->ecause;
@@ -624,7 +722,7 @@ int gru_get_exception_detail(unsigned long arg)
excdet.exceptdet1 = cbe->idef3upd;
excdet.cbrstate = cbe->cbrstate;
excdet.cbrexecstatus = cbe->cbrexecstatus;
- gru_flush_cache(cbe);
+ gru_flush_cache_cbe(cbe);
ret = 0;
} else {
ret = -EAGAIN;
@@ -733,6 +831,11 @@ long gru_get_gseg_statistics(unsigned long arg)
if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
return -EFAULT;
+ /*
+ * The library creates arrays of contexts for threaded programs.
+ * If no gts exists in the array, the context has never been used & all
+ * statistics are implicitly 0.
+ */
gts = gru_find_lock_gts(req.gseg);
if (gts) {
memcpy(&req.stats, &gts->ustats, sizeof(gts->ustats));
@@ -762,11 +865,25 @@ int gru_set_context_option(unsigned long arg)
return -EFAULT;
gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1);
- gts = gru_alloc_locked_gts(req.gseg);
- if (!gts)
- return -EINVAL;
+ gts = gru_find_lock_gts(req.gseg);
+ if (!gts) {
+ gts = gru_alloc_locked_gts(req.gseg);
+ if (IS_ERR(gts))
+ return PTR_ERR(gts);
+ }
switch (req.op) {
+ case sco_blade_chiplet:
+ /* Select blade/chiplet for GRU context */
+ if (req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || !gru_base[req.val1] ||
+ req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB) {
+ ret = -EINVAL;
+ } else {
+ gts->ts_user_blade_id = req.val1;
+ gts->ts_user_chiplet_id = req.val0;
+ gru_check_context_placement(gts);
+ }
+ break;
case sco_gseg_owner:
/* Register the current task as the GSEG owner */
gts->ts_tgid_owner = current->tgid;
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
index ce5eda985ab0..cb3b4d228475 100644
--- a/drivers/misc/sgi-gru/grufile.c
+++ b/drivers/misc/sgi-gru/grufile.c
@@ -35,6 +35,9 @@
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
+#ifdef CONFIG_X86_64
+#include <asm/uv/uv_irq.h>
+#endif
#include <asm/uv/uv.h>
#include "gru.h"
#include "grulib.h"
@@ -130,7 +133,6 @@ static int gru_create_new_context(unsigned long arg)
struct gru_vma_data *vdata;
int ret = -EINVAL;
-
if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
return -EFAULT;
@@ -150,6 +152,7 @@ static int gru_create_new_context(unsigned long arg)
vdata->vd_dsr_au_count =
GRU_DS_BYTES_TO_AU(req.data_segment_bytes);
vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks);
+ vdata->vd_tlb_preload_count = req.tlb_preload_count;
ret = 0;
}
up_write(&current->mm->mmap_sem);
@@ -190,7 +193,7 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req,
{
int err = -EBADRQC;
- gru_dbg(grudev, "file %p\n", file);
+ gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg);
switch (req) {
case GRU_CREATE_CONTEXT:
@@ -232,23 +235,24 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req,
* system.
*/
static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr,
- void *vaddr, int nid, int bid, int grunum)
+ void *vaddr, int blade_id, int chiplet_id)
{
spin_lock_init(&gru->gs_lock);
spin_lock_init(&gru->gs_asid_lock);
gru->gs_gru_base_paddr = paddr;
gru->gs_gru_base_vaddr = vaddr;
- gru->gs_gid = bid * GRU_CHIPLETS_PER_BLADE + grunum;
- gru->gs_blade = gru_base[bid];
- gru->gs_blade_id = bid;
+ gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id;
+ gru->gs_blade = gru_base[blade_id];
+ gru->gs_blade_id = blade_id;
+ gru->gs_chiplet_id = chiplet_id;
gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1;
gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1;
gru->gs_asid_limit = MAX_ASID;
gru_tgh_flush_init(gru);
if (gru->gs_gid >= gru_max_gids)
gru_max_gids = gru->gs_gid + 1;
- gru_dbg(grudev, "bid %d, nid %d, gid %d, vaddr %p (0x%lx)\n",
- bid, nid, gru->gs_gid, gru->gs_gru_base_vaddr,
+ gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n",
+ blade_id, gru->gs_gid, gru->gs_gru_base_vaddr,
gru->gs_gru_base_paddr);
}
@@ -264,12 +268,10 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
max_user_cbrs = GRU_NUM_CB;
max_user_dsr_bytes = GRU_NUM_DSR_BYTES;
- for_each_online_node(nid) {
- bid = uv_node_to_blade_id(nid);
- pnode = uv_node_to_pnode(nid);
- if (bid < 0 || gru_base[bid])
- continue;
- page = alloc_pages_exact_node(nid, GFP_KERNEL, order);
+ for_each_possible_blade(bid) {
+ pnode = uv_blade_to_pnode(bid);
+ nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */
+ page = alloc_pages_node(nid, GFP_KERNEL, order);
if (!page)
goto fail;
gru_base[bid] = page_address(page);
@@ -285,7 +287,7 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
chip++, gru++) {
paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip);
vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip);
- gru_init_chiplet(gru, paddr, vaddr, nid, bid, chip);
+ gru_init_chiplet(gru, paddr, vaddr, bid, chip);
n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
cbrs = max(cbrs, n);
n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
@@ -298,39 +300,215 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
return 0;
fail:
- for (nid--; nid >= 0; nid--)
- free_pages((unsigned long)gru_base[nid], order);
+ for (bid--; bid >= 0; bid--)
+ free_pages((unsigned long)gru_base[bid], order);
return -ENOMEM;
}
-#ifdef CONFIG_IA64
+static void gru_free_tables(void)
+{
+ int bid;
+ int order = get_order(sizeof(struct gru_state) *
+ GRU_CHIPLETS_PER_BLADE);
-static int get_base_irq(void)
+ for (bid = 0; bid < GRU_MAX_BLADES; bid++)
+ free_pages((unsigned long)gru_base[bid], order);
+}
+
+static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep)
{
- return IRQ_GRU;
+ unsigned long mmr = 0;
+ int core;
+
+ /*
+ * We target the cores of a blade and not the hyperthreads themselves.
+ * There is a max of 8 cores per socket and 2 sockets per blade,
+ * making for a max total of 16 cores (i.e., 16 CPUs without
+ * hyperthreading and 32 CPUs with hyperthreading).
+ */
+ core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
+ if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu))
+ return 0;
+
+ if (chiplet == 0) {
+ mmr = UVH_GR0_TLB_INT0_CONFIG +
+ core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG);
+ } else if (chiplet == 1) {
+ mmr = UVH_GR1_TLB_INT0_CONFIG +
+ core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG);
+ } else {
+ BUG();
+ }
+
+ *corep = core;
+ return mmr;
}
-#elif defined CONFIG_X86_64
+#ifdef CONFIG_IA64
-static void noop(unsigned int irq)
+static int gru_irq_count[GRU_CHIPLETS_PER_BLADE];
+
+static void gru_noop(unsigned int irq)
{
}
-static struct irq_chip gru_chip = {
- .name = "gru",
- .mask = noop,
- .unmask = noop,
- .ack = noop,
+static struct irq_chip gru_chip[GRU_CHIPLETS_PER_BLADE] = {
+ [0 ... GRU_CHIPLETS_PER_BLADE - 1] {
+ .mask = gru_noop,
+ .unmask = gru_noop,
+ .ack = gru_noop
+ }
};
-static int get_base_irq(void)
+static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name,
+ irq_handler_t irq_handler, int cpu, int blade)
+{
+ unsigned long mmr;
+ int irq = IRQ_GRU + chiplet;
+ int ret, core;
+
+ mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
+ if (mmr == 0)
+ return 0;
+
+ if (gru_irq_count[chiplet] == 0) {
+ gru_chip[chiplet].name = irq_name;
+ ret = set_irq_chip(irq, &gru_chip[chiplet]);
+ if (ret) {
+ printk(KERN_ERR "%s: set_irq_chip failed, errno=%d\n",
+ GRU_DRIVER_ID_STR, -ret);
+ return ret;
+ }
+
+ ret = request_irq(irq, irq_handler, 0, irq_name, NULL);
+ if (ret) {
+ printk(KERN_ERR "%s: request_irq failed, errno=%d\n",
+ GRU_DRIVER_ID_STR, -ret);
+ return ret;
+ }
+ }
+ gru_irq_count[chiplet]++;
+
+ return 0;
+}
+
+static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade)
+{
+ unsigned long mmr;
+ int core, irq = IRQ_GRU + chiplet;
+
+ if (gru_irq_count[chiplet] == 0)
+ return;
+
+ mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
+ if (mmr == 0)
+ return;
+
+ if (--gru_irq_count[chiplet] == 0)
+ free_irq(irq, NULL);
+}
+
+#elif defined CONFIG_X86_64
+
+static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name,
+ irq_handler_t irq_handler, int cpu, int blade)
+{
+ unsigned long mmr;
+ int irq, core;
+ int ret;
+
+ mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
+ if (mmr == 0)
+ return 0;
+
+ irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU);
+ if (irq < 0) {
+ printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n",
+ GRU_DRIVER_ID_STR, -irq);
+ return irq;
+ }
+
+ ret = request_irq(irq, irq_handler, 0, irq_name, NULL);
+ if (ret) {
+ uv_teardown_irq(irq);
+ printk(KERN_ERR "%s: request_irq failed, errno=%d\n",
+ GRU_DRIVER_ID_STR, -ret);
+ return ret;
+ }
+ gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq;
+ return 0;
+}
+
+static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade)
{
- set_irq_chip(IRQ_GRU, &gru_chip);
- set_irq_chip(IRQ_GRU + 1, &gru_chip);
- return IRQ_GRU;
+ int irq, core;
+ unsigned long mmr;
+
+ mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
+ if (mmr) {
+ irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core];
+ if (irq) {
+ free_irq(irq, NULL);
+ uv_teardown_irq(irq);
+ }
+ }
}
+
#endif
+static void gru_teardown_tlb_irqs(void)
+{
+ int blade;
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ blade = uv_cpu_to_blade_id(cpu);
+ gru_chiplet_teardown_tlb_irq(0, cpu, blade);
+ gru_chiplet_teardown_tlb_irq(1, cpu, blade);
+ }
+ for_each_possible_blade(blade) {
+ if (uv_blade_nr_possible_cpus(blade))
+ continue;
+ gru_chiplet_teardown_tlb_irq(0, 0, blade);
+ gru_chiplet_teardown_tlb_irq(1, 0, blade);
+ }
+}
+
+static int gru_setup_tlb_irqs(void)
+{
+ int blade;
+ int cpu;
+ int ret;
+
+ for_each_online_cpu(cpu) {
+ blade = uv_cpu_to_blade_id(cpu);
+ ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade);
+ if (ret != 0)
+ goto exit1;
+
+ ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade);
+ if (ret != 0)
+ goto exit1;
+ }
+ for_each_possible_blade(blade) {
+ if (uv_blade_nr_possible_cpus(blade))
+ continue;
+ ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade);
+ if (ret != 0)
+ goto exit1;
+
+ ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade);
+ if (ret != 0)
+ goto exit1;
+ }
+
+ return 0;
+
+exit1:
+ gru_teardown_tlb_irqs();
+ return ret;
+}
+
/*
* gru_init
*
@@ -338,8 +516,7 @@ static int get_base_irq(void)
*/
static int __init gru_init(void)
{
- int ret, irq, chip;
- char id[10];
+ int ret;
if (!is_uv_system())
return 0;
@@ -354,41 +531,29 @@ static int __init gru_init(void)
gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE;
printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n",
gru_start_paddr, gru_end_paddr);
- irq = get_base_irq();
- for (chip = 0; chip < GRU_CHIPLETS_PER_BLADE; chip++) {
- ret = request_irq(irq + chip, gru_intr, 0, id, NULL);
- /* TODO: fix irq handling on x86. For now ignore failure because
- * interrupts are not required & not yet fully supported */
- if (ret) {
- printk(KERN_WARNING
- "!!!WARNING: GRU ignoring request failure!!!\n");
- ret = 0;
- }
- if (ret) {
- printk(KERN_ERR "%s: request_irq failed\n",
- GRU_DRIVER_ID_STR);
- goto exit1;
- }
- }
-
ret = misc_register(&gru_miscdev);
if (ret) {
printk(KERN_ERR "%s: misc_register failed\n",
GRU_DRIVER_ID_STR);
- goto exit1;
+ goto exit0;
}
ret = gru_proc_init();
if (ret) {
printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR);
- goto exit2;
+ goto exit1;
}
ret = gru_init_tables(gru_start_paddr, gru_start_vaddr);
if (ret) {
printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR);
- goto exit3;
+ goto exit2;
}
+
+ ret = gru_setup_tlb_irqs();
+ if (ret != 0)
+ goto exit3;
+
gru_kservices_init();
printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR,
@@ -396,31 +561,24 @@ static int __init gru_init(void)
return 0;
exit3:
- gru_proc_exit();
+ gru_free_tables();
exit2:
- misc_deregister(&gru_miscdev);
+ gru_proc_exit();
exit1:
- for (--chip; chip >= 0; chip--)
- free_irq(irq + chip, NULL);
+ misc_deregister(&gru_miscdev);
+exit0:
return ret;
}
static void __exit gru_exit(void)
{
- int i, bid;
- int order = get_order(sizeof(struct gru_state) *
- GRU_CHIPLETS_PER_BLADE);
-
if (!is_uv_system())
return;
- for (i = 0; i < GRU_CHIPLETS_PER_BLADE; i++)
- free_irq(IRQ_GRU + i, NULL);
+ gru_teardown_tlb_irqs();
gru_kservices_exit();
- for (bid = 0; bid < GRU_MAX_BLADES; bid++)
- free_pages((unsigned long)gru_base[bid], order);
-
+ gru_free_tables();
misc_deregister(&gru_miscdev);
gru_proc_exit();
}
diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c
index 37e7cfc53b9c..2f30badc6ffd 100644
--- a/drivers/misc/sgi-gru/gruhandles.c
+++ b/drivers/misc/sgi-gru/gruhandles.c
@@ -27,9 +27,11 @@
#ifdef CONFIG_IA64
#include <asm/processor.h>
#define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10)
+#define CLKS2NSEC(c) ((c) *1000000000 / local_cpu_data->itc_freq)
#else
#include <asm/tsc.h>
#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
+#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz)
#endif
/* Extract the status field from a kernel handle */
@@ -39,21 +41,39 @@ struct mcs_op_statistic mcs_op_statistics[mcsop_last];
static void update_mcs_stats(enum mcs_op op, unsigned long clks)
{
+ unsigned long nsec;
+
+ nsec = CLKS2NSEC(clks);
atomic_long_inc(&mcs_op_statistics[op].count);
- atomic_long_add(clks, &mcs_op_statistics[op].total);
- if (mcs_op_statistics[op].max < clks)
- mcs_op_statistics[op].max = clks;
+ atomic_long_add(nsec, &mcs_op_statistics[op].total);
+ if (mcs_op_statistics[op].max < nsec)
+ mcs_op_statistics[op].max = nsec;
}
static void start_instruction(void *h)
{
unsigned long *w0 = h;
- wmb(); /* setting CMD bit must be last */
- *w0 = *w0 | 1;
+ wmb(); /* setting CMD/STATUS bits must be last */
+ *w0 = *w0 | 0x20001;
gru_flush_cache(h);
}
+static void report_instruction_timeout(void *h)
+{
+ unsigned long goff = GSEGPOFF((unsigned long)h);
+ char *id = "???";
+
+ if (TYPE_IS(CCH, goff))
+ id = "CCH";
+ else if (TYPE_IS(TGH, goff))
+ id = "TGH";
+ else if (TYPE_IS(TFH, goff))
+ id = "TFH";
+
+ panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id);
+}
+
static int wait_instruction_complete(void *h, enum mcs_op opc)
{
int status;
@@ -64,9 +84,10 @@ static int wait_instruction_complete(void *h, enum mcs_op opc)
status = GET_MSEG_HANDLE_STATUS(h);
if (status != CCHSTATUS_ACTIVE)
break;
- if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time))
- panic("GRU %p is malfunctioning: start %ld, end %ld\n",
- h, start_time, (unsigned long)get_cycles());
+ if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
+ report_instruction_timeout(h);
+ start_time = get_cycles();
+ }
}
if (gru_options & OPT_STATS)
update_mcs_stats(opc, get_cycles() - start_time);
@@ -75,9 +96,18 @@ static int wait_instruction_complete(void *h, enum mcs_op opc)
int cch_allocate(struct gru_context_configuration_handle *cch)
{
+ int ret;
+
cch->opc = CCHOP_ALLOCATE;
start_instruction(cch);
- return wait_instruction_complete(cch, cchop_allocate);
+ ret = wait_instruction_complete(cch, cchop_allocate);
+
+ /*
+ * Stop speculation into the GSEG being mapped by the previous ALLOCATE.
+ * The GSEG memory does not exist until the ALLOCATE completes.
+ */
+ sync_core();
+ return ret;
}
int cch_start(struct gru_context_configuration_handle *cch)
@@ -96,9 +126,18 @@ int cch_interrupt(struct gru_context_configuration_handle *cch)
int cch_deallocate(struct gru_context_configuration_handle *cch)
{
+ int ret;
+
cch->opc = CCHOP_DEALLOCATE;
start_instruction(cch);
- return wait_instruction_complete(cch, cchop_deallocate);
+ ret = wait_instruction_complete(cch, cchop_deallocate);
+
+ /*
+ * Stop speculation into the GSEG being unmapped by the previous
+ * DEALLOCATE.
+ */
+ sync_core();
+ return ret;
}
int cch_interrupt_sync(struct gru_context_configuration_handle
@@ -126,17 +165,20 @@ int tgh_invalidate(struct gru_tlb_global_handle *tgh,
return wait_instruction_complete(tgh, tghop_invalidate);
}
-void tfh_write_only(struct gru_tlb_fault_handle *tfh,
- unsigned long pfn, unsigned long vaddr,
- int asid, int dirty, int pagesize)
+int tfh_write_only(struct gru_tlb_fault_handle *tfh,
+ unsigned long paddr, int gaa,
+ unsigned long vaddr, int asid, int dirty,
+ int pagesize)
{
tfh->fillasid = asid;
tfh->fillvaddr = vaddr;
- tfh->pfn = pfn;
+ tfh->pfn = paddr >> GRU_PADDR_SHIFT;
+ tfh->gaa = gaa;
tfh->dirty = dirty;
tfh->pagesize = pagesize;
tfh->opc = TFHOP_WRITE_ONLY;
start_instruction(tfh);
+ return wait_instruction_complete(tfh, tfhop_write_only);
}
void tfh_write_restart(struct gru_tlb_fault_handle *tfh,
diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h
index f44112242d00..3f998b924d8f 100644
--- a/drivers/misc/sgi-gru/gruhandles.h
+++ b/drivers/misc/sgi-gru/gruhandles.h
@@ -91,6 +91,12 @@
/* Convert an arbitrary handle address to the beginning of the GRU segment */
#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1)))
+/* Test a valid handle address to determine the type */
+#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \
+ GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \
+ (((h) & (GRU_HANDLE_STRIDE - 1)) == 0))
+
+
/* General addressing macros. */
static inline void *get_gseg_base_address(void *base, int ctxnum)
{
@@ -158,6 +164,16 @@ static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet)
return vaddr + GRU_SIZE * (2 * pnode + chiplet);
}
+static inline struct gru_control_block_extended *gru_tfh_to_cbe(
+ struct gru_tlb_fault_handle *tfh)
+{
+ unsigned long cbe;
+
+ cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE;
+ return (struct gru_control_block_extended*)cbe;
+}
+
+
/*
@@ -236,6 +252,17 @@ enum gru_tgh_state {
TGHSTATE_RESTART_CTX,
};
+enum gru_tgh_cause {
+ TGHCAUSE_RR_ECC,
+ TGHCAUSE_TLB_ECC,
+ TGHCAUSE_LRU_ECC,
+ TGHCAUSE_PS_ECC,
+ TGHCAUSE_MUL_ERR,
+ TGHCAUSE_DATA_ERR,
+ TGHCAUSE_SW_FORCE
+};
+
+
/*
* TFH - TLB Global Handle
* Used for TLB dropins into the GRU TLB.
@@ -440,6 +467,12 @@ struct gru_control_block_extended {
unsigned int cbrexecstatus:8;
};
+/* CBE fields for active BCOPY instructions */
+#define cbe_baddr0 idef1upd
+#define cbe_baddr1 idef3upd
+#define cbe_src_cl idef6cpy
+#define cbe_nelemcur idef5upd
+
enum gru_cbr_state {
CBRSTATE_INACTIVE,
CBRSTATE_IDLE,
@@ -487,8 +520,8 @@ int cch_interrupt_sync(struct gru_context_configuration_handle *cch);
int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr,
unsigned long vaddrmask, int asid, int pagesize, int global, int n,
unsigned short ctxbitmap);
-void tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long pfn,
- unsigned long vaddr, int asid, int dirty, int pagesize);
+int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
+ int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
void tfh_restart(struct gru_tlb_fault_handle *tfh);
diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c
index 55eabfa85585..9b2062d17327 100644
--- a/drivers/misc/sgi-gru/grukdump.c
+++ b/drivers/misc/sgi-gru/grukdump.c
@@ -44,7 +44,8 @@ static int gru_user_copy_handle(void __user **dp, void *s)
static int gru_dump_context_data(void *grubase,
struct gru_context_configuration_handle *cch,
- void __user *ubuf, int ctxnum, int dsrcnt)
+ void __user *ubuf, int ctxnum, int dsrcnt,
+ int flush_cbrs)
{
void *cb, *cbe, *tfh, *gseg;
int i, scr;
@@ -55,6 +56,8 @@ static int gru_dump_context_data(void *grubase,
tfh = grubase + GRU_TFH_BASE;
for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
+ if (flush_cbrs)
+ gru_flush_cache(cb);
if (gru_user_copy_handle(&ubuf, cb))
goto fail;
if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE))
@@ -115,7 +118,7 @@ fail:
static int gru_dump_context(struct gru_state *gru, int ctxnum,
void __user *ubuf, void __user *ubufend, char data_opt,
- char lock_cch)
+ char lock_cch, char flush_cbrs)
{
struct gru_dump_context_header hdr;
struct gru_dump_context_header __user *uhdr = ubuf;
@@ -159,8 +162,7 @@ static int gru_dump_context(struct gru_state *gru, int ctxnum,
ret = -EFBIG;
else
ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
- dsrcnt);
-
+ dsrcnt, flush_cbrs);
}
if (cch_locked)
unlock_cch_handle(cch);
@@ -215,7 +217,8 @@ int gru_dump_chiplet_request(unsigned long arg)
for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
if (req.ctxnum == ctxnum || req.ctxnum < 0) {
ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
- req.data_opt, req.lock_cch);
+ req.data_opt, req.lock_cch,
+ req.flush_cbrs);
if (ret < 0)
goto fail;
ubuf += ret;
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c
index 766e21e15574..34749ee88dfa 100644
--- a/drivers/misc/sgi-gru/grukservices.c
+++ b/drivers/misc/sgi-gru/grukservices.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
+#include <asm/io_apic.h>
#include "gru.h"
#include "grulib.h"
#include "grutables.h"
@@ -97,9 +98,6 @@
#define ASYNC_HAN_TO_BID(h) ((h) - 1)
#define ASYNC_BID_TO_HAN(b) ((b) + 1)
#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)]
-#define KCB_TO_GID(cb) ((cb - gru_start_vaddr) / \
- (GRU_SIZE * GRU_CHIPLETS_PER_BLADE))
-#define KCB_TO_BS(cb) gru_base[KCB_TO_GID(cb)]
#define GRU_NUM_KERNEL_CBR 1
#define GRU_NUM_KERNEL_DSR_BYTES 256
@@ -160,8 +158,10 @@ static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
up_read(&bs->bs_kgts_sema);
down_write(&bs->bs_kgts_sema);
- if (!bs->bs_kgts)
- bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0);
+ if (!bs->bs_kgts) {
+ bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0);
+ bs->bs_kgts->ts_user_blade_id = blade_id;
+ }
kgts = bs->bs_kgts;
if (!kgts->ts_gru) {
@@ -172,9 +172,9 @@ static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU(
GRU_NUM_KERNEL_DSR_BYTES * ncpus +
bs->bs_async_dsr_bytes);
- while (!gru_assign_gru_context(kgts, blade_id)) {
+ while (!gru_assign_gru_context(kgts)) {
msleep(1);
- gru_steal_context(kgts, blade_id);
+ gru_steal_context(kgts);
}
gru_load_context(kgts);
gru = bs->bs_kgts->ts_gru;
@@ -200,13 +200,15 @@ static int gru_free_kernel_contexts(void)
bs = gru_base[bid];
if (!bs)
continue;
+
+ /* Ignore busy contexts. Don't want to block here. */
if (down_write_trylock(&bs->bs_kgts_sema)) {
kgts = bs->bs_kgts;
if (kgts && kgts->ts_gru)
gru_unload_context(kgts, 0);
- kfree(kgts);
bs->bs_kgts = NULL;
up_write(&bs->bs_kgts_sema);
+ kfree(kgts);
} else {
ret++;
}
@@ -220,13 +222,21 @@ static int gru_free_kernel_contexts(void)
static struct gru_blade_state *gru_lock_kernel_context(int blade_id)
{
struct gru_blade_state *bs;
+ int bid;
STAT(lock_kernel_context);
- bs = gru_base[blade_id];
+again:
+ bid = blade_id < 0 ? uv_numa_blade_id() : blade_id;
+ bs = gru_base[bid];
+ /* Handle the case where migration occured while waiting for the sema */
down_read(&bs->bs_kgts_sema);
+ if (blade_id < 0 && bid != uv_numa_blade_id()) {
+ up_read(&bs->bs_kgts_sema);
+ goto again;
+ }
if (!bs->bs_kgts || !bs->bs_kgts->ts_gru)
- gru_load_kernel_context(bs, blade_id);
+ gru_load_kernel_context(bs, bid);
return bs;
}
@@ -255,7 +265,7 @@ static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr)
BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES);
preempt_disable();
- bs = gru_lock_kernel_context(uv_numa_blade_id());
+ bs = gru_lock_kernel_context(-1);
lcpu = uv_blade_processor_id();
*cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE;
*dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES;
@@ -384,13 +394,31 @@ int gru_get_cb_exception_detail(void *cb,
struct control_block_extended_exc_detail *excdet)
{
struct gru_control_block_extended *cbe;
- struct gru_blade_state *bs;
- int cbrnum;
-
- bs = KCB_TO_BS(cb);
- cbrnum = thread_cbr_number(bs->bs_kgts, get_cb_number(cb));
+ struct gru_thread_state *kgts = NULL;
+ unsigned long off;
+ int cbrnum, bid;
+
+ /*
+ * Locate kgts for cb. This algorithm is SLOW but
+ * this function is rarely called (ie., almost never).
+ * Performance does not matter.
+ */
+ for_each_possible_blade(bid) {
+ if (!gru_base[bid])
+ break;
+ kgts = gru_base[bid]->bs_kgts;
+ if (!kgts || !kgts->ts_gru)
+ continue;
+ off = cb - kgts->ts_gru->gs_gru_base_vaddr;
+ if (off < GRU_SIZE)
+ break;
+ kgts = NULL;
+ }
+ BUG_ON(!kgts);
+ cbrnum = thread_cbr_number(kgts, get_cb_number(cb));
cbe = get_cbe(GRUBASE(cb), cbrnum);
gru_flush_cache(cbe); /* CBE not coherent */
+ sync_core();
excdet->opc = cbe->opccpy;
excdet->exopc = cbe->exopccpy;
excdet->ecause = cbe->ecause;
@@ -409,8 +437,8 @@ char *gru_get_cb_exception_detail_str(int ret, void *cb,
if (ret > 0 && gen->istatus == CBS_EXCEPTION) {
gru_get_cb_exception_detail(cb, &excdet);
snprintf(buf, size,
- "GRU exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
- "excdet0 0x%lx, excdet1 0x%x",
+ "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
+ "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(),
gen, excdet.opc, excdet.exopc, excdet.ecause,
excdet.exceptdet0, excdet.exceptdet1);
} else {
@@ -457,9 +485,10 @@ int gru_check_status_proc(void *cb)
int ret;
ret = gen->istatus;
- if (ret != CBS_EXCEPTION)
- return ret;
- return gru_retry_exception(cb);
+ if (ret == CBS_EXCEPTION)
+ ret = gru_retry_exception(cb);
+ rmb();
+ return ret;
}
@@ -471,7 +500,7 @@ int gru_wait_proc(void *cb)
ret = gru_wait_idle_or_exception(gen);
if (ret == CBS_EXCEPTION)
ret = gru_retry_exception(cb);
-
+ rmb();
return ret;
}
@@ -538,7 +567,7 @@ int gru_create_message_queue(struct gru_message_queue_desc *mqd,
mqd->mq = mq;
mqd->mq_gpa = uv_gpa(mq);
mqd->qlines = qlines;
- mqd->interrupt_pnode = UV_NASID_TO_PNODE(nasid);
+ mqd->interrupt_pnode = nasid >> 1;
mqd->interrupt_vector = vector;
mqd->interrupt_apicid = apicid;
return 0;
@@ -598,6 +627,8 @@ static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd,
ret = MQE_UNEXPECTED_CB_ERR;
break;
case CBSS_PAGE_OVERFLOW:
+ STAT(mesq_noop_page_overflow);
+ /* fallthru */
default:
BUG();
}
@@ -673,18 +704,6 @@ cberr:
}
/*
- * Send a cross-partition interrupt to the SSI that contains the target
- * message queue. Normally, the interrupt is automatically delivered by hardware
- * but some error conditions require explicit delivery.
- */
-static void send_message_queue_interrupt(struct gru_message_queue_desc *mqd)
-{
- if (mqd->interrupt_vector)
- uv_hub_send_ipi(mqd->interrupt_pnode, mqd->interrupt_apicid,
- mqd->interrupt_vector);
-}
-
-/*
* Handle a PUT failure. Note: if message was a 2-line message, one of the
* lines might have successfully have been written. Before sending the
* message, "present" must be cleared in BOTH lines to prevent the receiver
@@ -693,7 +712,8 @@ static void send_message_queue_interrupt(struct gru_message_queue_desc *mqd)
static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
void *mesg, int lines)
{
- unsigned long m;
+ unsigned long m, *val = mesg, gpa, save;
+ int ret;
m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
if (lines == 2) {
@@ -704,7 +724,26 @@ static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA);
if (gru_wait(cb) != CBS_IDLE)
return MQE_UNEXPECTED_CB_ERR;
- send_message_queue_interrupt(mqd);
+
+ if (!mqd->interrupt_vector)
+ return MQE_OK;
+
+ /*
+ * Send a cross-partition interrupt to the SSI that contains the target
+ * message queue. Normally, the interrupt is automatically delivered by
+ * hardware but some error conditions require explicit delivery.
+ * Use the GRU to deliver the interrupt. Otherwise partition failures
+ * could cause unrecovered errors.
+ */
+ gpa = uv_global_gru_mmr_address(mqd->interrupt_pnode, UVH_IPI_INT);
+ save = *val;
+ *val = uv_hub_ipi_value(mqd->interrupt_apicid, mqd->interrupt_vector,
+ dest_Fixed);
+ gru_vstore_phys(cb, gpa, gru_get_tri(mesg), IAA_REGISTER, IMA);
+ ret = gru_wait(cb);
+ *val = save;
+ if (ret != CBS_IDLE)
+ return MQE_UNEXPECTED_CB_ERR;
return MQE_OK;
}
@@ -739,6 +778,9 @@ static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd,
STAT(mesq_send_put_nacked);
ret = send_message_put_nacked(cb, mqd, mesg, lines);
break;
+ case CBSS_PAGE_OVERFLOW:
+ STAT(mesq_page_overflow);
+ /* fallthru */
default:
BUG();
}
@@ -831,7 +873,6 @@ void *gru_get_next_message(struct gru_message_queue_desc *mqd)
int present = mhdr->present;
/* skip NOOP messages */
- STAT(mesq_receive);
while (present == MQS_NOOP) {
gru_free_message(mqd, mhdr);
mhdr = mq->next;
@@ -851,6 +892,7 @@ void *gru_get_next_message(struct gru_message_queue_desc *mqd)
if (mhdr->lines == 2)
restore_present2(mhdr, mhdr->present2);
+ STAT(mesq_receive);
return mhdr;
}
EXPORT_SYMBOL_GPL(gru_get_next_message);
@@ -858,6 +900,29 @@ EXPORT_SYMBOL_GPL(gru_get_next_message);
/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/
/*
+ * Load a DW from a global GPA. The GPA can be a memory or MMR address.
+ */
+int gru_read_gpa(unsigned long *value, unsigned long gpa)
+{
+ void *cb;
+ void *dsr;
+ int ret, iaa;
+
+ STAT(read_gpa);
+ if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
+ return MQE_BUG_NO_RESOURCES;
+ iaa = gpa >> 62;
+ gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA);
+ ret = gru_wait(cb);
+ if (ret == CBS_IDLE)
+ *value = *(unsigned long *)dsr;
+ gru_free_cpu_resources(cb, dsr);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gru_read_gpa);
+
+
+/*
* Copy a block of data using the GRU resources
*/
int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa,
@@ -898,24 +963,24 @@ static int quicktest0(unsigned long arg)
gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
if (gru_wait(cb) != CBS_IDLE) {
- printk(KERN_DEBUG "GRU quicktest0: CBR failure 1\n");
+ printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id());
goto done;
}
if (*p != MAGIC) {
- printk(KERN_DEBUG "GRU: quicktest0 bad magic 0x%lx\n", *p);
+ printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p);
goto done;
}
gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
if (gru_wait(cb) != CBS_IDLE) {
- printk(KERN_DEBUG "GRU quicktest0: CBR failure 2\n");
+ printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id());
goto done;
}
if (word0 != word1 || word1 != MAGIC) {
printk(KERN_DEBUG
- "GRU quicktest0 err: found 0x%lx, expected 0x%lx\n",
- word1, MAGIC);
+ "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n",
+ smp_processor_id(), word1, MAGIC);
goto done;
}
ret = 0;
@@ -952,8 +1017,11 @@ static int quicktest1(unsigned long arg)
if (ret)
break;
}
- if (ret != MQE_QUEUE_FULL || i != 4)
+ if (ret != MQE_QUEUE_FULL || i != 4) {
+ printk(KERN_DEBUG "GRU:%d quicktest1: unexpect status %d, i %d\n",
+ smp_processor_id(), ret, i);
goto done;
+ }
for (i = 0; i < 6; i++) {
m = gru_get_next_message(&mqd);
@@ -961,7 +1029,12 @@ static int quicktest1(unsigned long arg)
break;
gru_free_message(&mqd, m);
}
- ret = (i == 4) ? 0 : -EIO;
+ if (i != 4) {
+ printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n",
+ smp_processor_id(), i, m, m ? m[8] : -1);
+ goto done;
+ }
+ ret = 0;
done:
kfree(p);
@@ -977,6 +1050,7 @@ static int quicktest2(unsigned long arg)
int ret = 0;
unsigned long *buf;
void *cb0, *cb;
+ struct gru_control_block_status *gen;
int i, k, istatus, bytes;
bytes = numcb * 4 * 8;
@@ -996,20 +1070,30 @@ static int quicktest2(unsigned long arg)
XTYPE_DW, 4, 1, IMA_INTERRUPT);
ret = 0;
- for (k = 0; k < numcb; k++) {
+ k = numcb;
+ do {
gru_wait_async_cbr(han);
for (i = 0; i < numcb; i++) {
cb = cb0 + i * GRU_HANDLE_STRIDE;
istatus = gru_check_status(cb);
- if (istatus == CBS_ACTIVE)
- continue;
- if (istatus == CBS_EXCEPTION)
- ret = -EFAULT;
- else if (buf[i] || buf[i + 1] || buf[i + 2] ||
- buf[i + 3])
- ret = -EIO;
+ if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS)
+ break;
}
- }
+ if (i == numcb)
+ continue;
+ if (istatus != CBS_IDLE) {
+ printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i);
+ ret = -EFAULT;
+ } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] ||
+ buf[4 * i + 3]) {
+ printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]);
+ ret = -EIO;
+ }
+ k--;
+ gen = cb;
+ gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */
+ } while (k);
BUG_ON(cmp.done);
gru_unlock_async_resource(han);
@@ -1019,6 +1103,22 @@ done:
return ret;
}
+#define BUFSIZE 200
+static int quicktest3(unsigned long arg)
+{
+ char buf1[BUFSIZE], buf2[BUFSIZE];
+ int ret = 0;
+
+ memset(buf2, 0, sizeof(buf2));
+ memset(buf1, get_cycles() & 255, sizeof(buf1));
+ gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE);
+ if (memcmp(buf1, buf2, BUFSIZE)) {
+ printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id());
+ ret = -EIO;
+ }
+ return ret;
+}
+
/*
* Debugging only. User hook for various kernel tests
* of driver & gru.
@@ -1037,6 +1137,9 @@ int gru_ktest(unsigned long arg)
case 2:
ret = quicktest2(arg);
break;
+ case 3:
+ ret = quicktest3(arg);
+ break;
case 99:
ret = gru_free_kernel_contexts();
break;
diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h
index d60d34bca44d..02aa94d8484a 100644
--- a/drivers/misc/sgi-gru/grukservices.h
+++ b/drivers/misc/sgi-gru/grukservices.h
@@ -131,6 +131,20 @@ extern void *gru_get_next_message(struct gru_message_queue_desc *mqd);
/*
+ * Read a GRU global GPA. Source can be located in a remote partition.
+ *
+ * Input:
+ * value memory address where MMR value is returned
+ * gpa source numalink physical address of GPA
+ *
+ * Output:
+ * 0 OK
+ * >0 error
+ */
+int gru_read_gpa(unsigned long *value, unsigned long gpa);
+
+
+/*
* Copy data using the GRU. Source or destination can be located in a remote
* partition.
*
diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h
index 889bc442a3e8..e77d1b1f9d05 100644
--- a/drivers/misc/sgi-gru/grulib.h
+++ b/drivers/misc/sgi-gru/grulib.h
@@ -63,18 +63,9 @@
#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th))
#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1)))
-/*
- * Statictics kept on a per-GTS basis.
- */
-struct gts_statistics {
- unsigned long fmm_tlbdropin;
- unsigned long upm_tlbdropin;
- unsigned long context_stolen;
-};
-
struct gru_get_gseg_statistics_req {
- unsigned long gseg;
- struct gts_statistics stats;
+ unsigned long gseg;
+ struct gru_gseg_statistics stats;
};
/*
@@ -86,6 +77,7 @@ struct gru_create_context_req {
unsigned int control_blocks;
unsigned int maximum_thread_count;
unsigned int options;
+ unsigned char tlb_preload_count;
};
/*
@@ -98,11 +90,12 @@ struct gru_unload_context_req {
/*
* Structure used to set context options
*/
-enum {sco_gseg_owner, sco_cch_req_slice};
+enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet};
struct gru_set_context_option_req {
unsigned long gseg;
int op;
- unsigned long val1;
+ int val0;
+ long val1;
};
/*
@@ -124,6 +117,8 @@ struct gru_dump_chiplet_state_req {
int ctxnum;
char data_opt;
char lock_cch;
+ char flush_cbrs;
+ char fill[10];
pid_t pid;
void *buf;
size_t buflen;
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c
index 3bc643dad606..f8538bbd0bfa 100644
--- a/drivers/misc/sgi-gru/grumain.c
+++ b/drivers/misc/sgi-gru/grumain.c
@@ -27,6 +27,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/list.h>
+#include <linux/err.h>
#include <asm/uv/uv_hub.h>
#include "gru.h"
#include "grutables.h"
@@ -48,12 +49,20 @@ struct device *grudev = &gru_device;
/*
* Select a gru fault map to be used by the current cpu. Note that
* multiple cpus may be using the same map.
- * ZZZ should "shift" be used?? Depends on HT cpu numbering
* ZZZ should be inline but did not work on emulator
*/
int gru_cpu_fault_map_id(void)
{
+#ifdef CONFIG_IA64
return uv_blade_processor_id() % GRU_NUM_TFM;
+#else
+ int cpu = smp_processor_id();
+ int id, core;
+
+ core = uv_cpu_core_number(cpu);
+ id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
+ return id;
+#endif
}
/*--------- ASID Management -------------------------------------------
@@ -286,7 +295,8 @@ static void gru_unload_mm_tracker(struct gru_state *gru,
void gts_drop(struct gru_thread_state *gts)
{
if (gts && atomic_dec_return(&gts->ts_refcnt) == 0) {
- gru_drop_mmu_notifier(gts->ts_gms);
+ if (gts->ts_gms)
+ gru_drop_mmu_notifier(gts->ts_gms);
kfree(gts);
STAT(gts_free);
}
@@ -310,16 +320,18 @@ static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data
* Allocate a thread state structure.
*/
struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
- int cbr_au_count, int dsr_au_count, int options, int tsid)
+ int cbr_au_count, int dsr_au_count,
+ unsigned char tlb_preload_count, int options, int tsid)
{
struct gru_thread_state *gts;
+ struct gru_mm_struct *gms;
int bytes;
bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count);
bytes += sizeof(struct gru_thread_state);
gts = kmalloc(bytes, GFP_KERNEL);
if (!gts)
- return NULL;
+ return ERR_PTR(-ENOMEM);
STAT(gts_alloc);
memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */
@@ -327,7 +339,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
mutex_init(&gts->ts_ctxlock);
gts->ts_cbr_au_count = cbr_au_count;
gts->ts_dsr_au_count = dsr_au_count;
+ gts->ts_tlb_preload_count = tlb_preload_count;
gts->ts_user_options = options;
+ gts->ts_user_blade_id = -1;
+ gts->ts_user_chiplet_id = -1;
gts->ts_tsid = tsid;
gts->ts_ctxnum = NULLCTX;
gts->ts_tlb_int_select = -1;
@@ -336,9 +351,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
if (vma) {
gts->ts_mm = current->mm;
gts->ts_vma = vma;
- gts->ts_gms = gru_register_mmu_notifier();
- if (!gts->ts_gms)
+ gms = gru_register_mmu_notifier();
+ if (IS_ERR(gms))
goto err;
+ gts->ts_gms = gms;
}
gru_dbg(grudev, "alloc gts %p\n", gts);
@@ -346,7 +362,7 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
err:
gts_drop(gts);
- return NULL;
+ return ERR_CAST(gms);
}
/*
@@ -360,6 +376,7 @@ struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid)
if (!vdata)
return NULL;
+ STAT(vdata_alloc);
INIT_LIST_HEAD(&vdata->vd_head);
spin_lock_init(&vdata->vd_lock);
gru_dbg(grudev, "alloc vdata %p\n", vdata);
@@ -392,10 +409,12 @@ struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma,
struct gru_vma_data *vdata = vma->vm_private_data;
struct gru_thread_state *gts, *ngts;
- gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, vdata->vd_dsr_au_count,
+ gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count,
+ vdata->vd_dsr_au_count,
+ vdata->vd_tlb_preload_count,
vdata->vd_user_options, tsid);
- if (!gts)
- return NULL;
+ if (IS_ERR(gts))
+ return gts;
spin_lock(&vdata->vd_lock);
ngts = gru_find_current_gts_nolock(vdata, tsid);
@@ -493,6 +512,9 @@ static void gru_load_context_data(void *save, void *grubase, int ctxnum,
memset(cbe + i * GRU_HANDLE_STRIDE, 0,
GRU_CACHE_LINE_BYTES);
}
+ /* Flush CBE to hide race in context restart */
+ mb();
+ gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
cb += GRU_HANDLE_STRIDE;
}
@@ -513,6 +535,12 @@ static void gru_unload_context_data(void *save, void *grubase, int ctxnum,
cb = gseg + GRU_CB_BASE;
cbe = grubase + GRU_CBE_BASE;
length = hweight64(dsrmap) * GRU_DSR_AU_BYTES;
+
+ /* CBEs may not be coherent. Flush them from cache */
+ for_each_cbr_in_allocation_map(i, &cbrmap, scr)
+ gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
+ mb(); /* Let the CL flush complete */
+
gru_prefetch_context(gseg, cb, cbe, cbrmap, length);
for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
@@ -533,7 +561,8 @@ void gru_unload_context(struct gru_thread_state *gts, int savestate)
zap_vma_ptes(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE);
cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
- gru_dbg(grudev, "gts %p\n", gts);
+ gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n",
+ gts, gts->ts_cbr_map, gts->ts_dsr_map);
lock_cch_handle(cch);
if (cch_interrupt_sync(cch))
BUG();
@@ -549,7 +578,6 @@ void gru_unload_context(struct gru_thread_state *gts, int savestate)
if (cch_deallocate(cch))
BUG();
- gts->ts_force_unload = 0; /* ts_force_unload locked by CCH lock */
unlock_cch_handle(cch);
gru_free_gru_context(gts);
@@ -565,9 +593,7 @@ void gru_load_context(struct gru_thread_state *gts)
struct gru_context_configuration_handle *cch;
int i, err, asid, ctxnum = gts->ts_ctxnum;
- gru_dbg(grudev, "gts %p\n", gts);
cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
-
lock_cch_handle(cch);
cch->tfm_fault_bit_enable =
(gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
@@ -591,6 +617,7 @@ void gru_load_context(struct gru_thread_state *gts)
cch->unmap_enable = 1;
cch->tfm_done_bit_enable = 1;
cch->cb_int_enable = 1;
+ cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */
} else {
cch->unmap_enable = 0;
cch->tfm_done_bit_enable = 0;
@@ -616,17 +643,18 @@ void gru_load_context(struct gru_thread_state *gts)
if (cch_start(cch))
BUG();
unlock_cch_handle(cch);
+
+ gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n",
+ gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map,
+ (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select);
}
/*
* Update fields in an active CCH:
* - retarget interrupts on local blade
* - update sizeavail mask
- * - force a delayed context unload by clearing the CCH asids. This
- * forces TLB misses for new GRU instructions. The context is unloaded
- * when the next TLB miss occurs.
*/
-int gru_update_cch(struct gru_thread_state *gts, int force_unload)
+int gru_update_cch(struct gru_thread_state *gts)
{
struct gru_context_configuration_handle *cch;
struct gru_state *gru = gts->ts_gru;
@@ -640,21 +668,13 @@ int gru_update_cch(struct gru_thread_state *gts, int force_unload)
goto exit;
if (cch_interrupt(cch))
BUG();
- if (!force_unload) {
- for (i = 0; i < 8; i++)
- cch->sizeavail[i] = gts->ts_sizeavail;
- gts->ts_tlb_int_select = gru_cpu_fault_map_id();
- cch->tlb_int_select = gru_cpu_fault_map_id();
- cch->tfm_fault_bit_enable =
- (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
- || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- } else {
- for (i = 0; i < 8; i++)
- cch->asid[i] = 0;
- cch->tfm_fault_bit_enable = 0;
- cch->tlb_int_enable = 0;
- gts->ts_force_unload = 1;
- }
+ for (i = 0; i < 8; i++)
+ cch->sizeavail[i] = gts->ts_sizeavail;
+ gts->ts_tlb_int_select = gru_cpu_fault_map_id();
+ cch->tlb_int_select = gru_cpu_fault_map_id();
+ cch->tfm_fault_bit_enable =
+ (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
+ || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
if (cch_start(cch))
BUG();
ret = 1;
@@ -679,7 +699,54 @@ static int gru_retarget_intr(struct gru_thread_state *gts)
gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select,
gru_cpu_fault_map_id());
- return gru_update_cch(gts, 0);
+ return gru_update_cch(gts);
+}
+
+/*
+ * Check if a GRU context is allowed to use a specific chiplet. By default
+ * a context is assigned to any blade-local chiplet. However, users can
+ * override this.
+ * Returns 1 if assignment allowed, 0 otherwise
+ */
+static int gru_check_chiplet_assignment(struct gru_state *gru,
+ struct gru_thread_state *gts)
+{
+ int blade_id;
+ int chiplet_id;
+
+ blade_id = gts->ts_user_blade_id;
+ if (blade_id < 0)
+ blade_id = uv_numa_blade_id();
+
+ chiplet_id = gts->ts_user_chiplet_id;
+ return gru->gs_blade_id == blade_id &&
+ (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id);
+}
+
+/*
+ * Unload the gru context if it is not assigned to the correct blade or
+ * chiplet. Misassignment can occur if the process migrates to a different
+ * blade or if the user changes the selected blade/chiplet.
+ */
+void gru_check_context_placement(struct gru_thread_state *gts)
+{
+ struct gru_state *gru;
+
+ /*
+ * If the current task is the context owner, verify that the
+ * context is correctly placed. This test is skipped for non-owner
+ * references. Pthread apps use non-owner references to the CBRs.
+ */
+ gru = gts->ts_gru;
+ if (!gru || gts->ts_tgid_owner != current->tgid)
+ return;
+
+ if (!gru_check_chiplet_assignment(gru, gts)) {
+ STAT(check_context_unload);
+ gru_unload_context(gts, 1);
+ } else if (gru_retarget_intr(gts)) {
+ STAT(check_context_retarget_intr);
+ }
}
@@ -712,13 +779,17 @@ static void gts_stolen(struct gru_thread_state *gts,
}
}
-void gru_steal_context(struct gru_thread_state *gts, int blade_id)
+void gru_steal_context(struct gru_thread_state *gts)
{
struct gru_blade_state *blade;
struct gru_state *gru, *gru0;
struct gru_thread_state *ngts = NULL;
int ctxnum, ctxnum0, flag = 0, cbr, dsr;
+ int blade_id;
+ blade_id = gts->ts_user_blade_id;
+ if (blade_id < 0)
+ blade_id = uv_numa_blade_id();
cbr = gts->ts_cbr_au_count;
dsr = gts->ts_dsr_au_count;
@@ -729,35 +800,39 @@ void gru_steal_context(struct gru_thread_state *gts, int blade_id)
gru = blade->bs_lru_gru;
if (ctxnum == 0)
gru = next_gru(blade, gru);
+ blade->bs_lru_gru = gru;
+ blade->bs_lru_ctxnum = ctxnum;
ctxnum0 = ctxnum;
gru0 = gru;
while (1) {
- if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH))
- break;
- spin_lock(&gru->gs_lock);
- for (; ctxnum < GRU_NUM_CCH; ctxnum++) {
- if (flag && gru == gru0 && ctxnum == ctxnum0)
+ if (gru_check_chiplet_assignment(gru, gts)) {
+ if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH))
break;
- ngts = gru->gs_gts[ctxnum];
- /*
- * We are grabbing locks out of order, so trylock is
- * needed. GTSs are usually not locked, so the odds of
- * success are high. If trylock fails, try to steal a
- * different GSEG.
- */
- if (ngts && is_gts_stealable(ngts, blade))
+ spin_lock(&gru->gs_lock);
+ for (; ctxnum < GRU_NUM_CCH; ctxnum++) {
+ if (flag && gru == gru0 && ctxnum == ctxnum0)
+ break;
+ ngts = gru->gs_gts[ctxnum];
+ /*
+ * We are grabbing locks out of order, so trylock is
+ * needed. GTSs are usually not locked, so the odds of
+ * success are high. If trylock fails, try to steal a
+ * different GSEG.
+ */
+ if (ngts && is_gts_stealable(ngts, blade))
+ break;
+ ngts = NULL;
+ }
+ spin_unlock(&gru->gs_lock);
+ if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0))
break;
- ngts = NULL;
- flag = 1;
}
- spin_unlock(&gru->gs_lock);
- if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0))
+ if (flag && gru == gru0)
break;
+ flag = 1;
ctxnum = 0;
gru = next_gru(blade, gru);
}
- blade->bs_lru_gru = gru;
- blade->bs_lru_ctxnum = ctxnum;
spin_unlock(&blade->bs_lock);
if (ngts) {
@@ -776,19 +851,34 @@ void gru_steal_context(struct gru_thread_state *gts, int blade_id)
}
/*
+ * Assign a gru context.
+ */
+static int gru_assign_context_number(struct gru_state *gru)
+{
+ int ctxnum;
+
+ ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH);
+ __set_bit(ctxnum, &gru->gs_context_map);
+ return ctxnum;
+}
+
+/*
* Scan the GRUs on the local blade & assign a GRU context.
*/
-struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts,
- int blade)
+struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts)
{
struct gru_state *gru, *grux;
int i, max_active_contexts;
+ int blade_id = gts->ts_user_blade_id;
-
+ if (blade_id < 0)
+ blade_id = uv_numa_blade_id();
again:
gru = NULL;
max_active_contexts = GRU_NUM_CCH;
- for_each_gru_on_blade(grux, blade, i) {
+ for_each_gru_on_blade(grux, blade_id, i) {
+ if (!gru_check_chiplet_assignment(grux, gts))
+ continue;
if (check_gru_resources(grux, gts->ts_cbr_au_count,
gts->ts_dsr_au_count,
max_active_contexts)) {
@@ -809,12 +899,9 @@ again:
reserve_gru_resources(gru, gts);
gts->ts_gru = gru;
gts->ts_blade = gru->gs_blade_id;
- gts->ts_ctxnum =
- find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH);
- BUG_ON(gts->ts_ctxnum == GRU_NUM_CCH);
+ gts->ts_ctxnum = gru_assign_context_number(gru);
atomic_inc(&gts->ts_refcnt);
gru->gs_gts[gts->ts_ctxnum] = gts;
- __set_bit(gts->ts_ctxnum, &gru->gs_context_map);
spin_unlock(&gru->gs_lock);
STAT(assign_context);
@@ -842,7 +929,6 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct gru_thread_state *gts;
unsigned long paddr, vaddr;
- int blade_id;
vaddr = (unsigned long)vmf->virtual_address;
gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n",
@@ -857,28 +943,18 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
again:
mutex_lock(&gts->ts_ctxlock);
preempt_disable();
- blade_id = uv_numa_blade_id();
- if (gts->ts_gru) {
- if (gts->ts_gru->gs_blade_id != blade_id) {
- STAT(migrated_nopfn_unload);
- gru_unload_context(gts, 1);
- } else {
- if (gru_retarget_intr(gts))
- STAT(migrated_nopfn_retarget);
- }
- }
+ gru_check_context_placement(gts);
if (!gts->ts_gru) {
STAT(load_user_context);
- if (!gru_assign_gru_context(gts, blade_id)) {
+ if (!gru_assign_gru_context(gts)) {
preempt_enable();
mutex_unlock(&gts->ts_ctxlock);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */
- blade_id = uv_numa_blade_id();
if (gts->ts_steal_jiffies + GRU_STEAL_DELAY < jiffies)
- gru_steal_context(gts, blade_id);
+ gru_steal_context(gts);
goto again;
}
gru_load_context(gts);
diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c
index 3f2375c5ba5b..7768b87d995b 100644
--- a/drivers/misc/sgi-gru/gruprocfs.c
+++ b/drivers/misc/sgi-gru/gruprocfs.c
@@ -36,8 +36,7 @@ static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id)
{
unsigned long val = atomic_long_read(v);
- if (val)
- seq_printf(s, "%16lu %s\n", val, id);
+ seq_printf(s, "%16lu %s\n", val, id);
}
static int statistics_show(struct seq_file *s, void *p)
@@ -46,7 +45,8 @@ static int statistics_show(struct seq_file *s, void *p)
printstat(s, vdata_free);
printstat(s, gts_alloc);
printstat(s, gts_free);
- printstat(s, vdata_double_alloc);
+ printstat(s, gms_alloc);
+ printstat(s, gms_free);
printstat(s, gts_double_allocate);
printstat(s, assign_context);
printstat(s, assign_context_failed);
@@ -59,28 +59,25 @@ static int statistics_show(struct seq_file *s, void *p)
printstat(s, steal_kernel_context);
printstat(s, steal_context_failed);
printstat(s, nopfn);
- printstat(s, break_cow);
printstat(s, asid_new);
printstat(s, asid_next);
printstat(s, asid_wrap);
printstat(s, asid_reuse);
printstat(s, intr);
+ printstat(s, intr_cbr);
+ printstat(s, intr_tfh);
+ printstat(s, intr_spurious);
printstat(s, intr_mm_lock_failed);
printstat(s, call_os);
- printstat(s, call_os_offnode_reference);
- printstat(s, call_os_check_for_bug);
printstat(s, call_os_wait_queue);
printstat(s, user_flush_tlb);
printstat(s, user_unload_context);
printstat(s, user_exception);
printstat(s, set_context_option);
- printstat(s, migrate_check);
- printstat(s, migrated_retarget);
- printstat(s, migrated_unload);
- printstat(s, migrated_unload_delay);
- printstat(s, migrated_nopfn_retarget);
- printstat(s, migrated_nopfn_unload);
+ printstat(s, check_context_retarget_intr);
+ printstat(s, check_context_unload);
printstat(s, tlb_dropin);
+ printstat(s, tlb_preload_page);
printstat(s, tlb_dropin_fail_no_asid);
printstat(s, tlb_dropin_fail_upm);
printstat(s, tlb_dropin_fail_invalid);
@@ -88,16 +85,15 @@ static int statistics_show(struct seq_file *s, void *p)
printstat(s, tlb_dropin_fail_idle);
printstat(s, tlb_dropin_fail_fmm);
printstat(s, tlb_dropin_fail_no_exception);
- printstat(s, tlb_dropin_fail_no_exception_war);
printstat(s, tfh_stale_on_fault);
printstat(s, mmu_invalidate_range);
printstat(s, mmu_invalidate_page);
- printstat(s, mmu_clear_flush_young);
printstat(s, flush_tlb);
printstat(s, flush_tlb_gru);
printstat(s, flush_tlb_gru_tgh);
printstat(s, flush_tlb_gru_zero_asid);
printstat(s, copy_gpa);
+ printstat(s, read_gpa);
printstat(s, mesq_receive);
printstat(s, mesq_receive_none);
printstat(s, mesq_send);
@@ -108,7 +104,6 @@ static int statistics_show(struct seq_file *s, void *p)
printstat(s, mesq_send_qlimit_reached);
printstat(s, mesq_send_amo_nacked);
printstat(s, mesq_send_put_nacked);
- printstat(s, mesq_qf_not_full);
printstat(s, mesq_qf_locked);
printstat(s, mesq_qf_noop_not_full);
printstat(s, mesq_qf_switch_head_failed);
@@ -118,6 +113,7 @@ static int statistics_show(struct seq_file *s, void *p)
printstat(s, mesq_noop_qlimit_reached);
printstat(s, mesq_noop_amo_nacked);
printstat(s, mesq_noop_put_nacked);
+ printstat(s, mesq_noop_page_overflow);
return 0;
}
@@ -133,8 +129,10 @@ static int mcs_statistics_show(struct seq_file *s, void *p)
int op;
unsigned long total, count, max;
static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt",
- "cch_interrupt_sync", "cch_deallocate", "tgh_invalidate"};
+ "cch_interrupt_sync", "cch_deallocate", "tfh_write_only",
+ "tfh_write_restart", "tgh_invalidate"};
+ seq_printf(s, "%-20s%12s%12s%12s\n", "#id", "count", "aver-clks", "max-clks");
for (op = 0; op < mcsop_last; op++) {
count = atomic_long_read(&mcs_op_statistics[op].count);
total = atomic_long_read(&mcs_op_statistics[op].total);
@@ -154,6 +152,7 @@ static ssize_t mcs_statistics_write(struct file *file,
static int options_show(struct seq_file *s, void *p)
{
+ seq_printf(s, "#bitmask: 1=trace, 2=statistics\n");
seq_printf(s, "0x%lx\n", gru_options);
return 0;
}
@@ -183,16 +182,17 @@ static int cch_seq_show(struct seq_file *file, void *data)
const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" };
if (gid == 0)
- seq_printf(file, "#%5s%5s%6s%9s%6s%8s%8s\n", "gid", "bid",
- "ctx#", "pid", "cbrs", "dsbytes", "mode");
+ seq_printf(file, "#%5s%5s%6s%7s%9s%6s%8s%8s\n", "gid", "bid",
+ "ctx#", "asid", "pid", "cbrs", "dsbytes", "mode");
if (gru)
for (i = 0; i < GRU_NUM_CCH; i++) {
ts = gru->gs_gts[i];
if (!ts)
continue;
- seq_printf(file, " %5d%5d%6d%9d%6d%8d%8s\n",
+ seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n",
gru->gs_gid, gru->gs_blade_id, i,
- ts->ts_tgid_owner,
+ is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid,
+ is_kernel_context(ts) ? 0 : ts->ts_tgid_owner,
ts->ts_cbr_au_count * GRU_CBR_AU_SIZE,
ts->ts_cbr_au_count * GRU_DSR_AU_BYTES,
mode[ts->ts_user_options &
@@ -355,7 +355,7 @@ static void delete_proc_files(void)
for (p = proc_files; p->name; p++)
if (p->entry)
remove_proc_entry(p->name, proc_gru);
- remove_proc_entry("gru", NULL);
+ remove_proc_entry("gru", proc_gru->parent);
}
}
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h
index 46990bcfa536..02a77b8b8eef 100644
--- a/drivers/misc/sgi-gru/grutables.h
+++ b/drivers/misc/sgi-gru/grutables.h
@@ -161,7 +161,7 @@ extern unsigned int gru_max_gids;
#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE)
#define GRU_DRIVER_ID_STR "SGI GRU Device Driver"
-#define GRU_DRIVER_VERSION_STR "0.80"
+#define GRU_DRIVER_VERSION_STR "0.85"
/*
* GRU statistics.
@@ -171,7 +171,8 @@ struct gru_stats_s {
atomic_long_t vdata_free;
atomic_long_t gts_alloc;
atomic_long_t gts_free;
- atomic_long_t vdata_double_alloc;
+ atomic_long_t gms_alloc;
+ atomic_long_t gms_free;
atomic_long_t gts_double_allocate;
atomic_long_t assign_context;
atomic_long_t assign_context_failed;
@@ -184,28 +185,25 @@ struct gru_stats_s {
atomic_long_t steal_kernel_context;
atomic_long_t steal_context_failed;
atomic_long_t nopfn;
- atomic_long_t break_cow;
atomic_long_t asid_new;
atomic_long_t asid_next;
atomic_long_t asid_wrap;
atomic_long_t asid_reuse;
atomic_long_t intr;
+ atomic_long_t intr_cbr;
+ atomic_long_t intr_tfh;
+ atomic_long_t intr_spurious;
atomic_long_t intr_mm_lock_failed;
atomic_long_t call_os;
- atomic_long_t call_os_offnode_reference;
- atomic_long_t call_os_check_for_bug;
atomic_long_t call_os_wait_queue;
atomic_long_t user_flush_tlb;
atomic_long_t user_unload_context;
atomic_long_t user_exception;
atomic_long_t set_context_option;
- atomic_long_t migrate_check;
- atomic_long_t migrated_retarget;
- atomic_long_t migrated_unload;
- atomic_long_t migrated_unload_delay;
- atomic_long_t migrated_nopfn_retarget;
- atomic_long_t migrated_nopfn_unload;
+ atomic_long_t check_context_retarget_intr;
+ atomic_long_t check_context_unload;
atomic_long_t tlb_dropin;
+ atomic_long_t tlb_preload_page;
atomic_long_t tlb_dropin_fail_no_asid;
atomic_long_t tlb_dropin_fail_upm;
atomic_long_t tlb_dropin_fail_invalid;
@@ -213,17 +211,16 @@ struct gru_stats_s {
atomic_long_t tlb_dropin_fail_idle;
atomic_long_t tlb_dropin_fail_fmm;
atomic_long_t tlb_dropin_fail_no_exception;
- atomic_long_t tlb_dropin_fail_no_exception_war;
atomic_long_t tfh_stale_on_fault;
atomic_long_t mmu_invalidate_range;
atomic_long_t mmu_invalidate_page;
- atomic_long_t mmu_clear_flush_young;
atomic_long_t flush_tlb;
atomic_long_t flush_tlb_gru;
atomic_long_t flush_tlb_gru_tgh;
atomic_long_t flush_tlb_gru_zero_asid;
atomic_long_t copy_gpa;
+ atomic_long_t read_gpa;
atomic_long_t mesq_receive;
atomic_long_t mesq_receive_none;
@@ -235,7 +232,7 @@ struct gru_stats_s {
atomic_long_t mesq_send_qlimit_reached;
atomic_long_t mesq_send_amo_nacked;
atomic_long_t mesq_send_put_nacked;
- atomic_long_t mesq_qf_not_full;
+ atomic_long_t mesq_page_overflow;
atomic_long_t mesq_qf_locked;
atomic_long_t mesq_qf_noop_not_full;
atomic_long_t mesq_qf_switch_head_failed;
@@ -245,11 +242,13 @@ struct gru_stats_s {
atomic_long_t mesq_noop_qlimit_reached;
atomic_long_t mesq_noop_amo_nacked;
atomic_long_t mesq_noop_put_nacked;
+ atomic_long_t mesq_noop_page_overflow;
};
enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync,
- cchop_deallocate, tghop_invalidate, mcsop_last};
+ cchop_deallocate, tfhop_write_only, tfhop_write_restart,
+ tghop_invalidate, mcsop_last};
struct mcs_op_statistic {
atomic_long_t count;
@@ -259,8 +258,8 @@ struct mcs_op_statistic {
extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
-#define OPT_DPRINT 1
-#define OPT_STATS 2
+#define OPT_DPRINT 1
+#define OPT_STATS 2
#define IRQ_GRU 110 /* Starting IRQ number for interrupts */
@@ -283,7 +282,7 @@ extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
#define gru_dbg(dev, fmt, x...) \
do { \
if (gru_options & OPT_DPRINT) \
- dev_dbg(dev, "%s: " fmt, __func__, x); \
+ printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\
} while (0)
#else
#define gru_dbg(x...)
@@ -297,13 +296,7 @@ extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
#define ASID_INC 8 /* number of regions */
/* Generate a GRU asid value from a GRU base asid & a virtual address. */
-#if defined CONFIG_IA64
#define VADDR_HI_BIT 64
-#elif defined CONFIG_X86_64
-#define VADDR_HI_BIT 48
-#else
-#error "Unsupported architecture"
-#endif
#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3)
#define GRUASID(asid, addr) ((asid) + GRUREGION(addr))
@@ -345,6 +338,7 @@ struct gru_vma_data {
long vd_user_options;/* misc user option flags */
int vd_cbr_au_count;
int vd_dsr_au_count;
+ unsigned char vd_tlb_preload_count;
};
/*
@@ -360,6 +354,7 @@ struct gru_thread_state {
struct gru_state *ts_gru; /* GRU where the context is
loaded */
struct gru_mm_struct *ts_gms; /* asid & ioproc struct */
+ unsigned char ts_tlb_preload_count; /* TLB preload pages */
unsigned long ts_cbr_map; /* map of allocated CBRs */
unsigned long ts_dsr_map; /* map of allocated DATA
resources */
@@ -368,6 +363,8 @@ struct gru_thread_state {
long ts_user_options;/* misc user option flags */
pid_t ts_tgid_owner; /* task that is using the
context - for migration */
+ short ts_user_blade_id;/* user selected blade */
+ char ts_user_chiplet_id;/* user selected chiplet */
unsigned short ts_sizeavail; /* Pagesizes in use */
int ts_tsid; /* thread that owns the
structure */
@@ -384,13 +381,11 @@ struct gru_thread_state {
char ts_blade; /* If >= 0, migrate context if
ref from diferent blade */
char ts_force_cch_reload;
- char ts_force_unload;/* force context to be unloaded
- after migration */
char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each
allocated CB */
int ts_data_valid; /* Indicates if ts_gdata has
valid data */
- struct gts_statistics ustats; /* User statistics */
+ struct gru_gseg_statistics ustats; /* User statistics */
unsigned long ts_gdata[0]; /* save area for GRU data (CB,
DS, CBE) */
};
@@ -422,6 +417,7 @@ struct gru_state {
gru segments (64) */
unsigned short gs_gid; /* unique GRU number */
unsigned short gs_blade_id; /* blade of GRU */
+ unsigned char gs_chiplet_id; /* blade chiplet of GRU */
unsigned char gs_tgh_local_shift; /* used to pick TGH for
local flush */
unsigned char gs_tgh_first_remote; /* starting TGH# for
@@ -453,6 +449,7 @@ struct gru_state {
in use */
struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using
the context */
+ int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */
};
/*
@@ -619,6 +616,15 @@ static inline int is_kernel_context(struct gru_thread_state *gts)
return !gts->ts_mm;
}
+/*
+ * The following are for Nehelem-EX. A more general scheme is needed for
+ * future processors.
+ */
+#define UV_MAX_INT_CORES 8
+#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1)
+#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1)
+#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \
+ ((cpu_physical_id(p) >> 1) & 3))
/*-----------------------------------------------------------------------------
* Function prototypes & externs
*/
@@ -633,24 +639,26 @@ extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct
*vma, int tsid);
extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct
*vma, int tsid);
-extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts,
- int blade);
+extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts);
extern void gru_load_context(struct gru_thread_state *gts);
-extern void gru_steal_context(struct gru_thread_state *gts, int blade_id);
+extern void gru_steal_context(struct gru_thread_state *gts);
extern void gru_unload_context(struct gru_thread_state *gts, int savestate);
-extern int gru_update_cch(struct gru_thread_state *gts, int force_unload);
+extern int gru_update_cch(struct gru_thread_state *gts);
extern void gts_drop(struct gru_thread_state *gts);
extern void gru_tgh_flush_init(struct gru_state *gru);
extern int gru_kservices_init(void);
extern void gru_kservices_exit(void);
+extern irqreturn_t gru0_intr(int irq, void *dev_id);
+extern irqreturn_t gru1_intr(int irq, void *dev_id);
+extern irqreturn_t gru_intr_mblade(int irq, void *dev_id);
extern int gru_dump_chiplet_request(unsigned long arg);
extern long gru_get_gseg_statistics(unsigned long arg);
-extern irqreturn_t gru_intr(int irq, void *dev_id);
extern int gru_handle_user_call_os(unsigned long address);
extern int gru_user_flush_tlb(unsigned long arg);
extern int gru_user_unload_context(unsigned long arg);
extern int gru_get_exception_detail(unsigned long arg);
extern int gru_set_context_option(unsigned long address);
+extern void gru_check_context_placement(struct gru_thread_state *gts);
extern int gru_cpu_fault_map_id(void);
extern struct vm_area_struct *gru_find_vma(unsigned long vaddr);
extern void gru_flush_all_tlb(struct gru_state *gru);
@@ -658,7 +666,8 @@ extern int gru_proc_init(void);
extern void gru_proc_exit(void);
extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
- int cbr_au_count, int dsr_au_count, int options, int tsid);
+ int cbr_au_count, int dsr_au_count,
+ unsigned char tlb_preload_count, int options, int tsid);
extern unsigned long gru_reserve_cb_resources(struct gru_state *gru,
int cbr_au_count, char *cbmap);
extern unsigned long gru_reserve_ds_resources(struct gru_state *gru,
diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c
index 1d125091f5e7..240a6d361665 100644
--- a/drivers/misc/sgi-gru/grutlbpurge.c
+++ b/drivers/misc/sgi-gru/grutlbpurge.c
@@ -184,8 +184,8 @@ void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start,
STAT(flush_tlb_gru_tgh);
asid = GRUASID(asid, start);
gru_dbg(grudev,
- " FLUSH gruid %d, asid 0x%x, num %ld, cbmap 0x%x\n",
- gid, asid, num, asids->mt_ctxbitmap);
+ " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n",
+ gid, asid, start, grupagesize, num, asids->mt_ctxbitmap);
tgh = get_lock_tgh_handle(gru);
tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0,
num - 1, asids->mt_ctxbitmap);
@@ -299,6 +299,7 @@ struct gru_mm_struct *gru_register_mmu_notifier(void)
{
struct gru_mm_struct *gms;
struct mmu_notifier *mn;
+ int err;
mn = mmu_find_ops(current->mm, &gru_mmuops);
if (mn) {
@@ -307,16 +308,22 @@ struct gru_mm_struct *gru_register_mmu_notifier(void)
} else {
gms = kzalloc(sizeof(*gms), GFP_KERNEL);
if (gms) {
+ STAT(gms_alloc);
spin_lock_init(&gms->ms_asid_lock);
gms->ms_notifier.ops = &gru_mmuops;
atomic_set(&gms->ms_refcnt, 1);
init_waitqueue_head(&gms->ms_wait_queue);
- __mmu_notifier_register(&gms->ms_notifier, current->mm);
+ err = __mmu_notifier_register(&gms->ms_notifier, current->mm);
+ if (err)
+ goto error;
}
}
gru_dbg(grudev, "gms %p, refcnt %d\n", gms,
atomic_read(&gms->ms_refcnt));
return gms;
+error:
+ kfree(gms);
+ return ERR_PTR(err);
}
void gru_drop_mmu_notifier(struct gru_mm_struct *gms)
@@ -327,6 +334,7 @@ void gru_drop_mmu_notifier(struct gru_mm_struct *gms)
if (!gms->ms_released)
mmu_notifier_unregister(&gms->ms_notifier, current->mm);
kfree(gms);
+ STAT(gms_free);
}
}
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h
index 2275126cb334..851b2f25ce0e 100644
--- a/drivers/misc/sgi-xp/xp.h
+++ b/drivers/misc/sgi-xp/xp.h
@@ -339,6 +339,7 @@ extern short xp_partition_id;
extern u8 xp_region_size;
extern unsigned long (*xp_pa) (void *);
+extern unsigned long (*xp_socket_pa) (unsigned long);
extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long,
size_t);
extern int (*xp_cpu_to_nasid) (int);
diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c
index 7896849b16dc..01be66d02ca8 100644
--- a/drivers/misc/sgi-xp/xp_main.c
+++ b/drivers/misc/sgi-xp/xp_main.c
@@ -44,6 +44,9 @@ EXPORT_SYMBOL_GPL(xp_region_size);
unsigned long (*xp_pa) (void *addr);
EXPORT_SYMBOL_GPL(xp_pa);
+unsigned long (*xp_socket_pa) (unsigned long gpa);
+EXPORT_SYMBOL_GPL(xp_socket_pa);
+
enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa,
const unsigned long src_gpa, size_t len);
EXPORT_SYMBOL_GPL(xp_remote_memcpy);
diff --git a/drivers/misc/sgi-xp/xp_sn2.c b/drivers/misc/sgi-xp/xp_sn2.c
index fb3ec9d735a9..d8e463f87241 100644
--- a/drivers/misc/sgi-xp/xp_sn2.c
+++ b/drivers/misc/sgi-xp/xp_sn2.c
@@ -84,6 +84,15 @@ xp_pa_sn2(void *addr)
}
/*
+ * Convert a global physical to a socket physical address.
+ */
+static unsigned long
+xp_socket_pa_sn2(unsigned long gpa)
+{
+ return gpa;
+}
+
+/*
* Wrapper for bte_copy().
*
* dst_pa - physical address of the destination of the transfer.
@@ -162,6 +171,7 @@ xp_init_sn2(void)
xp_region_size = sn_region_size;
xp_pa = xp_pa_sn2;
+ xp_socket_pa = xp_socket_pa_sn2;
xp_remote_memcpy = xp_remote_memcpy_sn2;
xp_cpu_to_nasid = xp_cpu_to_nasid_sn2;
xp_expand_memprotect = xp_expand_memprotect_sn2;
diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c
index d238576b26fa..a0d093274dc0 100644
--- a/drivers/misc/sgi-xp/xp_uv.c
+++ b/drivers/misc/sgi-xp/xp_uv.c
@@ -32,12 +32,44 @@ xp_pa_uv(void *addr)
return uv_gpa(addr);
}
+/*
+ * Convert a global physical to socket physical address.
+ */
+static unsigned long
+xp_socket_pa_uv(unsigned long gpa)
+{
+ return uv_gpa_to_soc_phys_ram(gpa);
+}
+
+static enum xp_retval
+xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa,
+ size_t len)
+{
+ int ret;
+ unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa));
+
+ BUG_ON(!uv_gpa_in_mmr_space(src_gpa));
+ BUG_ON(len != 8);
+
+ ret = gru_read_gpa(dst_va, src_gpa);
+ if (ret == 0)
+ return xpSuccess;
+
+ dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx "
+ "len=%ld\n", dst_gpa, src_gpa, len);
+ return xpGruCopyError;
+}
+
+
static enum xp_retval
xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa,
size_t len)
{
int ret;
+ if (uv_gpa_in_mmr_space(src_gpa))
+ return xp_remote_mmr_read(dst_gpa, src_gpa, len);
+
ret = gru_copy_gpa(dst_gpa, src_gpa, len);
if (ret == 0)
return xpSuccess;
@@ -123,6 +155,7 @@ xp_init_uv(void)
xp_region_size = sn_region_size;
xp_pa = xp_pa_uv;
+ xp_socket_pa = xp_socket_pa_uv;
xp_remote_memcpy = xp_remote_memcpy_uv;
xp_cpu_to_nasid = xp_cpu_to_nasid_uv;
xp_expand_memprotect = xp_expand_memprotect_uv;
diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c
index 65877bc5edaa..9a6268c89fdd 100644
--- a/drivers/misc/sgi-xp/xpc_partition.c
+++ b/drivers/misc/sgi-xp/xpc_partition.c
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/hardirq.h>
#include "xpc.h"
+#include <asm/uv/uv_hub.h>
/* XPC is exiting flag */
int xpc_exiting;
@@ -92,8 +93,12 @@ xpc_get_rsvd_page_pa(int nasid)
break;
/* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */
- if (L1_CACHE_ALIGN(len) > buf_len) {
- kfree(buf_base);
+ if (is_shub())
+ len = L1_CACHE_ALIGN(len);
+
+ if (len > buf_len) {
+ if (buf_base != NULL)
+ kfree(buf_base);
buf_len = L1_CACHE_ALIGN(len);
buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
&buf_base);
@@ -105,7 +110,7 @@ xpc_get_rsvd_page_pa(int nasid)
}
}
- ret = xp_remote_memcpy(xp_pa(buf), rp_pa, buf_len);
+ ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
if (ret != xpSuccess) {
dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
break;
@@ -143,7 +148,7 @@ xpc_setup_rsvd_page(void)
dev_err(xpc_part, "SAL failed to locate the reserved page\n");
return -ESRCH;
}
- rp = (struct xpc_rsvd_page *)__va(rp_pa);
+ rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
if (rp->SAL_version < 3) {
/* SAL_versions < 3 had a SAL_partid defined as a u8 */
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index b5bbe59f9c57..8725d5e8ab0c 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -157,22 +157,24 @@ xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq)
{
int ret;
-#if defined CONFIG_X86_64
- ret = uv_bios_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address),
- mq->order, &mq->mmr_offset);
- if (ret < 0) {
- dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
- "ret=%d\n", ret);
- return ret;
- }
-#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
- ret = sn_mq_watchlist_alloc(mq->mmr_blade, (void *)uv_gpa(mq->address),
+#if defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
+
+ ret = sn_mq_watchlist_alloc(mmr_pnode, (void *)uv_gpa(mq->address),
mq->order, &mq->mmr_offset);
if (ret < 0) {
dev_err(xpc_part, "sn_mq_watchlist_alloc() failed, ret=%d\n",
ret);
return -EBUSY;
}
+#elif defined CONFIG_X86_64
+ ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address),
+ mq->order, &mq->mmr_offset);
+ if (ret < 0) {
+ dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
+ "ret=%d\n", ret);
+ return ret;
+ }
#else
#error not a supported configuration
#endif
@@ -185,12 +187,13 @@ static void
xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq)
{
int ret;
+ int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
#if defined CONFIG_X86_64
- ret = uv_bios_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
+ ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num);
BUG_ON(ret != BIOS_STATUS_SUCCESS);
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
- ret = sn_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
+ ret = sn_mq_watchlist_free(mmr_pnode, mq->watchlist_num);
BUG_ON(ret != SALRET_OK);
#else
#error not a supported configuration
@@ -204,6 +207,7 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
enum xp_retval xp_ret;
int ret;
int nid;
+ int nasid;
int pg_order;
struct page *page;
struct xpc_gru_mq_uv *mq;
@@ -259,9 +263,11 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
goto out_5;
}
+ nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu));
+
mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value;
ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size,
- nid, mmr_value->vector, mmr_value->dest);
+ nasid, mmr_value->vector, mmr_value->dest);
if (ret != 0) {
dev_err(xpc_part, "gru_create_message_queue() returned "
"error=%d\n", ret);
@@ -946,11 +952,13 @@ xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head)
head->first = first->next;
if (head->first == NULL)
head->last = NULL;
+
+ head->n_entries--;
+ BUG_ON(head->n_entries < 0);
+
+ first->next = NULL;
}
- head->n_entries--;
- BUG_ON(head->n_entries < 0);
spin_unlock_irqrestore(&head->lock, irq_flags);
- first->next = NULL;
return first;
}
@@ -1019,7 +1027,8 @@ xpc_make_first_contact_uv(struct xpc_partition *part)
xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV);
- while (part->sn.uv.remote_act_state != XPC_P_AS_ACTIVATING) {
+ while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) ||
+ (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) {
dev_dbg(xpc_part, "waiting to make first contact with "
"partition %d\n", XPC_PARTID(part));
@@ -1422,7 +1431,6 @@ xpc_handle_notify_mq_msg_uv(struct xpc_partition *part,
msg_slot = ch_uv->recv_msg_slots +
(msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size;
- BUG_ON(msg->hdr.msg_slot_number != msg_slot->hdr.msg_slot_number);
BUG_ON(msg_slot->hdr.size != 0);
memcpy(msg_slot, msg, msg->hdr.size);
@@ -1646,8 +1654,6 @@ xpc_received_payload_uv(struct xpc_channel *ch, void *payload)
sizeof(struct xpc_notify_mq_msghdr_uv));
if (ret != xpSuccess)
XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret);
-
- msg->hdr.msg_slot_number += ch->remote_nentries;
}
static struct xpc_arch_operations xpc_arch_ops_uv = {
diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c
new file mode 100644
index 000000000000..d3f229a3a77e
--- /dev/null
+++ b/drivers/misc/ti_dac7512.c
@@ -0,0 +1,101 @@
+/*
+ * dac7512.c - Linux kernel module for
+ * Texas Instruments DAC7512
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * 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/spi/spi.h>
+
+#define DAC7512_DRV_NAME "dac7512"
+#define DRIVER_VERSION "1.0"
+
+static ssize_t dac7512_store_val(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char tmp[2];
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ tmp[0] = val >> 8;
+ tmp[1] = val & 0xff;
+ spi_write(spi, tmp, sizeof(tmp));
+ return count;
+}
+
+static DEVICE_ATTR(value, S_IWUSR, NULL, dac7512_store_val);
+
+static struct attribute *dac7512_attributes[] = {
+ &dev_attr_value.attr,
+ NULL
+};
+
+static const struct attribute_group dac7512_attr_group = {
+ .attrs = dac7512_attributes,
+};
+
+static int __devinit dac7512_probe(struct spi_device *spi)
+{
+ int ret;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ return sysfs_create_group(&spi->dev.kobj, &dac7512_attr_group);
+}
+
+static int __devexit dac7512_remove(struct spi_device *spi)
+{
+ sysfs_remove_group(&spi->dev.kobj, &dac7512_attr_group);
+ return 0;
+}
+
+static struct spi_driver dac7512_driver = {
+ .driver = {
+ .name = DAC7512_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = dac7512_probe,
+ .remove = __devexit_p(dac7512_remove),
+};
+
+static int __init dac7512_init(void)
+{
+ return spi_register_driver(&dac7512_driver);
+}
+
+static void __exit dac7512_exit(void)
+{
+ spi_unregister_driver(&dac7512_driver);
+}
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("DAC7512 16-bit DAC");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(dac7512_init);
+module_exit(dac7512_exit);
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ab37a6d9d32a..bb22ffd76ef8 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -3,7 +3,7 @@
#
config MMC_UNSAFE_RESUME
- bool "Allow unsafe resume (DANGEROUS)"
+ bool "Assume MMC/SD cards are non-removable (DANGEROUS)"
help
If you say Y here, the MMC layer will assume that all cards
stayed in their respective slots during the suspend. The
@@ -14,3 +14,5 @@ config MMC_UNSAFE_RESUME
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
+ This option sets a default which can be overridden by the
+ module parameter "removable=0" or "removable=1".
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 7dab2e5f4bc9..30acd5265821 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -48,6 +48,22 @@ int use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);
/*
+ * We normally treat cards as removed during suspend if they are not
+ * known to be on a non-removable bus, to avoid the risk of writing
+ * back data to a different card after resume. Allow this to be
+ * overridden if necessary.
+ */
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+int mmc_assume_removable;
+#else
+int mmc_assume_removable = 1;
+#endif
+module_param_named(removable, mmc_assume_removable, bool, 0644);
+MODULE_PARM_DESC(
+ removable,
+ "MMC/SD cards are removable and may be removed during suspend");
+
+/*
* Internal function. Schedule delayed work in the MMC work queue.
*/
static int mmc_schedule_delayed_work(struct delayed_work *work,
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 67ae6abc4230..a811c52a1659 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -54,7 +54,9 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
int mmc_attach_sd(struct mmc_host *host, u32 ocr);
int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
+/* Module parameters */
extern int use_spi_crc;
+extern int mmc_assume_removable;
/* Debugfs information for hosts and cards */
void mmc_add_host_debugfs(struct mmc_host *host);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index bfefce365ae7..c11189446a1f 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -602,25 +602,6 @@ static int mmc_awake(struct mmc_host *host)
return err;
}
-#ifdef CONFIG_MMC_UNSAFE_RESUME
-
-static const struct mmc_bus_ops mmc_ops = {
- .awake = mmc_awake,
- .sleep = mmc_sleep,
- .remove = mmc_remove,
- .detect = mmc_detect,
- .suspend = mmc_suspend,
- .resume = mmc_resume,
- .power_restore = mmc_power_restore,
-};
-
-static void mmc_attach_bus_ops(struct mmc_host *host)
-{
- mmc_attach_bus(host, &mmc_ops);
-}
-
-#else
-
static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
@@ -645,15 +626,13 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
{
const struct mmc_bus_ops *bus_ops;
- if (host->caps & MMC_CAP_NONREMOVABLE)
+ if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
bus_ops = &mmc_ops_unsafe;
else
bus_ops = &mmc_ops;
mmc_attach_bus(host, bus_ops);
}
-#endif
-
/*
* Starting point for MMC card init.
*/
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 10b2a4d20f5a..fdd414eded09 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -606,23 +606,6 @@ static void mmc_sd_power_restore(struct mmc_host *host)
mmc_release_host(host);
}
-#ifdef CONFIG_MMC_UNSAFE_RESUME
-
-static const struct mmc_bus_ops mmc_sd_ops = {
- .remove = mmc_sd_remove,
- .detect = mmc_sd_detect,
- .suspend = mmc_sd_suspend,
- .resume = mmc_sd_resume,
- .power_restore = mmc_sd_power_restore,
-};
-
-static void mmc_sd_attach_bus_ops(struct mmc_host *host)
-{
- mmc_attach_bus(host, &mmc_sd_ops);
-}
-
-#else
-
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
@@ -643,15 +626,13 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
{
const struct mmc_bus_ops *bus_ops;
- if (host->caps & MMC_CAP_NONREMOVABLE)
+ if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
bus_ops = &mmc_sd_ops_unsafe;
else
bus_ops = &mmc_sd_ops;
mmc_attach_bus(host, bus_ops);
}
-#endif
-
/*
* Starting point for SD card init.
*/
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index f85dcd536508..9538389783c1 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -97,26 +97,56 @@ static const unsigned char speed_val[16] =
static const unsigned int speed_unit[8] =
{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
-/* FUNCE tuples with these types get passed to SDIO drivers */
-static const unsigned char funce_type_whitelist[] = {
- 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */
+
+typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
+ const unsigned char *, unsigned);
+
+struct cis_tpl {
+ unsigned char code;
+ unsigned char min_size;
+ tpl_parse_t *parse;
};
-static int cistpl_funce_whitelisted(unsigned char type)
+static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
+ const char *tpl_descr,
+ const struct cis_tpl *tpl, int tpl_count,
+ unsigned char code,
+ const unsigned char *buf, unsigned size)
{
- int i;
+ int i, ret;
- for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) {
- if (funce_type_whitelist[i] == type)
- return 1;
+ /* look for a matching code in the table */
+ for (i = 0; i < tpl_count; i++, tpl++) {
+ if (tpl->code == code)
+ break;
}
- return 0;
+ if (i < tpl_count) {
+ if (size >= tpl->min_size) {
+ if (tpl->parse)
+ ret = tpl->parse(card, func, buf, size);
+ else
+ ret = -EILSEQ; /* known tuple, not parsed */
+ } else {
+ /* invalid tuple */
+ ret = -EINVAL;
+ }
+ if (ret && ret != -EILSEQ && ret != -ENOENT) {
+ printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
+ mmc_hostname(card->host), tpl_descr, code, size);
+ }
+ } else {
+ /* unknown tuple */
+ ret = -ENOENT;
+ }
+
+ return ret;
}
-static int cistpl_funce_common(struct mmc_card *card,
+static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
- if (size < 0x04 || buf[0] != 0)
+ /* Only valid for the common CIS (function 0) */
+ if (func)
return -EINVAL;
/* TPLFE_FN0_BLK_SIZE */
@@ -129,20 +159,24 @@ static int cistpl_funce_common(struct mmc_card *card,
return 0;
}
-static int cistpl_funce_func(struct sdio_func *func,
+static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned vsn;
unsigned min_size;
- /* let SDIO drivers take care of whitelisted FUNCE tuples */
- if (cistpl_funce_whitelisted(buf[0]))
- return -EILSEQ;
+ /* Only valid for the individual function's CIS (1-7) */
+ if (!func)
+ return -EINVAL;
+ /*
+ * This tuple has a different length depending on the SDIO spec
+ * version.
+ */
vsn = func->card->cccr.sdio_vsn;
min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
- if (size < min_size || buf[0] != 1)
+ if (size < min_size)
return -EINVAL;
/* TPLFE_MAX_BLK_SIZE */
@@ -157,39 +191,32 @@ static int cistpl_funce_func(struct sdio_func *func,
return 0;
}
+/*
+ * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples.
+ *
+ * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending
+ * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO
+ * TPLFID_FUNCTION is always hardcoded to 0x0C.
+ */
+static const struct cis_tpl cis_tpl_funce_list[] = {
+ { 0x00, 4, cistpl_funce_common },
+ { 0x01, 0, cistpl_funce_func },
+ { 0x04, 1+1+6, /* CISTPL_FUNCE_LAN_NODE_ID */ },
+};
+
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
- int ret;
-
- /*
- * There should be two versions of the CISTPL_FUNCE tuple,
- * one for the common CIS (function 0) and a version used by
- * the individual function's CIS (1-7). Yet, the later has a
- * different length depending on the SDIO spec version.
- */
- if (func)
- ret = cistpl_funce_func(func, buf, size);
- else
- ret = cistpl_funce_common(card, buf, size);
-
- if (ret && ret != -EILSEQ) {
- printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
- "type %u\n", mmc_hostname(card->host), size, buf[0]);
- }
+ if (size < 1)
+ return -EINVAL;
- return ret;
+ return cis_tpl_parse(card, func, "CISTPL_FUNCE",
+ cis_tpl_funce_list,
+ ARRAY_SIZE(cis_tpl_funce_list),
+ buf[0], buf, size);
}
-typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
- const unsigned char *, unsigned);
-
-struct cis_tpl {
- unsigned char code;
- unsigned char min_size;
- tpl_parse_t *parse;
-};
-
+/* Known TPL_CODEs table for CIS tuples */
static const struct cis_tpl cis_tpl_list[] = {
{ 0x15, 3, cistpl_vers_1 },
{ 0x20, 4, cistpl_manfid },
@@ -268,46 +295,38 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
break;
}
- for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
- if (cis_tpl_list[i].code == tpl_code)
- break;
- if (i < ARRAY_SIZE(cis_tpl_list)) {
- const struct cis_tpl *tpl = cis_tpl_list + i;
- if (tpl_link < tpl->min_size) {
- printk(KERN_ERR
- "%s: bad CIS tuple 0x%02x"
- " (length = %u, expected >= %u)\n",
- mmc_hostname(card->host),
- tpl_code, tpl_link, tpl->min_size);
- ret = -EINVAL;
- } else if (tpl->parse) {
- ret = tpl->parse(card, func,
- this->data, tpl_link);
- }
+ /* Try to parse the CIS tuple */
+ ret = cis_tpl_parse(card, func, "CIS",
+ cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
+ tpl_code, this->data, tpl_link);
+ if (ret == -EILSEQ || ret == -ENOENT) {
/*
- * We don't need the tuple anymore if it was
- * successfully parsed by the SDIO core or if it is
- * not going to be parsed by SDIO drivers.
+ * The tuple is unknown or known but not parsed.
+ * Queue the tuple for the function driver.
*/
- if (!ret || ret != -EILSEQ)
- kfree(this);
- } else {
- /* unknown tuple */
- ret = -EILSEQ;
- }
-
- if (ret == -EILSEQ) {
- /* this tuple is unknown to the core or whitelisted */
this->next = NULL;
this->code = tpl_code;
this->size = tpl_link;
*prev = this;
prev = &this->next;
- printk(KERN_DEBUG
- "%s: queuing CIS tuple 0x%02x length %u\n",
- mmc_hostname(card->host), tpl_code, tpl_link);
+
+ if (ret == -ENOENT) {
+ /* warn about unknown tuples */
+ printk(KERN_WARNING "%s: queuing unknown"
+ " CIS tuple 0x%02x (%u bytes)\n",
+ mmc_hostname(card->host),
+ tpl_code, tpl_link);
+ }
+
/* keep on analyzing tuples */
ret = 0;
+ } else {
+ /*
+ * We don't need the tuple anymore if it was
+ * successfully parsed by the SDIO core or if it is
+ * not going to be queued for a driver.
+ */
+ kfree(this);
}
ptr += tpl_link;
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index e04b751680d0..9d405b181781 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -251,6 +251,14 @@ config MMC_MVSDIO
To compile this driver as a module, choose M here: the
module will be called mvsdio.
+config MMC_DAVINCI
+ tristate "TI DAVINCI Multimedia Card Interface support"
+ depends on ARCH_DAVINCI
+ help
+ This selects the TI DAVINCI Multimedia card Interface.
+ If you have an DAVINCI board with a Multimedia Card slot,
+ say Y or M here. If unsure, say N.
+
config MMC_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
@@ -357,3 +365,22 @@ config MMC_VIA_SDMMC
If you have a controller with this interface, say Y or M here.
If unsure, say N.
+
+config SDH_BFIN
+ tristate "Blackfin Secure Digital Host support"
+ depends on MMC && ((BF54x && !BF544) || (BF51x && !BF512))
+ help
+ If you say yes here you will get support for the Blackfin on-chip
+ Secure Digital Host interface. This includes support for MMC and
+ SD cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_sdh.
+
+ If unsure, say N.
+
+config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
+ bool "Blackfin EZkit Missing SDH_CMD Pull Up Resistor Workaround"
+ depends on SDH_BFIN
+ help
+ If you say yes here SD-Cards may work on the EZkit.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index abcb0400e06d..ded4d8cdd9d7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -25,6 +25,7 @@ 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_MVSDIO) += mvsdio.o
+obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
+obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index fc25586b7ee1..8072128e933b 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -25,6 +25,8 @@
#include <linux/stat.h>
#include <linux/mmc/host.h>
+
+#include <mach/atmel-mci.h>
#include <linux/atmel-mci.h>
#include <asm/io.h>
@@ -92,6 +94,7 @@ struct atmel_mci_dma {
* @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request.
* @mode_reg: Value of the MR register.
+ * @cfg_reg: Value of the CFG register.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
* @mapbase: Physical address of the MMIO registers.
@@ -155,6 +158,7 @@ struct atmel_mci {
bool need_clock_update;
bool need_reset;
u32 mode_reg;
+ u32 cfg_reg;
unsigned long bus_hz;
unsigned long mapbase;
struct clk *mck;
@@ -223,6 +227,19 @@ static bool mci_has_rwproof(void)
}
/*
+ * The new MCI2 module isn't 100% compatible with the old MCI module,
+ * and it has a few nice features which we want to use...
+ */
+static inline bool atmci_is_mci2(void)
+{
+ if (cpu_is_at91sam9g45())
+ return true;
+
+ return false;
+}
+
+
+/*
* The debugfs stuff below is mostly optimized away when
* CONFIG_DEBUG_FS is not set.
*/
@@ -357,12 +374,33 @@ static int atmci_regs_show(struct seq_file *s, void *v)
buf[MCI_BLKR / 4],
buf[MCI_BLKR / 4] & 0xffff,
(buf[MCI_BLKR / 4] >> 16) & 0xffff);
+ if (atmci_is_mci2())
+ seq_printf(s, "CSTOR:\t0x%08x\n", buf[MCI_CSTOR / 4]);
/* Don't read RSPR and RDR; it will consume the data there */
atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
+ if (atmci_is_mci2()) {
+ u32 val;
+
+ val = buf[MCI_DMA / 4];
+ seq_printf(s, "DMA:\t0x%08x OFFSET=%u CHKSIZE=%u%s\n",
+ val, val & 3,
+ ((val >> 4) & 3) ?
+ 1 << (((val >> 4) & 3) + 1) : 1,
+ val & MCI_DMAEN ? " DMAEN" : "");
+
+ val = buf[MCI_CFG / 4];
+ seq_printf(s, "CFG:\t0x%08x%s%s%s%s\n",
+ val,
+ val & MCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
+ val & MCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
+ val & MCI_CFG_HSMODE ? " HSMODE" : "",
+ val & MCI_CFG_LSYNC ? " LSYNC" : "");
+ }
+
kfree(buf);
return 0;
@@ -557,6 +595,10 @@ static void atmci_dma_complete(void *arg)
dev_vdbg(&host->pdev->dev, "DMA complete\n");
+ if (atmci_is_mci2())
+ /* Disable DMA hardware handshaking on MCI */
+ mci_writel(host, DMA, mci_readl(host, DMA) & ~MCI_DMAEN);
+
atmci_dma_cleanup(host);
/*
@@ -592,7 +634,7 @@ static void atmci_dma_complete(void *arg)
}
static int
-atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
{
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
@@ -624,6 +666,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
if (!chan)
return -ENODEV;
+ if (atmci_is_mci2())
+ mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN);
+
if (data->flags & MMC_DATA_READ)
direction = DMA_FROM_DEVICE;
else
@@ -641,10 +686,6 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
host->dma.data_desc = desc;
desc->callback = atmci_dma_complete;
desc->callback_param = host;
- desc->tx_submit(desc);
-
- /* Go! */
- chan->device->device_issue_pending(chan);
return 0;
unmap_exit:
@@ -652,13 +693,26 @@ unmap_exit:
return -ENOMEM;
}
+static void atmci_submit_data(struct atmel_mci *host)
+{
+ struct dma_chan *chan = host->data_chan;
+ struct dma_async_tx_descriptor *desc = host->dma.data_desc;
+
+ if (chan) {
+ desc->tx_submit(desc);
+ chan->device->device_issue_pending(chan);
+ }
+}
+
#else /* CONFIG_MMC_ATMELMCI_DMA */
-static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+static int atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
{
return -ENOSYS;
}
+static void atmci_submit_data(struct atmel_mci *host) {}
+
static void atmci_stop_dma(struct atmel_mci *host)
{
/* Data transfer was stopped by the interrupt handler */
@@ -672,7 +726,7 @@ static void atmci_stop_dma(struct atmel_mci *host)
* Returns a mask of interrupt flags to be enabled after the whole
* request has been prepared.
*/
-static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
+static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
{
u32 iflags;
@@ -683,7 +737,7 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
host->data = data;
iflags = ATMCI_DATA_ERROR_FLAGS;
- if (atmci_submit_data_dma(host, data)) {
+ if (atmci_prepare_data_dma(host, data)) {
host->data_chan = NULL;
/*
@@ -729,6 +783,8 @@ static void atmci_start_request(struct atmel_mci *host,
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg);
+ if (atmci_is_mci2())
+ mci_writel(host, CFG, host->cfg_reg);
host->need_reset = false;
}
mci_writel(host, SDCR, slot->sdc_reg);
@@ -744,6 +800,7 @@ static void atmci_start_request(struct atmel_mci *host,
while (!(mci_readl(host, SR) & MCI_CMDRDY))
cpu_relax();
}
+ iflags = 0;
data = mrq->data;
if (data) {
atmci_set_timeout(host, slot, data);
@@ -753,15 +810,17 @@ static void atmci_start_request(struct atmel_mci *host,
| MCI_BLKLEN(data->blksz));
dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
+
+ iflags |= atmci_prepare_data(host, data);
}
- iflags = MCI_CMDRDY;
+ iflags |= MCI_CMDRDY;
cmd = mrq->cmd;
cmdflags = atmci_prepare_command(slot->mmc, cmd);
atmci_start_command(host, cmd, cmdflags);
if (data)
- iflags |= atmci_submit_data(host, data);
+ atmci_submit_data(host);
if (mrq->stop) {
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
@@ -857,6 +916,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_enable(host->mck);
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN);
+ if (atmci_is_mci2())
+ mci_writel(host, CFG, host->cfg_reg);
}
/*
@@ -1095,6 +1156,8 @@ static void atmci_detect_change(unsigned long data)
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg);
+ if (atmci_is_mci2())
+ mci_writel(host, CFG, host->cfg_reg);
host->data = NULL;
host->cmd = NULL;
@@ -1584,14 +1647,47 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
#ifdef CONFIG_MMC_ATMELMCI_DMA
static bool filter(struct dma_chan *chan, void *slave)
{
- struct dw_dma_slave *dws = slave;
+ struct mci_dma_data *sl = slave;
- if (dws->dma_dev == chan->device->dev) {
- chan->private = dws;
+ if (sl && find_slave_dev(sl) == chan->device->dev) {
+ chan->private = slave_data_ptr(sl);
return true;
- } else
+ } else {
return false;
+ }
}
+
+static void atmci_configure_dma(struct atmel_mci *host)
+{
+ struct mci_platform_data *pdata;
+
+ if (host == NULL)
+ return;
+
+ pdata = host->pdev->dev.platform_data;
+
+ if (pdata && find_slave_dev(pdata->dma_slave)) {
+ dma_cap_mask_t mask;
+
+ setup_dma_addr(pdata->dma_slave,
+ host->mapbase + MCI_TDR,
+ host->mapbase + MCI_RDR);
+
+ /* Try to grab a DMA channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ host->dma.chan =
+ dma_request_channel(mask, filter, pdata->dma_slave);
+ }
+ if (!host->dma.chan)
+ dev_notice(&host->pdev->dev, "DMA not available, using PIO\n");
+ else
+ dev_info(&host->pdev->dev,
+ "Using %s for DMA transfers\n",
+ dma_chan_name(host->dma.chan));
+}
+#else
+static void atmci_configure_dma(struct atmel_mci *host) {}
#endif
static int __init atmci_probe(struct platform_device *pdev)
@@ -1645,22 +1741,7 @@ static int __init atmci_probe(struct platform_device *pdev)
if (ret)
goto err_request_irq;
-#ifdef CONFIG_MMC_ATMELMCI_DMA
- if (pdata->dma_slave.dma_dev) {
- struct dw_dma_slave *dws = &pdata->dma_slave;
- dma_cap_mask_t mask;
-
- dws->tx_reg = regs->start + MCI_TDR;
- dws->rx_reg = regs->start + MCI_RDR;
-
- /* Try to grab a DMA channel */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- host->dma.chan = dma_request_channel(mask, filter, dws);
- }
- if (!host->dma.chan)
- dev_notice(&pdev->dev, "DMA not available, using PIO\n");
-#endif /* CONFIG_MMC_ATMELMCI_DMA */
+ atmci_configure_dma(host);
platform_set_drvdata(pdev, host);
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
new file mode 100644
index 000000000000..3343a57355cc
--- /dev/null
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -0,0 +1,639 @@
+/*
+ * bfin_sdh.c - Analog Devices Blackfin SDH Controller
+ *
+ * Copyright (C) 2007-2009 Analog Device Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#define DRIVER_NAME "bfin-sdh"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/proc_fs.h>
+
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <asm/bfin_sdh.h>
+
+#if defined(CONFIG_BF51x)
+#define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CTL
+#define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CTL
+#define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CTL
+#define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CTL
+#define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT
+#define bfin_write_SDH_COMMAND bfin_write_RSI_COMMAND
+#define bfin_write_SDH_DATA_TIMER bfin_write_RSI_DATA_TIMER
+#define bfin_read_SDH_RESPONSE0 bfin_read_RSI_RESPONSE0
+#define bfin_read_SDH_RESPONSE1 bfin_read_RSI_RESPONSE1
+#define bfin_read_SDH_RESPONSE2 bfin_read_RSI_RESPONSE2
+#define bfin_read_SDH_RESPONSE3 bfin_read_RSI_RESPONSE3
+#define bfin_write_SDH_DATA_LGTH bfin_write_RSI_DATA_LGTH
+#define bfin_read_SDH_DATA_CTL bfin_read_RSI_DATA_CTL
+#define bfin_write_SDH_DATA_CTL bfin_write_RSI_DATA_CTL
+#define bfin_read_SDH_DATA_CNT bfin_read_RSI_DATA_CNT
+#define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUS_CLR
+#define bfin_read_SDH_E_STATUS bfin_read_RSI_E_STATUS
+#define bfin_write_SDH_E_STATUS bfin_write_RSI_E_STATUS
+#define bfin_read_SDH_STATUS bfin_read_RSI_STATUS
+#define bfin_write_SDH_MASK0 bfin_write_RSI_MASK0
+#define bfin_read_SDH_CFG bfin_read_RSI_CFG
+#define bfin_write_SDH_CFG bfin_write_RSI_CFG
+#endif
+
+struct dma_desc_array {
+ unsigned long start_addr;
+ unsigned short cfg;
+ unsigned short x_count;
+ short x_modify;
+} __packed;
+
+struct sdh_host {
+ struct mmc_host *mmc;
+ spinlock_t lock;
+ struct resource *res;
+ void __iomem *base;
+ int irq;
+ int stat_irq;
+ int dma_ch;
+ int dma_dir;
+ struct dma_desc_array *sg_cpu;
+ dma_addr_t sg_dma;
+ int dma_len;
+
+ unsigned int imask;
+ unsigned int power_mode;
+ unsigned int clk_div;
+
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+};
+
+static struct bfin_sd_host *get_sdh_data(struct platform_device *pdev)
+{
+ return pdev->dev.platform_data;
+}
+
+static void sdh_stop_clock(struct sdh_host *host)
+{
+ bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() & ~CLK_E);
+ SSYNC();
+}
+
+static void sdh_enable_stat_irq(struct sdh_host *host, unsigned int mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->imask |= mask;
+ bfin_write_SDH_MASK0(mask);
+ SSYNC();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdh_disable_stat_irq(struct sdh_host *host, unsigned int mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->imask &= ~mask;
+ bfin_write_SDH_MASK0(host->imask);
+ SSYNC();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
+{
+ unsigned int length;
+ unsigned int data_ctl;
+ unsigned int dma_cfg;
+ struct scatterlist *sg;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter flags: 0x%x\n", __func__, data->flags);
+ host->data = data;
+ data_ctl = 0;
+ dma_cfg = 0;
+
+ length = data->blksz * data->blocks;
+ bfin_write_SDH_DATA_LGTH(length);
+
+ if (data->flags & MMC_DATA_STREAM)
+ data_ctl |= DTX_MODE;
+
+ if (data->flags & MMC_DATA_READ)
+ data_ctl |= DTX_DIR;
+ /* Only supports power-of-2 block size */
+ if (data->blksz & (data->blksz - 1))
+ return -EINVAL;
+ data_ctl |= ((ffs(data->blksz) - 1) << 4);
+
+ bfin_write_SDH_DATA_CTL(data_ctl);
+
+ bfin_write_SDH_DATA_TIMER(0xFFFF);
+ SSYNC();
+
+ if (data->flags & MMC_DATA_READ) {
+ host->dma_dir = DMA_FROM_DEVICE;
+ dma_cfg |= WNR;
+ } else
+ host->dma_dir = DMA_TO_DEVICE;
+
+ sdh_enable_stat_irq(host, (DAT_CRC_FAIL | DAT_TIME_OUT | DAT_END));
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
+#if defined(CONFIG_BF54x)
+ dma_cfg |= DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_32 | DMAEN;
+ {
+ int i;
+ for_each_sg(data->sg, sg, host->dma_len, i) {
+ host->sg_cpu[i].start_addr = sg_dma_address(sg);
+ host->sg_cpu[i].cfg = dma_cfg;
+ host->sg_cpu[i].x_count = sg_dma_len(sg) / 4;
+ host->sg_cpu[i].x_modify = 4;
+ dev_dbg(mmc_dev(host->mmc), "%d: start_addr:0x%lx, "
+ "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
+ i, host->sg_cpu[i].start_addr,
+ host->sg_cpu[i].cfg, host->sg_cpu[i].x_count,
+ host->sg_cpu[i].x_modify);
+ }
+ }
+ flush_dcache_range((unsigned int)host->sg_cpu,
+ (unsigned int)host->sg_cpu +
+ host->dma_len * sizeof(struct dma_desc_array));
+ /* Set the last descriptor to stop mode */
+ host->sg_cpu[host->dma_len - 1].cfg &= ~(DMAFLOW | NDSIZE);
+ host->sg_cpu[host->dma_len - 1].cfg |= DI_EN;
+
+ set_dma_curr_desc_addr(host->dma_ch, (unsigned long *)host->sg_dma);
+ set_dma_x_count(host->dma_ch, 0);
+ set_dma_x_modify(host->dma_ch, 0);
+ set_dma_config(host->dma_ch, dma_cfg);
+#elif defined(CONFIG_BF51x)
+ /* RSI DMA doesn't work in array mode */
+ dma_cfg |= WDSIZE_32 | DMAEN;
+ set_dma_start_addr(host->dma_ch, sg_dma_address(&data->sg[0]));
+ set_dma_x_count(host->dma_ch, length / 4);
+ set_dma_x_modify(host->dma_ch, 4);
+ set_dma_config(host->dma_ch, dma_cfg);
+#endif
+ bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
+
+ SSYNC();
+
+ dev_dbg(mmc_dev(host->mmc), "%s exit\n", __func__);
+ return 0;
+}
+
+static void sdh_start_cmd(struct sdh_host *host, struct mmc_command *cmd)
+{
+ unsigned int sdh_cmd;
+ unsigned int stat_mask;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter cmd: 0x%p\n", __func__, cmd);
+ WARN_ON(host->cmd != NULL);
+ host->cmd = cmd;
+
+ sdh_cmd = 0;
+ stat_mask = 0;
+
+ sdh_cmd |= cmd->opcode;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ sdh_cmd |= CMD_RSP;
+ stat_mask |= CMD_RESP_END;
+ } else {
+ stat_mask |= CMD_SENT;
+ }
+
+ if (cmd->flags & MMC_RSP_136)
+ sdh_cmd |= CMD_L_RSP;
+
+ stat_mask |= CMD_CRC_FAIL | CMD_TIME_OUT;
+
+ sdh_enable_stat_irq(host, stat_mask);
+
+ bfin_write_SDH_ARGUMENT(cmd->arg);
+ bfin_write_SDH_COMMAND(sdh_cmd | CMD_E);
+ bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() | CLK_E);
+ SSYNC();
+}
+
+static void sdh_finish_request(struct sdh_host *host, struct mmc_request *mrq)
+{
+ dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
+static int sdh_cmd_done(struct sdh_host *host, unsigned int stat)
+{
+ struct mmc_command *cmd = host->cmd;
+ int ret = 0;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter cmd: %p\n", __func__, cmd);
+ if (!cmd)
+ return 0;
+
+ host->cmd = NULL;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ cmd->resp[0] = bfin_read_SDH_RESPONSE0();
+ if (cmd->flags & MMC_RSP_136) {
+ cmd->resp[1] = bfin_read_SDH_RESPONSE1();
+ cmd->resp[2] = bfin_read_SDH_RESPONSE2();
+ cmd->resp[3] = bfin_read_SDH_RESPONSE3();
+ }
+ }
+ if (stat & CMD_TIME_OUT)
+ cmd->error = -ETIMEDOUT;
+ else if (stat & CMD_CRC_FAIL && cmd->flags & MMC_RSP_CRC)
+ cmd->error = -EILSEQ;
+
+ sdh_disable_stat_irq(host, (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL));
+
+ if (host->data && !cmd->error) {
+ if (host->data->flags & MMC_DATA_WRITE) {
+ ret = sdh_setup_data(host, host->data);
+ if (ret)
+ return 0;
+ }
+
+ sdh_enable_stat_irq(host, DAT_END | RX_OVERRUN | TX_UNDERRUN | DAT_TIME_OUT);
+ } else
+ sdh_finish_request(host, host->mrq);
+
+ return 1;
+}
+
+static int sdh_data_done(struct sdh_host *host, unsigned int stat)
+{
+ struct mmc_data *data = host->data;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter stat: 0x%x\n", __func__, stat);
+ if (!data)
+ return 0;
+
+ disable_dma(host->dma_ch);
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma_dir);
+
+ if (stat & DAT_TIME_OUT)
+ data->error = -ETIMEDOUT;
+ else if (stat & DAT_CRC_FAIL)
+ data->error = -EILSEQ;
+ else if (stat & (RX_OVERRUN | TX_UNDERRUN))
+ data->error = -EIO;
+
+ if (!data->error)
+ data->bytes_xfered = data->blocks * data->blksz;
+ else
+ data->bytes_xfered = 0;
+
+ sdh_disable_stat_irq(host, DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN);
+ bfin_write_SDH_STATUS_CLR(DAT_END_STAT | DAT_TIMEOUT_STAT | \
+ DAT_CRC_FAIL_STAT | DAT_BLK_END_STAT | RX_OVERRUN | TX_UNDERRUN);
+ bfin_write_SDH_DATA_CTL(0);
+ SSYNC();
+
+ host->data = NULL;
+ if (host->mrq->stop) {
+ sdh_stop_clock(host);
+ sdh_start_cmd(host, host->mrq->stop);
+ } else {
+ sdh_finish_request(host, host->mrq);
+ }
+
+ return 1;
+}
+
+static void sdh_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdh_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter, mrp:%p, cmd:%p\n", __func__, mrq, mrq->cmd);
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = mrq;
+ host->data = mrq->data;
+
+ if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
+ ret = sdh_setup_data(host, mrq->data);
+ if (ret)
+ return;
+ }
+
+ sdh_start_cmd(host, mrq->cmd);
+}
+
+static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdh_host *host;
+ unsigned long flags;
+ u16 clk_ctl = 0;
+ u16 pwr_ctl = 0;
+ u16 cfg;
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (ios->clock) {
+ unsigned long sys_clk, ios_clk;
+ unsigned char clk_div;
+ ios_clk = 2 * ios->clock;
+ sys_clk = get_sclk();
+ clk_div = sys_clk / ios_clk;
+ if (sys_clk % ios_clk == 0)
+ clk_div -= 1;
+ clk_div = min_t(unsigned char, clk_div, 0xFF);
+ clk_ctl |= clk_div;
+ clk_ctl |= CLK_E;
+ host->clk_div = clk_div;
+ } else
+ sdh_stop_clock(host);
+
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+#ifdef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
+ pwr_ctl |= ROD_CTL;
+#else
+ pwr_ctl |= SD_CMD_OD | ROD_CTL;
+#endif
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
+ cfg = bfin_read_SDH_CFG();
+ cfg &= ~PD_SDDAT3;
+ cfg |= PUP_SDDAT3;
+ /* Enable 4 bit SDIO */
+ cfg |= (SD4E | MWE);
+ bfin_write_SDH_CFG(cfg);
+ clk_ctl |= WIDE_BUS;
+ } else {
+ cfg = bfin_read_SDH_CFG();
+ cfg |= MWE;
+ bfin_write_SDH_CFG(cfg);
+ }
+
+ bfin_write_SDH_CLK_CTL(clk_ctl);
+
+ host->power_mode = ios->power_mode;
+ if (ios->power_mode == MMC_POWER_ON)
+ pwr_ctl |= PWR_ON;
+
+ bfin_write_SDH_PWR_CTL(pwr_ctl);
+ SSYNC();
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ dev_dbg(mmc_dev(host->mmc), "SDH: clk_div = 0x%x actual clock:%ld expected clock:%d\n",
+ host->clk_div,
+ host->clk_div ? get_sclk() / (2 * (host->clk_div + 1)) : 0,
+ ios->clock);
+}
+
+static const struct mmc_host_ops sdh_ops = {
+ .request = sdh_request,
+ .set_ios = sdh_set_ios,
+};
+
+static irqreturn_t sdh_dma_irq(int irq, void *devid)
+{
+ struct sdh_host *host = devid;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04x\n", __func__,
+ get_dma_curr_irqstat(host->dma_ch));
+ clear_dma_irqstat(host->dma_ch);
+ SSYNC();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sdh_stat_irq(int irq, void *devid)
+{
+ struct sdh_host *host = devid;
+ unsigned int status;
+ int handled = 0;
+
+ dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
+ status = bfin_read_SDH_E_STATUS();
+ if (status & SD_CARD_DET) {
+ mmc_detect_change(host->mmc, 0);
+ bfin_write_SDH_E_STATUS(SD_CARD_DET);
+ }
+ status = bfin_read_SDH_STATUS();
+ if (status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL)) {
+ handled |= sdh_cmd_done(host, status);
+ bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | \
+ CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
+ SSYNC();
+ }
+
+ status = bfin_read_SDH_STATUS();
+ if (status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN))
+ handled |= sdh_data_done(host, status);
+
+ dev_dbg(mmc_dev(host->mmc), "%s exit\n\n", __func__);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int __devinit sdh_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct sdh_host *host;
+ struct bfin_sd_host *drv_data = get_sdh_data(pdev);
+ int ret;
+
+ if (!drv_data) {
+ dev_err(&pdev->dev, "missing platform driver data\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mmc = mmc_alloc_host(sizeof(*mmc), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mmc->ops = &sdh_ops;
+ mmc->max_phys_segs = 32;
+ mmc->max_seg_size = 1 << 16;
+ mmc->max_blk_size = 1 << 11;
+ mmc->max_blk_count = 1 << 11;
+ mmc->max_req_size = PAGE_SIZE;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->f_max = get_sclk();
+ mmc->f_min = mmc->f_max >> 9;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL;
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ spin_lock_init(&host->lock);
+ host->irq = drv_data->irq_int0;
+ host->dma_ch = drv_data->dma_chan;
+
+ ret = request_dma(host->dma_ch, DRIVER_NAME "DMA");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request DMA channel\n");
+ goto out1;
+ }
+
+ ret = set_dma_callback(host->dma_ch, sdh_dma_irq, host);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request DMA irq\n");
+ goto out2;
+ }
+
+ host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+ if (host->sg_cpu == NULL) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ platform_set_drvdata(pdev, mmc);
+ mmc_add_host(mmc);
+
+ ret = request_irq(host->irq, sdh_stat_irq, 0, "SDH Status IRQ", host);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request status irq\n");
+ goto out3;
+ }
+
+ ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request peripheral pins\n");
+ goto out4;
+ }
+#if defined(CONFIG_BF54x)
+ /* Secure Digital Host shares DMA with Nand controller */
+ bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
+#endif
+
+ bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
+ SSYNC();
+
+ /* Disable card inserting detection pin. set MMC_CAP_NEES_POLL, and
+ * mmc stack will do the detection.
+ */
+ bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
+ SSYNC();
+
+ return 0;
+
+out4:
+ free_irq(host->irq, host);
+out3:
+ mmc_remove_host(mmc);
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+out2:
+ free_dma(host->dma_ch);
+out1:
+ mmc_free_host(mmc);
+ out:
+ return ret;
+}
+
+static int __devexit sdh_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (mmc) {
+ struct sdh_host *host = mmc_priv(mmc);
+
+ mmc_remove_host(mmc);
+
+ sdh_stop_clock(host);
+ free_irq(host->irq, host);
+ free_dma(host->dma_ch);
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+ mmc_free_host(mmc);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sdh_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct bfin_sd_host *drv_data = get_sdh_data(dev);
+ int ret = 0;
+
+ if (mmc)
+ ret = mmc_suspend_host(mmc, state);
+
+ bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() & ~PWR_ON);
+ peripheral_free_list(drv_data->pin_req);
+
+ return ret;
+}
+
+static int sdh_resume(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct bfin_sd_host *drv_data = get_sdh_data(dev);
+ int ret = 0;
+
+ ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
+ if (ret) {
+ dev_err(&dev->dev, "unable to request peripheral pins\n");
+ return ret;
+ }
+
+ bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() | PWR_ON);
+#if defined(CONFIG_BF54x)
+ /* Secure Digital Host shares DMA with Nand controller */
+ bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
+#endif
+ bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
+ SSYNC();
+
+ bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
+ SSYNC();
+
+ if (mmc)
+ ret = mmc_resume_host(mmc);
+
+ return ret;
+}
+#else
+# define sdh_suspend NULL
+# define sdh_resume NULL
+#endif
+
+static struct platform_driver sdh_driver = {
+ .probe = sdh_probe,
+ .remove = __devexit_p(sdh_remove),
+ .suspend = sdh_suspend,
+ .resume = sdh_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init sdh_init(void)
+{
+ return platform_driver_register(&sdh_driver);
+}
+module_init(sdh_init);
+
+static void __exit sdh_exit(void)
+{
+ platform_driver_unregister(&sdh_driver);
+}
+module_exit(sdh_exit);
+
+MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
+MODULE_AUTHOR("Cliff Cai, Roy Huang");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
new file mode 100644
index 000000000000..dd45e7c3517e
--- /dev/null
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -0,0 +1,1349 @@
+/*
+ * davinci_mmc.c - TI DaVinci MMC/SD/SDIO driver
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Original author: Purushotam Kumar
+ * Copyright (C) 2009 David Brownell
+ *
+ * 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/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/cpufreq.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/mmc.h>
+
+#include <mach/mmc.h>
+#include <mach/edma.h>
+
+/*
+ * Register Definitions
+ */
+#define DAVINCI_MMCCTL 0x00 /* Control Register */
+#define DAVINCI_MMCCLK 0x04 /* Memory Clock Control Register */
+#define DAVINCI_MMCST0 0x08 /* Status Register 0 */
+#define DAVINCI_MMCST1 0x0C /* Status Register 1 */
+#define DAVINCI_MMCIM 0x10 /* Interrupt Mask Register */
+#define DAVINCI_MMCTOR 0x14 /* Response Time-Out Register */
+#define DAVINCI_MMCTOD 0x18 /* Data Read Time-Out Register */
+#define DAVINCI_MMCBLEN 0x1C /* Block Length Register */
+#define DAVINCI_MMCNBLK 0x20 /* Number of Blocks Register */
+#define DAVINCI_MMCNBLC 0x24 /* Number of Blocks Counter Register */
+#define DAVINCI_MMCDRR 0x28 /* Data Receive Register */
+#define DAVINCI_MMCDXR 0x2C /* Data Transmit Register */
+#define DAVINCI_MMCCMD 0x30 /* Command Register */
+#define DAVINCI_MMCARGHL 0x34 /* Argument Register */
+#define DAVINCI_MMCRSP01 0x38 /* Response Register 0 and 1 */
+#define DAVINCI_MMCRSP23 0x3C /* Response Register 0 and 1 */
+#define DAVINCI_MMCRSP45 0x40 /* Response Register 0 and 1 */
+#define DAVINCI_MMCRSP67 0x44 /* Response Register 0 and 1 */
+#define DAVINCI_MMCDRSP 0x48 /* Data Response Register */
+#define DAVINCI_MMCETOK 0x4C
+#define DAVINCI_MMCCIDX 0x50 /* Command Index Register */
+#define DAVINCI_MMCCKC 0x54
+#define DAVINCI_MMCTORC 0x58
+#define DAVINCI_MMCTODC 0x5C
+#define DAVINCI_MMCBLNC 0x60
+#define DAVINCI_SDIOCTL 0x64
+#define DAVINCI_SDIOST0 0x68
+#define DAVINCI_SDIOEN 0x6C
+#define DAVINCI_SDIOST 0x70
+#define DAVINCI_MMCFIFOCTL 0x74 /* FIFO Control Register */
+
+/* DAVINCI_MMCCTL definitions */
+#define MMCCTL_DATRST (1 << 0)
+#define MMCCTL_CMDRST (1 << 1)
+#define MMCCTL_WIDTH_4_BIT (1 << 2)
+#define MMCCTL_DATEG_DISABLED (0 << 6)
+#define MMCCTL_DATEG_RISING (1 << 6)
+#define MMCCTL_DATEG_FALLING (2 << 6)
+#define MMCCTL_DATEG_BOTH (3 << 6)
+#define MMCCTL_PERMDR_LE (0 << 9)
+#define MMCCTL_PERMDR_BE (1 << 9)
+#define MMCCTL_PERMDX_LE (0 << 10)
+#define MMCCTL_PERMDX_BE (1 << 10)
+
+/* DAVINCI_MMCCLK definitions */
+#define MMCCLK_CLKEN (1 << 8)
+#define MMCCLK_CLKRT_MASK (0xFF << 0)
+
+/* IRQ bit definitions, for DAVINCI_MMCST0 and DAVINCI_MMCIM */
+#define MMCST0_DATDNE BIT(0) /* data done */
+#define MMCST0_BSYDNE BIT(1) /* busy done */
+#define MMCST0_RSPDNE BIT(2) /* command done */
+#define MMCST0_TOUTRD BIT(3) /* data read timeout */
+#define MMCST0_TOUTRS BIT(4) /* command response timeout */
+#define MMCST0_CRCWR BIT(5) /* data write CRC error */
+#define MMCST0_CRCRD BIT(6) /* data read CRC error */
+#define MMCST0_CRCRS BIT(7) /* command response CRC error */
+#define MMCST0_DXRDY BIT(9) /* data transmit ready (fifo empty) */
+#define MMCST0_DRRDY BIT(10) /* data receive ready (data in fifo)*/
+#define MMCST0_DATED BIT(11) /* DAT3 edge detect */
+#define MMCST0_TRNDNE BIT(12) /* transfer done */
+
+/* DAVINCI_MMCST1 definitions */
+#define MMCST1_BUSY (1 << 0)
+
+/* DAVINCI_MMCCMD definitions */
+#define MMCCMD_CMD_MASK (0x3F << 0)
+#define MMCCMD_PPLEN (1 << 7)
+#define MMCCMD_BSYEXP (1 << 8)
+#define MMCCMD_RSPFMT_MASK (3 << 9)
+#define MMCCMD_RSPFMT_NONE (0 << 9)
+#define MMCCMD_RSPFMT_R1456 (1 << 9)
+#define MMCCMD_RSPFMT_R2 (2 << 9)
+#define MMCCMD_RSPFMT_R3 (3 << 9)
+#define MMCCMD_DTRW (1 << 11)
+#define MMCCMD_STRMTP (1 << 12)
+#define MMCCMD_WDATX (1 << 13)
+#define MMCCMD_INITCK (1 << 14)
+#define MMCCMD_DCLR (1 << 15)
+#define MMCCMD_DMATRIG (1 << 16)
+
+/* DAVINCI_MMCFIFOCTL definitions */
+#define MMCFIFOCTL_FIFORST (1 << 0)
+#define MMCFIFOCTL_FIFODIR_WR (1 << 1)
+#define MMCFIFOCTL_FIFODIR_RD (0 << 1)
+#define MMCFIFOCTL_FIFOLEV (1 << 2) /* 0 = 128 bits, 1 = 256 bits */
+#define MMCFIFOCTL_ACCWD_4 (0 << 3) /* access width of 4 bytes */
+#define MMCFIFOCTL_ACCWD_3 (1 << 3) /* access width of 3 bytes */
+#define MMCFIFOCTL_ACCWD_2 (2 << 3) /* access width of 2 bytes */
+#define MMCFIFOCTL_ACCWD_1 (3 << 3) /* access width of 1 byte */
+
+
+/* MMCSD Init clock in Hz in opendrain mode */
+#define MMCSD_INIT_CLOCK 200000
+
+/*
+ * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
+ * and we handle up to NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
+ * for drivers with max_hw_segs == 1, making the segments bigger (64KB)
+ * than the page or two that's otherwise typical. NR_SG == 16 gives at
+ * least the same throughput boost, using EDMA transfer linkage instead
+ * of spending CPU time copying pages.
+ */
+#define MAX_CCNT ((1 << 16) - 1)
+
+#define NR_SG 16
+
+static unsigned rw_threshold = 32;
+module_param(rw_threshold, uint, S_IRUGO);
+MODULE_PARM_DESC(rw_threshold,
+ "Read/Write threshold. Default = 32");
+
+static unsigned __initdata use_dma = 1;
+module_param(use_dma, uint, 0);
+MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
+
+struct mmc_davinci_host {
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_host *mmc;
+ struct clk *clk;
+ unsigned int mmc_input_clk;
+ void __iomem *base;
+ struct resource *mem_res;
+ int irq;
+ unsigned char bus_mode;
+
+#define DAVINCI_MMC_DATADIR_NONE 0
+#define DAVINCI_MMC_DATADIR_READ 1
+#define DAVINCI_MMC_DATADIR_WRITE 2
+ unsigned char data_dir;
+
+ /* buffer is used during PIO of one scatterlist segment, and
+ * is updated along with buffer_bytes_left. bytes_left applies
+ * to all N blocks of the PIO transfer.
+ */
+ u8 *buffer;
+ u32 buffer_bytes_left;
+ u32 bytes_left;
+
+ u32 rxdma, txdma;
+ bool use_dma;
+ bool do_dma;
+
+ /* Scatterlist DMA uses one or more parameter RAM entries:
+ * the main one (associated with rxdma or txdma) plus zero or
+ * more links. The entries for a given transfer differ only
+ * by memory buffer (address, length) and link field.
+ */
+ struct edmacc_param tx_template;
+ struct edmacc_param rx_template;
+ unsigned n_link;
+ u32 links[NR_SG - 1];
+
+ /* For PIO we walk scatterlists one segment at a time. */
+ unsigned int sg_len;
+ struct scatterlist *sg;
+
+ /* Version of the MMC/SD controller */
+ u8 version;
+ /* for ns in one cycle calculation */
+ unsigned ns_in_one_cycle;
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
+};
+
+
+/* PIO only */
+static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
+{
+ host->buffer_bytes_left = sg_dma_len(host->sg);
+ host->buffer = sg_virt(host->sg);
+ if (host->buffer_bytes_left > host->bytes_left)
+ host->buffer_bytes_left = host->bytes_left;
+}
+
+static void davinci_fifo_data_trans(struct mmc_davinci_host *host,
+ unsigned int n)
+{
+ u8 *p;
+ unsigned int i;
+
+ if (host->buffer_bytes_left == 0) {
+ host->sg = sg_next(host->data->sg);
+ mmc_davinci_sg_to_buf(host);
+ }
+
+ p = host->buffer;
+ if (n > host->buffer_bytes_left)
+ n = host->buffer_bytes_left;
+ host->buffer_bytes_left -= n;
+ host->bytes_left -= n;
+
+ /* NOTE: we never transfer more than rw_threshold bytes
+ * to/from the fifo here; there's no I/O overlap.
+ * This also assumes that access width( i.e. ACCWD) is 4 bytes
+ */
+ if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
+ for (i = 0; i < (n >> 2); i++) {
+ writel(*((u32 *)p), host->base + DAVINCI_MMCDXR);
+ p = p + 4;
+ }
+ if (n & 3) {
+ iowrite8_rep(host->base + DAVINCI_MMCDXR, p, (n & 3));
+ p = p + (n & 3);
+ }
+ } else {
+ for (i = 0; i < (n >> 2); i++) {
+ *((u32 *)p) = readl(host->base + DAVINCI_MMCDRR);
+ p = p + 4;
+ }
+ if (n & 3) {
+ ioread8_rep(host->base + DAVINCI_MMCDRR, p, (n & 3));
+ p = p + (n & 3);
+ }
+ }
+ host->buffer = p;
+}
+
+static void mmc_davinci_start_command(struct mmc_davinci_host *host,
+ struct mmc_command *cmd)
+{
+ u32 cmd_reg = 0;
+ u32 im_val;
+
+ dev_dbg(mmc_dev(host->mmc), "CMD%d, arg 0x%08x%s\n",
+ cmd->opcode, cmd->arg,
+ ({ char *s;
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1:
+ s = ", R1/R5/R6/R7 response";
+ break;
+ case MMC_RSP_R1B:
+ s = ", R1b response";
+ break;
+ case MMC_RSP_R2:
+ s = ", R2 response";
+ break;
+ case MMC_RSP_R3:
+ s = ", R3/R4 response";
+ break;
+ default:
+ s = ", (R? response)";
+ break;
+ }; s; }));
+ host->cmd = cmd;
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1B:
+ /* There's some spec confusion about when R1B is
+ * allowed, but if the card doesn't issue a BUSY
+ * then it's harmless for us to allow it.
+ */
+ cmd_reg |= MMCCMD_BSYEXP;
+ /* FALLTHROUGH */
+ case MMC_RSP_R1: /* 48 bits, CRC */
+ cmd_reg |= MMCCMD_RSPFMT_R1456;
+ break;
+ case MMC_RSP_R2: /* 136 bits, CRC */
+ cmd_reg |= MMCCMD_RSPFMT_R2;
+ break;
+ case MMC_RSP_R3: /* 48 bits, no CRC */
+ cmd_reg |= MMCCMD_RSPFMT_R3;
+ break;
+ default:
+ cmd_reg |= MMCCMD_RSPFMT_NONE;
+ dev_dbg(mmc_dev(host->mmc), "unknown resp_type %04x\n",
+ mmc_resp_type(cmd));
+ break;
+ }
+
+ /* Set command index */
+ cmd_reg |= cmd->opcode;
+
+ /* Enable EDMA transfer triggers */
+ if (host->do_dma)
+ cmd_reg |= MMCCMD_DMATRIG;
+
+ if (host->version == MMC_CTLR_VERSION_2 && host->data != NULL &&
+ host->data_dir == DAVINCI_MMC_DATADIR_READ)
+ cmd_reg |= MMCCMD_DMATRIG;
+
+ /* Setting whether command involves data transfer or not */
+ if (cmd->data)
+ cmd_reg |= MMCCMD_WDATX;
+
+ /* Setting whether stream or block transfer */
+ if (cmd->flags & MMC_DATA_STREAM)
+ cmd_reg |= MMCCMD_STRMTP;
+
+ /* Setting whether data read or write */
+ if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
+ cmd_reg |= MMCCMD_DTRW;
+
+ if (host->bus_mode == MMC_BUSMODE_PUSHPULL)
+ cmd_reg |= MMCCMD_PPLEN;
+
+ /* set Command timeout */
+ writel(0x1FFF, host->base + DAVINCI_MMCTOR);
+
+ /* Enable interrupt (calculate here, defer until FIFO is stuffed). */
+ im_val = MMCST0_RSPDNE | MMCST0_CRCRS | MMCST0_TOUTRS;
+ if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
+ im_val |= MMCST0_DATDNE | MMCST0_CRCWR;
+
+ if (!host->do_dma)
+ im_val |= MMCST0_DXRDY;
+ } else if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
+ im_val |= MMCST0_DATDNE | MMCST0_CRCRD | MMCST0_TOUTRD;
+
+ if (!host->do_dma)
+ im_val |= MMCST0_DRRDY;
+ }
+
+ /*
+ * Before non-DMA WRITE commands the controller needs priming:
+ * FIFO should be populated with 32 bytes i.e. whatever is the FIFO size
+ */
+ if (!host->do_dma && (host->data_dir == DAVINCI_MMC_DATADIR_WRITE))
+ davinci_fifo_data_trans(host, rw_threshold);
+
+ writel(cmd->arg, host->base + DAVINCI_MMCARGHL);
+ writel(cmd_reg, host->base + DAVINCI_MMCCMD);
+ writel(im_val, host->base + DAVINCI_MMCIM);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* DMA infrastructure */
+
+static void davinci_abort_dma(struct mmc_davinci_host *host)
+{
+ int sync_dev;
+
+ if (host->data_dir == DAVINCI_MMC_DATADIR_READ)
+ sync_dev = host->rxdma;
+ else
+ sync_dev = host->txdma;
+
+ edma_stop(sync_dev);
+ edma_clean_channel(sync_dev);
+}
+
+static void
+mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data);
+
+static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
+{
+ if (DMA_COMPLETE != ch_status) {
+ struct mmc_davinci_host *host = data;
+
+ /* Currently means: DMA Event Missed, or "null" transfer
+ * request was seen. In the future, TC errors (like bad
+ * addresses) might be presented too.
+ */
+ dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
+ (host->data->flags & MMC_DATA_WRITE)
+ ? "write" : "read");
+ host->data->error = -EIO;
+ mmc_davinci_xfer_done(host, host->data);
+ }
+}
+
+/* Set up tx or rx template, to be modified and updated later */
+static void __init mmc_davinci_dma_setup(struct mmc_davinci_host *host,
+ bool tx, struct edmacc_param *template)
+{
+ unsigned sync_dev;
+ const u16 acnt = 4;
+ const u16 bcnt = rw_threshold >> 2;
+ const u16 ccnt = 0;
+ u32 src_port = 0;
+ u32 dst_port = 0;
+ s16 src_bidx, dst_bidx;
+ s16 src_cidx, dst_cidx;
+
+ /*
+ * A-B Sync transfer: each DMA request is for one "frame" of
+ * rw_threshold bytes, broken into "acnt"-size chunks repeated
+ * "bcnt" times. Each segment needs "ccnt" such frames; since
+ * we tell the block layer our mmc->max_seg_size limit, we can
+ * trust (later) that it's within bounds.
+ *
+ * The FIFOs are read/written in 4-byte chunks (acnt == 4) and
+ * EDMA will optimize memory operations to use larger bursts.
+ */
+ if (tx) {
+ sync_dev = host->txdma;
+
+ /* src_prt, ccnt, and link to be set up later */
+ src_bidx = acnt;
+ src_cidx = acnt * bcnt;
+
+ dst_port = host->mem_res->start + DAVINCI_MMCDXR;
+ dst_bidx = 0;
+ dst_cidx = 0;
+ } else {
+ sync_dev = host->rxdma;
+
+ src_port = host->mem_res->start + DAVINCI_MMCDRR;
+ src_bidx = 0;
+ src_cidx = 0;
+
+ /* dst_prt, ccnt, and link to be set up later */
+ dst_bidx = acnt;
+ dst_cidx = acnt * bcnt;
+ }
+
+ /*
+ * We can't use FIFO mode for the FIFOs because MMC FIFO addresses
+ * are not 256-bit (32-byte) aligned. So we use INCR, and the W8BIT
+ * parameter is ignored.
+ */
+ edma_set_src(sync_dev, src_port, INCR, W8BIT);
+ edma_set_dest(sync_dev, dst_port, INCR, W8BIT);
+
+ edma_set_src_index(sync_dev, src_bidx, src_cidx);
+ edma_set_dest_index(sync_dev, dst_bidx, dst_cidx);
+
+ edma_set_transfer_params(sync_dev, acnt, bcnt, ccnt, 8, ABSYNC);
+
+ edma_read_slot(sync_dev, template);
+
+ /* don't bother with irqs or chaining */
+ template->opt |= EDMA_CHAN_SLOT(sync_dev) << 12;
+}
+
+static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
+ struct mmc_data *data)
+{
+ struct edmacc_param *template;
+ int channel, slot;
+ unsigned link;
+ struct scatterlist *sg;
+ unsigned sg_len;
+ unsigned bytes_left = host->bytes_left;
+ const unsigned shift = ffs(rw_threshold) - 1;;
+
+ if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
+ template = &host->tx_template;
+ channel = host->txdma;
+ } else {
+ template = &host->rx_template;
+ channel = host->rxdma;
+ }
+
+ /* We know sg_len and ccnt will never be out of range because
+ * we told the mmc layer which in turn tells the block layer
+ * to ensure that it only hands us one scatterlist segment
+ * per EDMA PARAM entry. Update the PARAM
+ * entries needed for each segment of this scatterlist.
+ */
+ for (slot = channel, link = 0, sg = data->sg, sg_len = host->sg_len;
+ sg_len-- != 0 && bytes_left;
+ sg = sg_next(sg), slot = host->links[link++]) {
+ u32 buf = sg_dma_address(sg);
+ unsigned count = sg_dma_len(sg);
+
+ template->link_bcntrld = sg_len
+ ? (EDMA_CHAN_SLOT(host->links[link]) << 5)
+ : 0xffff;
+
+ if (count > bytes_left)
+ count = bytes_left;
+ bytes_left -= count;
+
+ if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
+ template->src = buf;
+ else
+ template->dst = buf;
+ template->ccnt = count >> shift;
+
+ edma_write_slot(slot, template);
+ }
+
+ if (host->version == MMC_CTLR_VERSION_2)
+ edma_clear_event(channel);
+
+ edma_start(channel);
+}
+
+static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
+ struct mmc_data *data)
+{
+ int i;
+ int mask = rw_threshold - 1;
+
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ ((data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE));
+
+ /* no individual DMA segment should need a partial FIFO */
+ for (i = 0; i < host->sg_len; i++) {
+ if (sg_dma_len(data->sg + i) & mask) {
+ dma_unmap_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ return -1;
+ }
+ }
+
+ host->do_dma = 1;
+ mmc_davinci_send_dma_request(host, data);
+
+ return 0;
+}
+
+static void __init_or_module
+davinci_release_dma_channels(struct mmc_davinci_host *host)
+{
+ unsigned i;
+
+ if (!host->use_dma)
+ return;
+
+ for (i = 0; i < host->n_link; i++)
+ edma_free_slot(host->links[i]);
+
+ edma_free_channel(host->txdma);
+ edma_free_channel(host->rxdma);
+}
+
+static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
+{
+ int r, i;
+
+ /* Acquire master DMA write channel */
+ r = edma_alloc_channel(host->txdma, mmc_davinci_dma_cb, host,
+ EVENTQ_DEFAULT);
+ if (r < 0) {
+ dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
+ "tx", r);
+ return r;
+ }
+ mmc_davinci_dma_setup(host, true, &host->tx_template);
+
+ /* Acquire master DMA read channel */
+ r = edma_alloc_channel(host->rxdma, mmc_davinci_dma_cb, host,
+ EVENTQ_DEFAULT);
+ if (r < 0) {
+ dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
+ "rx", r);
+ goto free_master_write;
+ }
+ mmc_davinci_dma_setup(host, false, &host->rx_template);
+
+ /* Allocate parameter RAM slots, which will later be bound to a
+ * channel as needed to handle a scatterlist.
+ */
+ for (i = 0; i < ARRAY_SIZE(host->links); i++) {
+ r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY);
+ if (r < 0) {
+ dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
+ r);
+ break;
+ }
+ host->links[i] = r;
+ }
+ host->n_link = i;
+
+ return 0;
+
+free_master_write:
+ edma_free_channel(host->txdma);
+
+ return r;
+}
+
+/*----------------------------------------------------------------------*/
+
+static void
+mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
+{
+ int fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0;
+ int timeout;
+ struct mmc_data *data = req->data;
+
+ if (host->version == MMC_CTLR_VERSION_2)
+ fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0;
+
+ host->data = data;
+ if (data == NULL) {
+ host->data_dir = DAVINCI_MMC_DATADIR_NONE;
+ writel(0, host->base + DAVINCI_MMCBLEN);
+ writel(0, host->base + DAVINCI_MMCNBLK);
+ return;
+ }
+
+ dev_dbg(mmc_dev(host->mmc), "%s %s, %d blocks of %d bytes\n",
+ (data->flags & MMC_DATA_STREAM) ? "stream" : "block",
+ (data->flags & MMC_DATA_WRITE) ? "write" : "read",
+ data->blocks, data->blksz);
+ dev_dbg(mmc_dev(host->mmc), " DTO %d cycles + %d ns\n",
+ data->timeout_clks, data->timeout_ns);
+ timeout = data->timeout_clks +
+ (data->timeout_ns / host->ns_in_one_cycle);
+ if (timeout > 0xffff)
+ timeout = 0xffff;
+
+ writel(timeout, host->base + DAVINCI_MMCTOD);
+ writel(data->blocks, host->base + DAVINCI_MMCNBLK);
+ writel(data->blksz, host->base + DAVINCI_MMCBLEN);
+
+ /* Configure the FIFO */
+ switch (data->flags & MMC_DATA_WRITE) {
+ case MMC_DATA_WRITE:
+ host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
+ writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
+ host->base + DAVINCI_MMCFIFOCTL);
+ writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
+ host->base + DAVINCI_MMCFIFOCTL);
+ break;
+
+ default:
+ host->data_dir = DAVINCI_MMC_DATADIR_READ;
+ writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
+ host->base + DAVINCI_MMCFIFOCTL);
+ writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD,
+ host->base + DAVINCI_MMCFIFOCTL);
+ break;
+ }
+
+ host->buffer = NULL;
+ host->bytes_left = data->blocks * data->blksz;
+
+ /* For now we try to use DMA whenever we won't need partial FIFO
+ * reads or writes, either for the whole transfer (as tested here)
+ * or for any individual scatterlist segment (tested when we call
+ * start_dma_transfer).
+ *
+ * While we *could* change that, unusual block sizes are rarely
+ * used. The occasional fallback to PIO should't hurt.
+ */
+ if (host->use_dma && (host->bytes_left & (rw_threshold - 1)) == 0
+ && mmc_davinci_start_dma_transfer(host, data) == 0) {
+ /* zero this to ensure we take no PIO paths */
+ host->bytes_left = 0;
+ } else {
+ /* Revert to CPU Copy */
+ host->sg_len = data->sg_len;
+ host->sg = host->data->sg;
+ mmc_davinci_sg_to_buf(host);
+ }
+}
+
+static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct mmc_davinci_host *host = mmc_priv(mmc);
+ unsigned long timeout = jiffies + msecs_to_jiffies(900);
+ u32 mmcst1 = 0;
+
+ /* Card may still be sending BUSY after a previous operation,
+ * typically some kind of write. If so, we can't proceed yet.
+ */
+ while (time_before(jiffies, timeout)) {
+ mmcst1 = readl(host->base + DAVINCI_MMCST1);
+ if (!(mmcst1 & MMCST1_BUSY))
+ break;
+ cpu_relax();
+ }
+ if (mmcst1 & MMCST1_BUSY) {
+ dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n");
+ req->cmd->error = -ETIMEDOUT;
+ mmc_request_done(mmc, req);
+ return;
+ }
+
+ host->do_dma = 0;
+ mmc_davinci_prepare_data(host, req);
+ mmc_davinci_start_command(host, req->cmd);
+}
+
+static unsigned int calculate_freq_for_card(struct mmc_davinci_host *host,
+ unsigned int mmc_req_freq)
+{
+ unsigned int mmc_freq = 0, mmc_pclk = 0, mmc_push_pull_divisor = 0;
+
+ mmc_pclk = host->mmc_input_clk;
+ if (mmc_req_freq && mmc_pclk > (2 * mmc_req_freq))
+ mmc_push_pull_divisor = ((unsigned int)mmc_pclk
+ / (2 * mmc_req_freq)) - 1;
+ else
+ mmc_push_pull_divisor = 0;
+
+ mmc_freq = (unsigned int)mmc_pclk
+ / (2 * (mmc_push_pull_divisor + 1));
+
+ if (mmc_freq > mmc_req_freq)
+ mmc_push_pull_divisor = mmc_push_pull_divisor + 1;
+ /* Convert ns to clock cycles */
+ if (mmc_req_freq <= 400000)
+ host->ns_in_one_cycle = (1000000) / (((mmc_pclk
+ / (2 * (mmc_push_pull_divisor + 1)))/1000));
+ else
+ host->ns_in_one_cycle = (1000000) / (((mmc_pclk
+ / (2 * (mmc_push_pull_divisor + 1)))/1000000));
+
+ return mmc_push_pull_divisor;
+}
+
+static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ unsigned int open_drain_freq = 0, mmc_pclk = 0;
+ unsigned int mmc_push_pull_freq = 0;
+ struct mmc_davinci_host *host = mmc_priv(mmc);
+
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
+ u32 temp;
+
+ /* Ignoring the init clock value passed for fixing the inter
+ * operability with different cards.
+ */
+ open_drain_freq = ((unsigned int)mmc_pclk
+ / (2 * MMCSD_INIT_CLOCK)) - 1;
+
+ if (open_drain_freq > 0xFF)
+ open_drain_freq = 0xFF;
+
+ temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKRT_MASK;
+ temp |= open_drain_freq;
+ writel(temp, host->base + DAVINCI_MMCCLK);
+
+ /* Convert ns to clock cycles */
+ host->ns_in_one_cycle = (1000000) / (MMCSD_INIT_CLOCK/1000);
+ } else {
+ u32 temp;
+ mmc_push_pull_freq = calculate_freq_for_card(host, ios->clock);
+
+ if (mmc_push_pull_freq > 0xFF)
+ mmc_push_pull_freq = 0xFF;
+
+ temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN;
+ writel(temp, host->base + DAVINCI_MMCCLK);
+
+ udelay(10);
+
+ temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKRT_MASK;
+ temp |= mmc_push_pull_freq;
+ writel(temp, host->base + DAVINCI_MMCCLK);
+
+ writel(temp | MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
+
+ udelay(10);
+ }
+}
+
+static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ unsigned int mmc_pclk = 0;
+ struct mmc_davinci_host *host = mmc_priv(mmc);
+
+ mmc_pclk = host->mmc_input_clk;
+ dev_dbg(mmc_dev(host->mmc),
+ "clock %dHz busmode %d powermode %d Vdd %04x\n",
+ ios->clock, ios->bus_mode, ios->power_mode,
+ ios->vdd);
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
+ dev_dbg(mmc_dev(host->mmc), "Enabling 4 bit mode\n");
+ writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_WIDTH_4_BIT,
+ host->base + DAVINCI_MMCCTL);
+ } else {
+ dev_dbg(mmc_dev(host->mmc), "Disabling 4 bit mode\n");
+ writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_WIDTH_4_BIT,
+ host->base + DAVINCI_MMCCTL);
+ }
+
+ calculate_clk_divider(mmc, ios);
+
+ host->bus_mode = ios->bus_mode;
+ if (ios->power_mode == MMC_POWER_UP) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(50);
+ bool lose = true;
+
+ /* Send clock cycles, poll completion */
+ writel(0, host->base + DAVINCI_MMCARGHL);
+ writel(MMCCMD_INITCK, host->base + DAVINCI_MMCCMD);
+ while (time_before(jiffies, timeout)) {
+ u32 tmp = readl(host->base + DAVINCI_MMCST0);
+
+ if (tmp & MMCST0_RSPDNE) {
+ lose = false;
+ break;
+ }
+ cpu_relax();
+ }
+ if (lose)
+ dev_warn(mmc_dev(host->mmc), "powerup timeout\n");
+ }
+
+ /* FIXME on power OFF, reset things ... */
+}
+
+static void
+mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
+{
+ host->data = NULL;
+
+ if (host->do_dma) {
+ davinci_abort_dma(host);
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ host->do_dma = false;
+ }
+ host->data_dir = DAVINCI_MMC_DATADIR_NONE;
+
+ if (!data->stop || (host->cmd && host->cmd->error)) {
+ mmc_request_done(host->mmc, data->mrq);
+ writel(0, host->base + DAVINCI_MMCIM);
+ } else
+ mmc_davinci_start_command(host, data->stop);
+}
+
+static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
+ struct mmc_command *cmd)
+{
+ host->cmd = NULL;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->resp[3] = readl(host->base + DAVINCI_MMCRSP01);
+ cmd->resp[2] = readl(host->base + DAVINCI_MMCRSP23);
+ cmd->resp[1] = readl(host->base + DAVINCI_MMCRSP45);
+ cmd->resp[0] = readl(host->base + DAVINCI_MMCRSP67);
+ } else {
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->resp[0] = readl(host->base + DAVINCI_MMCRSP67);
+ }
+ }
+
+ if (host->data == NULL || cmd->error) {
+ if (cmd->error == -ETIMEDOUT)
+ cmd->mrq->cmd->retries = 0;
+ mmc_request_done(host->mmc, cmd->mrq);
+ writel(0, host->base + DAVINCI_MMCIM);
+ }
+}
+
+static void
+davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
+{
+ u32 temp;
+
+ /* reset command and data state machines */
+ temp = readl(host->base + DAVINCI_MMCCTL);
+ writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
+ host->base + DAVINCI_MMCCTL);
+
+ temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
+ udelay(10);
+ writel(temp, host->base + DAVINCI_MMCCTL);
+}
+
+static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
+{
+ struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
+ unsigned int status, qstatus;
+ int end_command = 0;
+ int end_transfer = 0;
+ struct mmc_data *data = host->data;
+
+ if (host->cmd == NULL && host->data == NULL) {
+ status = readl(host->base + DAVINCI_MMCST0);
+ dev_dbg(mmc_dev(host->mmc),
+ "Spurious interrupt 0x%04x\n", status);
+ /* Disable the interrupt from mmcsd */
+ writel(0, host->base + DAVINCI_MMCIM);
+ return IRQ_NONE;
+ }
+
+ status = readl(host->base + DAVINCI_MMCST0);
+ qstatus = status;
+
+ /* handle FIFO first when using PIO for data.
+ * bytes_left will decrease to zero as I/O progress and status will
+ * read zero over iteration because this controller status
+ * register(MMCST0) reports any status only once and it is cleared
+ * by read. So, it is not unbouned loop even in the case of
+ * non-dma.
+ */
+ while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) {
+ davinci_fifo_data_trans(host, rw_threshold);
+ status = readl(host->base + DAVINCI_MMCST0);
+ if (!status)
+ break;
+ qstatus |= status;
+ }
+
+ if (qstatus & MMCST0_DATDNE) {
+ /* All blocks sent/received, and CRC checks passed */
+ if (data != NULL) {
+ if ((host->do_dma == 0) && (host->bytes_left > 0)) {
+ /* if datasize < rw_threshold
+ * no RX ints are generated
+ */
+ davinci_fifo_data_trans(host, host->bytes_left);
+ }
+ end_transfer = 1;
+ data->bytes_xfered = data->blocks * data->blksz;
+ } else {
+ dev_err(mmc_dev(host->mmc),
+ "DATDNE with no host->data\n");
+ }
+ }
+
+ if (qstatus & MMCST0_TOUTRD) {
+ /* Read data timeout */
+ data->error = -ETIMEDOUT;
+ end_transfer = 1;
+
+ dev_dbg(mmc_dev(host->mmc),
+ "read data timeout, status %x\n",
+ qstatus);
+
+ davinci_abort_data(host, data);
+ }
+
+ if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
+ /* Data CRC error */
+ data->error = -EILSEQ;
+ end_transfer = 1;
+
+ /* NOTE: this controller uses CRCWR to report both CRC
+ * errors and timeouts (on writes). MMCDRSP values are
+ * only weakly documented, but 0x9f was clearly a timeout
+ * case and the two three-bit patterns in various SD specs
+ * (101, 010) aren't part of it ...
+ */
+ if (qstatus & MMCST0_CRCWR) {
+ u32 temp = readb(host->base + DAVINCI_MMCDRSP);
+
+ if (temp == 0x9f)
+ data->error = -ETIMEDOUT;
+ }
+ dev_dbg(mmc_dev(host->mmc), "data %s %s error\n",
+ (qstatus & MMCST0_CRCWR) ? "write" : "read",
+ (data->error == -ETIMEDOUT) ? "timeout" : "CRC");
+
+ davinci_abort_data(host, data);
+ }
+
+ if (qstatus & MMCST0_TOUTRS) {
+ /* Command timeout */
+ if (host->cmd) {
+ dev_dbg(mmc_dev(host->mmc),
+ "CMD%d timeout, status %x\n",
+ host->cmd->opcode, qstatus);
+ host->cmd->error = -ETIMEDOUT;
+ if (data) {
+ end_transfer = 1;
+ davinci_abort_data(host, data);
+ } else
+ end_command = 1;
+ }
+ }
+
+ if (qstatus & MMCST0_CRCRS) {
+ /* Command CRC error */
+ dev_dbg(mmc_dev(host->mmc), "Command CRC error\n");
+ if (host->cmd) {
+ host->cmd->error = -EILSEQ;
+ end_command = 1;
+ }
+ }
+
+ if (qstatus & MMCST0_RSPDNE) {
+ /* End of command phase */
+ end_command = (int) host->cmd;
+ }
+
+ if (end_command)
+ mmc_davinci_cmd_done(host, host->cmd);
+ if (end_transfer)
+ mmc_davinci_xfer_done(host, data);
+ return IRQ_HANDLED;
+}
+
+static int mmc_davinci_get_cd(struct mmc_host *mmc)
+{
+ struct platform_device *pdev = to_platform_device(mmc->parent);
+ struct davinci_mmc_config *config = pdev->dev.platform_data;
+
+ if (!config || !config->get_cd)
+ return -ENOSYS;
+ return config->get_cd(pdev->id);
+}
+
+static int mmc_davinci_get_ro(struct mmc_host *mmc)
+{
+ struct platform_device *pdev = to_platform_device(mmc->parent);
+ struct davinci_mmc_config *config = pdev->dev.platform_data;
+
+ if (!config || !config->get_ro)
+ return -ENOSYS;
+ return config->get_ro(pdev->id);
+}
+
+static struct mmc_host_ops mmc_davinci_ops = {
+ .request = mmc_davinci_request,
+ .set_ios = mmc_davinci_set_ios,
+ .get_cd = mmc_davinci_get_cd,
+ .get_ro = mmc_davinci_get_ro,
+};
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_CPU_FREQ
+static int mmc_davinci_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct mmc_davinci_host *host;
+ unsigned int mmc_pclk;
+ struct mmc_host *mmc;
+ unsigned long flags;
+
+ host = container_of(nb, struct mmc_davinci_host, freq_transition);
+ mmc = host->mmc;
+ mmc_pclk = clk_get_rate(host->clk);
+
+ if (val == CPUFREQ_POSTCHANGE) {
+ spin_lock_irqsave(&mmc->lock, flags);
+ host->mmc_input_clk = mmc_pclk;
+ calculate_clk_divider(mmc, &mmc->ios);
+ spin_unlock_irqrestore(&mmc->lock, flags);
+ }
+
+ return 0;
+}
+
+static inline int mmc_davinci_cpufreq_register(struct mmc_davinci_host *host)
+{
+ host->freq_transition.notifier_call = mmc_davinci_cpufreq_transition;
+
+ return cpufreq_register_notifier(&host->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
+{
+ cpufreq_unregister_notifier(&host->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+#else
+static inline int mmc_davinci_cpufreq_register(struct mmc_davinci_host *host)
+{
+ return 0;
+}
+
+static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
+{
+}
+#endif
+static void __init init_mmcsd_host(struct mmc_davinci_host *host)
+{
+ /* DAT line portion is diabled and in reset state */
+ writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_DATRST,
+ host->base + DAVINCI_MMCCTL);
+
+ /* CMD line portion is diabled and in reset state */
+ writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_CMDRST,
+ host->base + DAVINCI_MMCCTL);
+
+ udelay(10);
+
+ writel(0, host->base + DAVINCI_MMCCLK);
+ writel(MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
+
+ writel(0x1FFF, host->base + DAVINCI_MMCTOR);
+ writel(0xFFFF, host->base + DAVINCI_MMCTOD);
+
+ writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_DATRST,
+ host->base + DAVINCI_MMCCTL);
+ writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_CMDRST,
+ host->base + DAVINCI_MMCCTL);
+
+ udelay(10);
+}
+
+static int __init davinci_mmcsd_probe(struct platform_device *pdev)
+{
+ struct davinci_mmc_config *pdata = pdev->dev.platform_data;
+ struct mmc_davinci_host *host = NULL;
+ struct mmc_host *mmc = NULL;
+ struct resource *r, *mem = NULL;
+ int ret = 0, irq = 0;
+ size_t mem_size;
+
+ /* REVISIT: when we're fully converted, fail if pdata is NULL */
+
+ ret = -ENODEV;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!r || irq == NO_IRQ)
+ goto out;
+
+ ret = -EBUSY;
+ mem_size = resource_size(r);
+ mem = request_mem_region(r->start, mem_size, pdev->name);
+ if (!mem)
+ goto out;
+
+ ret = -ENOMEM;
+ mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev);
+ if (!mmc)
+ goto out;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc; /* Important */
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r)
+ goto out;
+ host->rxdma = r->start;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!r)
+ goto out;
+ host->txdma = r->start;
+
+ host->mem_res = mem;
+ host->base = ioremap(mem->start, mem_size);
+ if (!host->base)
+ goto out;
+
+ ret = -ENXIO;
+ host->clk = clk_get(&pdev->dev, "MMCSDCLK");
+ if (IS_ERR(host->clk)) {
+ ret = PTR_ERR(host->clk);
+ goto out;
+ }
+ clk_enable(host->clk);
+ host->mmc_input_clk = clk_get_rate(host->clk);
+
+ init_mmcsd_host(host);
+
+ host->use_dma = use_dma;
+ host->irq = irq;
+
+ if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
+ host->use_dma = 0;
+
+ /* REVISIT: someday, support IRQ-driven card detection. */
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ if (!pdata || pdata->wires == 4 || pdata->wires == 0)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ host->version = pdata->version;
+
+ mmc->ops = &mmc_davinci_ops;
+ mmc->f_min = 312500;
+ mmc->f_max = 25000000;
+ if (pdata && pdata->max_freq)
+ mmc->f_max = pdata->max_freq;
+ if (pdata && pdata->caps)
+ mmc->caps |= pdata->caps;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ /* With no iommu coalescing pages, each phys_seg is a hw_seg.
+ * Each hw_seg uses one EDMA parameter RAM slot, always one
+ * channel and then usually some linked slots.
+ */
+ mmc->max_hw_segs = 1 + host->n_link;
+ mmc->max_phys_segs = mmc->max_hw_segs;
+
+ /* EDMA limit per hw segment (one or two MBytes) */
+ mmc->max_seg_size = MAX_CCNT * rw_threshold;
+
+ /* MMC/SD controller limits for multiblock requests */
+ mmc->max_blk_size = 4095; /* BLEN is 12 bits */
+ mmc->max_blk_count = 65535; /* NBLK is 16 bits */
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+
+ dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs);
+ dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs);
+ dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size);
+ dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size);
+ dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size);
+
+ platform_set_drvdata(pdev, host);
+
+ ret = mmc_davinci_cpufreq_register(host);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register cpufreq\n");
+ goto cpu_freq_fail;
+ }
+
+ ret = mmc_add_host(mmc);
+ if (ret < 0)
+ goto out;
+
+ ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host);
+ if (ret)
+ goto out;
+
+ rename_region(mem, mmc_hostname(mmc));
+
+ dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
+ host->use_dma ? "DMA" : "PIO",
+ (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
+
+ return 0;
+
+out:
+ mmc_davinci_cpufreq_deregister(host);
+cpu_freq_fail:
+ if (host) {
+ davinci_release_dma_channels(host);
+
+ if (host->clk) {
+ clk_disable(host->clk);
+ clk_put(host->clk);
+ }
+
+ if (host->base)
+ iounmap(host->base);
+ }
+
+ if (mmc)
+ mmc_free_host(mmc);
+
+ if (mem)
+ release_resource(mem);
+
+ dev_dbg(&pdev->dev, "probe err %d\n", ret);
+
+ return ret;
+}
+
+static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
+{
+ struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ if (host) {
+ mmc_davinci_cpufreq_deregister(host);
+
+ mmc_remove_host(host->mmc);
+ free_irq(host->irq, host);
+
+ davinci_release_dma_channels(host);
+
+ clk_disable(host->clk);
+ clk_put(host->clk);
+
+ iounmap(host->base);
+
+ release_resource(host->mem_res);
+
+ mmc_free_host(host->mmc);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int davinci_mmcsd_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+
+ return mmc_suspend_host(host->mmc, msg);
+}
+
+static int davinci_mmcsd_resume(struct platform_device *pdev)
+{
+ struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+
+ return mmc_resume_host(host->mmc);
+}
+#else
+#define davinci_mmcsd_suspend NULL
+#define davinci_mmcsd_resume NULL
+#endif
+
+static struct platform_driver davinci_mmcsd_driver = {
+ .driver = {
+ .name = "davinci_mmc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(davinci_mmcsd_remove),
+ .suspend = davinci_mmcsd_suspend,
+ .resume = davinci_mmcsd_resume,
+};
+
+static int __init davinci_mmcsd_init(void)
+{
+ return platform_driver_probe(&davinci_mmcsd_driver,
+ davinci_mmcsd_probe);
+}
+module_init(davinci_mmcsd_init);
+
+static void __exit davinci_mmcsd_exit(void)
+{
+ platform_driver_unregister(&davinci_mmcsd_driver);
+}
+module_exit(davinci_mmcsd_exit);
+
+MODULE_AUTHOR("Texas Instruments India");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller");
+
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 88671529c45d..60a2b69e54f5 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -679,17 +679,17 @@ static int mxcmci_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct mxcmci_host *host = NULL;
- struct resource *r;
+ struct resource *iores, *r;
int ret = 0, irq;
printk(KERN_INFO "i.MX SDHC driver\n");
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (!r || irq < 0)
+ if (!iores || irq < 0)
return -EINVAL;
- r = request_mem_region(r->start, resource_size(r), pdev->name);
+ r = request_mem_region(iores->start, resource_size(iores), pdev->name);
if (!r)
return -EBUSY;
@@ -809,7 +809,7 @@ out_iounmap:
out_free:
mmc_free_host(mmc);
out_release_mem:
- release_mem_region(host->res->start, resource_size(host->res));
+ release_mem_region(iores->start, resource_size(iores));
return ret;
}
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 5f970e253e50..c6d7e8ecadbf 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1459,8 +1459,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
goto err_ioremap;
host->iclk = clk_get(&pdev->dev, "ick");
- if (IS_ERR(host->iclk))
+ if (IS_ERR(host->iclk)) {
+ ret = PTR_ERR(host->iclk);
goto err_free_mmc_host;
+ }
clk_enable(host->iclk);
host->fclk = clk_get(&pdev->dev, "fck");
@@ -1500,10 +1502,8 @@ err_free_irq:
err_free_fclk:
clk_put(host->fclk);
err_free_iclk:
- if (host->iclk != NULL) {
- clk_disable(host->iclk);
- clk_put(host->iclk);
- }
+ clk_disable(host->iclk);
+ clk_put(host->iclk);
err_free_mmc_host:
iounmap(host->virt_base);
err_ioremap:
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index bb47ff465c04..0d783f3e79ed 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -828,7 +828,7 @@ static int pxamci_resume(struct device *dev)
return ret;
}
-static struct dev_pm_ops pxamci_pm_ops = {
+static const struct dev_pm_ops pxamci_pm_ops = {
.suspend = pxamci_suspend,
.resume = pxamci_resume,
};
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 941a4d35ef8d..d96e1abf2d64 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -820,7 +820,7 @@ fail_request:
static void finalize_request(struct s3cmci_host *host)
{
struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+ struct mmc_command *cmd;
int debug_as_failure = 0;
if (host->complete_what != COMPLETION_FINALIZE)
@@ -828,6 +828,7 @@ static void finalize_request(struct s3cmci_host *host)
if (!mrq)
return;
+ cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
if (cmd->data && (cmd->error == 0) &&
(cmd->data->error == 0)) {
@@ -1302,10 +1303,8 @@ static int s3cmci_get_ro(struct mmc_host *mmc)
if (pdata->no_wprotect)
return 0;
- ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
-
- if (pdata->wprotect_invert)
- ret = !ret;
+ ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;
+ ret ^= pdata->wprotect_invert;
return ret;
}
@@ -1654,7 +1653,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
goto probe_free_irq;
}
- host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
+ host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
if (host->irq_cd >= 0) {
if (request_irq(host->irq_cd, s3cmci_irq_cd,
@@ -1892,7 +1891,7 @@ static int s3cmci_resume(struct device *dev)
return mmc_resume_host(mmc);
}
-static struct dev_pm_ops s3cmci_pm = {
+static const struct dev_pm_ops s3cmci_pm = {
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index e0356644d1aa..5c3a1767770a 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -285,6 +285,73 @@ static const struct sdhci_pci_fixes sdhci_jmicron = {
.resume = jmicron_resume,
};
+/* SysKonnect CardBus2SDIO extra registers */
+#define SYSKT_CTRL 0x200
+#define SYSKT_RDFIFO_STAT 0x204
+#define SYSKT_WRFIFO_STAT 0x208
+#define SYSKT_POWER_DATA 0x20c
+#define SYSKT_POWER_330 0xef
+#define SYSKT_POWER_300 0xf8
+#define SYSKT_POWER_184 0xcc
+#define SYSKT_POWER_CMD 0x20d
+#define SYSKT_POWER_START (1 << 7)
+#define SYSKT_POWER_STATUS 0x20e
+#define SYSKT_POWER_STATUS_OK (1 << 0)
+#define SYSKT_BOARD_REV 0x210
+#define SYSKT_CHIP_REV 0x211
+#define SYSKT_CONF_DATA 0x212
+#define SYSKT_CONF_DATA_1V8 (1 << 2)
+#define SYSKT_CONF_DATA_2V5 (1 << 1)
+#define SYSKT_CONF_DATA_3V3 (1 << 0)
+
+static int syskt_probe(struct sdhci_pci_chip *chip)
+{
+ if ((chip->pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
+ chip->pdev->class &= ~0x0000FF;
+ chip->pdev->class |= PCI_SDHCI_IFDMA;
+ }
+ return 0;
+}
+
+static int syskt_probe_slot(struct sdhci_pci_slot *slot)
+{
+ int tm, ps;
+
+ u8 board_rev = readb(slot->host->ioaddr + SYSKT_BOARD_REV);
+ u8 chip_rev = readb(slot->host->ioaddr + SYSKT_CHIP_REV);
+ dev_info(&slot->chip->pdev->dev, "SysKonnect CardBus2SDIO, "
+ "board rev %d.%d, chip rev %d.%d\n",
+ board_rev >> 4, board_rev & 0xf,
+ chip_rev >> 4, chip_rev & 0xf);
+ if (chip_rev >= 0x20)
+ slot->host->quirks |= SDHCI_QUIRK_FORCE_DMA;
+
+ writeb(SYSKT_POWER_330, slot->host->ioaddr + SYSKT_POWER_DATA);
+ writeb(SYSKT_POWER_START, slot->host->ioaddr + SYSKT_POWER_CMD);
+ udelay(50);
+ tm = 10; /* Wait max 1 ms */
+ do {
+ ps = readw(slot->host->ioaddr + SYSKT_POWER_STATUS);
+ if (ps & SYSKT_POWER_STATUS_OK)
+ break;
+ udelay(100);
+ } while (--tm);
+ if (!tm) {
+ dev_err(&slot->chip->pdev->dev,
+ "power regulator never stabilized");
+ writeb(0, slot->host->ioaddr + SYSKT_POWER_CMD);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_syskt = {
+ .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER,
+ .probe = syskt_probe,
+ .probe_slot = syskt_probe_slot,
+};
+
static int via_probe(struct sdhci_pci_chip *chip)
{
if (chip->pdev->revision == 0x10)
@@ -363,6 +430,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
},
{
+ .vendor = PCI_VENDOR_ID_SYSKONNECT,
+ .device = 0x8000,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_syskt,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_VIA,
.device = 0x95d0,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 91991b460c45..7cccc8523747 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -591,7 +591,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
disable_mmc_irqs(host, TMIO_MASK_ALL);
ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED |
- IRQF_TRIGGER_FALLING, "tmio-mmc", host);
+ IRQF_TRIGGER_FALLING, dev_name(&dev->dev), host);
if (ret)
goto unmap_cnf;
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 7c302d55910e..66123419f65d 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -216,7 +216,7 @@ static int nomadik_nand_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops nomadik_nand_pm_ops = {
+static const struct dev_pm_ops nomadik_nand_pm_ops = {
.suspend = nomadik_nand_suspend,
.resume = nomadik_nand_resume,
};
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 78b7167a8ce3..39db0e96815d 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -837,7 +837,7 @@ static int vortex_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops vortex_pm_ops = {
+static const struct dev_pm_ops vortex_pm_ops = {
.suspend = vortex_suspend,
.resume = vortex_resume,
.freeze = vortex_suspend,
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
index 8c658cf6f62f..109d2783e4d8 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/chelsio/sge.c
@@ -1378,7 +1378,7 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
}
__skb_pull(skb, sizeof(*p));
- st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id());
+ st = this_cpu_ptr(sge->port_stats[p->iff]);
skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev);
if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff &&
@@ -1780,8 +1780,7 @@ netdev_tx_t t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct adapter *adapter = dev->ml_priv;
struct sge *sge = adapter->sge;
- struct sge_port_stats *st = per_cpu_ptr(sge->port_stats[dev->if_port],
- smp_processor_id());
+ struct sge_port_stats *st = this_cpu_ptr(sge->port_stats[dev->if_port]);
struct cpl_tx_pkt *cpl;
struct sk_buff *orig_skb = skb;
int ret;
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 0cbe3c0e7c06..b37730065688 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -1646,7 +1646,7 @@ dm9000_drv_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops dm9000_drv_pm_ops = {
+static const struct dev_pm_ops dm9000_drv_pm_ops = {
.suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume,
};
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index eae4ad749e9d..b9fcc9819837 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -81,7 +81,7 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb,
/* it's OK to use per_cpu_ptr() because BHs are off */
pcpu_lstats = dev->ml_priv;
- lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id());
+ lb_stats = this_cpu_ptr(pcpu_lstats);
len = skb->len;
if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {
diff --git a/drivers/net/mlx4/alloc.c b/drivers/net/mlx4/alloc.c
index ad95d5f7b630..8c8515619b8e 100644
--- a/drivers/net/mlx4/alloc.c
+++ b/drivers/net/mlx4/alloc.c
@@ -72,35 +72,6 @@ void mlx4_bitmap_free(struct mlx4_bitmap *bitmap, u32 obj)
mlx4_bitmap_free_range(bitmap, obj, 1);
}
-static unsigned long find_aligned_range(unsigned long *bitmap,
- u32 start, u32 nbits,
- int len, int align)
-{
- unsigned long end, i;
-
-again:
- start = ALIGN(start, align);
-
- while ((start < nbits) && test_bit(start, bitmap))
- start += align;
-
- if (start >= nbits)
- return -1;
-
- end = start+len;
- if (end > nbits)
- return -1;
-
- for (i = start + 1; i < end; i++) {
- if (test_bit(i, bitmap)) {
- start = i + 1;
- goto again;
- }
- }
-
- return start;
-}
-
u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt, int align)
{
u32 obj, i;
@@ -110,13 +81,13 @@ u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt, int align)
spin_lock(&bitmap->lock);
- obj = find_aligned_range(bitmap->table, bitmap->last,
- bitmap->max, cnt, align);
+ obj = bitmap_find_next_zero_area(bitmap->table, bitmap->max,
+ bitmap->last, cnt, align - 1);
if (obj >= bitmap->max) {
bitmap->top = (bitmap->top + bitmap->max + bitmap->reserved_top)
& bitmap->mask;
- obj = find_aligned_range(bitmap->table, 0, bitmap->max,
- cnt, align);
+ obj = bitmap_find_next_zero_area(bitmap->table, bitmap->max,
+ 0, cnt, align - 1);
}
if (obj < bitmap->max) {
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 81bafd578478..d431b59e7d11 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -270,7 +270,7 @@ static int try_io_port(struct pcmcia_device *link)
/* for master/slave multifunction cards */
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ IRQ_TYPE_DYNAMIC_SHARING;
}
} else {
/* This should be two 16-port windows */
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index 8ad8384fc1c0..813aca3fc433 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -426,7 +426,7 @@ static int fmvj18x_config(struct pcmcia_device *link)
if (link->io.NumPorts2 != 0) {
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ IRQ_TYPE_DYNAMIC_SHARING;
ret = mfc_try_io_port(link);
if (ret != 0) goto failed;
} else if (cardtype == UNGERMANN) {
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index 2d26b6ca28b9..92ed3fbf89a5 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -490,7 +490,7 @@ static int try_io_port(struct pcmcia_device *link)
/* for master/slave multifunction cards */
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ IRQ_TYPE_DYNAMIC_SHARING;
}
} else {
/* This should be two 16-port windows */
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index cc4853bc0253..6dd486d2977b 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -454,7 +454,7 @@ static int mhz_mfc_config(struct pcmcia_device *link)
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ IRQ_TYPE_DYNAMIC_SHARING;
link->io.IOAddrLines = 16;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index a2eda28f903e..466fc72698c0 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -841,7 +841,7 @@ xirc2ps_config(struct pcmcia_device * link)
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status |= CCSR_AUDIO_ENA;
}
- link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED ;
+ link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING;
link->io.NumPorts2 = 8;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
if (local->dingo) {
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index acfc5a3aa490..60f96c468a24 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -4859,7 +4859,7 @@ out:
return 0;
}
-static struct dev_pm_ops rtl8169_pm_ops = {
+static const struct dev_pm_ops rtl8169_pm_ops = {
.suspend = rtl8169_suspend,
.resume = rtl8169_resume,
.freeze = rtl8169_suspend,
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index 20d6095cf411..494cd91ea39c 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -2154,7 +2154,7 @@ static int smsc911x_resume(struct device *dev)
return (to == 0) ? -EIO : 0;
}
-static struct dev_pm_ops smsc911x_pm_ops = {
+static const struct dev_pm_ops smsc911x_pm_ops = {
.suspend = smsc911x_suspend,
.resume = smsc911x_resume,
};
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 63099c58a6dd..3a15de56df9c 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -153,15 +153,14 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
struct net_device *rcv = NULL;
struct veth_priv *priv, *rcv_priv;
struct veth_net_stats *stats, *rcv_stats;
- int length, cpu;
+ int length;
priv = netdev_priv(dev);
rcv = priv->peer;
rcv_priv = netdev_priv(rcv);
- cpu = smp_processor_id();
- stats = per_cpu_ptr(priv->stats, cpu);
- rcv_stats = per_cpu_ptr(rcv_priv->stats, cpu);
+ stats = this_cpu_ptr(priv->stats);
+ rcv_stats = this_cpu_ptr(rcv_priv->stats);
if (!(rcv->flags & IFF_UP))
goto tx_drop;
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 1ceb9d0f8b97..9cc438282d77 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -2689,7 +2689,7 @@ vmxnet3_resume(struct device *device)
return 0;
}
-static struct dev_pm_ops vmxnet3_pm_ops = {
+static const struct dev_pm_ops vmxnet3_pm_ops = {
.suspend = vmxnet3_suspend,
.resume = vmxnet3_resume,
};
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index a7aae24f2889..166b67ea622f 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -47,7 +47,7 @@
*/
static struct ring_buffer *op_ring_buffer_read;
static struct ring_buffer *op_ring_buffer_write;
-DEFINE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
+DEFINE_PER_CPU(struct oprofile_cpu_buffer, op_cpu_buffer);
static void wq_sync_buffer(struct work_struct *work);
@@ -61,8 +61,7 @@ unsigned long oprofile_get_cpu_buffer_size(void)
void oprofile_cpu_buffer_inc_smpl_lost(void)
{
- struct oprofile_cpu_buffer *cpu_buf
- = &__get_cpu_var(cpu_buffer);
+ struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
cpu_buf->sample_lost_overflow++;
}
@@ -95,7 +94,7 @@ int alloc_cpu_buffers(void)
goto fail;
for_each_possible_cpu(i) {
- struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i);
+ struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i);
b->last_task = NULL;
b->last_is_kernel = -1;
@@ -122,7 +121,7 @@ void start_cpu_work(void)
work_enabled = 1;
for_each_online_cpu(i) {
- struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i);
+ struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i);
/*
* Spread the work by 1 jiffy per cpu so they dont all
@@ -139,7 +138,7 @@ void end_cpu_work(void)
work_enabled = 0;
for_each_online_cpu(i) {
- struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i);
+ struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i);
cancel_delayed_work(&b->work);
}
@@ -330,7 +329,7 @@ static inline void
__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
unsigned long event, int is_kernel)
{
- struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
+ struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
unsigned long backtrace = oprofile_backtrace_depth;
/*
@@ -375,7 +374,7 @@ oprofile_write_reserve(struct op_entry *entry, struct pt_regs * const regs,
{
struct op_sample *sample;
int is_kernel = !user_mode(regs);
- struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
+ struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
cpu_buf->sample_received++;
@@ -430,13 +429,13 @@ int oprofile_write_commit(struct op_entry *entry)
void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event)
{
- struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
+ struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
log_sample(cpu_buf, pc, 0, is_kernel, event);
}
void oprofile_add_trace(unsigned long pc)
{
- struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
+ struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
if (!cpu_buf->tracing)
return;
diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h
index 272995d20293..68ea16ab645f 100644
--- a/drivers/oprofile/cpu_buffer.h
+++ b/drivers/oprofile/cpu_buffer.h
@@ -50,7 +50,7 @@ struct oprofile_cpu_buffer {
struct delayed_work work;
};
-DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
+DECLARE_PER_CPU(struct oprofile_cpu_buffer, op_cpu_buffer);
/*
* Resets the cpu buffer to a sane state.
@@ -60,7 +60,7 @@ DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
*/
static inline void op_cpu_buffer_reset(int cpu)
{
- struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
+ struct oprofile_cpu_buffer *cpu_buf = &per_cpu(op_cpu_buffer, cpu);
cpu_buf->last_is_kernel = -1;
cpu_buf->last_task = NULL;
diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c
index 61689e814d46..917d28ebeacd 100644
--- a/drivers/oprofile/oprofile_stats.c
+++ b/drivers/oprofile/oprofile_stats.c
@@ -23,7 +23,7 @@ void oprofile_reset_stats(void)
int i;
for_each_possible_cpu(i) {
- cpu_buf = &per_cpu(cpu_buffer, i);
+ cpu_buf = &per_cpu(op_cpu_buffer, i);
cpu_buf->sample_received = 0;
cpu_buf->sample_lost_overflow = 0;
cpu_buf->backtrace_aborted = 0;
@@ -51,7 +51,7 @@ void oprofile_create_stats_files(struct super_block *sb, struct dentry *root)
return;
for_each_possible_cpu(i) {
- cpu_buf = &per_cpu(cpu_buffer, i);
+ cpu_buf = &per_cpu(op_cpu_buffer, i);
snprintf(buf, 10, "cpu%d", i);
cpudir = oprofilefs_mkdir(sb, dir, buf);
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
index 13a64bc081b6..0bc5d474b168 100644
--- a/drivers/parisc/pdc_stable.c
+++ b/drivers/parisc/pdc_stable.c
@@ -779,12 +779,9 @@ static ssize_t pdcs_auto_write(struct kobject *kobj,
read_unlock(&pathentry->rw_lock);
DPRINTK("%s: flags before: 0x%X\n", __func__, flags);
-
- temp = in;
-
- while (*temp && isspace(*temp))
- temp++;
-
+
+ temp = skip_spaces(in);
+
c = *temp++ - '0';
if ((c != 0) && (c != 1))
goto parse_error;
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 2597145a066e..ad113b0f62db 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -3403,7 +3403,7 @@ static int __init parport_parse_param(const char *s, int *val,
*val = automatic;
else if (!strncmp(s, "none", 4))
*val = none;
- else if (nofifo && !strncmp(s, "nofifo", 4))
+ else if (nofifo && !strncmp(s, "nofifo", 6))
*val = nofifo;
else {
char *ep;
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index ce52ea34fee5..a49452e2aed9 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -43,7 +43,7 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
}
#ifdef CONFIG_PM
-static struct dev_pm_ops pcie_portdrv_pm_ops = {
+static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.suspend = pcie_port_device_suspend,
.resume = pcie_port_device_resume,
.freeze = pcie_port_device_suspend,
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index cd5082d3ca19..9f3adbd9f700 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -64,7 +64,7 @@ config PCMCIA_IOCTL
If unsure, say Y.
config CARDBUS
- bool "32-bit CardBus support"
+ bool "32-bit CardBus support"
depends on PCI
default y
---help---
@@ -87,8 +87,8 @@ config YENTA
select PCCARD_NONSTATIC
---help---
This option enables support for CardBus host bridges. Virtually
- all modern PCMCIA bridges are CardBus compatible. A "bridge" is
- the hardware inside your computer that PCMCIA cards are plugged
+ all modern PCMCIA bridges are CardBus compatible. A "bridge" is
+ the hardware inside your computer that PCMCIA cards are plugged
into.
To compile this driver as modules, choose M here: the
@@ -208,7 +208,7 @@ config PCMCIA_PXA2XX
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
- || ARCH_VIPER || ARCH_PXA_ESERIES || MACH_STARGATE2)
+ || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2)
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 382938313991..83ff802de544 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -67,7 +67,7 @@ pxa2xx-obj-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock_cs.o
pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o
pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o
pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx_cs.o
-pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o
+pxa2xx-obj-$(CONFIG_ARCOM_PCMCIA) += pxa2xx_viper.o
pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o
pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index a73b040ddbfb..cdf50f3bc2df 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -27,8 +27,8 @@
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/ioport.h>
+#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/io.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
@@ -58,7 +58,7 @@
image number and an offset within that image. xlate_rom_addr()
converts an image/offset address to an absolute offset from the
ROM's base address.
-
+
=====================================================================*/
static u_int xlate_rom_addr(void __iomem *b, u_int addr)
@@ -85,10 +85,10 @@ static u_int xlate_rom_addr(void __iomem *b, u_int addr)
These are similar to setup_cis_mem and release_cis_mem for 16-bit
cards. The "result" that is used externally is the cb_cis_virt
pointer in the struct pcmcia_socket structure.
-
+
=====================================================================*/
-static void cb_release_cis_mem(struct pcmcia_socket * s)
+static void cb_release_cis_mem(struct pcmcia_socket *s)
{
if (s->cb_cis_virt) {
dev_dbg(&s->dev, "cb_release_cis_mem()\n");
@@ -98,7 +98,7 @@ static void cb_release_cis_mem(struct pcmcia_socket * s)
}
}
-static int cb_setup_cis_mem(struct pcmcia_socket * s, struct resource *res)
+static int cb_setup_cis_mem(struct pcmcia_socket *s, struct resource *res)
{
unsigned int start, size;
@@ -124,10 +124,11 @@ static int cb_setup_cis_mem(struct pcmcia_socket * s, struct resource *res)
This is used by the CIS processing code to read CIS information
from a CardBus device.
-
+
=====================================================================*/
-int read_cb_mem(struct pcmcia_socket * s, int space, u_int addr, u_int len, void *ptr)
+int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len,
+ void *ptr)
{
struct pci_dev *dev;
struct resource *res;
@@ -181,7 +182,7 @@ fail:
cb_alloc() and cb_free() allocate and free the kernel data
structures for a Cardbus device, and handle the lowest level PCI
device setup issues.
-
+
=====================================================================*/
static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
@@ -214,14 +215,14 @@ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
}
}
-int __ref cb_alloc(struct pcmcia_socket * s)
+int __ref cb_alloc(struct pcmcia_socket *s)
{
struct pci_bus *bus = s->cb_dev->subordinate;
struct pci_dev *dev;
unsigned int max, pass;
s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0));
-// pcibios_fixup_bus(bus);
+/* pcibios_fixup_bus(bus); */
max = bus->secondary;
for (pass = 0; pass < 2; pass++)
@@ -248,7 +249,7 @@ int __ref cb_alloc(struct pcmcia_socket * s)
return 0;
}
-void cb_free(struct pcmcia_socket * s)
+void cb_free(struct pcmcia_socket *s)
{
struct pci_dev *bridge = s->cb_dev;
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 8c1b73cf021b..25b1cd219e37 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -23,7 +23,7 @@
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/ioport.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
@@ -125,7 +125,7 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag
Low-level functions to read and write CIS memory. I think the
write routine is only useful for writing one-byte registers.
-
+
======================================================================*/
/* Bits in attr field */
@@ -137,7 +137,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
{
void __iomem *sys, *end;
unsigned char *buf = ptr;
-
+
dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
if (attr & IS_INDIRECT) {
@@ -203,7 +203,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
{
void __iomem *sys, *end;
unsigned char *buf = ptr;
-
+
dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
if (attr & IS_INDIRECT) {
@@ -262,7 +262,7 @@ EXPORT_SYMBOL(pcmcia_write_cis_mem);
This is a wrapper around read_cis_mem, with the same interface,
but which caches information, for cards whose CIS may not be
readable all the time.
-
+
======================================================================*/
static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
@@ -342,7 +342,7 @@ EXPORT_SYMBOL(destroy_cis_cache);
This verifies if the CIS of a card matches what is in the CIS
cache.
-
+
======================================================================*/
int verify_cis_cache(struct pcmcia_socket *s)
@@ -381,7 +381,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
For really bad cards, we provide a facility for uploading a
replacement CIS.
-
+
======================================================================*/
int pcmcia_replace_cis(struct pcmcia_socket *s,
@@ -406,7 +406,7 @@ EXPORT_SYMBOL(pcmcia_replace_cis);
/*======================================================================
The high-level CIS tuple services
-
+
======================================================================*/
typedef struct tuple_flags {
@@ -421,8 +421,6 @@ typedef struct tuple_flags {
#define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn)
#define SPACE(f) (((tuple_flags *)(&(f)))->space)
-int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int func, tuple_t *tuple);
-
int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
{
if (!s)
@@ -523,10 +521,11 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
ofs++; continue;
}
}
-
+
/* End of chain? Follow long link if possible */
if (link[0] == CISTPL_END) {
- if ((ofs = follow_link(s, tuple)) < 0)
+ ofs = follow_link(s, tuple);
+ if (ofs < 0)
return -ENOSPC;
attr = SPACE(tuple->Flags);
read_cis_cache(s, attr, ofs, 2, link);
@@ -578,7 +577,7 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
} else
if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
break;
-
+
if (link[0] == tuple->DesiredTuple)
break;
ofs += link[1] + 2;
@@ -587,7 +586,7 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
return -ENOSPC;
}
-
+
tuple->TupleCode = link[0];
tuple->TupleLink = link[1];
tuple->CISOffset = ofs + 2;
@@ -623,7 +622,7 @@ EXPORT_SYMBOL(pccard_get_tuple_data);
/*======================================================================
Parsing routines for individual tuples
-
+
======================================================================*/
static int parse_device(tuple_t *tuple, cistpl_device_t *device)
@@ -637,26 +636,37 @@ static int parse_device(tuple_t *tuple, cistpl_device_t *device)
device->ndev = 0;
for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
-
- if (*p == 0xff) break;
+
+ if (*p == 0xff)
+ break;
device->dev[i].type = (*p >> 4);
device->dev[i].wp = (*p & 0x08) ? 1 : 0;
switch (*p & 0x07) {
- case 0: device->dev[i].speed = 0; break;
- case 1: device->dev[i].speed = 250; break;
- case 2: device->dev[i].speed = 200; break;
- case 3: device->dev[i].speed = 150; break;
- case 4: device->dev[i].speed = 100; break;
+ case 0:
+ device->dev[i].speed = 0;
+ break;
+ case 1:
+ device->dev[i].speed = 250;
+ break;
+ case 2:
+ device->dev[i].speed = 200;
+ break;
+ case 3:
+ device->dev[i].speed = 150;
+ break;
+ case 4:
+ device->dev[i].speed = 100;
+ break;
case 7:
- if (++p == q)
- return -EINVAL;
- device->dev[i].speed = SPEED_CVT(*p);
- while (*p & 0x80)
if (++p == q)
return -EINVAL;
- break;
+ device->dev[i].speed = SPEED_CVT(*p);
+ while (*p & 0x80)
+ if (++p == q)
+ return -EINVAL;
+ break;
default:
- return -EINVAL;
+ return -EINVAL;
}
if (++p == q)
@@ -671,7 +681,7 @@ static int parse_device(tuple_t *tuple, cistpl_device_t *device)
if (++p == q)
break;
}
-
+
return 0;
}
@@ -706,9 +716,9 @@ static int parse_longlink_mfc(tuple_t *tuple,
{
u_char *p;
int i;
-
+
p = (u_char *)tuple->TupleData;
-
+
link->nfn = *p; p++;
if (tuple->TupleDataLen <= link->nfn*5)
return -EINVAL;
@@ -737,11 +747,13 @@ static int parse_strings(u_char *p, u_char *q, int max,
ns++;
for (;;) {
s[j++] = (*p == 0xff) ? '\0' : *p;
- if ((*p == '\0') || (*p == 0xff)) break;
+ if ((*p == '\0') || (*p == 0xff))
+ break;
if (++p == q)
return -EINVAL;
}
- if ((*p == 0xff) || (++p == q)) break;
+ if ((*p == 0xff) || (++p == q))
+ break;
}
if (found) {
*found = ns;
@@ -756,10 +768,10 @@ static int parse_strings(u_char *p, u_char *q, int max,
static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
{
u_char *p, *q;
-
+
p = (u_char *)tuple->TupleData;
q = p + tuple->TupleDataLen;
-
+
vers_1->major = *p; p++;
vers_1->minor = *p; p++;
if (p >= q)
@@ -774,10 +786,10 @@ static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
{
u_char *p, *q;
-
+
p = (u_char *)tuple->TupleData;
q = p + tuple->TupleDataLen;
-
+
return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
altstr->str, altstr->ofs, &altstr->ns);
}
@@ -793,7 +805,8 @@ static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
q = p + tuple->TupleDataLen;
for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
- if (p > q-2) break;
+ if (p > q-2)
+ break;
jedec->id[nid].mfr = p[0];
jedec->id[nid].info = p[1];
p += 2;
@@ -871,7 +884,7 @@ static int parse_config(tuple_t *tuple, cistpl_config_t *config)
The following routines are all used to parse the nightmarish
config table entries.
-
+
======================================================================*/
static u_char *parse_power(u_char *p, u_char *q,
@@ -880,17 +893,20 @@ static u_char *parse_power(u_char *p, u_char *q,
int i;
u_int scale;
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
pwr->present = *p;
pwr->flags = 0;
p++;
for (i = 0; i < 7; i++)
if (pwr->present & (1<<i)) {
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
pwr->param[i] = POWER_CVT(*p);
scale = POWER_SCALE(*p);
while (*p & 0x80) {
- if (++p == q) return NULL;
+ if (++p == q)
+ return NULL;
if ((*p & 0x7f) < 100)
pwr->param[i] += (*p & 0x7f) * scale / 100;
else if (*p == 0x7d)
@@ -914,24 +930,28 @@ static u_char *parse_timing(u_char *p, u_char *q,
{
u_char scale;
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
scale = *p;
if ((scale & 3) != 3) {
- if (++p == q) return NULL;
+ if (++p == q)
+ return NULL;
timing->wait = SPEED_CVT(*p);
timing->waitscale = exponent[scale & 3];
} else
timing->wait = 0;
scale >>= 2;
if ((scale & 7) != 7) {
- if (++p == q) return NULL;
+ if (++p == q)
+ return NULL;
timing->ready = SPEED_CVT(*p);
timing->rdyscale = exponent[scale & 7];
} else
timing->ready = 0;
scale >>= 3;
if (scale != 7) {
- if (++p == q) return NULL;
+ if (++p == q)
+ return NULL;
timing->reserved = SPEED_CVT(*p);
timing->rsvscale = exponent[scale];
} else
@@ -946,7 +966,8 @@ static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
{
int i, j, bsz, lsz;
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
io->flags = *p;
if (!(*p & 0x80)) {
@@ -955,24 +976,29 @@ static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
return p+1;
}
-
- if (++p == q) return NULL;
+
+ if (++p == q)
+ return NULL;
io->nwin = (*p & 0x0f) + 1;
bsz = (*p & 0x30) >> 4;
- if (bsz == 3) bsz++;
+ if (bsz == 3)
+ bsz++;
lsz = (*p & 0xc0) >> 6;
- if (lsz == 3) lsz++;
+ if (lsz == 3)
+ lsz++;
p++;
-
+
for (i = 0; i < io->nwin; i++) {
io->win[i].base = 0;
io->win[i].len = 1;
for (j = 0; j < bsz; j++, p++) {
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
io->win[i].base += *p << (j*8);
}
for (j = 0; j < lsz; j++, p++) {
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
io->win[i].len += *p << (j*8);
}
}
@@ -986,27 +1012,32 @@ static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
int i, j, asz, lsz, has_ha;
u_int len, ca, ha;
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
mem->nwin = (*p & 0x07) + 1;
lsz = (*p & 0x18) >> 3;
asz = (*p & 0x60) >> 5;
has_ha = (*p & 0x80);
- if (++p == q) return NULL;
-
+ if (++p == q)
+ return NULL;
+
for (i = 0; i < mem->nwin; i++) {
len = ca = ha = 0;
for (j = 0; j < lsz; j++, p++) {
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
len += *p << (j*8);
}
for (j = 0; j < asz; j++, p++) {
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
ca += *p << (j*8);
}
if (has_ha)
for (j = 0; j < asz; j++, p++) {
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
ha += *p << (j*8);
}
mem->win[i].len = len << 8;
@@ -1095,7 +1126,7 @@ static int parse_cftable_entry(tuple_t *tuple,
entry->timing.ready = 0;
entry->timing.reserved = 0;
}
-
+
/* I/O window options */
if (features & 0x08) {
p = parse_io(p, q, &entry->io);
@@ -1103,7 +1134,7 @@ static int parse_cftable_entry(tuple_t *tuple,
return -EINVAL;
} else
entry->io.nwin = 0;
-
+
/* Interrupt options */
if (features & 0x10) {
p = parse_irq(p, q, &entry->irq);
@@ -1153,7 +1184,7 @@ static int parse_cftable_entry(tuple_t *tuple,
}
entry->subtuples = q-p;
-
+
return 0;
}
@@ -1176,7 +1207,7 @@ static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
{
u_char *p;
-
+
p = (u_char *)tuple->TupleData;
if ((*p != 3) || (tuple->TupleDataLen < 6))
return -EINVAL;
@@ -1231,7 +1262,7 @@ static int parse_cftable_entry_cb(tuple_t *tuple,
entry->io = *p; p++;
} else
entry->io = 0;
-
+
/* Interrupt options */
if (features & 0x10) {
p = parse_irq(p, q, &entry->irq);
@@ -1264,7 +1295,7 @@ static int parse_cftable_entry_cb(tuple_t *tuple,
}
entry->subtuples = q-p;
-
+
return 0;
}
@@ -1281,7 +1312,8 @@ static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
q = p + tuple->TupleDataLen;
for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
- if (p > q-6) break;
+ if (p > q-6)
+ break;
geo->geo[n].buswidth = p[0];
geo->geo[n].erase_block = 1 << (p[1]-1);
geo->geo[n].read_block = 1 << (p[2]-1);
@@ -1302,13 +1334,13 @@ static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
if (tuple->TupleDataLen < 10)
return -EINVAL;
-
+
p = tuple->TupleData;
q = p + tuple->TupleDataLen;
v2->vers = p[0];
v2->comply = p[1];
- v2->dindex = get_unaligned_le16(p +2 );
+ v2->dindex = get_unaligned_le16(p + 2);
v2->vspec8 = p[6];
v2->vspec9 = p[7];
v2->nhdr = p[8];
@@ -1322,7 +1354,7 @@ static int parse_org(tuple_t *tuple, cistpl_org_t *org)
{
u_char *p, *q;
int i;
-
+
p = tuple->TupleData;
q = p + tuple->TupleDataLen;
if (p == q)
@@ -1332,7 +1364,8 @@ static int parse_org(tuple_t *tuple, cistpl_org_t *org)
return -EINVAL;
for (i = 0; i < 30; i++) {
org->desc[i] = *p;
- if (*p == '\0') break;
+ if (*p == '\0')
+ break;
if (++p == q)
return -EINVAL;
}
@@ -1363,7 +1396,7 @@ static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
{
int ret = 0;
-
+
if (tuple->TupleDataLen > tuple->TupleDataMax)
return -EINVAL;
switch (tuple->TupleCode) {
@@ -1448,7 +1481,7 @@ EXPORT_SYMBOL(pcmcia_parse_tuple);
/*======================================================================
This is used internally by Card Services to look up CIS stuff.
-
+
======================================================================*/
int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse)
@@ -1550,7 +1583,7 @@ EXPORT_SYMBOL(pccard_loop_tuple);
checks include making sure several critical tuples are present and
valid; seeing if the total number of tuples is reasonable; and
looking for tuples that use reserved codes.
-
+
======================================================================*/
int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 790af87a922f..6d6f82b38a68 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -135,7 +135,7 @@ int pcmcia_socket_dev_resume(struct device *dev)
EXPORT_SYMBOL(pcmcia_socket_dev_resume);
-struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt)
+struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt)
{
struct device *dev = get_device(&skt->dev);
if (!dev)
@@ -145,7 +145,7 @@ struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt)
put_device(&skt->dev);
return NULL;
}
- return (skt);
+ return skt;
}
EXPORT_SYMBOL(pcmcia_get_socket);
@@ -297,7 +297,7 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket)
EXPORT_SYMBOL(pcmcia_unregister_socket);
-struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr)
+struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr)
{
struct pcmcia_socket *s;
@@ -736,7 +736,7 @@ EXPORT_SYMBOL(pcmcia_parse_events);
/* register pcmcia_callback */
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
{
- int ret = 0;
+ int ret = 0;
/* s->skt_mutex also protects s->callback */
mutex_lock(&s->skt_mutex);
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(pcmcia_suspend_card);
int pcmcia_resume_card(struct pcmcia_socket *skt)
{
int ret;
-
+
dev_dbg(&skt->dev, "waking up socket\n");
mutex_lock(&skt->skt_mutex);
@@ -876,7 +876,7 @@ EXPORT_SYMBOL(pcmcia_resume_card);
int pcmcia_eject_card(struct pcmcia_socket *skt)
{
int ret;
-
+
dev_dbg(&skt->dev, "user eject request\n");
mutex_lock(&skt->skt_mutex);
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 05893d41dd41..1a4a3c49cc15 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -57,7 +57,7 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
"function\n", p_drv->drv.name);
while (did && did->match_flags) {
- for (i=0; i<4; i++) {
+ for (i = 0; i < 4; i++) {
if (!did->prod_id[i])
continue;
@@ -105,7 +105,7 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count)
__u16 match_flags, manf_id, card_id;
__u8 func_id, function, device_no;
__u32 prod_id_hash[4] = {0, 0, 0, 0};
- int fields=0;
+ int fields = 0;
int retval = 0;
fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x",
@@ -214,7 +214,7 @@ EXPORT_SYMBOL(pcmcia_unregister_driver);
/* pcmcia_device handling */
-struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev)
+struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev)
{
struct device *tmp_dev;
tmp_dev = get_device(&p_dev->dev);
@@ -258,7 +258,7 @@ static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
return;
}
-static int pcmcia_device_probe(struct device * dev)
+static int pcmcia_device_probe(struct device *dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
@@ -325,7 +325,7 @@ put_module:
put_dev:
if (ret)
put_device(dev);
- return (ret);
+ return ret;
}
@@ -354,7 +354,7 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
list_del(&p_dev->socket_device_list);
- p_dev->_removed=1;
+ p_dev->_removed = 1;
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
dev_dbg(&p_dev->dev, "unregistering device\n");
@@ -364,7 +364,7 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
return;
}
-static int pcmcia_device_remove(struct device * dev)
+static int pcmcia_device_remove(struct device *dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
@@ -391,7 +391,7 @@ static int pcmcia_device_remove(struct device * dev)
return 0;
if (p_drv->remove)
- p_drv->remove(p_dev);
+ p_drv->remove(p_dev);
p_dev->dev_node = NULL;
@@ -499,7 +499,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
*/
static DEFINE_MUTEX(device_add_lock);
-struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
+struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
{
struct pcmcia_device *p_dev, *tmp_dev;
unsigned long flags;
@@ -545,8 +545,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
* Note that this is serialized by the device_add_lock, so that
* only one such struct will be created.
*/
- list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
- if (p_dev->func == tmp_dev->func) {
+ list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
+ if (p_dev->func == tmp_dev->func) {
p_dev->function_config = tmp_dev->function_config;
p_dev->io = tmp_dev->io;
p_dev->irq = tmp_dev->irq;
@@ -627,10 +627,10 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
no_funcs = 1;
s->functions = no_funcs;
- for (i=0; i < no_funcs; i++)
+ for (i = 0; i < no_funcs; i++)
pcmcia_device_add(s, i);
- return (ret);
+ return ret;
}
@@ -756,7 +756,7 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
release:
release_firmware(fw);
- return (ret);
+ return ret;
}
#else /* !CONFIG_PCMCIA_LOAD_CIS */
@@ -852,7 +852,7 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) {
int i;
- for (i=0; i<4; i++)
+ for (i = 0; i < 4; i++)
if (dev->prod_id[i])
return 0;
if (dev->has_manf_id || dev->has_card_id || dev->has_func_id)
@@ -865,9 +865,10 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
}
-static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
- struct pcmcia_device * p_dev = to_pcmcia_dev(dev);
- struct pcmcia_driver * p_drv = to_pcmcia_drv(drv);
+static int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+ struct pcmcia_driver *p_drv = to_pcmcia_drv(drv);
struct pcmcia_device_id *did = p_drv->id_table;
struct pcmcia_dynid *dynid;
@@ -917,7 +918,7 @@ static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
p_dev = to_pcmcia_dev(dev);
/* calculate hashes */
- for (i=0; i<4; i++) {
+ for (i = 0; i < 4; i++) {
if (!p_dev->prod_id[i])
continue;
hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i]));
@@ -984,14 +985,14 @@ static void runtime_resume(struct device *dev)
static ssize_t field##_show (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct pcmcia_device *p_dev = to_pcmcia_dev(dev); \
- return p_dev->test ? sprintf (buf, format, p_dev->field) : -ENODEV; \
+ return p_dev->test ? sprintf(buf, format, p_dev->field) : -ENODEV; \
}
#define pcmcia_device_stringattr(name, field) \
static ssize_t name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct pcmcia_device *p_dev = to_pcmcia_dev(dev); \
- return p_dev->field ? sprintf (buf, "%s\n", p_dev->field) : -ENODEV; \
+ return p_dev->field ? sprintf(buf, "%s\n", p_dev->field) : -ENODEV; \
}
pcmcia_device_attr(func, socket, "0x%02x\n");
@@ -1020,8 +1021,8 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
int ret = 0;
- if (!count)
- return -EINVAL;
+ if (!count)
+ return -EINVAL;
if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
ret = runtime_suspend(dev);
@@ -1039,10 +1040,11 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
u32 hash[4] = { 0, 0, 0, 0};
/* calculate hashes */
- for (i=0; i<4; i++) {
+ for (i = 0; i < 4; i++) {
if (!p_dev->prod_id[i])
continue;
- hash[i] = crc32(0,p_dev->prod_id[i],strlen(p_dev->prod_id[i]));
+ hash[i] = crc32(0, p_dev->prod_id[i],
+ strlen(p_dev->prod_id[i]));
}
return sprintf(buf, "pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
"pa%08Xpb%08Xpc%08Xpd%08X\n",
@@ -1091,7 +1093,7 @@ static struct device_attribute pcmcia_dev_attrs[] = {
/* PM support, also needed for reset */
-static int pcmcia_dev_suspend(struct device * dev, pm_message_t state)
+static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
struct pcmcia_driver *p_drv = NULL;
@@ -1131,10 +1133,10 @@ static int pcmcia_dev_suspend(struct device * dev, pm_message_t state)
}
-static int pcmcia_dev_resume(struct device * dev)
+static int pcmcia_dev_resume(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- struct pcmcia_driver *p_drv = NULL;
+ struct pcmcia_driver *p_drv = NULL;
int ret = 0;
if (!p_dev->suspended)
@@ -1211,7 +1213,7 @@ static int pcmcia_bus_suspend(struct pcmcia_socket *skt)
/*======================================================================
The card status event handler.
-
+
======================================================================*/
/* Normally, the event is passed to individual drivers after
@@ -1264,7 +1266,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
} /* ds_event */
-struct pcmcia_device * pcmcia_dev_present(struct pcmcia_device *_p_dev)
+struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_device *ret = NULL;
@@ -1329,7 +1331,7 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev,
if (ret) {
dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
pcmcia_put_socket(socket);
- return (ret);
+ return ret;
}
return 0;
@@ -1400,7 +1402,7 @@ static int __init init_pcmcia_bus(void)
return 0;
}
-fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
+fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
* pcmcia_socket_class is already registered */
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c
index c4d7908fa37f..f73fd5beaa37 100644
--- a/drivers/pcmcia/pcmcia_ioctl.c
+++ b/drivers/pcmcia/pcmcia_ioctl.c
@@ -88,12 +88,12 @@ static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info)
p_drv = container_of(drv, struct pcmcia_driver, drv);
- return (p_drv);
+ return p_drv;
}
#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry *proc_pccard = NULL;
+static struct proc_dir_entry *proc_pccard;
static int proc_read_drivers_callback(struct device_driver *driver, void *_m)
{
@@ -158,7 +158,8 @@ static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
#else
-static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
+static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
+{
return 0;
}
@@ -195,7 +196,7 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
begin = adj->resource.memory.Base;
end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
if (s->resource_ops->add_mem)
- ret =s->resource_ops->add_mem(s, adj->Action, begin, end);
+ ret = s->resource_ops->add_mem(s, adj->Action, begin, end);
case RES_IO_RANGE:
begin = adj->resource.io.BasePort;
end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
@@ -215,7 +216,7 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
}
up_read(&pcmcia_socket_list_rwsem);
- return (ret);
+ return ret;
}
@@ -490,7 +491,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
}
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
+ list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == bind_info->function) {
if ((p_dev->dev.driver == &p_drv->drv)) {
if (p_dev->cardmgr) {
@@ -558,7 +559,7 @@ rescan:
err_put:
pcmcia_put_socket(s);
- return (ret);
+ return ret;
} /* bind_request */
#ifdef CONFIG_CARDBUS
@@ -655,7 +656,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
err_put:
pcmcia_put_dev(p_dev);
- return (ret);
+ return ret;
} /* get_device_info */
@@ -664,7 +665,7 @@ static int ds_open(struct inode *inode, struct file *file)
socket_t i = iminor(inode);
struct pcmcia_socket *s;
user_info_t *user;
- static int warning_printed = 0;
+ static int warning_printed;
int ret = 0;
pr_debug("ds_open(socket %d)\n", i);
@@ -738,12 +739,13 @@ static int ds_release(struct inode *inode, struct file *file)
s = user->socket;
/* Unlink user data structure */
- if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY)
s->pcmcia_state.busy = 0;
- }
+
file->private_data = NULL;
for (link = &s->user; *link; link = &(*link)->next)
- if (*link == user) break;
+ if (*link == user)
+ break;
if (link == NULL)
goto out;
*link = user->next;
@@ -774,7 +776,7 @@ static ssize_t ds_read(struct file *file, char __user *buf,
s = user->socket;
if (s->pcmcia_state.dead)
- return -EIO;
+ return -EIO;
ret = wait_event_interruptible(s->queue, !queue_empty(user));
if (ret == 0)
@@ -824,7 +826,7 @@ static u_int ds_poll(struct file *file, poll_table *wait)
/*====================================================================*/
-static int ds_ioctl(struct inode * inode, struct file * file,
+static int ds_ioctl(struct inode *inode, struct file *file,
u_int cmd, u_long arg)
{
struct pcmcia_socket *s;
@@ -842,10 +844,11 @@ static int ds_ioctl(struct inode * inode, struct file * file,
s = user->socket;
if (s->pcmcia_state.dead)
- return -EIO;
+ return -EIO;
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
- if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
+ if (size > sizeof(ds_ioctl_arg_t))
+ return -EINVAL;
/* Permission check */
if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
@@ -1024,8 +1027,8 @@ static int ds_ioctl(struct inode * inode, struct file * file,
}
if (cmd & IOC_OUT) {
- if (__copy_to_user(uarg, (char *)buf, size))
- err = -EFAULT;
+ if (__copy_to_user(uarg, (char *)buf, size))
+ err = -EFAULT;
}
free_out:
@@ -1045,7 +1048,8 @@ static const struct file_operations ds_fops = {
.poll = ds_poll,
};
-void __init pcmcia_setup_ioctl(void) {
+void __init pcmcia_setup_ioctl(void)
+{
int i;
/* Set up character device for user mode clients */
@@ -1064,7 +1068,8 @@ void __init pcmcia_setup_ioctl(void) {
}
-void __exit pcmcia_cleanup_ioctl(void) {
+void __exit pcmcia_cleanup_ioctl(void)
+{
#ifdef CONFIG_PROC_FS
if (proc_pccard) {
remove_proc_entry("drivers", proc_pccard);
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index a8bf8c1b45ed..d5db95644b64 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -33,7 +33,7 @@
/* Access speed for IO windows */
-static int io_speed = 0;
+static int io_speed;
module_param(io_speed, int, 0444);
@@ -62,7 +62,8 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr,
num, align);
align = 0;
} else
- while (align && (align < num)) align <<= 1;
+ while (align && (align < num))
+ align <<= 1;
}
if (*base & ~(align-1)) {
dev_dbg(&s->dev, "odd IO request: base %#x align %#x\n",
@@ -338,7 +339,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
struct pcmcia_socket *s = p_dev->socket;
config_t *c = p_dev->function_config;
- if (!p_dev->_io )
+ if (!p_dev->_io)
return -EINVAL;
p_dev->_io = 0;
@@ -362,7 +363,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
{
struct pcmcia_socket *s = p_dev->socket;
- config_t *c= p_dev->function_config;
+ config_t *c = p_dev->function_config;
if (!p_dev->_irq)
return -EINVAL;
@@ -383,9 +384,8 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
s->irq.AssignedIRQ = 0;
}
- if (req->Handler) {
+ if (req->Handler)
free_irq(req->AssignedIRQ, p_dev->priv);
- }
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[req->AssignedIRQ]--;
@@ -656,7 +656,8 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
type = IRQF_SHARED;
else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)
type = IRQF_SHARED;
- else printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n");
+ else
+ printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n");
#ifdef CONFIG_PCMCIA_PROBE
@@ -788,7 +789,8 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
/* Allocate system memory window */
for (w = 0; w < MAX_WIN; w++)
- if (!(s->state & SOCKET_WIN_REQ(w))) break;
+ if (!(s->state & SOCKET_WIN_REQ(w)))
+ break;
if (w == MAX_WIN) {
dev_dbg(&s->dev, "all windows are used already\n");
return -EINVAL;
@@ -826,18 +828,19 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
s->state |= SOCKET_WIN_REQ(w);
/* Return window handle */
- if (s->features & SS_CAP_STATIC_MAP) {
+ if (s->features & SS_CAP_STATIC_MAP)
req->Base = win->static_start;
- } else {
+ else
req->Base = win->res->start;
- }
+
*wh = w + 1;
return 0;
} /* pcmcia_request_window */
EXPORT_SYMBOL(pcmcia_request_window);
-void pcmcia_disable_device(struct pcmcia_device *p_dev) {
+void pcmcia_disable_device(struct pcmcia_device *p_dev)
+{
pcmcia_release_configuration(p_dev);
pcmcia_release_io(p_dev, &p_dev->io);
pcmcia_release_irq(p_dev, &p_dev->irq);
@@ -970,7 +973,7 @@ int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
&loop, pcmcia_do_loop_tuple);
-};
+}
EXPORT_SYMBOL(pcmcia_loop_tuple);
@@ -1000,7 +1003,7 @@ static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple,
} else
dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n");
return 0;
-};
+}
/**
* pcmcia_get_tuple() - get first tuple from CIS
@@ -1024,7 +1027,7 @@ size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code,
pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get);
return get.len;
-};
+}
EXPORT_SYMBOL(pcmcia_get_tuple);
@@ -1057,7 +1060,7 @@ static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple,
for (i = 0; i < 6; i++)
dev->dev_addr[i] = tuple->TupleData[i+2];
return 0;
-};
+}
/**
* pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE
@@ -1071,6 +1074,6 @@ static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple,
int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev)
{
return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev);
-};
+}
EXPORT_SYMBOL(pcmcia_get_mac_from_cis);
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 84dde7768ad5..3aabf1e37988 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -214,7 +214,8 @@ static void pxa2xx_configure_sockets(struct device *dev)
MECR |= MECR_CIT;
/* Set MECR:NOS (Number Of Sockets) */
- if ((ops->first + ops->nr) > 1 || machine_is_viper())
+ if ((ops->first + ops->nr) > 1 ||
+ machine_is_viper() || machine_is_arcom_zeus())
MECR |= MECR_NOS;
else
MECR &= ~MECR_NOS;
@@ -252,6 +253,7 @@ int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
return soc_pcmcia_add_one(skt);
}
+EXPORT_SYMBOL(pxa2xx_drv_pcmcia_add_one);
void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
{
@@ -261,19 +263,19 @@ void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
ops->frequency_change = pxa2xx_pcmcia_frequency_change;
#endif
}
+EXPORT_SYMBOL(pxa2xx_drv_pcmcia_ops);
-int __pxa2xx_drv_pcmcia_probe(struct device *dev)
+static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
{
int i, ret = 0;
struct pcmcia_low_level *ops;
struct skt_dev_info *sinfo;
struct soc_pcmcia_socket *skt;
- if (!dev || !dev->platform_data)
+ ops = (struct pcmcia_low_level *)dev->dev.platform_data;
+ if (!ops)
return -ENODEV;
- ops = (struct pcmcia_low_level *)dev->platform_data;
-
pxa2xx_drv_pcmcia_ops(ops);
sinfo = kzalloc(SKT_DEV_INFO_SIZE(ops->nr), GFP_KERNEL);
@@ -308,13 +310,6 @@ int __pxa2xx_drv_pcmcia_probe(struct device *dev)
return ret;
}
-EXPORT_SYMBOL(__pxa2xx_drv_pcmcia_probe);
-
-
-static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
-{
- return __pxa2xx_drv_pcmcia_probe(&dev->dev);
-}
static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev)
{
@@ -341,7 +336,7 @@ static int pxa2xx_drv_pcmcia_resume(struct device *dev)
return pcmcia_socket_dev_resume(dev);
}
-static struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = {
+static const struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = {
.suspend = pxa2xx_drv_pcmcia_suspend,
.resume = pxa2xx_drv_pcmcia_resume,
};
diff --git a/drivers/pcmcia/pxa2xx_base.h b/drivers/pcmcia/pxa2xx_base.h
index cb5efaec886f..bb62ea87b8f9 100644
--- a/drivers/pcmcia/pxa2xx_base.h
+++ b/drivers/pcmcia/pxa2xx_base.h
@@ -1,6 +1,3 @@
-/* temporary measure */
-extern int __pxa2xx_drv_pcmcia_probe(struct device *);
-
int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt);
void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops);
diff --git a/drivers/pcmcia/pxa2xx_palmtc.c b/drivers/pcmcia/pxa2xx_palmtc.c
index 3a8993ed5621..459a232d66be 100644
--- a/drivers/pcmcia/pxa2xx_palmtc.c
+++ b/drivers/pcmcia/pxa2xx_palmtc.c
@@ -67,7 +67,7 @@ static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
if (ret)
goto err7;
- skt->irq = IRQ_GPIO(GPIO_NR_PALMTC_PCMCIA_READY);
+ skt->socket.pci_irq = IRQ_GPIO(GPIO_NR_PALMTC_PCMCIA_READY);
return 0;
err7:
diff --git a/drivers/pcmcia/pxa2xx_stargate2.c b/drivers/pcmcia/pxa2xx_stargate2.c
index 490749ea677f..d08802fe35f9 100644
--- a/drivers/pcmcia/pxa2xx_stargate2.c
+++ b/drivers/pcmcia/pxa2xx_stargate2.c
@@ -40,7 +40,7 @@ static struct pcmcia_irqs irqs[] = {
static int sg2_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->irq = IRQ_GPIO(SG2_S0_GPIO_READY);
+ skt->socket.pci_irq = IRQ_GPIO(SG2_S0_GPIO_READY);
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c
index 27be2e154df2..a51f2077644a 100644
--- a/drivers/pcmcia/pxa2xx_viper.c
+++ b/drivers/pcmcia/pxa2xx_viper.c
@@ -1,9 +1,8 @@
/*
- * VIPER PCMCIA support
+ * Viper/Zeus PCMCIA support
* Copyright 2004 Arcom Control Systems
*
* Maintained by Marc Zyngier <maz@misterjones.org>
- * <marc.zyngier@altran.com>
*
* Based on:
* iPAQ h2200 PCMCIA support
@@ -26,37 +25,47 @@
#include <asm/irq.h>
-#include <mach/viper.h>
-#include <asm/mach-types.h>
+#include <mach/arcom-pcmcia.h>
#include "soc_common.h"
#include "pxa2xx_base.h"
+static struct platform_device *arcom_pcmcia_dev;
+
static struct pcmcia_irqs irqs[] = {
- { 0, gpio_to_irq(VIPER_CF_CD_GPIO), "PCMCIA_CD" }
+ {
+ .sock = 0,
+ .str = "PCMCIA_CD",
+ },
};
+static inline struct arcom_pcmcia_pdata *viper_get_pdata(void)
+{
+ return arcom_pcmcia_dev->dev.platform_data;
+}
+
static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
+ struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
unsigned long flags;
- skt->socket.pci_irq = gpio_to_irq(VIPER_CF_RDY_GPIO);
+ skt->socket.pci_irq = gpio_to_irq(pdata->rdy_gpio);
+ irqs[0].irq = gpio_to_irq(pdata->cd_gpio);
- if (gpio_request(VIPER_CF_CD_GPIO, "CF detect"))
+ if (gpio_request(pdata->cd_gpio, "CF detect"))
goto err_request_cd;
- if (gpio_request(VIPER_CF_RDY_GPIO, "CF ready"))
+ if (gpio_request(pdata->rdy_gpio, "CF ready"))
goto err_request_rdy;
- if (gpio_request(VIPER_CF_POWER_GPIO, "CF power"))
+ if (gpio_request(pdata->pwr_gpio, "CF power"))
goto err_request_pwr;
local_irq_save(flags);
- /* GPIO 82 is the CF power enable line. initially off */
- if (gpio_direction_output(VIPER_CF_POWER_GPIO, 0) ||
- gpio_direction_input(VIPER_CF_CD_GPIO) ||
- gpio_direction_input(VIPER_CF_RDY_GPIO)) {
+ if (gpio_direction_output(pdata->pwr_gpio, 0) ||
+ gpio_direction_input(pdata->cd_gpio) ||
+ gpio_direction_input(pdata->rdy_gpio)) {
local_irq_restore(flags);
goto err_dir;
}
@@ -66,13 +75,13 @@ static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
err_dir:
- gpio_free(VIPER_CF_POWER_GPIO);
+ gpio_free(pdata->pwr_gpio);
err_request_pwr:
- gpio_free(VIPER_CF_RDY_GPIO);
+ gpio_free(pdata->rdy_gpio);
err_request_rdy:
- gpio_free(VIPER_CF_CD_GPIO);
+ gpio_free(pdata->cd_gpio);
err_request_cd:
- printk(KERN_ERR "viper: Failed to setup PCMCIA GPIOs\n");
+ dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n");
return -1;
}
@@ -81,17 +90,21 @@ err_request_cd:
*/
static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
+ struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
+
soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
- gpio_free(VIPER_CF_POWER_GPIO);
- gpio_free(VIPER_CF_RDY_GPIO);
- gpio_free(VIPER_CF_CD_GPIO);
+ gpio_free(pdata->pwr_gpio);
+ gpio_free(pdata->rdy_gpio);
+ gpio_free(pdata->cd_gpio);
}
static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- state->detect = gpio_get_value(VIPER_CF_CD_GPIO) ? 0 : 1;
- state->ready = gpio_get_value(VIPER_CF_RDY_GPIO) ? 1 : 0;
+ struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
+
+ state->detect = !gpio_get_value(pdata->cd_gpio);
+ state->ready = !!gpio_get_value(pdata->rdy_gpio);
state->bvd1 = 1;
state->bvd2 = 1;
state->wrprot = 0;
@@ -102,20 +115,21 @@ static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
+ struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
+
/* Silently ignore Vpp, output enable, speaker enable. */
- viper_cf_rst(state->flags & SS_RESET);
+ pdata->reset(state->flags & SS_RESET);
/* Apply socket voltage */
switch (state->Vcc) {
case 0:
- gpio_set_value(VIPER_CF_POWER_GPIO, 0);
+ gpio_set_value(pdata->pwr_gpio, 0);
break;
case 33:
- gpio_set_value(VIPER_CF_POWER_GPIO, 1);
+ gpio_set_value(pdata->pwr_gpio, 1);
break;
default:
- printk(KERN_ERR "%s: Unsupported Vcc:%d\n",
- __func__, state->Vcc);
+ dev_err(&arcom_pcmcia_dev->dev, "Unsupported Vcc:%d\n", state->Vcc);
return -1;
}
@@ -130,7 +144,7 @@ static void viper_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
}
-static struct pcmcia_low_level viper_pcmcia_ops __initdata = {
+static struct pcmcia_low_level viper_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = viper_pcmcia_hw_init,
.hw_shutdown = viper_pcmcia_hw_shutdown,
@@ -143,17 +157,25 @@ static struct pcmcia_low_level viper_pcmcia_ops __initdata = {
static struct platform_device *viper_pcmcia_device;
-static int __init viper_pcmcia_init(void)
+static int viper_pcmcia_probe(struct platform_device *pdev)
{
int ret;
- if (!machine_is_viper())
- return -ENODEV;
+ /* I can't imagine more than one device, but you never know... */
+ if (arcom_pcmcia_dev)
+ return -EEXIST;
+
+ if (!pdev->dev.platform_data)
+ return -EINVAL;
viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!viper_pcmcia_device)
return -ENOMEM;
+ arcom_pcmcia_dev = pdev;
+
+ viper_pcmcia_device->dev.parent = &pdev->dev;
+
ret = platform_device_add_data(viper_pcmcia_device,
&viper_pcmcia_ops,
sizeof(viper_pcmcia_ops));
@@ -161,18 +183,49 @@ static int __init viper_pcmcia_init(void)
if (!ret)
ret = platform_device_add(viper_pcmcia_device);
- if (ret)
+ if (ret) {
platform_device_put(viper_pcmcia_device);
+ arcom_pcmcia_dev = NULL;
+ }
return ret;
}
-static void __exit viper_pcmcia_exit(void)
+static int viper_pcmcia_remove(struct platform_device *pdev)
{
platform_device_unregister(viper_pcmcia_device);
+ arcom_pcmcia_dev = NULL;
+ return 0;
+}
+
+static struct platform_device_id viper_pcmcia_id_table[] = {
+ { .name = "viper-pcmcia", },
+ { .name = "zeus-pcmcia", },
+ { },
+};
+
+static struct platform_driver viper_pcmcia_driver = {
+ .probe = viper_pcmcia_probe,
+ .remove = viper_pcmcia_remove,
+ .driver = {
+ .name = "arcom-pcmcia",
+ .owner = THIS_MODULE,
+ },
+ .id_table = viper_pcmcia_id_table,
+};
+
+static int __init viper_pcmcia_init(void)
+{
+ return platform_driver_register(&viper_pcmcia_driver);
+}
+
+static void __exit viper_pcmcia_exit(void)
+{
+ return platform_driver_unregister(&viper_pcmcia_driver);
}
module_init(viper_pcmcia_init);
module_exit(viper_pcmcia_exit);
+MODULE_DEVICE_TABLE(platform, viper_pcmcia_id_table);
MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
index de0e770ce6a3..52db17263d8b 100644
--- a/drivers/pcmcia/rsrc_mgr.c
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -126,16 +126,16 @@ static void pcmcia_align(void *align_data, struct resource *res,
res->start = start;
#ifdef CONFIG_X86
- if (res->flags & IORESOURCE_IO) {
- if (start & 0x300) {
- start = (start + 0x3ff) & ~0x3ff;
- res->start = start;
- }
- }
+ if (res->flags & IORESOURCE_IO) {
+ if (start & 0x300) {
+ start = (start + 0x3ff) & ~0x3ff;
+ res->start = start;
+ }
+ }
#endif
#ifdef CONFIG_M68K
- if (res->flags & IORESOURCE_IO) {
+ if (res->flags & IORESOURCE_IO) {
if ((res->start + size - 1) >= 1024)
res->start = res->end;
}
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 7039f3cf5b77..9b0dc433a8c3 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -24,9 +24,9 @@
#include <linux/timer.h>
#include <linux/pci.h>
#include <linux/device.h>
+#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/io.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
@@ -144,43 +144,44 @@ static int add_interval(struct resource_map *map, u_long base, u_long num)
static int sub_interval(struct resource_map *map, u_long base, u_long num)
{
- struct resource_map *p, *q;
-
- for (p = map; ; p = q) {
- q = p->next;
- if (q == map)
- break;
- if ((q->base+q->num > base) && (base+num > q->base)) {
- if (q->base >= base) {
- if (q->base+q->num <= base+num) {
- /* Delete whole block */
- p->next = q->next;
- kfree(q);
- /* don't advance the pointer yet */
- q = p;
- } else {
- /* Cut off bit from the front */
- q->num = q->base + q->num - base - num;
- q->base = base + num;
- }
- } else if (q->base+q->num <= base+num) {
- /* Cut off bit from the end */
- q->num = base - q->base;
- } else {
- /* Split the block into two pieces */
- p = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
- if (!p) {
- printk(KERN_WARNING "out of memory to update resources\n");
- return -ENOMEM;
+ struct resource_map *p, *q;
+
+ for (p = map; ; p = q) {
+ q = p->next;
+ if (q == map)
+ break;
+ if ((q->base+q->num > base) && (base+num > q->base)) {
+ if (q->base >= base) {
+ if (q->base+q->num <= base+num) {
+ /* Delete whole block */
+ p->next = q->next;
+ kfree(q);
+ /* don't advance the pointer yet */
+ q = p;
+ } else {
+ /* Cut off bit from the front */
+ q->num = q->base + q->num - base - num;
+ q->base = base + num;
+ }
+ } else if (q->base+q->num <= base+num) {
+ /* Cut off bit from the end */
+ q->num = base - q->base;
+ } else {
+ /* Split the block into two pieces */
+ p = kmalloc(sizeof(struct resource_map),
+ GFP_KERNEL);
+ if (!p) {
+ printk(KERN_WARNING "out of memory to update resources\n");
+ return -ENOMEM;
+ }
+ p->base = base+num;
+ p->num = q->base+q->num - p->base;
+ q->num = base - q->base;
+ p->next = q->next ; q->next = p;
+ }
}
- p->base = base+num;
- p->num = q->base+q->num - p->base;
- q->num = base - q->base;
- p->next = q->next ; q->next = p;
- }
}
- }
- return 0;
+ return 0;
}
/*======================================================================
@@ -194,69 +195,72 @@ static int sub_interval(struct resource_map *map, u_long base, u_long num)
static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
unsigned int num)
{
- struct resource *res;
- struct socket_data *s_data = s->resource_data;
- unsigned int i, j, bad;
- int any;
- u_char *b, hole, most;
-
- dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
- base, base+num-1);
-
- /* First, what does a floating port look like? */
- b = kzalloc(256, GFP_KERNEL);
- if (!b) {
- printk("\n");
- dev_printk(KERN_ERR, &s->dev,
- "do_io_probe: unable to kmalloc 256 bytes");
- return;
- }
- for (i = base, most = 0; i < base+num; i += 8) {
- res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
- if (!res)
- continue;
- hole = inb(i);
- for (j = 1; j < 8; j++)
- if (inb(i+j) != hole) break;
- free_region(res);
- if ((j == 8) && (++b[hole] > b[most]))
- most = hole;
- if (b[most] == 127) break;
- }
- kfree(b);
-
- bad = any = 0;
- for (i = base; i < base+num; i += 8) {
- res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
- if (!res)
- continue;
- for (j = 0; j < 8; j++)
- if (inb(i+j) != most) break;
- free_region(res);
- if (j < 8) {
- if (!any)
- printk(" excluding");
- if (!bad)
- bad = any = i;
- } else {
- if (bad) {
- sub_interval(&s_data->io_db, bad, i-bad);
- printk(" %#x-%#x", bad, i-1);
- bad = 0;
- }
+ struct resource *res;
+ struct socket_data *s_data = s->resource_data;
+ unsigned int i, j, bad;
+ int any;
+ u_char *b, hole, most;
+
+ dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
+ base, base+num-1);
+
+ /* First, what does a floating port look like? */
+ b = kzalloc(256, GFP_KERNEL);
+ if (!b) {
+ printk("\n");
+ dev_printk(KERN_ERR, &s->dev,
+ "do_io_probe: unable to kmalloc 256 bytes");
+ return;
}
- }
- if (bad) {
- if ((num > 16) && (bad == base) && (i == base+num)) {
- printk(" nothing: probe failed.\n");
- return;
- } else {
- sub_interval(&s_data->io_db, bad, i-bad);
- printk(" %#x-%#x", bad, i-1);
+ for (i = base, most = 0; i < base+num; i += 8) {
+ res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
+ if (!res)
+ continue;
+ hole = inb(i);
+ for (j = 1; j < 8; j++)
+ if (inb(i+j) != hole)
+ break;
+ free_region(res);
+ if ((j == 8) && (++b[hole] > b[most]))
+ most = hole;
+ if (b[most] == 127)
+ break;
}
- }
+ kfree(b);
- printk(any ? "\n" : " clean.\n");
+ bad = any = 0;
+ for (i = base; i < base+num; i += 8) {
+ res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
+ if (!res)
+ continue;
+ for (j = 0; j < 8; j++)
+ if (inb(i+j) != most)
+ break;
+ free_region(res);
+ if (j < 8) {
+ if (!any)
+ printk(" excluding");
+ if (!bad)
+ bad = any = i;
+ } else {
+ if (bad) {
+ sub_interval(&s_data->io_db, bad, i-bad);
+ printk(" %#x-%#x", bad, i-1);
+ bad = 0;
+ }
+ }
+ }
+ if (bad) {
+ if ((num > 16) && (bad == base) && (i == base+num)) {
+ printk(" nothing: probe failed.\n");
+ return;
+ } else {
+ sub_interval(&s_data->io_db, bad, i-bad);
+ printk(" %#x-%#x", bad, i-1);
+ }
+ }
+
+ printk(any ? "\n" : " clean.\n");
}
#endif
@@ -327,8 +331,9 @@ cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
unsigned int info1, info2;
int ret = 0;
- res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
- res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
+ res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
+ res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
+ "PCMCIA memprobe");
if (res1 && res2) {
ret = readable(s, res1, &info1);
@@ -347,8 +352,9 @@ checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
struct resource *res1, *res2;
int a = -1, b = -1;
- res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
- res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
+ res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
+ res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
+ "PCMCIA memprobe");
if (res1 && res2) {
a = checksum(s, res1);
@@ -371,42 +377,43 @@ checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
{
- struct socket_data *s_data = s->resource_data;
- u_long i, j, bad, fail, step;
-
- dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
- base, base+num-1);
- bad = fail = 0;
- step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
- /* don't allow too large steps */
- if (step > 0x800000)
- step = 0x800000;
- /* cis_readable wants to map 2x map_size */
- if (step < 2 * s->map_size)
- step = 2 * s->map_size;
- for (i = j = base; i < base+num; i = j + step) {
- if (!fail) {
- for (j = i; j < base+num; j += step) {
- if (cis_readable(s, j, step))
- break;
- }
- fail = ((i == base) && (j == base+num));
- }
- if (fail) {
- for (j = i; j < base+num; j += 2*step)
- if (checksum_match(s, j, step) &&
- checksum_match(s, j + step, step))
- break;
- }
- if (i != j) {
- if (!bad) printk(" excluding");
- printk(" %#05lx-%#05lx", i, j-1);
- sub_interval(&s_data->mem_db, i, j-i);
- bad += j-i;
+ struct socket_data *s_data = s->resource_data;
+ u_long i, j, bad, fail, step;
+
+ dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
+ base, base+num-1);
+ bad = fail = 0;
+ step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
+ /* don't allow too large steps */
+ if (step > 0x800000)
+ step = 0x800000;
+ /* cis_readable wants to map 2x map_size */
+ if (step < 2 * s->map_size)
+ step = 2 * s->map_size;
+ for (i = j = base; i < base+num; i = j + step) {
+ if (!fail) {
+ for (j = i; j < base+num; j += step) {
+ if (cis_readable(s, j, step))
+ break;
+ }
+ fail = ((i == base) && (j == base+num));
+ }
+ if (fail) {
+ for (j = i; j < base+num; j += 2*step)
+ if (checksum_match(s, j, step) &&
+ checksum_match(s, j + step, step))
+ break;
+ }
+ if (i != j) {
+ if (!bad)
+ printk(" excluding");
+ printk(" %#05lx-%#05lx", i, j-1);
+ sub_interval(&s_data->mem_db, i, j-i);
+ bad += j-i;
+ }
}
- }
- printk(bad ? "\n" : " clean.\n");
- return (num - bad);
+ printk(bad ? "\n" : " clean.\n");
+ return num - bad;
}
#ifdef CONFIG_PCMCIA_PROBE
@@ -656,7 +663,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
return res;
}
-static struct resource * nonstatic_find_mem_region(u_long base, u_long num,
+static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
u_long align, int low, struct pcmcia_socket *s)
{
struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev));
@@ -794,7 +801,7 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
return -EINVAL;
#endif
- for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) {
+ for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
res = s->cb_dev->bus->resource[i];
if (!res)
continue;
@@ -908,14 +915,14 @@ static ssize_t show_io_db(struct device *dev,
for (p = data->io_db.next; p != &data->io_db; p = p->next) {
if (ret > (PAGE_SIZE - 10))
continue;
- ret += snprintf (&buf[ret], (PAGE_SIZE - ret - 1),
- "0x%08lx - 0x%08lx\n",
- ((unsigned long) p->base),
- ((unsigned long) p->base + p->num - 1));
+ ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
+ "0x%08lx - 0x%08lx\n",
+ ((unsigned long) p->base),
+ ((unsigned long) p->base + p->num - 1));
}
mutex_unlock(&rsrc_mutex);
- return (ret);
+ return ret;
}
static ssize_t store_io_db(struct device *dev,
@@ -927,12 +934,13 @@ static ssize_t store_io_db(struct device *dev,
unsigned int add = ADD_MANAGED_RESOURCE;
ssize_t ret = 0;
- ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
+ ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
if (ret != 2) {
- ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
+ ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
add = REMOVE_MANAGED_RESOURCE;
if (ret != 2) {
- ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr);
+ ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
+ &end_addr);
add = ADD_MANAGED_RESOURCE;
if (ret != 2)
return -EINVAL;
@@ -963,14 +971,14 @@ static ssize_t show_mem_db(struct device *dev,
for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
if (ret > (PAGE_SIZE - 10))
continue;
- ret += snprintf (&buf[ret], (PAGE_SIZE - ret - 1),
- "0x%08lx - 0x%08lx\n",
- ((unsigned long) p->base),
- ((unsigned long) p->base + p->num - 1));
+ ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
+ "0x%08lx - 0x%08lx\n",
+ ((unsigned long) p->base),
+ ((unsigned long) p->base + p->num - 1));
}
mutex_unlock(&rsrc_mutex);
- return (ret);
+ return ret;
}
static ssize_t store_mem_db(struct device *dev,
@@ -982,12 +990,13 @@ static ssize_t store_mem_db(struct device *dev,
unsigned int add = ADD_MANAGED_RESOURCE;
ssize_t ret = 0;
- ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
+ ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
if (ret != 2) {
- ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
+ ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
add = REMOVE_MANAGED_RESOURCE;
if (ret != 2) {
- ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr);
+ ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
+ &end_addr);
add = ADD_MANAGED_RESOURCE;
if (ret != 2)
return -EINVAL;
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index 78d5aab542f7..7a456000332a 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -164,7 +164,7 @@ static ssize_t pccard_store_irq_mask(struct device *dev,
if (!count)
return -EINVAL;
- ret = sscanf (buf, "0x%x\n", &mask);
+ ret = sscanf(buf, "0x%x\n", &mask);
if (ret == 1) {
s->irq_mask &= mask;
@@ -278,7 +278,7 @@ static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off
free_tuple:
kfree(tuplebuffer);
- return (ret);
+ return ret;
}
static ssize_t pccard_show_cis(struct kobject *kobj,
@@ -308,7 +308,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj,
count = pccard_extract_cis(s, buf, off, count);
}
- return (count);
+ return count;
}
static ssize_t pccard_store_cis(struct kobject *kobj,
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 8be4cc447a17..e4d12acdd525 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -6,7 +6,7 @@
* Changelog:
* Aug 2002: Manfred Spraul <manfred@colorfullife.com>
* Dynamically adjust the size of the bridge resource
- *
+ *
* May 2003: Dominik Brodowski <linux@brodo.de>
* Merge pci_socket.c and yenta.c into one file
*/
@@ -16,13 +16,12 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/io.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
-#include <asm/io.h>
-
#include "yenta_socket.h"
#include "i82365.h"
@@ -55,7 +54,7 @@ static int yenta_probe_cb_irq(struct yenta_socket *socket);
static unsigned int override_bios;
module_param(override_bios, uint, 0000);
-MODULE_PARM_DESC (override_bios, "yenta ignore bios resource allocation");
+MODULE_PARM_DESC(override_bios, "yenta ignore bios resource allocation");
/*
* Generate easy-to-use ways of reading a cardbus sockets
@@ -237,24 +236,42 @@ static void yenta_set_power(struct yenta_socket *socket, socket_state_t *state)
/* i82365SL-DF style */
if (socket->flags & YENTA_16BIT_POWER_DF) {
switch (state->Vcc) {
- case 33: reg |= I365_VCC_3V; break;
- case 50: reg |= I365_VCC_5V; break;
- default: reg = 0; break;
+ case 33:
+ reg |= I365_VCC_3V;
+ break;
+ case 50:
+ reg |= I365_VCC_5V;
+ break;
+ default:
+ reg = 0;
+ break;
}
switch (state->Vpp) {
case 33:
- case 50: reg |= I365_VPP1_5V; break;
- case 120: reg |= I365_VPP1_12V; break;
+ case 50:
+ reg |= I365_VPP1_5V;
+ break;
+ case 120:
+ reg |= I365_VPP1_12V;
+ break;
}
} else {
/* i82365SL-B style */
switch (state->Vcc) {
- case 50: reg |= I365_VCC_5V; break;
- default: reg = 0; break;
+ case 50:
+ reg |= I365_VCC_5V;
+ break;
+ default:
+ reg = 0;
+ break;
}
switch (state->Vpp) {
- case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break;
- case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break;
+ case 50:
+ reg |= I365_VPP1_5V | I365_VPP2_5V;
+ break;
+ case 120:
+ reg |= I365_VPP1_12V | I365_VPP2_12V;
+ break;
}
}
@@ -263,14 +280,26 @@ static void yenta_set_power(struct yenta_socket *socket, socket_state_t *state)
} else {
u32 reg = 0; /* CB_SC_STPCLK? */
switch (state->Vcc) {
- case 33: reg = CB_SC_VCC_3V; break;
- case 50: reg = CB_SC_VCC_5V; break;
- default: reg = 0; break;
+ case 33:
+ reg = CB_SC_VCC_3V;
+ break;
+ case 50:
+ reg = CB_SC_VCC_5V;
+ break;
+ default:
+ reg = 0;
+ break;
}
switch (state->Vpp) {
- case 33: reg |= CB_SC_VPP_3V; break;
- case 50: reg |= CB_SC_VPP_5V; break;
- case 120: reg |= CB_SC_VPP_12V; break;
+ case 33:
+ reg |= CB_SC_VPP_3V;
+ break;
+ case 50:
+ reg |= CB_SC_VPP_5V;
+ break;
+ case 120:
+ reg |= CB_SC_VPP_12V;
+ break;
}
if (reg != cb_readl(socket, CB_SOCKET_CONTROL))
cb_writel(socket, CB_SOCKET_CONTROL, reg);
@@ -314,23 +343,29 @@ static int yenta_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
reg = exca_readb(socket, I365_POWER) & (I365_VCC_MASK|I365_VPP1_MASK);
reg |= I365_PWR_NORESET;
- if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
- if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
+ if (state->flags & SS_PWR_AUTO)
+ reg |= I365_PWR_AUTO;
+ if (state->flags & SS_OUTPUT_ENA)
+ reg |= I365_PWR_OUT;
if (exca_readb(socket, I365_POWER) != reg)
exca_writeb(socket, I365_POWER, reg);
/* CSC interrupt: no ISA irq for CSC */
reg = I365_CSC_DETECT;
if (state->flags & SS_IOCARD) {
- if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
+ if (state->csc_mask & SS_STSCHG)
+ reg |= I365_CSC_STSCHG;
} else {
- if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
- if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
- if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
+ if (state->csc_mask & SS_BATDEAD)
+ reg |= I365_CSC_BVD1;
+ if (state->csc_mask & SS_BATWARN)
+ reg |= I365_CSC_BVD2;
+ if (state->csc_mask & SS_READY)
+ reg |= I365_CSC_READY;
}
exca_writeb(socket, I365_CSCINT, reg);
exca_readb(socket, I365_CSC);
- if(sock->zoom_video)
+ if (sock->zoom_video)
sock->zoom_video(sock, state->flags & SS_ZVCARD);
}
config_writew(socket, CB_BRIDGE_CONTROL, bridge);
@@ -368,9 +403,12 @@ static int yenta_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io
exca_writew(socket, I365_IO(map)+I365_W_STOP, io->stop);
ioctl = exca_readb(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map);
- if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
- if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
- if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+ if (io->flags & MAP_0WS)
+ ioctl |= I365_IOCTL_0WS(map);
+ if (io->flags & MAP_16BIT)
+ ioctl |= I365_IOCTL_16BIT(map);
+ if (io->flags & MAP_AUTOSZ)
+ ioctl |= I365_IOCTL_IOCS16(map);
exca_writeb(socket, I365_IOCTL, ioctl);
if (io->flags & MAP_ACTIVE)
@@ -416,10 +454,17 @@ static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *
word = (stop >> 12) & 0x0fff;
switch (to_cycles(mem->speed)) {
- case 0: break;
- case 1: word |= I365_MEM_WS0; break;
- case 2: word |= I365_MEM_WS1; break;
- default: word |= I365_MEM_WS1 | I365_MEM_WS0; break;
+ case 0:
+ break;
+ case 1:
+ word |= I365_MEM_WS0;
+ break;
+ case 2:
+ word |= I365_MEM_WS1;
+ break;
+ default:
+ word |= I365_MEM_WS1 | I365_MEM_WS0;
+ break;
}
exca_writew(socket, I365_MEM(map) + I365_W_STOP, word);
@@ -547,9 +592,9 @@ static int yenta_sock_suspend(struct pcmcia_socket *sock)
* max 4 MB, min 16 kB. We try very hard to not get below
* the "ACC" values, though.
*/
-#define BRIDGE_MEM_MAX 4*1024*1024
-#define BRIDGE_MEM_ACC 128*1024
-#define BRIDGE_MEM_MIN 16*1024
+#define BRIDGE_MEM_MAX (4*1024*1024)
+#define BRIDGE_MEM_ACC (128*1024)
+#define BRIDGE_MEM_MIN (16*1024)
#define BRIDGE_IO_MAX 512
#define BRIDGE_IO_ACC 256
@@ -574,7 +619,7 @@ static int yenta_search_one_res(struct resource *root, struct resource *res,
int i;
size = BRIDGE_MEM_MAX;
if (size > avail/8) {
- size=(avail+1)/8;
+ size = (avail+1)/8;
/* round size down to next power of 2 */
i = 0;
while ((size /= 2) != 0)
@@ -590,7 +635,7 @@ static int yenta_search_one_res(struct resource *root, struct resource *res,
do {
if (allocate_resource(root, res, size, start, end, align,
- NULL, NULL)==0) {
+ NULL, NULL) == 0) {
return 1;
}
size = size/2;
@@ -605,8 +650,8 @@ static int yenta_search_res(struct yenta_socket *socket, struct resource *res,
u32 min)
{
int i;
- for (i=0; i<PCI_BUS_NUM_RESOURCES; i++) {
- struct resource * root = socket->dev->bus->resource[i];
+ for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+ struct resource *root = socket->dev->bus->resource[i];
if (!root)
continue;
@@ -704,7 +749,7 @@ static void yenta_allocate_resources(struct yenta_socket *socket)
static void yenta_free_resources(struct yenta_socket *socket)
{
int i;
- for (i=0;i<4;i++) {
+ for (i = 0; i < 4; i++) {
struct resource *res;
res = socket->dev->resource + PCI_BRIDGE_RESOURCES + i;
if (res->start != 0 && res->end != 0)
@@ -726,7 +771,7 @@ static void __devexit yenta_close(struct pci_dev *dev)
/* we don't want a dying socket registered */
pcmcia_unregister_socket(&sock->socket);
-
+
/* Disable all events so we don't die in an IRQ storm */
cb_writel(sock, CB_SOCKET_MASK, 0x0);
exca_writeb(sock, I365_CSCINT, 0);
@@ -898,7 +943,7 @@ static irqreturn_t yenta_probe_handler(int irq, void *dev_id)
{
struct yenta_socket *socket = (struct yenta_socket *) dev_id;
u8 csc;
- u32 cb_event;
+ u32 cb_event;
/* Clear interrupt status for the event */
cb_event = cb_readl(socket, CB_SOCKET_EVENT);
@@ -1019,7 +1064,7 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
{
struct list_head *tmp;
unsigned char upper_limit;
- /*
+ /*
* We only check and fix the parent bridge: All systems which need
* this fixup that have been reviewed are laptops and the only bridge
* which needed fixing was the parent bridge of the CardBus bridge:
@@ -1038,7 +1083,7 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
/* check the bus ranges of all silbling bridges to prevent overlap */
list_for_each(tmp, &bridge_to_fix->parent->children) {
- struct pci_bus * silbling = pci_bus_b(tmp);
+ struct pci_bus *silbling = pci_bus_b(tmp);
/*
* If the silbling has a higher secondary bus number
* and it's secondary is equal or smaller than our
@@ -1083,7 +1128,7 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
* interrupt, and that we can map the cardbus area. Fill in the
* socket information structure..
*/
-static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct yenta_socket *socket;
int ret;
@@ -1285,7 +1330,7 @@ static int yenta_dev_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops yenta_pm_ops = {
+static const struct dev_pm_ops yenta_pm_ops = {
.suspend_noirq = yenta_dev_suspend_noirq,
.resume_noirq = yenta_dev_resume_noirq,
.resume = yenta_dev_resume,
@@ -1302,7 +1347,7 @@ static struct dev_pm_ops yenta_pm_ops = {
#define YENTA_PM_OPS NULL
#endif
-#define CB_ID(vend,dev,type) \
+#define CB_ID(vend, dev, type) \
{ \
.vendor = vend, \
.device = dev, \
@@ -1313,7 +1358,7 @@ static struct dev_pm_ops yenta_pm_ops = {
.driver_data = CARDBUS_TYPE_##type, \
}
-static struct pci_device_id yenta_table [] = {
+static struct pci_device_id yenta_table[] = {
CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1031, TI),
/*
@@ -1403,13 +1448,13 @@ static struct pci_driver yenta_cardbus_driver = {
static int __init yenta_socket_init(void)
{
- return pci_register_driver (&yenta_cardbus_driver);
+ return pci_register_driver(&yenta_cardbus_driver);
}
-static void __exit yenta_socket_exit (void)
+static void __exit yenta_socket_exit(void)
{
- pci_unregister_driver (&yenta_cardbus_driver);
+ pci_unregister_driver(&yenta_cardbus_driver);
}
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index ab64522aaa64..be27aa47e810 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -460,7 +460,7 @@ static int acerhdf_remove(struct platform_device *device)
return 0;
}
-static struct dev_pm_ops acerhdf_pm_ops = {
+static const struct dev_pm_ops acerhdf_pm_ops = {
.suspend = acerhdf_suspend,
.freeze = acerhdf_suspend,
};
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 4226e5352738..e647a856b9bf 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -154,7 +154,7 @@ static struct eeepc_hotk *ehotk;
static int eeepc_hotk_thaw(struct device *device);
static int eeepc_hotk_restore(struct device *device);
-static struct dev_pm_ops eeepc_pm_ops = {
+static const struct dev_pm_ops eeepc_pm_ops = {
.thaw = eeepc_hotk_thaw,
.restore = eeepc_hotk_restore,
};
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index c2842171cec6..f00a71c58e69 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -94,7 +94,7 @@ static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
-static struct dev_pm_ops hp_wmi_pm_ops = {
+static const struct dev_pm_ops hp_wmi_pm_ops = {
.resume = hp_wmi_resume_handler,
.restore = hp_wmi_resume_handler,
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 0ed84806f8ae..cf61d6a8ef6f 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -1006,11 +1006,8 @@ static int parse_strtoul(const char *buf,
{
char *endp;
- while (*buf && isspace(*buf))
- buf++;
- *value = simple_strtoul(buf, &endp, 0);
- while (*endp && isspace(*endp))
- endp++;
+ *value = simple_strtoul(skip_spaces(buf), &endp, 0);
+ endp = skip_spaces(endp);
if (*endp || *value > max)
return -EINVAL;
diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c
index c3f1c8e9d254..68b0c04987e4 100644
--- a/drivers/pnp/interface.c
+++ b/drivers/pnp/interface.c
@@ -310,8 +310,7 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
goto done;
}
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf);
if (!strnicmp(buf, "disable", 7)) {
retval = pnp_disable_dev(dev);
goto done;
@@ -353,19 +352,13 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
pnp_init_resources(dev);
mutex_lock(&pnp_res_mutex);
while (1) {
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf);
if (!strnicmp(buf, "io", 2)) {
- buf += 2;
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf + 2);
start = simple_strtoul(buf, &buf, 0);
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf);
if (*buf == '-') {
- buf += 1;
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf + 1);
end = simple_strtoul(buf, &buf, 0);
} else
end = start;
@@ -373,16 +366,11 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
continue;
}
if (!strnicmp(buf, "mem", 3)) {
- buf += 3;
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf + 3);
start = simple_strtoul(buf, &buf, 0);
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf);
if (*buf == '-') {
- buf += 1;
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf + 1);
end = simple_strtoul(buf, &buf, 0);
} else
end = start;
@@ -390,17 +378,13 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
continue;
}
if (!strnicmp(buf, "irq", 3)) {
- buf += 3;
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf + 3);
start = simple_strtoul(buf, &buf, 0);
pnp_add_irq_resource(dev, start, 0);
continue;
}
if (!strnicmp(buf, "dma", 3)) {
- buf += 3;
- while (isspace(*buf))
- ++buf;
+ buf = skip_spaces(buf + 3);
start = simple_strtoul(buf, &buf, 0);
pnp_add_dma_resource(dev, start, 0);
continue;
diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c
index b35d921bac6e..2d8ac43f78e8 100644
--- a/drivers/pnp/pnpbios/proc.c
+++ b/drivers/pnp/pnpbios/proc.c
@@ -24,6 +24,7 @@
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/pnp.h>
+#include <linux/seq_file.h>
#include <linux/init.h>
#include <asm/uaccess.h>
@@ -33,42 +34,65 @@
static struct proc_dir_entry *proc_pnp = NULL;
static struct proc_dir_entry *proc_pnp_boot = NULL;
-static int proc_read_pnpconfig(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
+static int pnpconfig_proc_show(struct seq_file *m, void *v)
{
struct pnp_isa_config_struc pnps;
if (pnp_bios_isapnp_config(&pnps))
return -EIO;
- return snprintf(buf, count,
- "structure_revision %d\n"
- "number_of_CSNs %d\n"
- "ISA_read_data_port 0x%x\n",
- pnps.revision, pnps.no_csns, pnps.isa_rd_data_port);
+ seq_printf(m, "structure_revision %d\n"
+ "number_of_CSNs %d\n"
+ "ISA_read_data_port 0x%x\n",
+ pnps.revision, pnps.no_csns, pnps.isa_rd_data_port);
+ return 0;
}
-static int proc_read_escdinfo(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
+static int pnpconfig_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pnpconfig_proc_show, NULL);
+}
+
+static const struct file_operations pnpconfig_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pnpconfig_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int escd_info_proc_show(struct seq_file *m, void *v)
{
struct escd_info_struc escd;
if (pnp_bios_escd_info(&escd))
return -EIO;
- return snprintf(buf, count,
- "min_ESCD_write_size %d\n"
+ seq_printf(m, "min_ESCD_write_size %d\n"
"ESCD_size %d\n"
"NVRAM_base 0x%x\n",
escd.min_escd_write_size,
escd.escd_size, escd.nv_storage_base);
+ return 0;
}
+static int escd_info_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, escd_info_proc_show, NULL);
+}
+
+static const struct file_operations escd_info_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = escd_info_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
#define MAX_SANE_ESCD_SIZE (32*1024)
-static int proc_read_escd(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
+static int escd_proc_show(struct seq_file *m, void *v)
{
struct escd_info_struc escd;
char *tmpbuf;
- int escd_size, escd_left_to_read, n;
+ int escd_size;
if (pnp_bios_escd_info(&escd))
return -EIO;
@@ -76,7 +100,7 @@ static int proc_read_escd(char *buf, char **start, off_t pos,
/* sanity check */
if (escd.escd_size > MAX_SANE_ESCD_SIZE) {
printk(KERN_ERR
- "PnPBIOS: proc_read_escd: ESCD size reported by BIOS escd_info call is too great\n");
+ "PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great\n", __func__);
return -EFBIG;
}
@@ -94,56 +118,75 @@ static int proc_read_escd(char *buf, char **start, off_t pos,
/* sanity check */
if (escd_size > MAX_SANE_ESCD_SIZE) {
- printk(KERN_ERR "PnPBIOS: proc_read_escd: ESCD size reported by"
- " BIOS read_escd call is too great\n");
+ printk(KERN_ERR "PnPBIOS: %s: ESCD size reported by"
+ " BIOS read_escd call is too great\n", __func__);
kfree(tmpbuf);
return -EFBIG;
}
- escd_left_to_read = escd_size - pos;
- if (escd_left_to_read < 0)
- escd_left_to_read = 0;
- if (escd_left_to_read == 0)
- *eof = 1;
- n = min(count, escd_left_to_read);
- memcpy(buf, tmpbuf + pos, n);
+ seq_write(m, tmpbuf, escd_size);
kfree(tmpbuf);
- *start = buf;
- return n;
+ return 0;
}
-static int proc_read_legacyres(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
+static int escd_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, escd_proc_show, NULL);
+}
+
+static const struct file_operations escd_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = escd_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pnp_legacyres_proc_show(struct seq_file *m, void *v)
{
- /* Assume that the following won't overflow the buffer */
- if (pnp_bios_get_stat_res(buf))
+ void *buf;
+
+ buf = kmalloc(65536, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ if (pnp_bios_get_stat_res(buf)) {
+ kfree(buf);
return -EIO;
+ }
+
+ seq_write(m, buf, 65536);
+ kfree(buf);
+ return 0;
+}
- return count; // FIXME: Return actual length
+static int pnp_legacyres_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pnp_legacyres_proc_show, NULL);
}
-static int proc_read_devices(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
+static const struct file_operations pnp_legacyres_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pnp_legacyres_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pnp_devices_proc_show(struct seq_file *m, void *v)
{
struct pnp_bios_node *node;
u8 nodenum;
- char *p = buf;
-
- if (pos >= 0xff)
- return 0;
node = kzalloc(node_info.max_node_size, GFP_KERNEL);
if (!node)
return -ENOMEM;
- for (nodenum = pos; nodenum < 0xff;) {
+ for (nodenum = 0; nodenum < 0xff;) {
u8 thisnodenum = nodenum;
- /* 26 = the number of characters per line sprintf'ed */
- if ((p - buf + 26) > count)
- break;
+
if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node))
break;
- p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n",
+ seq_printf(m, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n",
node->handle, node->eisa_id,
node->type_code[0], node->type_code[1],
node->type_code[2], node->flags);
@@ -153,20 +196,29 @@ static int proc_read_devices(char *buf, char **start, off_t pos,
"PnPBIOS: proc_read_devices:",
(unsigned int)nodenum,
(unsigned int)thisnodenum);
- *eof = 1;
break;
}
}
kfree(node);
- if (nodenum == 0xff)
- *eof = 1;
- *start = (char *)((off_t) nodenum - pos);
- return p - buf;
+ return 0;
+}
+
+static int pnp_devices_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pnp_devices_proc_show, NULL);
}
-static int proc_read_node(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
+static const struct file_operations pnp_devices_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pnp_devices_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pnpbios_proc_show(struct seq_file *m, void *v)
{
+ void *data = m->private;
struct pnp_bios_node *node;
int boot = (long)data >> 8;
u8 nodenum = (long)data;
@@ -180,14 +232,20 @@ static int proc_read_node(char *buf, char **start, off_t pos,
return -EIO;
}
len = node->size - sizeof(struct pnp_bios_node);
- memcpy(buf, node->data, len);
+ seq_write(m, node->data, len);
kfree(node);
- return len;
+ return 0;
+}
+
+static int pnpbios_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pnpbios_proc_show, PDE(inode)->data);
}
-static int proc_write_node(struct file *file, const char __user * buf,
- unsigned long count, void *data)
+static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
{
+ void *data = PDE(file->f_path.dentry->d_inode)->data;
struct pnp_bios_node *node;
int boot = (long)data >> 8;
u8 nodenum = (long)data;
@@ -218,34 +276,33 @@ out:
return ret;
}
+static const struct file_operations pnpbios_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = pnpbios_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = pnpbios_proc_write,
+};
+
int pnpbios_interface_attach_device(struct pnp_bios_node *node)
{
char name[3];
- struct proc_dir_entry *ent;
sprintf(name, "%02x", node->handle);
if (!proc_pnp)
return -EIO;
if (!pnpbios_dont_use_current_config) {
- ent = create_proc_entry(name, 0, proc_pnp);
- if (ent) {
- ent->read_proc = proc_read_node;
- ent->write_proc = proc_write_node;
- ent->data = (void *)(long)(node->handle);
- }
+ proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_fops,
+ (void *)(long)(node->handle));
}
if (!proc_pnp_boot)
return -EIO;
- ent = create_proc_entry(name, 0, proc_pnp_boot);
- if (ent) {
- ent->read_proc = proc_read_node;
- ent->write_proc = proc_write_node;
- ent->data = (void *)(long)(node->handle + 0x100);
+ if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_fops,
+ (void *)(long)(node->handle + 0x100)))
return 0;
- }
-
return -EIO;
}
@@ -262,14 +319,11 @@ int __init pnpbios_proc_init(void)
proc_pnp_boot = proc_mkdir("boot", proc_pnp);
if (!proc_pnp_boot)
return -EIO;
- create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL);
- create_proc_read_entry("configuration_info", 0, proc_pnp,
- proc_read_pnpconfig, NULL);
- create_proc_read_entry("escd_info", 0, proc_pnp, proc_read_escdinfo,
- NULL);
- create_proc_read_entry("escd", S_IRUSR, proc_pnp, proc_read_escd, NULL);
- create_proc_read_entry("legacy_device_resources", 0, proc_pnp,
- proc_read_legacyres, NULL);
+ proc_create("devices", 0, proc_pnp, &pnp_devices_proc_fops);
+ proc_create("configuration_info", 0, proc_pnp, &pnpconfig_proc_fops);
+ proc_create("escd_info", 0, proc_pnp, &escd_info_proc_fops);
+ proc_create("escd", S_IRUSR, proc_pnp, &escd_proc_fops);
+ proc_create("legacy_device_resources", 0, proc_pnp, &pnp_legacyres_proc_fops);
return 0;
}
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 118674925516..d4b3d67f0548 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -29,6 +29,13 @@ config APM_POWER
Say Y here to enable support APM status emulation using
battery class devices.
+config WM831X_BACKUP
+ tristate "WM831X backup battery charger support"
+ depends on MFD_WM831X
+ help
+ Say Y here to enable support for the backup battery charger
+ in the Wolfson Microelectronics WM831x PMICs.
+
config WM831X_POWER
tristate "WM831X PMU support"
depends on MFD_WM831X
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 356cdfd3c8b2..573597c683b4 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
obj-$(CONFIG_PDA_POWER) += pda_power.o
obj-$(CONFIG_APM_POWER) += apm_power.o
+obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o
obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c
index e8b278f71781..ea3fdfaca90d 100644
--- a/drivers/power/pcf50633-charger.c
+++ b/drivers/power/pcf50633-charger.c
@@ -29,15 +29,12 @@
struct pcf50633_mbc {
struct pcf50633 *pcf;
- int adapter_active;
int adapter_online;
- int usb_active;
int usb_online;
struct power_supply usb;
struct power_supply adapter;
-
- struct delayed_work charging_restart_work;
+ struct power_supply ac;
};
int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
@@ -47,16 +44,21 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
u8 bits;
int charging_start = 1;
u8 mbcs2, chgmod;
+ unsigned int mbcc5;
- if (ma >= 1000)
+ if (ma >= 1000) {
bits = PCF50633_MBCC7_USB_1000mA;
- else if (ma >= 500)
+ ma = 1000;
+ } else if (ma >= 500) {
bits = PCF50633_MBCC7_USB_500mA;
- else if (ma >= 100)
+ ma = 500;
+ } else if (ma >= 100) {
bits = PCF50633_MBCC7_USB_100mA;
- else {
+ ma = 100;
+ } else {
bits = PCF50633_MBCC7_USB_SUSPEND;
charging_start = 0;
+ ma = 0;
}
ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
@@ -66,21 +68,40 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
else
dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
- /* Manual charging start */
- mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+ /*
+ * We limit the charging current to be the USB current limit.
+ * The reason is that on pcf50633, when it enters PMU Standby mode,
+ * which it does when the device goes "off", the USB current limit
+ * reverts to the variant default. In at least one common case, that
+ * default is 500mA. By setting the charging current to be the same
+ * as the USB limit we set here before PMU standby, we enforce it only
+ * using the correct amount of current even when the USB current limit
+ * gets reset to the wrong thing
+ */
+
+ if (mbc->pcf->pdata->charger_reference_current_ma) {
+ mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
+ if (mbcc5 > 255)
+ mbcc5 = 255;
+ pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
+ }
+
+ mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
/* If chgmod == BATFULL, setting chgena has no effect.
- * We need to set resume instead.
+ * Datasheet says we need to set resume instead but when autoresume is
+ * used resume doesn't work. Clear and set chgena instead.
*/
if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
- else
+ else {
+ pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
+ PCF50633_MBCC1_CHGENA);
pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
- PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
-
- mbc->usb_active = charging_start;
+ PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
+ }
power_supply_changed(&mbc->usb);
@@ -92,20 +113,44 @@ int pcf50633_mbc_get_status(struct pcf50633 *pcf)
{
struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
int status = 0;
+ u8 chgmod;
+
+ if (!mbc)
+ return 0;
+
+ chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
+ & PCF50633_MBCS2_MBC_MASK;
if (mbc->usb_online)
status |= PCF50633_MBC_USB_ONLINE;
- if (mbc->usb_active)
+ if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
+ chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
+ chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
+ chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
status |= PCF50633_MBC_USB_ACTIVE;
if (mbc->adapter_online)
status |= PCF50633_MBC_ADAPTER_ONLINE;
- if (mbc->adapter_active)
+ if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
+ chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
+ chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
+ chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
status |= PCF50633_MBC_ADAPTER_ACTIVE;
return status;
}
EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
+int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
+{
+ struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
+
+ if (!mbc)
+ return 0;
+
+ return mbc->usb_online;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
+
static ssize_t
show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -156,9 +201,55 @@ static ssize_t set_usblim(struct device *dev,
static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
+static ssize_t
+show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+ u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
+ unsigned int ma;
+
+ if (!mbc->pcf->pdata->charger_reference_current_ma)
+ return -ENODEV;
+
+ ma = (mbc->pcf->pdata->charger_reference_current_ma * mbcc5) >> 8;
+
+ return sprintf(buf, "%u\n", ma);
+}
+
+static ssize_t set_chglim(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+ unsigned long ma;
+ unsigned int mbcc5;
+ int ret;
+
+ if (!mbc->pcf->pdata->charger_reference_current_ma)
+ return -ENODEV;
+
+ ret = strict_strtoul(buf, 10, &ma);
+ if (ret)
+ return -EINVAL;
+
+ mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
+ if (mbcc5 > 255)
+ mbcc5 = 255;
+ pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
+
+ return count;
+}
+
+/*
+ * This attribute allows to change MBC charging limit on the fly
+ * independently of usb current limit. It also gets set automatically every
+ * time usb current limit is changed.
+ */
+static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
+
static struct attribute *pcf50633_mbc_sysfs_entries[] = {
&dev_attr_chgmode.attr,
&dev_attr_usb_curlim.attr,
+ &dev_attr_chg_curlim.attr,
NULL,
};
@@ -167,76 +258,26 @@ static struct attribute_group mbc_attr_group = {
.attrs = pcf50633_mbc_sysfs_entries,
};
-/* MBC state machine switches into charging mode when the battery voltage
- * falls below 96% of a battery float voltage. But the voltage drop in Li-ion
- * batteries is marginal(1~2 %) till about 80% of its capacity - which means,
- * after a BATFULL, charging won't be restarted until 80%.
- *
- * This work_struct function restarts charging at regular intervals to make
- * sure we don't discharge too much
- */
-
-static void pcf50633_mbc_charging_restart(struct work_struct *work)
-{
- struct pcf50633_mbc *mbc;
- u8 mbcs2, chgmod;
-
- mbc = container_of(work, struct pcf50633_mbc,
- charging_restart_work.work);
-
- mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
- chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
-
- if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
- return;
-
- /* Restart charging */
- pcf50633_reg_set_bit_mask(mbc->pcf, PCF50633_REG_MBCC1,
- PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
- mbc->usb_active = 1;
- power_supply_changed(&mbc->usb);
-
- dev_info(mbc->pcf->dev, "Charging restarted\n");
-}
-
static void
pcf50633_mbc_irq_handler(int irq, void *data)
{
struct pcf50633_mbc *mbc = data;
- int chg_restart_interval =
- mbc->pcf->pdata->charging_restart_interval;
/* USB */
if (irq == PCF50633_IRQ_USBINS) {
mbc->usb_online = 1;
} else if (irq == PCF50633_IRQ_USBREM) {
mbc->usb_online = 0;
- mbc->usb_active = 0;
pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
- cancel_delayed_work_sync(&mbc->charging_restart_work);
}
/* Adapter */
- if (irq == PCF50633_IRQ_ADPINS) {
+ if (irq == PCF50633_IRQ_ADPINS)
mbc->adapter_online = 1;
- mbc->adapter_active = 1;
- } else if (irq == PCF50633_IRQ_ADPREM) {
+ else if (irq == PCF50633_IRQ_ADPREM)
mbc->adapter_online = 0;
- mbc->adapter_active = 0;
- }
-
- if (irq == PCF50633_IRQ_BATFULL) {
- mbc->usb_active = 0;
- mbc->adapter_active = 0;
-
- if (chg_restart_interval > 0)
- schedule_delayed_work(&mbc->charging_restart_work,
- chg_restart_interval);
- } else if (irq == PCF50633_IRQ_USBLIMON)
- mbc->usb_active = 0;
- else if (irq == PCF50633_IRQ_USBLIMOFF)
- mbc->usb_active = 1;
+ power_supply_changed(&mbc->ac);
power_supply_changed(&mbc->usb);
power_supply_changed(&mbc->adapter);
@@ -269,10 +310,34 @@ static int usb_get_property(struct power_supply *psy,
{
struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
int ret = 0;
+ u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+ PCF50633_MBCC7_USB_MASK;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
- val->intval = mbc->usb_online;
+ val->intval = mbc->usb_online &&
+ (usblim <= PCF50633_MBCC7_USB_500mA);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, ac);
+ int ret = 0;
+ u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+ PCF50633_MBCC7_USB_MASK;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = mbc->usb_online &&
+ (usblim == PCF50633_MBCC7_USB_1000mA);
break;
default:
ret = -EINVAL;
@@ -303,7 +368,6 @@ static const u8 mbc_irq_handlers[] = {
static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
{
struct pcf50633_mbc *mbc;
- struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
int ret;
int i;
u8 mbcs1;
@@ -313,7 +377,7 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, mbc);
- mbc->pcf = pdata->pcf;
+ mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
/* Set up IRQ handlers */
for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
@@ -337,6 +401,14 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
mbc->usb.supplied_to = mbc->pcf->pdata->batteries;
mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries;
+ mbc->ac.name = "ac";
+ mbc->ac.type = POWER_SUPPLY_TYPE_MAINS;
+ mbc->ac.properties = power_props;
+ mbc->ac.num_properties = ARRAY_SIZE(power_props);
+ mbc->ac.get_property = ac_get_property;
+ mbc->ac.supplied_to = mbc->pcf->pdata->batteries;
+ mbc->ac.num_supplicants = mbc->pcf->pdata->num_batteries;
+
ret = power_supply_register(&pdev->dev, &mbc->adapter);
if (ret) {
dev_err(mbc->pcf->dev, "failed to register adapter\n");
@@ -352,8 +424,14 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
return ret;
}
- INIT_DELAYED_WORK(&mbc->charging_restart_work,
- pcf50633_mbc_charging_restart);
+ ret = power_supply_register(&pdev->dev, &mbc->ac);
+ if (ret) {
+ dev_err(mbc->pcf->dev, "failed to register ac\n");
+ power_supply_unregister(&mbc->adapter);
+ power_supply_unregister(&mbc->usb);
+ kfree(mbc);
+ return ret;
+ }
ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
if (ret)
@@ -379,8 +457,7 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev)
power_supply_unregister(&mbc->usb);
power_supply_unregister(&mbc->adapter);
-
- cancel_delayed_work_sync(&mbc->charging_restart_work);
+ power_supply_unregister(&mbc->ac);
kfree(mbc);
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 08144393d64b..c790e0c77d4b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -65,7 +65,10 @@ static ssize_t power_supply_show_property(struct device *dev,
ret = psy->get_property(psy, off, &value);
if (ret < 0) {
- if (ret != -ENODEV)
+ if (ret == -ENODATA)
+ dev_dbg(dev, "driver has no data for `%s' property\n",
+ attr->attr.name);
+ else if (ret != -ENODEV)
dev_err(dev, "driver failed to report `%s' property\n",
attr->attr.name);
return ret;
diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c
new file mode 100644
index 000000000000..bf4f387a8009
--- /dev/null
+++ b/drivers/power/wm831x_backup.c
@@ -0,0 +1,233 @@
+/*
+ * Backup battery driver for Wolfson Microelectronics wm831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * 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/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/pmu.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+struct wm831x_backup {
+ struct wm831x *wm831x;
+ struct power_supply backup;
+};
+
+static int wm831x_backup_read_voltage(struct wm831x *wm831x,
+ enum wm831x_auxadc src,
+ union power_supply_propval *val)
+{
+ int ret;
+
+ ret = wm831x_auxadc_read_uv(wm831x, src);
+ if (ret >= 0)
+ val->intval = ret;
+
+ return ret;
+}
+
+/*********************************************************************
+ * Backup supply properties
+ *********************************************************************/
+
+static void wm831x_config_backup(struct wm831x *wm831x)
+{
+ struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
+ struct wm831x_backup_pdata *pdata;
+ int ret, reg;
+
+ if (!wm831x_pdata || !wm831x_pdata->backup) {
+ dev_warn(wm831x->dev,
+ "No backup battery charger configuration\n");
+ return;
+ }
+
+ pdata = wm831x_pdata->backup;
+
+ reg = 0;
+
+ if (pdata->charger_enable)
+ reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
+ if (pdata->no_constant_voltage)
+ reg |= WM831X_BKUP_CHG_MODE;
+
+ switch (pdata->vlim) {
+ case 2500:
+ break;
+ case 3100:
+ reg |= WM831X_BKUP_CHG_VLIM;
+ break;
+ default:
+ dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
+ pdata->vlim);
+ }
+
+ switch (pdata->ilim) {
+ case 100:
+ break;
+ case 200:
+ reg |= 1;
+ break;
+ case 300:
+ reg |= 2;
+ break;
+ case 400:
+ reg |= 3;
+ break;
+ default:
+ dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
+ pdata->ilim);
+ }
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
+ return;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
+ WM831X_BKUP_CHG_ENA_MASK |
+ WM831X_BKUP_CHG_MODE_MASK |
+ WM831X_BKUP_BATT_DET_ENA_MASK |
+ WM831X_BKUP_CHG_VLIM_MASK |
+ WM831X_BKUP_CHG_ILIM_MASK,
+ reg);
+ if (ret != 0)
+ dev_err(wm831x->dev,
+ "Failed to set backup charger config: %d\n", ret);
+
+ wm831x_reg_lock(wm831x);
+}
+
+static int wm831x_backup_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct wm831x_backup *devdata = dev_get_drvdata(psy->dev->parent);
+ struct wm831x *wm831x = devdata->wm831x;
+ int ret = 0;
+
+ ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
+ if (ret < 0)
+ return ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (ret & WM831X_BKUP_CHG_STS)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
+ val);
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ if (ret & WM831X_BKUP_CHG_STS)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property wm831x_backup_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_PRESENT,
+};
+
+/*********************************************************************
+ * Initialisation
+ *********************************************************************/
+
+static __devinit int wm831x_backup_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_backup *devdata;
+ struct power_supply *backup;
+ int ret;
+
+ devdata = kzalloc(sizeof(struct wm831x_backup), GFP_KERNEL);
+ if (devdata == NULL)
+ return -ENOMEM;
+
+ devdata->wm831x = wm831x;
+ platform_set_drvdata(pdev, devdata);
+
+ backup = &devdata->backup;
+
+ /* We ignore configuration failures since we can still read
+ * back the status without enabling the charger (which may
+ * already be enabled anyway).
+ */
+ wm831x_config_backup(wm831x);
+
+ backup->name = "wm831x-backup";
+ backup->type = POWER_SUPPLY_TYPE_BATTERY;
+ backup->properties = wm831x_backup_props;
+ backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
+ backup->get_property = wm831x_backup_get_prop;
+ ret = power_supply_register(&pdev->dev, backup);
+ if (ret)
+ goto err_kmalloc;
+
+ return ret;
+
+err_kmalloc:
+ kfree(devdata);
+ return ret;
+}
+
+static __devexit int wm831x_backup_remove(struct platform_device *pdev)
+{
+ struct wm831x_backup *devdata = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&devdata->backup);
+ kfree(devdata);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_backup_driver = {
+ .probe = wm831x_backup_probe,
+ .remove = __devexit_p(wm831x_backup_remove),
+ .driver = {
+ .name = "wm831x-backup",
+ },
+};
+
+static int __init wm831x_backup_init(void)
+{
+ return platform_driver_register(&wm831x_backup_driver);
+}
+module_init(wm831x_backup_init);
+
+static void __exit wm831x_backup_exit(void)
+{
+ platform_driver_unregister(&wm831x_backup_driver);
+}
+module_exit(wm831x_backup_exit);
+
+MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-backup");
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
index 2a4c8b0b829c..f85e80b1b400 100644
--- a/drivers/power/wm831x_power.c
+++ b/drivers/power/wm831x_power.c
@@ -21,7 +21,6 @@
struct wm831x_power {
struct wm831x *wm831x;
struct power_supply wall;
- struct power_supply backup;
struct power_supply usb;
struct power_supply battery;
};
@@ -454,125 +453,6 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
/*********************************************************************
- * Backup supply properties
- *********************************************************************/
-
-static void wm831x_config_backup(struct wm831x *wm831x)
-{
- struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
- struct wm831x_backup_pdata *pdata;
- int ret, reg;
-
- if (!wm831x_pdata || !wm831x_pdata->backup) {
- dev_warn(wm831x->dev,
- "No backup battery charger configuration\n");
- return;
- }
-
- pdata = wm831x_pdata->backup;
-
- reg = 0;
-
- if (pdata->charger_enable)
- reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
- if (pdata->no_constant_voltage)
- reg |= WM831X_BKUP_CHG_MODE;
-
- switch (pdata->vlim) {
- case 2500:
- break;
- case 3100:
- reg |= WM831X_BKUP_CHG_VLIM;
- break;
- default:
- dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
- pdata->vlim);
- }
-
- switch (pdata->ilim) {
- case 100:
- break;
- case 200:
- reg |= 1;
- break;
- case 300:
- reg |= 2;
- break;
- case 400:
- reg |= 3;
- break;
- default:
- dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
- pdata->ilim);
- }
-
- ret = wm831x_reg_unlock(wm831x);
- if (ret != 0) {
- dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
- return;
- }
-
- ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
- WM831X_BKUP_CHG_ENA_MASK |
- WM831X_BKUP_CHG_MODE_MASK |
- WM831X_BKUP_BATT_DET_ENA_MASK |
- WM831X_BKUP_CHG_VLIM_MASK |
- WM831X_BKUP_CHG_ILIM_MASK,
- reg);
- if (ret != 0)
- dev_err(wm831x->dev,
- "Failed to set backup charger config: %d\n", ret);
-
- wm831x_reg_lock(wm831x);
-}
-
-static int wm831x_backup_get_prop(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
- struct wm831x *wm831x = wm831x_power->wm831x;
- int ret = 0;
-
- ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
- if (ret < 0)
- return ret;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- if (ret & WM831X_BKUP_CHG_STS)
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
- else
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- break;
-
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
- val);
- break;
-
- case POWER_SUPPLY_PROP_PRESENT:
- if (ret & WM831X_BKUP_CHG_STS)
- val->intval = 1;
- else
- val->intval = 0;
- break;
-
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static enum power_supply_property wm831x_backup_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_PRESENT,
-};
-
-/*********************************************************************
* Initialisation
*********************************************************************/
@@ -595,10 +475,7 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
dev_dbg(wm831x->dev, "Power source changed\n");
- /* Just notify for everything - little harm in overnotifying.
- * The backup battery is not a power source while the system
- * is running so skip that.
- */
+ /* Just notify for everything - little harm in overnotifying. */
power_supply_changed(&wm831x_power->battery);
power_supply_changed(&wm831x_power->usb);
power_supply_changed(&wm831x_power->wall);
@@ -613,7 +490,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
struct power_supply *usb;
struct power_supply *battery;
struct power_supply *wall;
- struct power_supply *backup;
int ret, irq, i;
power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
@@ -626,13 +502,11 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
usb = &power->usb;
battery = &power->battery;
wall = &power->wall;
- backup = &power->backup;
/* We ignore configuration failures since we can still read back
- * the status without enabling either of the chargers.
+ * the status without enabling the charger.
*/
wm831x_config_battery(wm831x);
- wm831x_config_backup(wm831x);
wall->name = "wm831x-wall";
wall->type = POWER_SUPPLY_TYPE_MAINS;
@@ -661,15 +535,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
if (ret)
goto err_battery;
- backup->name = "wm831x-backup";
- backup->type = POWER_SUPPLY_TYPE_BATTERY;
- backup->properties = wm831x_backup_props;
- backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
- backup->get_property = wm831x_backup_get_prop;
- ret = power_supply_register(&pdev->dev, backup);
- if (ret)
- goto err_usb;
-
irq = platform_get_irq_byname(pdev, "SYSLO");
ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
IRQF_TRIGGER_RISING, "SYSLO",
@@ -677,7 +542,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
irq, ret);
- goto err_backup;
+ goto err_usb;
}
irq = platform_get_irq_byname(pdev, "PWR SRC");
@@ -716,8 +581,6 @@ err_bat_irq:
err_syslo:
irq = platform_get_irq_byname(pdev, "SYSLO");
wm831x_free_irq(wm831x, irq, power);
-err_backup:
- power_supply_unregister(backup);
err_usb:
power_supply_unregister(usb);
err_battery:
@@ -746,7 +609,6 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "SYSLO");
wm831x_free_irq(wm831x, irq, wm831x_power);
- power_supply_unregister(&wm831x_power->backup);
power_supply_unregister(&wm831x_power->battery);
power_supply_unregister(&wm831x_power->wall);
power_supply_unregister(&wm831x_power->usb);
diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c
index 28b0299c0043..ad4f071e1287 100644
--- a/drivers/power/wm8350_power.c
+++ b/drivers/power/wm8350_power.c
@@ -184,8 +184,9 @@ static ssize_t charger_state_show(struct device *dev,
static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL);
-static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
+static irqreturn_t wm8350_charger_handler(int irq, void *data)
{
+ struct wm8350 *wm8350 = data;
struct wm8350_power *power = &wm8350->power;
struct wm8350_charger_policy *policy = power->policy;
@@ -238,6 +239,8 @@ static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
default:
dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);
}
+
+ return IRQ_HANDLED;
}
/*********************************************************************
@@ -387,73 +390,55 @@ static void wm8350_init_charger(struct wm8350 *wm8350)
{
/* register our interest in charger events */
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+ wm8350_charger_handler, 0, "Battery hot", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+ wm8350_charger_handler, 0, "Battery cold", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+ wm8350_charger_handler, 0, "Battery fail", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO);
+ wm8350_charger_handler, 0,
+ "Charger timeout", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END);
+ wm8350_charger_handler, 0,
+ "Charge end", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START);
+ wm8350_charger_handler, 0,
+ "Charge start", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY);
+ wm8350_charger_handler, 0,
+ "Fast charge ready", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+ wm8350_charger_handler, 0,
+ "Battery <3.9V", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+ wm8350_charger_handler, 0,
+ "Battery <3.1V", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+ wm8350_charger_handler, 0,
+ "Battery <2.85V", wm8350);
/* and supply change events */
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
+ wm8350_charger_handler, 0, "USB", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
+ wm8350_charger_handler, 0, "Wall", wm8350);
wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,
- wm8350_charger_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
+ wm8350_charger_handler, 0, "Battery", wm8350);
}
static void free_charger_irq(struct wm8350 *wm8350)
{
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
- wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
- wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
- wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
- wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
}
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c
index f2bfd296dbae..fa39e759a275 100644
--- a/drivers/power/wm97xx_battery.c
+++ b/drivers/power/wm97xx_battery.c
@@ -157,7 +157,7 @@ static int wm97xx_bat_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops wm97xx_bat_pm_ops = {
+static const struct dev_pm_ops wm97xx_bat_pm_ops = {
.suspend = wm97xx_bat_suspend,
.resume = wm97xx_bat_resume,
};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index bcbb161bde0b..7cfdd65bebb4 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -70,7 +70,7 @@ config REGULATOR_MAX1586
for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
config REGULATOR_TWL4030
- bool "TI TWL4030/TWL5030/TPS695x0 PMIC"
+ bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
depends on TWL4030_CORE
help
This driver supports the voltage regulators provided by
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4257a8683778..9ae3cc44e668 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
-obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o
+obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
index 0803ffe6236d..c8f41dc05b76 100644
--- a/drivers/regulator/pcf50633-regulator.c
+++ b/drivers/regulator/pcf50633-regulator.c
@@ -314,13 +314,15 @@ static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
struct pcf50633 *pcf;
/* Already set by core driver */
- pcf = platform_get_drvdata(pdev);
+ pcf = dev_to_pcf50633(pdev->dev.parent);
rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
pdev->dev.platform_data, pcf);
if (IS_ERR(rdev))
return PTR_ERR(rdev);
+ platform_set_drvdata(pdev, rdev);
+
if (pcf->pdata->regulator_registered)
pcf->pdata->regulator_registered(pcf, pdev->id);
@@ -331,6 +333,7 @@ static int __devexit pcf50633_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
regulator_unregister(rdev);
return 0;
diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl-regulator.c
index e2032fb60b55..7ea1c3a31081 100644
--- a/drivers/regulator/twl4030-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -1,5 +1,5 @@
/*
- * twl4030-regulator.c -- support regulators in twl4030 family chips
+ * twl-regulator.c -- support regulators in twl4030/twl6030 family chips
*
* Copyright (C) 2008 David Brownell
*
@@ -15,11 +15,11 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
/*
- * The TWL4030/TW5030/TPS659x0 family chips include power management, a
+ * The TWL4030/TW5030/TPS659x0/TWL6030 family chips include power management, a
* USB OTG transceiver, an RTC, ADC, PWM, and lots more. Some versions
* include an audio codec, battery charger, and more voltage regulators.
* These chips are often used in OMAP-based systems.
@@ -33,7 +33,7 @@ struct twlreg_info {
/* start of regulator's PM_RECEIVER control register bank */
u8 base;
- /* twl4030 resource ID, for resource control state machine */
+ /* twl resource ID, for resource control state machine */
u8 id;
/* voltage in mV = table[VSEL]; table_len must be a power-of-two */
@@ -52,27 +52,38 @@ struct twlreg_info {
* The first three registers of all power resource banks help hardware to
* manage the various resource groups.
*/
+/* Common offset in TWL4030/6030 */
#define VREG_GRP 0
+/* TWL4030 register offsets */
#define VREG_TYPE 1
#define VREG_REMAP 2
#define VREG_DEDICATED 3 /* LDO control */
-
+/* TWL6030 register offsets */
+#define VREG_TRANS 1
+#define VREG_STATE 2
+#define VREG_VOLTAGE 3
+/* TWL6030 Misc register offsets */
+#define VREG_BC_ALL 1
+#define VREG_BC_REF 2
+#define VREG_BC_PROC 3
+#define VREG_BC_CLK_RST 4
static inline int
-twl4030reg_read(struct twlreg_info *info, unsigned offset)
+twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset)
{
u8 value;
int status;
- status = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER,
+ status = twl_i2c_read_u8(slave_subgp,
&value, info->base + offset);
return (status < 0) ? status : value;
}
static inline int
-twl4030reg_write(struct twlreg_info *info, unsigned offset, u8 value)
+twlreg_write(struct twlreg_info *info, unsigned slave_subgp, unsigned offset,
+ u8 value)
{
- return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ return twl_i2c_write_u8(slave_subgp,
value, info->base + offset);
}
@@ -80,59 +91,79 @@ twl4030reg_write(struct twlreg_info *info, unsigned offset, u8 value)
/* generic power resource operations, which work on all regulators */
-static int twl4030reg_grp(struct regulator_dev *rdev)
+static int twlreg_grp(struct regulator_dev *rdev)
{
- return twl4030reg_read(rdev_get_drvdata(rdev), VREG_GRP);
+ return twlreg_read(rdev_get_drvdata(rdev), TWL_MODULE_PM_RECEIVER,
+ VREG_GRP);
}
/*
* Enable/disable regulators by joining/leaving the P1 (processor) group.
* We assume nobody else is updating the DEV_GRP registers.
*/
-
-#define P3_GRP BIT(7) /* "peripherals" */
-#define P2_GRP BIT(6) /* secondary processor, modem, etc */
-#define P1_GRP BIT(5) /* CPU/Linux */
-
-static int twl4030reg_is_enabled(struct regulator_dev *rdev)
+/* definition for 4030 family */
+#define P3_GRP_4030 BIT(7) /* "peripherals" */
+#define P2_GRP_4030 BIT(6) /* secondary processor, modem, etc */
+#define P1_GRP_4030 BIT(5) /* CPU/Linux */
+/* definition for 6030 family */
+#define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */
+#define P2_GRP_6030 BIT(1) /* "peripherals" */
+#define P1_GRP_6030 BIT(0) /* CPU/Linux */
+
+static int twlreg_is_enabled(struct regulator_dev *rdev)
{
- int state = twl4030reg_grp(rdev);
+ int state = twlreg_grp(rdev);
if (state < 0)
return state;
- return (state & P1_GRP) != 0;
+ if (twl_class_is_4030())
+ state &= P1_GRP_4030;
+ else
+ state &= P1_GRP_6030;
+ return state;
}
-static int twl4030reg_enable(struct regulator_dev *rdev)
+static int twlreg_enable(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp;
- grp = twl4030reg_read(info, VREG_GRP);
+ grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
if (grp < 0)
return grp;
- grp |= P1_GRP;
- return twl4030reg_write(info, VREG_GRP, grp);
+ if (twl_class_is_4030())
+ grp |= P1_GRP_4030;
+ else
+ grp |= P1_GRP_6030;
+
+ return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
}
-static int twl4030reg_disable(struct regulator_dev *rdev)
+static int twlreg_disable(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp;
- grp = twl4030reg_read(info, VREG_GRP);
+ grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
if (grp < 0)
return grp;
- grp &= ~P1_GRP;
- return twl4030reg_write(info, VREG_GRP, grp);
+ if (twl_class_is_4030())
+ grp &= ~P1_GRP_4030;
+ else
+ grp &= ~P1_GRP_6030;
+
+ return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
}
-static int twl4030reg_get_status(struct regulator_dev *rdev)
+static int twlreg_get_status(struct regulator_dev *rdev)
{
- int state = twl4030reg_grp(rdev);
+ int state = twlreg_grp(rdev);
+
+ if (twl_class_is_6030())
+ return 0; /* FIXME return for 6030 regulator */
if (state < 0)
return state;
@@ -146,12 +177,15 @@ static int twl4030reg_get_status(struct regulator_dev *rdev)
: REGULATOR_STATUS_STANDBY;
}
-static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
+static int twlreg_set_mode(struct regulator_dev *rdev, unsigned mode)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
unsigned message;
int status;
+ if (twl_class_is_6030())
+ return 0; /* FIXME return for 6030 regulator */
+
/* We can only set the mode through state machine commands... */
switch (mode) {
case REGULATOR_MODE_NORMAL:
@@ -165,18 +199,18 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
}
/* Ensure the resource is associated with some group */
- status = twl4030reg_grp(rdev);
+ status = twlreg_grp(rdev);
if (status < 0)
return status;
- if (!(status & (P3_GRP | P2_GRP | P1_GRP)))
+ if (!(status & (P3_GRP_4030 | P2_GRP_4030 | P1_GRP_4030)))
return -EACCES;
- status = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ status = twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
message >> 8, 0x15 /* PB_WORD_MSB */ );
if (status >= 0)
return status;
- return twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+ return twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
message, 0x16 /* PB_WORD_LSB */ );
}
@@ -260,9 +294,31 @@ static const u16 VSIM_VSEL_table[] = {
static const u16 VDAC_VSEL_table[] = {
1200, 1300, 1800, 1800,
};
+static const u16 VAUX1_6030_VSEL_table[] = {
+ 1000, 1300, 1800, 2500,
+ 2800, 2900, 3000, 3000,
+};
+static const u16 VAUX2_6030_VSEL_table[] = {
+ 1200, 1800, 2500, 2750,
+ 2800, 2800, 2800, 2800,
+};
+static const u16 VAUX3_6030_VSEL_table[] = {
+ 1000, 1200, 1300, 1800,
+ 2500, 2800, 3000, 3000,
+};
+static const u16 VMMC_VSEL_table[] = {
+ 1200, 1800, 2800, 2900,
+ 3000, 3000, 3000, 3000,
+};
+static const u16 VPP_VSEL_table[] = {
+ 1800, 1900, 2000, 2100,
+ 2200, 2300, 2400, 2500,
+};
+static const u16 VUSIM_VSEL_table[] = {
+ 1200, 1800, 2500, 2900,
+};
-
-static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index)
+static int twlldo_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int mV = info->table[index];
@@ -271,7 +327,7 @@ static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index)
}
static int
-twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
+twlldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int vsel;
@@ -288,16 +344,18 @@ twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
/* use the first in-range value */
if (min_uV <= uV && uV <= max_uV)
- return twl4030reg_write(info, VREG_DEDICATED, vsel);
+ return twlreg_write(info, TWL_MODULE_PM_RECEIVER,
+ VREG_VOLTAGE, vsel);
}
return -EDOM;
}
-static int twl4030ldo_get_voltage(struct regulator_dev *rdev)
+static int twlldo_get_voltage(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
- int vsel = twl4030reg_read(info, VREG_DEDICATED);
+ int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER,
+ VREG_VOLTAGE);
if (vsel < 0)
return vsel;
@@ -306,19 +364,19 @@ static int twl4030ldo_get_voltage(struct regulator_dev *rdev)
return LDO_MV(info->table[vsel]) * 1000;
}
-static struct regulator_ops twl4030ldo_ops = {
- .list_voltage = twl4030ldo_list_voltage,
+static struct regulator_ops twlldo_ops = {
+ .list_voltage = twlldo_list_voltage,
- .set_voltage = twl4030ldo_set_voltage,
- .get_voltage = twl4030ldo_get_voltage,
+ .set_voltage = twlldo_set_voltage,
+ .get_voltage = twlldo_get_voltage,
- .enable = twl4030reg_enable,
- .disable = twl4030reg_disable,
- .is_enabled = twl4030reg_is_enabled,
+ .enable = twlreg_enable,
+ .disable = twlreg_disable,
+ .is_enabled = twlreg_is_enabled,
- .set_mode = twl4030reg_set_mode,
+ .set_mode = twlreg_set_mode,
- .get_status = twl4030reg_get_status,
+ .get_status = twlreg_get_status,
};
/*----------------------------------------------------------------------*/
@@ -326,60 +384,69 @@ static struct regulator_ops twl4030ldo_ops = {
/*
* Fixed voltage LDOs don't have a VSEL field to update.
*/
-static int twl4030fixed_list_voltage(struct regulator_dev *rdev, unsigned index)
+static int twlfixed_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
return info->min_mV * 1000;
}
-static int twl4030fixed_get_voltage(struct regulator_dev *rdev)
+static int twlfixed_get_voltage(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
return info->min_mV * 1000;
}
-static struct regulator_ops twl4030fixed_ops = {
- .list_voltage = twl4030fixed_list_voltage,
+static struct regulator_ops twlfixed_ops = {
+ .list_voltage = twlfixed_list_voltage,
- .get_voltage = twl4030fixed_get_voltage,
+ .get_voltage = twlfixed_get_voltage,
- .enable = twl4030reg_enable,
- .disable = twl4030reg_disable,
- .is_enabled = twl4030reg_is_enabled,
+ .enable = twlreg_enable,
+ .disable = twlreg_disable,
+ .is_enabled = twlreg_is_enabled,
- .set_mode = twl4030reg_set_mode,
+ .set_mode = twlreg_set_mode,
- .get_status = twl4030reg_get_status,
+ .get_status = twlreg_get_status,
};
/*----------------------------------------------------------------------*/
-#define TWL_ADJUSTABLE_LDO(label, offset, num) { \
+#define TWL4030_ADJUSTABLE_LDO(label, offset, num) \
+ TWL_ADJUSTABLE_LDO(label, offset, num, TWL4030)
+#define TWL4030_FIXED_LDO(label, offset, mVolts, num) \
+ TWL_FIXED_LDO(label, offset, mVolts, num, TWL4030)
+#define TWL6030_ADJUSTABLE_LDO(label, offset, num) \
+ TWL_ADJUSTABLE_LDO(label, offset, num, TWL6030)
+#define TWL6030_FIXED_LDO(label, offset, mVolts, num) \
+ TWL_FIXED_LDO(label, offset, mVolts, num, TWL6030)
+
+#define TWL_ADJUSTABLE_LDO(label, offset, num, family) { \
.base = offset, \
.id = num, \
.table_len = ARRAY_SIZE(label##_VSEL_table), \
.table = label##_VSEL_table, \
.desc = { \
.name = #label, \
- .id = TWL4030_REG_##label, \
+ .id = family##_REG_##label, \
.n_voltages = ARRAY_SIZE(label##_VSEL_table), \
- .ops = &twl4030ldo_ops, \
+ .ops = &twlldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
}
-#define TWL_FIXED_LDO(label, offset, mVolts, num) { \
+#define TWL_FIXED_LDO(label, offset, mVolts, num, family) { \
.base = offset, \
.id = num, \
.min_mV = mVolts, \
.desc = { \
.name = #label, \
- .id = TWL4030_REG_##label, \
+ .id = family##_REG_##label, \
.n_voltages = 1, \
- .ops = &twl4030fixed_ops, \
+ .ops = &twlfixed_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
@@ -389,35 +456,47 @@ static struct regulator_ops twl4030fixed_ops = {
* We list regulators here if systems need some level of
* software control over them after boot.
*/
-static struct twlreg_info twl4030_regs[] = {
- TWL_ADJUSTABLE_LDO(VAUX1, 0x17, 1),
- TWL_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2),
- TWL_ADJUSTABLE_LDO(VAUX2, 0x1b, 2),
- TWL_ADJUSTABLE_LDO(VAUX3, 0x1f, 3),
- TWL_ADJUSTABLE_LDO(VAUX4, 0x23, 4),
- TWL_ADJUSTABLE_LDO(VMMC1, 0x27, 5),
- TWL_ADJUSTABLE_LDO(VMMC2, 0x2b, 6),
+static struct twlreg_info twl_regs[] = {
+ TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1),
+ TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2),
+ TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2),
+ TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3),
+ TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4),
+ TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5),
+ TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6),
/*
- TWL_ADJUSTABLE_LDO(VPLL1, 0x2f, 7),
+ TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7),
*/
- TWL_ADJUSTABLE_LDO(VPLL2, 0x33, 8),
- TWL_ADJUSTABLE_LDO(VSIM, 0x37, 9),
- TWL_ADJUSTABLE_LDO(VDAC, 0x3b, 10),
+ TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8),
+ TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9),
+ TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10),
/*
- TWL_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11),
- TWL_ADJUSTABLE_LDO(VINTANA2, 0x43, 12),
- TWL_ADJUSTABLE_LDO(VINTDIG, 0x47, 13),
- TWL_SMPS(VIO, 0x4b, 14),
- TWL_SMPS(VDD1, 0x55, 15),
- TWL_SMPS(VDD2, 0x63, 16),
+ TWL4030_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11),
+ TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12),
+ TWL4030_ADJUSTABLE_LDO(VINTDIG, 0x47, 13),
+ TWL4030_SMPS(VIO, 0x4b, 14),
+ TWL4030_SMPS(VDD1, 0x55, 15),
+ TWL4030_SMPS(VDD2, 0x63, 16),
*/
- TWL_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
- TWL_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
- TWL_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
+ TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
+ TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
+ TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
/* VUSBCP is managed *only* by the USB subchip */
+
+ /* 6030 REG with base as PMC Slave Misc : 0x0030 */
+ TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1),
+ TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2),
+ TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3),
+ TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4),
+ TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5),
+ TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7),
+ TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15),
+ TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16),
+ TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17),
+ TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18)
};
-static int twl4030reg_probe(struct platform_device *pdev)
+static int twlreg_probe(struct platform_device *pdev)
{
int i;
struct twlreg_info *info;
@@ -425,10 +504,10 @@ static int twl4030reg_probe(struct platform_device *pdev)
struct regulation_constraints *c;
struct regulator_dev *rdev;
- for (i = 0, info = NULL; i < ARRAY_SIZE(twl4030_regs); i++) {
- if (twl4030_regs[i].desc.id != pdev->id)
+ for (i = 0, info = NULL; i < ARRAY_SIZE(twl_regs); i++) {
+ if (twl_regs[i].desc.id != pdev->id)
continue;
- info = twl4030_regs + i;
+ info = twl_regs + i;
break;
}
if (!info)
@@ -466,35 +545,35 @@ static int twl4030reg_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit twl4030reg_remove(struct platform_device *pdev)
+static int __devexit twlreg_remove(struct platform_device *pdev)
{
regulator_unregister(platform_get_drvdata(pdev));
return 0;
}
-MODULE_ALIAS("platform:twl4030_reg");
+MODULE_ALIAS("platform:twl_reg");
-static struct platform_driver twl4030reg_driver = {
- .probe = twl4030reg_probe,
- .remove = __devexit_p(twl4030reg_remove),
+static struct platform_driver twlreg_driver = {
+ .probe = twlreg_probe,
+ .remove = __devexit_p(twlreg_remove),
/* NOTE: short name, to work around driver model truncation of
- * "twl4030_regulator.12" (and friends) to "twl4030_regulator.1".
+ * "twl_regulator.12" (and friends) to "twl_regulator.1".
*/
- .driver.name = "twl4030_reg",
+ .driver.name = "twl_reg",
.driver.owner = THIS_MODULE,
};
-static int __init twl4030reg_init(void)
+static int __init twlreg_init(void)
{
- return platform_driver_register(&twl4030reg_driver);
+ return platform_driver_register(&twlreg_driver);
}
-subsys_initcall(twl4030reg_init);
+subsys_initcall(twlreg_init);
-static void __exit twl4030reg_exit(void)
+static void __exit twlreg_exit(void)
{
- platform_driver_unregister(&twl4030reg_driver);
+ platform_driver_unregister(&twlreg_driver);
}
-module_exit(twl4030reg_exit)
+module_exit(twlreg_exit)
-MODULE_DESCRIPTION("TWL4030 regulator driver");
+MODULE_DESCRIPTION("TWL regulator driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
index 768bd0e5b48b..1bbff099a546 100644
--- a/drivers/regulator/wm8350-regulator.c
+++ b/drivers/regulator/wm8350-regulator.c
@@ -1330,9 +1330,10 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = {
},
};
-static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data)
+static irqreturn_t pmic_uv_handler(int irq, void *data)
{
struct regulator_dev *rdev = (struct regulator_dev *)data;
+ struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
mutex_lock(&rdev->mutex);
if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
@@ -1344,6 +1345,8 @@ static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data)
REGULATOR_EVENT_UNDER_VOLTAGE,
wm8350);
mutex_unlock(&rdev->mutex);
+
+ return IRQ_HANDLED;
}
static int wm8350_regulator_probe(struct platform_device *pdev)
@@ -1388,7 +1391,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev)
/* register regulator IRQ */
ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq,
- pmic_uv_handler, rdev);
+ pmic_uv_handler, 0, "UV", rdev);
if (ret < 0) {
regulator_unregister(rdev);
dev_err(&pdev->dev, "failed to register regulator %s IRQ\n",
@@ -1396,8 +1399,6 @@ static int wm8350_regulator_probe(struct platform_device *pdev)
return ret;
}
- wm8350_unmask_irq(wm8350, wm8350_reg[pdev->id].irq);
-
return 0;
}
@@ -1406,7 +1407,6 @@ static int wm8350_regulator_remove(struct platform_device *pdev)
struct regulator_dev *rdev = platform_get_drvdata(pdev);
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
- wm8350_mask_irq(wm8350, wm8350_reg[pdev->id].irq);
wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq);
regulator_unregister(rdev);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index f2e1004d12c7..8167e9e6827a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -242,6 +242,15 @@ config RTC_DRV_M41T80_WDT
If you say Y here you will get support for the
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
+config RTC_DRV_BQ32K
+ tristate "TI BQ32000"
+ help
+ If you say Y here you will get support for the TI
+ BQ32000 I2C RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bq32k.
+
config RTC_DRV_DM355EVM
tristate "TI DaVinci DM355 EVM RTC"
depends on MFD_DM355EVM_MSP
@@ -258,14 +267,14 @@ config RTC_DRV_TWL92330
the Menelaus driver; it's not separate module.
config RTC_DRV_TWL4030
- tristate "TI TWL4030/TWL5030/TPS659x0"
+ tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
depends on RTC_CLASS && TWL4030_CORE
help
If you say yes here you get support for the RTC on the
- TWL4030 family chips, used mostly with OMAP3 platforms.
+ TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
This driver can also be built as a module. If so, the module
- will be called rtc-twl4030.
+ will be called rtc-twl.
config RTC_DRV_S35390A
tristate "Seiko Instruments S-35390A"
@@ -592,15 +601,22 @@ config RTC_DRV_AB3100
Select this to enable the ST-Ericsson AB3100 Mixed Signal IC RTC
support. This chip contains a battery- and capacitor-backed RTC.
+config RTC_DRV_NUC900
+ tristate "NUC910/NUC920 RTC driver"
+ depends on RTC_CLASS && ARCH_W90X900
+ help
+ If you say yes here you get support for the RTC subsystem of the
+ NUC910/NUC920 used in embedded systems.
comment "on-CPU RTC drivers"
config RTC_DRV_OMAP
tristate "TI OMAP1"
- depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730
+ depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX
help
- Say "yes" here to support the real time clock on TI OMAP1 chips.
- This driver can also be built as a module called rtc-omap.
+ Say "yes" here to support the real time clock on TI OMAP1 and
+ DA8xx/OMAP-L13x chips. This driver can also be built as a
+ module called rtc-omap.
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
@@ -846,4 +862,10 @@ config RTC_DRV_PCAP
If you say Y here you will get support for the RTC found on
the PCAP2 ASIC used on some Motorola phones.
+config RTC_DRV_MC13783
+ depends on MFD_MC13783
+ tristate "Freescale MC13783 RTC"
+ help
+ This enables support for the Freescale MC13783 PMIC RTC
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index af1ba7ae2857..e5160fddc446 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
+obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
@@ -52,8 +53,10 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
+obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
@@ -80,7 +83,7 @@ obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
-obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o
+obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
index e1ec33e40e38..8825695777df 100644
--- a/drivers/rtc/rtc-at32ap700x.c
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -256,6 +256,8 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
goto out_iounmap;
}
+ platform_set_drvdata(pdev, rtc);
+
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&at32_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc)) {
@@ -264,7 +266,6 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
goto out_free_irq;
}
- platform_set_drvdata(pdev, rtc);
device_init_wakeup(&pdev->dev, 1);
dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
@@ -273,6 +274,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
return 0;
out_free_irq:
+ platform_set_drvdata(pdev, NULL);
free_irq(irq, rtc);
out_iounmap:
iounmap(rtc->regs);
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
new file mode 100644
index 000000000000..408cc8f735be
--- /dev/null
+++ b/drivers/rtc/rtc-bq32k.c
@@ -0,0 +1,204 @@
+/*
+ * Driver for TI BQ32000 RTC.
+ *
+ * Copyright (C) 2009 Semihalf.
+ *
+ * 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/i2c.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/bcd.h>
+
+#define BQ32K_SECONDS 0x00 /* Seconds register address */
+#define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */
+#define BQ32K_STOP 0x80 /* Oscillator Stop flat */
+
+#define BQ32K_MINUTES 0x01 /* Minutes register address */
+#define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */
+#define BQ32K_OF 0x80 /* Oscillator Failure flag */
+
+#define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */
+#define BQ32K_CENT 0x40 /* Century flag */
+#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */
+
+struct bq32k_regs {
+ uint8_t seconds;
+ uint8_t minutes;
+ uint8_t cent_hours;
+ uint8_t day;
+ uint8_t date;
+ uint8_t month;
+ uint8_t years;
+};
+
+static struct i2c_driver bq32k_driver;
+
+static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &off,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = data,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msgs, 2) == 2)
+ return 0;
+
+ return -EIO;
+}
+
+static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ uint8_t buffer[len + 1];
+
+ buffer[0] = off;
+ memcpy(&buffer[1], data, len);
+
+ if (i2c_master_send(client, buffer, len + 1) == len + 1)
+ return 0;
+
+ return -EIO;
+}
+
+static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct bq32k_regs regs;
+ int error;
+
+ error = bq32k_read(dev, &regs, 0, sizeof(regs));
+ if (error)
+ return error;
+
+ tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK);
+ tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK);
+ tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK);
+ tm->tm_mday = bcd2bin(regs.date);
+ tm->tm_wday = bcd2bin(regs.day) - 1;
+ tm->tm_mon = bcd2bin(regs.month) - 1;
+ tm->tm_year = bcd2bin(regs.years) +
+ ((regs.cent_hours & BQ32K_CENT) ? 100 : 0);
+
+ return rtc_valid_tm(tm);
+}
+
+static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct bq32k_regs regs;
+
+ regs.seconds = bin2bcd(tm->tm_sec);
+ regs.minutes = bin2bcd(tm->tm_min);
+ regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN;
+ regs.day = bin2bcd(tm->tm_wday + 1);
+ regs.date = bin2bcd(tm->tm_mday);
+ regs.month = bin2bcd(tm->tm_mon + 1);
+
+ if (tm->tm_year >= 100) {
+ regs.cent_hours |= BQ32K_CENT;
+ regs.years = bin2bcd(tm->tm_year - 100);
+ } else
+ regs.years = bin2bcd(tm->tm_year);
+
+ return bq32k_write(dev, &regs, 0, sizeof(regs));
+}
+
+static const struct rtc_class_ops bq32k_rtc_ops = {
+ .read_time = bq32k_rtc_read_time,
+ .set_time = bq32k_rtc_set_time,
+};
+
+static int bq32k_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct rtc_device *rtc;
+ uint8_t reg;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ /* Check Oscillator Stop flag */
+ error = bq32k_read(dev, &reg, BQ32K_SECONDS, 1);
+ if (!error && (reg & BQ32K_STOP)) {
+ dev_warn(dev, "Oscillator was halted. Restarting...\n");
+ reg &= ~BQ32K_STOP;
+ error = bq32k_write(dev, &reg, BQ32K_SECONDS, 1);
+ }
+ if (error)
+ return error;
+
+ /* Check Oscillator Failure flag */
+ error = bq32k_read(dev, &reg, BQ32K_MINUTES, 1);
+ if (!error && (reg & BQ32K_OF)) {
+ dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
+ reg &= ~BQ32K_OF;
+ error = bq32k_write(dev, &reg, BQ32K_MINUTES, 1);
+ }
+ if (error)
+ return error;
+
+ rtc = rtc_device_register(bq32k_driver.driver.name, &client->dev,
+ &bq32k_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static int __devexit bq32k_remove(struct i2c_client *client)
+{
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+
+ rtc_device_unregister(rtc);
+ return 0;
+}
+
+static const struct i2c_device_id bq32k_id[] = {
+ { "bq32000", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bq32k_id);
+
+static struct i2c_driver bq32k_driver = {
+ .driver = {
+ .name = "bq32k",
+ .owner = THIS_MODULE,
+ },
+ .probe = bq32k_probe,
+ .remove = __devexit_p(bq32k_remove),
+ .id_table = bq32k_id,
+};
+
+static __init int bq32k_init(void)
+{
+ return i2c_add_driver(&bq32k_driver);
+}
+module_init(bq32k_init);
+
+static __exit void bq32k_exit(void)
+{
+ i2c_del_driver(&bq32k_driver);
+}
+module_exit(bq32k_exit);
+
+MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>");
+MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c
index d00a274df8fc..280fe48ada0b 100644
--- a/drivers/rtc/rtc-bq4802.c
+++ b/drivers/rtc/rtc-bq4802.c
@@ -169,6 +169,8 @@ static int __devinit bq4802_probe(struct platform_device *pdev)
goto out_free;
}
+ platform_set_drvdata(pdev, p);
+
p->rtc = rtc_device_register("bq4802", &pdev->dev,
&bq4802_ops, THIS_MODULE);
if (IS_ERR(p->rtc)) {
@@ -176,7 +178,6 @@ static int __devinit bq4802_probe(struct platform_device *pdev)
goto out_iounmap;
}
- platform_set_drvdata(pdev, p);
err = 0;
out:
return err;
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index f7a4701bf863..eb154dc57164 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -420,49 +420,43 @@ static int cmos_irq_set_state(struct device *dev, int enabled)
return 0;
}
-#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
-
-static int
-cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned long flags;
- switch (cmd) {
- case RTC_AIE_OFF:
- case RTC_AIE_ON:
- case RTC_UIE_OFF:
- case RTC_UIE_ON:
- if (!is_valid_irq(cmos->irq))
- return -EINVAL;
- break;
- /* PIE ON/OFF is handled by cmos_irq_set_state() */
- default:
- return -ENOIOCTLCMD;
- }
+ if (!is_valid_irq(cmos->irq))
+ return -EINVAL;
spin_lock_irqsave(&rtc_lock, flags);
- switch (cmd) {
- case RTC_AIE_OFF: /* alarm off */
- cmos_irq_disable(cmos, RTC_AIE);
- break;
- case RTC_AIE_ON: /* alarm on */
+
+ if (enabled)
cmos_irq_enable(cmos, RTC_AIE);
- break;
- case RTC_UIE_OFF: /* update off */
- cmos_irq_disable(cmos, RTC_UIE);
- break;
- case RTC_UIE_ON: /* update on */
- cmos_irq_enable(cmos, RTC_UIE);
- break;
- }
+ else
+ cmos_irq_disable(cmos, RTC_AIE);
+
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
-#else
-#define cmos_rtc_ioctl NULL
-#endif
+static int cmos_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (!is_valid_irq(cmos->irq))
+ return -EINVAL;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+
+ if (enabled)
+ cmos_irq_enable(cmos, RTC_UIE);
+ else
+ cmos_irq_disable(cmos, RTC_UIE);
+
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ return 0;
+}
#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
@@ -503,14 +497,15 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
#endif
static const struct rtc_class_ops cmos_rtc_ops = {
- .ioctl = cmos_rtc_ioctl,
- .read_time = cmos_read_time,
- .set_time = cmos_set_time,
- .read_alarm = cmos_read_alarm,
- .set_alarm = cmos_set_alarm,
- .proc = cmos_procfs,
- .irq_set_freq = cmos_irq_set_freq,
- .irq_set_state = cmos_irq_set_state,
+ .read_time = cmos_read_time,
+ .set_time = cmos_set_time,
+ .read_alarm = cmos_read_alarm,
+ .set_alarm = cmos_set_alarm,
+ .proc = cmos_procfs,
+ .irq_set_freq = cmos_irq_set_freq,
+ .irq_set_state = cmos_irq_set_state,
+ .alarm_irq_enable = cmos_alarm_irq_enable,
+ .update_irq_enable = cmos_update_irq_enable,
};
/*----------------------------------------------------------------*/
@@ -871,8 +866,9 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)
mask = RTC_IRQMASK;
tmp &= ~mask;
CMOS_WRITE(tmp, RTC_CONTROL);
- hpet_mask_rtc_irq_bit(mask);
+ /* shut down hpet emulation - we don't need it for alarm */
+ hpet_mask_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
cmos_checkintr(cmos, tmp);
}
spin_unlock_irq(&rtc_lock);
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 1e73c8f42e38..532acf9b05d8 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -143,7 +143,6 @@ static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd,
#ifdef RTC_SET_CHARGE
case RTC_SET_CHARGE:
{
- struct ds1302_rtc *rtc = dev_get_drvdata(dev);
int tcs_val;
if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int)))
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 2736b11a1b1e..259db7f3535b 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -617,7 +617,6 @@ static struct bin_attribute nvram = {
static int __devinit ds1305_probe(struct spi_device *spi)
{
struct ds1305 *ds1305;
- struct rtc_device *rtc;
int status;
u8 addr, value;
struct ds1305_platform_data *pdata = spi->dev.platform_data;
@@ -756,14 +755,13 @@ static int __devinit ds1305_probe(struct spi_device *spi)
dev_dbg(&spi->dev, "AM/PM\n");
/* register RTC ... from here on, ds1305->ctrl needs locking */
- rtc = rtc_device_register("ds1305", &spi->dev,
+ ds1305->rtc = rtc_device_register("ds1305", &spi->dev,
&ds1305_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- status = PTR_ERR(rtc);
+ if (IS_ERR(ds1305->rtc)) {
+ status = PTR_ERR(ds1305->rtc);
dev_dbg(&spi->dev, "register rtc --> %d\n", status);
goto fail0;
}
- ds1305->rtc = rtc;
/* Maybe set up alarm IRQ; be ready to handle it triggering right
* away. NOTE that we don't share this. The signal is active low,
@@ -774,7 +772,7 @@ static int __devinit ds1305_probe(struct spi_device *spi)
if (spi->irq) {
INIT_WORK(&ds1305->work, ds1305_work);
status = request_irq(spi->irq, ds1305_irq,
- 0, dev_name(&rtc->dev), ds1305);
+ 0, dev_name(&ds1305->rtc->dev), ds1305);
if (status < 0) {
dev_dbg(&spi->dev, "request_irq %d --> %d\n",
spi->irq, status);
@@ -794,7 +792,7 @@ static int __devinit ds1305_probe(struct spi_device *spi)
fail2:
free_irq(spi->irq, ds1305);
fail1:
- rtc_device_unregister(rtc);
+ rtc_device_unregister(ds1305->rtc);
fail0:
kfree(ds1305);
return status;
@@ -802,7 +800,7 @@ fail0:
static int __devexit ds1305_remove(struct spi_device *spi)
{
- struct ds1305 *ds1305 = spi_get_drvdata(spi);
+ struct ds1305 *ds1305 = spi_get_drvdata(spi);
sysfs_remove_bin_file(&spi->dev.kobj, &nvram);
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index eb99ee4fa0f5..8a99da6f2f24 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -874,7 +874,7 @@ read_rtc:
}
if (want_irq) {
- err = request_irq(client->irq, ds1307_irq, 0,
+ err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,
ds1307->rtc->name, client);
if (err) {
dev_err(&client->dev,
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index 539676e25fd8..4166b84cb514 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -87,7 +87,6 @@ enum ds1511reg {
struct rtc_plat_data {
struct rtc_device *rtc;
void __iomem *ioaddr; /* virtual base address */
- unsigned long baseaddr; /* physical base address */
int size; /* amount of memory mapped */
int irq;
unsigned int irqen;
@@ -95,6 +94,7 @@ struct rtc_plat_data {
int alrm_min;
int alrm_hour;
int alrm_mday;
+ spinlock_t lock;
};
static DEFINE_SPINLOCK(ds1511_lock);
@@ -302,7 +302,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata)
{
unsigned long flags;
- spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
+ spin_lock_irqsave(&pdata->lock, flags);
rtc_write(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
0x80 : bin2bcd(pdata->alrm_mday) & 0x3f,
RTC_ALARM_DATE);
@@ -317,7 +317,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata)
RTC_ALARM_SEC);
rtc_write(rtc_read(RTC_CMD) | (pdata->irqen ? RTC_TIE : 0), RTC_CMD);
rtc_read(RTC_CMD1); /* clear interrupts */
- spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
+ spin_unlock_irqrestore(&pdata->lock, flags);
}
static int
@@ -362,61 +362,63 @@ ds1511_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
- unsigned long events = RTC_IRQF;
+ unsigned long events = 0;
+ spin_lock(&pdata->lock);
/*
* read and clear interrupt
*/
- if (!(rtc_read(RTC_CMD1) & DS1511_IRQF)) {
- return IRQ_NONE;
- }
- if (rtc_read(RTC_ALARM_SEC) & 0x80) {
- events |= RTC_UF;
- } else {
- events |= RTC_AF;
- }
- rtc_update_irq(pdata->rtc, 1, events);
- return IRQ_HANDLED;
+ if (rtc_read(RTC_CMD1) & DS1511_IRQF) {
+ events = RTC_IRQF;
+ if (rtc_read(RTC_ALARM_SEC) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ if (likely(pdata->rtc))
+ rtc_update_irq(pdata->rtc, 1, events);
+ }
+ spin_unlock(&pdata->lock);
+ return events ? IRQ_HANDLED : IRQ_NONE;
}
- static int
-ds1511_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+static int ds1511_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
- if (pdata->irq <= 0) {
- return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
- }
- switch (cmd) {
- case RTC_AIE_OFF:
- pdata->irqen &= ~RTC_AF;
- ds1511_rtc_update_alarm(pdata);
- break;
- case RTC_AIE_ON:
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ if (enabled)
pdata->irqen |= RTC_AF;
- ds1511_rtc_update_alarm(pdata);
- break;
- case RTC_UIE_OFF:
- pdata->irqen &= ~RTC_UF;
- ds1511_rtc_update_alarm(pdata);
- break;
- case RTC_UIE_ON:
+ else
+ pdata->irqen &= ~RTC_AF;
+ ds1511_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static int ds1511_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ if (enabled)
pdata->irqen |= RTC_UF;
- ds1511_rtc_update_alarm(pdata);
- break;
- default:
- return -ENOIOCTLCMD;
- }
+ else
+ pdata->irqen &= ~RTC_UF;
+ ds1511_rtc_update_alarm(pdata);
return 0;
}
static const struct rtc_class_ops ds1511_rtc_ops = {
- .read_time = ds1511_rtc_read_time,
- .set_time = ds1511_rtc_set_time,
- .read_alarm = ds1511_rtc_read_alarm,
- .set_alarm = ds1511_rtc_set_alarm,
- .ioctl = ds1511_rtc_ioctl,
+ .read_time = ds1511_rtc_read_time,
+ .set_time = ds1511_rtc_set_time,
+ .read_alarm = ds1511_rtc_read_alarm,
+ .set_alarm = ds1511_rtc_set_alarm,
+ .alarm_irq_enable = ds1511_rtc_alarm_irq_enable,
+ .update_irq_enable = ds1511_rtc_update_irq_enable,
};
static ssize_t
@@ -492,29 +494,23 @@ ds1511_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
- struct rtc_plat_data *pdata = NULL;
+ struct rtc_plat_data *pdata;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
return -ENODEV;
}
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
return -ENOMEM;
- }
pdata->size = res->end - res->start + 1;
- if (!request_mem_region(res->start, pdata->size, pdev->name)) {
- ret = -EBUSY;
- goto out;
- }
- pdata->baseaddr = res->start;
- pdata->size = pdata->size;
- ds1511_base = ioremap(pdata->baseaddr, pdata->size);
- if (!ds1511_base) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size,
+ pdev->name))
+ return -EBUSY;
+ ds1511_base = devm_ioremap(&pdev->dev, res->start, pdata->size);
+ if (!ds1511_base)
+ return -ENOMEM;
pdata->ioaddr = ds1511_base;
pdata->irq = platform_get_irq(pdev, 0);
@@ -540,13 +536,15 @@ ds1511_rtc_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "voltage-low detected.\n");
}
+ spin_lock_init(&pdata->lock);
+ platform_set_drvdata(pdev, pdata);
/*
* if the platform has an interrupt in mind for this device,
* then by all means, set it
*/
if (pdata->irq > 0) {
rtc_read(RTC_CMD1);
- if (request_irq(pdata->irq, ds1511_interrupt,
+ if (devm_request_irq(&pdev->dev, pdata->irq, ds1511_interrupt,
IRQF_DISABLED | IRQF_SHARED, pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
@@ -556,33 +554,13 @@ ds1511_rtc_probe(struct platform_device *pdev)
rtc = rtc_device_register(pdev->name, &pdev->dev, &ds1511_rtc_ops,
THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto out;
- }
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
pdata->rtc = rtc;
- platform_set_drvdata(pdev, pdata);
+
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
- if (ret) {
- goto out;
- }
- return 0;
- out:
- if (pdata->rtc) {
+ if (ret)
rtc_device_unregister(pdata->rtc);
- }
- if (pdata->irq > 0) {
- free_irq(pdata->irq, pdev);
- }
- if (ds1511_base) {
- iounmap(ds1511_base);
- ds1511_base = NULL;
- }
- if (pdata->baseaddr) {
- release_mem_region(pdata->baseaddr, pdata->size);
- }
-
- kfree(pdata);
return ret;
}
@@ -593,19 +571,13 @@ ds1511_rtc_remove(struct platform_device *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
rtc_device_unregister(pdata->rtc);
- pdata->rtc = NULL;
if (pdata->irq > 0) {
/*
* disable the alarm interrupt
*/
rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD);
rtc_read(RTC_CMD1);
- free_irq(pdata->irq, pdev);
}
- iounmap(pdata->ioaddr);
- ds1511_base = NULL;
- release_mem_region(pdata->baseaddr, pdata->size);
- kfree(pdata);
return 0;
}
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 717288527c6b..ed1ef7c9cc06 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -18,7 +18,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#define DRV_VERSION "0.2"
+#define DRV_VERSION "0.3"
#define RTC_REG_SIZE 0x2000
#define RTC_OFFSET 0x1ff0
@@ -61,7 +61,6 @@
struct rtc_plat_data {
struct rtc_device *rtc;
void __iomem *ioaddr;
- resource_size_t baseaddr;
unsigned long last_jiffies;
int irq;
unsigned int irqen;
@@ -69,6 +68,7 @@ struct rtc_plat_data {
int alrm_min;
int alrm_hour;
int alrm_mday;
+ spinlock_t lock;
};
static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -139,7 +139,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
void __iomem *ioaddr = pdata->ioaddr;
unsigned long flags;
- spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
+ spin_lock_irqsave(&pdata->lock, flags);
writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
0x80 : bin2bcd(pdata->alrm_mday),
ioaddr + RTC_DATE_ALARM);
@@ -154,7 +154,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
ioaddr + RTC_SECONDS_ALARM);
writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
readb(ioaddr + RTC_FLAGS); /* clear interrupts */
- spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
+ spin_unlock_irqrestore(&pdata->lock, flags);
}
static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -194,64 +194,69 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
struct platform_device *pdev = dev_id;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
- unsigned long events = RTC_IRQF;
+ unsigned long events = 0;
+ spin_lock(&pdata->lock);
/* read and clear interrupt */
- if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF))
- return IRQ_NONE;
- if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
- events |= RTC_UF;
- else
- events |= RTC_AF;
- rtc_update_irq(pdata->rtc, 1, events);
- return IRQ_HANDLED;
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
+ events = RTC_IRQF;
+ if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ if (likely(pdata->rtc))
+ rtc_update_irq(pdata->rtc, 1, events);
+ }
+ spin_unlock(&pdata->lock);
+ return events ? IRQ_HANDLED : IRQ_NONE;
}
-static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq <= 0)
- return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
- switch (cmd) {
- case RTC_AIE_OFF:
- pdata->irqen &= ~RTC_AF;
- ds1553_rtc_update_alarm(pdata);
- break;
- case RTC_AIE_ON:
+ return -EINVAL;
+ if (enabled)
pdata->irqen |= RTC_AF;
- ds1553_rtc_update_alarm(pdata);
- break;
- case RTC_UIE_OFF:
- pdata->irqen &= ~RTC_UF;
- ds1553_rtc_update_alarm(pdata);
- break;
- case RTC_UIE_ON:
+ else
+ pdata->irqen &= ~RTC_AF;
+ ds1553_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static int ds1553_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ if (enabled)
pdata->irqen |= RTC_UF;
- ds1553_rtc_update_alarm(pdata);
- break;
- default:
- return -ENOIOCTLCMD;
- }
+ else
+ pdata->irqen &= ~RTC_UF;
+ ds1553_rtc_update_alarm(pdata);
return 0;
}
static const struct rtc_class_ops ds1553_rtc_ops = {
- .read_time = ds1553_rtc_read_time,
- .set_time = ds1553_rtc_set_time,
- .read_alarm = ds1553_rtc_read_alarm,
- .set_alarm = ds1553_rtc_set_alarm,
- .ioctl = ds1553_rtc_ioctl,
+ .read_time = ds1553_rtc_read_time,
+ .set_time = ds1553_rtc_set_time,
+ .read_alarm = ds1553_rtc_read_alarm,
+ .set_alarm = ds1553_rtc_set_alarm,
+ .alarm_irq_enable = ds1553_rtc_alarm_irq_enable,
+ .update_irq_enable = ds1553_rtc_update_irq_enable,
};
static ssize_t ds1553_nvram_read(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
ssize_t count;
@@ -265,8 +270,8 @@ static ssize_t ds1553_nvram_write(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
ssize_t count;
@@ -291,26 +296,23 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
struct rtc_device *rtc;
struct resource *res;
unsigned int cen, sec;
- struct rtc_plat_data *pdata = NULL;
- void __iomem *ioaddr = NULL;
+ struct rtc_plat_data *pdata;
+ void __iomem *ioaddr;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
- ret = -EBUSY;
- goto out;
- }
- pdata->baseaddr = res->start;
- ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
- if (!ioaddr) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE,
+ pdev->name))
+ return -EBUSY;
+
+ ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE);
+ if (!ioaddr)
+ return -ENOMEM;
pdata->ioaddr = ioaddr;
pdata->irq = platform_get_irq(pdev, 0);
@@ -326,9 +328,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF)
dev_warn(&pdev->dev, "voltage-low detected.\n");
+ spin_lock_init(&pdata->lock);
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
if (pdata->irq > 0) {
writeb(0, ioaddr + RTC_INTERRUPTS);
- if (request_irq(pdata->irq, ds1553_rtc_interrupt,
+ if (devm_request_irq(&pdev->dev, pdata->irq,
+ ds1553_rtc_interrupt,
IRQF_DISABLED, pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = 0;
@@ -337,27 +343,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
rtc = rtc_device_register(pdev->name, &pdev->dev,
&ds1553_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto out;
- }
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
pdata->rtc = rtc;
- pdata->last_jiffies = jiffies;
- platform_set_drvdata(pdev, pdata);
+
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
if (ret)
- goto out;
- return 0;
- out:
- if (pdata->rtc)
- rtc_device_unregister(pdata->rtc);
- if (pdata->irq > 0)
- free_irq(pdata->irq, pdev);
- if (ioaddr)
- iounmap(ioaddr);
- if (pdata->baseaddr)
- release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
- kfree(pdata);
+ rtc_device_unregister(rtc);
return ret;
}
@@ -367,13 +359,8 @@ static int __devexit ds1553_rtc_remove(struct platform_device *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
rtc_device_unregister(pdata->rtc);
- if (pdata->irq > 0) {
+ if (pdata->irq > 0)
writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
- free_irq(pdata->irq, pdev);
- }
- iounmap(pdata->ioaddr);
- release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
- kfree(pdata);
return 0;
}
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 09249459e9a4..a1273360a44e 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -21,7 +21,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#define DRV_VERSION "0.3"
+#define DRV_VERSION "0.4"
#define RTC_SIZE 8
@@ -55,7 +55,6 @@ struct rtc_plat_data {
void __iomem *ioaddr_rtc;
size_t size_nvram;
size_t size;
- resource_size_t baseaddr;
unsigned long last_jiffies;
struct bin_attribute nvram_attr;
};
@@ -132,8 +131,8 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr_nvram;
ssize_t count;
@@ -147,8 +146,8 @@ static ssize_t ds1742_nvram_write(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr_nvram;
ssize_t count;
@@ -163,27 +162,24 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev)
struct rtc_device *rtc;
struct resource *res;
unsigned int cen, sec;
- struct rtc_plat_data *pdata = NULL;
- void __iomem *ioaddr = NULL;
+ struct rtc_plat_data *pdata;
+ void __iomem *ioaddr;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->size = res->end - res->start + 1;
- if (!request_mem_region(res->start, pdata->size, pdev->name)) {
- ret = -EBUSY;
- goto out;
- }
- pdata->baseaddr = res->start;
- ioaddr = ioremap(pdata->baseaddr, pdata->size);
- if (!ioaddr) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size,
+ pdev->name))
+ return -EBUSY;
+ ioaddr = devm_ioremap(&pdev->dev, res->start, pdata->size);
+ if (!ioaddr)
+ return -ENOMEM;
+
pdata->ioaddr_nvram = ioaddr;
pdata->size_nvram = pdata->size - RTC_SIZE;
pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
@@ -207,31 +203,19 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev)
if (!(readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG))
dev_warn(&pdev->dev, "voltage-low detected.\n");
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
rtc = rtc_device_register(pdev->name, &pdev->dev,
&ds1742_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto out;
- }
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
pdata->rtc = rtc;
- pdata->last_jiffies = jiffies;
- platform_set_drvdata(pdev, pdata);
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
if (ret) {
dev_err(&pdev->dev, "creating nvram file in sysfs failed\n");
- goto out;
+ rtc_device_unregister(rtc);
}
-
- return 0;
- out:
- if (pdata->rtc)
- rtc_device_unregister(pdata->rtc);
- if (pdata->ioaddr_nvram)
- iounmap(pdata->ioaddr_nvram);
- if (pdata->baseaddr)
- release_mem_region(pdata->baseaddr, pdata->size);
- kfree(pdata);
return ret;
}
@@ -241,9 +225,6 @@ static int __devexit ds1742_rtc_remove(struct platform_device *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
rtc_device_unregister(pdata->rtc);
- iounmap(pdata->ioaddr_nvram);
- release_mem_region(pdata->baseaddr, pdata->size);
- kfree(pdata);
return 0;
}
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 0b2197559940..8cb5b8959e5b 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -142,7 +142,6 @@ static const struct rtc_class_ops m48t35_ops = {
static int __devinit m48t35_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc;
struct resource *res;
struct m48t35_priv *priv;
int ret = 0;
@@ -171,20 +170,21 @@ static int __devinit m48t35_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto out;
}
+
spin_lock_init(&priv->lock);
- rtc = rtc_device_register("m48t35", &pdev->dev,
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->rtc = rtc_device_register("m48t35", &pdev->dev,
&m48t35_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
+ if (IS_ERR(priv->rtc)) {
+ ret = PTR_ERR(priv->rtc);
goto out;
}
- priv->rtc = rtc;
- platform_set_drvdata(pdev, priv);
+
return 0;
out:
- if (priv->rtc)
- rtc_device_unregister(priv->rtc);
if (priv->reg)
iounmap(priv->reg);
if (priv->baseaddr)
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index 33921a6b1707..ede43b846859 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -481,6 +481,9 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
goto out;
}
+ spin_lock_init(&m48t59->lock);
+ platform_set_drvdata(pdev, m48t59);
+
m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE);
if (IS_ERR(m48t59->rtc)) {
ret = PTR_ERR(m48t59->rtc);
@@ -490,16 +493,14 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
m48t59_nvram_attr.size = pdata->offset;
ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
- if (ret)
+ if (ret) {
+ rtc_device_unregister(m48t59->rtc);
goto out;
+ }
- spin_lock_init(&m48t59->lock);
- platform_set_drvdata(pdev, m48t59);
return 0;
out:
- if (!IS_ERR(m48t59->rtc))
- rtc_device_unregister(m48t59->rtc);
if (m48t59->irq != NO_IRQ)
free_irq(m48t59->irq, &pdev->dev);
if (m48t59->ioaddr)
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
new file mode 100644
index 000000000000..850f983c039c
--- /dev/null
+++ b/drivers/rtc/rtc-mc13783.c
@@ -0,0 +1,262 @@
+/*
+ * Real Time Clock driver for Freescale MC13783 PMIC
+ *
+ * (C) 2009 Sascha Hauer, Pengutronix
+ * (C) 2009 Uwe Kleine-Koenig, Pengutronix
+ *
+ * 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/mfd/mc13783.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#define DRIVER_NAME "mc13783-rtc"
+
+#define MC13783_RTCTOD 20
+#define MC13783_RTCTODA 21
+#define MC13783_RTCDAY 22
+#define MC13783_RTCDAYA 23
+
+struct mc13783_rtc {
+ struct rtc_device *rtc;
+ struct mc13783 *mc13783;
+ int valid;
+};
+
+static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mc13783_rtc *priv = dev_get_drvdata(dev);
+ unsigned int seconds, days1, days2;
+ unsigned long s1970;
+ int ret;
+
+ mc13783_lock(priv->mc13783);
+
+ if (!priv->valid) {
+ ret = -ENODATA;
+ goto out;
+ }
+
+ ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
+out:
+ mc13783_unlock(priv->mc13783);
+
+ if (ret)
+ return ret;
+
+ if (days2 == days1 + 1) {
+ if (seconds >= 86400 / 2)
+ days2 = days1;
+ else
+ days1 = days2;
+ }
+
+ if (days1 != days2)
+ return -EIO;
+
+ s1970 = days1 * 86400 + seconds;
+
+ rtc_time_to_tm(s1970, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct mc13783_rtc *priv = dev_get_drvdata(dev);
+ unsigned int seconds, days;
+ int ret;
+
+ seconds = secs % 86400;
+ days = secs / 86400;
+
+ mc13783_lock(priv->mc13783);
+
+ /*
+ * first write seconds=0 to prevent a day switch between writing days
+ * and seconds below
+ */
+ ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
+out:
+ priv->valid = !ret;
+
+ mc13783_unlock(priv->mc13783);
+
+ return ret;
+}
+
+static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
+{
+ struct mc13783_rtc *priv = dev;
+ struct mc13783 *mc13783 = priv->mc13783;
+
+ dev_dbg(&priv->rtc->dev, "1HZ\n");
+
+ rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+ mc13783_ackirq(mc13783, irq);
+
+ return IRQ_HANDLED;
+}
+
+static int mc13783_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mc13783_rtc *priv = dev_get_drvdata(dev);
+ int ret = -ENODATA;
+
+ mc13783_lock(priv->mc13783);
+ if (!priv->valid)
+ goto out;
+
+ ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
+ MC13783_IRQ_1HZ);
+out:
+ mc13783_unlock(priv->mc13783);
+
+ return ret;
+}
+
+static const struct rtc_class_ops mc13783_rtc_ops = {
+ .read_time = mc13783_rtc_read_time,
+ .set_mmss = mc13783_rtc_set_mmss,
+ .update_irq_enable = mc13783_rtc_update_irq_enable,
+};
+
+static irqreturn_t mc13783_rtc_reset_handler(int irq, void *dev)
+{
+ struct mc13783_rtc *priv = dev;
+ struct mc13783 *mc13783 = priv->mc13783;
+
+ dev_dbg(&priv->rtc->dev, "RTCRST\n");
+ priv->valid = 0;
+
+ mc13783_mask(mc13783, irq);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mc13783_rtc *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, priv);
+
+ priv->valid = 1;
+
+ mc13783_lock(priv->mc13783);
+
+ ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
+ mc13783_rtc_reset_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_reset_irq_request;
+
+ ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
+ mc13783_rtc_update_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_update_irq_request;
+
+ mc13783_unlock(priv->mc13783);
+
+ priv->rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(priv->rtc)) {
+ ret = PTR_ERR(priv->rtc);
+
+ mc13783_lock(priv->mc13783);
+
+ mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+err_update_irq_request:
+
+ mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+err_reset_irq_request:
+
+ mc13783_unlock(priv->mc13783);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
+{
+ struct mc13783_rtc *priv = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(priv->rtc);
+
+ mc13783_lock(priv->mc13783);
+
+ mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+ mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+
+ mc13783_unlock(priv->mc13783);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_rtc_driver = {
+ .remove = __devexit_p(mc13783_rtc_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mc13783_rtc_init(void)
+{
+ return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
+}
+module_init(mc13783_rtc_init);
+
+static void __exit mc13783_rtc_exit(void)
+{
+ platform_driver_unregister(&mc13783_rtc_driver);
+}
+module_exit(mc13783_rtc_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index e0263d2005ee..dc052ce6e63a 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -27,10 +27,17 @@
#define RTC_MONTH_OFFS 8
#define RTC_YEAR_OFFS 16
+#define RTC_ALARM_TIME_REG_OFFS 8
+#define RTC_ALARM_DATE_REG_OFFS 0xc
+#define RTC_ALARM_VALID (1 << 7)
+
+#define RTC_ALARM_INTERRUPT_MASK_REG_OFFS 0x10
+#define RTC_ALARM_INTERRUPT_CASUE_REG_OFFS 0x14
struct rtc_plat_data {
struct rtc_device *rtc;
void __iomem *ioaddr;
+ int irq;
};
static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -84,12 +91,134 @@ static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm)
return rtc_valid_tm(tm);
}
+static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 rtc_time, rtc_date;
+ unsigned int year, month, day, hour, minute, second, wday;
+
+ rtc_time = readl(ioaddr + RTC_ALARM_TIME_REG_OFFS);
+ rtc_date = readl(ioaddr + RTC_ALARM_DATE_REG_OFFS);
+
+ second = rtc_time & 0x7f;
+ minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
+ hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */
+ wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
+
+ day = rtc_date & 0x3f;
+ month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f;
+ year = (rtc_date >> RTC_YEAR_OFFS) & 0xff;
+
+ alm->time.tm_sec = bcd2bin(second);
+ alm->time.tm_min = bcd2bin(minute);
+ alm->time.tm_hour = bcd2bin(hour);
+ alm->time.tm_mday = bcd2bin(day);
+ alm->time.tm_wday = bcd2bin(wday);
+ alm->time.tm_mon = bcd2bin(month) - 1;
+ /* hw counts from year 2000, but tm_year is relative to 1900 */
+ alm->time.tm_year = bcd2bin(year) + 100;
+
+ if (rtc_valid_tm(&alm->time) < 0) {
+ dev_err(dev, "retrieved alarm date/time is not valid.\n");
+ rtc_time_to_tm(0, &alm->time);
+ }
+
+ alm->enabled = !!readl(ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ return 0;
+}
+
+static int mv_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 rtc_reg = 0;
+
+ if (alm->time.tm_sec >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_sec))
+ << RTC_SECONDS_OFFS;
+ if (alm->time.tm_min >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_min))
+ << RTC_MINUTES_OFFS;
+ if (alm->time.tm_hour >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_hour))
+ << RTC_HOURS_OFFS;
+
+ writel(rtc_reg, ioaddr + RTC_ALARM_TIME_REG_OFFS);
+
+ if (alm->time.tm_mday >= 0)
+ rtc_reg = (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mday))
+ << RTC_MDAY_OFFS;
+ else
+ rtc_reg = 0;
+
+ if (alm->time.tm_mon >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mon + 1))
+ << RTC_MONTH_OFFS;
+
+ if (alm->time.tm_year >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year % 100))
+ << RTC_YEAR_OFFS;
+
+ writel(rtc_reg, ioaddr + RTC_ALARM_DATE_REG_OFFS);
+ writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
+ writel(alm->enabled ? 1 : 0,
+ ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+
+ return 0;
+}
+
+static int mv_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ if (pdata->irq < 0)
+ return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ writel(0, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ break;
+ case RTC_AIE_ON:
+ writel(1, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static irqreturn_t mv_rtc_interrupt(int irq, void *data)
+{
+ struct rtc_plat_data *pdata = data;
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ /* alarm irq? */
+ if (!readl(ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS))
+ return IRQ_NONE;
+
+ /* clear interrupt */
+ writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
+ rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
static const struct rtc_class_ops mv_rtc_ops = {
.read_time = mv_rtc_read_time,
.set_time = mv_rtc_set_time,
};
-static int __init mv_rtc_probe(struct platform_device *pdev)
+static const struct rtc_class_ops mv_rtc_alarm_ops = {
+ .read_time = mv_rtc_read_time,
+ .set_time = mv_rtc_set_time,
+ .read_alarm = mv_rtc_read_alarm,
+ .set_alarm = mv_rtc_set_alarm,
+ .ioctl = mv_rtc_ioctl,
+};
+
+static int __devinit mv_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
struct rtc_plat_data *pdata;
@@ -130,12 +259,31 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
}
}
+ pdata->irq = platform_get_irq(pdev, 0);
+
platform_set_drvdata(pdev, pdata);
- pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
- &mv_rtc_ops, THIS_MODULE);
+
+ if (pdata->irq >= 0) {
+ device_init_wakeup(&pdev->dev, 1);
+ pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &mv_rtc_alarm_ops,
+ THIS_MODULE);
+ } else
+ pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &mv_rtc_ops, THIS_MODULE);
if (IS_ERR(pdata->rtc))
return PTR_ERR(pdata->rtc);
+ if (pdata->irq >= 0) {
+ writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt,
+ IRQF_DISABLED | IRQF_SHARED,
+ pdev->name, pdata) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ }
+ }
+
return 0;
}
@@ -143,6 +291,9 @@ static int __exit mv_rtc_remove(struct platform_device *pdev)
{
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ if (pdata->irq >= 0)
+ device_init_wakeup(&pdev->dev, 0);
+
rtc_device_unregister(pdata->rtc);
return 0;
}
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
new file mode 100644
index 000000000000..bf59c9c586b2
--- /dev/null
+++ b/drivers/rtc/rtc-nuc900.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2008-2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@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 of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+
+/* RTC Control Registers */
+#define REG_RTC_INIR 0x00
+#define REG_RTC_AER 0x04
+#define REG_RTC_FCR 0x08
+#define REG_RTC_TLR 0x0C
+#define REG_RTC_CLR 0x10
+#define REG_RTC_TSSR 0x14
+#define REG_RTC_DWR 0x18
+#define REG_RTC_TAR 0x1C
+#define REG_RTC_CAR 0x20
+#define REG_RTC_LIR 0x24
+#define REG_RTC_RIER 0x28
+#define REG_RTC_RIIR 0x2C
+#define REG_RTC_TTR 0x30
+
+#define RTCSET 0x01
+#define AERRWENB 0x10000
+#define INIRRESET 0xa5eb1357
+#define AERPOWERON 0xA965
+#define AERPOWEROFF 0x0000
+#define LEAPYEAR 0x0001
+#define TICKENB 0x80
+#define TICKINTENB 0x0002
+#define ALARMINTENB 0x0001
+#define MODE24 0x0001
+
+struct nuc900_rtc {
+ int irq_num;
+ void __iomem *rtc_reg;
+ struct rtc_device *rtcdev;
+};
+
+struct nuc900_bcd_time {
+ int bcd_sec;
+ int bcd_min;
+ int bcd_hour;
+ int bcd_mday;
+ int bcd_mon;
+ int bcd_year;
+};
+
+static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
+{
+ struct nuc900_rtc *rtc = _rtc;
+ unsigned long events = 0, rtc_irq;
+
+ rtc_irq = __raw_readl(rtc->rtc_reg + REG_RTC_RIIR);
+
+ if (rtc_irq & ALARMINTENB) {
+ rtc_irq &= ~ALARMINTENB;
+ __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
+ events |= RTC_AF | RTC_IRQF;
+ }
+
+ if (rtc_irq & TICKINTENB) {
+ rtc_irq &= ~TICKINTENB;
+ __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
+ events |= RTC_UF | RTC_IRQF;
+ }
+
+ rtc_update_irq(rtc->rtcdev, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
+{
+ unsigned int i;
+ __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;
+ }
+
+ if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0)
+ return ERR_PTR(-ENODEV);
+
+ return ERR_PTR(-EPERM);
+}
+
+static void nuc900_rtc_bcd2bin(unsigned int timereg,
+ unsigned int calreg, struct rtc_time *tm)
+{
+ tm->tm_mday = bcd2bin(calreg >> 0);
+ tm->tm_mon = bcd2bin(calreg >> 8);
+ tm->tm_year = bcd2bin(calreg >> 16) + 100;
+
+ tm->tm_sec = bcd2bin(timereg >> 0);
+ tm->tm_min = bcd2bin(timereg >> 8);
+ tm->tm_hour = bcd2bin(timereg >> 16);
+
+ rtc_valid_tm(tm);
+}
+
+static void nuc900_rtc_bin2bcd(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;
+
+ gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
+ gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
+ gettm->bcd_hour = bin2bcd(settm->tm_hour) << 16;
+}
+
+static int nuc900_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
+ (TICKINTENB), rtc->rtc_reg + REG_RTC_RIER);
+ else
+ __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
+ (~TICKINTENB), rtc->rtc_reg + REG_RTC_RIER);
+
+ return 0;
+}
+
+static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
+ (ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
+ else
+ __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
+ (~ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
+
+ return 0;
+}
+
+static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int timeval, clrval;
+
+ 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;
+}
+
+static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ struct nuc900_bcd_time gettm;
+ unsigned long val;
+ int *err;
+
+ nuc900_rtc_bin2bcd(tm, &gettm);
+
+ err = check_rtc_access_enable(rtc);
+ if (IS_ERR(err))
+ return PTR_ERR(err);
+
+ val = gettm.bcd_mday | gettm.bcd_mon | gettm.bcd_year;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_CLR);
+
+ val = gettm.bcd_sec | gettm.bcd_min | gettm.bcd_hour;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_TLR);
+
+ return 0;
+}
+
+static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int timeval, carval;
+
+ 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;
+}
+
+static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ struct nuc900_bcd_time tm;
+ unsigned long val;
+ int *err;
+
+ nuc900_rtc_bin2bcd(&alrm->time, &tm);
+
+ err = check_rtc_access_enable(rtc);
+ if (IS_ERR(err))
+ return PTR_ERR(err);
+
+ val = tm.bcd_mday | tm.bcd_mon | tm.bcd_year;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_CAR);
+
+ val = tm.bcd_sec | tm.bcd_min | tm.bcd_hour;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_TAR);
+
+ return 0;
+}
+
+static struct rtc_class_ops nuc900_rtc_ops = {
+ .read_time = nuc900_rtc_read_time,
+ .set_time = nuc900_rtc_set_time,
+ .read_alarm = nuc900_rtc_read_alarm,
+ .set_alarm = nuc900_rtc_set_alarm,
+ .alarm_irq_enable = nuc900_alarm_irq_enable,
+ .update_irq_enable = nuc900_update_irq_enable,
+};
+
+static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct nuc900_rtc *nuc900_rtc;
+ int err = 0;
+
+ nuc900_rtc = kzalloc(sizeof(struct nuc900_rtc), GFP_KERNEL);
+ if (!nuc900_rtc) {
+ dev_err(&pdev->dev, "kzalloc nuc900_rtc failed\n");
+ return -ENOMEM;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "platform_get_resource failed\n");
+ err = -ENXIO;
+ goto fail1;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "request_mem_region failed\n");
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ nuc900_rtc->rtc_reg = ioremap(res->start, resource_size(res));
+ if (!nuc900_rtc->rtc_reg) {
+ dev_err(&pdev->dev, "ioremap rtc_reg failed\n");
+ err = -ENOMEM;
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+
+ return 0;
+
+fail4: free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+fail3: iounmap(nuc900_rtc->rtc_reg);
+fail2: release_mem_region(res->start, resource_size(res));
+fail1: kfree(nuc900_rtc);
+ return err;
+}
+
+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);
+ iounmap(nuc900_rtc->rtc_reg);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(nuc900_rtc);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver nuc900_rtc_driver = {
+ .remove = __devexit_p(nuc900_rtc_remove),
+ .driver = {
+ .name = "nuc900-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init nuc900_rtc_init(void)
+{
+ return platform_driver_probe(&nuc900_rtc_driver, nuc900_rtc_probe);
+}
+
+static void __exit nuc900_rtc_exit(void)
+{
+ platform_driver_unregister(&nuc900_rtc_driver);
+}
+
+module_init(nuc900_rtc_init);
+module_exit(nuc900_rtc_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-rtc");
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 0587d53987fe..64d9727b7229 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -87,9 +87,10 @@
#define OMAP_RTC_INTERRUPTS_IT_ALARM (1<<3)
#define OMAP_RTC_INTERRUPTS_IT_TIMER (1<<2)
+static void __iomem *rtc_base;
-#define rtc_read(addr) omap_readb(OMAP_RTC_BASE + (addr))
-#define rtc_write(val, addr) omap_writeb(val, OMAP_RTC_BASE + (addr))
+#define rtc_read(addr) __raw_readb(rtc_base + (addr))
+#define rtc_write(val, addr) __raw_writeb(val, rtc_base + (addr))
/* we rely on the rtc framework to handle locking (rtc->ops_lock),
@@ -330,32 +331,31 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
return -ENOENT;
}
- /* NOTE: using static mapping for RTC registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res && res->start != OMAP_RTC_BASE) {
- pr_debug("%s: RTC registers at %08x, expected %08x\n",
- pdev->name, (unsigned) res->start, OMAP_RTC_BASE);
+ if (!res) {
+ pr_debug("%s: RTC resource data missing\n", pdev->name);
return -ENOENT;
}
- if (res)
- mem = request_mem_region(res->start,
- res->end - res->start + 1,
- pdev->name);
- else
- mem = NULL;
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
if (!mem) {
pr_debug("%s: RTC registers at %08x are not free\n",
- pdev->name, OMAP_RTC_BASE);
+ pdev->name, res->start);
return -EBUSY;
}
+ rtc_base = ioremap(res->start, resource_size(res));
+ if (!rtc_base) {
+ pr_debug("%s: RTC registers can't be mapped\n", pdev->name);
+ goto fail;
+ }
+
rtc = rtc_device_register(pdev->name, &pdev->dev,
&omap_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
pr_debug("%s: can't register RTC device, err %ld\n",
pdev->name, PTR_ERR(rtc));
- goto fail;
+ goto fail0;
}
platform_set_drvdata(pdev, rtc);
dev_set_drvdata(&rtc->dev, mem);
@@ -380,13 +380,14 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
dev_name(&rtc->dev), rtc)) {
pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_timer);
- goto fail0;
+ goto fail1;
}
- if (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,
- dev_name(&rtc->dev), rtc)) {
+ if ((omap_rtc_timer != omap_rtc_alarm) &&
+ (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,
+ dev_name(&rtc->dev), rtc))) {
pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_alarm);
- goto fail1;
+ goto fail2;
}
/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
@@ -419,10 +420,12 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
return 0;
-fail1:
+fail2:
free_irq(omap_rtc_timer, NULL);
-fail0:
+fail1:
rtc_device_unregister(rtc);
+fail0:
+ iounmap(rtc_base);
fail:
release_resource(mem);
return -EIO;
@@ -438,7 +441,9 @@ static int __exit omap_rtc_remove(struct platform_device *pdev)
rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
free_irq(omap_rtc_timer, rtc);
- free_irq(omap_rtc_alarm, rtc);
+
+ if (omap_rtc_timer != omap_rtc_alarm)
+ free_irq(omap_rtc_alarm, rtc);
release_resource(dev_get_drvdata(&rtc->dev));
rtc_device_unregister(rtc);
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
index 4c5d5d0c4cfc..854c3cb365a1 100644
--- a/drivers/rtc/rtc-pcf50633.c
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -58,6 +58,7 @@ struct pcf50633_time {
struct pcf50633_rtc {
int alarm_enabled;
int second_enabled;
+ int alarm_pending;
struct pcf50633 *pcf;
struct rtc_device *rtc_dev;
@@ -209,6 +210,7 @@ static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
rtc = dev_get_drvdata(dev);
alrm->enabled = rtc->alarm_enabled;
+ alrm->pending = rtc->alarm_pending;
ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
PCF50633_TI_EXTENT, &pcf_tm.time[0]);
@@ -244,6 +246,8 @@ static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
/* Returns 0 on success */
ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
PCF50633_TI_EXTENT, &pcf_tm.time[0]);
+ if (!alrm->enabled)
+ rtc->alarm_pending = 0;
if (!alarm_masked || alrm->enabled)
pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
@@ -268,6 +272,7 @@ static void pcf50633_rtc_irq(int irq, void *data)
switch (irq) {
case PCF50633_IRQ_ALARM:
rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ rtc->alarm_pending = 1;
break;
case PCF50633_IRQ_SECOND:
rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
@@ -277,16 +282,13 @@ static void pcf50633_rtc_irq(int irq, void *data)
static int __devinit pcf50633_rtc_probe(struct platform_device *pdev)
{
- struct pcf50633_subdev_pdata *pdata;
struct pcf50633_rtc *rtc;
-
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
- pdata = pdev->dev.platform_data;
- rtc->pcf = pdata->pcf;
+ rtc->pcf = dev_to_pcf50633(pdev->dev.parent);
platform_set_drvdata(pdev, rtc);
rtc->rtc_dev = rtc_device_register("pcf50633-rtc", &pdev->dev,
&pcf50633_rtc_ops, THIS_MODULE);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index b725913ccbe8..65f346b2fbae 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -212,6 +212,8 @@ static int pcf8563_probe(struct i2c_client *client,
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+ i2c_set_clientdata(client, pcf8563);
+
pcf8563->rtc = rtc_device_register(pcf8563_driver.driver.name,
&client->dev, &pcf8563_rtc_ops, THIS_MODULE);
@@ -220,8 +222,6 @@ static int pcf8563_probe(struct i2c_client *client,
goto exit_kfree;
}
- i2c_set_clientdata(client, pcf8563);
-
return 0;
exit_kfree:
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index 7d33cda3f8f6..2d201afead3b 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -277,6 +277,8 @@ static int pcf8583_probe(struct i2c_client *client,
if (!pcf8583)
return -ENOMEM;
+ i2c_set_clientdata(client, pcf8583);
+
pcf8583->rtc = rtc_device_register(pcf8583_driver.driver.name,
&client->dev, &pcf8583_rtc_ops, THIS_MODULE);
@@ -285,7 +287,6 @@ static int pcf8583_probe(struct i2c_client *client,
goto exit_kfree;
}
- i2c_set_clientdata(client, pcf8583);
return 0;
exit_kfree:
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index f41873f98f66..0264b117893b 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -51,10 +51,10 @@ static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
switch (cmd) {
case RTC_AIE_OFF:
- __raw_writel(1, ldata->base + RTC_MIS);
+ writel(1, ldata->base + RTC_MIS);
return 0;
case RTC_AIE_ON:
- __raw_writel(0, ldata->base + RTC_MIS);
+ writel(0, ldata->base + RTC_MIS);
return 0;
}
@@ -65,7 +65,7 @@ static int pl031_read_time(struct device *dev, struct rtc_time *tm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
- rtc_time_to_tm(__raw_readl(ldata->base + RTC_DR), tm);
+ rtc_time_to_tm(readl(ldata->base + RTC_DR), tm);
return 0;
}
@@ -76,7 +76,7 @@ static int pl031_set_time(struct device *dev, struct rtc_time *tm)
struct pl031_local *ldata = dev_get_drvdata(dev);
rtc_tm_to_time(tm, &time);
- __raw_writel(time, ldata->base + RTC_LR);
+ writel(time, ldata->base + RTC_LR);
return 0;
}
@@ -85,9 +85,9 @@ static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
- rtc_time_to_tm(__raw_readl(ldata->base + RTC_MR), &alarm->time);
- alarm->pending = __raw_readl(ldata->base + RTC_RIS);
- alarm->enabled = __raw_readl(ldata->base + RTC_IMSC);
+ rtc_time_to_tm(readl(ldata->base + RTC_MR), &alarm->time);
+ alarm->pending = readl(ldata->base + RTC_RIS);
+ alarm->enabled = readl(ldata->base + RTC_IMSC);
return 0;
}
@@ -99,8 +99,8 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
rtc_tm_to_time(&alarm->time, &time);
- __raw_writel(time, ldata->base + RTC_MR);
- __raw_writel(!alarm->enabled, ldata->base + RTC_MIS);
+ writel(time, ldata->base + RTC_MR);
+ writel(!alarm->enabled, ldata->base + RTC_MIS);
return 0;
}
@@ -180,8 +180,9 @@ err_req:
static struct amba_id pl031_ids[] __initdata = {
{
- .id = 0x00041031,
- .mask = 0x000fffff, },
+ .id = 0x00041031,
+ .mask = 0x000fffff,
+ },
{0, 0},
};
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index 747ca194fad4..e6351b743da6 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -456,7 +456,7 @@ static int pxa_rtc_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops pxa_rtc_pm_ops = {
+static const struct dev_pm_ops pxa_rtc_pm_ops = {
.suspend = pxa_rtc_suspend,
.resume = pxa_rtc_resume,
};
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index 29f98a70586e..e4a44b641702 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -407,7 +407,7 @@ static int sa1100_rtc_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops sa1100_rtc_pm_ops = {
+static const struct dev_pm_ops sa1100_rtc_pm_ops = {
.suspend = sa1100_rtc_suspend,
.resume = sa1100_rtc_resume,
};
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index e6ed5404bca0..e95cc6f8d61e 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -826,7 +826,7 @@ static int sh_rtc_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops sh_rtc_dev_pm_ops = {
+static const struct dev_pm_ops sh_rtc_dev_pm_ops = {
.suspend = sh_rtc_suspend,
.resume = sh_rtc_resume,
};
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index d491eb265c38..67700831b5c9 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -62,7 +62,6 @@
struct rtc_plat_data {
struct rtc_device *rtc;
void __iomem *ioaddr;
- unsigned long baseaddr;
unsigned long last_jiffies;
int irq;
unsigned int irqen;
@@ -70,6 +69,7 @@ struct rtc_plat_data {
int alrm_min;
int alrm_hour;
int alrm_mday;
+ spinlock_t lock;
};
static int stk17ta8_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -142,7 +142,7 @@ static void stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata)
unsigned long irqflags;
u8 flags;
- spin_lock_irqsave(&pdata->rtc->irq_lock, irqflags);
+ spin_lock_irqsave(&pdata->lock, irqflags);
flags = readb(ioaddr + RTC_FLAGS);
writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
@@ -162,7 +162,7 @@ static void stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata)
writeb(pdata->irqen ? RTC_INTS_AIE : 0, ioaddr + RTC_INTERRUPTS);
readb(ioaddr + RTC_FLAGS); /* clear interrupts */
writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
- spin_unlock_irqrestore(&pdata->rtc->irq_lock, irqflags);
+ spin_unlock_irqrestore(&pdata->lock, irqflags);
}
static int stk17ta8_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -202,56 +202,53 @@ static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id)
struct platform_device *pdev = dev_id;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
- unsigned long events = RTC_IRQF;
+ unsigned long events = 0;
+ spin_lock(&pdata->lock);
/* read and clear interrupt */
- if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF))
- return IRQ_NONE;
- if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
- events |= RTC_UF;
- else
- events |= RTC_AF;
- rtc_update_irq(pdata->rtc, 1, events);
- return IRQ_HANDLED;
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
+ events = RTC_IRQF;
+ if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ if (likely(pdata->rtc))
+ rtc_update_irq(pdata->rtc, 1, events);
+ }
+ spin_unlock(&pdata->lock);
+ return events ? IRQ_HANDLED : IRQ_NONE;
}
-static int stk17ta8_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int stk17ta8_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq <= 0)
- return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
- switch (cmd) {
- case RTC_AIE_OFF:
- pdata->irqen &= ~RTC_AF;
- stk17ta8_rtc_update_alarm(pdata);
- break;
- case RTC_AIE_ON:
+ return -EINVAL;
+ if (enabled)
pdata->irqen |= RTC_AF;
- stk17ta8_rtc_update_alarm(pdata);
- break;
- default:
- return -ENOIOCTLCMD;
- }
+ else
+ pdata->irqen &= ~RTC_AF;
+ stk17ta8_rtc_update_alarm(pdata);
return 0;
}
static const struct rtc_class_ops stk17ta8_rtc_ops = {
- .read_time = stk17ta8_rtc_read_time,
- .set_time = stk17ta8_rtc_set_time,
- .read_alarm = stk17ta8_rtc_read_alarm,
- .set_alarm = stk17ta8_rtc_set_alarm,
- .ioctl = stk17ta8_rtc_ioctl,
+ .read_time = stk17ta8_rtc_read_time,
+ .set_time = stk17ta8_rtc_set_time,
+ .read_alarm = stk17ta8_rtc_read_alarm,
+ .set_alarm = stk17ta8_rtc_set_alarm,
+ .alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable,
};
static ssize_t stk17ta8_nvram_read(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
ssize_t count;
@@ -265,8 +262,8 @@ static ssize_t stk17ta8_nvram_write(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t size)
{
- struct platform_device *pdev =
- to_platform_device(container_of(kobj, struct device, kobj));
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
ssize_t count;
@@ -288,31 +285,26 @@ static struct bin_attribute stk17ta8_nvram_attr = {
static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc;
struct resource *res;
unsigned int cal;
unsigned int flags;
struct rtc_plat_data *pdata;
- void __iomem *ioaddr = NULL;
+ void __iomem *ioaddr;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
- ret = -EBUSY;
- goto out;
- }
- pdata->baseaddr = res->start;
- ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
- if (!ioaddr) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE,
+ pdev->name))
+ return -EBUSY;
+ ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE);
+ if (!ioaddr)
+ return -ENOMEM;
pdata->ioaddr = ioaddr;
pdata->irq = platform_get_irq(pdev, 0);
@@ -328,9 +320,13 @@ static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev)
if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_PF)
dev_warn(&pdev->dev, "voltage-low detected.\n");
+ spin_lock_init(&pdata->lock);
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
if (pdata->irq > 0) {
writeb(0, ioaddr + RTC_INTERRUPTS);
- if (request_irq(pdata->irq, stk17ta8_rtc_interrupt,
+ if (devm_request_irq(&pdev->dev, pdata->irq,
+ stk17ta8_rtc_interrupt,
IRQF_DISABLED | IRQF_SHARED,
pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
@@ -338,29 +334,14 @@ static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev)
}
}
- rtc = rtc_device_register(pdev->name, &pdev->dev,
+ pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
&stk17ta8_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto out;
- }
- pdata->rtc = rtc;
- pdata->last_jiffies = jiffies;
- platform_set_drvdata(pdev, pdata);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
if (ret)
- goto out;
- return 0;
- out:
- if (pdata->rtc)
rtc_device_unregister(pdata->rtc);
- if (pdata->irq > 0)
- free_irq(pdata->irq, pdev);
- if (ioaddr)
- iounmap(ioaddr);
- if (pdata->baseaddr)
- release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
- kfree(pdata);
return ret;
}
@@ -370,13 +351,8 @@ static int __devexit stk17ta8_rtc_remove(struct platform_device *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
rtc_device_unregister(pdata->rtc);
- if (pdata->irq > 0) {
+ if (pdata->irq > 0)
writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
- free_irq(pdata->irq, pdev);
- }
- iounmap(pdata->ioaddr);
- release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
- kfree(pdata);
return 0;
}
diff --git a/drivers/rtc/rtc-twl4030.c b/drivers/rtc/rtc-twl.c
index 9c8c70c497dc..c6a83a2a722c 100644
--- a/drivers/rtc/rtc-twl4030.c
+++ b/drivers/rtc/rtc-twl.c
@@ -1,5 +1,5 @@
/*
- * rtc-twl4030.c -- TWL4030 Real Time Clock interface
+ * rtc-twl.c -- TWL Real Time Clock interface
*
* Copyright (C) 2007 MontaVista Software, Inc
* Author: Alexandre Rusev <source@mvista.com>
@@ -28,33 +28,81 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
/*
* RTC block register offsets (use TWL_MODULE_RTC)
*/
-#define REG_SECONDS_REG 0x00
-#define REG_MINUTES_REG 0x01
-#define REG_HOURS_REG 0x02
-#define REG_DAYS_REG 0x03
-#define REG_MONTHS_REG 0x04
-#define REG_YEARS_REG 0x05
-#define REG_WEEKS_REG 0x06
-
-#define REG_ALARM_SECONDS_REG 0x07
-#define REG_ALARM_MINUTES_REG 0x08
-#define REG_ALARM_HOURS_REG 0x09
-#define REG_ALARM_DAYS_REG 0x0A
-#define REG_ALARM_MONTHS_REG 0x0B
-#define REG_ALARM_YEARS_REG 0x0C
-
-#define REG_RTC_CTRL_REG 0x0D
-#define REG_RTC_STATUS_REG 0x0E
-#define REG_RTC_INTERRUPTS_REG 0x0F
-
-#define REG_RTC_COMP_LSB_REG 0x10
-#define REG_RTC_COMP_MSB_REG 0x11
+enum {
+ REG_SECONDS_REG = 0,
+ REG_MINUTES_REG,
+ REG_HOURS_REG,
+ REG_DAYS_REG,
+ REG_MONTHS_REG,
+ REG_YEARS_REG,
+ REG_WEEKS_REG,
+
+ REG_ALARM_SECONDS_REG,
+ REG_ALARM_MINUTES_REG,
+ REG_ALARM_HOURS_REG,
+ REG_ALARM_DAYS_REG,
+ REG_ALARM_MONTHS_REG,
+ REG_ALARM_YEARS_REG,
+
+ REG_RTC_CTRL_REG,
+ REG_RTC_STATUS_REG,
+ REG_RTC_INTERRUPTS_REG,
+
+ REG_RTC_COMP_LSB_REG,
+ REG_RTC_COMP_MSB_REG,
+};
+const static u8 twl4030_rtc_reg_map[] = {
+ [REG_SECONDS_REG] = 0x00,
+ [REG_MINUTES_REG] = 0x01,
+ [REG_HOURS_REG] = 0x02,
+ [REG_DAYS_REG] = 0x03,
+ [REG_MONTHS_REG] = 0x04,
+ [REG_YEARS_REG] = 0x05,
+ [REG_WEEKS_REG] = 0x06,
+
+ [REG_ALARM_SECONDS_REG] = 0x07,
+ [REG_ALARM_MINUTES_REG] = 0x08,
+ [REG_ALARM_HOURS_REG] = 0x09,
+ [REG_ALARM_DAYS_REG] = 0x0A,
+ [REG_ALARM_MONTHS_REG] = 0x0B,
+ [REG_ALARM_YEARS_REG] = 0x0C,
+
+ [REG_RTC_CTRL_REG] = 0x0D,
+ [REG_RTC_STATUS_REG] = 0x0E,
+ [REG_RTC_INTERRUPTS_REG] = 0x0F,
+
+ [REG_RTC_COMP_LSB_REG] = 0x10,
+ [REG_RTC_COMP_MSB_REG] = 0x11,
+};
+const static u8 twl6030_rtc_reg_map[] = {
+ [REG_SECONDS_REG] = 0x00,
+ [REG_MINUTES_REG] = 0x01,
+ [REG_HOURS_REG] = 0x02,
+ [REG_DAYS_REG] = 0x03,
+ [REG_MONTHS_REG] = 0x04,
+ [REG_YEARS_REG] = 0x05,
+ [REG_WEEKS_REG] = 0x06,
+
+ [REG_ALARM_SECONDS_REG] = 0x08,
+ [REG_ALARM_MINUTES_REG] = 0x09,
+ [REG_ALARM_HOURS_REG] = 0x0A,
+ [REG_ALARM_DAYS_REG] = 0x0B,
+ [REG_ALARM_MONTHS_REG] = 0x0C,
+ [REG_ALARM_YEARS_REG] = 0x0D,
+
+ [REG_RTC_CTRL_REG] = 0x10,
+ [REG_RTC_STATUS_REG] = 0x11,
+ [REG_RTC_INTERRUPTS_REG] = 0x12,
+
+ [REG_RTC_COMP_LSB_REG] = 0x13,
+ [REG_RTC_COMP_MSB_REG] = 0x14,
+};
/* RTC_CTRL_REG bitfields */
#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01
@@ -84,31 +132,32 @@
#define ALL_TIME_REGS 6
/*----------------------------------------------------------------------*/
+static u8 *rtc_reg_map;
/*
- * Supports 1 byte read from TWL4030 RTC register.
+ * Supports 1 byte read from TWL RTC register.
*/
-static int twl4030_rtc_read_u8(u8 *data, u8 reg)
+static int twl_rtc_read_u8(u8 *data, u8 reg)
{
int ret;
- ret = twl4030_i2c_read_u8(TWL4030_MODULE_RTC, data, reg);
+ ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
if (ret < 0)
- pr_err("twl4030_rtc: Could not read TWL4030"
+ pr_err("twl_rtc: Could not read TWL"
"register %X - error %d\n", reg, ret);
return ret;
}
/*
- * Supports 1 byte write to TWL4030 RTC registers.
+ * Supports 1 byte write to TWL RTC registers.
*/
-static int twl4030_rtc_write_u8(u8 data, u8 reg)
+static int twl_rtc_write_u8(u8 data, u8 reg)
{
int ret;
- ret = twl4030_i2c_write_u8(TWL4030_MODULE_RTC, data, reg);
+ ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
if (ret < 0)
- pr_err("twl4030_rtc: Could not write TWL4030"
+ pr_err("twl_rtc: Could not write TWL"
"register %X - error %d\n", reg, ret);
return ret;
}
@@ -129,7 +178,7 @@ static int set_rtc_irq_bit(unsigned char bit)
val = rtc_irq_bits | bit;
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
- ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+ ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
rtc_irq_bits = val;
@@ -145,14 +194,14 @@ static int mask_rtc_irq_bit(unsigned char bit)
int ret;
val = rtc_irq_bits & ~bit;
- ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+ ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
rtc_irq_bits = val;
return ret;
}
-static int twl4030_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
{
int ret;
@@ -164,7 +213,7 @@ static int twl4030_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
return ret;
}
-static int twl4030_rtc_update_irq_enable(struct device *dev, unsigned enabled)
+static int twl_rtc_update_irq_enable(struct device *dev, unsigned enabled)
{
int ret;
@@ -177,7 +226,7 @@ static int twl4030_rtc_update_irq_enable(struct device *dev, unsigned enabled)
}
/*
- * Gets current TWL4030 RTC time and date parameters.
+ * Gets current TWL RTC time and date parameters.
*
* The RTC's time/alarm representation is not what gmtime(3) requires
* Linux to use:
@@ -185,24 +234,24 @@ static int twl4030_rtc_update_irq_enable(struct device *dev, unsigned enabled)
* - Months are 1..12 vs Linux 0-11
* - Years are 0..99 vs Linux 1900..N (we assume 21st century)
*/
-static int twl4030_rtc_read_time(struct device *dev, struct rtc_time *tm)
+static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned char rtc_data[ALL_TIME_REGS + 1];
int ret;
u8 save_control;
- ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
if (ret < 0)
return ret;
save_control |= BIT_RTC_CTRL_REG_GET_TIME_M;
- ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
if (ret < 0)
return ret;
- ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
- REG_SECONDS_REG, ALL_TIME_REGS);
+ ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
+ (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_read_time error %d\n", ret);
@@ -219,7 +268,7 @@ static int twl4030_rtc_read_time(struct device *dev, struct rtc_time *tm)
return ret;
}
-static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
+static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned char save_control;
unsigned char rtc_data[ALL_TIME_REGS + 1];
@@ -233,18 +282,18 @@ static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_data[6] = bin2bcd(tm->tm_year - 100);
/* Stop RTC while updating the TC registers */
- ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
- twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
/* update all the time registers in one shot */
- ret = twl4030_i2c_write(TWL4030_MODULE_RTC, rtc_data,
- REG_SECONDS_REG, ALL_TIME_REGS);
+ ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data,
+ (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_set_time error %d\n", ret);
goto out;
@@ -252,22 +301,22 @@ static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
/* Start back RTC */
save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
- ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
out:
return ret;
}
/*
- * Gets current TWL4030 RTC alarm time.
+ * Gets current TWL RTC alarm time.
*/
-static int twl4030_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
unsigned char rtc_data[ALL_TIME_REGS + 1];
int ret;
- ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
- REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
+ ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
+ (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_read_alarm error %d\n", ret);
return ret;
@@ -288,12 +337,12 @@ static int twl4030_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
return ret;
}
-static int twl4030_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
unsigned char alarm_data[ALL_TIME_REGS + 1];
int ret;
- ret = twl4030_rtc_alarm_irq_enable(dev, 0);
+ ret = twl_rtc_alarm_irq_enable(dev, 0);
if (ret)
goto out;
@@ -305,20 +354,20 @@ static int twl4030_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
alarm_data[6] = bin2bcd(alm->time.tm_year - 100);
/* update all the alarm registers in one shot */
- ret = twl4030_i2c_write(TWL4030_MODULE_RTC, alarm_data,
- REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
+ ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data,
+ (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
if (ret) {
dev_err(dev, "rtc_set_alarm error %d\n", ret);
goto out;
}
if (alm->enabled)
- ret = twl4030_rtc_alarm_irq_enable(dev, 1);
+ ret = twl_rtc_alarm_irq_enable(dev, 1);
out:
return ret;
}
-static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
+static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
{
unsigned long events = 0;
int ret = IRQ_NONE;
@@ -333,7 +382,7 @@ static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
local_irq_enable();
#endif
- res = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (res)
goto out;
/*
@@ -347,26 +396,28 @@ static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
else
events |= RTC_IRQF | RTC_UF;
- res = twl4030_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
+ res = twl_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
REG_RTC_STATUS_REG);
if (res)
goto out;
- /* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
- * needs 2 reads to clear the interrupt. One read is done in
- * do_twl4030_pwrirq(). Doing the second read, to clear
- * the bit.
- *
- * FIXME the reason PWR_ISR1 needs an extra read is that
- * RTC_IF retriggered until we cleared REG_ALARM_M above.
- * But re-reading like this is a bad hack; by doing so we
- * risk wrongly clearing status for some other IRQ (losing
- * the interrupt). Be smarter about handling RTC_UF ...
- */
- res = twl4030_i2c_read_u8(TWL4030_MODULE_INT,
+ if (twl_class_is_4030()) {
+ /* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
+ * needs 2 reads to clear the interrupt. One read is done in
+ * do_twl_pwrirq(). Doing the second read, to clear
+ * the bit.
+ *
+ * FIXME the reason PWR_ISR1 needs an extra read is that
+ * RTC_IF retriggered until we cleared REG_ALARM_M above.
+ * But re-reading like this is a bad hack; by doing so we
+ * risk wrongly clearing status for some other IRQ (losing
+ * the interrupt). Be smarter about handling RTC_UF ...
+ */
+ res = twl_i2c_read_u8(TWL4030_MODULE_INT,
&rd_reg, TWL4030_INT_PWR_ISR1);
- if (res)
- goto out;
+ if (res)
+ goto out;
+ }
/* Notify RTC core on event */
rtc_update_irq(rtc, 1, events);
@@ -376,18 +427,18 @@ out:
return ret;
}
-static struct rtc_class_ops twl4030_rtc_ops = {
- .read_time = twl4030_rtc_read_time,
- .set_time = twl4030_rtc_set_time,
- .read_alarm = twl4030_rtc_read_alarm,
- .set_alarm = twl4030_rtc_set_alarm,
- .alarm_irq_enable = twl4030_rtc_alarm_irq_enable,
- .update_irq_enable = twl4030_rtc_update_irq_enable,
+static struct rtc_class_ops twl_rtc_ops = {
+ .read_time = twl_rtc_read_time,
+ .set_time = twl_rtc_set_time,
+ .read_alarm = twl_rtc_read_alarm,
+ .set_alarm = twl_rtc_set_alarm,
+ .alarm_irq_enable = twl_rtc_alarm_irq_enable,
+ .update_irq_enable = twl_rtc_update_irq_enable,
};
/*----------------------------------------------------------------------*/
-static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
+static int __devinit twl_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
int ret = 0;
@@ -398,7 +449,7 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
return -EINVAL;
rtc = rtc_device_register(pdev->name,
- &pdev->dev, &twl4030_rtc_ops, THIS_MODULE);
+ &pdev->dev, &twl_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
@@ -409,7 +460,7 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rtc);
- ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
goto out1;
@@ -420,11 +471,11 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
/* Clear RTC Power up reset and pending alarm interrupts */
- ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
+ ret = twl_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
goto out1;
- ret = request_irq(irq, twl4030_rtc_interrupt,
+ ret = request_irq(irq, twl_rtc_interrupt,
IRQF_TRIGGER_RISING,
dev_name(&rtc->dev), rtc);
if (ret < 0) {
@@ -432,21 +483,28 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
goto out1;
}
+ if (twl_class_is_6030()) {
+ twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_LINE_A);
+ twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_STS_A);
+ }
+
/* Check RTC module status, Enable if it is off */
- ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
+ ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
if (ret < 0)
goto out2;
if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
- dev_info(&pdev->dev, "Enabling TWL4030-RTC.\n");
+ dev_info(&pdev->dev, "Enabling TWL-RTC.\n");
rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
- ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
if (ret < 0)
goto out2;
}
/* init cached IRQ enable bits */
- ret = twl4030_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
+ ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
goto out2;
@@ -461,10 +519,10 @@ out0:
}
/*
- * Disable all TWL4030 RTC module interrupts.
+ * Disable all TWL RTC module interrupts.
* Sets status flag to free.
*/
-static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
+static int __devexit twl_rtc_remove(struct platform_device *pdev)
{
/* leave rtc running, but disable irqs */
struct rtc_device *rtc = platform_get_drvdata(pdev);
@@ -472,6 +530,13 @@ static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ if (twl_class_is_6030()) {
+ twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_LINE_A);
+ twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_STS_A);
+ }
+
free_irq(irq, rtc);
@@ -480,7 +545,7 @@ static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
return 0;
}
-static void twl4030_rtc_shutdown(struct platform_device *pdev)
+static void twl_rtc_shutdown(struct platform_device *pdev)
{
/* mask timer interrupts, but leave alarm interrupts on to enable
power-on when alarm is triggered */
@@ -491,7 +556,7 @@ static void twl4030_rtc_shutdown(struct platform_device *pdev)
static unsigned char irqstat;
-static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+static int twl_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
irqstat = rtc_irq_bits;
@@ -499,42 +564,47 @@ static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int twl4030_rtc_resume(struct platform_device *pdev)
+static int twl_rtc_resume(struct platform_device *pdev)
{
set_rtc_irq_bit(irqstat);
return 0;
}
#else
-#define twl4030_rtc_suspend NULL
-#define twl4030_rtc_resume NULL
+#define twl_rtc_suspend NULL
+#define twl_rtc_resume NULL
#endif
-MODULE_ALIAS("platform:twl4030_rtc");
+MODULE_ALIAS("platform:twl_rtc");
static struct platform_driver twl4030rtc_driver = {
- .probe = twl4030_rtc_probe,
- .remove = __devexit_p(twl4030_rtc_remove),
- .shutdown = twl4030_rtc_shutdown,
- .suspend = twl4030_rtc_suspend,
- .resume = twl4030_rtc_resume,
+ .probe = twl_rtc_probe,
+ .remove = __devexit_p(twl_rtc_remove),
+ .shutdown = twl_rtc_shutdown,
+ .suspend = twl_rtc_suspend,
+ .resume = twl_rtc_resume,
.driver = {
.owner = THIS_MODULE,
- .name = "twl4030_rtc",
+ .name = "twl_rtc",
},
};
-static int __init twl4030_rtc_init(void)
+static int __init twl_rtc_init(void)
{
+ if (twl_class_is_4030())
+ rtc_reg_map = (u8 *) twl4030_rtc_reg_map;
+ else
+ rtc_reg_map = (u8 *) twl6030_rtc_reg_map;
+
return platform_driver_register(&twl4030rtc_driver);
}
-module_init(twl4030_rtc_init);
+module_init(twl_rtc_init);
-static void __exit twl4030_rtc_exit(void)
+static void __exit twl_rtc_exit(void)
{
platform_driver_unregister(&twl4030rtc_driver);
}
-module_exit(twl4030_rtc_exit);
+module_exit(twl_rtc_exit);
MODULE_AUTHOR("Texas Instruments, MontaVista Software");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 4a6ed1104fbb..9ee81d8aa7c0 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -17,6 +17,7 @@
struct tx4939rtc_plat_data {
struct rtc_device *rtc;
struct tx4939_rtc_reg __iomem *rtcreg;
+ spinlock_t lock;
};
static struct tx4939rtc_plat_data *get_tx4939rtc_plat_data(struct device *dev)
@@ -52,14 +53,14 @@ static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs)
buf[3] = secs >> 8;
buf[4] = secs >> 16;
buf[5] = secs >> 24;
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
__raw_writel(0, &rtcreg->adr);
for (i = 0; i < 6; i++)
__raw_writel(buf[i], &rtcreg->dat);
ret = tx4939_rtc_cmd(rtcreg,
TX4939_RTCCTL_COMMAND_SETTIME |
(__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return ret;
}
@@ -71,18 +72,18 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
unsigned long sec;
unsigned char buf[6];
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
ret = tx4939_rtc_cmd(rtcreg,
TX4939_RTCCTL_COMMAND_GETTIME |
(__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
if (ret) {
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return ret;
}
__raw_writel(2, &rtcreg->adr);
for (i = 2; i < 6; i++)
buf[i] = __raw_readl(&rtcreg->dat);
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, tm);
return rtc_valid_tm(tm);
@@ -110,13 +111,13 @@ static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
buf[3] = sec >> 8;
buf[4] = sec >> 16;
buf[5] = sec >> 24;
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
__raw_writel(0, &rtcreg->adr);
for (i = 0; i < 6; i++)
__raw_writel(buf[i], &rtcreg->dat);
ret = tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_SETALARM |
(alrm->enabled ? TX4939_RTCCTL_ALME : 0));
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return ret;
}
@@ -129,12 +130,12 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned char buf[6];
u32 ctl;
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
ret = tx4939_rtc_cmd(rtcreg,
TX4939_RTCCTL_COMMAND_GETALARM |
(__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
if (ret) {
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return ret;
}
__raw_writel(2, &rtcreg->adr);
@@ -143,7 +144,7 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
ctl = __raw_readl(&rtcreg->ctl);
alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0;
alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0;
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, &alrm->time);
return rtc_valid_tm(&alrm->time);
@@ -153,11 +154,11 @@ static int tx4939_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
tx4939_rtc_cmd(pdata->rtcreg,
TX4939_RTCCTL_COMMAND_NOP |
(enabled ? TX4939_RTCCTL_ALME : 0));
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return 0;
}
@@ -167,13 +168,14 @@ static irqreturn_t tx4939_rtc_interrupt(int irq, void *dev_id)
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
unsigned long events = RTC_IRQF;
- spin_lock(&pdata->rtc->irq_lock);
+ spin_lock(&pdata->lock);
if (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALMD) {
events |= RTC_AF;
tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_NOP);
}
- spin_unlock(&pdata->rtc->irq_lock);
- rtc_update_irq(pdata->rtc, 1, events);
+ spin_unlock(&pdata->lock);
+ if (likely(pdata->rtc))
+ rtc_update_irq(pdata->rtc, 1, events);
return IRQ_HANDLED;
}
@@ -194,13 +196,13 @@ static ssize_t tx4939_rtc_nvram_read(struct kobject *kobj,
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
ssize_t count;
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
for (count = 0; size > 0 && pos < TX4939_RTC_REG_RAMSIZE;
count++, size--) {
__raw_writel(pos++, &rtcreg->adr);
*buf++ = __raw_readl(&rtcreg->dat);
}
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return count;
}
@@ -213,13 +215,13 @@ static ssize_t tx4939_rtc_nvram_write(struct kobject *kobj,
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
ssize_t count;
- spin_lock_irq(&pdata->rtc->irq_lock);
+ spin_lock_irq(&pdata->lock);
for (count = 0; size > 0 && pos < TX4939_RTC_REG_RAMSIZE;
count++, size--) {
__raw_writel(pos++, &rtcreg->adr);
__raw_writel(*buf++, &rtcreg->dat);
}
- spin_unlock_irq(&pdata->rtc->irq_lock);
+ spin_unlock_irq(&pdata->lock);
return count;
}
@@ -259,6 +261,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
if (!pdata->rtcreg)
return -EBUSY;
+ spin_lock_init(&pdata->lock);
tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt,
IRQF_DISABLED, pdev->name, &pdev->dev) < 0)
@@ -277,14 +280,12 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
static int __exit tx4939_rtc_remove(struct platform_device *pdev)
{
struct tx4939rtc_plat_data *pdata = platform_get_drvdata(pdev);
- struct rtc_device *rtc = pdata->rtc;
- spin_lock_irq(&rtc->irq_lock);
- tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
- spin_unlock_irq(&rtc->irq_lock);
sysfs_remove_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
- rtc_device_unregister(rtc);
- platform_set_drvdata(pdev, NULL);
+ rtc_device_unregister(pdata->rtc);
+ spin_lock_irq(&pdata->lock);
+ tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
+ spin_unlock_irq(&pdata->lock);
return 0;
}
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index ad741afd47d8..bed4cab07043 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -304,7 +304,6 @@ static int rtc_probe(struct platform_device *pdev)
{
struct v3020_platform_data *pdata = pdev->dev.platform_data;
struct v3020 *chip;
- struct rtc_device *rtc;
int retval = -EBUSY;
int i;
int temp;
@@ -353,13 +352,12 @@ static int rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, chip);
- rtc = rtc_device_register("v3020",
+ chip->rtc = rtc_device_register("v3020",
&pdev->dev, &v3020_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- retval = PTR_ERR(rtc);
+ if (IS_ERR(chip->rtc)) {
+ retval = PTR_ERR(chip->rtc);
goto err_io;
}
- chip->rtc = rtc;
return 0;
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index fadddac1e5a4..c3244244e8cf 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -327,7 +327,7 @@ static int __devinit rtc_probe(struct platform_device *pdev)
if (!res)
return -EBUSY;
- rtc1_base = ioremap(res->start, res->end - res->start + 1);
+ rtc1_base = ioremap(res->start, resource_size(res));
if (!rtc1_base)
return -EBUSY;
@@ -337,7 +337,7 @@ static int __devinit rtc_probe(struct platform_device *pdev)
goto err_rtc1_iounmap;
}
- rtc2_base = ioremap(res->start, res->end - res->start + 1);
+ rtc2_base = ioremap(res->start, resource_size(res));
if (!rtc2_base) {
retval = -EBUSY;
goto err_rtc1_iounmap;
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
index 79795cdf6ed8..000c7e481e59 100644
--- a/drivers/rtc/rtc-wm831x.c
+++ b/drivers/rtc/rtc-wm831x.c
@@ -485,7 +485,7 @@ static int __devexit wm831x_rtc_remove(struct platform_device *pdev)
return 0;
}
-static struct dev_pm_ops wm831x_rtc_pm_ops = {
+static const struct dev_pm_ops wm831x_rtc_pm_ops = {
.suspend = wm831x_rtc_suspend,
.resume = wm831x_rtc_resume,
diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c
index c91edc572eb6..f1e440521c54 100644
--- a/drivers/rtc/rtc-wm8350.c
+++ b/drivers/rtc/rtc-wm8350.c
@@ -315,9 +315,9 @@ static int wm8350_rtc_update_irq_enable(struct device *dev,
return 0;
}
-static void wm8350_rtc_alarm_handler(struct wm8350 *wm8350, int irq,
- void *data)
+static irqreturn_t wm8350_rtc_alarm_handler(int irq, void *data)
{
+ struct wm8350 *wm8350 = data;
struct rtc_device *rtc = wm8350->rtc.rtc;
int ret;
@@ -330,14 +330,18 @@ static void wm8350_rtc_alarm_handler(struct wm8350 *wm8350, int irq,
dev_err(&(wm8350->rtc.pdev->dev),
"Failed to disable alarm: %d\n", ret);
}
+
+ return IRQ_HANDLED;
}
-static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq,
- void *data)
+static irqreturn_t wm8350_rtc_update_handler(int irq, void *data)
{
+ struct wm8350 *wm8350 = data;
struct rtc_device *rtc = wm8350->rtc.rtc;
rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF);
+
+ return IRQ_HANDLED;
}
static const struct rtc_class_ops wm8350_rtc_ops = {
@@ -350,8 +354,9 @@ static const struct rtc_class_ops wm8350_rtc_ops = {
};
#ifdef CONFIG_PM
-static int wm8350_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+static int wm8350_rtc_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
int ret = 0;
u16 reg;
@@ -369,8 +374,9 @@ static int wm8350_rtc_suspend(struct platform_device *pdev, pm_message_t state)
return ret;
}
-static int wm8350_rtc_resume(struct platform_device *pdev)
+static int wm8350_rtc_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
int ret;
@@ -455,15 +461,14 @@ static int wm8350_rtc_probe(struct platform_device *pdev)
return ret;
}
- wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
- wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_PER);
-
wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
- wm8350_rtc_update_handler, NULL);
+ wm8350_rtc_update_handler, 0,
+ "RTC Seconds", wm8350);
+ wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
- wm8350_rtc_alarm_handler, NULL);
- wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_ALM);
+ wm8350_rtc_alarm_handler, 0,
+ "RTC Alarm", wm8350);
return 0;
}
@@ -473,8 +478,6 @@ static int __devexit wm8350_rtc_remove(struct platform_device *pdev)
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct wm8350_rtc *wm_rtc = &wm8350->rtc;
- wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
-
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM);
@@ -483,13 +486,17 @@ static int __devexit wm8350_rtc_remove(struct platform_device *pdev)
return 0;
}
+static struct dev_pm_ops wm8350_rtc_pm_ops = {
+ .suspend = wm8350_rtc_suspend,
+ .resume = wm8350_rtc_resume,
+};
+
static struct platform_driver wm8350_rtc_driver = {
.probe = wm8350_rtc_probe,
.remove = __devexit_p(wm8350_rtc_remove),
- .suspend = wm8350_rtc_suspend,
- .resume = wm8350_rtc_resume,
.driver = {
.name = "wm8350-rtc",
+ .pm = &wm8350_rtc_pm_ops,
},
};
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 6583c1a8b070..9aae49139a0a 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -155,11 +155,11 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
}
static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
- int datetoo, u8 reg_base, unsigned char alm_enable)
+ u8 reg_base, unsigned char alm_enable)
{
- int i, xfer, nbytes;
- unsigned char buf[8];
+ int i, xfer;
unsigned char rdata[10] = { 0, reg_base };
+ unsigned char *buf = rdata + 2;
static const unsigned char wel[3] = { 0, X1205_REG_SR,
X1205_SR_WEL };
@@ -170,9 +170,9 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
dev_dbg(&client->dev,
- "%s: secs=%d, mins=%d, hours=%d\n",
- __func__,
- tm->tm_sec, tm->tm_min, tm->tm_hour);
+ "%s: sec=%d min=%d hour=%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);
buf[CCR_SEC] = bin2bcd(tm->tm_sec);
buf[CCR_MIN] = bin2bcd(tm->tm_min);
@@ -180,23 +180,15 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
/* set hour and 24hr bit */
buf[CCR_HOUR] = bin2bcd(tm->tm_hour) | X1205_HR_MIL;
- /* should we also set the date? */
- if (datetoo) {
- dev_dbg(&client->dev,
- "%s: mday=%d, mon=%d, year=%d, wday=%d\n",
- __func__,
- tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+ buf[CCR_MDAY] = bin2bcd(tm->tm_mday);
- buf[CCR_MDAY] = bin2bcd(tm->tm_mday);
+ /* month, 1 - 12 */
+ buf[CCR_MONTH] = bin2bcd(tm->tm_mon + 1);
- /* month, 1 - 12 */
- buf[CCR_MONTH] = bin2bcd(tm->tm_mon + 1);
-
- /* year, since the rtc epoch*/
- buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100);
- buf[CCR_WDAY] = tm->tm_wday & 0x07;
- buf[CCR_Y2K] = bin2bcd((tm->tm_year + 1900) / 100);
- }
+ /* year, since the rtc epoch*/
+ buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100);
+ buf[CCR_WDAY] = tm->tm_wday & 0x07;
+ buf[CCR_Y2K] = bin2bcd((tm->tm_year + 1900) / 100);
/* If writing alarm registers, set compare bits on registers 0-4 */
if (reg_base < X1205_CCR_BASE)
@@ -214,17 +206,8 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
return -EIO;
}
-
- /* write register's data */
- if (datetoo)
- nbytes = 8;
- else
- nbytes = 3;
- for (i = 0; i < nbytes; i++)
- rdata[2+i] = buf[i];
-
- xfer = i2c_master_send(client, rdata, nbytes+2);
- if (xfer != nbytes+2) {
+ xfer = i2c_master_send(client, rdata, sizeof(rdata));
+ if (xfer != sizeof(rdata)) {
dev_err(&client->dev,
"%s: result=%d addr=%02x, data=%02x\n",
__func__,
@@ -282,7 +265,7 @@ static int x1205_fix_osc(struct i2c_client *client)
memset(&tm, 0, sizeof(tm));
- err = x1205_set_datetime(client, &tm, 1, X1205_CCR_BASE, 0);
+ err = x1205_set_datetime(client, &tm, X1205_CCR_BASE, 0);
if (err < 0)
dev_err(&client->dev, "unable to restart the oscillator\n");
@@ -481,7 +464,7 @@ static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
return x1205_set_datetime(to_i2c_client(dev),
- &alrm->time, 1, X1205_ALM0_BASE, alrm->enabled);
+ &alrm->time, X1205_ALM0_BASE, alrm->enabled);
}
static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -493,7 +476,7 @@ static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return x1205_set_datetime(to_i2c_client(dev),
- tm, 1, X1205_CCR_BASE, 0);
+ tm, X1205_CCR_BASE, 0);
}
static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 5f23eca82804..6315fbd8e68b 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -14,6 +14,7 @@
#define KMSG_COMPONENT "dasd"
#include <linux/ctype.h>
+#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
@@ -272,10 +273,10 @@ dasd_statistics_write(struct file *file, const char __user *user_buf,
DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer);
/* check for valid verbs */
- for (str = buffer; isspace(*str); str++);
+ str = skip_spaces(buffer);
if (strncmp(str, "set", 3) == 0 && isspace(str[3])) {
/* 'set xxx' was given */
- for (str = str + 4; isspace(*str); str++);
+ str = skip_spaces(str + 4);
if (strcmp(str, "on") == 0) {
/* switch on statistics profiling */
dasd_profile_level = DASD_PROFILE_ON;
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index f76f4bd82b9f..9b43ae94beba 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -1005,7 +1005,7 @@ static int dcssblk_thaw(struct device *dev)
return 0;
}
-static struct dev_pm_ops dcssblk_pm_ops = {
+static const struct dev_pm_ops dcssblk_pm_ops = {
.freeze = dcssblk_freeze,
.thaw = dcssblk_thaw,
.restore = dcssblk_restore,
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 116d1b3eeb15..118de392af63 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -407,7 +407,7 @@ static int xpram_restore(struct device *dev)
return 0;
}
-static struct dev_pm_ops xpram_pm_ops = {
+static const struct dev_pm_ops xpram_pm_ops = {
.restore = xpram_restore,
};
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 60473f86e1f9..33e96484d54f 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -529,7 +529,7 @@ static int monreader_restore(struct device *dev)
return monreader_thaw(dev);
}
-static struct dev_pm_ops monreader_pm_ops = {
+static const struct dev_pm_ops monreader_pm_ops = {
.freeze = monreader_freeze,
.thaw = monreader_thaw,
.restore = monreader_restore,
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index 6532ed8b4afa..668a0579b26b 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -323,7 +323,7 @@ static int monwriter_thaw(struct device *dev)
return monwriter_restore(dev);
}
-static struct dev_pm_ops monwriter_pm_ops = {
+static const struct dev_pm_ops monwriter_pm_ops = {
.freeze = monwriter_freeze,
.thaw = monwriter_thaw,
.restore = monwriter_restore,
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index a983f5086788..ec88c59842e3 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -1019,7 +1019,7 @@ static int sclp_restore(struct device *dev)
return sclp_undo_suspend(SCLP_PM_EVENT_RESTORE);
}
-static struct dev_pm_ops sclp_pm_ops = {
+static const struct dev_pm_ops sclp_pm_ops = {
.freeze = sclp_freeze,
.thaw = sclp_thaw,
.restore = sclp_restore,
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 28b5afc129c3..b3beab610da4 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -547,7 +547,7 @@ struct read_storage_sccb {
u32 entries[0];
} __packed;
-static struct dev_pm_ops sclp_mem_pm_ops = {
+static const struct dev_pm_ops sclp_mem_pm_ops = {
.freeze = sclp_mem_freeze,
};
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 899aa795bf38..7dfa5412d5a8 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -675,7 +675,7 @@ static int vmlogrdr_pm_prepare(struct device *dev)
}
-static struct dev_pm_ops vmlogrdr_pm_ops = {
+static const struct dev_pm_ops vmlogrdr_pm_ops = {
.prepare = vmlogrdr_pm_prepare,
};
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index a5a62f1f7747..5f97ea2ee6b1 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -560,7 +560,7 @@ static int ccwgroup_pm_restore(struct device *dev)
return gdrv->restore ? gdrv->restore(gdev) : 0;
}
-static struct dev_pm_ops ccwgroup_pm_ops = {
+static const struct dev_pm_ops ccwgroup_pm_ops = {
.prepare = ccwgroup_pm_prepare,
.complete = ccwgroup_pm_complete,
.freeze = ccwgroup_pm_freeze,
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 92ff88ac1107..7679aee6fa14 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -1148,7 +1148,7 @@ static int css_pm_restore(struct device *dev)
return drv->restore ? drv->restore(sch) : 0;
}
-static struct dev_pm_ops css_pm_ops = {
+static const struct dev_pm_ops css_pm_ops = {
.prepare = css_pm_prepare,
.complete = css_pm_complete,
.freeze = css_pm_freeze,
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 9fecfb4223a8..73901c9e260f 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1904,7 +1904,7 @@ out_unlock:
return ret;
}
-static struct dev_pm_ops ccw_pm_ops = {
+static const struct dev_pm_ops ccw_pm_ops = {
.prepare = ccw_device_pm_prepare,
.complete = ccw_device_pm_complete,
.freeze = ccw_device_pm_freeze,
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 395c04c2b00f..65ebee0a3266 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -113,11 +113,9 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
#define IUCV_DBF_TEXT_(name, level, text...) \
do { \
if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
- char* iucv_dbf_txt_buf = \
- get_cpu_var(iucv_dbf_txt_buf); \
- sprintf(iucv_dbf_txt_buf, text); \
- debug_text_event(iucv_dbf_##name, level, \
- iucv_dbf_txt_buf); \
+ char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
+ sprintf(__buf, text); \
+ debug_text_event(iucv_dbf_##name, level, __buf); \
put_cpu_var(iucv_dbf_txt_buf); \
} \
} while (0)
@@ -161,7 +159,7 @@ static void netiucv_pm_complete(struct device *);
static int netiucv_pm_freeze(struct device *);
static int netiucv_pm_restore_thaw(struct device *);
-static struct dev_pm_ops netiucv_pm_ops = {
+static const struct dev_pm_ops netiucv_pm_ops = {
.prepare = netiucv_pm_prepare,
.complete = netiucv_pm_complete,
.freeze = netiucv_pm_freeze,
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 3012355f8304..67f2485d2372 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -168,7 +168,7 @@ static int smsg_pm_restore_thaw(struct device *dev)
return 0;
}
-static struct dev_pm_ops smsg_pm_ops = {
+static const struct dev_pm_ops smsg_pm_ops = {
.freeze = smsg_pm_freeze,
.thaw = smsg_pm_restore_thaw,
.restore = smsg_pm_restore_thaw,
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 206c2fa8c1ba..8643f5089361 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -1333,7 +1333,7 @@ static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
error = &hostrcb->hcam.u.error.u.type_17_error;
error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
- strstrip(error->failure_reason);
+ strim(error->failure_reason);
ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
be32_to_cpu(hostrcb->hcam.u.error.prc));
@@ -1359,7 +1359,7 @@ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
error = &hostrcb->hcam.u.error.u.type_07_error;
error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
- strstrip(error->failure_reason);
+ strim(error->failure_reason);
ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
be32_to_cpu(hostrcb->hcam.u.error.prc));
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 2b38f6ad6e11..8b955b534a36 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -984,7 +984,7 @@ static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc)
}
}
-static int skip_spaces(char *ptr, int len)
+static int sym_skip_spaces(char *ptr, int len)
{
int cnt, c;
@@ -1012,7 +1012,7 @@ static int is_keyword(char *ptr, int len, char *verb)
}
#define SKIP_SPACES(ptr, len) \
- if ((arg_len = skip_spaces(ptr, len)) < 1) \
+ if ((arg_len = sym_skip_spaces(ptr, len)) < 1) \
return -EINVAL; \
ptr += arg_len; len -= arg_len;
diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c
index d8983dd5c4b2..85dc0410ac1a 100644
--- a/drivers/serial/ioc3_serial.c
+++ b/drivers/serial/ioc3_serial.c
@@ -2162,7 +2162,7 @@ static struct ioc3_submodule ioc3uart_ops = {
/**
* ioc3_detect - module init called,
*/
-static int __devinit ioc3uart_init(void)
+static int __init ioc3uart_init(void)
{
int ret;
@@ -2179,7 +2179,7 @@ static int __devinit ioc3uart_init(void)
return ret;
}
-static void __devexit ioc3uart_exit(void)
+static void __exit ioc3uart_exit(void)
{
ioc3_unregister_submodule(&ioc3uart_ops);
uart_unregister_driver(&ioc3_uart);
diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c
index 2e02c3026d24..836d9ab4f729 100644
--- a/drivers/serial/ioc4_serial.c
+++ b/drivers/serial/ioc4_serial.c
@@ -2904,7 +2904,7 @@ static struct ioc4_submodule ioc4_serial_submodule = {
/**
* ioc4_serial_init - module init
*/
-int ioc4_serial_init(void)
+static int __init ioc4_serial_init(void)
{
int ret;
@@ -2913,20 +2913,30 @@ int ioc4_serial_init(void)
printk(KERN_WARNING
"%s: Couldn't register rs232 IOC4 serial driver\n",
__func__);
- return ret;
+ goto out;
}
if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) {
printk(KERN_WARNING
"%s: Couldn't register rs422 IOC4 serial driver\n",
__func__);
- return ret;
+ goto out_uart_rs232;
}
/* register with IOC4 main module */
- return ioc4_register_submodule(&ioc4_serial_submodule);
+ ret = ioc4_register_submodule(&ioc4_serial_submodule);
+ if (ret)
+ goto out_uart_rs422;
+ return 0;
+
+out_uart_rs422:
+ uart_unregister_driver(&ioc4_uart_rs422);
+out_uart_rs232:
+ uart_unregister_driver(&ioc4_uart_rs232);
+out:
+ return ret;
}
-static void __devexit ioc4_serial_exit(void)
+static void __exit ioc4_serial_exit(void)
{
ioc4_unregister_submodule(&ioc4_serial_submodule);
uart_unregister_driver(&ioc4_uart_rs232);
diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
index 4a821046baae..56ee082157aa 100644
--- a/drivers/serial/pxa.c
+++ b/drivers/serial/pxa.c
@@ -756,7 +756,7 @@ static int serial_pxa_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops serial_pxa_pm_ops = {
+static const struct dev_pm_ops serial_pxa_pm_ops = {
.suspend = serial_pxa_suspend,
.resume = serial_pxa_resume,
};
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index ff38dbdb5c6e..7e3f4ff58cfd 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -1312,7 +1312,7 @@ static int sci_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops sci_dev_pm_ops = {
+static const struct dev_pm_ops sci_dev_pm_ops = {
.suspend = sci_suspend,
.resume = sci_resume,
};
diff --git a/drivers/sn/ioc3.c b/drivers/sn/ioc3.c
index 816d4c592a3c..66802a4390cc 100644
--- a/drivers/sn/ioc3.c
+++ b/drivers/sn/ioc3.c
@@ -574,11 +574,11 @@ void ioc3_unregister_submodule(struct ioc3_submodule *is)
* Device management *
*********************/
-static char *
+static char * __devinitdata
ioc3_class_names[]={"unknown", "IP27 BaseIO", "IP30 system", "MENET 1/2/3",
"MENET 4", "CADduo", "Altix Serial"};
-static int ioc3_class(struct ioc3_driver_data *idd)
+static int __devinit ioc3_class(struct ioc3_driver_data *idd)
{
int res = IOC3_CLASS_NONE;
/* NIC-based logic */
@@ -601,7 +601,8 @@ static int ioc3_class(struct ioc3_driver_data *idd)
return res;
}
/* Adds a new instance of an IOC3 card */
-static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+static int __devinit
+ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
struct ioc3_driver_data *idd;
uint32_t pcmd;
@@ -753,7 +754,7 @@ out:
}
/* Removes a particular instance of an IOC3 card. */
-static void ioc3_remove(struct pci_dev *pdev)
+static void __devexit ioc3_remove(struct pci_dev *pdev)
{
int id;
struct ioc3_driver_data *idd;
@@ -805,7 +806,7 @@ static struct pci_driver ioc3_driver = {
.name = "IOC3",
.id_table = ioc3_id_table,
.probe = ioc3_probe,
- .remove = ioc3_remove,
+ .remove = __devexit_p(ioc3_remove),
};
MODULE_DEVICE_TABLE(pci, ioc3_id_table);
@@ -815,15 +816,15 @@ MODULE_DEVICE_TABLE(pci, ioc3_id_table);
*********************/
/* Module load */
-static int __devinit ioc3_init(void)
+static int __init ioc3_init(void)
{
if (ia64_platform_is("sn2"))
return pci_register_driver(&ioc3_driver);
- return 0;
+ return -ENODEV;
}
/* Module unload */
-static void __devexit ioc3_exit(void)
+static void __exit ioc3_exit(void)
{
pci_unregister_driver(&ioc3_driver);
}
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 28fce65b8594..2d9d70359360 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -169,6 +169,12 @@ config SPI_OMAP24XX
SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
(McSPI) modules.
+config SPI_OMAP_100K
+ tristate "OMAP SPI 100K"
+ depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730)
+ help
+ OMAP SPI 100K master controller for omap7xx boards.
+
config SPI_ORION
tristate "Orion SPI master (EXPERIMENTAL)"
depends on PLAT_ORION && EXPERIMENTAL
@@ -220,6 +226,13 @@ config SPI_S3C24XX_GPIO
the inbuilt hardware cannot provide the transfer mode, or
where the board is using non hardware connected pins.
+config SPI_SH_MSIOF
+ tristate "SuperH MSIOF SPI controller"
+ depends on SUPERH && HAVE_CLK
+ select SPI_BITBANG
+ help
+ SPI driver for SuperH MSIOF blocks.
+
config SPI_SH_SCI
tristate "SuperH SCI SPI controller"
depends on SUPERH
@@ -240,15 +253,38 @@ config SPI_TXX9
SPI driver for Toshiba TXx9 MIPS SoCs
config SPI_XILINX
- tristate "Xilinx SPI controller"
- depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
+ tristate "Xilinx SPI controller common module"
+ depends on HAS_IOMEM && EXPERIMENTAL
select SPI_BITBANG
+ select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE)
help
This exposes the SPI controller IP from the Xilinx EDK.
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details.
+ Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
+
+config SPI_XILINX_OF
+ tristate "Xilinx SPI controller OF device"
+ depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE)
+ help
+ This is the OF driver for the SPI controller IP from the Xilinx EDK.
+
+config SPI_XILINX_PLTFM
+ tristate "Xilinx SPI controller platform device"
+ depends on SPI_XILINX
+ help
+ This is the platform driver for the SPI controller IP
+ from the Xilinx EDK.
+
+config SPI_NUC900
+ tristate "Nuvoton NUC900 series SPI"
+ depends on ARCH_W90X900 && EXPERIMENTAL
+ select SPI_BITBANG
+ help
+ SPI driver for Nuvoton NUC900 series ARM SoCs
+
#
# Add new SPI master controllers in alphabetical order above this line
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e3f092a9afa5..ed8c1675b52f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
+obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
obj-$(CONFIG_SPI_ORION) += orion_spi.o
obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
@@ -32,8 +33,12 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
+obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
+obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o
obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
+obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
+obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
# ... add above this line ...
# SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c
index 76cbc1a66598..cfd5ff9508fa 100644
--- a/drivers/spi/au1550_spi.c
+++ b/drivers/spi/au1550_spi.c
@@ -237,8 +237,14 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
unsigned bpw, hz;
u32 cfg, stat;
- bpw = t ? t->bits_per_word : spi->bits_per_word;
- hz = t ? t->speed_hz : spi->max_speed_hz;
+ bpw = spi->bits_per_word;
+ hz = spi->max_speed_hz;
+ if (t) {
+ if (t->bits_per_word)
+ bpw = t->bits_per_word;
+ if (t->speed_hz)
+ hz = t->speed_hz;
+ }
if (bpw < 4 || bpw > 24) {
dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n",
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
index ef8379b2c172..45bfe6458173 100644
--- a/drivers/spi/mpc52xx_spi.c
+++ b/drivers/spi/mpc52xx_spi.c
@@ -18,9 +18,9 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
-#include <linux/spi/mpc52xx_spi.h>
#include <linux/of_spi.h>
#include <linux/io.h>
+#include <linux/of_gpio.h>
#include <asm/time.h>
#include <asm/mpc52xx.h>
@@ -53,7 +53,7 @@ MODULE_LICENSE("GPL");
/* FSM state return values */
#define FSM_STOP 0 /* Nothing more for the state machine to */
/* do. If something interesting happens */
- /* then and IRQ will be received */
+ /* then an IRQ will be received */
#define FSM_POLL 1 /* need to poll for completion, an IRQ is */
/* not expected */
#define FSM_CONTINUE 2 /* Keep iterating the state machine */
@@ -61,13 +61,12 @@ MODULE_LICENSE("GPL");
/* Driver internal data */
struct mpc52xx_spi {
struct spi_master *master;
- u32 sysclk;
void __iomem *regs;
int irq0; /* MODF irq */
int irq1; /* SPIF irq */
- int ipb_freq;
+ unsigned int ipb_freq;
- /* Statistics */
+ /* Statistics; not used now, but will be reintroduced for debugfs */
int msg_count;
int wcol_count;
int wcol_ticks;
@@ -79,7 +78,6 @@ struct mpc52xx_spi {
spinlock_t lock;
struct work_struct work;
-
/* Details of current transfer (length, and buffer pointers) */
struct spi_message *message; /* current message */
struct spi_transfer *transfer; /* current transfer */
@@ -89,6 +87,8 @@ struct mpc52xx_spi {
u8 *rx_buf;
const u8 *tx_buf;
int cs_change;
+ int gpio_cs_count;
+ unsigned int *gpio_cs;
};
/*
@@ -96,7 +96,13 @@ struct mpc52xx_spi {
*/
static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
{
- out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
+ int cs;
+
+ if (ms->gpio_cs_count > 0) {
+ cs = ms->message->spi->chip_select;
+ gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1);
+ } else
+ out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
}
/*
@@ -221,7 +227,7 @@ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
ms->wcol_tx_timestamp = get_tbl();
data = 0;
if (ms->tx_buf)
- data = *(ms->tx_buf-1);
+ data = *(ms->tx_buf - 1);
out_8(ms->regs + SPI_DATA, data); /* try again */
return FSM_CONTINUE;
} else if (status & SPI_STATUS_MODF) {
@@ -390,7 +396,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
struct spi_master *master;
struct mpc52xx_spi *ms;
void __iomem *regs;
- int rc;
+ u8 ctrl1;
+ int rc, i = 0;
+ int gpio_cs;
/* MMIO registers */
dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
@@ -399,7 +407,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
return -ENODEV;
/* initialize the device */
- out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR);
+ ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR;
+ out_8(regs + SPI_CTRL1, ctrl1);
out_8(regs + SPI_CTRL2, 0x0);
out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */
out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */
@@ -409,6 +418,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
* on the SPI bus. This fault will also occur if the SPI signals
* are not connected to any pins (port_config setting) */
in_8(regs + SPI_STATUS);
+ out_8(regs + SPI_CTRL1, ctrl1);
+
in_8(regs + SPI_DATA);
if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) {
dev_err(&op->dev, "mode fault; is port_config correct?\n");
@@ -422,10 +433,12 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
rc = -ENOMEM;
goto err_alloc;
}
+
master->bus_num = -1;
- master->num_chipselect = 1;
master->setup = mpc52xx_spi_setup;
master->transfer = mpc52xx_spi_transfer;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+
dev_set_drvdata(&op->dev, master);
ms = spi_master_get_devdata(master);
@@ -435,16 +448,51 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
ms->irq1 = irq_of_parse_and_map(op->node, 1);
ms->state = mpc52xx_spi_fsmstate_idle;
ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node);
+ ms->gpio_cs_count = of_gpio_count(op->node);
+ if (ms->gpio_cs_count > 0) {
+ master->num_chipselect = ms->gpio_cs_count;
+ ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!ms->gpio_cs) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ for (i = 0; i < ms->gpio_cs_count; i++) {
+ gpio_cs = of_get_gpio(op->node, i);
+ if (gpio_cs < 0) {
+ dev_err(&op->dev,
+ "could not parse the gpio field "
+ "in oftree\n");
+ rc = -ENODEV;
+ goto err_gpio;
+ }
+
+ rc = gpio_request(gpio_cs, dev_name(&op->dev));
+ if (rc) {
+ dev_err(&op->dev,
+ "can't request spi cs gpio #%d "
+ "on gpio line %d\n", i, gpio_cs);
+ goto err_gpio;
+ }
+
+ gpio_direction_output(gpio_cs, 1);
+ ms->gpio_cs[i] = gpio_cs;
+ }
+ } else {
+ master->num_chipselect = 1;
+ }
+
spin_lock_init(&ms->lock);
INIT_LIST_HEAD(&ms->queue);
INIT_WORK(&ms->work, mpc52xx_spi_wq);
/* Decide if interrupts can be used */
if (ms->irq0 && ms->irq1) {
- rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
+ rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0,
"mpc5200-spi-modf", ms);
- rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
- "mpc5200-spi-spiF", ms);
+ rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0,
+ "mpc5200-spi-spif", ms);
if (rc) {
free_irq(ms->irq0, ms);
free_irq(ms->irq1, ms);
@@ -471,6 +519,11 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
err_register:
dev_err(&ms->master->dev, "initialization failed\n");
spi_master_put(master);
+ err_gpio:
+ while (i-- > 0)
+ gpio_free(ms->gpio_cs[i]);
+
+ kfree(ms->gpio_cs);
err_alloc:
err_init:
iounmap(regs);
@@ -481,10 +534,15 @@ static int __devexit mpc52xx_spi_remove(struct of_device *op)
{
struct spi_master *master = dev_get_drvdata(&op->dev);
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
+ int i;
free_irq(ms->irq0, ms);
free_irq(ms->irq1, ms);
+ for (i = 0; i < ms->gpio_cs_count; i++)
+ gpio_free(ms->gpio_cs[i]);
+
+ kfree(ms->gpio_cs);
spi_unregister_master(master);
spi_master_put(master);
iounmap(ms->regs);
diff --git a/drivers/spi/omap_spi_100k.c b/drivers/spi/omap_spi_100k.c
new file mode 100644
index 000000000000..5355d90d1bee
--- /dev/null
+++ b/drivers/spi/omap_spi_100k.c
@@ -0,0 +1,635 @@
+/*
+ * OMAP7xx SPI 100k controller driver
+ * Author: Fabrice Crohas <fcrohas@gmail.com>
+ * from original omap1_mcspi driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Author: Samuel Ortiz <samuel.ortiz@nokia.com> and
+ * Juha Yrj�l� <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <linux/spi/spi.h>
+
+#include <plat/clock.h>
+
+#define OMAP1_SPI100K_MAX_FREQ 48000000
+
+#define ICR_SPITAS (OMAP7XX_ICR_BASE + 0x12)
+
+#define SPI_SETUP1 0x00
+#define SPI_SETUP2 0x02
+#define SPI_CTRL 0x04
+#define SPI_STATUS 0x06
+#define SPI_TX_LSB 0x08
+#define SPI_TX_MSB 0x0a
+#define SPI_RX_LSB 0x0c
+#define SPI_RX_MSB 0x0e
+
+#define SPI_SETUP1_INT_READ_ENABLE (1UL << 5)
+#define SPI_SETUP1_INT_WRITE_ENABLE (1UL << 4)
+#define SPI_SETUP1_CLOCK_DIVISOR(x) ((x) << 1)
+#define SPI_SETUP1_CLOCK_ENABLE (1UL << 0)
+
+#define SPI_SETUP2_ACTIVE_EDGE_FALLING (0UL << 0)
+#define SPI_SETUP2_ACTIVE_EDGE_RISING (1UL << 0)
+#define SPI_SETUP2_NEGATIVE_LEVEL (0UL << 5)
+#define SPI_SETUP2_POSITIVE_LEVEL (1UL << 5)
+#define SPI_SETUP2_LEVEL_TRIGGER (0UL << 10)
+#define SPI_SETUP2_EDGE_TRIGGER (1UL << 10)
+
+#define SPI_CTRL_SEN(x) ((x) << 7)
+#define SPI_CTRL_WORD_SIZE(x) (((x) - 1) << 2)
+#define SPI_CTRL_WR (1UL << 1)
+#define SPI_CTRL_RD (1UL << 0)
+
+#define SPI_STATUS_WE (1UL << 1)
+#define SPI_STATUS_RD (1UL << 0)
+
+#define WRITE 0
+#define READ 1
+
+
+/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
+ * cache operations; better heuristics consider wordsize and bitrate.
+ */
+#define DMA_MIN_BYTES 8
+
+#define SPI_RUNNING 0
+#define SPI_SHUTDOWN 1
+
+struct omap1_spi100k {
+ struct work_struct work;
+
+ /* lock protects queue and registers */
+ spinlock_t lock;
+ struct list_head msg_queue;
+ struct spi_master *master;
+ struct clk *ick;
+ struct clk *fck;
+
+ /* Virtual base address of the controller */
+ void __iomem *base;
+
+ /* State of the SPI */
+ unsigned int state;
+};
+
+struct omap1_spi100k_cs {
+ void __iomem *base;
+ int word_len;
+};
+
+static struct workqueue_struct *omap1_spi100k_wq;
+
+#define MOD_REG_BIT(val, mask, set) do { \
+ if (set) \
+ val |= mask; \
+ else \
+ val &= ~mask; \
+} while (0)
+
+static void spi100k_enable_clock(struct spi_master *master)
+{
+ unsigned int val;
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+ /* enable SPI */
+ val = readw(spi100k->base + SPI_SETUP1);
+ val |= SPI_SETUP1_CLOCK_ENABLE;
+ writew(val, spi100k->base + SPI_SETUP1);
+}
+
+static void spi100k_disable_clock(struct spi_master *master)
+{
+ unsigned int val;
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+ /* disable SPI */
+ val = readw(spi100k->base + SPI_SETUP1);
+ val &= ~SPI_SETUP1_CLOCK_ENABLE;
+ writew(val, spi100k->base + SPI_SETUP1);
+}
+
+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 */
+ spi100k_enable_clock(master);
+ writew( data , spi100k->base + SPI_TX_MSB);
+
+ writew(SPI_CTRL_SEN(0) |
+ SPI_CTRL_WORD_SIZE(len) |
+ SPI_CTRL_WR,
+ spi100k->base + SPI_CTRL);
+
+ /* Wait for bit ack send change */
+ while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE);
+ udelay(1000);
+
+ spi100k_disable_clock(master);
+}
+
+static int spi100k_read_data(struct spi_master *master, int len)
+{
+ int dataH,dataL;
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+ spi100k_enable_clock(master);
+ writew(SPI_CTRL_SEN(0) |
+ SPI_CTRL_WORD_SIZE(len) |
+ SPI_CTRL_RD,
+ spi100k->base + SPI_CTRL);
+
+ while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD);
+ udelay(1000);
+
+ dataL = readw(spi100k->base + SPI_RX_LSB);
+ dataH = readw(spi100k->base + SPI_RX_MSB);
+ spi100k_disable_clock(master);
+
+ return dataL;
+}
+
+static void spi100k_open(struct spi_master *master)
+{
+ /* get control of SPI */
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+ writew(SPI_SETUP1_INT_READ_ENABLE |
+ SPI_SETUP1_INT_WRITE_ENABLE |
+ SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1);
+
+ /* configure clock and interrupts */
+ writew(SPI_SETUP2_ACTIVE_EDGE_FALLING |
+ SPI_SETUP2_NEGATIVE_LEVEL |
+ SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2);
+}
+
+static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
+{
+ if (enable)
+ writew(0x05fc, spi100k->base + SPI_CTRL);
+ else
+ writew(0x05fd, spi100k->base + SPI_CTRL);
+}
+
+static unsigned
+omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
+{
+ struct omap1_spi100k *spi100k;
+ struct omap1_spi100k_cs *cs = spi->controller_state;
+ unsigned int count, c;
+ int word_len;
+
+ spi100k = spi_master_get_devdata(spi->master);
+ count = xfer->len;
+ 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;
+
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
+ do {
+ c-=1;
+ if (xfer->tx_buf != NULL)
+ spi100k_write_data(spi->master,word_len, *tx);
+ if (xfer->rx_buf != NULL)
+ *rx = spi100k_read_data(spi->master,word_len);
+ } while(c);
+ } else if (word_len <= 16) {
+ u16 *rx;
+ const u16 *tx;
+
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
+ do {
+ c-=2;
+ if (xfer->tx_buf != NULL)
+ spi100k_write_data(spi->master,word_len, *tx++);
+ if (xfer->rx_buf != NULL)
+ *rx++ = spi100k_read_data(spi->master,word_len);
+ } while(c);
+ } else if (word_len <= 32) {
+ u32 *rx;
+ const u32 *tx;
+
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
+ do {
+ c-=4;
+ if (xfer->tx_buf != NULL)
+ spi100k_write_data(spi->master,word_len, *tx);
+ if (xfer->rx_buf != NULL)
+ *rx = spi100k_read_data(spi->master,word_len);
+ } while(c);
+ }
+ return count - c;
+}
+
+/* called only when no transfer is active to this device */
+static int omap1_spi100k_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master);
+ struct omap1_spi100k_cs *cs = spi->controller_state;
+ u8 word_len = spi->bits_per_word;
+
+ if (t != NULL && t->bits_per_word)
+ word_len = t->bits_per_word;
+ if (!word_len)
+ word_len = 8;
+
+ if (spi->bits_per_word > 32)
+ return -EINVAL;
+ cs->word_len = word_len;
+
+ /* SPI init before transfer */
+ writew(0x3e , spi100k->base + SPI_SETUP1);
+ writew(0x00 , spi100k->base + SPI_STATUS);
+ writew(0x3e , spi100k->base + SPI_CTRL);
+
+ return 0;
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
+
+static int omap1_spi100k_setup(struct spi_device *spi)
+{
+ int ret;
+ struct omap1_spi100k *spi100k;
+ struct omap1_spi100k_cs *cs = spi->controller_state;
+
+ if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
+ dev_dbg(&spi->dev, "setup: unsupported %d bit words\n",
+ spi->bits_per_word);
+ return -EINVAL;
+ }
+
+ spi100k = spi_master_get_devdata(spi->master);
+
+ if (!cs) {
+ cs = kzalloc(sizeof *cs, GFP_KERNEL);
+ if (!cs)
+ return -ENOMEM;
+ cs->base = spi100k->base + spi->chip_select * 0x14;
+ spi->controller_state = cs;
+ }
+
+ spi100k_open(spi->master);
+
+ clk_enable(spi100k->ick);
+ clk_enable(spi100k->fck);
+
+ ret = omap1_spi100k_setup_transfer(spi, NULL);
+
+ clk_disable(spi100k->ick);
+ clk_disable(spi100k->fck);
+
+ return ret;
+}
+
+static void omap1_spi100k_work(struct work_struct *work)
+{
+ struct omap1_spi100k *spi100k;
+ int status = 0;
+
+ spi100k = container_of(work, struct omap1_spi100k, work);
+ spin_lock_irq(&spi100k->lock);
+
+ clk_enable(spi100k->ick);
+ clk_enable(spi100k->fck);
+
+ /* We only enable one channel at a time -- the one whose message is
+ * at the head of the queue -- although this controller would gladly
+ * arbitrate among multiple channels. This corresponds to "single
+ * channel" master mode. As a side effect, we need to manage the
+ * chipselect with the FORCE bit ... CS != channel enable.
+ */
+ while (!list_empty(&spi100k->msg_queue)) {
+ struct spi_message *m;
+ struct spi_device *spi;
+ struct spi_transfer *t = NULL;
+ int cs_active = 0;
+ struct omap1_spi100k_cs *cs;
+ int par_override = 0;
+
+ m = container_of(spi100k->msg_queue.next, struct spi_message,
+ queue);
+
+ list_del_init(&m->queue);
+ spin_unlock_irq(&spi100k->lock);
+
+ spi = m->spi;
+ cs = spi->controller_state;
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
+ status = -EINVAL;
+ break;
+ }
+ if (par_override || t->speed_hz || t->bits_per_word) {
+ par_override = 1;
+ status = omap1_spi100k_setup_transfer(spi, t);
+ if (status < 0)
+ break;
+ if (!t->speed_hz && !t->bits_per_word)
+ par_override = 0;
+ }
+
+ if (!cs_active) {
+ omap1_spi100k_force_cs(spi100k, 1);
+ cs_active = 1;
+ }
+
+ 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;
+
+ if (count != t->len) {
+ status = -EIO;
+ break;
+ }
+ }
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ /* ignore the "leave it on after last xfer" hint */
+
+ if (t->cs_change) {
+ omap1_spi100k_force_cs(spi100k, 0);
+ cs_active = 0;
+ }
+ }
+
+ /* Restore defaults if they were overriden */
+ if (par_override) {
+ par_override = 0;
+ status = omap1_spi100k_setup_transfer(spi, NULL);
+ }
+
+ if (cs_active)
+ omap1_spi100k_force_cs(spi100k, 0);
+
+ m->status = status;
+ m->complete(m->context);
+
+ spin_lock_irq(&spi100k->lock);
+ }
+
+ clk_disable(spi100k->ick);
+ clk_disable(spi100k->fck);
+ spin_unlock_irq(&spi100k->lock);
+
+ if (status < 0)
+ printk(KERN_WARNING "spi transfer failed with %d\n", status);
+}
+
+static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
+{
+ struct omap1_spi100k *spi100k;
+ unsigned long flags;
+ struct spi_transfer *t;
+
+ m->actual_length = 0;
+ m->status = -EINPROGRESS;
+
+ spi100k = spi_master_get_devdata(spi->master);
+
+ /* Don't accept new work if we're shutting down */
+ if (spi100k->state == SPI_SHUTDOWN)
+ return -ESHUTDOWN;
+
+ /* reject invalid messages and transfers */
+ if (list_empty(&m->transfers) || !m->complete)
+ return -EINVAL;
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ const void *tx_buf = t->tx_buf;
+ void *rx_buf = t->rx_buf;
+ unsigned len = t->len;
+
+ if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
+ || (len && !(rx_buf || tx_buf))
+ || (t->bits_per_word &&
+ ( t->bits_per_word < 4
+ || t->bits_per_word > 32))) {
+ dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
+ t->speed_hz,
+ len,
+ tx_buf ? "tx" : "",
+ rx_buf ? "rx" : "",
+ t->bits_per_word);
+ return -EINVAL;
+ }
+
+ if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
+ dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
+ t->speed_hz,
+ OMAP1_SPI100K_MAX_FREQ/(1<<16));
+ return -EINVAL;
+ }
+
+ }
+
+ spin_lock_irqsave(&spi100k->lock, flags);
+ list_add_tail(&m->queue, &spi100k->msg_queue);
+ queue_work(omap1_spi100k_wq, &spi100k->work);
+ spin_unlock_irqrestore(&spi100k->lock, flags);
+
+ return 0;
+}
+
+static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k)
+{
+ return 0;
+}
+
+static int __devinit omap1_spi100k_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct omap1_spi100k *spi100k;
+ int status = 0;
+
+ if (!pdev->id)
+ return -EINVAL;
+
+ master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
+ if (master == NULL) {
+ dev_dbg(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ if (pdev->id != -1)
+ master->bus_num = pdev->id;
+
+ master->setup = omap1_spi100k_setup;
+ master->transfer = omap1_spi100k_transfer;
+ master->cleanup = NULL;
+ master->num_chipselect = 2;
+ master->mode_bits = MODEBITS;
+
+ dev_set_drvdata(&pdev->dev, master);
+
+ spi100k = spi_master_get_devdata(master);
+ spi100k->master = master;
+
+ /*
+ * The memory region base address is taken as the platform_data.
+ * You should allocate this with ioremap() before initializing
+ * the SPI.
+ */
+ spi100k->base = (void __iomem *) pdev->dev.platform_data;
+
+ INIT_WORK(&spi100k->work, omap1_spi100k_work);
+
+ spin_lock_init(&spi100k->lock);
+ INIT_LIST_HEAD(&spi100k->msg_queue);
+ spi100k->ick = clk_get(&pdev->dev, "ick");
+ if (IS_ERR(spi100k->ick)) {
+ dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
+ status = PTR_ERR(spi100k->ick);
+ goto err1;
+ }
+
+ spi100k->fck = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(spi100k->fck)) {
+ dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
+ status = PTR_ERR(spi100k->fck);
+ goto err2;
+ }
+
+ if (omap1_spi100k_reset(spi100k) < 0)
+ goto err3;
+
+ status = spi_register_master(master);
+ if (status < 0)
+ goto err3;
+
+ spi100k->state = SPI_RUNNING;
+
+ return status;
+
+err3:
+ clk_put(spi100k->fck);
+err2:
+ clk_put(spi100k->ick);
+err1:
+ spi_master_put(master);
+ return status;
+}
+
+static int __exit omap1_spi100k_remove(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct omap1_spi100k *spi100k;
+ struct resource *r;
+ unsigned limit = 500;
+ unsigned long flags;
+ int status = 0;
+
+ master = dev_get_drvdata(&pdev->dev);
+ spi100k = spi_master_get_devdata(master);
+
+ spin_lock_irqsave(&spi100k->lock, flags);
+
+ spi100k->state = SPI_SHUTDOWN;
+ while (!list_empty(&spi100k->msg_queue) && limit--) {
+ spin_unlock_irqrestore(&spi100k->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&spi100k->lock, flags);
+ }
+
+ if (!list_empty(&spi100k->msg_queue))
+ status = -EBUSY;
+
+ spin_unlock_irqrestore(&spi100k->lock, flags);
+
+ if (status != 0)
+ return status;
+
+ clk_put(spi100k->fck);
+ clk_put(spi100k->ick);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ spi_unregister_master(master);
+
+ return 0;
+}
+
+static struct platform_driver omap1_spi100k_driver = {
+ .driver = {
+ .name = "omap1_spi100k",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(omap1_spi100k_remove),
+};
+
+
+static int __init omap1_spi100k_init(void)
+{
+ omap1_spi100k_wq = create_singlethread_workqueue(
+ omap1_spi100k_driver.driver.name);
+
+ if (omap1_spi100k_wq == NULL)
+ return -1;
+
+ return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe);
+}
+
+static void __exit omap1_spi100k_exit(void)
+{
+ platform_driver_unregister(&omap1_spi100k_driver);
+
+ destroy_workqueue(omap1_spi100k_wq);
+}
+
+module_init(omap1_spi100k_init);
+module_exit(omap1_spi100k_exit);
+
+MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
+MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index c8c2b693ffac..c2f707e5ce74 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -1709,7 +1709,7 @@ static int pxa2xx_spi_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops pxa2xx_spi_pm_ops = {
+static const struct dev_pm_ops pxa2xx_spi_pm_ops = {
.suspend = pxa2xx_spi_suspend,
.resume = pxa2xx_spi_resume,
};
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c
index 89c22efedfb0..1893f1e96dc4 100644
--- a/drivers/spi/spi_imx.c
+++ b/drivers/spi/spi_imx.c
@@ -44,6 +44,9 @@
#define MXC_CSPIINT 0x0c
#define MXC_RESET 0x1c
+#define MX3_CSPISTAT 0x14
+#define MX3_CSPISTAT_RR (1 << 3)
+
/* generic defines to abstract from the different register layouts */
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
@@ -205,7 +208,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
if (cpu_is_mx31())
reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT;
- else if (cpu_is_mx35()) {
+ else if (cpu_is_mx25() || cpu_is_mx35()) {
reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
reg |= MX31_CSPICTRL_SSCTL;
}
@@ -219,7 +222,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
if (config->cs < 0) {
if (cpu_is_mx31())
reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT;
- else if (cpu_is_mx35())
+ else if (cpu_is_mx25() || cpu_is_mx35())
reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT;
}
@@ -481,7 +484,7 @@ static void spi_imx_cleanup(struct spi_device *spi)
{
}
-static int __init spi_imx_probe(struct platform_device *pdev)
+static int __devinit spi_imx_probe(struct platform_device *pdev)
{
struct spi_imx_master *mxc_platform_info;
struct spi_master *master;
@@ -489,7 +492,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
struct resource *res;
int i, ret;
- mxc_platform_info = (struct spi_imx_master *)pdev->dev.platform_data;
+ mxc_platform_info = dev_get_platdata(&pdev->dev);
if (!mxc_platform_info) {
dev_err(&pdev->dev, "can't get the platform data\n");
return -EINVAL;
@@ -513,11 +516,12 @@ static int __init spi_imx_probe(struct platform_device *pdev)
continue;
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
if (ret) {
- i--;
- while (i > 0)
+ while (i > 0) {
+ i--;
if (spi_imx->chipselect[i] >= 0)
- gpio_free(spi_imx->chipselect[i--]);
- dev_err(&pdev->dev, "can't get cs gpios");
+ gpio_free(spi_imx->chipselect[i]);
+ }
+ dev_err(&pdev->dev, "can't get cs gpios\n");
goto out_master_put;
}
}
@@ -551,7 +555,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
}
spi_imx->irq = platform_get_irq(pdev, 0);
- if (!spi_imx->irq) {
+ if (spi_imx->irq <= 0) {
ret = -EINVAL;
goto out_iounmap;
}
@@ -562,7 +566,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
goto out_iounmap;
}
- if (cpu_is_mx31() || cpu_is_mx35()) {
+ if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) {
spi_imx->intctrl = mx31_intctrl;
spi_imx->config = mx31_config;
spi_imx->trigger = mx31_trigger;
@@ -590,9 +594,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
clk_enable(spi_imx->clk);
spi_imx->spi_clk = clk_get_rate(spi_imx->clk);
- if (!cpu_is_mx31() || !cpu_is_mx35())
+ if (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27())
writel(1, spi_imx->base + MXC_RESET);
+ /* drain receive buffer */
+ if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35())
+ while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR)
+ readl(spi_imx->base + MXC_CSPIRXDATA);
+
spi_imx->intctrl(spi_imx, 0);
ret = spi_bitbang_start(&spi_imx->bitbang);
@@ -625,7 +634,7 @@ out_master_put:
return ret;
}
-static int __exit spi_imx_remove(struct platform_device *pdev)
+static int __devexit spi_imx_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -659,7 +668,7 @@ static struct platform_driver spi_imx_driver = {
.owner = THIS_MODULE,
},
.probe = spi_imx_probe,
- .remove = __exit_p(spi_imx_remove),
+ .remove = __devexit_p(spi_imx_remove),
};
static int __init spi_imx_init(void)
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 930135dc73ba..e9390d747bfc 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -1356,7 +1356,7 @@ static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:mpc8xxx_spi");
static struct platform_driver mpc8xxx_spi_driver = {
.probe = plat_mpc8xxx_spi_probe,
- .remove = __exit_p(plat_mpc8xxx_spi_remove),
+ .remove = __devexit_p(plat_mpc8xxx_spi_remove),
.driver = {
.name = "mpc8xxx_spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi_nuc900.c
new file mode 100644
index 000000000000..b319f9bf9b9b
--- /dev/null
+++ b/drivers/spi/spi_nuc900.c
@@ -0,0 +1,504 @@
+/* linux/drivers/spi/spi_nuc900.c
+ *
+ * Copyright (c) 2009 Nuvoton technology.
+ * Wan ZongShun <mcuos.com@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/init.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <mach/nuc900_spi.h>
+
+/* usi registers offset */
+#define USI_CNT 0x00
+#define USI_DIV 0x04
+#define USI_SSR 0x08
+#define USI_RX0 0x10
+#define USI_TX0 0x10
+
+/* usi register bit */
+#define ENINT (0x01 << 17)
+#define ENFLG (0x01 << 16)
+#define TXNUM (0x03 << 8)
+#define TXNEG (0x01 << 2)
+#define RXNEG (0x01 << 1)
+#define LSB (0x01 << 10)
+#define SELECTLEV (0x01 << 2)
+#define SELECTPOL (0x01 << 31)
+#define SELECTSLAVE 0x01
+#define GOBUSY 0x01
+
+struct nuc900_spi {
+ struct spi_bitbang bitbang;
+ struct completion done;
+ void __iomem *regs;
+ int irq;
+ int len;
+ int count;
+ const unsigned char *tx;
+ unsigned char *rx;
+ struct clk *clk;
+ struct resource *ioarea;
+ struct spi_master *master;
+ struct spi_device *curdev;
+ struct device *dev;
+ struct nuc900_spi_info *pdata;
+ spinlock_t lock;
+ struct resource *res;
+};
+
+static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
+{
+ return spi_master_get_devdata(sdev->master);
+}
+
+static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr)
+{
+ struct nuc900_spi *hw = to_hw(spi);
+ unsigned int val;
+ unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0;
+ unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_SSR);
+
+ if (!cs)
+ val &= ~SELECTLEV;
+ else
+ val |= SELECTLEV;
+
+ if (!ssr)
+ val &= ~SELECTSLAVE;
+ else
+ val |= SELECTSLAVE;
+
+ __raw_writel(val, hw->regs + USI_SSR);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ if (!cpol)
+ val &= ~SELECTPOL;
+ else
+ val |= SELECTPOL;
+
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_spi_chipsel(struct spi_device *spi, int value)
+{
+ switch (value) {
+ case BITBANG_CS_INACTIVE:
+ nuc900_slave_select(spi, 0);
+ break;
+
+ case BITBANG_CS_ACTIVE:
+ nuc900_slave_select(spi, 1);
+ break;
+ }
+}
+
+static void nuc900_spi_setup_txnum(struct nuc900_spi *hw,
+ unsigned int txnum)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ if (!txnum)
+ val &= ~TXNUM;
+ else
+ val |= txnum << 0x08;
+
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+}
+
+static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw,
+ unsigned int txbitlen)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ val |= (txbitlen << 0x03);
+
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_spi_gobusy(struct nuc900_spi *hw)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ val |= GOBUSY;
+
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static int nuc900_spi_setupxfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ return 0;
+}
+
+static int nuc900_spi_setup(struct spi_device *spi)
+{
+ return 0;
+}
+
+static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
+{
+ return hw->tx ? hw->tx[count] : 0;
+}
+
+static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct nuc900_spi *hw = to_hw(spi);
+
+ hw->tx = t->tx_buf;
+ hw->rx = t->rx_buf;
+ hw->len = t->len;
+ hw->count = 0;
+
+ __raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0);
+
+ nuc900_spi_gobusy(hw);
+
+ wait_for_completion(&hw->done);
+
+ return hw->count;
+}
+
+static irqreturn_t nuc900_spi_irq(int irq, void *dev)
+{
+ struct nuc900_spi *hw = dev;
+ unsigned int status;
+ unsigned int count = hw->count;
+
+ status = __raw_readl(hw->regs + USI_CNT);
+ __raw_writel(status, hw->regs + USI_CNT);
+
+ if (status & ENFLG) {
+ hw->count++;
+
+ if (hw->rx)
+ hw->rx[count] = __raw_readl(hw->regs + USI_RX0);
+ count++;
+
+ if (count < hw->len) {
+ __raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0);
+ nuc900_spi_gobusy(hw);
+ } else {
+ complete(&hw->done);
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ complete(&hw->done);
+ return IRQ_HANDLED;
+}
+
+static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ if (edge)
+ val |= TXNEG;
+ else
+ val &= ~TXNEG;
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ if (edge)
+ val |= RXNEG;
+ else
+ val &= ~RXNEG;
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ if (lsb)
+ val |= LSB;
+ else
+ val &= ~LSB;
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ if (sleep)
+ val |= (sleep << 12);
+ else
+ val &= ~(0x0f << 12);
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_enable_int(struct nuc900_spi *hw)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+
+ val = __raw_readl(hw->regs + USI_CNT);
+
+ val |= ENINT;
+
+ __raw_writel(val, hw->regs + USI_CNT);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_set_divider(struct nuc900_spi *hw)
+{
+ __raw_writel(hw->pdata->divider, hw->regs + USI_DIV);
+}
+
+static void nuc900_init_spi(struct nuc900_spi *hw)
+{
+ clk_enable(hw->clk);
+ spin_lock_init(&hw->lock);
+
+ nuc900_tx_edge(hw, hw->pdata->txneg);
+ nuc900_rx_edge(hw, hw->pdata->rxneg);
+ nuc900_send_first(hw, hw->pdata->lsb);
+ nuc900_set_sleep(hw, hw->pdata->sleep);
+ nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen);
+ nuc900_spi_setup_txnum(hw, hw->pdata->txnum);
+ nuc900_set_divider(hw);
+ nuc900_enable_int(hw);
+}
+
+static int __devinit nuc900_spi_probe(struct platform_device *pdev)
+{
+ struct nuc900_spi *hw;
+ struct spi_master *master;
+ int err = 0;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi));
+ if (master == NULL) {
+ dev_err(&pdev->dev, "No memory for spi_master\n");
+ err = -ENOMEM;
+ goto err_nomem;
+ }
+
+ hw = spi_master_get_devdata(master);
+ memset(hw, 0, sizeof(struct nuc900_spi));
+
+ hw->master = spi_master_get(master);
+ hw->pdata = pdev->dev.platform_data;
+ hw->dev = &pdev->dev;
+
+ if (hw->pdata == NULL) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ err = -ENOENT;
+ goto err_pdata;
+ }
+
+ platform_set_drvdata(pdev, hw);
+ init_completion(&hw->done);
+
+ master->mode_bits = SPI_MODE_0;
+ master->num_chipselect = hw->pdata->num_cs;
+ master->bus_num = hw->pdata->bus_num;
+ hw->bitbang.master = hw->master;
+ hw->bitbang.setup_transfer = nuc900_spi_setupxfer;
+ hw->bitbang.chipselect = nuc900_spi_chipsel;
+ hw->bitbang.txrx_bufs = nuc900_spi_txrx;
+ hw->bitbang.master->setup = nuc900_spi_setup;
+
+ hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (hw->res == NULL) {
+ dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+ err = -ENOENT;
+ goto err_pdata;
+ }
+
+ hw->ioarea = request_mem_region(hw->res->start,
+ resource_size(hw->res), pdev->name);
+
+ if (hw->ioarea == NULL) {
+ dev_err(&pdev->dev, "Cannot reserve region\n");
+ err = -ENXIO;
+ goto err_pdata;
+ }
+
+ hw->regs = ioremap(hw->res->start, resource_size(hw->res));
+ if (hw->regs == NULL) {
+ dev_err(&pdev->dev, "Cannot map IO\n");
+ err = -ENXIO;
+ goto err_iomap;
+ }
+
+ hw->irq = platform_get_irq(pdev, 0);
+ if (hw->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ specified\n");
+ err = -ENOENT;
+ goto err_irq;
+ }
+
+ err = request_irq(hw->irq, nuc900_spi_irq, 0, pdev->name, hw);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot claim IRQ\n");
+ goto err_irq;
+ }
+
+ hw->clk = clk_get(&pdev->dev, "spi");
+ if (IS_ERR(hw->clk)) {
+ dev_err(&pdev->dev, "No clock for device\n");
+ err = PTR_ERR(hw->clk);
+ goto err_clk;
+ }
+
+ mfp_set_groupg(&pdev->dev);
+ nuc900_init_spi(hw);
+
+ err = spi_bitbang_start(&hw->bitbang);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register SPI master\n");
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+ clk_disable(hw->clk);
+ clk_put(hw->clk);
+err_clk:
+ free_irq(hw->irq, hw);
+err_irq:
+ iounmap(hw->regs);
+err_iomap:
+ release_mem_region(hw->res->start, resource_size(hw->res));
+ kfree(hw->ioarea);
+err_pdata:
+ spi_master_put(hw->master);;
+
+err_nomem:
+ return err;
+}
+
+static int __devexit nuc900_spi_remove(struct platform_device *dev)
+{
+ struct nuc900_spi *hw = platform_get_drvdata(dev);
+
+ free_irq(hw->irq, hw);
+
+ platform_set_drvdata(dev, NULL);
+
+ spi_unregister_master(hw->master);
+
+ clk_disable(hw->clk);
+ clk_put(hw->clk);
+
+ iounmap(hw->regs);
+
+ release_mem_region(hw->res->start, resource_size(hw->res));
+ kfree(hw->ioarea);
+
+ spi_master_put(hw->master);
+ return 0;
+}
+
+static struct platform_driver nuc900_spi_driver = {
+ .probe = nuc900_spi_probe,
+ .remove = __devexit_p(nuc900_spi_remove),
+ .driver = {
+ .name = "nuc900-spi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init nuc900_spi_init(void)
+{
+ return platform_driver_register(&nuc900_spi_driver);
+}
+
+static void __exit nuc900_spi_exit(void)
+{
+ platform_driver_unregister(&nuc900_spi_driver);
+}
+
+module_init(nuc900_spi_init);
+module_exit(nuc900_spi_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("nuc900 spi driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-spi");
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 33d94f76b9ef..276591569c8b 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -489,7 +489,7 @@ static int s3c24xx_spi_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops s3c24xx_spi_pmops = {
+static const struct dev_pm_ops s3c24xx_spi_pmops = {
.suspend = s3c24xx_spi_suspend,
.resume = s3c24xx_spi_resume,
};
diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c
new file mode 100644
index 000000000000..51e5e1dfa6e5
--- /dev/null
+++ b/drivers/spi/spi_sh_msiof.c
@@ -0,0 +1,691 @@
+/*
+ * SuperH MSIOF SPI Master Interface
+ *
+ * Copyright (c) 2009 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/sh_msiof.h>
+
+#include <asm/spi.h>
+#include <asm/unaligned.h>
+
+struct sh_msiof_spi_priv {
+ struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */
+ void __iomem *mapbase;
+ struct clk *clk;
+ struct platform_device *pdev;
+ struct sh_msiof_spi_info *info;
+ struct completion done;
+ unsigned long flags;
+ int tx_fifo_size;
+ int rx_fifo_size;
+};
+
+#define TMDR1 0x00
+#define TMDR2 0x04
+#define TMDR3 0x08
+#define RMDR1 0x10
+#define RMDR2 0x14
+#define RMDR3 0x18
+#define TSCR 0x20
+#define RSCR 0x22
+#define CTR 0x28
+#define FCTR 0x30
+#define STR 0x40
+#define IER 0x44
+#define TDR1 0x48
+#define TDR2 0x4c
+#define TFDR 0x50
+#define RDR1 0x58
+#define RDR2 0x5c
+#define RFDR 0x60
+
+#define CTR_TSCKE (1 << 15)
+#define CTR_TFSE (1 << 14)
+#define CTR_TXE (1 << 9)
+#define CTR_RXE (1 << 8)
+
+#define STR_TEOF (1 << 23)
+#define STR_REOF (1 << 7)
+
+static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
+{
+ switch (reg_offs) {
+ case TSCR:
+ case RSCR:
+ return ioread16(p->mapbase + reg_offs);
+ default:
+ return ioread32(p->mapbase + reg_offs);
+ }
+}
+
+static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
+ unsigned long value)
+{
+ switch (reg_offs) {
+ case TSCR:
+ case RSCR:
+ iowrite16(value, p->mapbase + reg_offs);
+ break;
+ default:
+ iowrite32(value, p->mapbase + reg_offs);
+ break;
+ }
+}
+
+static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
+ unsigned long clr, unsigned long set)
+{
+ unsigned long mask = clr | set;
+ unsigned long data;
+ int k;
+
+ data = sh_msiof_read(p, CTR);
+ data &= ~clr;
+ data |= set;
+ sh_msiof_write(p, CTR, data);
+
+ for (k = 100; k > 0; k--) {
+ if ((sh_msiof_read(p, CTR) & mask) == set)
+ break;
+
+ udelay(10);
+ }
+
+ return k > 0 ? 0 : -ETIMEDOUT;
+}
+
+static irqreturn_t sh_msiof_spi_irq(int irq, void *data)
+{
+ struct sh_msiof_spi_priv *p = data;
+
+ /* just disable the interrupt and wake up */
+ sh_msiof_write(p, IER, 0);
+ complete(&p->done);
+
+ return IRQ_HANDLED;
+}
+
+static struct {
+ unsigned short div;
+ unsigned short scr;
+} const sh_msiof_spi_clk_table[] = {
+ { 1, 0x0007 },
+ { 2, 0x0000 },
+ { 4, 0x0001 },
+ { 8, 0x0002 },
+ { 16, 0x0003 },
+ { 32, 0x0004 },
+ { 64, 0x1f00 },
+ { 128, 0x1f01 },
+ { 256, 0x1f02 },
+ { 512, 0x1f03 },
+ { 1024, 0x1f04 },
+};
+
+static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
+ unsigned long parent_rate,
+ unsigned long spi_hz)
+{
+ unsigned long div = 1024;
+ size_t k;
+
+ if (!WARN_ON(!spi_hz || !parent_rate))
+ div = parent_rate / spi_hz;
+
+ /* TODO: make more fine grained */
+
+ for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_clk_table); k++) {
+ if (sh_msiof_spi_clk_table[k].div >= div)
+ break;
+ }
+
+ k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
+
+ sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
+ sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
+}
+
+static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
+ int cpol, int cpha,
+ int tx_hi_z, int lsb_first)
+{
+ unsigned long tmp;
+ int edge;
+
+ /*
+ * CPOL CPHA TSCKIZ RSCKIZ TEDG REDG(!)
+ * 0 0 10 10 1 0
+ * 0 1 10 10 0 1
+ * 1 0 11 11 0 1
+ * 1 1 11 11 1 0
+ *
+ * (!) Note: REDG is inverted recommended data sheet setting
+ */
+
+ sh_msiof_write(p, FCTR, 0);
+ sh_msiof_write(p, TMDR1, 0xe2000005 | (lsb_first << 24));
+ sh_msiof_write(p, RMDR1, 0x22000005 | (lsb_first << 24));
+
+ tmp = 0xa0000000;
+ tmp |= cpol << 30; /* TSCKIZ */
+ tmp |= cpol << 28; /* RSCKIZ */
+
+ edge = cpol ? cpha : !cpha;
+
+ tmp |= edge << 27; /* TEDG */
+ tmp |= !edge << 26; /* REDG */
+ tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */
+ sh_msiof_write(p, CTR, tmp);
+}
+
+static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
+ const void *tx_buf, void *rx_buf,
+ int bits, int words)
+{
+ unsigned long dr2;
+
+ dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
+
+ if (tx_buf)
+ sh_msiof_write(p, TMDR2, dr2);
+ else
+ sh_msiof_write(p, TMDR2, dr2 | 1);
+
+ if (rx_buf)
+ sh_msiof_write(p, RMDR2, dr2);
+
+ sh_msiof_write(p, IER, STR_TEOF | STR_REOF);
+}
+
+static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
+{
+ sh_msiof_write(p, STR, sh_msiof_read(p, STR));
+}
+
+static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
+ const void *tx_buf, int words, int fs)
+{
+ const unsigned char *buf_8 = tx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ sh_msiof_write(p, TFDR, buf_8[k] << fs);
+}
+
+static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
+ const void *tx_buf, int words, int fs)
+{
+ const unsigned short *buf_16 = tx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ sh_msiof_write(p, TFDR, buf_16[k] << fs);
+}
+
+static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
+ const void *tx_buf, int words, int fs)
+{
+ const unsigned short *buf_16 = tx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ sh_msiof_write(p, TFDR, get_unaligned(&buf_16[k]) << fs);
+}
+
+static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
+ const void *tx_buf, int words, int fs)
+{
+ const unsigned int *buf_32 = tx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ sh_msiof_write(p, TFDR, buf_32[k] << fs);
+}
+
+static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p,
+ const void *tx_buf, int words, int fs)
+{
+ const unsigned int *buf_32 = tx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs);
+}
+
+static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
+ void *rx_buf, int words, int fs)
+{
+ unsigned char *buf_8 = rx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ buf_8[k] = sh_msiof_read(p, RFDR) >> fs;
+}
+
+static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
+ void *rx_buf, int words, int fs)
+{
+ unsigned short *buf_16 = rx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ buf_16[k] = sh_msiof_read(p, RFDR) >> fs;
+}
+
+static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
+ void *rx_buf, int words, int fs)
+{
+ unsigned short *buf_16 = rx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_16[k]);
+}
+
+static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
+ void *rx_buf, int words, int fs)
+{
+ unsigned int *buf_32 = rx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ buf_32[k] = sh_msiof_read(p, RFDR) >> fs;
+}
+
+static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p,
+ void *rx_buf, int words, int fs)
+{
+ unsigned int *buf_32 = rx_buf;
+ int k;
+
+ for (k = 0; k < words; k++)
+ put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]);
+}
+
+static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
+{
+ int bits;
+
+ bits = t ? t->bits_per_word : 0;
+ bits = bits ? bits : spi->bits_per_word;
+ return bits;
+}
+
+static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ unsigned long hz;
+
+ hz = t ? t->speed_hz : 0;
+ hz = hz ? hz : spi->max_speed_hz;
+ return hz;
+}
+
+static int sh_msiof_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ int bits;
+
+ /* noting to check hz values against since parent clock is disabled */
+
+ bits = sh_msiof_spi_bits(spi, t);
+ if (bits < 8)
+ return -EINVAL;
+ if (bits > 32)
+ return -EINVAL;
+
+ return spi_bitbang_setup_transfer(spi, t);
+}
+
+static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on)
+{
+ struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
+ int value;
+
+ /* chip select is active low unless SPI_CS_HIGH is set */
+ if (spi->mode & SPI_CS_HIGH)
+ value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0;
+ else
+ value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1;
+
+ if (is_on == BITBANG_CS_ACTIVE) {
+ if (!test_and_set_bit(0, &p->flags)) {
+ pm_runtime_get_sync(&p->pdev->dev);
+ clk_enable(p->clk);
+ }
+
+ /* Configure pins before asserting CS */
+ sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
+ !!(spi->mode & SPI_CPHA),
+ !!(spi->mode & SPI_3WIRE),
+ !!(spi->mode & SPI_LSB_FIRST));
+ }
+
+ /* use spi->controller data for CS (same strategy as spi_gpio) */
+ gpio_set_value((unsigned)spi->controller_data, value);
+
+ if (is_on == BITBANG_CS_INACTIVE) {
+ if (test_and_clear_bit(0, &p->flags)) {
+ clk_disable(p->clk);
+ pm_runtime_put(&p->pdev->dev);
+ }
+ }
+}
+
+static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
+ void (*tx_fifo)(struct sh_msiof_spi_priv *,
+ const void *, int, int),
+ void (*rx_fifo)(struct sh_msiof_spi_priv *,
+ void *, int, int),
+ const void *tx_buf, void *rx_buf,
+ int words, int bits)
+{
+ int fifo_shift;
+ int ret;
+
+ /* limit maximum word transfer to rx/tx fifo size */
+ if (tx_buf)
+ words = min_t(int, words, p->tx_fifo_size);
+ if (rx_buf)
+ words = min_t(int, words, p->rx_fifo_size);
+
+ /* the fifo contents need shifting */
+ fifo_shift = 32 - bits;
+
+ /* setup msiof transfer mode registers */
+ sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words);
+
+ /* write tx fifo */
+ if (tx_buf)
+ tx_fifo(p, tx_buf, words, fifo_shift);
+
+ /* setup clock and rx/tx signals */
+ ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
+ if (rx_buf)
+ ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
+ ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
+
+ /* start by setting frame bit */
+ INIT_COMPLETION(p->done);
+ ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
+ if (ret) {
+ dev_err(&p->pdev->dev, "failed to start hardware\n");
+ goto err;
+ }
+
+ /* wait for tx fifo to be emptied / rx fifo to be filled */
+ wait_for_completion(&p->done);
+
+ /* read rx fifo */
+ if (rx_buf)
+ rx_fifo(p, rx_buf, words, fifo_shift);
+
+ /* clear status bits */
+ sh_msiof_reset_str(p);
+
+ /* shut down frame, tx/tx and clock signals */
+ ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
+ ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
+ if (rx_buf)
+ ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
+ ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
+ if (ret) {
+ dev_err(&p->pdev->dev, "failed to shut down hardware\n");
+ goto err;
+ }
+
+ return words;
+
+ err:
+ sh_msiof_write(p, IER, 0);
+ return ret;
+}
+
+static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
+ void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
+ void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
+ int bits;
+ int bytes_per_word;
+ int bytes_done;
+ int words;
+ int n;
+
+ bits = sh_msiof_spi_bits(spi, t);
+
+ /* setup bytes per word and fifo read/write functions */
+ if (bits <= 8) {
+ bytes_per_word = 1;
+ tx_fifo = sh_msiof_spi_write_fifo_8;
+ rx_fifo = sh_msiof_spi_read_fifo_8;
+ } else if (bits <= 16) {
+ bytes_per_word = 2;
+ if ((unsigned long)t->tx_buf & 0x01)
+ tx_fifo = sh_msiof_spi_write_fifo_16u;
+ else
+ tx_fifo = sh_msiof_spi_write_fifo_16;
+
+ if ((unsigned long)t->rx_buf & 0x01)
+ rx_fifo = sh_msiof_spi_read_fifo_16u;
+ else
+ rx_fifo = sh_msiof_spi_read_fifo_16;
+ } else {
+ bytes_per_word = 4;
+ if ((unsigned long)t->tx_buf & 0x03)
+ tx_fifo = sh_msiof_spi_write_fifo_32u;
+ else
+ tx_fifo = sh_msiof_spi_write_fifo_32;
+
+ if ((unsigned long)t->rx_buf & 0x03)
+ rx_fifo = sh_msiof_spi_read_fifo_32u;
+ else
+ rx_fifo = sh_msiof_spi_read_fifo_32;
+ }
+
+ /* setup clocks (clock already enabled in chipselect()) */
+ sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk),
+ sh_msiof_spi_hz(spi, t));
+
+ /* transfer in fifo sized chunks */
+ words = t->len / bytes_per_word;
+ bytes_done = 0;
+
+ while (bytes_done < t->len) {
+ n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo,
+ t->tx_buf + bytes_done,
+ t->rx_buf + bytes_done,
+ words, bits);
+ if (n < 0)
+ break;
+
+ bytes_done += n * bytes_per_word;
+ words -= n;
+ }
+
+ return bytes_done;
+}
+
+static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
+ u32 word, u8 bits)
+{
+ BUG(); /* unused but needed by bitbang code */
+ return 0;
+}
+
+static int sh_msiof_spi_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct spi_master *master;
+ struct sh_msiof_spi_priv *p;
+ char clk_name[16];
+ int i;
+ int ret;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv));
+ if (master == NULL) {
+ dev_err(&pdev->dev, "failed to allocate spi master\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ p = spi_master_get_devdata(master);
+
+ platform_set_drvdata(pdev, p);
+ p->info = pdev->dev.platform_data;
+ init_completion(&p->done);
+
+ snprintf(clk_name, sizeof(clk_name), "msiof%d", pdev->id);
+ p->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(p->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ ret = PTR_ERR(p->clk);
+ goto err1;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i = platform_get_irq(pdev, 0);
+ if (!r || i < 0) {
+ dev_err(&pdev->dev, "cannot get platform resources\n");
+ ret = -ENOENT;
+ goto err2;
+ }
+ p->mapbase = ioremap_nocache(r->start, resource_size(r));
+ if (!p->mapbase) {
+ dev_err(&pdev->dev, "unable to ioremap\n");
+ ret = -ENXIO;
+ goto err2;
+ }
+
+ ret = request_irq(i, sh_msiof_spi_irq, IRQF_DISABLED,
+ dev_name(&pdev->dev), p);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request irq\n");
+ goto err3;
+ }
+
+ p->pdev = pdev;
+ pm_runtime_enable(&pdev->dev);
+
+ /* The standard version of MSIOF use 64 word FIFOs */
+ p->tx_fifo_size = 64;
+ p->rx_fifo_size = 64;
+
+ /* Platform data may override FIFO sizes */
+ if (p->info->tx_fifo_override)
+ p->tx_fifo_size = p->info->tx_fifo_override;
+ if (p->info->rx_fifo_override)
+ p->rx_fifo_size = p->info->rx_fifo_override;
+
+ /* init master and bitbang code */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
+ master->flags = 0;
+ master->bus_num = pdev->id;
+ master->num_chipselect = p->info->num_chipselect;
+ master->setup = spi_bitbang_setup;
+ master->cleanup = spi_bitbang_cleanup;
+
+ p->bitbang.master = master;
+ p->bitbang.chipselect = sh_msiof_spi_chipselect;
+ p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer;
+ p->bitbang.txrx_bufs = sh_msiof_spi_txrx;
+ p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word;
+ p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word;
+ p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word;
+ p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word;
+
+ ret = spi_bitbang_start(&p->bitbang);
+ if (ret == 0)
+ return 0;
+
+ pm_runtime_disable(&pdev->dev);
+ err3:
+ iounmap(p->mapbase);
+ err2:
+ clk_put(p->clk);
+ err1:
+ spi_master_put(master);
+ err0:
+ return ret;
+}
+
+static int sh_msiof_spi_remove(struct platform_device *pdev)
+{
+ struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = spi_bitbang_stop(&p->bitbang);
+ if (!ret) {
+ pm_runtime_disable(&pdev->dev);
+ free_irq(platform_get_irq(pdev, 0), sh_msiof_spi_irq);
+ iounmap(p->mapbase);
+ clk_put(p->clk);
+ spi_master_put(p->bitbang.master);
+ }
+ return ret;
+}
+
+static int sh_msiof_spi_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = {
+ .runtime_suspend = sh_msiof_spi_runtime_nop,
+ .runtime_resume = sh_msiof_spi_runtime_nop,
+};
+
+static struct platform_driver sh_msiof_spi_drv = {
+ .probe = sh_msiof_spi_probe,
+ .remove = sh_msiof_spi_remove,
+ .driver = {
+ .name = "spi_sh_msiof",
+ .owner = THIS_MODULE,
+ .pm = &sh_msiof_spi_dev_pm_ops,
+ },
+};
+
+static int __init sh_msiof_spi_init(void)
+{
+ return platform_driver_register(&sh_msiof_spi_drv);
+}
+module_init(sh_msiof_spi_init);
+
+static void __exit sh_msiof_spi_exit(void)
+{
+ platform_driver_unregister(&sh_msiof_spi_drv);
+}
+module_exit(sh_msiof_spi_exit);
+
+MODULE_DESCRIPTION("SuperH MSIOF SPI Master Interface Driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spi_sh_msiof");
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 20d7322e2f71..9c446e6003d5 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -266,15 +266,15 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->delay_usecs = u_tmp->delay_usecs;
k_tmp->speed_hz = u_tmp->speed_hz;
#ifdef VERBOSE
- dev_dbg(&spi->dev,
+ dev_dbg(&spidev->spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "",
u_tmp->cs_change ? "cs " : "",
- u_tmp->bits_per_word ? : spi->bits_per_word,
+ u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
u_tmp->delay_usecs,
- u_tmp->speed_hz ? : spi->max_speed_hz);
+ u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg);
}
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index 5a143b9f6361..9f386379c169 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -14,22 +14,20 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-#include <linux/of_spi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/io.h>
+#include "xilinx_spi.h"
+#include <linux/spi/xilinx_spi.h>
+
#define XILINX_SPI_NAME "xilinx_spi"
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
* Product Specification", DS464
*/
-#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */
+#define XSPI_CR_OFFSET 0x60 /* Control Register */
#define XSPI_CR_ENABLE 0x02
#define XSPI_CR_MASTER_MODE 0x04
@@ -40,8 +38,9 @@
#define XSPI_CR_RXFIFO_RESET 0x40
#define XSPI_CR_MANUAL_SSELECT 0x80
#define XSPI_CR_TRANS_INHIBIT 0x100
+#define XSPI_CR_LSB_FIRST 0x200
-#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */
+#define XSPI_SR_OFFSET 0x64 /* Status Register */
#define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */
#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */
@@ -49,8 +48,8 @@
#define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */
#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */
-#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */
-#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */
+#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */
+#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */
#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */
@@ -70,6 +69,7 @@
#define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */
#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */
#define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */
+#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */
#define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */
#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */
@@ -78,35 +78,85 @@ struct xilinx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
-
+ struct resource mem; /* phys mem */
void __iomem *regs; /* virt. address of the control registers */
u32 irq;
- u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */
-
u8 *rx_ptr; /* pointer in the Tx buffer */
const u8 *tx_ptr; /* pointer in the Rx buffer */
int remaining_bytes; /* the number of bytes left to transfer */
+ u8 bits_per_word;
+ unsigned int (*read_fn) (void __iomem *);
+ void (*write_fn) (u32, void __iomem *);
+ void (*tx_fn) (struct xilinx_spi *);
+ void (*rx_fn) (struct xilinx_spi *);
};
-static void xspi_init_hw(void __iomem *regs_base)
+static void xspi_tx8(struct xilinx_spi *xspi)
+{
+ xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET);
+ xspi->tx_ptr++;
+}
+
+static void xspi_tx16(struct xilinx_spi *xspi)
+{
+ xspi->write_fn(*(u16 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET);
+ xspi->tx_ptr += 2;
+}
+
+static void xspi_tx32(struct xilinx_spi *xspi)
+{
+ xspi->write_fn(*(u32 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET);
+ xspi->tx_ptr += 4;
+}
+
+static void xspi_rx8(struct xilinx_spi *xspi)
+{
+ u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
+ if (xspi->rx_ptr) {
+ *xspi->rx_ptr = data & 0xff;
+ xspi->rx_ptr++;
+ }
+}
+
+static void xspi_rx16(struct xilinx_spi *xspi)
{
+ u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
+ if (xspi->rx_ptr) {
+ *(u16 *)(xspi->rx_ptr) = data & 0xffff;
+ xspi->rx_ptr += 2;
+ }
+}
+
+static void xspi_rx32(struct xilinx_spi *xspi)
+{
+ u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
+ if (xspi->rx_ptr) {
+ *(u32 *)(xspi->rx_ptr) = data;
+ xspi->rx_ptr += 4;
+ }
+}
+
+static void xspi_init_hw(struct xilinx_spi *xspi)
+{
+ void __iomem *regs_base = xspi->regs;
+
/* Reset the SPI device */
- out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
- XIPIF_V123B_RESET_MASK);
+ xspi->write_fn(XIPIF_V123B_RESET_MASK,
+ regs_base + XIPIF_V123B_RESETR_OFFSET);
/* Disable all the interrupts just in case */
- out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+ xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET);
/* Enable the global IPIF interrupt */
- out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
- XIPIF_V123B_GINTR_ENABLE);
+ xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
+ regs_base + XIPIF_V123B_DGIER_OFFSET);
/* Deselect the slave on the SPI bus */
- out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+ xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET);
/* Disable the transmitter, enable Manual Slave Select Assertion,
* put SPI controller into master mode, and enable it */
- out_be16(regs_base + XSPI_CR_OFFSET,
- XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
- | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+ xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT |
+ XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET |
+ XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET);
}
static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
@@ -115,16 +165,16 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
if (is_on == BITBANG_CS_INACTIVE) {
/* Deselect the slave on the SPI bus */
- out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
+ xspi->write_fn(0xffff, xspi->regs + XSPI_SSR_OFFSET);
} else if (is_on == BITBANG_CS_ACTIVE) {
/* Set the SPI clock phase and polarity */
- u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
+ u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET)
& ~XSPI_CR_MODE_MASK;
if (spi->mode & SPI_CPHA)
cr |= XSPI_CR_CPHA;
if (spi->mode & SPI_CPOL)
cr |= XSPI_CR_CPOL;
- out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+ xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
/* We do not check spi->max_speed_hz here as the SPI clock
* frequency is not software programmable (the IP block design
@@ -132,25 +182,27 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
*/
/* Activate the chip select */
- out_be32(xspi->regs + XSPI_SSR_OFFSET,
- ~(0x0001 << spi->chip_select));
+ xspi->write_fn(~(0x0001 << spi->chip_select),
+ xspi->regs + XSPI_SSR_OFFSET);
}
}
/* spi_bitbang requires custom setup_transfer() to be defined if there is a
* custom txrx_bufs(). We have nothing to setup here as the SPI IP block
- * supports just 8 bits per word, and SPI clock can't be changed in software.
- * Check for 8 bits per word. Chip select delay calculations could be
+ * supports 8 or 16 bits per word which cannot be changed in software.
+ * SPI clock can't be changed in software either.
+ * Check for correct bits per word. Chip select delay calculations could be
* added here as soon as bitbang_work() can be made aware of the delay value.
*/
static int xilinx_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
+ struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
u8 bits_per_word;
bits_per_word = (t && t->bits_per_word)
? t->bits_per_word : spi->bits_per_word;
- if (bits_per_word != 8) {
+ if (bits_per_word != xspi->bits_per_word) {
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
__func__, bits_per_word);
return -EINVAL;
@@ -161,17 +213,16 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
static int xilinx_spi_setup(struct spi_device *spi)
{
- struct spi_bitbang *bitbang;
- struct xilinx_spi *xspi;
- int retval;
-
- xspi = spi_master_get_devdata(spi->master);
- bitbang = &xspi->bitbang;
-
- retval = xilinx_spi_setup_transfer(spi, NULL);
- if (retval < 0)
- return retval;
-
+ /* always return 0, we can not check the number of bits.
+ * There are cases when SPI setup is called before any driver is
+ * there, in that case the SPI core defaults to 8 bits, which we
+ * do not support in some cases. But if we return an error, the
+ * SPI device would not be registered and no driver can get hold of it
+ * When the driver is there, it will call SPI setup again with the
+ * correct number of bits per transfer.
+ * If a driver setups with the wrong bit number, it will fail when
+ * it tries to do a transfer
+ */
return 0;
}
@@ -180,15 +231,14 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
u8 sr;
/* Fill the Tx FIFO with as many bytes as possible */
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
- if (xspi->tx_ptr) {
- out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
- } else {
- out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
- }
- xspi->remaining_bytes--;
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ if (xspi->tx_ptr)
+ xspi->tx_fn(xspi);
+ else
+ xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET);
+ xspi->remaining_bytes -= xspi->bits_per_word / 8;
+ sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
}
}
@@ -210,18 +260,19 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Enable the transmit empty interrupt, which we use to determine
* progress on the transmission.
*/
- ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
- out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
- ipif_ier | XSPI_INTR_TX_EMPTY);
+ ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET);
+ xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY,
+ xspi->regs + XIPIF_V123B_IIER_OFFSET);
/* Start the transfer by not inhibiting the transmitter any longer */
- cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
- out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+ cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) &
+ ~XSPI_CR_TRANS_INHIBIT;
+ xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
wait_for_completion(&xspi->done);
/* Disable the transmit empty interrupt */
- out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+ xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET);
return t->len - xspi->remaining_bytes;
}
@@ -238,8 +289,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
u32 ipif_isr;
/* Get the IPIF interrupts, and clear them immediately */
- ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
- out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+ ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
+ xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET);
if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
u16 cr;
@@ -250,20 +301,15 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
* transmitter while the Isr refills the transmit register/FIFO,
* or make sure it is stopped if we're done.
*/
- cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
- out_be16(xspi->regs + XSPI_CR_OFFSET,
- cr | XSPI_CR_TRANS_INHIBIT);
+ cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
+ xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
+ xspi->regs + XSPI_CR_OFFSET);
/* Read out all the data from the Rx FIFO */
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
- u8 data;
-
- data = in_8(xspi->regs + XSPI_RXD_OFFSET);
- if (xspi->rx_ptr) {
- *xspi->rx_ptr++ = data;
- }
- sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+ xspi->rx_fn(xspi);
+ sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
}
/* See if there is more data to send */
@@ -272,7 +318,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
/* Start the transfer by not inhibiting the
* transmitter any longer
*/
- out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+ xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
} else {
/* No more data to send.
* Indicate the transfer is completed.
@@ -284,40 +330,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __init xilinx_spi_of_probe(struct of_device *ofdev,
- const struct of_device_id *match)
+struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
+ u32 irq, s16 bus_num)
{
struct spi_master *master;
struct xilinx_spi *xspi;
- struct resource r_irq_struct;
- struct resource r_mem_struct;
-
- struct resource *r_irq = &r_irq_struct;
- struct resource *r_mem = &r_mem_struct;
- int rc = 0;
- const u32 *prop;
- int len;
-
- /* Get resources(memory, IRQ) associated with the device */
- master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi));
+ struct xspi_platform_data *pdata = dev->platform_data;
+ int ret;
- if (master == NULL) {
- return -ENOMEM;
+ if (!pdata) {
+ dev_err(dev, "No platform data attached\n");
+ return NULL;
}
- dev_set_drvdata(&ofdev->dev, master);
-
- rc = of_address_to_resource(ofdev->node, 0, r_mem);
- if (rc) {
- dev_warn(&ofdev->dev, "invalid address\n");
- goto put_master;
- }
-
- rc = of_irq_to_resource(ofdev->node, 0, r_irq);
- if (rc == NO_IRQ) {
- dev_warn(&ofdev->dev, "no IRQ found\n");
- goto put_master;
- }
+ master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
+ if (!master)
+ return NULL;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA;
@@ -330,128 +358,87 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
xspi->bitbang.master->setup = xilinx_spi_setup;
init_completion(&xspi->done);
- xspi->irq = r_irq->start;
-
- if (!request_mem_region(r_mem->start,
- r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) {
- rc = -ENXIO;
- dev_warn(&ofdev->dev, "memory request failure\n");
+ if (!request_mem_region(mem->start, resource_size(mem),
+ XILINX_SPI_NAME))
goto put_master;
- }
- xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1);
+ xspi->regs = ioremap(mem->start, resource_size(mem));
if (xspi->regs == NULL) {
- rc = -ENOMEM;
- dev_warn(&ofdev->dev, "ioremap failure\n");
- goto release_mem;
+ dev_warn(dev, "ioremap failure\n");
+ goto map_failed;
}
- xspi->irq = r_irq->start;
-
- /* dynamic bus assignment */
- master->bus_num = -1;
- /* number of slave select bits is required */
- prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
- if (!prop || len < sizeof(*prop)) {
- dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
- goto unmap_io;
+ master->bus_num = bus_num;
+ master->num_chipselect = pdata->num_chipselect;
+
+ xspi->mem = *mem;
+ xspi->irq = irq;
+ if (pdata->little_endian) {
+ xspi->read_fn = ioread32;
+ xspi->write_fn = iowrite32;
+ } else {
+ xspi->read_fn = ioread32be;
+ xspi->write_fn = iowrite32be;
}
- master->num_chipselect = *prop;
+ xspi->bits_per_word = pdata->bits_per_word;
+ if (xspi->bits_per_word == 8) {
+ xspi->tx_fn = xspi_tx8;
+ xspi->rx_fn = xspi_rx8;
+ } else if (xspi->bits_per_word == 16) {
+ xspi->tx_fn = xspi_tx16;
+ xspi->rx_fn = xspi_rx16;
+ } else if (xspi->bits_per_word == 32) {
+ xspi->tx_fn = xspi_tx32;
+ xspi->rx_fn = xspi_rx32;
+ } else
+ goto unmap_io;
+
/* SPI controller initializations */
- xspi_init_hw(xspi->regs);
+ xspi_init_hw(xspi);
/* Register for SPI Interrupt */
- rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
- if (rc != 0) {
- dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq);
+ ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+ if (ret)
goto unmap_io;
- }
- rc = spi_bitbang_start(&xspi->bitbang);
- if (rc != 0) {
- dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n");
+ ret = spi_bitbang_start(&xspi->bitbang);
+ if (ret) {
+ dev_err(dev, "spi_bitbang_start FAILED\n");
goto free_irq;
}
- dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
- (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq);
-
- /* Add any subnodes on the SPI bus */
- of_register_spi_devices(master, ofdev->node);
-
- return rc;
+ dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
+ (unsigned long long)mem->start, xspi->regs, xspi->irq);
+ return master;
free_irq:
free_irq(xspi->irq, xspi);
unmap_io:
iounmap(xspi->regs);
-release_mem:
- release_mem_region(r_mem->start, resource_size(r_mem));
+map_failed:
+ release_mem_region(mem->start, resource_size(mem));
put_master:
spi_master_put(master);
- return rc;
+ return NULL;
}
+EXPORT_SYMBOL(xilinx_spi_init);
-static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+void xilinx_spi_deinit(struct spi_master *master)
{
struct xilinx_spi *xspi;
- struct spi_master *master;
- struct resource r_mem;
- master = platform_get_drvdata(ofdev);
xspi = spi_master_get_devdata(master);
spi_bitbang_stop(&xspi->bitbang);
free_irq(xspi->irq, xspi);
iounmap(xspi->regs);
- if (!of_address_to_resource(ofdev->node, 0, &r_mem))
- release_mem_region(r_mem.start, resource_size(&r_mem));
- dev_set_drvdata(&ofdev->dev, 0);
- spi_master_put(xspi->bitbang.master);
-
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:" XILINX_SPI_NAME);
-
-static int __exit xilinx_spi_of_remove(struct of_device *op)
-{
- return xilinx_spi_remove(op);
-}
-static struct of_device_id xilinx_spi_of_match[] = {
- { .compatible = "xlnx,xps-spi-2.00.a", },
- { .compatible = "xlnx,xps-spi-2.00.b", },
- {}
-};
-
-MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
-
-static struct of_platform_driver xilinx_spi_of_driver = {
- .owner = THIS_MODULE,
- .name = "xilinx-xps-spi",
- .match_table = xilinx_spi_of_match,
- .probe = xilinx_spi_of_probe,
- .remove = __exit_p(xilinx_spi_of_remove),
- .driver = {
- .name = "xilinx-xps-spi",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init xilinx_spi_init(void)
-{
- return of_register_platform_driver(&xilinx_spi_of_driver);
+ release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
+ spi_master_put(xspi->bitbang.master);
}
-module_init(xilinx_spi_init);
+EXPORT_SYMBOL(xilinx_spi_deinit);
-static void __exit xilinx_spi_exit(void)
-{
- of_unregister_platform_driver(&xilinx_spi_of_driver);
-}
-module_exit(xilinx_spi_exit);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
MODULE_DESCRIPTION("Xilinx SPI driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h
new file mode 100644
index 000000000000..d211accf68d2
--- /dev/null
+++ b/drivers/spi/xilinx_spi.h
@@ -0,0 +1,32 @@
+/*
+ * Xilinx SPI device driver API and platform data header file
+ *
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _XILINX_SPI_H_
+#define _XILINX_SPI_H_
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#define XILINX_SPI_NAME "xilinx_spi"
+
+struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
+ u32 irq, s16 bus_num);
+
+void xilinx_spi_deinit(struct spi_master *master);
+#endif
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
new file mode 100644
index 000000000000..71dc3adc0495
--- /dev/null
+++ b/drivers/spi/xilinx_spi_of.c
@@ -0,0 +1,134 @@
+/*
+ * Xilinx SPI OF device driver
+ *
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx SPI devices as OF devices
+ *
+ * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_spi.h>
+
+#include <linux/spi/xilinx_spi.h>
+#include "xilinx_spi.h"
+
+
+static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct spi_master *master;
+ struct xspi_platform_data *pdata;
+ struct resource r_mem;
+ struct resource r_irq;
+ int rc = 0;
+ const u32 *prop;
+ int len;
+
+ rc = of_address_to_resource(ofdev->node, 0, &r_mem);
+ if (rc) {
+ dev_warn(&ofdev->dev, "invalid address\n");
+ return rc;
+ }
+
+ rc = of_irq_to_resource(ofdev->node, 0, &r_irq);
+ if (rc == NO_IRQ) {
+ dev_warn(&ofdev->dev, "no IRQ found\n");
+ return -ENODEV;
+ }
+
+ ofdev->dev.platform_data =
+ kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL);
+ pdata = ofdev->dev.platform_data;
+ if (!pdata)
+ return -ENOMEM;
+
+ /* number of slave select bits is required */
+ prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
+ if (!prop || len < sizeof(*prop)) {
+ dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
+ return -EINVAL;
+ }
+ pdata->num_chipselect = *prop;
+ pdata->bits_per_word = 8;
+ master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1);
+ if (!master)
+ return -ENODEV;
+
+ dev_set_drvdata(&ofdev->dev, master);
+
+ /* Add any subnodes on the SPI bus */
+ of_register_spi_devices(master, ofdev->node);
+
+ return 0;
+}
+
+static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+{
+ xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev));
+ dev_set_drvdata(&ofdev->dev, 0);
+ kfree(ofdev->dev.platform_data);
+ ofdev->dev.platform_data = NULL;
+ return 0;
+}
+
+static int __exit xilinx_spi_of_remove(struct of_device *op)
+{
+ return xilinx_spi_remove(op);
+}
+
+static struct of_device_id xilinx_spi_of_match[] = {
+ { .compatible = "xlnx,xps-spi-2.00.a", },
+ { .compatible = "xlnx,xps-spi-2.00.b", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
+
+static struct of_platform_driver xilinx_spi_of_driver = {
+ .match_table = xilinx_spi_of_match,
+ .probe = xilinx_spi_of_probe,
+ .remove = __exit_p(xilinx_spi_of_remove),
+ .driver = {
+ .name = "xilinx-xps-spi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init xilinx_spi_of_init(void)
+{
+ return of_register_platform_driver(&xilinx_spi_of_driver);
+}
+module_init(xilinx_spi_of_init);
+
+static void __exit xilinx_spi_of_exit(void)
+{
+ of_unregister_platform_driver(&xilinx_spi_of_driver);
+}
+module_exit(xilinx_spi_of_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("Xilinx SPI platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/xilinx_spi_pltfm.c b/drivers/spi/xilinx_spi_pltfm.c
new file mode 100644
index 000000000000..24debac646a9
--- /dev/null
+++ b/drivers/spi/xilinx_spi_pltfm.c
@@ -0,0 +1,102 @@
+/*
+ * Support for Xilinx SPI platform devices
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx SPI devices as platform devices
+ *
+ * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/xilinx_spi.h>
+
+#include "xilinx_spi.h"
+
+static int __devinit xilinx_spi_probe(struct platform_device *dev)
+{
+ struct xspi_platform_data *pdata;
+ struct resource *r;
+ int irq;
+ struct spi_master *master;
+ u8 i;
+
+ pdata = dev->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -ENODEV;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ master = xilinx_spi_init(&dev->dev, r, irq, dev->id);
+ if (!master)
+ return -ENODEV;
+
+ for (i = 0; i < pdata->num_devices; i++)
+ spi_new_device(master, pdata->devices + i);
+
+ platform_set_drvdata(dev, master);
+ return 0;
+}
+
+static int __devexit xilinx_spi_remove(struct platform_device *dev)
+{
+ xilinx_spi_deinit(platform_get_drvdata(dev));
+ platform_set_drvdata(dev, 0);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:" XILINX_SPI_NAME);
+
+static struct platform_driver xilinx_spi_driver = {
+ .probe = xilinx_spi_probe,
+ .remove = __devexit_p(xilinx_spi_remove),
+ .driver = {
+ .name = XILINX_SPI_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init xilinx_spi_pltfm_init(void)
+{
+ return platform_driver_register(&xilinx_spi_driver);
+}
+module_init(xilinx_spi_pltfm_init);
+
+static void __exit xilinx_spi_pltfm_exit(void)
+{
+ platform_driver_unregister(&xilinx_spi_driver);
+}
+module_exit(xilinx_spi_pltfm_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("Xilinx SPI platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/cx25821/cx25821-audups11.c b/drivers/staging/cx25821/cx25821-audups11.c
index f78b8912d905..89c8fe2997fa 100644
--- a/drivers/staging/cx25821/cx25821-audups11.c
+++ b/drivers/staging/cx25821/cx25821-audups11.c
@@ -94,36 +94,20 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH11]
- && h->video_dev[SRAM_CH11]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
file->private_data = fh;
fh->dev = dev;
@@ -427,7 +411,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template11 = {
.name = "cx25821-audioupstream",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c
index 8834bc80a5ab..c7c14c7698a7 100644
--- a/drivers/staging/cx25821/cx25821-video.c
+++ b/drivers/staging/cx25821/cx25821-video.c
@@ -184,11 +184,11 @@ struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
if (NULL == vfd)
return NULL;
*vfd = *template;
- vfd->minor = -1;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type,
cx25821_boards[dev->board].name);
+ video_set_drvdata(vfd, dev);
return vfd;
}
@@ -424,7 +424,7 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
void cx25821_videoioctl_unregister(struct cx25821_dev *dev)
{
if (dev->ioctl_dev) {
- if (dev->ioctl_dev->minor != -1)
+ if (video_is_registered(dev->ioctl_dev))
video_unregister_device(dev->ioctl_dev);
else
video_device_release(dev->ioctl_dev);
@@ -438,7 +438,7 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
cx_clear(PCI_INT_MSK, 1);
if (dev->video_dev[chan_num]) {
- if (-1 != dev->video_dev[chan_num]->minor)
+ if (video_is_registered(dev->video_dev[chan_num]))
video_unregister_device(dev->video_dev[chan_num]);
else
video_device_release(dev->video_dev[chan_num]);
diff --git a/drivers/staging/cx25821/cx25821-video0.c b/drivers/staging/cx25821/cx25821-video0.c
index 950fac1d7003..ad7a69129118 100644
--- a/drivers/staging/cx25821/cx25821-video0.c
+++ b/drivers/staging/cx25821/cx25821-video0.c
@@ -94,37 +94,21 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH00]
- && h->video_dev[SRAM_CH00]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
file->private_data = fh;
fh->dev = dev;
@@ -444,7 +428,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template0 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video1.c b/drivers/staging/cx25821/cx25821-video1.c
index a4dddc684adf..e3f3c4ac7908 100644
--- a/drivers/staging/cx25821/cx25821-video1.c
+++ b/drivers/staging/cx25821/cx25821-video1.c
@@ -94,37 +94,21 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH01]
- && h->video_dev[SRAM_CH01]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
file->private_data = fh;
fh->dev = dev;
@@ -444,7 +428,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template1 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video2.c b/drivers/staging/cx25821/cx25821-video2.c
index 8e04e253f5d9..36fb855a497e 100644
--- a/drivers/staging/cx25821/cx25821-video2.c
+++ b/drivers/staging/cx25821/cx25821-video2.c
@@ -94,37 +94,22 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH02]
- && h->video_dev[SRAM_CH02]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->type = type;
@@ -445,7 +430,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template2 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video3.c b/drivers/staging/cx25821/cx25821-video3.c
index 8801a8ead904..1e0f10abdbcd 100644
--- a/drivers/staging/cx25821/cx25821-video3.c
+++ b/drivers/staging/cx25821/cx25821-video3.c
@@ -94,37 +94,22 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH03]
- && h->video_dev[SRAM_CH03]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->type = type;
@@ -444,7 +429,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template3 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video4.c b/drivers/staging/cx25821/cx25821-video4.c
index ab0d747138ad..0cbe7a79d8c0 100644
--- a/drivers/staging/cx25821/cx25821-video4.c
+++ b/drivers/staging/cx25821/cx25821-video4.c
@@ -94,37 +94,22 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH04]
- && h->video_dev[SRAM_CH04]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->type = type;
@@ -443,7 +428,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template4 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video5.c b/drivers/staging/cx25821/cx25821-video5.c
index 7ef0b971f5cf..5dc08adc12e8 100644
--- a/drivers/staging/cx25821/cx25821-video5.c
+++ b/drivers/staging/cx25821/cx25821-video5.c
@@ -94,37 +94,22 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH05]
- && h->video_dev[SRAM_CH05]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->type = type;
@@ -443,7 +428,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template5 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video6.c b/drivers/staging/cx25821/cx25821-video6.c
index 3c41b49e2ea9..2938ad3ad3c5 100644
--- a/drivers/staging/cx25821/cx25821-video6.c
+++ b/drivers/staging/cx25821/cx25821-video6.c
@@ -94,37 +94,22 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH06]
- && h->video_dev[SRAM_CH06]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->type = type;
@@ -443,7 +428,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template6 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-video7.c b/drivers/staging/cx25821/cx25821-video7.c
index 625c9b78a9cf..458e525d72af 100644
--- a/drivers/staging/cx25821/cx25821-video7.c
+++ b/drivers/staging/cx25821/cx25821-video7.c
@@ -93,37 +93,22 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH07]
- && h->video_dev[SRAM_CH07]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
+
file->private_data = fh;
fh->dev = dev;
fh->type = type;
@@ -442,7 +427,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template7 = {
.name = "cx25821-video",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-videoioctl.c b/drivers/staging/cx25821/cx25821-videoioctl.c
index 2a312ce78c63..1da52b54a454 100644
--- a/drivers/staging/cx25821/cx25821-videoioctl.c
+++ b/drivers/staging/cx25821/cx25821-videoioctl.c
@@ -94,36 +94,21 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
u32 pix_format;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->ioctl_dev && h->ioctl_dev->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
file->private_data = fh;
fh->dev = dev;
@@ -489,7 +474,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_videoioctl_template = {
.name = "cx25821-videoioctl",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-vidups10.c b/drivers/staging/cx25821/cx25821-vidups10.c
index 77b63b060405..b76d9f62c3d1 100644
--- a/drivers/staging/cx25821/cx25821-vidups10.c
+++ b/drivers/staging/cx25821/cx25821-vidups10.c
@@ -94,36 +94,20 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH10]
- && h->video_dev[SRAM_CH10]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
file->private_data = fh;
fh->dev = dev;
@@ -428,7 +412,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template10 = {
.name = "cx25821-upstream10",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/cx25821/cx25821-vidups9.c b/drivers/staging/cx25821/cx25821-vidups9.c
index 75c8c1eed2da..1580da3b29aa 100644
--- a/drivers/staging/cx25821/cx25821-vidups9.c
+++ b/drivers/staging/cx25821/cx25821-vidups9.c
@@ -94,36 +94,20 @@ static struct videobuf_queue_ops cx25821_video_qops = {
static int video_open(struct file *file)
{
- int minor = video_devdata(file)->minor;
- struct cx25821_dev *h, *dev = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *dev = video_drvdata(file);
struct cx25821_fh *fh;
- struct list_head *list;
- enum v4l2_buf_type type = 0;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- lock_kernel();
- list_for_each(list, &cx25821_devlist) {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- if (h->video_dev[SRAM_CH09]
- && h->video_dev[SRAM_CH09]->minor == minor) {
- dev = h;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
-
- if (NULL == dev) {
- unlock_kernel();
- return -ENODEV;
- }
-
- printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]);
+ printk("open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- unlock_kernel();
+ if (NULL == fh)
return -ENOMEM;
- }
+
+ lock_kernel();
file->private_data = fh;
fh->dev = dev;
@@ -426,7 +410,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
struct video_device cx25821_video_template9 = {
.name = "cx25821-upstream9",
.fops = &video_fops,
- .minor = -1,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX25821_NORMS,
.current_norm = V4L2_STD_NTSC_M,
diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c
index b18d8e2d4c5e..3af79242313e 100644
--- a/drivers/staging/go7007/go7007-v4l2.c
+++ b/drivers/staging/go7007/go7007-v4l2.c
@@ -1787,7 +1787,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
static struct video_device go7007_template = {
.name = "go7007",
.fops = &go7007_fops,
- .minor = -1,
.release = go7007_vfl_release,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = V4L2_STD_ALL,
@@ -1817,8 +1816,8 @@ int go7007_v4l2_init(struct go7007 *go)
}
video_set_drvdata(go->video_dev, go);
++go->ref_count;
- printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
- go->video_dev->name, go->video_dev->num);
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+ go->video_dev->name, video_device_node_name(go->video_dev));
return 0;
}
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index aa53db9f2e88..1ef3b8fc50b3 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -210,7 +210,7 @@ static int uio_pdrv_genirq_runtime_nop(struct device *dev)
return 0;
}
-static struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
+static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
.runtime_suspend = uio_pdrv_genirq_runtime_nop,
.runtime_resume = uio_pdrv_genirq_runtime_nop,
};
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 91f2885b6ee1..2dcf906df569 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -363,7 +363,7 @@ static int hcd_pci_restore(struct device *dev)
return resume_common(dev, true);
}
-struct dev_pm_ops usb_hcd_pci_pm_ops = {
+const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.suspend = hcd_pci_suspend,
.suspend_noirq = hcd_pci_suspend_noirq,
.resume_noirq = hcd_pci_resume_noirq,
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index d8b43aee581e..bbe2b924aae8 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -330,7 +330,7 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev);
extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
#ifdef CONFIG_PM_SLEEP
-extern struct dev_pm_ops usb_hcd_pci_pm_ops;
+extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
#endif
#endif /* CONFIG_PCI */
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 4e2c6df8d3cc..2fb42043b305 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -167,18 +167,23 @@ struct usb_host_interface *usb_altnum_to_altsetting(
}
EXPORT_SYMBOL_GPL(usb_altnum_to_altsetting);
+struct find_interface_arg {
+ int minor;
+ struct device_driver *drv;
+};
+
static int __find_interface(struct device *dev, void *data)
{
- int *minor = data;
+ struct find_interface_arg *arg = data;
struct usb_interface *intf;
if (!is_usb_interface(dev))
return 0;
+ if (dev->driver != arg->drv)
+ return 0;
intf = to_usb_interface(dev);
- if (intf->minor != -1 && intf->minor == *minor)
- return 1;
- return 0;
+ return intf->minor == arg->minor;
}
/**
@@ -187,14 +192,18 @@ static int __find_interface(struct device *dev, void *data)
* @minor: the minor number of the desired device
*
* This walks the bus device list and returns a pointer to the interface
- * with the matching minor. Note, this only works for devices that share the
- * USB major number.
+ * with the matching minor and driver. Note, this only works for devices
+ * that share the USB major number.
*/
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
+ struct find_interface_arg argb;
struct device *dev;
- dev = bus_find_device(&usb_bus_type, NULL, &minor, __find_interface);
+ argb.minor = minor;
+ argb.drv = &drv->drvwrap.driver;
+
+ dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface);
/* Drop reference count from bus_find_device */
put_device(dev);
@@ -320,7 +329,7 @@ static int usb_dev_restore(struct device *dev)
return usb_resume(dev, PMSG_RESTORE);
}
-static struct dev_pm_ops usb_device_pm_ops = {
+static const struct dev_pm_ops usb_device_pm_ops = {
.prepare = usb_dev_prepare,
.complete = usb_dev_complete,
.suspend = usb_dev_suspend,
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index ed77be76d6bb..dbfb482a94e3 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -297,7 +297,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops au1xxx_ehci_pmops = {
+static const struct dev_pm_ops au1xxx_ehci_pmops = {
.suspend = ehci_hcd_au1xxx_drv_suspend,
.resume = ehci_hcd_au1xxx_drv_resume,
};
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 5c774ab98252..73352f3739b5 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -80,7 +80,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/io.h>
-#include <linux/bitops.h>
+#include <linux/bitmap.h>
#include <asm/irq.h>
#include <asm/system.h>
@@ -190,10 +190,8 @@ static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
struct isp1362_ep *ep, u16 len)
{
int ptd_offset = -EINVAL;
- int index;
int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1;
- int found = -1;
- int last = -1;
+ int found;
BUG_ON(len > epq->buf_size);
@@ -205,20 +203,9 @@ static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map);
BUG_ON(ep->num_ptds != 0);
- for (index = 0; index <= epq->buf_count - num_ptds; index++) {
- if (test_bit(index, &epq->buf_map))
- continue;
- found = index;
- for (last = index + 1; last < index + num_ptds; last++) {
- if (test_bit(last, &epq->buf_map)) {
- found = -1;
- break;
- }
- }
- if (found >= 0)
- break;
- }
- if (found < 0)
+ found = bitmap_find_next_zero_area(&epq->buf_map, epq->buf_count, 0,
+ num_ptds, 0);
+ if (found >= epq->buf_count)
return -EOVERFLOW;
DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__,
@@ -230,8 +217,7 @@ static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
epq->buf_avail -= num_ptds;
BUG_ON(epq->buf_avail > epq->buf_count);
ep->ptd_index = found;
- for (index = found; index < last; index++)
- __set_bit(index, &epq->buf_map);
+ bitmap_set(&epq->buf_map, found, num_ptds);
DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n",
__func__, epq->name, ep->ptd_index, ep->ptd_offset,
epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map);
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index e4380082ebb1..17a6043c1fa0 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -294,7 +294,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops au1xxx_ohci_pmops = {
+static const struct dev_pm_ops au1xxx_ohci_pmops = {
.suspend = ohci_hcd_au1xxx_drv_suspend,
.resume = ohci_hcd_au1xxx_drv_resume,
};
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index f1c06202fdf2..a18debdd79b8 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -518,7 +518,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
+static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
};
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 41dbc70ae752..b7a661c02bcd 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2353,7 +2353,7 @@ static int r8a66597_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops r8a66597_dev_pm_ops = {
+static const struct dev_pm_ops r8a66597_dev_pm_ops = {
.suspend = r8a66597_suspend,
.resume = r8a66597_resume,
.poweroff = r8a66597_suspend,
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 49f2346afad3..bfe08f4975a3 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -2214,7 +2214,7 @@ static int musb_resume_noirq(struct device *dev)
return 0;
}
-static struct dev_pm_ops musb_dev_pm_ops = {
+static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
.resume_noirq = musb_resume_noirq,
};
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index bd9883f41e63..2be9f2fa41f9 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -33,7 +33,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
@@ -276,16 +276,16 @@ static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
{
u8 check;
- if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
- (twl4030_i2c_read_u8(module, &check, address) >= 0) &&
+ if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+ (twl_i2c_read_u8(module, &check, address) >= 0) &&
(check == data))
return 0;
dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
1, module, address, check, data);
/* Failed once: Try again */
- if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
- (twl4030_i2c_read_u8(module, &check, address) >= 0) &&
+ if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+ (twl_i2c_read_u8(module, &check, address) >= 0) &&
(check == data))
return 0;
dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
@@ -303,7 +303,7 @@ static inline int twl4030_usb_write(struct twl4030_usb *twl,
{
int ret = 0;
- ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB, data, address);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_USB, data, address);
if (ret < 0)
dev_dbg(twl->dev,
"TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
@@ -315,7 +315,7 @@ static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
u8 data;
int ret = 0;
- ret = twl4030_i2c_read_u8(module, &data, address);
+ ret = twl_i2c_read_u8(module, &data, address);
if (ret >= 0)
ret = data;
else
@@ -462,7 +462,7 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
* SLEEP. We work around this by clearing the bit after usv3v1
* is re-activated. This ensures that VUSB3V1 is really active.
*/
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
VUSB_DEDICATED2);
regulator_enable(twl->usb1v5);
pwr &= ~PHY_PWR_PHYPWD;
@@ -505,44 +505,44 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
{
/* Enable writing to power configuration registers */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
/* put VUSB3V1 LDO in active state */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
/* input to VUSB3V1 LDO is from VBAT, not VBUS */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
/* Initialize 3.1V regulator */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
twl->usb3v1 = regulator_get(twl->dev, "usb3v1");
if (IS_ERR(twl->usb3v1))
return -ENODEV;
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
/* Initialize 1.5V regulator */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
twl->usb1v5 = regulator_get(twl->dev, "usb1v5");
if (IS_ERR(twl->usb1v5))
goto fail1;
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
/* Initialize 1.8V regulator */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
twl->usb1v8 = regulator_get(twl->dev, "usb1v8");
if (IS_ERR(twl->usb1v8))
goto fail2;
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
/* disable access to power configuration registers */
- twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
+ twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
return 0;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 99c0df1c7ebf..5a5c303a6373 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -614,6 +614,21 @@ config FB_BFIN_T350MCQB
This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI
It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
+config FB_BFIN_LQ035Q1
+ tristate "SHARP LQ035Q1DH02 TFT LCD"
+ depends on FB && BLACKFIN && SPI
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select BFIN_GPTIMERS
+ help
+ This is the framebuffer device driver for a SHARP LQ035Q1DH02 TFT display found on
+ the Blackfin Landscape LCD EZ-Extender Card.
+ This display is a QVGA 320x240 18-bit RGB display interfaced by an 16-bit wide PPI
+ It uses PPI[0..15] PPI_FS1, PPI_FS2 and PPI_CLK.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin-lq035q1-fb.
config FB_STI
tristate "HP STI frame buffer device support"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 0f8da331ba0f..4ecb30c4f3f2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -137,6 +137,7 @@ obj-$(CONFIG_FB_EFI) += efifb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o
obj-$(CONFIG_FB_OF) += offb.o
obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
+obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o
obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c
index b7687c55fe16..2051c9dc813b 100644
--- a/drivers/video/atafb.c
+++ b/drivers/video/atafb.c
@@ -2245,6 +2245,9 @@ static int ext_setcolreg(unsigned int regno, unsigned int red,
if (regno > 255)
return 1;
+ if (regno > 255)
+ return 1;
+
switch (external_card_type) {
case IS_VGA:
OUTB(0x3c8, regno);
diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c
index ad05da5ba3c7..4c10edecfb66 100644
--- a/drivers/video/backlight/adp5520_bl.c
+++ b/drivers/video/backlight/adp5520_bl.c
@@ -15,7 +15,7 @@
struct adp5520_bl {
struct device *master;
- struct adp5520_backlight_platfrom_data *pdata;
+ struct adp5520_backlight_platform_data *pdata;
struct mutex lock;
unsigned long cached_daylight_max;
int id;
@@ -31,29 +31,30 @@ static int adp5520_bl_set(struct backlight_device *bl, int brightness)
if (data->pdata->en_ambl_sens) {
if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) {
/* Disable Ambient Light auto adjust */
- ret |= adp5520_clr_bits(master, BL_CONTROL,
- BL_AUTO_ADJ);
- ret |= adp5520_write(master, DAYLIGHT_MAX, brightness);
+ ret |= adp5520_clr_bits(master, ADP5520_BL_CONTROL,
+ ADP5520_BL_AUTO_ADJ);
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
+ brightness);
} else {
/*
* MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
* restore daylight l3 sysfs brightness
*/
- ret |= adp5520_write(master, DAYLIGHT_MAX,
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
data->cached_daylight_max);
- ret |= adp5520_set_bits(master, BL_CONTROL,
- BL_AUTO_ADJ);
+ ret |= adp5520_set_bits(master, ADP5520_BL_CONTROL,
+ ADP5520_BL_AUTO_ADJ);
}
} else {
- ret |= adp5520_write(master, DAYLIGHT_MAX, brightness);
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, brightness);
}
if (data->current_brightness && brightness == 0)
ret |= adp5520_set_bits(master,
- MODE_STATUS, DIM_EN);
+ ADP5520_MODE_STATUS, ADP5520_DIM_EN);
else if (data->current_brightness == 0 && brightness)
ret |= adp5520_clr_bits(master,
- MODE_STATUS, DIM_EN);
+ ADP5520_MODE_STATUS, ADP5520_DIM_EN);
if (!ret)
data->current_brightness = brightness;
@@ -79,7 +80,7 @@ static int adp5520_bl_get_brightness(struct backlight_device *bl)
int error;
uint8_t reg_val;
- error = adp5520_read(data->master, BL_VALUE, &reg_val);
+ error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val);
return error ? data->current_brightness : reg_val;
}
@@ -93,33 +94,46 @@ static int adp5520_bl_setup(struct backlight_device *bl)
{
struct adp5520_bl *data = bl_get_data(bl);
struct device *master = data->master;
- struct adp5520_backlight_platfrom_data *pdata = data->pdata;
+ struct adp5520_backlight_platform_data *pdata = data->pdata;
int ret = 0;
- ret |= adp5520_write(master, DAYLIGHT_MAX, pdata->l1_daylight_max);
- ret |= adp5520_write(master, DAYLIGHT_DIM, pdata->l1_daylight_dim);
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
+ pdata->l1_daylight_max);
+ ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM,
+ pdata->l1_daylight_dim);
if (pdata->en_ambl_sens) {
data->cached_daylight_max = pdata->l1_daylight_max;
- ret |= adp5520_write(master, OFFICE_MAX, pdata->l2_office_max);
- ret |= adp5520_write(master, OFFICE_DIM, pdata->l2_office_dim);
- ret |= adp5520_write(master, DARK_MAX, pdata->l3_dark_max);
- ret |= adp5520_write(master, DARK_DIM, pdata->l3_dark_dim);
- ret |= adp5520_write(master, L2_TRIP, pdata->l2_trip);
- ret |= adp5520_write(master, L2_HYS, pdata->l2_hyst);
- ret |= adp5520_write(master, L3_TRIP, pdata->l3_trip);
- ret |= adp5520_write(master, L3_HYS, pdata->l3_hyst);
- ret |= adp5520_write(master, ALS_CMPR_CFG,
- ALS_CMPR_CFG_VAL(pdata->abml_filt, L3_EN));
+ ret |= adp5520_write(master, ADP5520_OFFICE_MAX,
+ pdata->l2_office_max);
+ ret |= adp5520_write(master, ADP5520_OFFICE_DIM,
+ pdata->l2_office_dim);
+ ret |= adp5520_write(master, ADP5520_DARK_MAX,
+ pdata->l3_dark_max);
+ ret |= adp5520_write(master, ADP5520_DARK_DIM,
+ pdata->l3_dark_dim);
+ ret |= adp5520_write(master, ADP5520_L2_TRIP,
+ pdata->l2_trip);
+ ret |= adp5520_write(master, ADP5520_L2_HYS,
+ pdata->l2_hyst);
+ ret |= adp5520_write(master, ADP5520_L3_TRIP,
+ pdata->l3_trip);
+ ret |= adp5520_write(master, ADP5520_L3_HYS,
+ pdata->l3_hyst);
+ ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG,
+ ALS_CMPR_CFG_VAL(pdata->abml_filt,
+ ADP5520_L3_EN));
}
- ret |= adp5520_write(master, BL_CONTROL,
- BL_CTRL_VAL(pdata->fade_led_law, pdata->en_ambl_sens));
+ ret |= adp5520_write(master, ADP5520_BL_CONTROL,
+ BL_CTRL_VAL(pdata->fade_led_law,
+ pdata->en_ambl_sens));
- ret |= adp5520_write(master, BL_FADE, FADE_VAL(pdata->fade_in,
+ ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in,
pdata->fade_out));
- ret |= adp5520_set_bits(master, MODE_STATUS, BL_EN | DIM_EN);
+ ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS,
+ ADP5520_BL_EN | ADP5520_DIM_EN);
return ret;
}
@@ -156,29 +170,31 @@ static ssize_t adp5520_store(struct device *dev, const char *buf,
}
static ssize_t adp5520_bl_dark_max_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
- return adp5520_show(dev, buf, DARK_MAX);
+ return adp5520_show(dev, buf, ADP5520_DARK_MAX);
}
static ssize_t adp5520_bl_dark_max_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- return adp5520_store(dev, buf, count, DARK_MAX);
+ return adp5520_store(dev, buf, count, ADP5520_DARK_MAX);
}
static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show,
adp5520_bl_dark_max_store);
static ssize_t adp5520_bl_office_max_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
- return adp5520_show(dev, buf, OFFICE_MAX);
+ return adp5520_show(dev, buf, ADP5520_OFFICE_MAX);
}
static ssize_t adp5520_bl_office_max_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- return adp5520_store(dev, buf, count, OFFICE_MAX);
+ return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX);
}
static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
adp5520_bl_office_max_store);
@@ -186,16 +202,17 @@ static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
static ssize_t adp5520_bl_daylight_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return adp5520_show(dev, buf, DAYLIGHT_MAX);
+ return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX);
}
static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adp5520_bl *data = dev_get_drvdata(dev);
strict_strtoul(buf, 10, &data->cached_daylight_max);
- return adp5520_store(dev, buf, count, DAYLIGHT_MAX);
+ return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX);
}
static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
adp5520_bl_daylight_max_store);
@@ -203,14 +220,14 @@ static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
static ssize_t adp5520_bl_dark_dim_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return adp5520_show(dev, buf, DARK_DIM);
+ return adp5520_show(dev, buf, ADP5520_DARK_DIM);
}
static ssize_t adp5520_bl_dark_dim_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- return adp5520_store(dev, buf, count, DARK_DIM);
+ return adp5520_store(dev, buf, count, ADP5520_DARK_DIM);
}
static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
adp5520_bl_dark_dim_store);
@@ -218,29 +235,29 @@ static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
static ssize_t adp5520_bl_office_dim_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return adp5520_show(dev, buf, OFFICE_DIM);
+ return adp5520_show(dev, buf, ADP5520_OFFICE_DIM);
}
static ssize_t adp5520_bl_office_dim_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- return adp5520_store(dev, buf, count, OFFICE_DIM);
+ return adp5520_store(dev, buf, count, ADP5520_OFFICE_DIM);
}
static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show,
adp5520_bl_office_dim_store);
static ssize_t adp5520_bl_daylight_dim_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
- return adp5520_show(dev, buf, DAYLIGHT_DIM);
+ return adp5520_show(dev, buf, ADP5520_DAYLIGHT_DIM);
}
static ssize_t adp5520_bl_daylight_dim_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- return adp5520_store(dev, buf, count, DAYLIGHT_DIM);
+ return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_DIM);
}
static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show,
adp5520_bl_daylight_dim_store);
@@ -316,7 +333,7 @@ static int __devexit adp5520_bl_remove(struct platform_device *pdev)
struct backlight_device *bl = platform_get_drvdata(pdev);
struct adp5520_bl *data = bl_get_data(bl);
- adp5520_clr_bits(data->master, MODE_STATUS, BL_EN);
+ adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN);
if (data->pdata->en_ambl_sens)
sysfs_remove_group(&bl->dev.kobj,
diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c
index 7fcb0eb54c60..f2d76dae1eb3 100644
--- a/drivers/video/backlight/da903x_bl.c
+++ b/drivers/video/backlight/da903x_bl.c
@@ -177,7 +177,7 @@ static int da903x_backlight_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops da903x_backlight_pm_ops = {
+static const struct dev_pm_ops da903x_backlight_pm_ops = {
.suspend = da903x_backlight_suspend,
.resume = da903x_backlight_resume,
};
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c
index a482dd7b0311..9b3be74cee5a 100644
--- a/drivers/video/backlight/lcd.c
+++ b/drivers/video/backlight/lcd.c
@@ -101,7 +101,7 @@ static ssize_t lcd_store_power(struct device *dev,
int power = simple_strtoul(buf, &endp, 0);
size_t size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
return -EINVAL;
@@ -140,7 +140,7 @@ static ssize_t lcd_store_contrast(struct device *dev,
int contrast = simple_strtoul(buf, &endp, 0);
size_t size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
return -EINVAL;
diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c
new file mode 100644
index 000000000000..b690c269784a
--- /dev/null
+++ b/drivers/video/bfin-lq035q1-fb.c
@@ -0,0 +1,826 @@
+/*
+ * Blackfin LCD Framebuffer driver SHARP LQ035Q1DH02
+ *
+ * Copyright 2008-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#define DRIVER_NAME "bfin-lq035q1"
+#define pr_fmt(fmt) DRIVER_NAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/blackfin.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <asm/gptimers.h>
+
+#include <asm/bfin-lq035q1.h>
+
+#if defined(BF533_FAMILY) || defined(BF538_FAMILY)
+#define TIMER_HSYNC_id TIMER1_id
+#define TIMER_HSYNCbit TIMER1bit
+#define TIMER_HSYNC_STATUS_TRUN TIMER_STATUS_TRUN1
+#define TIMER_HSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL1
+#define TIMER_HSYNC_STATUS_TOVF TIMER_STATUS_TOVF1
+
+#define TIMER_VSYNC_id TIMER2_id
+#define TIMER_VSYNCbit TIMER2bit
+#define TIMER_VSYNC_STATUS_TRUN TIMER_STATUS_TRUN2
+#define TIMER_VSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL2
+#define TIMER_VSYNC_STATUS_TOVF TIMER_STATUS_TOVF2
+#else
+#define TIMER_HSYNC_id TIMER0_id
+#define TIMER_HSYNCbit TIMER0bit
+#define TIMER_HSYNC_STATUS_TRUN TIMER_STATUS_TRUN0
+#define TIMER_HSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL0
+#define TIMER_HSYNC_STATUS_TOVF TIMER_STATUS_TOVF0
+
+#define TIMER_VSYNC_id TIMER1_id
+#define TIMER_VSYNCbit TIMER1bit
+#define TIMER_VSYNC_STATUS_TRUN TIMER_STATUS_TRUN1
+#define TIMER_VSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL1
+#define TIMER_VSYNC_STATUS_TOVF TIMER_STATUS_TOVF1
+#endif
+
+#define LCD_X_RES 320 /* Horizontal Resolution */
+#define LCD_Y_RES 240 /* Vertical Resolution */
+#define DMA_BUS_SIZE 16
+
+#define USE_RGB565_16_BIT_PPI
+
+#ifdef USE_RGB565_16_BIT_PPI
+#define LCD_BPP 16 /* Bit Per Pixel */
+#define CLOCKS_PER_PIX 1
+#define CPLD_PIPELINE_DELAY_COR 0 /* NO CPLB */
+#endif
+
+/* Interface 16/18-bit TFT over an 8-bit wide PPI using a small Programmable Logic Device (CPLD)
+ * http://blackfin.uclinux.org/gf/project/stamp/frs/?action=FrsReleaseBrowse&frs_package_id=165
+ */
+
+#ifdef USE_RGB565_8_BIT_PPI
+#define LCD_BPP 16 /* Bit Per Pixel */
+#define CLOCKS_PER_PIX 2
+#define CPLD_PIPELINE_DELAY_COR 3 /* RGB565 */
+#endif
+
+#ifdef USE_RGB888_8_BIT_PPI
+#define LCD_BPP 24 /* Bit Per Pixel */
+#define CLOCKS_PER_PIX 3
+#define CPLD_PIPELINE_DELAY_COR 5 /* RGB888 */
+#endif
+
+ /*
+ * HS and VS timing parameters (all in number of PPI clk ticks)
+ */
+
+#define U_LINE 4 /* Blanking Lines */
+
+#define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */
+#define H_PERIOD (336 * CLOCKS_PER_PIX) /* HS period */
+#define H_PULSE (2 * CLOCKS_PER_PIX) /* HS pulse width */
+#define H_START (7 * CLOCKS_PER_PIX + CPLD_PIPELINE_DELAY_COR) /* first valid pixel */
+
+#define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */
+#define V_PULSE (2 * CLOCKS_PER_PIX) /* VS pulse width (1-5 H_PERIODs) */
+#define V_PERIOD (H_PERIOD * V_LINES) /* VS period */
+
+#define ACTIVE_VIDEO_MEM_OFFSET ((U_LINE / 2) * LCD_X_RES * (LCD_BPP / 8))
+
+#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
+
+#define PPI_TX_MODE 0x2
+#define PPI_XFER_TYPE_11 0xC
+#define PPI_PORT_CFG_01 0x10
+#define PPI_POLS_1 0x8000
+
+#if (CLOCKS_PER_PIX > 1)
+#define PPI_PMODE (DLEN_8 | PACK_EN)
+#else
+#define PPI_PMODE (DLEN_16)
+#endif
+
+#define LQ035_INDEX 0x74
+#define LQ035_DATA 0x76
+
+#define LQ035_DRIVER_OUTPUT_CTL 0x1
+#define LQ035_SHUT_CTL 0x11
+
+#define LQ035_DRIVER_OUTPUT_MASK (LQ035_LR | LQ035_TB | LQ035_BGR | LQ035_REV)
+#define LQ035_DRIVER_OUTPUT_DEFAULT (0x2AEF & ~LQ035_DRIVER_OUTPUT_MASK)
+
+#define LQ035_SHUT (1 << 0) /* Shutdown */
+#define LQ035_ON (0 << 0) /* Shutdown */
+
+struct bfin_lq035q1fb_info {
+ struct fb_info *fb;
+ struct device *dev;
+ struct spi_driver spidrv;
+ struct bfin_lq035q1fb_disp_info *disp_info;
+ unsigned char *fb_buffer; /* RGB Buffer */
+ dma_addr_t dma_handle;
+ int lq035_open_cnt;
+ int irq;
+ spinlock_t lock; /* lock */
+ u32 pseudo_pal[16];
+};
+
+static int nocursor;
+module_param(nocursor, int, 0644);
+MODULE_PARM_DESC(nocursor, "cursor enable/disable");
+
+struct spi_control {
+ unsigned short mode;
+};
+
+static int lq035q1_control(struct spi_device *spi, unsigned char reg, unsigned short value)
+{
+ int ret;
+ u8 regs[3] = { LQ035_INDEX, 0, 0 };
+ u8 dat[3] = { LQ035_DATA, 0, 0 };
+
+ if (!spi)
+ return -ENODEV;
+
+ regs[2] = reg;
+ dat[1] = value >> 8;
+ dat[2] = value & 0xFF;
+
+ ret = spi_write(spi, regs, ARRAY_SIZE(regs));
+ ret |= spi_write(spi, dat, ARRAY_SIZE(dat));
+ return ret;
+}
+
+static int __devinit lq035q1_spidev_probe(struct spi_device *spi)
+{
+ int ret;
+ struct spi_control *ctl;
+ struct bfin_lq035q1fb_info *info = container_of(spi->dev.driver,
+ struct bfin_lq035q1fb_info,
+ spidrv.driver);
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+
+ if (!ctl)
+ return -ENOMEM;
+
+ ctl->mode = (info->disp_info->mode &
+ LQ035_DRIVER_OUTPUT_MASK) | LQ035_DRIVER_OUTPUT_DEFAULT;
+
+ ret = lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON);
+ ret |= lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, ctl);
+
+ return 0;
+}
+
+static int lq035q1_spidev_remove(struct spi_device *spi)
+{
+ return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
+}
+
+#ifdef CONFIG_PM
+static int lq035q1_spidev_suspend(struct spi_device *spi, pm_message_t state)
+{
+ return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
+}
+
+static int lq035q1_spidev_resume(struct spi_device *spi)
+{
+ int ret;
+ struct spi_control *ctl = spi_get_drvdata(spi);
+
+ ret = lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode);
+ if (ret)
+ return ret;
+
+ return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON);
+}
+#else
+# define lq035q1_spidev_suspend NULL
+# define lq035q1_spidev_resume NULL
+#endif
+
+/* Power down all displays on reboot, poweroff or halt */
+static void lq035q1_spidev_shutdown(struct spi_device *spi)
+{
+ lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
+}
+
+static int lq035q1_backlight(struct bfin_lq035q1fb_info *info, unsigned arg)
+{
+ if (info->disp_info->use_bl)
+ gpio_set_value(info->disp_info->gpio_bl, arg);
+
+ return 0;
+}
+
+static void bfin_lq035q1_config_ppi(struct bfin_lq035q1fb_info *fbi)
+{
+ bfin_write_PPI_DELAY(H_START);
+ bfin_write_PPI_COUNT(H_ACTPIX - 1);
+ bfin_write_PPI_FRAME(V_LINES);
+
+ bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */
+ PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */
+ PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */
+ PPI_PMODE | /* 8/16 bit data length / PACK_EN? */
+ PPI_POLS_1); /* faling edge syncs POLS */
+}
+
+static inline void bfin_lq035q1_disable_ppi(void)
+{
+ bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
+}
+
+static inline void bfin_lq035q1_enable_ppi(void)
+{
+ bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
+}
+
+static void bfin_lq035q1_start_timers(void)
+{
+ enable_gptimers(TIMER_VSYNCbit | TIMER_HSYNCbit);
+}
+
+static void bfin_lq035q1_stop_timers(void)
+{
+ disable_gptimers(TIMER_HSYNCbit | TIMER_VSYNCbit);
+
+ set_gptimer_status(0, TIMER_HSYNC_STATUS_TRUN | TIMER_VSYNC_STATUS_TRUN |
+ TIMER_HSYNC_STATUS_TIMIL | TIMER_VSYNC_STATUS_TIMIL |
+ TIMER_HSYNC_STATUS_TOVF | TIMER_VSYNC_STATUS_TOVF);
+
+}
+
+static void bfin_lq035q1_init_timers(void)
+{
+
+ bfin_lq035q1_stop_timers();
+
+ set_gptimer_period(TIMER_HSYNC_id, H_PERIOD);
+ set_gptimer_pwidth(TIMER_HSYNC_id, H_PULSE);
+ set_gptimer_config(TIMER_HSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
+ TIMER_TIN_SEL | TIMER_CLK_SEL|
+ TIMER_EMU_RUN);
+
+ set_gptimer_period(TIMER_VSYNC_id, V_PERIOD);
+ set_gptimer_pwidth(TIMER_VSYNC_id, V_PULSE);
+ set_gptimer_config(TIMER_VSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
+ TIMER_TIN_SEL | TIMER_CLK_SEL |
+ TIMER_EMU_RUN);
+
+}
+
+static void bfin_lq035q1_config_dma(struct bfin_lq035q1fb_info *fbi)
+{
+
+ set_dma_config(CH_PPI,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO,
+ INTR_DISABLE, DIMENSION_2D,
+ DATA_SIZE_16,
+ DMA_NOSYNC_KEEP_DMA_BUF));
+ set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE);
+ set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8);
+ set_dma_y_count(CH_PPI, V_LINES);
+
+ set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8);
+ set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer);
+
+}
+
+#if (CLOCKS_PER_PIX == 1)
+static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
+ P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
+ P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
+ P_PPI0_D6, P_PPI0_D7, P_PPI0_D8,
+ P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
+ P_PPI0_D12, P_PPI0_D13, P_PPI0_D14,
+ P_PPI0_D15, 0};
+#else
+static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
+ P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
+ P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
+ P_PPI0_D6, P_PPI0_D7, 0};
+#endif
+
+static inline void bfin_lq035q1_free_ports(void)
+{
+ peripheral_free_list(ppi0_req_16);
+ if (ANOMALY_05000400)
+ gpio_free(P_IDENT(P_PPI0_FS3));
+}
+
+static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev)
+{
+ /* ANOMALY_05000400 - PPI Does Not Start Properly In Specific Mode:
+ * Drive PPI_FS3 Low
+ */
+ if (ANOMALY_05000400) {
+ int ret = gpio_request(P_IDENT(P_PPI0_FS3), "PPI_FS3");
+ if (ret)
+ return ret;
+ gpio_direction_output(P_IDENT(P_PPI0_FS3), 0);
+ }
+
+ if (peripheral_request_list(ppi0_req_16, DRIVER_NAME)) {
+ dev_err(&pdev->dev, "requesting peripherals failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int bfin_lq035q1_fb_open(struct fb_info *info, int user)
+{
+ struct bfin_lq035q1fb_info *fbi = info->par;
+
+ spin_lock(&fbi->lock);
+ fbi->lq035_open_cnt++;
+
+ if (fbi->lq035_open_cnt <= 1) {
+
+ bfin_lq035q1_disable_ppi();
+ SSYNC();
+
+ bfin_lq035q1_config_dma(fbi);
+ bfin_lq035q1_config_ppi(fbi);
+ bfin_lq035q1_init_timers();
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_lq035q1_enable_ppi();
+ bfin_lq035q1_start_timers();
+ lq035q1_backlight(fbi, 1);
+ }
+
+ spin_unlock(&fbi->lock);
+
+ return 0;
+}
+
+static int bfin_lq035q1_fb_release(struct fb_info *info, int user)
+{
+ struct bfin_lq035q1fb_info *fbi = info->par;
+
+ spin_lock(&fbi->lock);
+
+ fbi->lq035_open_cnt--;
+
+ if (fbi->lq035_open_cnt <= 0) {
+ lq035q1_backlight(fbi, 0);
+ bfin_lq035q1_disable_ppi();
+ SSYNC();
+ disable_dma(CH_PPI);
+ bfin_lq035q1_stop_timers();
+ }
+
+ spin_unlock(&fbi->lock);
+
+ return 0;
+}
+
+static int bfin_lq035q1_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ switch (var->bits_per_pixel) {
+#if (LCD_BPP == 24)
+ case 24:/* TRUECOLOUR, 16m */
+#else
+ case 16:/* DIRECTCOLOUR, 64k */
+#endif
+ var->red.offset = info->var.red.offset;
+ var->green.offset = info->var.green.offset;
+ var->blue.offset = info->var.blue.offset;
+ var->red.length = info->var.red.length;
+ var->green.length = info->var.green.length;
+ var->blue.length = info->var.blue.length;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ var->transp.msb_right = 0;
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ break;
+ default:
+ pr_debug("%s: depth not supported: %u BPP\n", __func__,
+ var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ if (info->var.xres != var->xres || info->var.yres != var->yres ||
+ info->var.xres_virtual != var->xres_virtual ||
+ info->var.yres_virtual != var->yres_virtual) {
+ pr_debug("%s: Resolution not supported: X%u x Y%u \n",
+ __func__, var->xres, var->yres);
+ return -EINVAL;
+ }
+
+ /*
+ * Memory limit
+ */
+
+ if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
+ pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
+ __func__, var->yres_virtual);
+ return -ENOMEM;
+ }
+
+
+ return 0;
+}
+
+int bfin_lq035q1_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ if (nocursor)
+ return 0;
+ else
+ return -EINVAL; /* just to force soft_cursor() call */
+}
+
+static int bfin_lq035q1_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp,
+ struct fb_info *info)
+{
+ if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
+ return -EINVAL;
+
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+
+ u32 value;
+ /* Place color in the pseudopalette */
+ if (regno > 16)
+ return -EINVAL;
+
+ red >>= (16 - info->var.red.length);
+ green >>= (16 - info->var.green.length);
+ blue >>= (16 - info->var.blue.length);
+
+ value = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ value &= 0xFFFFFF;
+
+ ((u32 *) (info->pseudo_palette))[regno] = value;
+
+ }
+
+ return 0;
+}
+
+static struct fb_ops bfin_lq035q1_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = bfin_lq035q1_fb_open,
+ .fb_release = bfin_lq035q1_fb_release,
+ .fb_check_var = bfin_lq035q1_fb_check_var,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_cursor = bfin_lq035q1_fb_cursor,
+ .fb_setcolreg = bfin_lq035q1_fb_setcolreg,
+};
+
+static irqreturn_t bfin_lq035q1_irq_error(int irq, void *dev_id)
+{
+ /*struct bfin_lq035q1fb_info *info = (struct bfin_lq035q1fb_info *)dev_id;*/
+
+ u16 status = bfin_read_PPI_STATUS();
+ bfin_write_PPI_STATUS(-1);
+
+ if (status) {
+ bfin_lq035q1_disable_ppi();
+ disable_dma(CH_PPI);
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_lq035q1_enable_ppi();
+ bfin_write_PPI_STATUS(-1);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit bfin_lq035q1_probe(struct platform_device *pdev)
+{
+ struct bfin_lq035q1fb_info *info;
+ struct fb_info *fbinfo;
+ int ret;
+
+ ret = request_dma(CH_PPI, DRIVER_NAME"_CH_PPI");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "PPI DMA unavailable\n");
+ goto out1;
+ }
+
+ fbinfo = framebuffer_alloc(sizeof(*info), &pdev->dev);
+ if (!fbinfo) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ info = fbinfo->par;
+ info->fb = fbinfo;
+ info->dev = &pdev->dev;
+
+ info->disp_info = pdev->dev.platform_data;
+
+ platform_set_drvdata(pdev, fbinfo);
+
+ strcpy(fbinfo->fix.id, DRIVER_NAME);
+
+ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbinfo->fix.type_aux = 0;
+ fbinfo->fix.xpanstep = 0;
+ fbinfo->fix.ypanstep = 0;
+ fbinfo->fix.ywrapstep = 0;
+ fbinfo->fix.accel = FB_ACCEL_NONE;
+ fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ fbinfo->var.nonstd = 0;
+ fbinfo->var.activate = FB_ACTIVATE_NOW;
+ fbinfo->var.height = -1;
+ fbinfo->var.width = -1;
+ fbinfo->var.accel_flags = 0;
+ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+ fbinfo->var.xres = LCD_X_RES;
+ fbinfo->var.xres_virtual = LCD_X_RES;
+ fbinfo->var.yres = LCD_Y_RES;
+ fbinfo->var.yres_virtual = LCD_Y_RES;
+ fbinfo->var.bits_per_pixel = LCD_BPP;
+
+ if (info->disp_info->mode & LQ035_BGR) {
+#if (LCD_BPP == 24)
+ fbinfo->var.red.offset = 0;
+ fbinfo->var.green.offset = 8;
+ fbinfo->var.blue.offset = 16;
+#else
+ fbinfo->var.red.offset = 0;
+ fbinfo->var.green.offset = 5;
+ fbinfo->var.blue.offset = 11;
+#endif
+ } else {
+#if (LCD_BPP == 24)
+ fbinfo->var.red.offset = 16;
+ fbinfo->var.green.offset = 8;
+ fbinfo->var.blue.offset = 0;
+#else
+ fbinfo->var.red.offset = 11;
+ fbinfo->var.green.offset = 5;
+ fbinfo->var.blue.offset = 0;
+#endif
+ }
+
+ fbinfo->var.transp.offset = 0;
+
+#if (LCD_BPP == 24)
+ fbinfo->var.red.length = 8;
+ fbinfo->var.green.length = 8;
+ fbinfo->var.blue.length = 8;
+#else
+ fbinfo->var.red.length = 5;
+ fbinfo->var.green.length = 6;
+ fbinfo->var.blue.length = 5;
+#endif
+
+ fbinfo->var.transp.length = 0;
+
+ fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8
+ + ACTIVE_VIDEO_MEM_OFFSET;
+
+ fbinfo->fix.line_length = fbinfo->var.xres_virtual *
+ fbinfo->var.bits_per_pixel / 8;
+
+
+ fbinfo->fbops = &bfin_lq035q1_fb_ops;
+ fbinfo->flags = FBINFO_FLAG_DEFAULT;
+
+ info->fb_buffer =
+ dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
+ GFP_KERNEL);
+
+ if (NULL == info->fb_buffer) {
+ dev_err(&pdev->dev, "couldn't allocate dma buffer\n");
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
+ fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
+
+ fbinfo->fbops = &bfin_lq035q1_fb_ops;
+
+ fbinfo->pseudo_palette = &info->pseudo_pal;
+
+ ret = fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate colormap (%d entries)\n",
+ BFIN_LCD_NBR_PALETTE_ENTRIES);
+ goto out4;
+ }
+
+ ret = bfin_lq035q1_request_ports(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't request gpio port\n");
+ goto out6;
+ }
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ ret = -EINVAL;
+ goto out7;
+ }
+
+ ret = request_irq(info->irq, bfin_lq035q1_irq_error, IRQF_DISABLED,
+ DRIVER_NAME" PPI ERROR", info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to request PPI ERROR IRQ\n");
+ goto out7;
+ }
+
+ info->spidrv.driver.name = DRIVER_NAME"-spi";
+ info->spidrv.probe = lq035q1_spidev_probe;
+ info->spidrv.remove = __devexit_p(lq035q1_spidev_remove);
+ info->spidrv.shutdown = lq035q1_spidev_shutdown;
+ info->spidrv.suspend = lq035q1_spidev_suspend;
+ info->spidrv.resume = lq035q1_spidev_resume;
+
+ ret = spi_register_driver(&info->spidrv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "couldn't register SPI Interface\n");
+ goto out8;
+ }
+
+ if (info->disp_info->use_bl) {
+ ret = gpio_request(info->disp_info->gpio_bl, "LQ035 Backlight");
+
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request GPIO %d\n",
+ info->disp_info->gpio_bl);
+ goto out9;
+ }
+ gpio_direction_output(info->disp_info->gpio_bl, 0);
+ }
+
+ ret = register_framebuffer(fbinfo);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register framebuffer\n");
+ goto out10;
+ }
+
+ dev_info(&pdev->dev, "%dx%d %d-bit RGB FrameBuffer initialized\n",
+ LCD_X_RES, LCD_Y_RES, LCD_BPP);
+
+ return 0;
+
+ out10:
+ if (info->disp_info->use_bl)
+ gpio_free(info->disp_info->gpio_bl);
+ out9:
+ spi_unregister_driver(&info->spidrv);
+ out8:
+ free_irq(info->irq, info);
+ out7:
+ bfin_lq035q1_free_ports();
+ out6:
+ fb_dealloc_cmap(&fbinfo->cmap);
+ out4:
+ dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
+ info->dma_handle);
+ out3:
+ framebuffer_release(fbinfo);
+ out2:
+ free_dma(CH_PPI);
+ out1:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int __devexit bfin_lq035q1_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_lq035q1fb_info *info = fbinfo->par;
+
+ if (info->disp_info->use_bl)
+ gpio_free(info->disp_info->gpio_bl);
+
+ spi_unregister_driver(&info->spidrv);
+
+ unregister_framebuffer(fbinfo);
+
+ free_dma(CH_PPI);
+ free_irq(info->irq, info);
+
+ if (info->fb_buffer != NULL)
+ dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
+ info->dma_handle);
+
+ fb_dealloc_cmap(&fbinfo->cmap);
+
+ bfin_lq035q1_free_ports();
+
+ platform_set_drvdata(pdev, NULL);
+ framebuffer_release(fbinfo);
+
+ dev_info(&pdev->dev, "unregistered LCD driver\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_lq035q1_suspend(struct device *dev)
+{
+ struct fb_info *fbinfo = dev_get_drvdata(dev);
+ struct bfin_lq035q1fb_info *info = fbinfo->par;
+
+ if (info->lq035_open_cnt) {
+ lq035q1_backlight(info, 0);
+ bfin_lq035q1_disable_ppi();
+ SSYNC();
+ disable_dma(CH_PPI);
+ bfin_lq035q1_stop_timers();
+ bfin_write_PPI_STATUS(-1);
+ }
+
+ return 0;
+}
+
+static int bfin_lq035q1_resume(struct device *dev)
+{
+ struct fb_info *fbinfo = dev_get_drvdata(dev);
+ struct bfin_lq035q1fb_info *info = fbinfo->par;
+
+ if (info->lq035_open_cnt) {
+ bfin_lq035q1_disable_ppi();
+ SSYNC();
+
+ bfin_lq035q1_config_dma(info);
+ bfin_lq035q1_config_ppi(info);
+ bfin_lq035q1_init_timers();
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_lq035q1_enable_ppi();
+ bfin_lq035q1_start_timers();
+ lq035q1_backlight(info, 1);
+ }
+
+ return 0;
+}
+
+static struct dev_pm_ops bfin_lq035q1_dev_pm_ops = {
+ .suspend = bfin_lq035q1_suspend,
+ .resume = bfin_lq035q1_resume,
+};
+#endif
+
+static struct platform_driver bfin_lq035q1_driver = {
+ .probe = bfin_lq035q1_probe,
+ .remove = __devexit_p(bfin_lq035q1_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+#ifdef CONFIG_PM
+ .pm = &bfin_lq035q1_dev_pm_ops,
+#endif
+ },
+};
+
+static int __init bfin_lq035q1_driver_init(void)
+{
+ return platform_driver_register(&bfin_lq035q1_driver);
+}
+module_init(bfin_lq035q1_driver_init);
+
+static void __exit bfin_lq035q1_driver_cleanup(void)
+{
+ platform_driver_unregister(&bfin_lq035q1_driver);
+}
+module_exit(bfin_lq035q1_driver_cleanup);
+
+MODULE_DESCRIPTION("Blackfin TFT LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c
index 5cc36cfbf07b..2549c53b26a0 100644
--- a/drivers/video/bfin-t350mcqb-fb.c
+++ b/drivers/video/bfin-t350mcqb-fb.c
@@ -487,8 +487,8 @@ static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev)
fbinfo->var.nonstd = 0;
fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.height = -1;
- fbinfo->var.width = -1;
+ fbinfo->var.height = 53;
+ fbinfo->var.width = 70;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
@@ -634,17 +634,35 @@ static int __devexit bfin_t350mcqb_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int bfin_t350mcqb_suspend(struct platform_device *pdev, pm_message_t state)
{
- bfin_t350mcqb_disable_ppi();
- disable_dma(CH_PPI);
- bfin_write_PPI_STATUS(0xFFFF);
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *fbi = fbinfo->par;
+
+ if (fbi->lq043_open_cnt) {
+ bfin_t350mcqb_disable_ppi();
+ disable_dma(CH_PPI);
+ bfin_t350mcqb_stop_timers();
+ bfin_write_PPI_STATUS(-1);
+ }
+
return 0;
}
static int bfin_t350mcqb_resume(struct platform_device *pdev)
{
- enable_dma(CH_PPI);
- bfin_t350mcqb_enable_ppi();
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *fbi = fbinfo->par;
+
+ if (fbi->lq043_open_cnt) {
+ bfin_t350mcqb_config_dma(fbi);
+ bfin_t350mcqb_config_ppi(fbi);
+ bfin_t350mcqb_init_timers();
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+ bfin_t350mcqb_start_timers();
+ }
return 0;
}
diff --git a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c
index 16f5db471ab5..99b354b8e257 100644
--- a/drivers/video/clps711xfb.c
+++ b/drivers/video/clps711xfb.c
@@ -19,8 +19,10 @@
*
* Framebuffer driver for the CLPS7111 and EP7212 processors.
*/
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/init.h>
@@ -38,14 +40,6 @@ struct fb_info *cfb;
#define CMAP_MAX_SIZE 16
-/* The /proc entry for the backlight. */
-static struct proc_dir_entry *clps7111fb_backlight_proc_entry = NULL;
-
-static int clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
- int count, int *eof, void *data);
-static int clps7111fb_proc_backlight_write(struct file *file,
- const char *buffer, unsigned long count, void *data);
-
/*
* LCD AC Prescale. This comes from the LCD panel manufacturers specifications.
* This determines how many clocks + 1 of CL1 before the M signal toggles.
@@ -221,26 +215,23 @@ static struct fb_ops clps7111fb_ops = {
.fb_imageblit = cfb_imageblit,
};
-static int
-clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int backlight_proc_show(struct seq_file *m, void *v)
{
- /* We need at least two characters, one for the digit, and one for
- * the terminating NULL. */
- if (count < 2)
- return -EINVAL;
-
if (machine_is_edb7211()) {
- return sprintf(page, "%d\n",
+ seq_printf(m, "%d\n",
(clps_readb(PDDR) & EDB_PD3_LCDBL) ? 1 : 0);
}
return 0;
}
-static int
-clps7111fb_proc_backlight_write(struct file *file, const char *buffer,
- unsigned long count, void *data)
+static int backlight_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, backlight_proc_show, NULL);
+}
+
+static ssize_t backlight_proc_write(struct file *file, const char *buffer,
+ size_t count, loff_t *pos)
{
unsigned char char_value;
int value;
@@ -271,6 +262,15 @@ clps7111fb_proc_backlight_write(struct file *file, const char *buffer,
return count;
}
+static const struct file_operations backlight_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = backlight_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = backlight_proc_write,
+};
+
static void __init clps711x_guess_lcd_params(struct fb_info *info)
{
unsigned int lcdcon, syscon, size;
@@ -379,19 +379,11 @@ int __init clps711xfb_init(void)
fb_alloc_cmap(&cfb->cmap, CMAP_MAX_SIZE, 0);
- /* Register the /proc entries. */
- clps7111fb_backlight_proc_entry = create_proc_entry("backlight", 0444,
- NULL);
- if (clps7111fb_backlight_proc_entry == NULL) {
+ if (!proc_create("backlight", 0444, NULL, &backlight_proc_fops)) {
printk("Couldn't create the /proc entry for the backlight.\n");
return -EINVAL;
}
- clps7111fb_backlight_proc_entry->read_proc =
- &clps7111fb_proc_backlight_read;
- clps7111fb_backlight_proc_entry->write_proc =
- &clps7111fb_proc_backlight_write;
-
/*
* Power up the LCD
*/
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index ea1fd3f47511..369a5b3ac649 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -28,6 +28,8 @@
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/console.h>
#include <video/da8xx-fb.h>
#define DRIVER_NAME "da8xx_lcdc"
@@ -113,6 +115,12 @@ struct da8xx_fb_par {
unsigned short pseudo_palette[16];
unsigned int databuf_sz;
unsigned int palette_sz;
+ unsigned int pxl_clk;
+ int blank;
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
+ void (*panel_power_ctrl)(int);
};
/* Variable Screen Information */
@@ -155,7 +163,7 @@ struct da8xx_panel {
int vfp; /* Vertical front porch */
int vbp; /* Vertical back porch */
int vsw; /* Vertical Sync Pulse Width */
- int pxl_clk; /* Pixel clock */
+ unsigned int pxl_clk; /* Pixel clock */
unsigned char invert_pxl_clk; /* Invert Pixel clock */
};
@@ -171,7 +179,7 @@ static struct da8xx_panel known_lcd_panels[] = {
.vfp = 2,
.vbp = 2,
.vsw = 0,
- .pxl_clk = 0x10,
+ .pxl_clk = 4608000,
.invert_pxl_clk = 1,
},
/* Sharp LK043T1DG01 */
@@ -185,13 +193,23 @@ static struct da8xx_panel known_lcd_panels[] = {
.vfp = 2,
.vbp = 2,
.vsw = 10,
- .pxl_clk = 0x12,
+ .pxl_clk = 7833600,
.invert_pxl_clk = 0,
},
};
+/* Enable the Raster Engine of the LCD Controller */
+static inline void lcd_enable_raster(void)
+{
+ u32 reg;
+
+ reg = lcdc_read(LCD_RASTER_CTRL_REG);
+ if (!(reg & LCD_RASTER_ENABLE))
+ lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
+}
+
/* Disable the Raster Engine of the LCD Controller */
-static void lcd_disable_raster(struct da8xx_fb_par *par)
+static inline void lcd_disable_raster(void)
{
u32 reg;
@@ -443,14 +461,25 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green,
static void lcd_reset(struct da8xx_fb_par *par)
{
/* Disable the Raster if previously Enabled */
- if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE)
- lcd_disable_raster(par);
+ lcd_disable_raster();
/* DMA has to be disabled */
lcdc_write(0, LCD_DMA_CTRL_REG);
lcdc_write(0, LCD_RASTER_CTRL_REG);
}
+static void lcd_calc_clk_divider(struct da8xx_fb_par *par)
+{
+ unsigned int lcd_clk, div;
+
+ lcd_clk = clk_get_rate(par->lcdc_clk);
+ div = lcd_clk / par->pxl_clk;
+
+ /* Configure the LCD clock divisor. */
+ lcdc_write(LCD_CLK_DIVISOR(div) |
+ (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
+}
+
static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
struct da8xx_panel *panel)
{
@@ -459,9 +488,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
lcd_reset(par);
- /* Configure the LCD clock divisor. */
- lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) |
- (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
+ /* Calculate the divider */
+ lcd_calc_clk_divider(par);
if (panel->invert_pxl_clk)
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) |
@@ -513,13 +541,11 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
static irqreturn_t lcdc_irq_handler(int irq, void *arg)
{
u32 stat = lcdc_read(LCD_STAT_REG);
- u32 reg;
if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
- reg = lcdc_read(LCD_RASTER_CTRL_REG);
- lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
+ lcd_disable_raster();
lcdc_write(stat, LCD_STAT_REG);
- lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
+ lcd_enable_raster();
} else
lcdc_write(stat, LCD_STAT_REG);
@@ -574,6 +600,38 @@ static int fb_check_var(struct fb_var_screeninfo *var,
return err;
}
+#ifdef CONFIG_CPU_FREQ
+static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct da8xx_fb_par *par;
+
+ par = container_of(nb, struct da8xx_fb_par, freq_transition);
+ if (val == CPUFREQ_PRECHANGE) {
+ lcd_disable_raster();
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ lcd_calc_clk_divider(par);
+ lcd_enable_raster();
+ }
+
+ return 0;
+}
+
+static inline int lcd_da8xx_cpufreq_register(struct da8xx_fb_par *par)
+{
+ par->freq_transition.notifier_call = lcd_da8xx_cpufreq_transition;
+
+ return cpufreq_register_notifier(&par->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void lcd_da8xx_cpufreq_deregister(struct da8xx_fb_par *par)
+{
+ cpufreq_unregister_notifier(&par->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+#endif
+
static int __devexit fb_remove(struct platform_device *dev)
{
struct fb_info *info = dev_get_drvdata(&dev->dev);
@@ -581,8 +639,13 @@ static int __devexit fb_remove(struct platform_device *dev)
if (info) {
struct da8xx_fb_par *par = info->par;
- if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE)
- lcd_disable_raster(par);
+#ifdef CONFIG_CPU_FREQ
+ lcd_da8xx_cpufreq_deregister(par);
+#endif
+ if (par->panel_power_ctrl)
+ par->panel_power_ctrl(0);
+
+ lcd_disable_raster();
lcdc_write(0, LCD_RASTER_CTRL_REG);
/* disable DMA */
@@ -639,6 +702,35 @@ static int fb_ioctl(struct fb_info *info, unsigned int cmd,
return 0;
}
+static int cfb_blank(int blank, struct fb_info *info)
+{
+ struct da8xx_fb_par *par = info->par;
+ int ret = 0;
+
+ if (par->blank == blank)
+ return 0;
+
+ par->blank = blank;
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ if (par->panel_power_ctrl)
+ par->panel_power_ctrl(1);
+
+ lcd_enable_raster();
+ break;
+ case FB_BLANK_POWERDOWN:
+ if (par->panel_power_ctrl)
+ par->panel_power_ctrl(0);
+
+ lcd_disable_raster();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
static struct fb_ops da8xx_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = fb_check_var,
@@ -647,6 +739,7 @@ static struct fb_ops da8xx_fb_ops = {
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_blank = cfb_blank,
};
static int __init fb_probe(struct platform_device *device)
@@ -721,6 +814,12 @@ static int __init fb_probe(struct platform_device *device)
}
par = da8xx_fb_info->par;
+ par->lcdc_clk = fb_clk;
+ par->pxl_clk = lcdc_info->pxl_clk;
+ if (fb_pdata->panel_power_ctrl) {
+ par->panel_power_ctrl = fb_pdata->panel_power_ctrl;
+ par->panel_power_ctrl(1);
+ }
if (lcd_init(par, lcd_cfg, lcdc_info) < 0) {
dev_err(&device->dev, "lcd_init failed\n");
@@ -754,8 +853,6 @@ static int __init fb_probe(struct platform_device *device)
da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz;
da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8;
- par->lcdc_clk = fb_clk;
-
par->irq = platform_get_irq(device, 0);
if (par->irq < 0) {
ret = -ENOENT;
@@ -814,12 +911,24 @@ static int __init fb_probe(struct platform_device *device)
goto err_dealloc_cmap;
}
+#ifdef CONFIG_CPU_FREQ
+ ret = lcd_da8xx_cpufreq_register(par);
+ if (ret) {
+ dev_err(&device->dev, "failed to register cpufreq\n");
+ goto err_cpu_freq;
+ }
+#endif
+
/* enable raster engine */
- lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) |
- LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
+ lcd_enable_raster();
return 0;
+#ifdef CONFIG_CPU_FREQ
+err_cpu_freq:
+ unregister_framebuffer(da8xx_fb_info);
+#endif
+
err_dealloc_cmap:
fb_dealloc_cmap(&da8xx_fb_info->cmap);
@@ -852,11 +961,35 @@ err_request_mem:
#ifdef CONFIG_PM
static int fb_suspend(struct platform_device *dev, pm_message_t state)
{
- return -EBUSY;
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct da8xx_fb_par *par = info->par;
+
+ acquire_console_sem();
+ if (par->panel_power_ctrl)
+ par->panel_power_ctrl(0);
+
+ fb_set_suspend(info, 1);
+ lcd_disable_raster();
+ clk_disable(par->lcdc_clk);
+ release_console_sem();
+
+ return 0;
}
static int fb_resume(struct platform_device *dev)
{
- return -EBUSY;
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct da8xx_fb_par *par = info->par;
+
+ acquire_console_sem();
+ if (par->panel_power_ctrl)
+ par->panel_power_ctrl(1);
+
+ clk_enable(par->lcdc_clk);
+ lcd_enable_raster();
+ fb_set_suspend(info, 0);
+ release_console_sem();
+
+ return 0;
}
#else
#define fb_suspend NULL
diff --git a/drivers/video/display/display-sysfs.c b/drivers/video/display/display-sysfs.c
index 4830b1bf51e5..80abbf323b99 100644
--- a/drivers/video/display/display-sysfs.c
+++ b/drivers/video/display/display-sysfs.c
@@ -67,7 +67,7 @@ static ssize_t display_store_contrast(struct device *dev,
contrast = simple_strtoul(buf, &endp, 0);
size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c
index bd9d46f95291..27aab4a06198 100644
--- a/drivers/video/ep93xx-fb.c
+++ b/drivers/video/ep93xx-fb.c
@@ -358,6 +358,8 @@ static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red,
switch (info->fix.visual) {
case FB_VISUAL_PSEUDOCOLOR:
+ if (regno > 255)
+ return 1;
rgb = ((red & 0xff00) << 8) | (green & 0xff00) |
((blue & 0xff00) >> 8);
diff --git a/drivers/video/geode/display_gx.c b/drivers/video/geode/display_gx.c
index e759895bf3d3..f0af911a096d 100644
--- a/drivers/video/geode/display_gx.c
+++ b/drivers/video/geode/display_gx.c
@@ -17,7 +17,7 @@
#include <asm/io.h>
#include <asm/div64.h>
#include <asm/delay.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
#include "gxfb.h"
@@ -25,7 +25,7 @@ unsigned int gx_frame_buffer_size(void)
{
unsigned int val;
- if (!geode_has_vsa2()) {
+ if (!cs5535_has_vsa2()) {
uint32_t hi, lo;
/* The number of pages is (PMAX - PMIN)+1 */
diff --git a/drivers/video/geode/gxfb.h b/drivers/video/geode/gxfb.h
index 16a96f8fd8c5..d19e9378b0c0 100644
--- a/drivers/video/geode/gxfb.h
+++ b/drivers/video/geode/gxfb.h
@@ -340,7 +340,7 @@ static inline void write_fp(struct gxfb_par *par, int reg, uint32_t val)
}
-/* MSRs are defined in asm/geode.h; their bitfields are here */
+/* MSRs are defined in linux/cs5535.h; their bitfields are here */
#define MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 (1 << 3)
#define MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 (1 << 2)
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index 2552cac39e1c..b3e639d1e12c 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -32,7 +32,7 @@
#include <linux/suspend.h>
#include <linux/init.h>
#include <linux/pci.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
#include "gxfb.h"
diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h
index 6a51448fd3f7..cc781c00f75d 100644
--- a/drivers/video/geode/lxfb.h
+++ b/drivers/video/geode/lxfb.h
@@ -1,3 +1,13 @@
+/* Geode LX framebuffer driver
+ *
+ * Copyright (C) 2006-2007, Advanced Micro Devices,Inc.
+ * Copyright (c) 2008 Andres Salomon <dilinger@debian.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.
+ */
#ifndef _LXFB_H_
#define _LXFB_H_
@@ -409,7 +419,7 @@ static inline void write_fp(struct lxfb_par *par, int reg, uint32_t val)
}
-/* MSRs are defined in asm/geode.h; their bitfields are here */
+/* MSRs are defined in linux/cs5535.h; their bitfields are here */
#define MSR_GLCP_DOTPLL_LOCK (1 << 25) /* r/o */
#define MSR_GLCP_DOTPLL_HALFPIX (1 << 24)
diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c
index b1cd49c99356..0e5d8c7c3eba 100644
--- a/drivers/video/geode/lxfb_ops.c
+++ b/drivers/video/geode/lxfb_ops.c
@@ -13,7 +13,7 @@
#include <linux/fb.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
#include "lxfb.h"
@@ -307,7 +307,7 @@ unsigned int lx_framebuffer_size(void)
{
unsigned int val;
- if (!geode_has_vsa2()) {
+ if (!cs5535_has_vsa2()) {
uint32_t hi, lo;
/* The number of pages is (PMAX - PMIN)+1 */
diff --git a/drivers/video/geode/suspend_gx.c b/drivers/video/geode/suspend_gx.c
index 9aff32ef8bb6..1bb043d70c64 100644
--- a/drivers/video/geode/suspend_gx.c
+++ b/drivers/video/geode/suspend_gx.c
@@ -10,7 +10,7 @@
#include <linux/fb.h>
#include <asm/io.h>
#include <asm/msr.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
#include <asm/delay.h>
#include "gxfb.h"
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c
index b8d52a8360db..6082f653c68a 100644
--- a/drivers/video/geode/video_gx.c
+++ b/drivers/video/geode/video_gx.c
@@ -16,7 +16,7 @@
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/msr.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
#include "gxfb.h"
diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c
index e7116a6d82d3..73c83a8de2d3 100644
--- a/drivers/video/hitfb.c
+++ b/drivers/video/hitfb.c
@@ -456,7 +456,7 @@ static int hitfb_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops hitfb_dev_pm_ops = {
+static const struct dev_pm_ops hitfb_dev_pm_ops = {
.suspend = hitfb_suspend,
.resume = hitfb_resume,
};
diff --git a/drivers/video/i810/i810_dvt.c b/drivers/video/i810/i810_dvt.c
index 27fa703a2e0a..b4b3670667ab 100644
--- a/drivers/video/i810/i810_dvt.c
+++ b/drivers/video/i810/i810_dvt.c
@@ -212,24 +212,29 @@ inline void round_off_yres(u32 *xres, u32 *yres)
*yres = (*xres * 3) >> 2;
}
-void i810fb_encode_registers(const struct fb_var_screeninfo *var,
- struct i810fb_par *par, u32 xres, u32 yres)
+static int i810fb_find_best_mode(u32 xres, u32 yres, u32 pixclock)
{
u32 diff = 0, diff_best = 0xFFFFFFFF, i = 0, i_best = 0;
- u8 hfl;
+ u8 hfl = (u8) ((xres >> 3) - 1);
- hfl = (u8) ((xres >> 3) - 1);
for (i = 0; i < ARRAY_SIZE(std_modes); i++) {
if (std_modes[i].cr01 == hfl) {
- if (std_modes[i].pixclock <= par->regs.pixclock)
- diff = par->regs.pixclock -
- std_modes[i].pixclock;
+ if (std_modes[i].pixclock <= pixclock)
+ diff = pixclock - std_modes[i].pixclock;
if (diff < diff_best) {
i_best = i;
diff_best = diff;
}
}
}
+ return i_best;
+}
+
+void i810fb_encode_registers(const struct fb_var_screeninfo *var,
+ struct i810fb_par *par, u32 xres, u32 yres)
+{
+ u32 i_best = i810fb_find_best_mode(xres, yres, par->regs.pixclock);
+
par->regs = std_modes[i_best];
/* overlay */
@@ -239,36 +244,36 @@ void i810fb_encode_registers(const struct fb_var_screeninfo *var,
void i810fb_fill_var_timings(struct fb_var_screeninfo *var)
{
- struct i810fb_par par;
u32 total, xres, yres;
+ u32 mode, pixclock;
xres = var->xres;
yres = var->yres;
- par.regs.pixclock = 1000000000/var->pixclock;
- i810fb_encode_registers(var, &par, xres, yres);
+ pixclock = 1000000000 / var->pixclock;
+ mode = i810fb_find_best_mode(xres, yres, pixclock);
- total = ((par.regs.cr00 | (par.regs.cr35 & 1) << 8) + 3) << 3;
+ total = (std_modes[mode].cr00 | (std_modes[mode].cr35 & 1) << 8) + 3;
+ total <<= 3;
- var->pixclock = 1000000000/par.regs.pixclock;
- var->right_margin = (par.regs.cr04 << 3) - xres;
- var->hsync_len = ((par.regs.cr05 & 0x1F) -
- (par.regs.cr04 & 0x1F)) << 3;
+ var->pixclock = 1000000000 / std_modes[mode].pixclock;
+ var->right_margin = (std_modes[mode].cr04 << 3) - xres;
+ var->hsync_len = ((std_modes[mode].cr05 & 0x1F) -
+ (std_modes[mode].cr04 & 0x1F)) << 3;
var->left_margin = (total - (xres + var->right_margin +
var->hsync_len));
var->sync = FB_SYNC_ON_GREEN;
- if (~(par.regs.msr & (1 << 6)))
+ if (~(std_modes[mode].msr & (1 << 6)))
var->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (~(par.regs.msr & (1 << 7)))
+ if (~(std_modes[mode].msr & (1 << 7)))
var->sync |= FB_SYNC_VERT_HIGH_ACT;
-
- total = ((par.regs.cr06 | (par.regs.cr30 & 0x0F) << 8)) + 2;
- var->lower_margin = (par.regs.cr10 |
- (par.regs.cr32 & 0x0F) << 8) - yres;
- var->vsync_len = (par.regs.cr11 & 0x0F) - (var->lower_margin & 0x0F);
- var->upper_margin = total - (yres + var->lower_margin +
- var->vsync_len);
+ total = (std_modes[mode].cr06 | (std_modes[mode].cr30 & 0xF) << 8) + 2;
+ var->lower_margin = (std_modes[mode].cr10 |
+ (std_modes[mode].cr32 & 0x0F) << 8) - yres;
+ var->vsync_len = (std_modes[mode].cr11 & 0x0F) -
+ (var->lower_margin & 0x0F);
+ var->upper_margin = total - (yres + var->lower_margin + var->vsync_len);
}
u32 i810_get_watermark(struct fb_var_screeninfo *var,
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c
index 0cafd642fbc0..5ba399991050 100644
--- a/drivers/video/intelfb/intelfbdrv.c
+++ b/drivers/video/intelfb/intelfbdrv.c
@@ -874,6 +874,9 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev,
if (bailearly == 18)
bailout(dinfo);
+ /* read active pipe */
+ dinfo->pipe = intelfbhw_active_pipe(&dinfo->save_state);
+
/* Cursor initialisation */
if (dinfo->hwcursor) {
intelfbhw_cursor_init(dinfo);
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c
index 0689f97c5238..81627466804e 100644
--- a/drivers/video/intelfb/intelfbhw.c
+++ b/drivers/video/intelfb/intelfbhw.c
@@ -469,6 +469,32 @@ void intelfbhw_do_blank(int blank, struct fb_info *info)
}
+/* Check which pipe is connected to an active display plane. */
+int intelfbhw_active_pipe(const struct intelfb_hwstate *hw)
+{
+ int pipe = -1;
+
+ /* keep old default behaviour - prefer PIPE_A */
+ if (hw->disp_b_ctrl & DISPPLANE_PLANE_ENABLE) {
+ pipe = (hw->disp_b_ctrl >> DISPPLANE_SEL_PIPE_SHIFT);
+ pipe &= PIPE_MASK;
+ if (unlikely(pipe == PIPE_A))
+ return PIPE_A;
+ }
+ if (hw->disp_a_ctrl & DISPPLANE_PLANE_ENABLE) {
+ pipe = (hw->disp_a_ctrl >> DISPPLANE_SEL_PIPE_SHIFT);
+ pipe &= PIPE_MASK;
+ if (likely(pipe == PIPE_A))
+ return PIPE_A;
+ }
+ /* Impossible that no pipe is selected - return PIPE_A */
+ WARN_ON(pipe == -1);
+ if (unlikely(pipe == -1))
+ pipe = PIPE_A;
+
+ return pipe;
+}
+
void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp)
@@ -1019,7 +1045,7 @@ int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
struct intelfb_hwstate *hw,
struct fb_var_screeninfo *var)
{
- int pipe = PIPE_A;
+ int pipe = intelfbhw_active_pipe(hw);
u32 *dpll, *fp0, *fp1;
u32 m1, m2, n, p1, p2, clock_target, clock;
u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive;
@@ -1033,12 +1059,6 @@ int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
/* Disable VGA */
hw->vgacntrl |= VGA_DISABLE;
- /* Check whether pipe A or pipe B is enabled. */
- if (hw->pipe_a_conf & PIPECONF_ENABLE)
- pipe = PIPE_A;
- else if (hw->pipe_b_conf & PIPECONF_ENABLE)
- pipe = PIPE_B;
-
/* Set which pipe's registers will be set. */
if (pipe == PIPE_B) {
dpll = &hw->dpll_b;
@@ -1262,7 +1282,6 @@ int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
int intelfbhw_program_mode(struct intelfb_info *dinfo,
const struct intelfb_hwstate *hw, int blank)
{
- int pipe = PIPE_A;
u32 tmp;
const u32 *dpll, *fp0, *fp1, *pipe_conf;
const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss;
@@ -1272,7 +1291,7 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo,
u32 src_size_reg;
u32 count, tmp_val[3];
- /* Assume single pipe, display plane A, analog CRT. */
+ /* Assume single pipe */
#if VERBOSE > 0
DBG_MSG("intelfbhw_program_mode\n");
@@ -1283,15 +1302,9 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo,
tmp |= VGA_DISABLE;
OUTREG(VGACNTRL, tmp);
- /* Check whether pipe A or pipe B is enabled. */
- if (hw->pipe_a_conf & PIPECONF_ENABLE)
- pipe = PIPE_A;
- else if (hw->pipe_b_conf & PIPECONF_ENABLE)
- pipe = PIPE_B;
-
- dinfo->pipe = pipe;
+ dinfo->pipe = intelfbhw_active_pipe(hw);
- if (pipe == PIPE_B) {
+ if (dinfo->pipe == PIPE_B) {
dpll = &hw->dpll_b;
fp0 = &hw->fpb0;
fp1 = &hw->fpb1;
diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h
index 0b076bac321b..216ca20f259f 100644
--- a/drivers/video/intelfb/intelfbhw.h
+++ b/drivers/video/intelfb/intelfbhw.h
@@ -604,5 +604,6 @@ extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
extern int intelfbhw_enable_irq(struct intelfb_info *dinfo);
extern void intelfbhw_disable_irq(struct intelfb_info *dinfo);
extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe);
+extern int intelfbhw_active_pipe(const struct intelfb_hwstate *hw);
#endif /* _INTELFBHW_H */
diff --git a/drivers/video/matrox/g450_pll.c b/drivers/video/matrox/g450_pll.c
index 09f6e045d5be..c15f8a57498e 100644
--- a/drivers/video/matrox/g450_pll.c
+++ b/drivers/video/matrox/g450_pll.c
@@ -368,7 +368,8 @@ static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout,
M1064_XDVICLKCTRL_C1DVICLKEN |
M1064_XDVICLKCTRL_DVILOOPCTL |
M1064_XDVICLKCTRL_P1LOOPBWDTCTL;
- matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp);
+ /* Setting this breaks PC systems so don't do it */
+ /* matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp); */
matroxfb_DAC_out(minfo, M1064_XPWRCTRL,
xpwrctrl);
diff --git a/drivers/video/maxinefb.c b/drivers/video/maxinefb.c
index 5e91c2b30af9..7854c7a37dc5 100644
--- a/drivers/video/maxinefb.c
+++ b/drivers/video/maxinefb.c
@@ -92,6 +92,9 @@ static int maxinefb_setcolreg(unsigned regno, unsigned red, unsigned green,
/* value to be written into the palette reg. */
unsigned long hw_colorvalue = 0;
+ if (regno > 255)
+ return 1;
+
red >>= 8; /* The cmap fields are 16 bits */
green >>= 8; /* wide, but the harware colormap */
blue >>= 8; /* registers are only 8 bits wide */
diff --git a/drivers/video/mb862xx/Makefile b/drivers/video/mb862xx/Makefile
index 07664814bb1d..d7777714166b 100644
--- a/drivers/video/mb862xx/Makefile
+++ b/drivers/video/mb862xx/Makefile
@@ -2,4 +2,4 @@
# Makefile for the MB862xx framebuffer driver
#
-obj-$(CONFIG_FB_MB862XX) := mb862xxfb.o
+obj-$(CONFIG_FB_MB862XX) := mb862xxfb.o mb862xxfb_accel.o
diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c
index a28e3cfbbf70..fabb0c59a211 100644
--- a/drivers/video/mb862xx/mb862xxfb.c
+++ b/drivers/video/mb862xx/mb862xxfb.c
@@ -214,6 +214,8 @@ static int mb862xxfb_set_par(struct fb_info *fbi)
unsigned long reg, sc;
dev_dbg(par->dev, "%s\n", __func__);
+ if (par->type == BT_CORALP)
+ mb862xxfb_init_accel(fbi, fbi->var.xres);
if (par->pre_init)
return 0;
@@ -453,6 +455,18 @@ static ssize_t mb862xxfb_show_dispregs(struct device *dev,
ptr += sprintf(ptr, "%08x = %08x\n",
reg, inreg(disp, reg));
+ for (reg = 0x400; reg <= 0x410; reg += 4)
+ ptr += sprintf(ptr, "geo %08x = %08x\n",
+ reg, inreg(geo, reg));
+
+ for (reg = 0x400; reg <= 0x410; reg += 4)
+ ptr += sprintf(ptr, "draw %08x = %08x\n",
+ reg, inreg(draw, reg));
+
+ for (reg = 0x440; reg <= 0x450; reg += 4)
+ ptr += sprintf(ptr, "draw %08x = %08x\n",
+ reg, inreg(draw, reg));
+
return ptr - buf;
}
diff --git a/drivers/video/mb862xx/mb862xxfb.h b/drivers/video/mb862xx/mb862xxfb.h
index c4c8f4dd2217..d7e7cb76bbf2 100644
--- a/drivers/video/mb862xx/mb862xxfb.h
+++ b/drivers/video/mb862xx/mb862xxfb.h
@@ -61,6 +61,8 @@ struct mb862xxfb_par {
u32 pseudo_palette[16];
};
+extern void mb862xxfb_init_accel(struct fb_info *info, int xres);
+
#if defined(CONFIG_FB_MB862XX_LIME) && defined(CONFIG_FB_MB862XX_PCI_GDC)
#error "Select Lime GDC or CoralP/Carmine support, but not both together"
#endif
diff --git a/drivers/video/mb862xx/mb862xxfb_accel.c b/drivers/video/mb862xx/mb862xxfb_accel.c
new file mode 100644
index 000000000000..049256052b1a
--- /dev/null
+++ b/drivers/video/mb862xx/mb862xxfb_accel.c
@@ -0,0 +1,331 @@
+/*
+ * drivers/mb862xx/mb862xxfb_accel.c
+ *
+ * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver acceleration support
+ *
+ * (C) 2007 Alexander Shishkin <virtuoso@slind.org>
+ * (C) 2009 Valentin Sitdikov <valentin.sitdikov@siemens.com>
+ * (C) 2009 Siemens AG
+ *
+ * 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/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#if defined(CONFIG_OF)
+#include <linux/of_platform.h>
+#endif
+#include "mb862xxfb.h"
+#include "mb862xx_reg.h"
+#include "mb862xxfb_accel.h"
+
+static void mb862xxfb_write_fifo(u32 count, u32 *data, struct fb_info *info)
+{
+ struct mb862xxfb_par *par = info->par;
+ static u32 free;
+
+ u32 total = 0;
+ while (total < count) {
+ if (free) {
+ outreg(geo, GDC_GEO_REG_INPUT_FIFO, data[total]);
+ total++;
+ free--;
+ } else {
+ free = (u32) inreg(draw, GDC_REG_FIFO_COUNT);
+ }
+ }
+}
+
+static void mb86290fb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ __u32 cmd[6];
+
+ cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP;
+ /* Set raster operation */
+ cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9);
+ cmd[2] = GDC_TYPE_BLTCOPYP << 24;
+
+ if (area->sx >= area->dx && area->sy >= area->dy)
+ cmd[2] |= GDC_CMD_BLTCOPY_TOP_LEFT << 16;
+ else if (area->sx >= area->dx && area->sy <= area->dy)
+ cmd[2] |= GDC_CMD_BLTCOPY_BOTTOM_LEFT << 16;
+ else if (area->sx <= area->dx && area->sy >= area->dy)
+ cmd[2] |= GDC_CMD_BLTCOPY_TOP_RIGHT << 16;
+ else
+ cmd[2] |= GDC_CMD_BLTCOPY_BOTTOM_RIGHT << 16;
+
+ cmd[3] = (area->sy << 16) | area->sx;
+ cmd[4] = (area->dy << 16) | area->dx;
+ cmd[5] = (area->height << 16) | area->width;
+ mb862xxfb_write_fifo(6, cmd, info);
+}
+
+/*
+ * Fill in the cmd array /GDC FIFO commands/ to draw a 1bit image.
+ * Make sure cmd has enough room!
+ */
+static void mb86290fb_imageblit1(u32 *cmd, u16 step, u16 dx, u16 dy,
+ u16 width, u16 height, u32 fgcolor,
+ u32 bgcolor, const struct fb_image *image,
+ struct fb_info *info)
+{
+ int i;
+ unsigned const char *line;
+ u16 bytes;
+
+ /* set colors and raster operation regs */
+ cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP;
+ /* Set raster operation */
+ cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9);
+ cmd[2] =
+ (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_FORE_COLOR << 16);
+ cmd[3] = fgcolor;
+ cmd[4] =
+ (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_BACK_COLOR << 16);
+ cmd[5] = bgcolor;
+
+ i = 0;
+ line = image->data;
+ bytes = (image->width + 7) >> 3;
+
+ /* and the image */
+ cmd[6] = (GDC_TYPE_DRAWBITMAPP << 24) |
+ (GDC_CMD_BITMAP << 16) | (2 + (step * height));
+ cmd[7] = (dy << 16) | dx;
+ cmd[8] = (height << 16) | width;
+
+ while (i < height) {
+ memcpy(&cmd[9 + i * step], line, step << 2);
+#ifdef __LITTLE_ENDIAN
+ {
+ int k = 0;
+ for (k = 0; k < step; k++)
+ cmd[9 + i * step + k] =
+ cpu_to_be32(cmd[9 + i * step + k]);
+ }
+#endif
+ line += bytes;
+ i++;
+ }
+}
+
+/*
+ * Fill in the cmd array /GDC FIFO commands/ to draw a 8bit image.
+ * Make sure cmd has enough room!
+ */
+static void mb86290fb_imageblit8(u32 *cmd, u16 step, u16 dx, u16 dy,
+ u16 width, u16 height, u32 fgcolor,
+ u32 bgcolor, const struct fb_image *image,
+ struct fb_info *info)
+{
+ int i, j;
+ unsigned const char *line, *ptr;
+ u16 bytes;
+
+ cmd[0] = (GDC_TYPE_DRAWBITMAPP << 24) |
+ (GDC_CMD_BLT_DRAW << 16) | (2 + (height * step));
+ cmd[1] = (dy << 16) | dx;
+ cmd[2] = (height << 16) | width;
+
+ i = 0;
+ line = ptr = image->data;
+ bytes = image->width;
+
+ while (i < height) {
+ ptr = line;
+ for (j = 0; j < step; j++) {
+ cmd[3 + i * step + j] =
+ (((u32 *) (info->pseudo_palette))[*ptr]) & 0xffff;
+ ptr++;
+ cmd[3 + i * step + j] |=
+ ((((u32 *) (info->
+ pseudo_palette))[*ptr]) & 0xffff) << 16;
+ ptr++;
+ }
+
+ line += bytes;
+ i++;
+ }
+}
+
+/*
+ * Fill in the cmd array /GDC FIFO commands/ to draw a 16bit image.
+ * Make sure cmd has enough room!
+ */
+static void mb86290fb_imageblit16(u32 *cmd, u16 step, u16 dx, u16 dy,
+ u16 width, u16 height, u32 fgcolor,
+ u32 bgcolor, const struct fb_image *image,
+ struct fb_info *info)
+{
+ int i;
+ unsigned const char *line;
+ u16 bytes;
+
+ i = 0;
+ line = image->data;
+ bytes = image->width << 1;
+
+ cmd[0] = (GDC_TYPE_DRAWBITMAPP << 24) |
+ (GDC_CMD_BLT_DRAW << 16) | (2 + step * height);
+ cmd[1] = (dy << 16) | dx;
+ cmd[2] = (height << 16) | width;
+
+ while (i < height) {
+ memcpy(&cmd[3 + i * step], line, step);
+ line += bytes;
+ i++;
+ }
+}
+
+static void mb86290fb_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ int mdr;
+ u32 *cmd = NULL;
+ void (*cmdfn) (u32 *, u16, u16, u16, u16, u16, u32, u32,
+ const struct fb_image *, struct fb_info *) = NULL;
+ u32 cmdlen;
+ u32 fgcolor = 0, bgcolor = 0;
+ u16 step;
+
+ u16 width = image->width, height = image->height;
+ u16 dx = image->dx, dy = image->dy;
+ int x2, y2, vxres, vyres;
+
+ mdr = (GDC_ROP_COPY << 9);
+ x2 = image->dx + image->width;
+ y2 = image->dy + image->height;
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+ x2 = min(x2, vxres);
+ y2 = min(y2, vyres);
+ width = x2 - dx;
+ height = y2 - dy;
+
+ switch (image->depth) {
+ case 1:
+ step = (width + 31) >> 5;
+ cmdlen = 9 + height * step;
+ cmdfn = mb86290fb_imageblit1;
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+ fgcolor =
+ ((u32 *) (info->pseudo_palette))[image->fg_color];
+ bgcolor =
+ ((u32 *) (info->pseudo_palette))[image->bg_color];
+ } else {
+ fgcolor = image->fg_color;
+ bgcolor = image->bg_color;
+ }
+
+ break;
+
+ case 8:
+ step = (width + 1) >> 1;
+ cmdlen = 3 + height * step;
+ cmdfn = mb86290fb_imageblit8;
+ break;
+
+ case 16:
+ step = (width + 1) >> 1;
+ cmdlen = 3 + height * step;
+ cmdfn = mb86290fb_imageblit16;
+ break;
+
+ default:
+ cfb_imageblit(info, image);
+ return;
+ }
+
+ cmd = kmalloc(cmdlen * 4, GFP_DMA);
+ if (!cmd)
+ return cfb_imageblit(info, image);
+ cmdfn(cmd, step, dx, dy, width, height, fgcolor, bgcolor, image, info);
+ mb862xxfb_write_fifo(cmdlen, cmd, info);
+ kfree(cmd);
+}
+
+static void mb86290fb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+
+ u32 x2, y2, vxres, vyres, height, width, fg;
+ u32 cmd[7];
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ if (!rect->width || !rect->height || rect->dx > vxres
+ || rect->dy > vyres)
+ return;
+
+ /* We could use hardware clipping but on many cards you get around
+ * hardware clipping by writing to framebuffer directly. */
+ x2 = rect->dx + rect->width;
+ y2 = rect->dy + rect->height;
+ x2 = min(x2, vxres);
+ y2 = min(y2, vyres);
+ width = x2 - rect->dx;
+ height = y2 - rect->dy;
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR)
+ fg = ((u32 *) (info->pseudo_palette))[rect->color];
+ else
+ fg = rect->color;
+
+ switch (rect->rop) {
+
+ case ROP_XOR:
+ /* Set raster operation */
+ cmd[1] = (2 << 7) | (GDC_ROP_XOR << 9);
+ break;
+
+ case ROP_COPY:
+ /* Set raster operation */
+ cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9);
+ break;
+
+ }
+
+ cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP;
+ /* cmd[1] set earlier */
+ cmd[2] =
+ (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_FORE_COLOR << 16);
+ cmd[3] = fg;
+ cmd[4] = (GDC_TYPE_DRAWRECTP << 24) | (GDC_CMD_BLT_FILL << 16);
+ cmd[5] = (rect->dy << 16) | (rect->dx);
+ cmd[6] = (height << 16) | width;
+
+ mb862xxfb_write_fifo(7, cmd, info);
+}
+
+void mb862xxfb_init_accel(struct fb_info *info, int xres)
+{
+ struct mb862xxfb_par *par = info->par;
+
+ if (info->var.bits_per_pixel == 32) {
+ info->fbops->fb_fillrect = cfb_fillrect;
+ info->fbops->fb_copyarea = cfb_copyarea;
+ info->fbops->fb_imageblit = cfb_imageblit;
+ } else {
+ outreg(disp, GC_L0EM, 3);
+ info->fbops->fb_fillrect = mb86290fb_fillrect;
+ info->fbops->fb_copyarea = mb86290fb_copyarea;
+ info->fbops->fb_imageblit = mb86290fb_imageblit;
+ }
+ outreg(draw, GDC_REG_DRAW_BASE, 0);
+ outreg(draw, GDC_REG_MODE_MISC, 0x8000);
+ outreg(draw, GDC_REG_X_RESOLUTION, xres);
+
+ info->flags |=
+ FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
+ FBINFO_HWACCEL_IMAGEBLIT;
+ info->fix.accel = 0xff; /*FIXME: add right define */
+}
+EXPORT_SYMBOL(mb862xxfb_init_accel);
diff --git a/drivers/video/mb862xx/mb862xxfb_accel.h b/drivers/video/mb862xx/mb862xxfb_accel.h
new file mode 100644
index 000000000000..96a2dfef0f60
--- /dev/null
+++ b/drivers/video/mb862xx/mb862xxfb_accel.h
@@ -0,0 +1,203 @@
+#ifndef __MB826XXFB_ACCEL_H__
+#define __MB826XXFB_ACCEL_H__
+
+/* registers */
+#define GDC_GEO_REG_INPUT_FIFO 0x00000400L
+
+/* Special Registers */
+#define GDC_REG_CTRL 0x00000400L
+#define GDC_REG_FIFO_STATUS 0x00000404L
+#define GDC_REG_FIFO_COUNT 0x00000408L
+#define GDC_REG_SETUP_STATUS 0x0000040CL
+#define GDC_REG_DDA_STATUS 0x00000410L
+#define GDC_REG_ENGINE_STATUS 0x00000414L
+#define GDC_REG_ERROR_STATUS 0x00000418L
+#define GDC_REG_MODE_MISC 0x00000420L /* MDR0 */
+#define GDC_REG_MODE_LINE 0x00000424L /* MDR1 */
+#define GDC_REG_MODE_POLYGON 0x00000428L /* MDR2 */
+#define GDC_REG_MODE_TEXTURE 0x0000042CL /* MDR3 */
+#define GDC_REG_MODE_BITMAP 0x00000430L /* MDR4 */
+#define GDC_REG_MODE_EXTENSION 0x0000043CL /* MDR7 */
+
+/* Configuration Registers */
+#define GDC_REG_DRAW_BASE 0x00000440L
+#define GDC_REG_X_RESOLUTION 0x00000444L
+#define GDC_REG_Z_BASE 0x00000448L
+#define GDC_REG_TEXTURE_BASE 0x0000044CL
+#define GDC_REG_POLYGON_FLAG_BASE 0x00000450L
+#define GDC_REG_CLIP_XMIN 0x00000454L
+#define GDC_REG_CLIP_XMAX 0x00000458L
+#define GDC_REG_CLIP_YMIN 0x0000045CL
+#define GDC_REG_CLIP_YMAX 0x00000460L
+#define GDC_REG_TEXURE_SIZE 0x00000464L
+#define GDC_REG_TILE_SIZE 0x00000468L
+#define GDC_REG_TEX_BUF_OFFSET 0x0000046CL
+
+/* for MB86293 or later */
+#define GDC_REG_ALPHA_MAP_BASE 0x00000474L /* ABR */
+
+/* Constant Registers */
+#define GDC_REG_FOREGROUND_COLOR 0x00000480L
+#define GDC_REG_BACKGROUND_COLOR 0x00000484L
+#define GDC_REG_ALPHA 0x00000488L
+#define GDC_REG_LINE_PATTERN 0x0000048CL
+#define GDC_REG_TEX_BORDER_COLOR 0x00000494L
+#define GDC_REG_LINE_PATTERN_OFFSET 0x000003E0L
+
+/* Coomand Code */
+#define GDC_CMD_PIXEL 0x00000000L
+#define GDC_CMD_PIXEL_Z 0x00000001L
+
+#define GDC_CMD_X_VECTOR 0x00000020L
+#define GDC_CMD_Y_VECTOR 0x00000021L
+#define GDC_CMD_X_VECTOR_NOEND 0x00000022L
+#define GDC_CMD_Y_VECTOR_NOEND 0x00000023L
+#define GDC_CMD_X_VECTOR_BLPO 0x00000024L
+#define GDC_CMD_Y_VECTOR_BLPO 0x00000025L
+#define GDC_CMD_X_VECTOR_NOEND_BLPO 0x00000026L
+#define GDC_CMD_Y_VECTOR_NOEND_BLPO 0x00000027L
+#define GDC_CMD_AA_X_VECTOR 0x00000028L
+#define GDC_CMD_AA_Y_VECTOR 0x00000029L
+#define GDC_CMD_AA_X_VECTOR_NOEND 0x0000002AL
+#define GDC_CMD_AA_Y_VECTOR_NOEND 0x0000002BL
+#define GDC_CMD_AA_X_VECTOR_BLPO 0x0000002CL
+#define GDC_CMD_AA_Y_VECTOR_BLPO 0x0000002DL
+#define GDC_CMD_AA_X_VECTOR_NOEND_BLPO 0x0000002EL
+#define GDC_CMD_AA_Y_VECTOR_NOEND_BLPO 0x0000002FL
+
+#define GDC_CMD_0_VECTOR 0x00000030L
+#define GDC_CMD_1_VECTOR 0x00000031L
+#define GDC_CMD_0_VECTOR_NOEND 0x00000032L
+#define GDC_CMD_1_VECTOR_NOEND 0x00000033L
+#define GDC_CMD_0_VECTOR_BLPO 0x00000034L
+#define GDC_CMD_1_VECTOR_BLPO 0x00000035L
+#define GDC_CMD_0_VECTOR_NOEND_BLPO 0x00000036L
+#define GDC_CMD_1_VECTOR_NOEND_BLPO 0x00000037L
+#define GDC_CMD_AA_0_VECTOR 0x00000038L
+#define GDC_CMD_AA_1_VECTOR 0x00000039L
+#define GDC_CMD_AA_0_VECTOR_NOEND 0x0000003AL
+#define GDC_CMD_AA_1_VECTOR_NOEND 0x0000003BL
+#define GDC_CMD_AA_0_VECTOR_BLPO 0x0000003CL
+#define GDC_CMD_AA_1_VECTOR_BLPO 0x0000003DL
+#define GDC_CMD_AA_0_VECTOR_NOEND_BLPO 0x0000003EL
+#define GDC_CMD_AA_1_VECTOR_NOEND_BLPO 0x0000003FL
+
+#define GDC_CMD_BLT_FILL 0x00000041L
+#define GDC_CMD_BLT_DRAW 0x00000042L
+#define GDC_CMD_BITMAP 0x00000043L
+#define GDC_CMD_BLTCOPY_TOP_LEFT 0x00000044L
+#define GDC_CMD_BLTCOPY_TOP_RIGHT 0x00000045L
+#define GDC_CMD_BLTCOPY_BOTTOM_LEFT 0x00000046L
+#define GDC_CMD_BLTCOPY_BOTTOM_RIGHT 0x00000047L
+#define GDC_CMD_LOAD_TEXTURE 0x00000048L
+#define GDC_CMD_LOAD_TILE 0x00000049L
+
+#define GDC_CMD_TRAP_RIGHT 0x00000060L
+#define GDC_CMD_TRAP_LEFT 0x00000061L
+#define GDC_CMD_TRIANGLE_FAN 0x00000062L
+#define GDC_CMD_FLAG_TRIANGLE_FAN 0x00000063L
+
+#define GDC_CMD_FLUSH_FB 0x000000C1L
+#define GDC_CMD_FLUSH_Z 0x000000C2L
+
+#define GDC_CMD_POLYGON_BEGIN 0x000000E0L
+#define GDC_CMD_POLYGON_END 0x000000E1L
+#define GDC_CMD_CLEAR_POLY_FLAG 0x000000E2L
+#define GDC_CMD_NORMAL 0x000000FFL
+
+#define GDC_CMD_VECTOR_BLPO_FLAG 0x00040000L
+#define GDC_CMD_FAST_VECTOR_BLPO_FLAG 0x00000004L
+
+/* for MB86293 or later */
+#define GDC_CMD_MDR1 0x00000000L
+#define GDC_CMD_MDR1S 0x00000002L
+#define GDC_CMD_MDR1B 0x00000004L
+#define GDC_CMD_MDR2 0x00000001L
+#define GDC_CMD_MDR2S 0x00000003L
+#define GDC_CMD_MDR2TL 0x00000007L
+#define GDC_CMD_GMDR1E 0x00000010L
+#define GDC_CMD_GMDR2E 0x00000020L
+#define GDC_CMD_OVERLAP_SHADOW_XY 0x00000000L
+#define GDC_CMD_OVERLAP_SHADOW_XY_COMPOSITION 0x00000001L
+#define GDC_CMD_OVERLAP_Z_PACKED_ONBS 0x00000007L
+#define GDC_CMD_OVERLAP_Z_ORIGIN 0x00000000L
+#define GDC_CMD_OVERLAP_Z_NON_TOPLEFT 0x00000001L
+#define GDC_CMD_OVERLAP_Z_BORDER 0x00000002L
+#define GDC_CMD_OVERLAP_Z_SHADOW 0x00000003L
+#define GDC_CMD_BLTCOPY_ALT_ALPHA 0x00000000L /* Reserverd */
+#define GDC_CMD_DC_LOGOUT 0x00000000L /* Reserverd */
+#define GDC_CMD_BODY_FORE_COLOR 0x00000000L
+#define GDC_CMD_BODY_BACK_COLOR 0x00000001L
+#define GDC_CMD_SHADOW_FORE_COLOR 0x00000002L
+#define GDC_CMD_SHADOW_BACK_COLOR 0x00000003L
+#define GDC_CMD_BORDER_FORE_COLOR 0x00000004L
+#define GDC_CMD_BORDER_BACK_COLOR 0x00000005L
+
+/* Type Code Table */
+#define GDC_TYPE_G_NOP 0x00000020L
+#define GDC_TYPE_G_BEGIN 0x00000021L
+#define GDC_TYPE_G_BEGINCONT 0x00000022L
+#define GDC_TYPE_G_END 0x00000023L
+#define GDC_TYPE_G_VERTEX 0x00000030L
+#define GDC_TYPE_G_VERTEXLOG 0x00000032L
+#define GDC_TYPE_G_VERTEXNOPLOG 0x00000033L
+#define GDC_TYPE_G_INIT 0x00000040L
+#define GDC_TYPE_G_VIEWPORT 0x00000041L
+#define GDC_TYPE_G_DEPTHRANGE 0x00000042L
+#define GDC_TYPE_G_LOADMATRIX 0x00000043L
+#define GDC_TYPE_G_VIEWVOLUMEXYCLIP 0x00000044L
+#define GDC_TYPE_G_VIEWVOLUMEZCLIP 0x00000045L
+#define GDC_TYPE_G_VIEWVOLUMEWCLIP 0x00000046L
+#define GDC_TYPE_SETLVERTEX2I 0x00000072L
+#define GDC_TYPE_SETLVERTEX2IP 0x00000073L
+#define GDC_TYPE_SETMODEREGISTER 0x000000C0L
+#define GDC_TYPE_SETGMODEREGISTER 0x000000C1L
+#define GDC_TYPE_OVERLAPXYOFFT 0x000000C8L
+#define GDC_TYPE_OVERLAPZOFFT 0x000000C9L
+#define GDC_TYPE_DC_LOGOUTADDR 0x000000CCL
+#define GDC_TYPE_SETCOLORREGISTER 0x000000CEL
+#define GDC_TYPE_G_BEGINE 0x000000E1L
+#define GDC_TYPE_G_BEGINCONTE 0x000000E2L
+#define GDC_TYPE_G_ENDE 0x000000E3L
+#define GDC_TYPE_DRAWPIXEL 0x00000000L
+#define GDC_TYPE_DRAWPIXELZ 0x00000001L
+#define GDC_TYPE_DRAWLINE 0x00000002L
+#define GDC_TYPE_DRAWLINE2I 0x00000003L
+#define GDC_TYPE_DRAWLINE2IP 0x00000004L
+#define GDC_TYPE_DRAWTRAP 0x00000005L
+#define GDC_TYPE_DRAWVERTEX2I 0x00000006L
+#define GDC_TYPE_DRAWVERTEX2IP 0x00000007L
+#define GDC_TYPE_DRAWRECTP 0x00000009L
+#define GDC_TYPE_DRAWBITMAPP 0x0000000BL
+#define GDC_TYPE_BLTCOPYP 0x0000000DL
+#define GDC_TYPE_BLTCOPYALTERNATEP 0x0000000FL
+#define GDC_TYPE_LOADTEXTUREP 0x00000011L
+#define GDC_TYPE_BLTTEXTUREP 0x00000013L
+#define GDC_TYPE_BLTCOPYALTALPHABLENDP 0x0000001FL
+#define GDC_TYPE_SETVERTEX2I 0x00000070L
+#define GDC_TYPE_SETVERTEX2IP 0x00000071L
+#define GDC_TYPE_DRAW 0x000000F0L
+#define GDC_TYPE_SETREGISTER 0x000000F1L
+#define GDC_TYPE_SYNC 0x000000FCL
+#define GDC_TYPE_INTERRUPT 0x000000FDL
+#define GDC_TYPE_NOP 0x0
+
+/* Raster operation */
+#define GDC_ROP_CLEAR 0x0000
+#define GDC_ROP_AND 0x0001
+#define GDC_ROP_AND_REVERSE 0x0002
+#define GDC_ROP_COPY 0x0003
+#define GDC_ROP_AND_INVERTED 0x0004
+#define GDC_ROP_NOP 0x0005
+#define GDC_ROP_XOR 0x0006
+#define GDC_ROP_OR 0x0007
+#define GDC_ROP_NOR 0x0008
+#define GDC_ROP_EQUIV 0x0009
+#define GDC_ROP_INVERT 0x000A
+#define GDC_ROP_OR_REVERSE 0x000B
+#define GDC_ROP_COPY_INVERTED 0x000C
+#define GDC_ROP_OR_INVERTED 0x000D
+#define GDC_ROP_NAND 0x000E
+#define GDC_ROP_SET 0x000F
+
+#endif
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index 34e4e7995169..0129f1bc3522 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/fb.h>
+#include <linux/kernel.h>
#undef DEBUG
@@ -402,21 +403,6 @@ const struct fb_videomode vesa_modes[] = {
EXPORT_SYMBOL(vesa_modes);
#endif /* CONFIG_FB_MODE_HELPERS */
-static int my_atoi(const char *name)
-{
- int val = 0;
-
- for (;; name++) {
- switch (*name) {
- case '0' ... '9':
- val = 10*val+(*name-'0');
- break;
- default:
- return val;
- }
- }
-}
-
/**
* fb_try_mode - test a video mode
* @var: frame buffer user defined part of display
@@ -539,7 +525,7 @@ int fb_find_mode(struct fb_var_screeninfo *var,
namelen = i;
if (!refresh_specified && !bpp_specified &&
!yres_specified) {
- refresh = my_atoi(&name[i+1]);
+ refresh = simple_strtol(&name[i+1], NULL, 10);
refresh_specified = 1;
if (cvt || rb)
cvt = 0;
@@ -549,7 +535,7 @@ int fb_find_mode(struct fb_var_screeninfo *var,
case '-':
namelen = i;
if (!bpp_specified && !yres_specified) {
- bpp = my_atoi(&name[i+1]);
+ bpp = simple_strtol(&name[i+1], NULL, 10);
bpp_specified = 1;
if (cvt || rb)
cvt = 0;
@@ -558,7 +544,7 @@ int fb_find_mode(struct fb_var_screeninfo *var,
break;
case 'x':
if (!yres_specified) {
- yres = my_atoi(&name[i+1]);
+ yres = simple_strtol(&name[i+1], NULL, 10);
yres_specified = 1;
} else
goto done;
@@ -586,7 +572,7 @@ int fb_find_mode(struct fb_var_screeninfo *var,
}
}
if (i < 0 && yres_specified) {
- xres = my_atoi(name);
+ xres = simple_strtol(name, NULL, 10);
res_specified = 1;
}
done:
diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c
index 760645d9dbb6..e3eccc9af78e 100644
--- a/drivers/video/omap/lcd_2430sdp.c
+++ b/drivers/video/omap/lcd_2430sdp.c
@@ -25,7 +25,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#include <plat/mux.h>
#include <asm/mach-types.h>
@@ -52,7 +52,7 @@ static unsigned enable_gpio;
#define TWL4030_VPLL2_DEV_GRP 0x33
#define TWL4030_VPLL2_DEDICATED 0x36
-#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v)
+#define t2_out(c, r, v) twl_i2c_write_u8(c, r, v)
static int sdp2430_panel_init(struct lcd_panel *panel,
diff --git a/drivers/video/output.c b/drivers/video/output.c
index 5e6439ae7394..5137aa016b83 100644
--- a/drivers/video/output.c
+++ b/drivers/video/output.c
@@ -50,7 +50,7 @@ static ssize_t video_output_store_state(struct device *dev,
int request_state = simple_strtoul(buf,&endp,0);
size_t size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
return -EINVAL;
diff --git a/drivers/video/pmag-ba-fb.c b/drivers/video/pmag-ba-fb.c
index 0573ec685a57..0f361b6100d2 100644
--- a/drivers/video/pmag-ba-fb.c
+++ b/drivers/video/pmag-ba-fb.c
@@ -98,7 +98,8 @@ static int pmagbafb_setcolreg(unsigned int regno, unsigned int red,
{
struct pmagbafb_par *par = info->par;
- BUG_ON(regno >= info->cmap.len);
+ if (regno >= info->cmap.len)
+ return 1;
red >>= 8; /* The cmap fields are 16 bits */
green >>= 8; /* wide, but the hardware colormap */
diff --git a/drivers/video/pmagb-b-fb.c b/drivers/video/pmagb-b-fb.c
index 98748723af9f..2de0806421b4 100644
--- a/drivers/video/pmagb-b-fb.c
+++ b/drivers/video/pmagb-b-fb.c
@@ -102,7 +102,8 @@ static int pmagbbfb_setcolreg(unsigned int regno, unsigned int red,
{
struct pmagbbfb_par *par = info->par;
- BUG_ON(regno >= info->cmap.len);
+ if (regno >= info->cmap.len)
+ return 1;
red >>= 8; /* The cmap fields are 16 bits */
green >>= 8; /* wide, but the hardware colormap */
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index f58a3aae6ea6..415858b421b3 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -1221,13 +1221,14 @@ static void setup_smart_timing(struct pxafb_info *fbi,
static int pxafb_smart_thread(void *arg)
{
struct pxafb_info *fbi = arg;
- struct pxafb_mach_info *inf = fbi->dev->platform_data;
+ struct pxafb_mach_info *inf;
- if (!fbi || !inf->smart_update) {
+ if (!fbi || !fbi->dev->platform_data->smart_update) {
pr_err("%s: not properly initialized, thread terminated\n",
__func__);
return -EINVAL;
}
+ inf = fbi->dev->platform_data;
pr_debug("%s(): task starting\n", __func__);
@@ -1667,7 +1668,7 @@ static int pxafb_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops pxafb_pm_ops = {
+static const struct dev_pm_ops pxafb_pm_ops = {
.suspend = pxafb_suspend,
.resume = pxafb_resume,
};
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index b4b5de930cf5..8a65fb6648a6 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -890,7 +890,7 @@ static int sh_mobile_lcdc_runtime_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
+static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
.suspend = sh_mobile_lcdc_suspend,
.resume = sh_mobile_lcdc_resume,
.runtime_suspend = sh_mobile_lcdc_runtime_suspend,
diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
index a4e05e4d7501..9d2b6bc49036 100644
--- a/drivers/video/sis/sis_main.c
+++ b/drivers/video/sis/sis_main.c
@@ -2115,7 +2115,7 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo)
if( (!(ivideo->vbflags2 & VB2_SISBRIDGE)) &&
(!((ivideo->sisvga_engine == SIS_315_VGA) &&
(ivideo->vbflags2 & VB2_CHRONTEL))) ) {
- if(ivideo->sisfb_tvstd & (TV_PALN | TV_PALN | TV_NTSCJ)) {
+ if(ivideo->sisfb_tvstd & (TV_PALM | TV_PALN | TV_NTSCJ)) {
ivideo->sisfb_tvstd = -1;
printk(KERN_ERR "sisfb: PALM/PALN/NTSCJ not supported\n");
}
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c
index 924d79462780..35370d0ecf03 100644
--- a/drivers/video/sm501fb.c
+++ b/drivers/video/sm501fb.c
@@ -29,8 +29,8 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/console.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
@@ -66,6 +66,7 @@ struct sm501fb_info {
struct fb_info *fb[2]; /* fb info for both heads */
struct resource *fbmem_res; /* framebuffer resource */
struct resource *regs_res; /* registers resource */
+ struct resource *regs2d_res; /* 2d registers resource */
struct sm501_platdata_fb *pdata; /* our platform data */
unsigned long pm_crt_ctrl; /* pm: crt ctrl save */
@@ -73,6 +74,7 @@ struct sm501fb_info {
int irq;
int swap_endian; /* set to swap rgb=>bgr */
void __iomem *regs; /* remapped registers */
+ void __iomem *regs2d; /* 2d remapped registers */
void __iomem *fbmem; /* remapped framebuffer */
size_t fbmem_len; /* length of remapped region */
};
@@ -123,9 +125,9 @@ static inline void sm501fb_sync_regs(struct sm501fb_info *info)
* This is an attempt to lay out memory for the two framebuffers and
* everything else
*
- * |fbmem_res->start fbmem_res->end|
- * | |
- * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K |
+ * |fbmem_res->start fbmem_res->end|
+ * | |
+ * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K |
* |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-|
*
* The "spare" space is for the 2d engine data
@@ -1246,7 +1248,173 @@ static ssize_t sm501fb_debug_show_pnl(struct device *dev,
static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL);
-/* framebuffer ops */
+/* acceleration operations */
+static int sm501fb_sync(struct fb_info *info)
+{
+ int count = 1000000;
+ struct sm501fb_par *par = info->par;
+ struct sm501fb_info *fbi = par->info;
+
+ /* wait for the 2d engine to be ready */
+ while ((count > 0) &&
+ (readl(fbi->regs + SM501_SYSTEM_CONTROL) &
+ SM501_SYSCTRL_2D_ENGINE_STATUS) != 0)
+ count--;
+
+ if (count <= 0) {
+ dev_err(info->dev, "Timeout waiting for 2d engine sync\n");
+ return 1;
+ }
+ return 0;
+}
+
+static void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct sm501fb_par *par = info->par;
+ struct sm501fb_info *fbi = par->info;
+ int width = area->width;
+ int height = area->height;
+ int sx = area->sx;
+ int sy = area->sy;
+ int dx = area->dx;
+ int dy = area->dy;
+ unsigned long rtl = 0;
+
+ /* source clip */
+ if ((sx >= info->var.xres_virtual) ||
+ (sy >= info->var.yres_virtual))
+ /* source Area not within virtual screen, skipping */
+ return;
+ if ((sx + width) >= info->var.xres_virtual)
+ width = info->var.xres_virtual - sx - 1;
+ if ((sy + height) >= info->var.yres_virtual)
+ height = info->var.yres_virtual - sy - 1;
+
+ /* dest clip */
+ if ((dx >= info->var.xres_virtual) ||
+ (dy >= info->var.yres_virtual))
+ /* Destination Area not within virtual screen, skipping */
+ return;
+ if ((dx + width) >= info->var.xres_virtual)
+ width = info->var.xres_virtual - dx - 1;
+ if ((dy + height) >= info->var.yres_virtual)
+ height = info->var.yres_virtual - dy - 1;
+
+ if ((sx < dx) || (sy < dy)) {
+ rtl = 1 << 27;
+ sx += width - 1;
+ dx += width - 1;
+ sy += height - 1;
+ dy += height - 1;
+ }
+
+ if (sm501fb_sync(info))
+ return;
+
+ /* set the base addresses */
+ writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
+ writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE);
+
+ /* set the window width */
+ writel((info->var.xres << 16) | info->var.xres,
+ fbi->regs2d + SM501_2D_WINDOW_WIDTH);
+
+ /* set window stride */
+ writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
+ fbi->regs2d + SM501_2D_PITCH);
+
+ /* set data format */
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ writel(0, fbi->regs2d + SM501_2D_STRETCH);
+ break;
+ case 16:
+ writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
+ break;
+ case 32:
+ writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
+ break;
+ }
+
+ /* 2d compare mask */
+ writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
+
+ /* 2d mask */
+ writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
+
+ /* source and destination x y */
+ writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE);
+ writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION);
+
+ /* w/h */
+ writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
+
+ /* do area move */
+ writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL);
+}
+
+static void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct sm501fb_par *par = info->par;
+ struct sm501fb_info *fbi = par->info;
+ int width = rect->width, height = rect->height;
+
+ if ((rect->dx >= info->var.xres_virtual) ||
+ (rect->dy >= info->var.yres_virtual))
+ /* Rectangle not within virtual screen, skipping */
+ return;
+ if ((rect->dx + width) >= info->var.xres_virtual)
+ width = info->var.xres_virtual - rect->dx - 1;
+ if ((rect->dy + height) >= info->var.yres_virtual)
+ height = info->var.yres_virtual - rect->dy - 1;
+
+ if (sm501fb_sync(info))
+ return;
+
+ /* set the base addresses */
+ writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
+ writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE);
+
+ /* set the window width */
+ writel((info->var.xres << 16) | info->var.xres,
+ fbi->regs2d + SM501_2D_WINDOW_WIDTH);
+
+ /* set window stride */
+ writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
+ fbi->regs2d + SM501_2D_PITCH);
+
+ /* set data format */
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ writel(0, fbi->regs2d + SM501_2D_STRETCH);
+ break;
+ case 16:
+ writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
+ break;
+ case 32:
+ writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
+ break;
+ }
+
+ /* 2d compare mask */
+ writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
+
+ /* 2d mask */
+ writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
+
+ /* colour */
+ writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND);
+
+ /* x y */
+ writel((rect->dx << 16) | rect->dy, fbi->regs2d + SM501_2D_DESTINATION);
+
+ /* w/h */
+ writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
+
+ /* do rectangle fill */
+ writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL);
+}
+
static struct fb_ops sm501fb_ops_crt = {
.owner = THIS_MODULE,
@@ -1256,9 +1424,10 @@ static struct fb_ops sm501fb_ops_crt = {
.fb_setcolreg = sm501fb_setcolreg,
.fb_pan_display = sm501fb_pan_crt,
.fb_cursor = sm501fb_cursor,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
+ .fb_fillrect = sm501fb_fillrect,
+ .fb_copyarea = sm501fb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_sync = sm501fb_sync,
};
static struct fb_ops sm501fb_ops_pnl = {
@@ -1269,9 +1438,10 @@ static struct fb_ops sm501fb_ops_pnl = {
.fb_blank = sm501fb_blank_pnl,
.fb_setcolreg = sm501fb_setcolreg,
.fb_cursor = sm501fb_cursor,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
+ .fb_fillrect = sm501fb_fillrect,
+ .fb_copyarea = sm501fb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_sync = sm501fb_sync,
};
/* sm501_init_cursor
@@ -1329,7 +1499,8 @@ static int sm501fb_start(struct sm501fb_info *info,
dev_warn(dev, "no irq for device\n");
}
- /* allocate, reserve and remap resources for registers */
+ /* allocate, reserve and remap resources for display
+ * controller registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev, "no resource definition for registers\n");
@@ -1338,7 +1509,7 @@ static int sm501fb_start(struct sm501fb_info *info,
}
info->regs_res = request_mem_region(res->start,
- res->end - res->start,
+ resource_size(res),
pdev->name);
if (info->regs_res == NULL) {
@@ -1347,37 +1518,63 @@ static int sm501fb_start(struct sm501fb_info *info,
goto err_release;
}
- info->regs = ioremap(res->start, (res->end - res->start)+1);
+ info->regs = ioremap(res->start, resource_size(res));
if (info->regs == NULL) {
dev_err(dev, "cannot remap registers\n");
ret = -ENXIO;
goto err_regs_res;
}
+ /* allocate, reserve and remap resources for 2d
+ * controller registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res == NULL) {
+ dev_err(dev, "no resource definition for 2d registers\n");
+ ret = -ENOENT;
+ goto err_regs_map;
+ }
+
+ info->regs2d_res = request_mem_region(res->start,
+ resource_size(res),
+ pdev->name);
+
+ if (info->regs2d_res == NULL) {
+ dev_err(dev, "cannot claim registers\n");
+ ret = -ENXIO;
+ goto err_regs_map;
+ }
+
+ info->regs2d = ioremap(res->start, resource_size(res));
+ if (info->regs2d == NULL) {
+ dev_err(dev, "cannot remap registers\n");
+ ret = -ENXIO;
+ goto err_regs2d_res;
+ }
+
/* allocate, reserve resources for framebuffer */
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (res == NULL) {
dev_err(dev, "no memory resource defined\n");
ret = -ENXIO;
- goto err_regs_map;
+ goto err_regs2d_map;
}
info->fbmem_res = request_mem_region(res->start,
- (res->end - res->start)+1,
+ resource_size(res),
pdev->name);
if (info->fbmem_res == NULL) {
dev_err(dev, "cannot claim framebuffer\n");
ret = -ENXIO;
- goto err_regs_map;
+ goto err_regs2d_map;
}
- info->fbmem = ioremap(res->start, (res->end - res->start)+1);
+ info->fbmem = ioremap(res->start, resource_size(res));
if (info->fbmem == NULL) {
dev_err(dev, "cannot remap framebuffer\n");
goto err_mem_res;
}
- info->fbmem_len = (res->end - res->start)+1;
+ info->fbmem_len = resource_size(res);
/* clear framebuffer memory - avoids garbage data on unused fb */
memset(info->fbmem, 0, info->fbmem_len);
@@ -1389,8 +1586,10 @@ static int sm501fb_start(struct sm501fb_info *info,
/* enable display controller */
sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1);
- /* setup cursors */
+ /* enable 2d controller */
+ sm501_unit_power(dev->parent, SM501_GATE_2D_ENGINE, 1);
+ /* setup cursors */
sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
@@ -1400,6 +1599,13 @@ static int sm501fb_start(struct sm501fb_info *info,
release_resource(info->fbmem_res);
kfree(info->fbmem_res);
+ err_regs2d_map:
+ iounmap(info->regs2d);
+
+ err_regs2d_res:
+ release_resource(info->regs2d_res);
+ kfree(info->regs2d_res);
+
err_regs_map:
iounmap(info->regs);
@@ -1420,6 +1626,10 @@ static void sm501fb_stop(struct sm501fb_info *info)
release_resource(info->fbmem_res);
kfree(info->fbmem_res);
+ iounmap(info->regs2d);
+ release_resource(info->regs2d_res);
+ kfree(info->regs2d_res);
+
iounmap(info->regs);
release_resource(info->regs_res);
kfree(info->regs_res);
@@ -1486,7 +1696,8 @@ static int sm501fb_init_fb(struct fb_info *fb,
par->ops.fb_cursor = NULL;
fb->fbops = &par->ops;
- fb->flags = FBINFO_FLAG_DEFAULT |
+ fb->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST |
+ FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
/* fixed data */
diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c
index e3e597f937a5..09353e2b92f6 100644
--- a/drivers/video/via/lcd.c
+++ b/drivers/video/via/lcd.c
@@ -1134,45 +1134,33 @@ static void integrated_lvds_enable(struct lvds_setting_information
*plvds_setting_info,
struct lvds_chip_information *plvds_chip_info)
{
- bool turn_on_first_powersequence = false;
- bool turn_on_second_powersequence = false;
-
DEBUG_MSG(KERN_INFO "integrated_lvds_enable, out_interface:%d\n",
plvds_chip_info->output_interface);
if (plvds_setting_info->lcd_mode == LCD_SPWG)
viafb_write_reg_mask(CRD2, VIACR, 0x00, BIT0 + BIT1);
- else
+ else
viafb_write_reg_mask(CRD2, VIACR, 0x03, BIT0 + BIT1);
- if (INTERFACE_LVDS0LVDS1 == plvds_chip_info->output_interface)
- turn_on_first_powersequence = true;
- if (INTERFACE_LVDS0 == plvds_chip_info->output_interface)
- turn_on_first_powersequence = true;
- if (INTERFACE_LVDS1 == plvds_chip_info->output_interface)
- turn_on_second_powersequence = true;
-
- if (turn_on_second_powersequence) {
- /* Use second power sequence control: */
-
- /* Use hardware control power sequence. */
- viafb_write_reg_mask(CRD3, VIACR, 0, BIT0);
-
- /* Turn on back light. */
- viafb_write_reg_mask(CRD3, VIACR, 0, BIT6 + BIT7);
- /* Turn on hardware power sequence. */
- viafb_write_reg_mask(CRD4, VIACR, 0x02, BIT1);
- }
- if (turn_on_first_powersequence) {
+ switch (plvds_chip_info->output_interface) {
+ case INTERFACE_LVDS0LVDS1:
+ case INTERFACE_LVDS0:
/* Use first power sequence control: */
-
/* Use hardware control power sequence. */
viafb_write_reg_mask(CR91, VIACR, 0, BIT0);
-
/* Turn on back light. */
viafb_write_reg_mask(CR91, VIACR, 0, BIT6 + BIT7);
-
/* Turn on hardware power sequence. */
viafb_write_reg_mask(CR6A, VIACR, 0x08, BIT3);
+ break;
+ case INTERFACE_LVDS1:
+ /* Use second power sequence control: */
+ /* Use hardware control power sequence. */
+ viafb_write_reg_mask(CRD3, VIACR, 0, BIT0);
+ /* Turn on back light. */
+ viafb_write_reg_mask(CRD3, VIACR, 0, BIT6 + BIT7);
+ /* Turn on hardware power sequence. */
+ viafb_write_reg_mask(CRD4, VIACR, 0x02, BIT1);
+ break;
}
/* Turn DFP High/Low pad on. */
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c
index 56ec696e8afa..10d8c4b4baeb 100644
--- a/drivers/video/via/viafbdev.c
+++ b/drivers/video/via/viafbdev.c
@@ -1797,7 +1797,7 @@ static const struct file_operations viafb_vt1636_proc_fops = {
static void viafb_init_proc(struct proc_dir_entry **viafb_entry)
{
*viafb_entry = proc_mkdir("viafb", NULL);
- if (viafb_entry) {
+ if (*viafb_entry) {
proc_create("dvp0", 0, *viafb_entry, &viafb_dvp0_proc_fops);
proc_create("dvp1", 0, *viafb_entry, &viafb_dvp1_proc_fops);
proc_create("dfph", 0, *viafb_entry, &viafb_dfph_proc_fops);
diff --git a/drivers/watchdog/adx_wdt.c b/drivers/watchdog/adx_wdt.c
index 77afb0acc500..9c6594473d3b 100644
--- a/drivers/watchdog/adx_wdt.c
+++ b/drivers/watchdog/adx_wdt.c
@@ -314,7 +314,7 @@ static int adx_wdt_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops adx_wdt_pm_ops = {
+static const struct dev_pm_ops adx_wdt_pm_ops = {
.suspend = adx_wdt_suspend,
.resume = adx_wdt_resume,
};
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index cb46556f2973..8162a40d1522 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -26,7 +26,7 @@
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
-#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl.h>
#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3
@@ -48,7 +48,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
static int twl4030_wdt_write(unsigned char val)
{
- return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
+ return twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
TWL4030_WATCHDOG_CFG_REG_OFFS);
}